How to move each of the many glb models holistically?


I need to move each model in glb format individually.
I tried passing an array of models to dragControls, this resulted in each child being able to move.
I tried setting to change the transformGroup property, this produced the desired result, but only for one model.
I was able to achieve the desired result by creating a dragController instance for each model. Something tells me that this is a very wrong approach.
In my code I tried to tie the coordinates of a model to the coordinates of a specific child (purple zone, material name: Zs) of that model. In dragControls I pass a list of children with this name. But when moving the model itself moves much slower than the child with material name Zs.
How to achieve holistic movement of each model individually?
I apologize for my bad English, I used a translator. Thank you very much for your attention.

  // Scene:
const scene = new THREE.Scene()
      scene.background = new THREE.Color(0xDAEDF5);

const safetyZonesIndexList = {},
      sceneModelsList = [],
      safetyZonesList = [];


// Light:
const pLight = new THREE.PointLight(0xffffff, 1)
      pLight.position.set(10, 10, 10)
                  
const dLight = new THREE.DirectionalLight(0xffffff, 5);
      dLight.position.set(12, 15, 12)

scene.add(pLight)
scene.add(dLight)


// Camera:
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
      camera.position.z = 3


// Renderer:
const renderer = new THREE.WebGLRenderer()
      renderer.setSize(window.innerWidth, window.innerHeight)

document.body.appendChild(renderer.domElement)


// Orbit Controls:
const orbitControls = new OrbitControls(camera, document.body);
      orbitControls.enableDamping = true; 
      orbitControls.dampingFactor = 0.05;
      orbitControls.screenSpacePanning = false;
      orbitControls.minDistance = 2;
      orbitControls.maxDistance = 300;
      orbitControls.maxPolarAngle = Math.PI / 2;


// Drag Controls:
const dragControls = new DragControls(safetyZonesList, camera, renderer.domElement)
      dragControls.addEventListener('dragstart', () => orbitControls.enabled = false)
      dragControls.addEventListener('dragend', () => orbitControls.enabled = true)

      dragControls.addEventListener('drag', e => {
        const m = e.object;
              m.position.y = 0;

        sceneModelsList[safetyZonesIndexList[m.uuid]].position.copy(m.position)
      })


// GLTF Loader:
const dracoLoader = new DRACOLoader();
const gltfLoader = new GLTFLoader();
      gltfLoader.setDRACOLoader(dracoLoader);

function loadModel(path, position = {x: 0, y: 0, z: 0}) {
    gltfLoader.load(path, (gltf) => {
        const {x, y, z} = position
        const m = gltf.scene;
              m.position.set(x, y, z)
              m.rotation.set(0, 0, 0);
                    
        sceneModelsList.push(m)

        m.traverse((child) => {
            if ( child.isMesh ) {
                if (child.material.name === 'Zs') {
                    safetyZonesIndexList[child.uuid] = sceneModelsList.length - 1
                    safetyZonesList.push(child)
                }

                child.geometry.computeVertexNormals();
                child.castShadow = true;
                child.receiveShadow = true;
            }
        });
                    
        scene.add(m);
    });
} 
loadModel('./models_with_zs/test_model.glb')
loadModel('./models_with_zs/test_model.glb', {x: 10, y: 0, z: 5})


// Plane:
const planeGeometry = new THREE.PlaneGeometry(30, 30)
const texture = new THREE.TextureLoader().load('./textures/test_texture_2.webp')
      texture.repeat.set(10, 10)
      texture.wrapS = THREE.RepeatWrapping;
      texture.wrapT = THREE.RepeatWrapping;

const plane = new THREE.Mesh(planeGeometry, new THREE.MeshPhongMaterial({ map: texture }))
      plane.rotateX(-Math.PI / 2)
      plane.receiveShadow = true

scene.add(plane)


function render() {
    renderer.render(scene, camera)
}

function animate() {
    requestAnimationFrame(animate)
    orbitControls.update();
    render()
}
animate()

For transformControls, usually you have to parent all the desired objects to a common root object and then use one transformcontrols to move them, then after the operation is done, re-attach them to their original parents.

The .add and .remove operations on object3d are the simple versions of changing the hierarchy.

A more advanced version is .attach

.attach is what you want to use in this scenario I think.

.attach makes sure that the object maintains its visual state even when changing parents (if that is possible)

So… you would figure out which objects are going to be moved “holistically” and .attach them to a single object… maybe like… “let dragRoot = new THREE.Object3D()”
then:

obj1.saveParent = obj1.parent;
dragRoot.attach( obj1 ) 
obj2.saveParent = obj2.parent;
dragRoot.attach( obj2 ) 
obj3.saveParent = obj3.parent;
dragRoot.attach( obj3 ) 

let transformControls  = new TransformControls(camera,renderer.domElement);

transformControls.attach(dragRoot);
transformControls.addEventListener('mouseUp',()=>{
obj1.attach(obj1.saveParent);
obj2.attach(obj2.saveParent);
obj3.attach(obj3.saveParent);
})

Thank you. TransformControls is well suited for this task.

1 Like