clone issue: SpotLight.target which is placed in scene graph is cloned in two roles
See original GitHub issueDescription of the problem
Case: Make many similar lamps with geometry and attached SpotLight.
let scene = new THREE.Scene();
let lampMesh = new THREE.Mesh(fooPars);
let spotLight = new THREE.SpotLight(barPars);
spotLight.target.position.set(1,2,3);
lampMesh.add(spotLight, spotLight.target);
for (let i = 0; i < numLights; i++) {
let lightClone = lampMesh.clone();
lightClone.position.set(0,0,i);
scene.add(lightClone);
}
The cloning will copy the target independently in the roles of lampMesh.children[1]
and of lampMesh.target
. This will cause the lamps to interpret the target position as a global position, rather than a local one, and moving the targets in the scene will have no effect on the lights, because their actual target
properties are independent clones with no parent
. And the error is/was really hard to locate.
Proposed solution: I am not sure. The only one I can think of is adding an optional parameter to all clone methods, that contains a map (literal object) from original ID/UUID to already cloned objects, such that whenever an already cloned object is encountered, the existing clone will be reused in the new role. The map is passed down the recursion, and must also be passed to the copy
methods. This seems like a drastic solution which will require a substantial amount of work, but it is also a general solution which will solve similar problems that may exist elsewhere. I am not sure it is without unintended consequences, though.
User solution with current implementation: Don’t use clone
without knowing how it will behave. For the case above, consider either adding targets to scene after cloning, or building the copies without using clone
, e.g. by using instances of a class which has static geometry and material.
Please also include a live example if possible (maybe later). You can start from these templates:
Issue Analytics
- State:
- Created 4 years ago
- Comments:27 (9 by maintainers)
@looeee
It looks like that’s true – admittedly my use case is pretty simple since I primarily am concerned with rigid, non deforming geometry.
I do think it’s nice to have at least some kind of reasonable default for cloning objects without having to resort to external utilities, especially for building creation tools, such as the three.js editor and other cases like mine.
@donmccurdy I feel like the cache approach could work without having to add extra callbacks or finalize functions. DirectionalLight might overly simplify the problem but here’s a quick pass:
Maybe I’ve missed something but I think this would work? So in this case if the directional light target is part of the subtree being cloned then it will be added to the correct corresponding parent in the newly cloned subtree and if it is outside the subtree (or not parented to anything at all) then it will not be added to anything.
edit: updated
cache[ this.UUID ] = this;
tocache[ source.UUID ] = this;
It’s probably best to explain this issue with
CameraHelper
. As we know,CameraHelper.clone()
is currently broken. When fixing the helper via #16076, another problem in context of the clone operations was revealed. Similar to what @EliasHasle describes here.When a 3D object has a property that refers to another 3D object in the scene e.g.
helper.camera
, there are two issues that might occur.helper.camera
still refers to the old camera.helper.camera
which results in two new, independent camera objects.When I understand @EliasHasle issue correctly, the latter one happens here. From my point of view, both behaviors are wrong and unexpected for the user.