question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

clone issue: SpotLight.target which is placed in scene graph is cloned in two roles

See original GitHub issue
Description 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:closed
  • Created 4 years ago
  • Comments:27 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
gkjohnsoncommented, Aug 27, 2019

@looeee

I expect that will fail when the model is a SkinnedMesh, and possibly in other unexpected places too.

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:

Object3D.copy = function ( source, recursive, cache = {} ) {

  // Save the newly cloned object to the cache so it can be reused
  // later if other objects reference it.
  cache[ source.UUID ] = this;

  // ...

  if ( recursive === true) {
    
    for ( var i = 0; i < source.children.length; i ++ ) {

      // use the already cloned object if it exists in the cache otherwise clone it, which
      // which will cause it to be added to the cache.
      var child = source.children[ i ];
      this.add( cache[ child.UUID ] || child.clone( recursive, cache ) );

    }

  }

}

DirectionaLight.copy = function ( source, recursive, cache = {} ) {

  cache[this.UUID] = this;

  // ...

  // If the target hasn't been created as a side effect of the subtree clone then create it
  // now and add it to the cache so it will be added into the correct parent later.
  if ( ! ( source.target.UUID in cache ) ) {

      cache[ source.target.UUID ] = source.target.UUID.clone( recursive, cache );

  }
  this.target = cache[ this.target.UUID ];

  // ...

}

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; to cache[ source.UUID ] = this;

2reactions
Mugen87commented, Aug 21, 2019

Can you please explain what you see as the problem with the library in this use case?

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.

  • The camera will be cloned but helper.camera still refers to the old camera.
  • The camera in the scene will be cloned and 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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Introduction to Computer Graphics, Section 5.1 -- Three.js Basics
To make it easy to duplicate parts of the structure of a scene graph, Object3D defines a clone() method. This method copies the...
Read more >
Clone issue with quick action (#9421) - GitLab.org
If it is set, the path is used for the target. Possible MVC breakdown. Clone w/ quick action. Show success message with link...
Read more >
Object3D#clone – three.js docs
This value allows the default rendering order of scene graph objects to be overridden although opaque and transparent objects remain sorted independently.
Read more >
Cloning - Wikipedia
Contents · 1 Etymology · 2 Natural cloning · 3 Molecular cloning · 4 Cell cloning. 4.1 Cloning unicellular organisms; 4.2 Cloning stem...
Read more >
Neural Information Processing Systems - NeurIPS 2022
Versatile Multi-stage Graph Neural Network for Circuit Representation ... Learning Neural Set Functions Under the Optimal Subset Oracle. In Poster Session 1.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found