When calling Object3D.clone() I get an exception a.geometries is undefined at Object3D.toJSON method line 11028. In my Object3D instance I try to clone, I also store a reference to the Scene object in the userData property.
As far as I can see, the exception appears since Object3D.clone() tries to copy the userData object. When calling Scene.toJSON method it is tried to copy all descendants of the scene. In my case it crashes when it is tried to copy a GridHelperchild object of the scene.
This error also appears when calling Object3D.clone(false) to prevent a recursive behavior. In this case, I would expect that this kind of recursive userData JSON copy is switched off.
Maybe it would be useful to document that also userData is cloned recursively and that the stored userData objects must provide a custom toJSON method to influence this behavior. What do you think?
[x] r84
[x] Chrome
[x] Firefox
[x] Windows
To summarize, I think this this is the current behavior.
object.root = scene; // object.clone() works, but .root is not a property of the cloned object
object.userData.root = scene; // object.clone() throws error
Thank you @WestLangley. That sums it up pretty well (although in my case I do not store the scene object directly in userData).
Anyway, in my opinion it should be up to the user if and how the user's data is cloned. When I want to clone an Object3D instance then it should clone only that. Furthermore, the line this.userData = JSON.parse( JSON.stringify(source.userData)); in three.js Object3D.copy() function will lead to problems with userData having any circular references.
In my use case, I solved the issue providing a custom toJSON method in the userData object: this._object3d.userData.toJSON = () => {};`
Let's leave this open until @mrdoob decides what he wants to do about it -- if anything.
Circular references can be problematic.
Here is a related use case: http://stackoverflow.com/questions/26202064/how-to-select-a-root-object3d-using-raycaster/26205768#26205768
userData should not contain three.js objects. If you want to save a reference, you could save the scene.uuid instead.
userData should not contain three.js objects.
@mrdoob So, suppose a user is raycasting against a group with child meshes. The raycast intersects a child mesh, and the user wants to drag the root object, that is, the group.
The previous solution was to use this pattern:
child.userData.root = myGroup; // but `myGroup` is a three.js object
So you are suggesting this pattern, instead?
child.userData.rootUuid = myGroup.uuid;
...
var root = scene.getObjectByProperty( 'uuid', child.userData.rootUuid );
Hmm... Well, I think it's fine to put object references directly in userData as long as toJSON() is not used in the mix.
Actually, maybe the solution to this is to change this line:
this.userData = JSON.parse( JSON.stringify( source.userData ) );
To this:
this.userData = Object.assign( {}, source.userData );
Most helpful comment
Actually, maybe the solution to this is to change this line:
To this: