2
\$\begingroup\$

I'm currently working on a small Voxel-Editor Project. In the editor, I plan on using an Arc-Ball camera which rotate around the model. My camera is currently working but in an odd way. I can't seem to be able to move around the Z-Axis on my model which cause my camera angle to get smaller as I reach one side. For example, if I go to the left side of my ball, I wont be able to turn around it completly.

Here is my code which update my current View-Matrix into the updated one.

void t_Math::CreateLookAt(glm::mat4& InMatrix, sf::Vector3f pointToFix,   GLfloat InPitch, GLfloat InYaw, GLfloat valueToScale)
{

    glm::mat4 rotationMatrix = glm::mat4(1.0);

    const GLfloat FinalPitchDegree = InPitch / 57.2958;
    const GLfloat FinalYawDegree = InYaw / 57.2958;

    rotationMatrix = glm::rotate(rotationMatrix, FinalPitchDegree,                               glm::vec3(1.0, 0.0, 0.0));
    rotationMatrix = glm::rotate(rotationMatrix, FinalYawDegree, glm::vec3(0.0, 1.0, 0.0));

    const glm::vec4 cameraPosition4 =  rotationMatrix * glm::vec4(1.0);
    const glm::vec3 cameraPositionFinal = valueToScale * glm::vec3(cameraPosition4);

    const glm::vec3 constObjetMiddle = glm::vec3(pointToFix.x, pointToFix.y, pointToFix.z);

    InMatrix = glm::lookAt(cameraPositionFinal, constObjetMiddle,         glm::vec3(0.0, 1.0, 0.0));
}

Here is what I believe my code do.

The function receive a View Matrix, The model Center Coords, The Pitch, The Yaw and finally the Scalar (The zoom). Note: My Pitch and Yaw are simple GLFloat which mean they don't have limits and can go from -R to +R.

Now for the code. -We first create a base Matrix 4 -We Divide the Pitch and Yaw by 57.2958 (a radian) because GLM::rotate works with radian and I want it to be precise so in degree. -We then rotate the base Matrix on their respective Axis using the proper Pitch and Yaw -We then calculate the position of the Camera by using the ROTATED matrix which we transform into a Vec4 -We calculate the final Camera position by multiplying by the Scaling Vector (The Zoom) and we transform this Vector4 into a Vec3 -We create another Vec3 which is the Camera Target by using the value received by the function -We use all we collected to create a new lookAt matrix and we apply it to the view matrix.

I think I might be missing the Z-Axis rotation but how am I suppose to calculate it ? Should I use the Pitch or the Yaw ? Since i'm using (W-A-S-D) to move around here is what my keys does.

W: Increase Pitch S: Decrease Pitch D: Increase Yaw A: Decrease Yaw

Do I need to put the Roll into this and which keys should increase and decrease it ?

\$\endgroup\$
  • \$\begingroup\$ Try to use Q and E, those are the szabdard key fo keyboard camera rotation. \$\endgroup\$ – Bálint Feb 8 '16 at 7:06
1
\$\begingroup\$

There are a few issues that are preventing your code from behaving like you expect.

First of all, your pitch and yaw rotations are applied in the wrong order. Remember non-coplanar 3D rotations are non-commutative, so euler angle (yaw/pitch/roll) rotations need to be applied in the proper order. So your rotation matrix should be constructed like this:

rotationMatrix = glm::rotate(rotationMatrix, FinalYawDegree, glm::vec3(0.0, 1.0, 0.0));
rotationMatrix = glm::rotate(rotationMatrix, FinalPitchDegree, glm::vec3(1.0, 0.0, 0.0));

Note that you used the X and Y axes for pitch and yaw. That means roll is a rotation around the Z axis, which means the camera is "looking down" the Z axis. So if yaw and pitch are 0 (ie. the rotation matrix is the identity matrix), the camera's position should be on the Z axis. But in your code, it ends up being at (1, 1, 1):

// vec4(1.0) is equivalent to vec4(1.0, 1.0, 1.0, 1.0)
const glm::vec4 cameraPosition4 =  rotationMatrix * glm::vec4(1.0);

What you want is something like this:

const glm::vec4 cameraPosition4 =  rotationMatrix * glm::vec4(0.0, 0.0, 1.0, 1.0);

Finally if your intent is for the camera to "orbit" the target position, then your code will only work like this when the target position is the origin. Otherwise the camera will orbit the origin, but look off at something else. You probably want:

cameraPositionFinal += constObjetMiddle; // what's an objet?
InMatrix = glm::lookAt(cameraPositionFinal, constObjetMiddle, glm::vec3(0.0, 1.0, 0.0));

After these changes your code will mostly work. Since you're using glm::lookAt with a fixed up vector, you're not going to be able to add roll to the camera, except by creating a separate rotation matrix for it and combining it with the result of lookAt by multiplication. It's not clear whether or not this is something that you wanted or not. Another consequence of using lookAt is that when the pitch causes the camera to cross the up vector "poles" the view will appear to flip.

There are also a variety of code quality issues which may or may not be important, depending on where this project fits on a continuum from "really rough prototype" to "production-ready library".

  • Consider using glm::radians and glm::degrees instead of magic literal constants. This makes your intent much clearer, in addition to being less error prone and often more accurate. Note also that older versions of GLM's GTC_matrix_transform took angles in degrees by default, but newer versions expect radians by default throughout the entire library.
  • glm::lookAt isn't the most efficient way to generate a view matrix in situations like yours, where you're just generating the camera position from the target position (or vice-versa). It would be more efficient and clearer to just compose the view matrix directly. GLM's GTX_euler_angles provides convenient methods for this. For example:

    glm::mat4 view = glm::translate(glm::mat4(), glm::vec3(0, 0, -10.f))
            * glm::eulerAngleXY(pitch_in_radians, yaw_in_radians)
    
  • Try to use a consistent naming scheme. This is often more difficult in C++ than other popular languages where libraries have mostly standardized on a single style, but it's still good practice to be consistent in your own code. You have some parameters and variables in PascalCase, while others are in camelCase. Try to avoid naming functions, variables, and parameters with PascalCase. Outside of C# (and Pascal, of course) capitalized identifiers are usually reserved for types. Additionally InMatrix is a confusing name, considering this is what would be known as an out-parameter in C#. In C++ it's typical not to clutter up names with such metadata. Instead just assume that if a function takes a non-const reference-type parameter T& param, it plans to modify it.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.