LookAt() functionality for FPS (Euler Angle) Camera

Started by
14 comments, last by Wavesonics 14 years, 1 month ago
I have a FPS &#115;tyle camera that accumulates Euler angles, and works well. But now I want to implement a lookAt( Vector ) method which will take in a vector, and then calculate an absolute rotation in Euler Angles to aim the camera at that point in space. This is what I have so far: <!–STARTSCRIPT–><!–source lang="cpp"–><div class="source"><pre> <span class="cpp-keyword">void</span> lookAt( <span class="cpp-keyword">float</span> x1, <span class="cpp-keyword">float</span> y1, <span class="cpp-keyword">float</span> z1 ) { VectorR3 v( x1, y1, z1 ); v.x = m_position.x - v.x; v.y = m_position.y - v.y; v.z = m_position.z - v.z; <span class="cpp-keyword">float</span> r = v.length(); <span class="cpp-keyword">float</span> yaw = acos( v.z / r ); <span class="cpp-keyword">float</span> pitch = atan2( v.y, v.x ); setRotation( yaw, pitch, <span class="cpp-number">0</span>.0f, <span class="cpp-keyword">true</span> ); <span class="cpp-comment">// This just wraps the values to ensure they stay within range</span> } </pre></div><!–ENDSCRIPT–> Bbuutt… things aren't working. This is Cartesian Coordinate to Spherical coordinate conversion, or at least it's suppose to be. Does anything jump out as majorly wrong here?
==============================
A Developers Blog | Dark Rock Studios - My Site
Advertisement
I'd think you'd want target - position, rather than position - target. Also, it looks like you might have yaw and pitch backwards. That is, it should be:
float pitch = acos( v.z / r );float yaw = atan2( v.y, v.x );
Also, since acos() can fail if the input falls outside of the range [-1, 1] (e.g. due to numerical error), an (IMO) better way to compute the pitch is:
float length = sqrt(v.x*v.x+v.y*v.y);float pitch = atan2(v.z, length);
Ok so let me see if I get this right:
    void lookAt( float x1, float y1, float z1 )    {        VectorR3 v( x1, y1, z1 );        v -= m_position;        float r = sqrt( v.x*v.x + v.y*v.y ); // I'm not suppose to take Z into account?        float yaw = atan2( v.y, v.x );        float pitch = atan2( v.z, r );        setRotation( yaw, pitch, 0.0f, true );    }


I'm not getting proper rotations with this.
==============================
A Developers Blog | Dark Rock Studios - My Site
Quote:Original post by Wavesonics
Ok so let me see if I get this right:
*** Source Snippet Removed ***

I'm not getting proper rotations with this.
Take another look at my example code (you're computing 'r' differently than what was shown).

But yeah, you don't need to include z there. Think about it this way. Take the vector to the target (v) and project it orthogonally onto the ground plane (call the projected vector v'). v and v' taken together form a right triangle. The length of one of the legs is |v'|, and the length of the other leg is v.z. The lengths of these edges are proportional to the cosine and sine (respectively) of the opposite angle, which means you can submit them directly to atan2(), as shown, to get the angle.

Ok, that wasn't a very good explanation, but maybe it'll be enough to give you the general idea...
I had miss-typed and left out the sqrt() but fixed it, is that what you are referring to? Even with the sqrt() i'm still getting incorrect results though.

If there is something else amiss, i don't see it.
==============================
A Developers Blog | Dark Rock Studios - My Site
Can you post your setRotation() function?
    inline float zeroClamp( float x )    {        return ( std::fabs(x) > std::numeric_limits<float>::epsilon() ? x : 0.0f );    }    inline float wrap( float a )    {        while( a >= RAD360 || a < 0.0f )        {            if(a >= RAD360 )                a -= RAD360;            else if( a < 0.0f )                a += RAD360;        }        return a;    }    void setRotation( float yaw, float pitch, float roll, bool isInRadians = false ) {        m_yaw = 0.0f;        m_pitch = 0.0f;        m_roll = 0.0f;        rotate( yaw, pitch, roll, isInRadians );    }    void rotate( float yaw, float pitch, float roll, bool isInRadians = false ) {        yaw = zeroClamp( yaw );        pitch = zeroClamp( pitch );        roll = zeroClamp( roll );        if( !isInRadians )        {            yaw = Rotation3D::deg2rad( yaw );            pitch = Rotation3D::deg2rad( pitch );            roll = Rotation3D::deg2rad( roll );        }        m_yaw += yaw;        m_yaw = wrap( m_yaw ); // Keep the angle between 0 and 360        m_pitch += pitch;        m_pitch = wrap( m_pitch );        m_roll += roll;        m_roll = wrap( m_roll );    }


[Edited by - Wavesonics on February 26, 2010 2:51:07 PM]
==============================
A Developers Blog | Dark Rock Studios - My Site
Hi Wavesonics,

I tried the following code and it works; It's pretty much what jyk was suggesting, however I still post it. However here I explicitly assume a right-hand setting in which plane XZ is the ground and y-axis is the UP direction, which I think would be the case in a typical OpenGL setting.

The program outputs:

Pitch = -35
Yaw = 135

Which makes sense for the input.

Bye!

#include <stdio.h>#include <math.h>int main(int argc, char *argv[]){        float x1 = -1;        float y1 = -1;        float z1 = 3;        float position_x = 1;        float position_y = 1;        float position_z = 1;        float v_x = x1 - position_x;        float v_y = y1 - position_y;        float v_z = z1 - position_z;        float length = sqrt(v_x * v_x + v_z * v_z);        float pitch = atan2(v_y, length);        float yaw = atan2(v_z, v_x);        printf("Pitch = %d\n", (int)(180 * (pitch / 3.1415)));        printf("Yaw   = %d\n", (int)(180 * (yaw / 3.1415)));        return 0;}
I don't see anything wrong with your code, after triple checking things. Are you sure the problem does not lie outside of this function?

For instance, you are using the yaw and pitch to rotate a vector (1, 0, 0) by first applying the pitch (rotation around Y), then the yaw (rotation around Z)? This vector should of course be added to your viewpoint to get your target point back.

And you are certain your Z axis is your up axis? If this would be Y, then you need to swap some coordinates and possibly place a - sign somewhere.
Ah no my Z axis is not up, it's standard OpenGL coordinate system so -Z should be forward if I'm not mistaken, and +y is up.

Also how I'm applying the rotation is by generating a quaternion from the Euler Angles, then extracting a matrix from that (Just for convince at the moment).
==============================
A Developers Blog | Dark Rock Studios - My Site

This topic is closed to new replies.

Advertisement