32

I think this picture best explains my issue:

hooray for optical illusions

First I translate the box along the red line. Next, I want the effect of rotation to be the blue line in a, but what's actually happening is more like the blue line in b. It feels like changing the rotation is always relative to the original object space, but the translation (despite happening first) is always relative to the parent, and doesn't really affect the geometry points in relation to the object space. I apologize if that's confusing; clearly I'm new at this.

The important part of the code which produces this effect is below. Please keep in mind that the orientation of the image is different than this code produces; the image is merely an example to show the effect clearly.

var objectContainer = new THREE.Object3D();

var tubeRadius = 100;
var tubeGeometry = new THREE.CylinderGeometry(tubeRadius, tubeRadius, tubeRadius * 3, 36, 1, false);
var tube = new THREE.Mesh(tubeGeomtry, material);
scene.add( tube );

var boxes = new THREE.Object3D();
var boxEdge = 50;
var boxGeometry = new THREE.CubeGeometry(boxEdge, boxEdge, boxEdge);
var box1 = new THREE.Mesh( boxGeometry, material );
box1.translateX(tubeRadius + boxEdge / 2 + 5);
box1.translateY(boxEdge / 2);
box1.rotation = new THREE.Vector3(0, 2*Math.PI/3*0, 0);
boxes.add(box1);
var box2 = box1.clone();
box2.rotation = new THREE.Vector3(0, 2*Math.PI/3*1, 0);
boxes.add(box2);
var box3 = box1.clone();
box3.rotation = new THREE.Vector3(0, 2*Math.PI/3*2, 0);
boxes.add(box3);
scene.add( boxes );

The only solution I can think of is to wrap each box in another object space and rotate about that, but it seems like excessive work. What is the preferred method to achieve the result I'm looking for?

Nick Larsen
  • 18,631
  • 6
  • 67
  • 96
  • I don’t know Three.js, but how about just translating it by `Math.cos(rotation) * boxEdge / 2, Math.sin(rotation) * boxEdge / 2` as well as the local rotation? – Ry- Mar 05 '13 at 02:13
  • That feels like double work, rotate and translate each box. Really what I want to do is rotate each clone about the same point, which is origin before the translation. – Nick Larsen Mar 05 '13 at 02:16

2 Answers2

34

There are several ways of doing what you want, but I think the easiest is like so:

// parent
parent = new THREE.Group();
scene.add( parent );

// pivots
var pivot1 = new THREE.Object3D();
var pivot2 = new THREE.Object3D();
var pivot3 = new THREE.Object3D();

pivot1.rotation.z = 0;
pivot2.rotation.z = 2 * Math.PI / 3;
pivot3.rotation.z = 4 * Math.PI / 3;

parent.add( pivot1 );
parent.add( pivot2 );
parent.add( pivot3 );

// mesh
var mesh1 = new THREE.Mesh( geometry, material );
var mesh2 = new THREE.Mesh( geometry, material );
var mesh3 = new THREE.Mesh( geometry, material );

mesh1.position.y = 5;
mesh2.position.y = 5;
mesh3.position.y = 5;

pivot1.add( mesh1 );
pivot2.add( mesh2 );
pivot3.add( mesh3 );

Then in your render loop:

parent.rotation.z += 0.01;

three.js r.147

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • Thanks for the example. As I mentioned in the question, this is what I was thinking. Is this a typical solution to the problem? I intend to develop this for a while and I just want to make sure I'm not shooting myself in the foot. – Nick Larsen Mar 05 '13 at 10:26
  • Yes. It is typical. The other solution is to have each box be a child of the scene, and compute the new `position` and `rotation` vector every frame using math. – WestLangley Mar 05 '13 at 14:39
  • If the fiddle is not working, `AxisHelper` should be `AxesHelper`. – Joe Moore Aug 24 '22 at 08:30
  • 1
    @meropis Thanks. Corrected outdated fiddle. – WestLangley Aug 24 '22 at 16:24
6

Creating a compound object whose centre will be the point about which the inner objects rotate is one obvious answer, and would be very quick to write. Just create an Object3D and add your box to it.

A similar approach is covered by this question. It shifts the point of the vertices for an object, so it effectively has a new centre.

Alternatively, you can mess around with the matrices by hand. Try this:

var boxGeometry = new THREE.CubeGeometry(boxEdge, boxEdge, boxEdge);
var mr = new THREE.Matrix4();
var mt = new THREE.Matrix4();
mt.setPosition(new THREE.Vector3(0,tubeRadius,0));
var box1 = new THREE.Mesh( boxGeometry, material );
box1.applyMatrix(mt);
var box2 = box1.clone();
mr.makeRotationZ(2 * Math.PI /3);
box2.applyMatrix(mr);
boxes.add(box2);
var box3 = box1.clone();
mr.makeRotationZ(4 * Math.PI /3);
box3.applyMatrix(mr);
boxes.add(box3);
boxes.add(box1);
scene.add( boxes );
Community
  • 1
  • 1
Jodes
  • 14,118
  • 26
  • 97
  • 156