Cloning a THREE.Group hierarchy - three.js

I have a hierarchy of groups, A -> B -> C. I wish to create a clone of this hierarchy, A2 -> B2 -> C2.
But Object3D.clone() removes the parent reference of the group.
Other than manually setting the parent for each of the child-groups after cloning, what other way is there?
If the hierarchy is deep this could get take to compute.
Thanks for the help!

Maybe you can check out this question Will three.js Object3D.clone() create a deep copy of the geometry?
I extends the copyand clone methods in Object3D to deep clone mesh materials.
And in your case , this should works too.
First,extends two new methods in THREE:
THREE.Object3D.prototype.deepClone = function ( recursive ) {
return new this.constructor().deepCopy( this, recursive );
},
THREE.Object3D.prototype.deepCopy = function( source, recursive ) {
if ( recursive === undefined ) recursive = true;
this.name = source.name;
this.up.copy( source.up );
this.position.copy( source.position );
this.quaternion.copy( source.quaternion );
this.scale.copy( source.scale );
this.matrix.copy( source.matrix );
this.matrixWorld.copy( source.matrixWorld );
if(source.material){
//changed
this.material = source.material.clone()
}
if(source.geometry){
//changed
this.geometry = source.geometry.clone()
}
this.matrixAutoUpdate = source.matrixAutoUpdate;
this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
this.layers.mask = source.layers.mask;
this.visible = source.visible;
this.castShadow = source.castShadow;
this.receiveShadow = source.receiveShadow;
this.frustumCulled = source.frustumCulled;
this.renderOrder = source.renderOrder;
this.userData = JSON.parse( JSON.stringify( source.userData ) );
if ( recursive === true ) {
for ( var i = 0; i < source.children.length; i ++ ) {
var child = source.children[ i ];
this.add( child.deepClone() ); //changed
}
}
return this;
}
Second,when you want to deep clone a Object3D or Scene named originalObj.just do var newObj = originalObj.deepClone()

Related

abort object3d.traverse in three.js

How can I abort the traverse when I traverse a three.js object?
scene.traverse(x => {
if (x.userData)
// stop traversing here
});
return and break do not work
Object3D.traverse code (r134)
You would need to modify the traverse function to bail based on output from your callback function.
Something like this (untested):
// three.js/src/core/Object3D.js
traverse( callback ) {
let bailedOut = callback( this );
if( !bailedOut ) {
const children = this.children;
for ( let i = 0, l = children.length; i < l && !bailedOut; i ++ ) {
bailedOut = children[ i ].traverse( callback );
}
}
return bailedOut;
}
And then call it like:
function doTheThing( node ) {
let bailOut = false;
if( node.userData ) {
bailOut = true;
}
else {
// do the thing
}
return bailOut;
}
scene.traverse( doTheThing );
The processing basically continues until your callback returns true, at which point it triggers a cascade back up the traverse recursion chain until it exits the original call.

ThreeJS: Is there any possible way to reduce the amount of triangles count

Hi I have some very complex models to display which comes from Revit files that my customer provides.
But sometimes the amount of details in the model is just way too much for the purpose of the website.
I would like to reduce the amount of vertexes/triangles in the model to simplify the display and enhance performance.
I have used simply modifiers where I cant able to view the model itself.
Is this possible from within ThreeJS? Or is there maybe an other solution for this?
const renderMeshFunction = async (material: any, geometry: any, id: any) => {
let count = 0;
const emptyFunction = () => { };
const onBR = () => {
const _count = (count < 3 ? count : Math.floor(Math.random() * count) + 1);
return ((renderer: WebGLRenderer, scene: Scene,
camera: Camera, geometry: Geometry | BufferGeometry,
_material: Material, group: Group) => {
_material.clippingPlanes = checkIsClipping();
});
};
count = geometry.maxInstancedCount;
const mesh = new Mesh(geometry, material);
let modifier = new SimplifyModifier();
let simplified = mesh.clone();
simplified.material = simplified.material.clone();
let countSimple = Math.floor((simplified.geometry as any).attributes.position.count * 1); // number of vertices to remove
simplified.geometry = modifier.modify(simplified.geometry, countSimple);
that.scene.add(simplified);
mesh.name = id || Date.now().toString();
mesh.matrixAutoUpdate = false;
mesh.drawMode = 0;
mesh.onBeforeRender = onBR.apply(that);
mesh.onAfterRender = emptyFn;
mesh.geometry.dispose();
mesh.material.dispose();
geometry.dispose();
material.dispose();
material = undefined;
return mesh;
};

