6

I am using SplineCurve3 to plot a line only on the X and Y axis, I have a cube successfully animating along this line by using spline.getPoint(t) where t is 0-1 in time. I am trying to orient the cube to the line via its up vector which is Y using the dot product.

It is almost aligned but ever so slightly out. I thought that I would use the dot product of the Y vector and the tangent of the current point as the angle to rotate a quaternion to.

Here is my render function:

function render() {

    var updateMatrix = new THREE.Matrix4(); 
    updateMatrix.setPosition(spline.getPoint(t));

    var angle = new THREE.Vector3(0,1,0).dot(spline.getTangent(t).normalize());

    var quat = new THREE.Quaternion;
    quat.setFromAxisAngle(new THREE.Vector3(0,0,1), angle);
    updateMatrix.setRotationFromQuaternion(quat);

    marker.rotation.getRotationFromMatrix(updateMatrix);
    marker.matrixWorld = updateMatrix;

    t = (t >= 1) ? 0 : t += 0.002;

    renderer.render(scene, camera); 
}

And here is a fiddle demonstrating my problem, can anyone tell me where I'm going wrong with the rotation aspect?

You can edit my - jsfiddle example

Neil
  • 7,861
  • 4
  • 53
  • 74
  • I know this is an old post but wanted to throw out a smaller version of this that others might find useful: `code obj.position.copy(spline.getPoint( t )); obj.lookAt(spline.getTangent( t ).add(obj.position)); ` – manthrax Sep 08 '16 at 07:45

1 Answers1

17

As a rule of thumb, it is best not to mess with the object.matrix directly, and instead just set the object position, rotation, and scale. Let the library handle the matrix manipulations. You need to have matrixAutoUpdate = true;.

To handle the rotation part, first get the tangent to the curve.

tangent = spline.getTangent( t ).normalize();

You want to rotate the object so that it's up-vector (local y-vector) points in the direction of the curve tangent vector. First calculate the axis to rotate around. The axis is a vector perpendicular to the plane defined by the up-vector and the tangent vector. You use the cross-product to get this vector.

axis.crossVectors( up, tangent ).normalize();

Note, in your case, since the spline lies in a plane perpendicular to the z-axis, the axis just computed will be parallel to the z-axis. However, it may point in the direction of the positive z-axis or the negative z-axis -- it depends on the direction of the tangent vector.

Now calculate the angle in radians between the up vector and the tangent vector. The dot-product of the up vector and the tangent vector give the cosine of the angle between them (since both vectors are of unit length). You then have to take the arc-cosine to get the angle itself.

radians = Math.acos( up.dot( tangent ) );

Now, extract the quaternion from the axis and angle.

marker.quaternion.setFromAxisAngle( axis, radians );

three.js r.144

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • edited answer with link to demo using r52, now uses setEulerFromRotationMatrix (r50) instead of getRotationFromMatrix (r49). – Neil Oct 25 '12 at 08:32
  • 1
    With the latest versions of threejs the axis.cross has to be changed to axis.crossVectors instead. (I've made the edit...) There may be other subtle changes here and there? – songololo Jan 14 '15 at 14:25
  • Great answer and thank you for including a working example! – Pål Thingbø Dec 30 '20 at 08:39