Get model to face direction of movement  SOLVED
This is a question that has been asked in MANY forums on the web as I've been reading most of them :s
That said, I'm still confused as to how to orient my 3D model to face the direction of travel. I have a normalized 3D vector that is pointing in the direction of travel and I want to orient my model to face that direction.
I'm thinking I need to create a 3D matrix from that vector, but all the examples I've been trying cause the model to rotate in all sorts of ways, but never face the direction I expect.
At the moment I'm trying to create a matrix from a vector using code that was inside the gluLookAt function. This code looks like this:
The model is rotating, but not in a way that keeps the front of the shop oriented toward the direction of travel. The models rolls and banks as the direction changes but not in the correct way.
I hope that has made some sense. If anyone has any pointers or suggestions that would be great.
Cheers
MikeD
That said, I'm still confused as to how to orient my 3D model to face the direction of travel. I have a normalized 3D vector that is pointing in the direction of travel and I want to orient my model to face that direction.
I'm thinking I need to create a 3D matrix from that vector, but all the examples I've been trying cause the model to rotate in all sorts of ways, but never face the direction I expect.
At the moment I'm trying to create a matrix from a vector using code that was inside the gluLookAt function. This code looks like this:
Code:
static inline void SSMatrixFromVector(float* mtx, const SSVector3D v, const float upx, const float upy, const float upz)
{
// GLfloat m[16];
GLfloat x[3], y[3], z[3];
GLfloat mag;
/* Make rotation matrix */
/* Z vector */
z[0] = 0  v.x;
z[1] = 0  v.y;
z[2] = 0  v.z;
mag = sqrtf(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
if (mag) { /* mpichler, 19950515 */
z[0] /= mag;
z[1] /= mag;
z[2] /= mag;
}
/* Y vector */
y[0] = upx;
y[1] = upy;
y[2] = upz;
/* X vector = Y cross Z */
x[0] = y[1] * z[2]  y[2] * z[1];
x[1] = y[0] * z[2] + y[2] * z[0];
x[2] = y[0] * z[1]  y[1] * z[0];
/* Recompute Y = Z cross X */
y[0] = z[1] * x[2]  z[2] * x[1];
y[1] = z[0] * x[2] + z[2] * x[0];
y[2] = z[0] * x[1]  z[1] * x[0];
/* mpichler, 19950515 */
/* cross product gives area of parallelogram, which is < 1.0 for
* nonperpendicular unitlength vectors; so normalize x, y here
*/
mag = sqrtf(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
if (mag) {
x[0] /= mag;
x[1] /= mag;
x[2] /= mag;
}
mag = sqrtf(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
if (mag) {
y[0] /= mag;
y[1] /= mag;
y[2] /= mag;
}
#define M(row,col) mtx[col*4+row]
M(0, 0) = x[0];
M(0, 1) = x[1];
M(0, 2) = x[2];
M(0, 3) = 0.0;
M(1, 0) = y[0];
M(1, 1) = y[1];
M(1, 2) = y[2];
M(1, 3) = 0.0;
M(2, 0) = z[0];
M(2, 1) = z[1];
M(2, 2) = z[2];
M(2, 3) = 0.0;
M(3, 0) = 0.0;
M(3, 1) = 0.0;
M(3, 2) = 0.0;
M(3, 3) = 1.0;
#undef M
}
The model is rotating, but not in a way that keeps the front of the shop oriented toward the direction of travel. The models rolls and banks as the direction changes but not in the correct way.
I hope that has made some sense. If anyone has any pointers or suggestions that would be great.
Cheers
MikeD
iPhone Game Development Blog  71Squared
This is what gluLookAt does  given a forward vector and an up vector:
normalize both
cross forward and up to get right/left (depending on handedness and order of cross product)
cross forward and right/left to get new up
use forward, right/left and new up as a rotation matrix
Of course, there are two cross products that you can change the order of to get the wrong answer, several stages that may or may not need renormalization, and you'll have to fight the fact that OpenGL's matrices aren't the same way round as your linear algebra text.
Your code would be a lot easier to read if you defined some functions like "normalize" and "cross".
normalize both
cross forward and up to get right/left (depending on handedness and order of cross product)
cross forward and right/left to get new up
use forward, right/left and new up as a rotation matrix
Of course, there are two cross products that you can change the order of to get the wrong answer, several stages that may or may not need renormalization, and you'll have to fight the fact that OpenGL's matrices aren't the same way round as your linear algebra text.
Your code would be a lot easier to read if you defined some functions like "normalize" and "cross".
If you're having difficulty doing this via gluLookAt, or your own UVN rotation code isn't working, you could also just go bruteforce on it and use some trig, as long as you aren't using it for anything particularly performance critical. This is assuming up is toward the zenith, so roll isn't taken into account here. I don't know if this code works or not. I just butchered it together for some ideas of how to approach it bruteforce style. I put in two functions, one for y being the zenith and one for z being the zenith.
You'd use it, something like this, where myPoint would be your movement vector and mySphereCoords would be filled in with angles you can use to rotate your object with to match the direction vector (theoretically anyway, like I said, I don't know if this works or not ):
It works by imagining you're at the center of the earth and know the cartesian point where something is located on the surface (your direction vector), and then converts that to lat/lon.
Code:
enum
{
RHO, // distance from origin to point
THETA, // similar to longitude (azimuth)
PHI // similar to latitude (altitude, elevation, or inclination)
};
enum { X, Y, Z };
#define RAD_TO_DEG 57.295779513082f
#define EPSILON 1e6f
void tqPointToSphericalZenithYDeg(float *point, float *sph)
{
// north pointing toward positive y axis
float x = point[X];
float y = point[Z];
float z = point[Y];
// distance
sph[RHO] = sqrtf((x * x) + (y * y) + (z * z));
// longitude
sph[THETA] = atan2f(y, x) * RAD_TO_DEG;
while (sph[THETA] < 0.0f)
sph[THETA] += 360.0f;
// latitude
if (sph[RHO] < EPSILON)
sph[PHI] = 0.0f;
else
sph[PHI] = acosf(z / sph[RHO]) * RAD_TO_DEG;
}
void tqPointToSphericalZenithZDeg(float *point, float *sph)
{
// north pointing toward positive z axis
float x = point[X];
float y = point[Y];
float z = point[Z];
// distance
sph[RHO] = sqrtf((x * x) + (y * y) + (z * z));
// longitude
sph[THETA] = atan2f(y, x) * RAD_TO_DEG;
while (sph[THETA] < 0.0f)
sph[THETA] += 360.0f;
// latitude
if (sph[RHO] < EPSILON)
sph[PHI] = 0.0f;
else
sph[PHI] = acosf(z / sph[RHO]) * RAD_TO_DEG;
}
You'd use it, something like this, where myPoint would be your movement vector and mySphereCoords would be filled in with angles you can use to rotate your object with to match the direction vector (theoretically anyway, like I said, I don't know if this works or not ):
Code:
// init to demo values (not normalized, but not necessary in this situation)
float myPoint[3] = { 2.1f, 1.1f, 5.4f };
float mySphereCoords[3];
// assuming Y axis is north pole (the zenith)
tqPointToSphericalZenithYDeg(myPoint, mySphereCoords);
// rotate azimuth around Y
glRotatef(mySphereCoords[THETA], 0.0f, 1.0f, 0.0f);
// tilt up/down
glRotatef(mySphereCoords[PHI], 1.0f, 0.0f, 0.0f);
It works by imagining you're at the center of the earth and know the cartesian point where something is located on the surface (your direction vector), and then converts that to lat/lon.
Thanks for the help guys. Based on your info and so more reading I've created a function that will create a matrix that can be used to orientate my model in the direction they are travelling, or any direction I pass in.
I've put the function below in case anyone else goes hunting for something similar.
Thanks again for the quick response and the help
Cheers
MikeD
I've put the function below in case anyone else goes hunting for something similar.
Code:
static inline void SSMatrixFaceVector(float* mtx, const SSVector3D dir, const SSVector3D up)
{
SSVector3D d = SSVector3DNormalize(dir);
SSVector3D right = SSVector3DNormalize(SSVector3DCross(up, d));
SSVector3D newUp = SSVector3DNormalize(SSVector3DCross(d, right));
SSVector3D backwards = SSVector3DNormalize(SSVector3DCross(right, newUp));
// Construct a matrix from the new values
mtx[0] = right.x; mtx[1] = right.y; mtx[2] = right.z; mtx[3] = 0;
mtx[4] = newUp.x; mtx[5] = newUp.y; mtx[6] = newUp.z; mtx[7] = 0;
mtx[8] = backwards.x; mtx[9] = backwards.y; mtx[10] = backwards.z; mtx[11] = 0;
mtx[12] = 0; mtx[13] = 0; mtx[14] = 0; mtx[15] = 1;
}
Thanks again for the quick response and the help
Cheers
MikeD
iPhone Game Development Blog  71Squared
Possibly Related Threads...
Thread:  Author  Replies:  Views:  Last Post  
3d movement  jjslay  2  5,222 
Jan 17, 2011 04:08 PM Last Post: jjslay 

Roll model when turning  SOLVED  MikeD  1  2,980 
Sep 29, 2010 10:55 AM Last Post: MikeD 

removeFromSuperview not calling dealloc of view  SOLVED  BeyondCloister  5  20,355 
Aug 11, 2010 01:18 PM Last Post: BeyondCloister 

Enemy movement like in the 80's  MikeD  4  4,665 
Apr 16, 2009 03:46 AM Last Post: MikeD 

Help with touch movement  shadow021  1  2,748 
Apr 7, 2009 12:19 AM Last Post: wonza 