Friday, July 6, 2012

Real Movement After Panning in Gladius

‹prev | My Chain | next›

I tried building a spaceship camera last in my Gladius solar system example. It worked. But not really.

I built a Gladius entity "actor" that was capable of panning up, down, left and right. I could even move forward and backwards. The definition was even pretty easy, thanks to the gladius-input extension:
``````        // ...
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)

var position;
if (controller.states["MoveForward"])
position = [0, 0, space.clock.delta * 0.01];
if (controller.states["MoveBackward"])
position = [0, 0, -space.clock.delta * 0.01];

if (position)
// ...``````
It was not until I started playing with it that I realized my mistake. All of the rotation is done relative to the original x- or y-axis. All of the positional movement is done along the original z-axis.

That is, if I moved down toward the Sun, then panned up to look at the Earth, panning to the left to follow the Earth's revolution around the Sun simply failed. I was still trying to rotate around the original y-axis when I wanted to pan around the y-axis of the direction that I was now facing.

More obvious is the failure of the move forward / backward option. After panning up to look at the Earth, I might want to fly to the Earth. The natural inclination is to move forward since I am looking at the Earth. Instead move forward continued me in the same direction: plunging into the Sun.

What I want to do is pan and move relative to the direction my spaceship is currently pointing. My strategy for solving this problem tonight will involve Gladius' `setParent()` method. After every movement, I will create a new transform describing the current orientation of the camera. I will use that transform as the spaceships parent / frame-of-reference. So when I next move or rotate, it will be relative to this new frame-of-reference.

And then I think better of it. Every time I move relative to the frame of reference, the frame of reference is going to have to change so that the next movement will be relative the new frame of reference. I can do that, except it is a pain to calculated relative angles and positions.

So how can I do this?

To answer, I fall back to the tank example that comes with Gladius. In there, movement is describe relative to the tank's frame of reference. In my case, I want to move a fraction of space units along the negative z-axis (into the Gladius page):
``````    var cameraLogic = {
"Update": function(event) {
if (!this.owner.hasComponent("Controller")) return;

var controller = this.owner.findComponent("Controller")
, transform = this.owner.findComponent("Transform");

// ...
if (controller.states["MoveForward"]) {
var direction = math.transform.translate([0, 0, -space.clock.delta * 0.005]);
// ... more calculations here
}
// ...
}
};
``````
Next, a bit of matrix calculations. Fortunately, Gladius has this built-in. I need the current rotation:
``````    var cameraLogic = {
"Update": function(event) {
if (!this.owner.hasComponent("Controller")) return;

var controller = this.owner.findComponent("Controller")
, transform = this.owner.findComponent("Transform");

// ...
if (controller.states["MoveForward"]) {
var direction = math.transform.translate([0, 0, -space.clock.delta * 0.005]);
rotation = math.transform.rotate(transform.rotation);
direction = math.matrix4.multiply( [direction, rotation] );
// apply the change here...
}
// ...
}
};
``````
Lastly, I pull out the difference in the x, y, and z positions, which come at the end of the 4x4 direction matrix. It is then a simple matter of adding those numbers to the camera's previous position:
``````    var cameraLogic = {
"Update": function(event) {
if (!this.owner.hasComponent("Controller")) return;

var controller = this.owner.findComponent("Controller")
, transform = this.owner.findComponent("Transform");

// ...
if (controller.states["MoveForward"]) {
var direction = math.transform.translate([0, 0, -space.clock.delta * 0.005]);
rotation = math.transform.rotate(transform.rotation);
direction = math.matrix4.multiply( [direction, rotation] );
direction = [direction[12], direction[13], direction[14]];
}
// ...
}
};
``````

The upshot is that I can now get a nice perspective on retrograde motion thanks to these new controls:

This matrix math is still a bit of a black box for me at this point. Parts are familiar, but I need a refresher course. I will try to find something suitable and revisit this tomorrow night when I tackle doing the same for rotation.

Day #439