Angles between two spaceships (think Wing Commander) [ALMOST got it]

Started by
33 comments, last by Zakwayda 13 years, 5 months ago
I'm working on a 3d space shooter game like Wing Commander and the X-Wing series and I have quite some trouble with finding the angles between the player ship (ie. the camera) and another one.

In the most basic form, every ship has a position in space. For every ship, I have 17 (vertical angles) * 32 (horizontal angles) pre-rendered sprites. My goal is to calculate both these angles and use them to determine which of the sprites I need to use.

Of course, later on I want ships to have a direction as well, but let's keep it simple for now and just have them all face one way (0/0/-1) for example.

I tried some stuff with atan2 but it ended up only working in special cases, I don't really have the mathematical education at the moment to come up with my own algorithm. I basically have no idea where to start, I suspect there is some Matrix magic out there that can be used?

Does anybody have experience with this and could lend me a helping hand in trying to solve this?

Any feedback is extremely appreciated, as always. Thanks ahead of time!

[Edited by - d h k on October 24, 2010 3:37:57 PM]
Advertisement
You know you are missing one angle of rotation, right? I am not sure what that thing you said about having them "all face one way (0/0/-1)" means.

I think I would represent the attitude of the ship as a quaternion, do the same thing with the attitudes for which you have sprites, and pick the sprite that fits best (the one that has the highest absolute value of dot product between quaternions).

Quote:Original post by alvaro
You know you are missing one angle of rotation, right? I am not sure what that thing you said about having them "all face one way (0/0/-1)" means.


Now I'm not sure what you mean. :D Do you mean I didn't mention rolling? Or that my atan2 approach misses an angle of rotation? If it's either of these two cases, then I'm well-aware of that. If not, please elaborate. With the all-facing-one-way I was just trying to simplify the situation by having all the other ships (apart from the "camera"/player ship) be static while in reality they all rotate around any axis as well of course.

Quote:Original post by alvaro
I think I would represent the attitude of the ship as a quaternion, do the same thing with the attitudes for which you have sprites, and pick the sprite that fits best (the one that has the highest absolute value of dot product between quaternions).


Very interesting solution. Didn't even know there was such a thing as a dot product between quaternions but that would certainly work. I am a little concerned about performance however, considering I'd have to work with one quaternion per ship plus 544 (17 * 32 angles) static quaternions to test against. If I calculate the dot product of two quaternions 544 times for each ship in the scene every frame, wouldn't that nuke the speed?

Also, out of interest, any insight into how the old games did this? Probably not quaternions, right?
First, take the matrix of your player and your enemy.
Invert your matrix, and multiply by the enemy matrix. This gets the enemy's matrix relative to you.

Then, take your resultant matrix and extract your yaw/pitch/roll from it to pick a sprite from your table by finding the one with the closest matching angles.

If you don't care about roll, you only have to transform the enemy's at vector by your inverse matrix. Then extract the yaw/pitch of just that vector.
Here's another idea: If your unit quaternion is x*i + y*j + z*k + w, first of all w can be derived from the others, except for the sign. Since a quaternion and its opposite represent the same rotation, you can force w to be positive. Then quantize (x,y,z). For instance, using 10 different values for x, y and z you would need 1000 sprites.

I don't know how they did this in the old days, but I don't think it used to look too good, so I wouldn't try to reproduce it.

Alright, trying it via matrices now. This is what I'm doing:

// GETTING ROTATION MATRIX FOR EVERY SPACESHIP AS WELL AS THE CAMERA// (FROM DIRECTION VECTOR)float yaw = atan2f ( direction.x, direction.y );float pitch = atan2f ( direction.z, sqrt ( ( direction.x * direction.x ) + ( direction.y * direction.y ) ) );// what about roll? ignore for now thoughD3DXMatrixRotationYawPitchRoll ( &matrix, yaw, pitch, 0.0f );// CALCULATING PROPER FRAME (OUT OF 32 HORIZONTAL AND 17 VERTICAL ONES)// WHEN I DRAW A SPACESHIPD3DXMATRIX inv_camera;D3DXMatrixInverse ( &inv_camera, NULL, &camera.matrix );D3DXMatrixMultiply ( &inv_camera, &inv_camera, &matrix );float yaw = ( ( D3DXToDegree ( atan2f ( inv_camera._21, inv_camera._11 ) ) + 180.0f ) / 360.0f ) * 32.0f;float pitch = ( ( D3DXToDegree ( atan2f ( -inv_camera._31, sqrt ( inv_camera._32 * inv_camera._32 + inv_camera._33 * inv_camera._33 ) ) ) + 180.0f ) / 360.0f ) * 17.0f;


Had to do a whole lot of looking up formulas so I'm not 100% certain they are even correct. This gives me very erratic frames, definitely not correct at all.

What am I doing wrong? Thanks ahead of time!
If all you have is the direction vector, don't convert it to angles then make a rotation matrix.

Make a look-at matrix. If i recall directx has a function for that, but as I don't use directx, I'm not having luck with a google for it.
For the most part the algorithm is:
zaxis = normal(At)xaxis = normal(cross(Up, zaxis))yaxis = cross(zaxis, xaxis)

Where Up is a guess at the up vector of the object.

And like I mentioned, if you only need the yaw and pitch, just transform the direction vector directly. You don't need to create a matrix for the enemy.

Also, I don't think you are taking into account the quadrant of the calculation. You need to check if the transformed direction is facing away (normally positive Z) or towards you so that you can pick the right half of the frames. atan2f returns the same values for the front two and the back two quadrants.
UPDATE / THREAD REVIVAL

