I'm programming a game engine for educational purposes and I'm a bit stucked with rotations. The thing is that I have in the UI 3 squares to represent an object's orientation in Euler Angles, and from there I can change the values.
To change them, I call:
void TransformComponent::SetOrientation(glm::vec3 eulerAxAngles)
{
glm::vec3 euler_inRadians = glm::radians(eulerAxAngles);
glm::quat newRot;
double cy = cos(euler_inRadians.y * 0.5);
double sy = sin(euler_inRadians.y * 0.5);
double cp = cos(euler_inRadians.x * 0.5);
double sp = sin(euler_inRadians.x * 0.5);
double cr = cos(euler_inRadians.z * 0.5);
double sr = sin(euler_inRadians.z * 0.5);
newRot.w = cy * cp * cr + sy * sp * sr;
newRot.x = cy * cp * sr - sy * sp * cr;
newRot.y = sy * cp * sr + cy * sp * cr;
newRot.z = sy * cp * cr - cy * sp * sr;
m_Orientation = newRot * m_Orientation;
UpdateTransform();
}
m_Orientation is a quaternion storing the object's current orientation.I know all that conversion from eulers to quaternion could be handled by glm but I changed to this to debug what was happening...
Then, UpdateTransform() does the next:
void TransformComponent::UpdateTransform()
{
m_TransformationMatrix = glm::translate(glm::mat4(1.0f), m_Translation) *
glm::scale(glm::mat4(1.0f), m_Scale) * glm::mat4_cast(m_Orientation);
//Set euler angles
m_Rotation_InEulerAngles = glm::eulerAngles(m_Orientation);
}
And m_Rotation_InEulerAngles is the vec3 that can be seen from the UI to change the angles of the object's orientation.
The problem is that when I try to modify one of the 3 euler angles, the 3 are modified, I mean, if I modify the Roll angle (around Z axis), it modifies also Pitch and Yaw (not with the Z value, it's like it sets another orientation to the object).
In this gif is shown how the translation/scaling are well performed but rotation (here is seen in radians by now), is not well performed, it changes the whole orientation, not just the one in the axis I want:
https://gfycat.com/plushhonestargentineruddyduck
Does somebody sees what I'm I doing wrong?
Changing, or setting orientation, is not the same as rotating:
To set your orientation, you want to do it like this:
void setOrientation(glm::vec3 eular)
{
m_Orientation = glm::quat(eular); // very simple
}
void setRotation(glm::vec3 eular)
{
m_Rotation = glm::quat(eular); // very simple
}
To rotate, we want to integrate the rotation quaternion, using SLERP:
void update(float deltaTimeS)
{
glm::quat ident(1.0f);
glm::quat rotation = glm::mix(ident, m_Rotation, deltaTimeS);
m_Orientation = rotation * m_Orientation;
}
And to get your rotation matrix:
void setTransform()
{
m_RotationMatrix = glm::gtx::quaternion::toMat4(quaternion);
}
See here for more information on quaternion rotations.
Related
I am developing a space shooter game using OpenGL. Trying to create a thruster effect for the player's spaceship using particles. I am facing a problem where the base of the thruster is not circular under some angles of the spaceship. You can see the effect in the video.
This is the code for calculating the circular base :
float random = fmod(static_cast<float>(rand()) / 100.0, mSize);
glm::vec3 radius = (mUp * random) + (mRight * random);
float angle = (float)i / (float)mNumOfInstances * 360.0f;
float x = mPos.x + glm::cos(angle) * radius.x;
float y = mPos.y + glm::sin(angle) * radius.y;
float z = mPos.z + glm::cos(angle) * radius.z;
particle.Position = glm::vec3(x, y, z);
Can someone suggest any corrections to this code to fix the problem?
The solution was the following for anyone having the same problem:
glm::vec3 up = (mUp * random);
glm::vec3 right = (mRight * random);
particle.Position = mPos + glm::cos(angle) * up + sin(angle) * right;
mUp is the Up vector, mRight is the right vector of the spaceship, angle is the angle for the specific point (0-360) and random is a random radius because I want a filled circle.
I cannot understand the math behind this problem, I am trying to create an FPS camera where I can look freely with my mouse input.
I am trying to rotate and position my lookat point with 180 degrees of freedom. I understand the easier solution is to glRotate the world to fit my perspective, but I do not want this approach. I am fairly unfamiliar with the trigonometry involved here and cannot figure out how to solve this problem the way I want to...
here is my attempt to do this so far...
code to get mouse coordinates relative to the center of the window, then process it in my camera object
#define DEG2RAD(a) (a * (M_PI / 180.0f))//convert to radians
static void glutPassiveMotionHandler(int x, int y) {
glf centerX = WinWidth / 2; glf centerY = WinHeight / 2;//get windows origin point
f speed = 0.2f;
f oldX = mouseX; f oldY = mouseY;
mouseX = DEG2RAD(-((x - centerX)));//get distance from 0 and convert to radians
mouseY = DEG2RAD(-((y - centerY)));//get distance from 0 and convert to radians
f diffX = mouseX - oldX; f diffY = mouseY - oldY;//get difference from last frame to this frame
if (mouseX != 0 || mouseY != 0) {
mainCamera->Rotate(diffX, diffY);
}
Code to rotate the camera
void Camera::Rotate(f angleX, f angleY) {
Camera::refrence = Vector3D::NormalizeVector(Camera::refrence * cos(angleX)) + (Camera::upVector * sin(angleY));//rot up
Camera::refrence = Vector3D::NormalizeVector((Camera::refrence * cos(angleY)) - (Camera::rightVector * sin(angleX)));//rot side to side
};
Camera::refrence is our lookat point, processing the lookat point is handled as follows
void Camera::LookAt(void) {
gluLookAt(
Camera::position.x, Camera::position.y, Camera::position.z,
Camera::refrence.x, Camera::refrence.y, Camera::refrence.z,
Camera::upVector.x, Camera::upVector.y, Camera::upVector.z
);
};
The camera is defined by a position point (position) a target point (refrence) and a up-vector upVector. If you want to change the orientation of the camera, then you've to rotate the direction vector from the position (position) to the target (refrence) rather then the target point by a Rotation matrix.
Note, since the 2 angles are angles which should change an already rotated view, you've to use a rotation matrix, to rotate the vectors which point in an arbitrary direction.
Write a function which set 3x3 rotation matrix around an arbitrary axis:
void RotateMat(float m[], float angle_radians, float x, float y, float z)
{
float c = cos(angle_radians);
float s = sin(angle_radians);
m[0] = x*x*(1.0f-c)+c; m[1] = x*y*(1.0f-c)-z*s; m[2] = x*z*(1.0f-c)+y*s;
m[3] = y*x*(1.0f-c)+z*s; m[4] = y*y*(1.0f-c)+c; m[5] = y*z*(1.0f-c)-x*s;
m[6] = z*x*(1.0f-c)-y*s; m[7] = z*y*(1.0f-c)+x*s; m[8] = z*z*(1.0f-c)+c };
}
Write a function which rotates a 3 dimensional vector by the matrix:
Vector3D Rotate(float m[], const Vector3D &v)
{
Vector3D rv;
rv.x = m[0] * v.x + m[3] * v.y + m[6] * v.z;
rv.y = m[1] * v.x + m[4] * v.y + m[7] * v.z;
rv.z = m[2] * v.x + m[5] * v.y + m[8] * v.z;
return rv;
}
Calculate the vector form the position to the target:
Vector3D los = Vector3D(refrence.x - position.x, refrence.y - position.y, refrence.z - position.z);
Rotate all the vectors around the z axis of the world by angleX:
float rotX[9];
RotateMat(rotX, angleX, Vector3D(0, 0, 1));
los = Rotate(rotX, los);
upVector = Rotate(rotX, upVector);
Rotate all the vectors around the current y axis of the view by angleY:
float rotY[9];
RotateMat(rotY, angleY, Vector3D(los.x, los.y, 0.0));
los = Rotate(rotY, los);
upVector = Rotate(rotY, upVector);
Calculate the new target point:
refrence = Vector3D(position.x + los.x, position.y + los.y, position.z + los.z);
U_Cam_X_angle is left right rotation.. U_Cam_Y_angle is up down rotation.
view_radius is the view distance (zoom) to U_look_point_x, U_look_point_y and U_look_point_z.
This is ALWAYS a negative number! This is because you are always looking in positive direction. Deeper in the screen is more positive.
This is all in radians.
The last three.. eyeX, eyeY and eyeZ is where the camera is in 3D space.
This code is in VB.net. Find a converter online for VB to C++ or do it manually.
Public Sub set_eyes()
Dim sin_x, sin_y, cos_x, cos_y As Single
sin_x = Sin(U_Cam_X_angle + angle_offset)
cos_x = Cos(U_Cam_X_angle + angle_offset)
cos_y = Cos(U_Cam_Y_angle)
sin_y = Sin(U_Cam_Y_angle)
cam_y = Sin(U_Cam_Y_angle) * view_radius
cam_x = (sin_x - (1 - cos_y) * sin_x) * view_radius
cam_z = (cos_x - (1 - cos_y) * cos_x) * view_radius
Glu.gluLookAt(cam_x + U_look_point_x, cam_y + U_look_point_y, cam_z + U_look_point_z, _
U_look_point_x, U_look_point_y, U_look_point_z, 0.0F, 1.0F, 0.0F)
eyeX = cam_x + U_look_point_x
eyeY = cam_y + U_look_point_y
eyeZ = cam_z + U_look_point_z
End Sub
I'm trying to make a controllable ball in OpenGL. I'm using my own matrix class to transform the object matrix, but I can't seem to get the Rotation right. I always end up with the ball rotating around the local axis.
This is how it looks right now https://gfycat.com/LongKindBassethound . The long line are the local axis.
So when the ball moves forward the next side movement will be wrong. Theres a function in the matrix class that allows rotation around any axis:
Matrix& Matrix::rotationAxis(const Vector& Axis, float Angle) {
const float Si = sin(Angle);
const float Co = cos(Angle);
const float OMCo = 1 - Co;
Vector Ax = Axis;
Ax.normalize();
m00= (Ax.X * Ax.X) * OMCo + Co;
m01= (Ax.X * Ax.Y) * OMCo - (Ax.Z * Si);
m02= (Ax.X * Ax.Z) * OMCo + (Ax.Y * Si);
m03= 0;
m10= (Ax.Y * Ax.X) * OMCo + (Ax.Z * Si);
m11= (Ax.Y * Ax.Y) * OMCo + Co;
m12= (Ax.Y * Ax.Z) * OMCo - (Ax.X * Si);
m13= 0;
m20= (Ax.Z * Ax.X) * OMCo - (Ax.Y * Si);
m21= (Ax.Z * Ax.Y) * OMCo + (Ax.X * Si);
m22= (Ax.Z * Ax.Z) * OMCo + Co;
m23= 0;
m30= 0;
m31= 0;
m32= 0;
m33= 1;
return *this;
}
I think with this I can take the world direction vectors and transform them to the local space of the object and then rotate around the result. I don't really know how to do that though (matrix of the ball * world vector? That doesn't work). I would really like to avoid quaternions, but if I can't do that I would appreciate suggestions in that direction too.
EDIT: More Info
The transforamtion Code. As you can see I tried different methods that all do the same... So no surprise there that it doesnt work.
Matrix transM, rotX, rotZ;
rotationX = straight;
rotationZ = side;
if (velocity != Vector(0, 0, 0)) {
velocity.X = -0.0005 * DeltaTime;
velocity.X = clamp(velocity.X, 0, FLT_MAX);
velocity.Z = -0.0005 * DeltaTime;
velocity.Z = clamp(velocity.Z, 0, FLT_MAX);
}
velocity.X += speed * -side * DeltaTime;
velocity.Z += speed * straight * DeltaTime;
transM.translation(velocity.X, 0, velocity.Z);
if (velocity.Z != 0 || velocity.X != 0) {
//http://mathworld.wolfram.com/EulerAngles.html
//http://gamedev.stackexchange.com/questions/67199/how-to-rotate-an-object-around-world-aligned-axes
Vector localAxisX = m_Ball * Vector(1, 0, 0);
Vector localAxisZ = m_Ball * Vector(0, 0, 1);
rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime);
rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime);
//rotX.rotationX(0.5* M_PI * straight * DeltaTime * 3);
//rotZ.rotationZ(0.5* M_PI * side * DeltaTime * 3);
//Matrix fullRotation.rotationYawPitchRoll(Vector(0, 0.5* M_PI * straight * DeltaTime, 0.5* M_PI * side * DeltaTime));
m_Ball = transM * m_Ball * (rotX*rotZ);
}
else {
m_Ball = transM * m_Ball;
}
Draw code with my previous attempt trying to use glRotatef (obviously commented out right now)
void Ball::draw(float DeltaTime) {
glPushMatrix();
glMultMatrixf(m_Ball);
if(rotationX)
glRotatef(0.5* M_PI * rotationX * DeltaTime, 1.0, 0.0, 0.0);
if(rotationZ)
glRotatef(0.5* M_PI * rotationZ * DeltaTime, 0.0, 0.0, 1.0);
g_Model_ball.drawTriangles();
glPopMatrix();
drawAxis();
}
I highly suggest using quaternions to easily handle compound rotations and avoid gimbal lock.
https://en.wikipedia.org/wiki/Gimbal_lock
Ok with regards to your comments and video, You want to rotate around the ball's center. It seems you accumulate your rotations in m_Ball but do a weird transM multiplication. Also you are probably accumulating translations in transM.
Try not to mix your translations and rotations and avoid accumulating them in your m_Ball. You can do something like this.
//transformation matrix
transM.translation(velocity.X, 0, velocity.Z);
//accumulate translations in m_BallT
m_BallT = transM * m_BallT;
//final Rotation
Matrix rotation = rotX * rotZ;
//accumulate rotations in m_BallR
m_BallR = rotation * m_BallR;
//now compute your final transformation matrix from accumulated rotations and translation
m_Ball = m_BallT * m_BallR;
note how m_BallR is just rotations accumulated. Post multiplication will ensure new rotation is applied after accumulated rotations in m_BallR. Finally translate to the final position accumulated in m_BallT. Your ball will rotate about its center and move according to m_BallT.
You could also simply replace the transformation component on your m_BallR to avoid extra matrix multiplications.
Vector newPos(m_Ball.translation().X + velocity.X, terrainNoise.GetHeight(m_Ball.translation().X, m_Ball.translation().Z) + 0.5, m_Ball.translation().Z + velocity.Z);
rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime * abs(velocity.Z) * 100);
rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime * abs(velocity.X) * 100);
m_Rotation = (rotX*rotZ);
m_Ball = (m_Ball.invert() * m_Rotation).invert();
m_Ball.m03 = newPos.X;
m_Ball.m13 = newPos.Y;
m_Ball.m23 = newPos.Z;
This is the solution I came up with after reading this link provided by #Spektre. Basically you just invert the ModelMatrix of the ball to get it into world position, do your rotation and then transform it back into local space.
You have to set the newPos Vector before you rotate, otherwise it would affect future transformations.
Is there a way to calculate the XYZ position in front of a quaternion (XYZW) rotation, preferably using GLM?
I know the Quat rotation and the Position of the object I want to calculate the position in front of.
I know how to calculate the position in front of a rotation matrix where you have a Front vector, Up vector and Right vector, but in this case I only have XYZW values (where W is always 0, I never see it becomming 1..?)
In very short:
The data I have: Quat (X Y Z W) and Position(X Y Z) and I want to calculate PositionInFront(Position, Quat, Distance, &X, &Y, &Z)
How to accomplish this goal?
I tried a cast to 3x3matrix and perform the Up,Right,Front (because a 3x3 matrix cast is these values, right?) calculations but they do not return the correct positions.
Or would it be possible to determine the objects Z Angle? (rotation around world Z / height axis only)
It seemed that there were 2 more quaternion structures for the vehicle which I forgot to use. and those 3 are the complete set needed for the Front,Right,Up calculation formula:
float offX = 10.0f;
float offY = 0.0f;
float offZ = 0.0f;
float x = offX * info.Rotation.Front.x + offY * info.Rotation.Right.x + offZ * info.Rotation.Up.x + info.Pos.x;
float y = offX * info.Rotation.Front.y + offY * info.Rotation.Right.y + offZ * info.Rotation.Up.y + info.Pos.y;
float z = offX * info.Rotation.Front.z + offY * info.Rotation.Right.z + offZ * info.Rotation.Up.z + info.Pos.z;
float Angle = (atan2(x-info.Pos.x, y-info.Pos.y) * 180.0f / PI);
So I'm trying to figure out how to mannually create a camera class that creates a local frame for camera transformations. I've created a player object based on OpenGL SuperBible's GLFrame class.
I got keyboard keys mapped to the MoveUp, MoveRight and MoveForward functions and the horizontal and vertical mouse movements are mapped to the xRot variable and rotateLocalY function. This is done to create a FPS style camera.
The problem however is in the RotateLocalY. Translation works fine and so does the vertical mouse movement but the horizontal movement scales all my objects down or up in a weird way. Besides the scaling, the rotation also seems to restrict itself to 180 degrees and rotates around the world origin (0.0) instead of my player's local position.
I figured that the scaling had something to do with normalizing vectors but the GLframe class (which I used for reference) never normalized any vectors and that class works just fine. Normalizing most of my vectors only solved the scaling and all the other problems were still there so I'm figuring one piece of code is causing all these problems?
I can't seem to figure out where the problem lies, I'll post all the appropriate code here and a screenshot to show the scaling.
Player object
Player::Player()
{
location[0] = 0.0f; location[1] = 0.0f; location[2] = 0.0f;
up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f;
forward[0] = 0.0f; forward[1] = 0.0f; forward[2] = -1.0f;
}
// Does all the camera transformation. Should be called before scene rendering!
void Player::ApplyTransform()
{
M3DMatrix44f cameraMatrix;
this->getTransformationMatrix(cameraMatrix);
glRotatef(xAngle, 1.0f, 0.0f, 0.0f);
glMultMatrixf(cameraMatrix);
}
void Player::MoveForward(GLfloat delta)
{
location[0] += forward[0] * delta;
location[1] += forward[1] * delta;
location[2] += forward[2] * delta;
}
void Player::MoveUp(GLfloat delta)
{
location[0] += up[0] * delta;
location[1] += up[1] * delta;
location[2] += up[2] * delta;
}
void Player::MoveRight(GLfloat delta)
{
// Get X axis vector first via cross product
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, forward);
location[0] += xAxis[0] * delta;
location[1] += xAxis[1] * delta;
location[2] += xAxis[2] * delta;
}
void Player::RotateLocalY(GLfloat angle)
{
// Calculate a rotation matrix first
M3DMatrix44f rotationMatrix;
// Rotate around the up vector
m3dRotationMatrix44(rotationMatrix, angle, up[0], up[1], up[2]); // Use up vector to get correct rotations even with multiple rotations used.
// Get new forward vector out of the rotation matrix
M3DVector3f newForward;
newForward[0] = rotationMatrix[0] * forward[0] + rotationMatrix[4] * forward[1] + rotationMatrix[8] * forward[2];
newForward[1] = rotationMatrix[1] * forward[1] + rotationMatrix[5] * forward[1] + rotationMatrix[9] * forward[2];
newForward[2] = rotationMatrix[2] * forward[2] + rotationMatrix[6] * forward[1] + rotationMatrix[10] * forward[2];
m3dCopyVector3(forward, newForward);
}
void Player::getTransformationMatrix(M3DMatrix44f matrix)
{
// Get Z axis (Z axis is reversed with camera transformations)
M3DVector3f zAxis;
zAxis[0] = -forward[0];
zAxis[1] = -forward[1];
zAxis[2] = -forward[2];
// Get X axis
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, zAxis);
// Fill in X column in transformation matrix
m3dSetMatrixColumn44(matrix, xAxis, 0); // first column
matrix[3] = 0.0f; // Set 4th value to 0
// Fill in the Y column
m3dSetMatrixColumn44(matrix, up, 1); // 2nd column
matrix[7] = 0.0f;
// Fill in the Z column
m3dSetMatrixColumn44(matrix, zAxis, 2); // 3rd column
matrix[11] = 0.0f;
// Do the translation
M3DVector3f negativeLocation; // Required for camera transform (right handed OpenGL system. Looking down negative Z axis)
negativeLocation[0] = -location[0];
negativeLocation[1] = -location[1];
negativeLocation[2] = -location[2];
m3dSetMatrixColumn44(matrix, negativeLocation, 3); // 4th column
matrix[15] = 1.0f;
}
Player object header
class Player
{
public:
//////////////////////////////////////
// Variables
M3DVector3f location;
M3DVector3f up;
M3DVector3f forward;
GLfloat xAngle; // Used for FPS divided X angle rotation (can't combine yaw and pitch since we'll also get a Roll which we don't want for FPS)
/////////////////////////////////////
// Functions
Player();
void ApplyTransform();
void MoveForward(GLfloat delta);
void MoveUp(GLfloat delta);
void MoveRight(GLfloat delta);
void RotateLocalY(GLfloat angle); // Only need rotation on local axis for FPS camera style. Then a translation on world X axis. (done in apply transform)
private:
void getTransformationMatrix(M3DMatrix44f matrix);
};
Applying transformations
// Clear screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Apply camera transforms
player.ApplyTransform();
// Set up lights
...
// Use shaders
...
// Render the scene
RenderScene();
// Do post rendering operations
glutSwapBuffers();
and mouse
float mouseSensitivity = 500.0f;
float horizontal = (width / 2) - mouseX;
float vertical = (height / 2) - mouseY;
horizontal /= mouseSensitivity;
vertical /= (mouseSensitivity / 25);
player.xAngle += -vertical;
player.RotateLocalY(horizontal);
glutWarpPointer((width / 2), (height / 2));
Honestly I think you are taking a way to complicated approach to your problem. There are many ways to create a camera. My favorite is using a R3-Vector and a Quaternion, but you could also work with a R3-Vector and two floats (pitch and yaw).
The setup with two angles is simple:
glLoadIdentity();
glTranslatef(-pos[0], -pos[1], -pos[2]);
glRotatef(-yaw, 0.0f, 0.0f, 1.0f);
glRotatef(-pitch, 0.0f, 1.0f, 0.0f);
The tricky part now is moving the camera. You must do something along the lines of:
flaot ds = speed * dt;
position += tranform_y(pich, tranform_z(yaw, Vector3(ds, 0, 0)));
How to do the transforms, I would have to look that up, but you could to it by using a rotation matrix
Rotation is trivial, just add or subtract from the pitch and yaw values.
I like using a quaternion for the orientation because it is general and thus you have a camera (any entity that is) that independent of any movement scheme. In this case you have a camera that looks like so:
class Camera
{
public:
// lots of stuff omitted
void setup();
void move_local(Vector3f value);
void rotate(float dy, float dz);
private:
mx::Vector3f position;
mx::Quaternionf orientation;
};
Then the setup code uses shamelessly gluLookAt; you could make a transformation matrix out of it, but I never got it to work right.
void Camera::setup()
{
// projection related stuff
mx::Vector3f eye = position;
mx::Vector3f forward = mx::transform(orientation, mx::Vector3f(1, 0, 0));
mx::Vector3f center = eye + forward;
mx::Vector3f up = mx::transform(orientation, mx::Vector3f(0, 0, 1));
gluLookAt(eye(0), eye(1), eye(2), center(0), center(1), center(2), up(0), up(1), up(2));
}
Moving the camera in local frame is also simple:
void Camera::move_local(Vector3f value)
{
position += mx::transform(orientation, value);
}
The rotation is also straight forward.
void Camera::rotate(float dy, float dz)
{
mx::Quaternionf o = orientation;
o = mx::axis_angle_to_quaternion(horizontal, mx::Vector3f(0, 0, 1)) * o;
o = o * mx::axis_angle_to_quaternion(vertical, mx::Vector3f(0, 1, 0));
orientation = o;
}
(Shameless plug):
If you are asking what math library I use, it is mathex. I wrote it...