gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > Flying in 3-D space using all 3 axis

#159675 - DiscoStew - Fri Jul 04, 2008 7:52 am

Would anyone happen to have an example, or a link pertaining to this? All I'm trying to do is have a ship moving forward at a constant speed while it's direction is affected by input (Y-axis = Left/Right, X-axis = Up/Down, Z-axis = L/R).

I thought it wouldn't be so hard. However, I begin to have problems when I involve more than 1 axis. When with just 1, I have no problems, but when I add another, the actual direction begins to screw up, even to a point where when I go to alter one axis, it shows up as altering another because of it's current direction.
_________________
DS - It's all about DiscoStew

#159677 - sgeos - Fri Jul 04, 2008 8:25 am

Have you work out all of your math on paper?

-Brendan

#159681 - DekuTree64 - Fri Jul 04, 2008 9:06 am

Rather than using "absolute" angles, just keep the current orientation stored as a matrix, and do your rotations relative to that. That's how real aircraft controls work. Pitch/yaw/roll are relative to your current orientation, not to the world coordinate system.

A rotation matrix is actually just 3 axis vectors. So if you want to turn left/right, then rotate the matrix's x and z axis vectors around its y axis. You could do that with vector-by-axis-angle rotation, or I think just multiplying with an x rotation matrix would work too.

You'll probably want to normalize the axis vectors every few frames to avoid fixed point error creep.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#159682 - sgeos - Fri Jul 04, 2008 9:34 am

After you work out your math, read up on Model-View-Controller if you do not know about it already. Position and orientation are separate but belong to your ship (model). The controls modify the orientation (controller), and the frame update modifies the position based on the orientation. The camera (view) reads this information and uses it to display the situation on the screen.

Create scenarios and desired outcomes for position updates given various orientations and then hash out the math that will get you there. Matrices are going the be the easiest way to do all of this.

-Brendan

#159690 - silent_code - Fri Jul 04, 2008 11:09 am

There's an alternative to matrices, but you'll have to see for yourself what you need / like better: Quaternions.
With unit quaternions you can represent arbritary rotations with four scalars, normalization is a lot cheaper than with matrices and rotations are also releative and axes preserving. Plus, you can still work with (relative) euler angles and don't get gimbal lock (when done right), but also don't need to do all the calculations to convert angles to matrices.
You can simply convert the resulting quaternion to a matrix in the end.

Happy rotating. :^)
_________________
July 5th 08: "Volumetric Shadow Demo" 1.6.0 (final) source released
June 5th 08: "Zombie NDS" WIP released!
It's all on my page, just click WWW below.

#159693 - DensitY - Fri Jul 04, 2008 12:32 pm

Its been said twice, using the ship's rotation matrix is a good idea.

its pretty simple. after you build the rotation matrix (or aquire it from the DS hardware). you can simply do something like this once you rip out the up, forward and right vectors out of the rotation matrix

