InstancedMesh.center()?

Hi,

What is the recommended way to center an instancedmesh?

I could not find a .center() method.

I am able to get the center vector as follows:

const center = new THREE.Vector3()
instancedMesh.computeBoundingBox()
instancedMesh.boundingBox.getCenter(center)

I am not sure how translate the instancedMesh by the negative value of this center vector…

Something like this?!?

instancedMesh.position.sub( center );

Alternatively, you can loop through all instances and move them individually.

1 Like

My main objective was to have the transform control at the centre of an instancedmesh.

This was the function I came up with

    function centredImesh(userData) {
        const n = userData.length
        const cImesh = new THREE.InstancedMesh(geometry, material, n)

        let { x, y, z } = userData[0]
        let xMax = x
        let xMin = x
        let yMax = y
        let yMin = y
        let zMax = z
        let zMin = z

        for (let i = 0; i < n; i++) {
            ({ x, y, z } = userData[i])
            xMax = Math.max(xMax, x)
            xMin = Math.min(xMin, x)
            yMax = Math.max(yMax, y)
            yMin = Math.min(yMin, y)
            zMax = Math.max(zMax, z)
            zMin = Math.min(zMin, z)
        }

        const xc = (xMax + xMin) / 2
        const yc = (yMax + yMin) / 2
        const zc = (zMax + zMin) / 2
        const obj3d = new THREE.Object3D()

        for (let i = 0; i < n; i++) {
            ({ x, y, z } = userData[i])
            obj3d.position.set(x - xc, y - yc, z - zc)
            obj3d.updateMatrix()
            cImesh.setMatrixAt(i, obj3d.matrix)
        }
        cImesh.position.set(xc, yc, zc)
        return cImesh
    }

It works, but I would be interested hear from anyone if there is room for performance improvement.

May be a jsfiddle may help a lot, The code snippet is missing a lot of information。And I’ve encounted similar issue with you, see Access instancedMesh's sub-instance origin and tranform with pivot Appreciate any help :grinning:

The performance issue I foresee with centredImesh is that 2 for loops are required: The first for-loop to determine the centrepoint and a 2nd for-loop to populated the instancedmesh (i.e. the centrepoint needs to be known ‘a priori’). I am not so familiar with the three.js api to do it with one for-loop where the instancedmesh is centred after being populated.

Below my full example

// threejs.org/examples/#misc_controls_transform
import * as THREE from 'three';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';

import './main.css'

let cameraPersp, cameraOrtho, currentCamera;
let scene, renderer, control, orbit;

init();
render();

function init() {

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    const aspect = window.innerWidth / window.innerHeight;

    cameraPersp = new THREE.PerspectiveCamera(50, aspect, 0.01, 30000);
    cameraOrtho = new THREE.OrthographicCamera(- 600 * aspect, 600 * aspect, 600, - 600, 0.01, 30000);
    currentCamera = cameraPersp;

    currentCamera.position.set(5, 2.5, 5);

    scene = new THREE.Scene();
    scene.add(new THREE.GridHelper(4, 4, 0x888888, 0x444444));

    const ambientLight = new THREE.AmbientLight(0xffffff);
    scene.add(ambientLight);

    const light = new THREE.DirectionalLight(0xffffff, 4);
    light.position.set(1, 1, 1);
    scene.add(light);

    orbit = new OrbitControls(currentCamera, renderer.domElement);
    orbit.update();
    orbit.addEventListener('change', render);

    control = new TransformControls(currentCamera, renderer.domElement);
    control.addEventListener('change', render);
    control.addEventListener('dragging-changed', function (event) {
        orbit.enabled = !event.value;
    });

    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshLambertMaterial();

    const iMesh = centredImesh([{ x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 2 }])
    scene.add(iMesh)
    control.attach(iMesh)
    scene.add(control);

    function centredImesh(userData) {
        const n = userData.length
        const cImesh = new THREE.InstancedMesh(geometry, material, n)

        let { x, y, z } = userData[0]
        let xMax = x
        let xMin = x
        let yMax = y
        let yMin = y
        let zMax = z
        let zMin = z

        for (let i = 0; i < n; i++) {
            ({ x, y, z } = userData[i])
            xMax = Math.max(xMax, x)
            xMin = Math.min(xMin, x)
            yMax = Math.max(yMax, y)
            yMin = Math.min(yMin, y)
            zMax = Math.max(zMax, z)
            zMin = Math.min(zMin, z)
        }

        const xc = (xMax + xMin) / 2
        const yc = (yMax + yMin) / 2
        const zc = (zMax + zMin) / 2
        const obj3d = new THREE.Object3D()

        for (let i = 0; i < n; i++) {
            ({ x, y, z } = userData[i])
            obj3d.position.set(x - xc, y - yc, z - zc)
            obj3d.updateMatrix()
            cImesh.setMatrixAt(i, obj3d.matrix)
        }
        cImesh.position.set(xc, yc, zc)
        cImesh.userData = userData
        return cImesh
    }

    window.addEventListener('resize', onWindowResize);

}

function onWindowResize() {
    const aspect = window.innerWidth / window.innerHeight;

    cameraPersp.aspect = aspect;
    cameraPersp.updateProjectionMatrix();

    cameraOrtho.left = cameraOrtho.bottom * aspect;
    cameraOrtho.right = cameraOrtho.top * aspect;
    cameraOrtho.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

    render();
}

function render() {
    renderer.render(scene, currentCamera);
}

Here is a solution:

 function centredImesh(userData) {
    const n = userData.length
    const cImesh = new THREE.InstancedMesh(geometry, material, n)
    let { x, y, z } = userData[0]
    const obj3d = new THREE.Object3D()
    for (let i = 0; i < n; i++) {
      ;({ x, y, z } = userData[i])
      obj3d.position.set(x, y, z)
      obj3d.updateMatrix()
      cImesh.setMatrixAt(i, obj3d.matrix)
      cImesh.instanceMatrix.needsUpdate = true
    }

    cImesh.computeBoundingBox()
    const box3 = cImesh.boundingBox

    const center = new THREE.Vector3()
    box3.getCenter(center)

    //move instances to the center
    for (let i = 0; i < 2; i++) {
      const matrix = new THREE.Matrix4()
      cImesh.getMatrixAt(i, matrix)
      const offset = center.clone().multiplyScalar(-1)
      const translationMatrix = new THREE.Matrix4().makeTranslation(offset.x, offset.y, offset.z)
      matrix.multiply(translationMatrix)
      cImesh.setMatrixAt(i, matrix)
      cImesh.instanceMatrix.needsUpdate = true
    }
    return cImesh
  }