Three.js: Object3D clone userData exception

Created on 5 Apr 2017  路  8Comments  路  Source: mrdoob/three.js

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?

Three.js version
  • [x] r84

    Browser
  • [x] Chrome

  • [x] Firefox

    OS
  • [x] Windows

Most helpful comment

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 );

All 8 comments

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.

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 );
Was this page helpful?
0 / 5 - 0 ratings