Wow, I'm super sorry that it took me this long to answer. I realized that I first had to implement a 6dof-camera properly before going on. I did, hopefully learned a bunch by doing that, and am now back with the same problem.

For reference, I do absolutely need to do this for all three rotational degrees of freedom (ie. with roll). Don't think I clarified this properly before. Sorry. :D

My camera now actually stores a forward, left and up axis (vector) so these calculations should be easier. This is my current attempt:

// RIGHT BEFORE WE RENDER AN ENEMYD3DXMATRIX camera_view, object_view;D3DXMatrixLookAtLH ( &camera_view, &camera.position, &D3DXVECTOR3 ( camera.position.x + camera.forward_axis.x, camera.position.y + camera.forward_axis.y, camera.position.z + camera.forward_axis.z ), &camera.up_axis );D3DXMatrixInverse ( &camera_view, NULL, &camera_view );// my object/enemy is at 0/0/0 in world space, looking down the z-axisD3DXMatrixLookAtLH ( &object_view, &D3DXVECTOR3 ( 0.0f, 0.0f, 0.0f ), &D3DXVECTOR3 ( 0.0f, 0.0f, -1.0f ), &D3DXVECTOR3 ( 0.0f, -1.0f, 0.0f ) );D3DXMatrixMultiply ( &camera_view, &camera_view, &object_view );float yaw = ( ( D3DXToDegree ( atan2f ( camera_view._21, camera_view._11 ) ) + 180.0f ) / 360.0f ) * 32.0f;float pitch = ( ( D3DXToDegree ( atan2f ( -camera_view._31, sqrt ( camera_view._32 * camera_view._32 + camera_view._33 * camera_view._33 ) ) ) + 180.0f ) / 360.0f ) * 17.0f;


As you can see, I'm now using D3DXMatrixLookAt as you recommended, KulSeran.

I realize the quadrant issues I'm getting into with using atan2f but at the moment, the above code actually gives me seemingly random results. If I just walk around the ship in a circle (keeping my cam target at the ship), it starts rotating properly, then all of the sudden stops for a bit. Then jumps to a frame showing it from the floor and so on.

What could I still be doing wrong. Any feedback is appreciated. And I promise I won't "forget" this thread again for such a long time!
I'm not sure how on-topic this post is, but alvaro's post made me start to think about what the best way to discretize rotations is; that's the subject of what follows...

(@ d h k : Sorry, this isn't really a reply to your most recent post. Hopefully someone will spot the bug though.)

Quote:Original post by alvaro
I think I would represent the attitude of the ship as a quaternion, do the same thing with the attitudes for which you have sprites, and pick the sprite that fits best (the one that has the highest absolute value of dot product between quaternions).


Let me continue this line of thought for a second.

In n dimensions, the largest system of equiangular lines contains n(n+1)/2 lines. E.g., in 3d, you can have 3*4/2=6 equiangular lines (each line intersects the unit sphere twice; those 12 points are the vertices of a regular icosahedron).

The unit quaternions modulo antipodes is really the set of lines in R^4. So the largest system of equiangular rotations contains 4(4+1)/2=10 elements. I.e., 10 pairs of quaternions. These 20 quaternions are the direct 4d analogue of the 12 vertices of an icosahedron in 3d.

In the same way that one builds a geosphere by subdividing triangles starting with an icosahedron, it seems that one can likewise build a regular, as-close-to-equiangular-as-possible set of rotations by subdividing the tetrahedra that form the faces of the convex hull of these 20 vertices.

What one would need to figure out is which subsets of the vertices are the tetrahedra...

...

Another, more-easily-understood, and pretty good (if not as "perfect") method would simply be the 4d analogue of cubemaps.

There's a straightforward bijection between points on the sphere and points on the cube (for the cube-to-sphere direction, just normalize the points), which works in any dimension.

In 4d, the cube ("tesseract") has 2^4=16 faces, each of which is a 3d cube. Since quaternions are all modulo antipodes, we only really need half this number; we need 8 faces. Then, discretizing each face is easy as a 3d grid.

This isn't quite as good as the equiangular method, but seems reasonable.

...

I'm more interested in the equiangular method though. I may revise this post to expand on that.

[Edited by - Emergent on October 19, 2010 4:01:35 PM]
Hi,

I can offer you another alternative. Not without its flaws but works good enough.
I used this on a J2ME game I was developing. Basically I had, in 2D, 24 frames
representing the rotation of objects.
Its the same kind of problem, discretization of possible rotations in order to
access a sprite, although here I only had 1 angle.

Basically, in 3D you can represent your orientation in several ways. People suggested quaternions, matrices and you can even use euler angles.

What I always considered simple is a direction vector (in 3D) and an angle of
rotation around that vector. Its easy to visualize (in your head) too.

A direction vector in 3D will be good for the following reasons:

1. You can easily determine the rotation angles (pitch/yaw) if given a vector.
2. If every ship has a direction vector, finding the relative vector between
them is trivial. Combined with 1 this gives you the relative angles between the
ships.


Now back to my game - I had only one angle but I'd divide it by the fixed factor
and get the frame number. If I had 24 frames for a given entity, the factor was
basically 15 degrees.

What you're left with is to know how to access your array of sprites correctly
based on the given angles and that's an easy problem of how you build the array.

I doubt you'll need more than that..

BTW, the code I implemented uses fixed point, not even floating point.
-----------------------------He moves in space with minimum waste and maximum joyGalactic Conflict demo reel -http://www.youtube.com/watch?v=hh8z5jdpfXY

This topic is closed to new replies.

Advertisement