TransformControls not updating position when multiple ObjLoader2 objects are loaded

Scenario:

  • Multiple obj file meshes loaded with ObjLoader2 are present in the scene

Issue:

  • After moving objects with transformControls, their position is not getting updated

Minimal Reproducible Code:

  • Unzip the file minRepro.zip (3.3 KB) inside three.js/examples directory

  • Open demo_objload.html in chrome browser.

  • Click on any of the objects and move around using the gizmo. In console logs, notice that the object positions are not getting updated.

  • Pasting the code here as well for a quick look:

      // transform controls
      const transformControls = new TransformControls(camera, canvas);
      transformControls.addEventListener("dragging-changed", function (event) {
          orbitControl.enabled = !event.value;
      });
      scene.add(transformControls);
    
      // objects
      let objects = [];
      function callbackOnLoad(obj) {
          const p = [0.5 * Math.random(), 0.5 * Math.random(), 0.5 * Math.random()];
          obj.position.set(p[0], p[1], p[2]);
          scene.add(obj);
          objects.push(obj);
      }
      let files = ["4.obj", "5.obj"]
      for (var i=0; i<files.length; i++) {
          const objLoader = new OBJLoader2();
          objLoader.load(files[i], callbackOnLoad, null, null, null);
      }
    
      document.addEventListener("click", function(event) {
          let mouse = new THREE.Vector2(
              (event.clientX / window.innerWidth) * 2 - 1,
              -(event.clientY / window.innerHeight) * 2 + 1
          );
          let raycaster = new THREE.Raycaster();
          raycaster.setFromCamera(mouse, camera);
          let intersections = raycaster.intersectObjects(objects, true);
          if (intersections.length > 0) {
              let object = intersections[0].object;
              transformControls.attach(object);
          } 
      });
    
      function render() {
          if (resizeRendererToDisplaySize(renderer)) {
              const canvas = renderer.domElement;
              camera.aspect = canvas.clientWidth / canvas.clientHeight;
              camera.updateProjectionMatrix();
          }
    
          for (var i=0; i<objects.length; i++) {
              let p = objects[i].position;
              console.log(`-- ${p.x} ${p.y} ${p.z}`);
          }
          console.log("======");
          renderer.render(scene, camera);
          requestAnimationFrame(render);
      }
      requestAnimationFrame(render);
    
      }
    

More details
I tried manually creating objects using THREE’s Mesh function (instead of loading via ObjLoader2) and it worked perfectly fine with TransformControls. Following is the code:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>TransformControl Issue</title>
	<style>
		html,
        body,
        #three_canvas {
			width: 100%;
            height: 100%;
            margin: 0px;
        }
        
    </style>
</head>

<body>
	<canvas id="three_canvas"></canvas>
	<script type="module">
		import * as THREE from "../build/three.module.js"
        import { OrbitControls } from './jsm/controls/OrbitControls.js';
        import { TransformControls } from './jsm/controls/TransformControls.js';

		function resizeRendererToDisplaySize(renderer) {
			const canvas = renderer.domElement;
			const width = canvas.clientWidth;
			const height = canvas.clientHeight;
			const needResize = canvas.width != width || canvas.height != height;
			if (needResize) {
				renderer.setSize(width, height, false);
			}
			return needResize;
		}

		function main() {
            // renderer
            const canvas = document.querySelector("#three_canvas")
            const renderer = new THREE.WebGLRenderer({
                canvas
            });

            // camera
            const camera = new THREE.PerspectiveCamera(75, 2, 0.1, 100);
            camera.position.set(0, 3, 3);

            // control
            const orbitControl = new OrbitControls(camera, canvas);
            orbitControl.target.set(0, 0, 0);
            orbitControl.update();

            // scene
            const scene = new THREE.Scene();
            scene.add(new THREE.GridHelper(5, 50));

            // transform controls
            const transformControls = new TransformControls(camera, canvas);
            transformControls.addEventListener("dragging-changed", function (event) {
                orbitControl.enabled = !event.value;
            });
            scene.add(transformControls);

            // lights
            {
                const light = new THREE.HemisphereLight();
                scene.add(light);
            } {
                const light = new THREE.DirectionalLight();
                light.position.set(0, 10, 0);
                light.target.position.set(0, 0, 0);
                scene.add(light);
                scene.add(light.target);
            }

            // objects
            let objects = [];
            // - cube 1
            const cubeGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
            const redMaterial = new THREE.MeshBasicMaterial({color: 0xf56342});
            const cube1 = new THREE.Mesh(cubeGeometry, redMaterial);
            cube1.position.x = 2;
            scene.add(cube1);
            objects.push(cube1);
            // - cube 2
            const greenMaterial = new THREE.MeshBasicMaterial({color: 0x025711});
            const cube2 = new THREE.Mesh(cubeGeometry, greenMaterial);
            cube2.position.y = 2;
            scene.add(cube2);
            objects.push(cube2);
            transformControls.attach(cube2);

            document.addEventListener("click", function(event) {
                let mouse = new THREE.Vector2(
                    (event.clientX / window.innerWidth) * 2 - 1,
                    -(event.clientY / window.innerHeight) * 2 + 1
                );
                let raycaster = new THREE.Raycaster();
                raycaster.setFromCamera(mouse, camera);
                let intersections = raycaster.intersectObjects(objects, true);
                if (intersections.length > 0) {
                    let object = intersections[0].object;
                    transformControls.attach(object);
                } 
            });

            function render() {
                if (resizeRendererToDisplaySize(renderer)) {
                    const canvas = renderer.domElement;
                    camera.aspect = canvas.clientWidth / canvas.clientHeight;
                    camera.updateProjectionMatrix();
                }

                for (var i=0; i<objects.length; i++) {
                    let p = objects[i].position;
                    console.log(`-- ${p.x} ${p.y} ${p.z}`);
                }
                console.log("======");
                renderer.render(scene, camera);
                requestAnimationFrame(render);
            }
            requestAnimationFrame(render);

		}
		main();
	</script>

</body>

</html>

I appreciate any help. Thanks.

1 Like

The objects array had wrong references. Fixed by following change:

                for (var i=0; i<objects.length; i++) {
                let p = objects[i].children[0].position; // xxx: hacky but works
                console.log(`-- ${p.x} ${p.y} ${p.z}`);
            }
2 Likes

It’s not possible to run your file without additional setup and it’s Friday and I’m incredibly lazy, so long story short - if your object position updates on the scene, but not in the console, it means the position updates correctly, but you’re reading it incorrectly.

If your models contain submeshes (or the main mesh is within a wrapper group), it’s likely you’re transforming the position of the submesh - in which case the position of the main mesh does remain unchanged (submesh is transformed within the local-space, not the world-space.)

Make sure you’re transforming the correct object in the correct space.

2 Likes