Update OBJ file after cut some vertix

I'm working on a project in the medical field using threejs, and OBJ files shin bone, my goal is that I get to cut part of the tibia on the basis of a given position, for now I can do that by replacing the vertix of the object, but my problem is that I can not directly update the object, so every time I delete the old object and I upload it, although i put verticeNeedUpdate …
My question is if it's possible to directly update the object?
loader.load('img/cube.obj',function(object){
var material = new THREE.MeshLambertMaterial({color:0xA0A0A0});
slice_onZ.onChange(function(z){
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = material;
geometry = new THREE.Geometry().fromBufferGeometry(child.geometry);
for(var i=0 ; i< geometry.vertices.length ; i++) {
if (geometry.vertices[ i ].x != 0) {
geometry.vertices[ i ].x = z;
}
if (geometry.vertices[ i ].y != 0) {
geometry.vertices[ i ].y = z;
}
if (geometry.vertices[ i ].z != 0) {
geometry.vertices[ i ].z = z;
}
}
geometry.verticesNeedUpdate = true;
mesh = new THREE.Mesh( geometry, material );
console.log(mesh.geometry.attributes);
scene.add(mesh);
}
});
});
},onProgress,onError);
}
there is my code, i try to update geometry but it's doesnt work, i have to reload everytime the object and delete the old one for display the new on.

Will three.js Object3D.clone() create a deep copy of the geometry?

I'm loading models using the collada loader. The loader returns an Object3D, "dae", with many child meshes. I'd like to instantiate the parent "dae" object many times without duplicating the meshes. Can I just use dae.clone()?
Put another way: I'd like to make shallow copies that all have their own transformation matrix but share the same geometry. What's the most efficient way to do this?
By default Object3D.clone() does create a deep copy. Let's take a look at the source
clone: function ( object, recursive ) {
if ( object === undefined ) object = new THREE.Object3D();
if ( recursive === undefined ) recursive = true;
object.name = this.name;
object.up.copy( this.up );
object.position.copy( this.position );
object.quaternion.copy( this.quaternion );
object.scale.copy( this.scale );
object.renderDepth = this.renderDepth;
object.rotationAutoUpdate = this.rotationAutoUpdate;
object.matrix.copy( this.matrix );
object.matrixWorld.copy( this.matrixWorld );
object.matrixAutoUpdate = this.matrixAutoUpdate;
object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
object.visible = this.visible;
object.castShadow = this.castShadow;
object.receiveShadow = this.receiveShadow;
object.frustumCulled = this.frustumCulled;
object.userData = JSON.parse( JSON.stringify( this.userData ) );
if ( recursive === true ) {
for ( var i = 0; i < this.children.length; i ++ ) {
var child = this.children[ i ];
object.add( child.clone() );
}
}
return object;
}
As we can see the clone function accepts two optional arguments:
the object to clone the Object3D into.
a flag to indicate whether or not to recursively clone the children.
So yes it is possible to make shallow copies with respect to the Object3D.children, but that's not what you want (based on your comment).
I believe you can actually use the default behavior of Object3D.clone() to get what you are after. Mesh.clone() does not clone the Geometry and Material properties.
THREE.Mesh.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
Here is a couple of function I use to clone object's materials deeply. You could modify this to your needs
Also consider this information : https://github.com/mrdoob/three.js/issues/5754
/** Gives the aptitude for an object3D to clone recursively with its material cloned (normal clone does not clone material)*/
THREE.Object3D.prototype.GdeepCloneMaterials = function() {
var object = this.clone( new THREE.Object3D(), false );
for ( var i = 0; i < this.children.length; i++ ) {
var child = this.children[ i ];
if ( child.GdeepCloneMaterials ) {
object.add( child.GdeepCloneMaterials() );
} else {
object.add( child.clone() );
}
}
return object;
};
THREE.Mesh.prototype.GdeepCloneMaterials = function( object, recursive ) {
if ( object === undefined ) {
object = new THREE.Mesh( this.geometry, this.material.clone() );
}
THREE.Object3D.prototype.GdeepCloneMaterials.call( this, object, recursive );
return object;
};
You can refer to the copyand clone methods in Object3D to deep clone mesh materials.
First,extends two new methods in THREE:
THREE.Object3D.prototype.deepClone = function ( recursive ) {
return new this.constructor().deepCopy( this, recursive );
},
THREE.Object3D.prototype.deepCopy = function( source, recursive ) {
if ( recursive === undefined ) recursive = true;
this.name = source.name;
this.up.copy( source.up );
this.position.copy( source.position );
this.quaternion.copy( source.quaternion );
this.scale.copy( source.scale );
this.matrix.copy( source.matrix );
this.matrixWorld.copy( source.matrixWorld );
if(source.material){
//changed
this.material = source.material.clone()
}
if(source.geometry){
//changed
this.geometry = source.geometry.clone()
}
this.matrixAutoUpdate = source.matrixAutoUpdate;
this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
this.layers.mask = source.layers.mask;
this.visible = source.visible;
this.castShadow = source.castShadow;
this.receiveShadow = source.receiveShadow;
this.frustumCulled = source.frustumCulled;
this.renderOrder = source.renderOrder;
this.userData = JSON.parse( JSON.stringify( source.userData ) );
if ( recursive === true ) {
for ( var i = 0; i < source.children.length; i ++ ) {
var child = source.children[ i ];
this.add( child.deepClone() ); //changed
}
}
return this;
}
Second,when you want to deep clone a Object3D or Scene named originalObj.just do var newObj = originalObj.deepClone()
three.js Object3D.clone() will not create a deep copy of geometry and material. It will reference geometry and material of the cloned object instead.
You can see this in three.module.js:
Mesh.prototype = ... {
...,
copy: function ( source ) {
Object3D.prototype.copy.call( this, source );
...
>>>>>>> this.material = source.material; <<<<<<<
>>>>>>> this.geometry = source.geometry; <<<<<<<
return this;
}
...
}
So, regarding the OP's question: Yes, you can just use dae.clone().
For a deep clone including geometry and material, just change two lines of the above Mesh.prototype.copy() function:
this.material = source.material.clone();
this.geometry = source.geometry.clone();
Keep in mind this is a performance issue because three.js has to calculate separate geometries for each object3D, including all Meshes.

