How to automatically update a mesh on changing parameters on THREE.js - javascript

I'm new to Threejs and to javascript in general. I'm building a mesh based on a json parameters object and it works great.
Now I'm adding some UI components (button, slider, etc) to change the json and rebuild the mesh. I'm writing some monkey-code and I'm sure it can be avoided. Now my stuff works correctly but I'm sure that the problem can be solved more elegantly.
This is my actual workflow:
on slider change use a callback
that callback calls a setter
the setter change the JSON parameters and rebuild the mesh
I'm sure all this can be automated by simply change the JSON and some javascript magic.
Here is the actual pseudocode:
sliderWidth.onChange( function(newValue) {
MyObject3D.setWidth(newValue);
});
and this is a skelton of my MyObject3D:
var MyObject3D = function(parameters) {
this.parameters = parameters;
this.init();
};
MyObject3D.prototype.init = function() {
var geometry = this.initGeometry();
var material = new THREE.MeshPhongMaterial( { color: this.parameters.color, shading: THREE.FlatShading } );
this.mesh = new THREE.Mesh(geometry, material);
}
MyObject3D.prototype.setWidth = function(n) {
this.parameters.width = n;
scene.remove( this.mesh );
this.init();
scene.add( this.mesh );
}
I'm sure that with the javascript high-dinamicity and with all brand new frameworks this piece of code can be easily automated. I've looked mainly at d3.js and at angular.js. In particular d3.js seems to be perfect with the enter-update-exit paradigm (but I didn't find a standard way to conjugate it with Three.js) and angular for sure has to be something in it.
Any kind of advice on structure, pattern, specific use case or examples based on one of the frameworks mentioned THREE.js, d3.js, angular.js) can be enlightening. Actually I'd prefer a solution with angular.js..

There was an excellent talk about using three.js and angular.js which exactly showcases your problem.
https://www.youtube.com/watch?v=mCIZoLaPJxM&t=8m5s
Im using angular directives that watch on a specific attribute and then execute some logic onchange in my projects.
$scope.$watch($attrs.something, function(changedSomething){
doSomeLogicWith(changedSomething);
});

Related

ID Rendering + Diffuse Rendering in THREE.js

