trig model FPS angle generation

I have come across a lot of math models lately for 3D camera systems. I am stuck on the easiest, which is so frustrating. Basically to calculate all of the trig functions, one has to know ‘the angle’. Or 2. I understand that if the spherical nomenclature is applied to the system, and then translated into cartesian coordinates, this would be explanatory. But I have seen programmers use the cartesian without the spherical system as well.

So, in the trig model, where exactly are the angles coming from that when broken down to the unit circle are used for translation and rotation. Is it a single angle for XZ b/c y = 0? I can figure out the rest of the trig math from here if I can imagine the proper use the angles.

my example with ‘w’ :




 if (key=='w')  //forwards
				    {
				    float xrotrad, yrotrad;
				    yrotrad = (pRot->yrot / 180 * 3.141592654f);
				    xrotrad = (pRot->xrot / 180 * 3.141592654f);
				    pPos->xpos += float(sin(yrotrad)) ;
				    pPos->zpos -= float(cos(yrotrad)) ;
				    pPos->ypos -= float(sin(xrotrad)) ;
				    }


Where do the rotation angles come from above, yrotrad, xrotrad etc. Are they more for analysis within unit circle, or do they represent a “heading”?
I think I am overthinking this, but there it is. I think I may be getting these mixed up with vectors.

yrotrad is the heading, xrotrad is the elevation (angle from horizontal). However, the change to xpos/zpos should also be multiplied by cos(xrotrad).

For the most part, the only trigonometry required for 3D graphics (and related simulation aspects) is conversion between polar and rectangular (Cartesian) coordinates.


void polar_to_rectangular(float angle, float radius, float& x, float& y)
{
    x = radius * cos(angle);
    y = radius * sin(angle);
}

void rectangular_to_polar(float x, float y, float &angle, float &radius)
{
    angle = atan2(y, x);
    radius = sqrt(x*x+y*y); // equivalent to hypot(x, y);
}

Spherical coordinates are just repeated application:


// +y is forward, +z is up
void spherical_to_rectangular(float heading, float elevation, float radius, float& x, float& y, float &z)
{
    float d;
    polar_to_rectangular(elevation, radius, d, z);
    polar_to_rectangular(heading, d, x, y);
}

If you expand out the polar_to_rectangular() calls, you’ll end up with something a lot like your existing code.

yrotrad is the heading, xrotrad is the elevation (angle from horizontal). However, the change to xpos/zpos should also be multiplied by cos(xrotrad).

Thanks for the response, it broke loose an avalanche of ideas as I thought it would. I was surprised to learn there were not 1 or 3 unit circles, but (2) for 3 Euler angles. Working out the math from that I had some questions:
0. is the unit vector from the Euler angles the “front” of the camera?

  1. which is the simplest way to “display” what’s in front of my camera now? double buffering? gluLookAt? Another way?
  2. I’ve noticed that the Euler rotations are also found in rotational matrices. I was able to calculate all of the trig “by hand” without matrices. Doesn’t make it the best way though - should I use the rotational matrices? I am eventually going to try Quats.
  3. can I derive my own rotational matrices, or this pennywise/poundfoolish.
  4. I want to implement a keyboard scheme - I have several prototypes already. What should my strategy be here on forwards/reverse/strafe - in general.

Thanks in advance for any answers you all could provide.

In terms of your code, it’s the direction you move when you press W. In the more general sense, Euler angles are just a way of representing a rotation (i.e. an orthonormal matrix). What that matrix represents depends upon how its used.

Construct matrices which rotate about the X and Y axes, and multiply them. Once you’ve done that, you can replace the movement code (the columns of the view matrix are the local X/Y/Z axes).

I’d suggest using matrices.

I’d suggest using GLM.

Whatever works. The main thing to bear in mind is that view orientation and motion orientation aren’t necessarily the same thing. E.g. if you look up or down, you may still want to rotate about the world’s vertical axis rather than the view axes (unless you’re piloting a spaceship, where there isn’t a global “up” direction).

Construct matrices which rotate about the X and Y axes, and multiply them. Once you’ve done that, you can replace the movement code (the columns of the view matrix are the local X/Y/Z axes).

Can you please expand on that? I think I understand how to make the Euler angle matrices for the angles, but I’ve lost you in bold & view matrix.

Another question:


roty(int a){
   s, c = sincos(a)
   return np.matrix([[c,0,s,0],
                     [0,1,0,0],
                     [-s,0,c,0],
                     [0,0,0,1]])
	}

I understand the math here. I believe this was written in python, just not c++. How do I write this snippet in c++?

[QUOTE=technologist;1289482]Can you please expand on that? I think I understand how to make the Euler angle matrices for the angles, but I’ve lost you in bold & view matrix.
[/QUOTE]


