2

Short Background

I'm working on a SceneManager which allows the creation of SceneNode objects. Each SceneNode contains information about position, scaling, and rotation. To make an object appear in the scene, it simply gets attached to a SceneNode and the transforms contained in the SceneNode are applied to it. For example, to get a camera to look at a sphere in the scene, we could write:

Camera cam           = sceneManager.createCamera("MainCamera", Camera.ProjectionType.PERSPECTIVE);
Entity sphere        = sceneManager.createEntity("Sphere", "sphere_mesh.obj");

SceneNode camNode    = sceneManager.createSceneNode(cam.getname() + "Node");
SceneNode sphereNode = sceneManager.createSceneNode(sphere.getname() + "Node");

// turns camera node towards the sphere node as expected
camNode.lookAt(sphereNode);

The simplified example above should give the general idea, and would produce the expected result, i.e. I have no problems getting things to appear on screen and making the camera look at them.

The Problem

I want to make a SceneNode containing an Entity point towards another SceneNode containing another Entity, not the Camera, but it ends up pointing in the opposite direction I intended it to look at.

For example, if we have our target node at position [0, 0, -5] and some other observer node at position [0, 0, 5], we want object the observer node's forward vector to be [0, 0, -1], but instead it ends up as [0, 0, 1], looking away from the target.

I discovered this bug recently after attaching a Light to a SceneNode and telling it to lookAt the cube's SceneNode to illuminate it, but it did not get lit.

This pic shows the problem, where the center cube should be lit, but isn't.

Forward looks back

The white sphere marks the position of the spot light, and it should be looking at the center cube, but it's not getting lit because it's actually looking in the opposite direction. In other words, I tried: lightNode.lookAt(cubeNode);

While debugging, I can see that the resulting rotation matrix looks like this:

 s  u  f  = side, up, forward; column-major matrix
[1  0  0]
[0  1  0]
[0  0  1] <-- wrong: looking down the +Z axis, towards viewer

Instead, it should look like this:

 s  u  f
[1  0  0]
[0  1  0]
[0  0 -1] <-- good: looking down the -Z axis, into screen

(Yes, the underlying rendering system is OpenGL-based, which means that "front" is actually down the -Z axis.)

If I modify the code above to look at the negative of the actual location, i.e. lightNode.lookAt(cubeNode.getRelativePosition().mult(-1));, then it looks at the cube and lights it up, as shown below.

Back looks forward

So, to make it look towards the "front", I had to tell it to look "back". I've been hunting this bug down for the last several days, even double-checking that the look-at matrix is being built correctly. Based on my references, it seems to be correct, but I've included it below anyway because it was a possible suspect at one point.

public static Matrix4 createLookAtMatrix(Vector4 eyePosition, Vector4 targetPosition, Vector4 upDirection) {
    Vector4 f = targetPosition.sub(eyePosition).normalize();
    Vector4 s = f.cross(upDirection).normalize();
    Vector4 u = s.cross(f).normalize();

    float[][] values = new float[][] {
        { s.x(),  s.y(),  s.z(), -s.dot(eyePosition) },
        { u.x(),  u.y(),  u.z(), -u.dot(eyePosition) },
        {-f.x(), -f.y(), -f.z(), -f.dot(eyePosition) },
        { 0f   ,  0f   ,  0f   ,  1f                 }
    };

    return createFrom(values);
}

I'm currently out of ideas and would appreciate the any help leading to the capture and execution of this bug.

I really don't know if it could be related to an older question I posted several months ago or not, which is still unresolved, but it's included here just in case it turns out to be relevant.

Community
  • 1
  • 1
code_dredd
  • 5,915
  • 1
  • 25
  • 53

1 Answers1

0

I assume you created the look_at function to work with the camera. That's why it doesn't work with other objects. The cameras z axis is always point away from what you are looking at which is why you added the minus signs to the third row of the matrix I assume.

One way to solve it would be to have a createLookAtMatrix and createCameraLookAtMatrix. The ladder like the one you have now and the other without the minus signs on the third row.

Another way to do it would be to always have the camera under a "camera control" node in the scene graph. You would have the cameras local transformation always rotated 180 degrees in the y axis and then set the look at matrix to the parent node instead.

In any case, this should work for objects other than the camera:

public static Matrix4 createLookAtMatrixForObjects(Vector4 eyePosition, Vector4 targetPosition, Vector4 upDirection) {
    Vector4 f = targetPosition.sub(eyePosition).normalize();
    Vector4 s = f.cross(upDirection).normalize();
    Vector4 u = s.cross(f).normalize();

    float[][] values = new float[][] {
        { s.x(),  s.y(),  s.z(), -s.dot(eyePosition) },
        { u.x(),  u.y(),  u.z(), -u.dot(eyePosition) },
        { f.x(),  f.y(),  f.z(), -f.dot(eyePosition) },
        { 0f   ,  0f   ,  0f   ,  1f                 }
    };

    return createFrom(values);
}
bofjas
  • 1,186
  • 7
  • 19
  • Yes, the `lookAt` function was created that way. Currently, `SceneNode`s don't need to know what kind of objects get attached to them, other than knowing they're of type `SceneObject`. But your suggestion would require adding special cases inside the `SceneNode` in case a `Camera` is attached. Is there a way to create a rotation matrix that allows one object to be pointed towards another object without having to use the `lookAt` implementation I included above? – code_dredd Feb 04 '17 at 11:52
  • Yes. That's what I meant by "without the minus signs on the third row". I've added the complete function that should work for non-camera objects just in case there is any doubt. – bofjas Feb 04 '17 at 14:28
  • What I meant was a look-at calculation that could be used for *both* camera and non-camera objects, without having to add special cases for handling them on a node? – code_dredd Feb 04 '17 at 19:37
  • While experimenting with the proposed solution, I can see the spot-light aims in the correct direction, but other rotations get inverted (e.g. the spotlight rotates counter-clockwise instead of clockwise). I hope this last one is not an initial indication of a (potentially) bigger problem later, but if it's not, then it's still less important than trying to avoid special cases as per my previous comment. Thanks again for the help. – code_dredd Feb 04 '17 at 20:57
  • Do you have any suggestions to avoid the special cases? As I understand it, a single look-at matrix function is supposed to work for both camera/non-camera cases. Is that understanding incorrect? – code_dredd Feb 08 '17 at 12:46