Quaternion from Euler angles and Quaternion Vector multiplication glitching out - c++

I've been working on a project for some time and Needed something that could from a Vector3 representing rotation in the XYZ axis make Forward, Right and Up vectors. I was looking through a lot of stuff and after some time I figured out I had to implement Quaternions (I have my own Math Libary but this same thing happened with glm) and Here is the code for calculating the Forward Vector: (quaternion is the rotation Quaternion member in my class and Quaternion::Euler is a static function that returns a Quaternion from Euler Angles)
quaternion = Quaternion::Euler(rotation);
Vector3 ret = quaternion * Vector3(0.0f, 0.0f, 1.0f);
when the rotation is 0, 0, 0 the function returns 0, 0, 1 as it should, but if I try something like 0, 180, 0 it should return 0, 0, -1, but instead I get -8.74228e-08, 0, -1. After some investigation I figured out that the Quaternion::Euler function returns a Quaternion where the w part is messed up. In the case where the rotation is 0, 180, 0 the Quaternion the Quaternion::Euler function returns is 0, 1, 0, -4.37113883e-08 which is almost exactly half of the random number the Forward functions returns. Here is Quaternion::Euler:
float x = Radians(euler.x);
float y = Radians(euler.y);
float z = Radians(euler.z);
x = x / 2;
y = y / 2;
z = z / 2;
return Quaternion(cos(z) * cos(y) * sin(x) - sin(z) * sin(y) * cos(x), //X
cos(z) * sin(y) * cos(x) + sin(z) * cos(y) * sin(x), //Y
sin(z) * cos(y) * cos(x) - cos(z) * sin(y) * sin(x), //Z
cos(z) * cos(y) * cos(x) + sin(z) * sin(y) * sin(x));//W
and Honestly, I stole this function from an article of a guy that was making his own Math Engine, in his case this seemed to work. Here is the Quaternion Vector Multiplication function, that I "borrowed" from the Unity Implementation: (in this case it's inside the Quaternion struct so this is a pointer to the quaternion from the Quaternion Vector multiplication)
inline Vector3 operator*(const Vector3& other) {
float x = this->x * 2.0f;
float y = this->y * 2.0f;
float z = this->z * 2.0f;
float xx = this->x * x;
float yy = this->y * y;
float zz = this->z * z;
float xy = this->x * y;
float xz = this->x * z;
float yz = this->y * z;
float wx = this->w * x;
float wy = this->w * y;
float wz = this->w * z;
Vector3 ret;
ret.x = (1.0f - (yy + zz)) * other.x + (xy - wz) * other.y + (xz + wy) * other.z;
ret.y = (xy + wz) * other.x + (1.0f - (xx + zz)) * other.y + (yz - wx) * other.z;
ret.z = (xz - wy) * other.x + (yz + wx) * other.y + (1.0f - (xx + yy)) * other.z;
return ret;
}
Does anyone know what might be wrong ? I tried to do this with glm:
glm::quat quat(glm::vec3(glm::radians(rotation.x), glm::radians(rotation.y), glm::radians(rotation.z)));
glm::vec3 v = quat * glm::vec3(0.0f, 0.0f, 1.0f);
but it's the same thing, the vector is the same and the quaternion is the same too, I've been reading into things a lot about this and couldn't find a fix, always when I tried to search implementation for the Quaternio::Euler function it just came up with how to use a math library. It would be best if the solution wouldn't require me to use glm, because I have to use my own Math Library, but honestly I will try anything to at least understand what is wrong.

Related

rotating bone with quaternion issue

I need to rotate bones of skeleton, i have already the quaterinion corresponding for each joints; and i am confused when it comes on rotating.
Skeleton to move is my opengl scene i need to move.
My problem is that i can't rotate the joint; Can anyone Help
Bellow is my code
//i evaluate each joint to get the translation and rotation.
void Node::EvaluatePoi(std::vector<POI> pois, const Vector &par_pos,
const Quaternion &par_rot, Vector Node::*world_pos,std::vector<Arti> joints)
{ Vector poi=this->PoiVec ;
Quaternion rot;
if (pois.empty()){
this->*world_pos= this->rest_position ;//OFFSET
rot= this-> rest_rotation ;//identity
}else{
if(this->name=="Hips")
{
this->*world_pos = eval_instant_positionPOI(poi);
rot= this-> rest_rotation ;// do not rotate
}else if(this->name=="LeftUpLeg")
{
this->*world_pos = this->rest_position;// set to OFFSET
rot= this-> rest_rotation ;// do not rotate
}else if(this->name=="RightUpLeg")
{
this->*world_pos = this->rest_position;
rot= this-> rest_rotation ;
}
else
{
this->*world_pos= this->rest_position;
rot= eval_instant_rotationPOI(joints);
}
}
//Applying transformation on the global position with rot =qparent * qchild
(this->*world_pos).rotate(par_rot);
this->*world_pos += par_pos;
rot = par_rot * rot;
// draw joint's subhierarchy
for (int i = 0; i < n_children; i++)
child[i]->EvaluatePoi(pois, this->*world_pos, rot, world_pos,joints);
}
EDIT:
//here i get the local rotation of each joint, after that create quaternions equivalent to individual Euler rotations and then compose to one rotation
Vector x_vector(1.0, 0.0, 0.0),
y_vector(0.0, 1.0, 0.0),
z_vector(0.0, 0.0, 1.0);
Quaternion Node::eval_instant_rotationPOI( std::vector<Arti> joints)
{
Quaternion roto;//= new Quaternion();
Quaternion sample;
double t= 0.02;
Vector v;
Vector Euler(0,0,0);;
string x =this->name;
if(x== "Head"){
Euler=GetEulers(joints,JOINT_HEAD);
}else if(x== "Neck"){
Euler=GetEulers(joints,JOINT_NECK);
}
else if(x== "LeftUpArm"){
Euler=GetEulers(joints,JOINT_LEFT_SHOULDER);
}
else if(x== "RightUpArm"){
Euler=GetEulers(joints,JOINT_RIGHT_SHOULDER);
}
else if(x== "LeftLowArm"){
Euler=GetEulers(joints,JOINT_LEFT_ELBOW);
}
else if(x== "LeftHand"){
Euler=GetEulers(joints,JOINT_LEFT_HAND);
}
else if(x== "RightLowArm"){
Euler=GetEulers(joints,JOINT_RIGHT_ELBOW);
}
else if(x== "RightHand"){
Euler=GetEulers(joints,JOINT_RIGHT_HAND);
}
else if(x== "Hips"){
Euler=GetEulers(joints,JOINT_TORSO);
}
else if(x== "LeftUpLeg"){
Euler=GetEulers(joints,JOINT_LEFT_HIP);
}
else if(x== "RightUpLeg"){
Euler=GetEulers(joints,JOINT_RIGHT_HIP);
}
else if(x== "LeftLowLeg"){
Euler=GetEulers(joints,JOINT_LEFT_KNEE);
}
else if(x== "LeftFoot"){
Euler=GetEulers(joints,JOINT_LEFT_FOOT);
}
else if(x== "RightLowLeg"){
Euler=GetEulers(joints,JOINT_RIGHT_KNEE);
}
else if(x== "RightFoot"){
Euler=GetEulers(joints,JOINT_RIGHT_FOOT);
}
Quaternion qx(x_vector, (Euler.x ));
Quaternion qy(y_vector, (Euler.y ));
Quaternion qz(z_vector, (Euler.z ));
sample = qz * qy * qx;
roto= slerp(qTemp, sample, t);
qTemp=roto;
return roto ;
}
/*here i multiply the joint and its parent to get the Euler Angle ; is it necessary to convert to
Euler Angle?/
Vector Node::GetEulers(std::vector<Arti> joints, const int idx) {
// Get the quaternion of its parent.
Quaternion q_parent;
Quaternion q_current;
if (idx == JOINT_TORSO) {
q_parent.identity();
}
/////
{
q_parent = Quaternion(joints[parent_joint_map[idx]].quat.x,
joints[parent_joint_map[idx]].quat.y,
joints[parent_joint_map[idx]].quat.z,
joints[parent_joint_map[idx]].quat.w);
}
// Get the quaternion of the joint.
q_current = Quaternion(joints[idx].quat.x, joints[idx].quat.y,
joints[idx].quat.z, joints[idx].quat.w);
// Calculate the relative quaternion.
Quaternion q_delta = quat_left_multiply(q_current , quat_inverse(q_parent));
Vector angle = euler_from_quat(q_delta);
// cout<<this->name<<" "<<angle<<" ";
return angle;
}
Quaternion quat_left_multiply(Quaternion l, Quaternion r) {
Quaternion q = {r.w * l.x + r.x * l.w + r.y * l.z - r.z * l.y,
r.w * l.y + r.y * l.w + r.z * l.x - r.x * l.z,
r.w * l.z + r.z * l.w + r.x * l.y - r.y * l.x,
r.w * l.w - r.x * l.x - r.y * l.y - r.z * l.z};
return q;
}
Vector& Vector::rotate(const Quaternion& q)
{
Quaternion p(x, y, z, 0.0f);
Quaternion qc(q);
qc.conjugate();
Quaternion pp(q * p * qc);
x = pp.x;
y = pp.y;
z = pp.z;
return *this;
}
Rotating a quaternion is actually multiplying a quaternion by another. Given the quaternion qA representing the current rotation of an object and qB the quaternion representing the amount of rotation to apply (to add) to this object, the resulting new rotation of this object is computed as follow (pseudocode):
qA = qA * qB;
Alternatively, you can apply (add) this rotation in what is called "object" or "local" transformation space by swapping operands:
qA = qB * qA
Each joint should hold (usualy as class member) a quaternion representing its current rotation in local space. This is probably what you already done. If you want to apply a rotation to that joint, then you simply need multiply the joint quaternion, by another quaternion representing the amount of rotation to apply. A quaterion rotation method can be like this (pseudocode):
Joint::Rotate(const quaterion& amount, bool local)
{
if(local) {
this->rotation = amount * this->rotation;
} else {
this->rotation = this->rotation * amount;
}
this->rotation.normalize();
}
That is all you need for the rotation part, nothing else. After that, you will need to convert the joint quaternion to a rotation matrix, so to be combined with the other joint transformations (translation, scale, whatever). Here is one implementation of the quaternion to rotation matrix conversion (pseudocode):
Matrix3 QuaternionToMatrix(const quaternion& q)
{
float x2 = q.x + q.x;
float y2 = q.y + q.y;
float z2 = q.z + q.z;
float xx = q.x * x2;
float xy = q.x * y2;
float xz = q.x * z2;
float yy = q.y * y2;
float yz = q.y * z2;
float zz = q.z * z2;
float wx = q.w * x2;
float wy = q.w * y2;
float wz = q.w * z2;
Matrix3 m; //< 3x3 matrix
m[0] = (1.0f - (yy + zz));
m[1] = (xy - wz);
m[2] = (xz + wy);
m[3] = (xy + wz);
m[4] = (1.0f - (xx + zz));
m[5] = (yz - wx);
m[6] = (xz - wy);
m[7] = (yz + wx);
m[8] = (1.0f - (xx + yy));
return m;
}
What you may finally need is to input rotation using Euler angles instead of quaternion values. Indeed, Euler angles are easier to handle and understand when it come to apply a rotation in a human point of view. In this case, you'll need to convert the input Euler angles to a quaternion. Here is one possible implementation of Euler angle to quaternion conversion:
Quaternion EulerToQuaternion(float x, float y, float z)
{
float sx = sinf(x * -0.5f);
float cx = cosf(x * -0.5f);
float sy = sinf(y * -0.5f);
float cy = cosf(y * -0.5f);
float sz = sinf(z * -0.5f);
float cz = cosf(z * -0.5f);
Quaternion q;
q.x = sx * cy * cz + cx * sy * sz;
q.y = cx * sy * cz - sx * cy * sz;
q.z = cx * cy * sz + sx * sy * cz;
q.w = cx * cy * cz - sx * sy * sz;
return q;
}

Problems rotating opengl camera

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

Calculate position and rotation of camera with mouse events

My plan:
1. Calculate mouse direction [x, y] [success]
I my Mouse Move event:
int directionX = lastPosition.x - position.x;
int directionY = lastPosition.y - position.y;
2. Calculate angles [theta, phi] [success]
float theta = fmod(lastTheta + sensibility * directionY, M_PI);
float phi = fmod(lastPhi + sensibility * directionX * -1, M_PI * 2);
Edit {
bug fix:
float theta = lastTheta + sensibility * directionY * -1;
if (theta < M_PI / -2)theta = M_PI / -2;
else if (theta > M_PI / 2)theta = M_PI / 2;
float phi = fmod(lastPhi + sensibility * directionX * -1, M_PI * 2);
}
Now I have given theta, phi, the centerpoint and the radius and I want to calculate the position and the rotation [that the camera look at the centerpoint]
3. Calculate position coordinates [X,Y,Z] [failed]
float newX = radius * sin(phi) * cos(theta);
float newY = radius * sin(phi) * sin(theta);
float newZ = radius * cos(phi);
Solution [by meowgoesthedog]:
float newX = radius * cos(theta) * cos(phi);
float newY = radius * sin(theta);
float newZ = radius * cos(theta) * sin(phi);
4. Calculate rotation [failed]
float pitch = ?;
float yaw = ?;
Solution [by meowgoesthedog]:
float pitch = -theta;
float yaw = -phi;
Thanks for your solutions!
Your attempt was almost (kinda) correct:
As the diagram shows, in OpenGL the "vertical" direction is conventionally taken to be Y, whereas your formulas assume it is Z
phi and theta are in the wrong order
Very simple conversion: yaw = -phi, pitch = -theta (from the perspective of the camera)
Fixed formulas:
float position_X = radius * cos(theta) * cos(phi);
float position_Y = radius * sin(theta);
float position_Z = radius * cos(theta) * sin(phi);
(There may also be some sign issues with the mouse deltas but they should be easy to fix.)

How to prevent gimbal lock using matrices in OpenGL?

I understand that both Euler and Quaternion rotation types have their own distinctive quirks, however the problem that I'm having is that (for example) when performing the following rotations to an object:
rotateX = 90.0
rotateY = 90.0
... Oh, hang on a minute... now the X and Z axis are basically the same!
See, what I want is to rotate a cube say 90 degrees X, 90 degrees Y and still have all axis points back in their original position as opposed of rotating locally.
Any code examples would be ideal - Here is the code I'm currently using:
_model = scale(_scale) *
translate(_position) *
( rotate(_rotation.data[0], 1.0f, 0.0f, 0.0f) *
rotate(_rotation.data[1], 0.0f, 1.0f, 0.0f) *
rotate(_rotation.data[2], 0.0f, 0.0f, 1.0f) );
I have a Math.h that calculates the rotations like so:
template <typename T>
static inline Tmat4<T> rotate(T angle, T x, T y, T z)
{
Tmat4<T> result;
const T x2 = x * x;
const T y2 = y * y;
const T z2 = z * z;
float rads = float(angle) * 0.0174532925f;
const float c = cosf(rads);
const float s = sinf(rads);
const float omc = 1.0f - c;
result[0] = Tvec4<T>(T(x2 * omc + c), T(y * x * omc + z * s), T(x * z * omc - y * s), T(0));
result[1] = Tvec4<T>(T(x * y * omc - z * s), T(y2 * omc + c), T(y * z * omc + x * s), T(0));
result[2] = Tvec4<T>(T(x * z * omc + y * s), T(y * z * omc - x * s), T(z2 * omc + c), T(0));
result[3] = Tvec4<T>(T(0), T(0), T(0), T(1));
return result;
}

Rotation: Quaternion to matrix

I am trying to display a 360 panorama using an IMU for head tracking.
Yaw works correctly but the roll and pitch are reverse. I also notice that the pitch contains some roll (and maybe vice-versa).
I am receiving (W, X, Y, Z) coordinate from the IMU that I am storing in an array as X, Y, Z, W.
The next step is converting the quaternion to a rotation matrix. I have looked at many examples, and can't seem to find anything wrong with the following code:
static GLfloat rotation[16];
// Quaternion (x, y, z, w)
static void quaternionToRotation(float* quaternion)
{
// Normalize quaternion
float magnitude = sqrt(quaternion[0] * quaternion[0] +
quaternion[1] * quaternion[1] +
quaternion[2] * quaternion[2] +
quaternion[3] * quaternion[3]);
for (int i = 0; i < 4; ++i)
{
quaternion[i] /= magnitude;
}
double xx = quaternion[0] * quaternion[0], xy = quaternion[0] * quaternion[1],
xz = quaternion[0] * quaternion[2], xw = quaternion[0] * quaternion[3];
double yy = quaternion[1] * quaternion[1], yz = quaternion[1] * quaternion[2],
yw = quaternion[1] * quaternion[3];
double zz = quaternion[2] * quaternion[2], zw = quaternion[2] * quaternion[3];
// Column major order
rotation[0] = 1.0f - 2.0f * (yy + zz);
rotation[1] = 2.0f * (xy - zw);
rotation[2] = 2.0f * (xz + yw);
rotation[3] = 0;
rotation[4] = 2.0f * (xy + zw);
rotation[5] = 1.0f - 2.0f * (xx + zz);
rotation[6] = 2.0f * (yz - xw);
rotation[7] = 0;
rotation[8] = 2.0f * (xz - yw);
rotation[9] = 2.0f * (yz + xw);
rotation[10] = 1.0f - 2.0f * (xx + yy);
rotation[11] = 0;
rotation[12] = 0;
rotation[13] = 0;
rotation[14] = 0;
rotation[15] = 1;
}
The rotation matrix is then used in the draw call as such:
static void draw()
{
// Get IMU quaternion
float* quaternion = tracker.getTrackingData();
if (quaternion != NULL)
{
quaternionToRotation(quaternion);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
// TODO: Multiply initialRotation quaternion with IMU quaternion
glMultMatrixf(initialRotation); // Initial rotation to point forward
glMultMatrixf(rotation); // Rotation based on IMU
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
gluSphere(quad, 0.1, 50, 50);
glBindTexture(GL_TEXTURE_2D, 0);
glPopMatrix();
glFlush();
glutSwapBuffers();
}
I tried to set all but one fields in the quaternion to 0, and I notice that they all work individually, except roll and pitch is swapped around. I tried swapping X and Y but this does not seem to help.
Any help would be really appreciated. Please let me know as well if you have any steps that can let me debug my issue. Thanks!