glm::mat4 my = glm::rotate(yrot, 0.0f, 1.0f, 0.0f);
glm::mat4 mx = glm::rotate(xrot, 1.0f, 0.0f, 0.0f);
glm::mat4 m = my * mx;

or:


glm::mat4 m = glm::mat4(1.0);
glm::rotate(m, yrot, 0.0f, 1.0f, 0.0f);
glm::rotate(m, xrot, 1.0f, 0.0f, 0.0f);

[QUOTE=technologist;1289482]
I understand the math here. I believe this was written in python, just not c++. How do I write this snippet in c++?[/QUOTE]


glm::mat4 roty(float a) {
    float s = std::sin(a);
    float c = std::cos(a);
    return glm::mat4(
           c, 0.0f,   -s, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f,  
           s, 0.0f,    c, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f);
}

Note that GLM wants the components in column-major order, while numpy uses row-major order.

Also, in the Python version (assuming that it’s my original code), sincos() converts its argument from degrees to radians, so code which uses it will be using angles in degrees (that was for compatibility with the legacy OpenGL functions; trig functions in languages or their libraries invariably use radians).

Thank you for the snippet…my hypothesis is that the result of roty(theta) customized is the same as the glm::rotation function for the same degree input. Its turning out differently. Maybe someone could please point out why??


glm::mat4 roty(float a) {
    float s = std::sin(a);
    float c = std::cos(a);
    return glm::mat4(
           c, 0.0f,   -s, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f,
           s, 0.0f,    c, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f);    
}

int main()
{
         // per function above
	glm::vec4 _vector(4.0f,5.0f,6.0f,1.0f);
	glm::mat4 _trans = glm::mat4(1.0f);
	_trans = roty(30.0f);
	_vector = _vector * _trans;
	std::cout<<glm::to_string(_vector)<<std::endl;

        // ::glm
	glm::vec4 _vector2(4.0f,5.0f,6.0f,1.0f);
	glm::mat4 _trans2 = glm::mat4(1.0f);
	_trans2 = glm::rotate(_trans2, 30.0f, glm::vec3(0,1,0));
	_vector2 = _trans2 * _vector2;
	std::cout<<glm::to_string(_vector2)<<std::endl;



Output for theta = 30 degrees(should I convert to radians for these functions?):
vec4(6.545196, 5.000000, -3.026618, 1.000000)
vec4(-5.311184, 5.000000, 4.877635, 1.000000)

Should be identical.


glm::mat4 my = glm::rotate(yrot, 0.0f, 1.0f, 0.0f);
glm::mat4 mx = glm::rotate(xrot, 1.0f, 0.0f, 0.0f);
glm::mat4 m = my * mx;

Q: I can see the logic in adding and subtracting vectors. Even multiplying them (like scale) by a scalar. What does multiplying rotational matrices my, mx(above) do, per se. What are we after with the product? What does multiplying them ‘do’?

This should be the other way around:


	_vector = _trans * _vector;

Reversing the order has the same effect as transposing the matrix:

(A.B)T=BT.AT

GLM has the same behaviour as GLSL: matrixvector treats the vector as a column vector, vectormatrix treats it as a row vector (i.e. it’s implicitly transposed to make the multiplication valid).

[QUOTE=technologist;1289509]
Q: I can see the logic in adding and subtracting vectors. Even multiplying them (like scale) by a scalar. What does multiplying rotational matrices my, mx(above) do, per se. What are we after with the product? What does multiplying them ‘do’?[/QUOTE]
A matrix represents a linear function. Multiplying matrices is equivalent to composing the functions.

Matrix multiplication is associative: (A.B).v = A.(B.v) (vectors are just matrices with a single row or column). So transforming a vector by the product of two matrices is equivalent to transforming it by the second matrix then by the first matrix.

Note that the GLM matrix generating functions (translate, rotate, scale) which take a matrix as the first parameter perform an implicit multiplication: the result is the product of the first parameter and the matrix defined by the other parameters. So e.g.


glm::mat4 m = glm::rotate(m0, angle, x, y, z);

is equivalent to


glm::mat4 m1 = glm::rotate(angle, x, y, z);
glm::mat4 m = m0 * m1;

Similarly, the legacy OpenGL matrix functions are equivalent to calling glMultMatrix() with a matrix defined by the parameters (the generated matrix is given in the function’s reference page).

[QUOTE=GClements;1289511]This should be the other way around:


	_vector = _trans * _vector;

Reversing the order has the same effect as transposing the matrix:

(A.B)T=BT.AT

GLM has the same behaviour as GLSL: matrixvector treats the vector as a column vector, vectormatrix treats it as a row vector (i.e. it’s implicitly transposed to make the multiplication valid).[/QUOTE]

Indeed:
changes:


_vector = _trans * _vector;

The result is the same:
vec4(-5.311184, 5.000000, 4.877635, 1.000000)
vec4(-5.311184, 5.000000, 4.877635, 1.000000)