1

I need to make a node in a node hierarchy face the camera, or in other words, be a billboard. For every node, I store its world matrix, and world rotation as a quaternion. By what I know of quaternions, the operation I want is to get the difference between the camera quaternion and the rotation quaternion of a node, and simply rotate the node by said difference.

My camera is stored as euler angles, so I construct the camera quaternion by first creating the X-axis rotation, then the Z-axis rotation (I don't have Y-axis rotations), and multiplying them.

From there it's just some math to get the difference and finally create a rotation matrix from it, and multiplying the node's world matrix with it.

This, however, ends up resulting in the nodes rotating like crazy goons, and in no way facing the camera.

Here's the relevant JavaScript code:

// If there is a parent, multiply both world matrix and rotation by it
// localMatrix here is the current local matrix of the node
// rotation here is the local rotation quaternion
if (node.parent) {
    math.Mat4.multMat(node.parent.worldMatrix, localMatrix, node.worldMatrix);
    math.Quaternion.concat(node.parent.worldRotation, rotation, node.worldRotation);
} else {
    node.worldMatrix = localMatrix;
    node.worldRotation = rotation.copy();
}

// And here the mess begins
if (node.billboarded) {
    var cameraX = [];
    var cameraZ = [];
    var camera = [];

    // transform.rotation is my camera's rotation stored as 3 euler angles,
    // but I am not using the Y-axis for now
    math.Quaternion.setFromAxisAngle([1, 0, 0], transform.rotation[0], cameraX);
    math.Quaternion.setFromAxisAngle([0, 0, 1], transform.rotation[2], cameraZ);
    math.Quaternion.concat(cameraX, cameraZ, camera);

    // The current rotation on the node
    var initial = node.worldRotation.copy();

    // Need to reverse it, since the difference is camera * -initial
    math.Quaternion.conjugate(initial, initial);

    var difference = [];

    math.Quaternion.concat(camera, initial, difference);

    var rotationMatrix = [];

    math.Quaternion.toRotationMatrix4(difference, rotationMatrix);

    var finalMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];

    // pivot is the node's position, need to keep the rotation in local space
    math.Mat4.translate(finalMatrix, pivot[0], pivot[1], pivot[2]);
    math.Mat4.multMat(finalMatrix, rotationMatrix, finalMatrix);
    math.Mat4.translate(finalMatrix, -pivot[0], -pivot[1], -pivot[2]);

    // And finally actually rotate the node
    math.Mat4.multMat(node.worldMatrix, finalMatrix, node.worldMatrix);
}

Is there some obvious mistake I am making?

user123
  • 8,970
  • 2
  • 31
  • 52
user2503048
  • 1,021
  • 1
  • 10
  • 22

1 Answers1

0

Your assumption is wrong:

the operation I want is to get the difference between the camera quaternion and the rotation quaternion of a node, and simply rotate the node by said difference.

If you did this correctly, it should result in your nodes facing the same direction that the camera faces. This is quite different from actually facing the camera.

What you need is to find the transformation that faces the camera's position in world space. You might benefit from some quick research into gluLookAt().

JCooper
  • 6,395
  • 1
  • 25
  • 31
  • I can't believe I somehow overlooked this. About look-at matrices, I can't use them here, because the nodes are affected by the rest of the node hierarchy, I can't just replace their transformation with a look-at matrix, and otherwise they wont work, because they work based on the fact that you have some fixed axis-aligned orientation. – user2503048 Jun 24 '13 at 16:10