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)
          transform.setRotation(math.vector3.add(rotation, transform.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)
          transform.setPosition(math.vector3.add(position, transform.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]];
          transform.setPosition(math.vector3.add(direction, transform.position));
        }
        // ...
      }
     };

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

No comments:

Post a Comment