Saturday, July 7, 2012

Gladius Rotation After Translation

‹prev | My Chain | next›

I continue to learn more about 3D coordinate systems than I ever expected as I muck with my 3D solar system example. Last night I figured out how to move after rotating an object (the camera in this case). Tonight, I hope to figure out how to do the opposite: rotate the camera after it has been moved.

Currently rotation is quite simple, mostly thanks to the gladius-input extension in Gladius. Whenever the event loop is checked, if the input controller's state involves panning, then I rotate about the x- or y-axis accordingly:
var rotation;
if (controller.states["PanUp"])
rotation = [space.clock.delta * 0.0005, 0, 0];
if (controller.states["PanDown"])
rotation = [-space.clock.delta * 0.0005, 0, 0];
if (controller.states["PanLeft"])
rotation = [0, space.clock.delta * 0.0005, 0];
if (controller.states["PanRight"])
rotation = [0, -space.clock.delta * 0.0005, 0];

if (rotation)
The transform at the end of that code snippet is the camera's current 3D transform. It is a list of 3 rotations about the x-, y-, and z-axis. Even though the list is angles expressed in radians, it is still a list of three dimensions and so I can perform vector3 (a 1×3 vector) arithmetic.

This works brilliantly when I start from above the Solar System:

If I fly down towards the Sun, then pan up to face the Earth and the green square "constellations", and move back for a better view, it all works:

This works because of the translation-after-panning work from last night. It took some relatively sophisticated matrix4 math to accomplish, but happily, Galdius includes some handy matrix4 helper functions. Better yet, it includes an example that makes use of it.

Anyhow, I can still pan up, down (n.b. this is incorrect) and back up after all of this:

But, if I try to pan left or right to see Mars, something goes wrong:

I have not panned to the left. Instead, I have rotated.

The problem here is that I expect to rotate around an axis that runs up and down in my current view. But, from above, when I pan left, I am rotating around the y-axis:
if (controller.states["PanLeft"])
rotation = [0, space.clock.delta * 0.0005, 0];
It seems that the usual way of doing this is fairly involved. It involves moving the origin of coordinate space to the current position, then rotating coordinate space to align with my current orientation, doing the normal rotation, and then undoing the coordinate space rotations and translation.

In my current naive state, all of that sounds a lot like creating a parent object for my camera entity, which effectively shifts the frame of reference to my camera. At this point, rotating should be no more than rotating around the y-axis.

So I try this for panning left:
if (controller.states["PanLeft"]) {
var frame = new engine.Entity( "camera-frame-of-reference", [transform]);
this.owner.setParent(frame);

transform = this.owner.findComponent("Transform");
rotation = [0, space.clock.delta * 0.0005, 0];

rotation = undefined;
this.owner.setParent(undefined);
space.remove(frame);
}
I create a new entity that contains only the same transform that currently describes my spaceship's position and orientation. After adding this transform entity to "space", I set it as the parent of my camera, which should recenter the frame of reference.

At this point, I begin to doubt whether this is going to work because I do not see anything in Gladius' setParent() to suggest that it is translating / rotating anything to the new frame of reference coordinates.

Still, I see it through to the end and grab the new transform, rotate about the y-axis, apply that rotation. I then remove all of this and...

It does not work. In fact is has no effect at all. Dang it.

So tomorrow, I pick up doing it the hard way. Or at least getting started with the hard way. It seems that hard.

Day #440