Get model to face direction of movement - SOLVED

Member
Posts: 65
Joined: 2009.03
Post: #1
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:
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
     * non-perpendicular unit-length 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
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
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".
Quote this message in a reply
Moderator
Posts: 3,571
Joined: 2003.06
Post: #3
If you're having difficulty doing this via gluLookAt, or your own UVN rotation code isn't working, you could also just go brute-force 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 brute-force style. I put in two functions, one for y being the zenith and one for z being the zenith.

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        1e-6f

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 Rasp ):

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.
Quote this message in a reply
Member
Posts: 65
Joined: 2009.03
Post: #4
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.
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 Grin

Cheers

MikeD

iPhone Game Development Blog - 71Squared
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  3d movement jjslay 2 3,933 Jan 17, 2011 04:08 PM
Last Post: jjslay
  Roll model when turning - SOLVED MikeD 1 2,140 Sep 29, 2010 10:55 AM
Last Post: MikeD
  removeFromSuperview not calling dealloc of view - SOLVED BeyondCloister 5 12,768 Aug 11, 2010 01:18 PM
Last Post: BeyondCloister
  Help with touch movement shadow021 1 1,756 Apr 7, 2009 12:19 AM
Last Post: wonza