70

enter image description here

two objects in the scene. the cube rotate axis should be the cube's center,that's my expect.

but the shoe model's rotate axis is the world's y axis.

my original code is.

cube.rotation.y += 0.01;
shoe.rotation.y += 0.01;

I found a solution in stackoverflow,like this:

cube.rotation.y += 0.01;
var pivot = new THREE.Object3D();
pivot.add(shoe);
pivot.rotation.y += 0.01;

But it doesn't work. And then, I change the shoe's position.

cube.rotation.y += 0.01;
var pivot = new THREE.Object3D();
shoe.position.set(-5,0,0);
pivot.add(shoe);
pivot.rotation.y += 0.01;

The result is better now, but it still not perfect. And since there are a lot of shoe's model, I can't determine different position for every shoe model.

Jinceon
  • 1,292
  • 2
  • 13
  • 21

5 Answers5

69

If your mesh is not rotating around its center, it is because the geometry vertices are offset from the origin.

You can automate repositioning by using a bounding box to define a reasonable center, and then offset the mesh's position like so:

var box = new THREE.Box3().setFromObject( mesh );
box.getCenter( mesh.position ); // this re-sets the mesh position
mesh.position.multiplyScalar( - 1 );

Then add the mesh to a pivot object:

var pivot = new THREE.Group();
scene.add( pivot );
pivot.add( mesh );

In your animation loop, rotate the pivot;

pivot.rotation.y += 0.01;

EDIT: A different solution is to translate the geometry vertices so the geometry is centered around, or near, the origin:

geometry.translate( distX, distY, distZ );

Or, alternatively, you could just call:

geometry.center();

which centers the vertices of the geometry for you based on the geometry's bounding box.

three.js r153

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • 2
    Quick question if i may ,how do we get the `geometry` of a custom glTF model, once the model is loaded all we do is access it via glTF.scene and add it to our scene. Not sure where to get the modal's geometry to center it :( . I am sure we can traverse the object and get the geometry on each node , but i am sure there should be a much more quick solution – semuzaboi Dec 19 '18 at 13:40
  • @semuzaboi Use the first approach, but instead of `mesh`, use `glTF.scene`. – WestLangley Dec 19 '18 at 13:51
  • 1
    @WestLangley the position is already set to the center of the bounding box in `box.center(mesh.position)`. What effect does multiplying -1 to the positive have? – Ajay Jan 04 '20 at 22:09
  • 1
    @Ajay If the bounding box is centered at ( 100, 0, 0 ), then we compensate by setting the mesh position to ( - 100, 0, 0 ). – WestLangley Jan 04 '20 at 22:20
  • @WestLangley aren't we trying to center the geometry such that the new center be (0,0,0)? To achieve that we should translate by (-100,0,0) using `mesh.position.add(new Vector3(-100,0,0))` – Ajay Jan 04 '20 at 22:27
  • 1
    Is it possible to achieve this without moving translating the objects to the origin? – Snippy Valson Oct 02 '20 at 20:43
29

Use THREE.Geometry.prototype.center as follows:

myGeometry.center();

This is like using myGeometery.translate(x,y,z) with automatic centering (x,y,z).

Samuel Danielson
  • 5,231
  • 3
  • 35
  • 37
  • 1
    This answer is most succinct. Here's the link to THREE.js source code. https://github.com/mrdoob/three.js/blob/51b2c730f13f2b234fc9cc2e328c065ad8d6bed4/src/core/Geometry.js#L325 – Ajay Jan 04 '20 at 22:33
12

The pivot solution was not working for me.

With OBJLoader.js (for loading .obj objects), you need to get the boundingBox of the object, get its center, multiply scalar by -1, and use this to translate the geometry of EVERY child's geometry. Then, you need to reUpdate the boundingBox if you want to use it for a purpose.

Here is a function that loads an obj and "normalize" its geometry vertexes, given the directory and name of the OBJ and MTL (texture) files (for example, if the OBJ and MTL files are dir1/myObject.obj and dir1/myObject.mtl, then you call loadObj('dir1','myObject')).

function loadObj(dir, objName) {
  var onProgress = function (xhr) {
    if (xhr.lengthComputable) {
      var percentComplete = (xhr.loaded / xhr.total) * 100;
      console.log(Math.round(percentComplete, 2) + "% downloaded");
    }
  };

  var onError = function (xhr) {};

  // Manager
  var manager = new THREE.LoadingManager();
  manager.onProgress = function (item, loaded, total) {
    console.log(
      "Started loading file: " +
        item +
        ".\nLoaded " +
        loaded +
        " of " +
        total +
        " files."
    );
  };

  var mtlLoader = new THREE.MTLLoader();
  mtlLoader.setPath(dir);
  mtlLoader.load(objName + ".mtl", function (materials) {
    materials.preload();

    // Model
    var loader = new THREE.OBJLoader(manager);
    loader.setMaterials(materials);
    loader.setPath(dir);
    loader.load(
      objName + ".obj",
      function (object) {
        var objBbox = new THREE.Box3().setFromObject(object);

        // Geometry vertices centering to world axis
        var bboxCenter = objBbox.getCenter().clone();
        bboxCenter.multiplyScalar(-1);

        object.traverse(function (child) {
          if (child instanceof THREE.Mesh) {
            child.geometry.translate(bboxCenter.x, bboxCenter.y, bboxCenter.z);
          }
        });

        objBbox.setFromObject(object); // Update the bounding box

        scene.add(object);
      },
      onProgress,
      onError
    );
  });
}

Enijar
  • 6,387
  • 9
  • 44
  • 73
Manuel Dipre
  • 463
  • 5
  • 9
3

If there are multiple meshes (which is most common), set the position of the parent node in the scene graph to achieve the goal of centering the model. If the scene graph is like this gltfObject -> childObject ->.. -> array of meshes

const bbox = new THREE.Box3().setFromObject( gltfObject );
const offset = new THREE.Vector3();
bbox.getCenter(offset).negate();
childObject.position.set(offset.x, offset.y, offset.z);
Ajay
  • 360
  • 1
  • 11
2

Heres how i got things to work on r86

// Store original position
let box = new THREE.Box3().setFromObject(this.mesh);
let offset = box.getCenter();

// Center geometry faces
this.geometry.center();

// Add to pivot group
this.group = new THREE.Object3D();
this.group.add(this.mesh);

// Offset pivot group by original position
this.group.position.set(offset.x, offset.y, offset.z);
Dustin Silk
  • 4,320
  • 5
  • 32
  • 48