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.
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.
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.