Set pivot point to center of object imported from Blender

I have scene with two Blender models imported into it. A brief experiment trying to rotate one of them shows that its rotation occurs around the world axes, not the object’s center point:

https://themoaa.org/temp/rotate/

I’ve read a bunch of posts on the subject, but I still don’t understand how to tell threejs to use the object’s center point as its pivot point (especially since many of the solutions seem to reference older versions of threejs). Could anyone direct me to the most up-to-date thinking on solving this issue please?

I’m also wondering if there’s anything I can in do Blender to better prepare the model for this kind of thing? The rotating object shown in the example above doesn’t sit at 0,0 in Blender, but it does have its pivot point set to its center, and rotates as desired in Blender.

(Note: If it makes any difference to your answers, in this particular instance the object I’m trying to rotate is the only object in the .glb file, but ideally I’d like to import a single model containing all objects and use well-named layer names to find and manipulate them. I plan on posting another question asking about the wisdom of that approach.)

2 Likes

Could you share source code used to load and rotate the object?


Threejs doesn’t have a concept of a “pivot point” like Blender does, but (both in Blender and threejs) nested objects can have exactly the same effect:

When you load a model from Blender, it is often not just an isolated mesh — it could have lots of things in it. The reference you get from GLTFLoader is a wrapper around all of that, gltf.scene, which is always centered at 0,0,0. If you know your model has only one mesh, and it’s centered somewhere else, then that parent object is going to effectively determine the “pivot”, because rotating it affects the child’s position, not just the child’s rotation.

If you know — like this scene — that you just have a simple mesh with a position, you could remove it from the parent and work with it separately:

var tape = gltf.scene.getObjectByName('Tape'); // or whatever mesh name is.
tape.rotation.y += 0.5;

If you’re not sure what the name is (note that . character’s in Blender object names get replaced in threejs) you can just traverse the model looking for a mesh:

var meshes = [];
gltf.scene.traverse((object) => {
  if ( object.isMesh ) meshes.push( object );
});
console.log(meshes);

Finally, and this isn’t the issue in your particular case, the mesh’s vertices can be recentered to its origin using mesh.geometry.center(). If the model didn’t have any parent or position and it still wasn’t centered at the origin, this would be the fix.

2 Likes

Related discussion at GitHub:

@Mugen87 looks like that request wont get anywhere fast…however for me personally I can see it being extremely useful. Is there anyway to vote/show interest in a feature request so you guys (by that I mean people who contribute to three) can see how many people would benefit from such a feature?

1 Like

I don’t think feature support for pivot points is likely to help with this question. It just becomes one more possible way to represent pivots, which users would then also have to check, in addition to everything else, when trying to figure out why their object doesn’t rotate around its own origin.

Ohh, that’s great, thank you so much, Don!

I thought that when I imported the tape.gld file I was just importing the tape mesh, and nothing else. I didn’t realize that the mesh was the child of a scene, and that I had to specifically find that child and manipulate it, rather than manipulate the entire scene that it sat inside. Poking around inside console.log(sceneTape); helped me understand that better.

I put a simplified working version here in case anyone finds this question later and wants to understand what I did:
https://themoaa.org/temp/rotate/index2.html

You can do a view source to see how that demo works. One thing that I did as an aide memoire was to use the variable name sceneTape so that it’s clear throughout the code that it’s referring to a whole scene, not just a mesh:

var sceneTape;

// load tape model
var loader = new THREE.GLTFLoader();
loader.load("tape.glb", function(gltf) {
  sceneTape = gltf.scene;
  sceneTape.scale.set(2,2,2);
  scene.add(sceneTape);
}, undefined, function(error) {
  console.error(error)
});

To spin the tape I used:

// spin the tape
function spinTape() {
  if (sceneTape) {
    //console.log(sceneTape);
    meshTape = sceneTape.getObjectByName('tape_-_left_-_reel');
    meshTape.rotation.y += 0.05;
  }
  updateRequest = window.requestAnimationFrame(spinTape);
}
spinTape();

(Note for other newbies: this was just a proof of concept, and some of the code was copied from elsewhere and might be out of date, so don’t take anything you see there as best practice.)

2 Likes

Hi,
to move pivot to center, you just need apply translate to geometry instead of translation matrix.
It will move all the pivots in middle of each mesh.
This is example of model view in threejs editor before:
Screenshot 2024-04-26 at 15.27.27

and after apply my code:
Screenshot 2024-04-26 at 15.27.39

You can do it using this code:

 object3D.traverse((child) => {
          const bbox = new Box3().setFromObject(child);
          const vec = new Vector3();
          bbox.getCenter(vec);
          if (child instanceof Mesh) {
            const material = child.material as MeshPhongMaterial;
            child.position.set(vec.x, vec.y, vec.z);
            if (child.geometry) {
              child.geometry.translate(-vec.x, -vec.y, -vec.z);
            }
            
          } else {
            child.position.set(vec.x, vec.y, vec.z);
            child.traverse((child) => {
              if (child instanceof Mesh) {
                child.geometry.translate(-vec.x, -vec.y, -vec.z);
              }
            });
            child.position.set(0, 0, 0);
          }
        });