I'm trying to use onBeforeRender to add an id material override function in my THREEjs app. This is a pretty common function for rendering applications, and I'd like to be able to use a scene.overrideMaterial to do it, but from my research it seems like I'm going to roll my own.
My approach is as follows.
When I instantiate objects:
...
m.onBeforeRender = function (){
if(Renderer._renderID){
this.material = new THREE.MeshBasicMaterial(this.userData.idColor.getHex());
this.material.needsUpdate = true;
// Attempt at using a shader material
//this.material = Renderer._idMat;
//this.material.uniforms.idColor.value = this.userData.idColor;
//this.material.uniforms.needsUpdate = true;
}
}
m.onAfterRender = function (){
if(Renderer._renderID){
this.material = this.userData.material;
}
}
...
In my render loop:
...
var idTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
Renderer._renderID = true;
Renderer._renderer.render(Renderer._scene, Renderer._camera, idTarget);
Renderer._renderID = false;
...
Right now the id buffer and diffuse buffer are both rendering with the diffuse material.
Any help would be much appreciated. Please let me know if additional information is needed, I will update this post.
Three.js has an official gpu picking example which can be seen here.
They seem to be rendering things in two different scenes and with vertex colors. That's not a bad approach, and i disagree with the comment and your commented out code. Changing the uniforms is not the only way to achieve this, the official example uses vertex colors and merges all the geometries.
There are many ways to skin this cat.
You should by all means avoid creating new materials in tight loops.
This is suuuuper bad:
m.onBeforeRender = function (){
if(Renderer._renderID){
this.material = new THREE.MeshBasicMaterial(this.userData.idColor.getHex()); //just a big NO
this.material.needsUpdate = true; //no it doesnt! it's a new material it has this true by default
To achieve something like this, or your own custom override:
const myMesh = new Mesh()
myMesh.myMaterials = [
someRenderMaterial,
someIDMaterial,
]
scene.traverse(o=>{if(o.myMaterials) o.material = o.myMaterials[1]})
renderer.render(scene,camera)
scene.traverse(o=>{if(o.myMaterials) o.material = o.myMaterials[0]})
renderer.render(scene,camera)
But again, there's many many ways to do this. You could just make another scene and render vertex colors. You could make an override material and render vertex colors. Many parameters, many permutations.

How do you implement drag controls in the AutoDesk forge viewer?

I am trying to implement drag controls on this text geometry that I am creating in the viewer. I create the text like so:
createText(params) {
const textGeometry = new TextGeometry(params.text,
Object.assign({}, {
font: new Font(FontJson),
params
}));
const geometry = new THREE.BufferGeometry;
geometry.fromGeometry(textGeometry);
const material = this.createColorMaterial(
params.color);
const text = new THREE.Mesh(
geometry, material);
text.scale.set(params.scale, params.scale, params.scale);
text.position.set(
params.position.x,
params.position.y,
10);
this.intersectMeshes.push(text);
this.viewer.impl.scene.add(text);
this.viewer.impl.sceneUpdated(true);
return text;
}
This works great, the meshes get added to the viewer, I can see them. Fantastic! Thus far, it is good. Now, I want to be able to drag them around with my mouse after I have added them. I noticed that Three.js already has drag controls built in, so I just implemented them like so:
enableDragging(){
let controls = new THREE.DragControls( this.viewer, this.viewer.impl.camera.perspectiveCamera, this.viewer.impl.canvas );
controls.addEventListener( 'dragstart', dragStartCallback );
let startColor;
controls.addEventListener( 'dragend', dragendCallback );
function dragStartCallback(event) {
startColor = event.object.material.color.getHex();
event.object.material.color.setHex(0x000000);
}
function dragendCallback(event) {
event.object.material.color.setColor(startColor);
}
}
After a big of debugging, I have seen where the problem occurs. For some reason, when I click on one of the meshes, the raycaster doesn't find any intersections. I.E. the array I get back is empty. No matter where I click on these objects.
Is my implementation wrong, or did I provision these meshes wrong to make them draggable? I have gotten the drag controls to work outside of the viewer, just not within it.
This will not work, looking at the code of DragControls, the viewer implementation is too different in the way it implements the camera. You would need to either implement a custom version of DragControls or take a look at my transform tool and adapt it for custom meshes:
Moving visually your components in the viewer using the TransformTool

Three.js - Load a model with material that doesn't require a light source to be seen

I'm currently using Three.js, version 71. I first create my models using blender, and then I export them as a JSON file. I then use THREE.JSONLoader to load the models into my scene using the following:
this.jsonLoader.load(pathToModelFile, function(geometry, materials) {
//...
});
The materials list only contains THREE.MeshPhongMaterial at index 0. This material seems to require a light source (like THREE.SpotLight for example) to be in my scene. Otherwise, my model will be black.
I basically just want to be able to load my models and not need to use a light source in order to see them. Therefore, I have the following questions, and answering any one of them would solve my problem:
Is there some flag or property in THREE.MeshPhongMaterial I could change that would allow my model to be seen without a light source?
If number 1 isn't possible, is there a way to use THREE.JSONLoader to give me a different kind of material that doesn't need a light source? For example, like THREE.MeshBasicMaterial?
Is there some way to export my models from blender that will already have the required flags/properties set (if possible)?
It sounds like I'm having the same problem that this guy mentions in the following link, but he never received an answer: Switch lighting of THREE.MeshPhongMaterial on / off dynamically
1 dont know, think not
2 yes
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load(model, addthree1ToScene);
function addthree1ToScene( geometry, materials )
{
material = new THREE.MeshBasicMaterial(blahblah);
three1 = new THREE.Mesh( geometry, material );
scene.add( three1 );
console.log(three1);
}
3 yes, in Blender you can set a material type on mesh before exporting, or you can edit material variables in the exported file
EDIT:
Or most stupid way, it is possible to edit Material in expoted Json File, via discussion to this answer.

How to remove and dispose of all child geometry and meshes from an Object3D?

In this project I am working on I have several Collada models being displayed and then removed when not needed anymore. There seems to be a memory leak somewhere in the project and I am looking for ways to get it to run as smooth as possible as time is not on my sideā€¦
I feel like I don't remove the meshes the right way and that this might cause some of the memory leakage I have.
I load the objects with LoadObject(level_1_character, "Assets/Level_1_Character.dae"); for example, where level_1_character is a Object3D. This calls the following function:
function LoadObject(name, url) {
var mesh, geometry, material, animation;
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(url, function(col) {
mesh = col;
geometry = col.scene;
name.name = url.toString();
name.add(geometry);
});
}
I add the objects to the scene depending on the level through scene.add(level_1_character); and then remove it by doing the following:
level_1_character.traverse(function(child){
if (child instanceof THREE.Mesh) {
child.material.dispose();
child.geometry.dispose();
}
});
I am not sure if this actually fully removes the object though. It seems as though the objects still are present in memory. Any idea what I'm doing wrong here?

Three.js: Import and use 3d models

Hello!
I have searched ALOT about this on the web but havnt got anything that is working.
My question is: HOW do I use 3d models like collada, stl, obj, AND MOVE it with example model.position.rotation=10;?
Whitch is the easiest way of importing models in these formats? I only need one format to import to my three.js code.
Tnx!
I always convert my .obj files to JSON and then load them into Three.js.
For that conversion I use convert_obj_three.py script. You just need to run it and it will do all the work with the conversion.
And for the loading part, you can do this (with some examples how to manipulate the mesh):
function addMapMesh()
{
var loader = new THREE.JSONLoader();
loader.load("convertedFile.js", function(geometry){
mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(geometry.materials));
mesh.position.x -= 5.0;
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.05;
mesh.rotation.x = .25*Math.PI;
scene.add(mesh);
//make sure mesh is loaded before renderering
loadRestOfScene()
});
}
can't say much about how to move, rotate or manipulate modells using Three.js, but I remember there were a few converter-scripts and some exporter-scripts for Blender to get different 3D-Modell-types into Three.js.
This link might help you. It provides Links to converter-scripts and even shows a rotation in the example code!

Categories