Code:

  if(keypress up)
  {
     speed = 20; (or whatever it is
      Trajectory.x += ship.up.x * speed;
      Trajectory.y += ship.up.y * speed;
      Trajectory.z += ship.up.z * speed;
  }
  if(keypress down)
  {
     speed = -20; (or whatever it is
      Trajectory.x += ship.up.x * speed;
      Trajectory.y += ship.up.y * speed;
      Trajectory.z += ship.up.z * speed;
   }
  if(keypress left)
  {
     speed = -20; (or whatever it is
      Trajectory.x += ship.right.x * speed;
      Trajectory.y += ship.right.y * speed;
      Trajectory.z += ship.right.z * speed;
   }
  if(keypress right)
  {
     speed = 20; (or whatever it is
      Trajectory.x += ship.right.x * speed;
      Trajectory.y += ship.right.y * speed;
      Trajectory.z += ship.right.z * speed;
   }
 ship.x += trajectory.x;
 and the same with y and z


etc, you get the idea.. this way it'll easily handle you pressing both forward and right or left and not have any annoying speed ups.

EDIT: the other thing is you can rotate the ship in any direction and forward will make the ship go forward in the direction its facing.

#159710 - sgeos - Fri Jul 04, 2008 9:59 pm

Code:
  if(keypress up)
  {
     speed = 20; (or whatever it is
      Trajectory.x += ship.up.x * speed;
      Trajectory.y += ship.up.y * speed;
      Trajectory.z += ship.up.z * speed;
  }

For what it is worth, this is a good place to use a macro.
Code:
// my_ship.h
#define SHIP_BASE_SPEED 20 // works for now

// my_ship.c
  if(keypress up)
  {
     speed = SHIP_BASE_SPEED;
      Trajectory.x += ship.up.x * speed;
      Trajectory.y += ship.up.y * speed;
      Trajectory.z += ship.up.z * speed;
  }

Although that is nitpicking. The principle remains the same.

-Brendan

#159715 - DiscoStew - Sat Jul 05, 2008 12:06 am

Oddly enough, I found the main culprit to my program. When I'd change an axis, I'd change it at a global level rather than what it was previously. What I mean is this. I kept values pertaining to the X/Y/Z axis, and I changed those depending on the input given. If I wanted to turn left/right, I adjusted the Y-axis. Afterwards, I'd throw those into a matrix. What ends up happening is being very dependent on a particular axis, and should it stray even a little, it messes up.

This goes to show just how much knowledge I had on the subject.

Anyways, I went the route of having a matrix that changes each frame depending on the rotations given it, and now it actually works. But, now I ran into another problem, which I believe was already spoken about in this thread. As I continue to alter the axis of each, the overall effect of everything on the screen begins to deform. Is this the consequence of not normalizing? Well, I went and did it to the 3 vector axis in the matrix itself, but it doesn't seem to be having any effect. Because of it continually changing the matrix, would it be better to work with quaternions which are then converted into matrices for use?
_________________
DS - It's all about DiscoStew

#159716 - DekuTree64 - Sat Jul 05, 2008 12:34 am

Yeah, now that you have the matrix style working, it might be easy enough to switch over to quaternions. Matrices are much easier to get running since you can really visualize what's happening, but quaternions seem easier to deal with accuracy. I've never used them myself though. Actually I'm trying to at the moment for a PC project, but appearently ending up mirrored or transposed or something...

Anyway, if you do stick with the matrix method, another accuracy-related problem that can come up is the axes gradually drifting away from being orthogonal to eachother. That could be the deforming you're still getting after normalizing.

For that, you can re-generate 2 of the axes using cross products. Say cross x with y to get a z axis orthogonal to both of them. Then cross x with that new z axis to get a new y axis, and all 3 are orthogonal again.

Also since the axes are unit-length, you can use 30-bit fixed point so the errors are much smaller. You'll still want to do the corrections occasionally, but it will be needed less often, and be less likely to jitter.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#159720 - silent_code - Sat Jul 05, 2008 2:27 am

@DT64: Very much what I wanted to say about matrix normalization. :^)

But why would you need to regenerate two base vectors?

The answer is quite easy: Imagine two base vecors in a plane, so that they look 2D. the angle between them would be 60?. You can calculate a third vector, which will be orthonormal to both of the initial vectors (pointing to you from the plane), but because they aren't orthonormal to each other (90? angle), the space they describe is not orthogonal (it's "warped".) That's why you would recalculate another base vector.

A "simple" optimization is to only regenerate one base vector at a time and "rotate" the order of which one get's regenerated next. You could make that order heuristically, depending on the change of the axes or whatever you find usefull, as long as it doesn't cost more than regenerating two base vectors each time. :^)

I hope that's not too much off and that the terms are right (it's been a while since I dealt with this stuff in a remotely formal way and communiacted these concepts with others :^D ) and of course, that it helps you understand the problem a little better.

With quaternions (if you choose to look into them - usually people like matrices more and I'm ok with that :^D ) there comes new power, but also new concepts of thinking and thus new problems.
But all in all, they are very handy. You can even compress them quite a lot and animations will still look pretty good on the NDS.
But the only thing I found I had to "hack around them", is with camera rotation. Because some camera models need fixed (absolute) rotation axes (their rotations are dependent on "the man holding them"), not relative ones, you need to rethink rotations for these cases.

Well, it's not a real problem and you can still use relative axes and avoid the gimbal lock, all it takes is a little understanding (that comes with time) and experience.

Btw: Usually (on the PC), all my rotations are represented by quaternions and back when I changed that from euler angles, all of the sudden all my cameras behaved very wrong... ;^)
Now I am still using euler angles as input, but internally everything is handled by quaternions, which "unconfuses" it (because quaternions *are* confusing!) :^)

Have some fun! :^)
_________________
July 5th 08: "Volumetric Shadow Demo" 1.6.0 (final) source released
June 5th 08: "Zombie NDS" WIP released!
It's all on my page, just click WWW below.

#159820 - DiscoStew - Mon Jul 07, 2008 6:27 am

I went and added quaternions into the project via creating from Euler angles, and at first, everything continued to act very wrong, deforming the scene, and I tried fixing it for the past few days. Only now did I realize something....

Shouldn't I be creating individual quaternions per axis instead of combining them into one?

I knew I saw something of that nature a while ago, so I did that, and now everything appears to be working (as far as rotation goes). The scene isn't deforming anymore, so that's a huge plus.

My method though is to take the input from the 3 axis (as Euler), create individual quaternions and multiply them together, normalize the result, then multiply the resulting quaternion into the ship's own quaternion (then normalizing, though perhaps I should only need to normalize once every few frames like you all say?). Once that is all done, the matrix is created from that quaternion, and everything is dealt with that matrix at that point. I'm still skeptical by gimbal lock in this case, because I'm starting from Euler angles. Should I be?

Anyways, now all I have to do is get it to go forward, and that is basically grabbed from the axis vectors in the matrix, right? If the ship is constantly moving forward, and I am rotating the ship via the inputs, won't I only need the forward vector (which is the first 3 values of the 3rd row in the matrix)? This may be perfect for me, because what I also need to do in the future is build a routine for dealing with lasers (of which the vector for it is the same as the ship's current forward vector).
_________________
DS - It's all about DiscoStew

#159832 - silent_code - Mon Jul 07, 2008 10:38 am

Well, I don't build quaternions for each euler component, but if it works for you, go with it, until you have time to fix it.

Just to make sure: You know that two quaternions p and q are "added" (concatenated is the right term) by multiplication and that, just like with matrices, the order of multiplication is important, because "p * q != q * p", right? I mean, I would expect it. ;^)

Well, you might as well get away with only one normalization for the final quaternion.

About the single base vector: You might be right. I have just thought about it for a moment and couldn't find any reason why you would want to use any other base vector for your ships movement. (At least when judging from my knowledge of aeronautics, which is very limited.)

Except if it's a helicopter. Those things can move in up to 6 dof. The only Jet I know of (so there might be more) that has more dof than usual, is the Harrier Jump Jet, which has the ability for vertical liftof (which would be modelled with the "up" base vector) adding a whole dof at liftof. After that, I guess it's just a regular jet... or can it hover mid flight?

Well, that's a bit off topic right now. :^)

Good luck! :^)
_________________
July 5th 08: "Volumetric Shadow Demo" 1.6.0 (final) source released
June 5th 08: "Zombie NDS" WIP released!
It's all on my page, just click WWW below.