How to Hide/Show Objects using Three.js release 54?

I've asked this question as a part of Huge question but was recommended to ask in parts. Here comes the part of my previous question. My previous question was: Here
I've been using Three.js Release 50 and able to show/hide the objects (In my application, it is a mesh child) with the help of:
THREE.SceneUtils.traverseHierarchy(mesh,function(child){
var z = document.getElementById("cameras").selectedIndex*5 -10;
if (z === -10){
child.visible = true;
} else if (child.position.z !== z){
child.visible = false;
} else {
child.visible = true;
};
});
But while using release 54, it is said that to use, object.traverse but found very difficult to the same. How to replace the above code using release 54? The error I'm getting while using release 54 is:
Please help me to sort this out.
Is the 'mesh' variable you are sending the 'traverseHierarchy' function an Object3d?
If so have you tried 'mesh.children' which should return an array of child objects, or you could use the traverse function on the mesh object.
See: http://mrdoob.github.com/three.js/docs/54/#Reference/Core/Object3D
mesh.traverse(function(child) {
var z = document.getElementById("cameras").selectedIndex * 5 - 10;
if (z === -10) {
child.visible = true;
} else if (child.position.z !== z) {
child.visible = false;
} else {
child.visible = true;
};
});
Dig deeper, answers you will find.
object.traverseHierarchy() was renamed to object.traverse()
$ grep -A10 'traverse: function' build/three.js
traverse: function ( callback ) {
callback( this );
for ( var i = 0, l = this.children.length; i < l; i ++ ) {
this.children[ i ].traverse( callback );
}
},
simply use the object traverse method to hide the mesh in three.js.In my code hide the object based on its name
object.traverse ( function (child) {
if (child instanceof THREE.Mesh) {
if (child.name.includes("3F")) {
child.visible = true;
} else {
child.visible = false;
}
}
});

Resources