Rotating a RigidBody around a pivot point - c++

I'm trying to rotate a rigidbody around a pivot point (in this case the origin), rather than its center of mass.
I had a suggestion to apply three transformations:
Transform the rigidbody to the origin
Rotate the rigidbody on its center of mass
Transform the rigidbody off of the origin.
Here is my code:
btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();
btQuaternion quat;
orn.getRotation(quat);
btVector3 axis = quat.getAxis();
//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btVector3(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ()));
//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);
//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();
//Move the rigidbody 2 units along new axis
btPhys->translate(btVector3(2.0 * axis.getX(), 0.0, 2.0 * axis.getZ()));
However, the pivot points appears to be moving around instead of staying in one place (the origin). Is there a better way (that actually works) to rotate a rigidbody around a pivot point?
EDIT:
I added some sanity-check code for the rotate function:
//Code that doesn't work
btVector3 invTrans = btPhys->offsetToPivot.rotate(btVector3(1.0, 0.0, 0.0), btScalar(degreesToRads(-1)));
//Values printed out are identical to offsetToPivot
printf("invTrans: %f %f %f\n", invTrans.getX(), invTrans.getY(), invTrans.getZ());
//Sanity code that DOES work
//Arbitrary vector
btVector3 temp = btVector3(0.0, 2.0, 0.0);
temp = temp.rotate(btVector3(1.0, 0.0, 0.0), btScalar(degreesToRads(-1)));
printf("temp %f %f %f\n", temp.getX(), temp.getY(), temp.getZ());

This method actually works, you're just applying it incorrectly. Your second translation is performed along world axis but you have rotated the object, so you have to translate it back along the rotated vector.
Correct code should look more or less like this:
btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();
btQuaternion quat;
orn.getRotation(quat);
btVector3 axis = quat.getAxis();
//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btVector3(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ()));
//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);
//Get rotation matrix
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(degreesToRads(-1))),btVector3(0,0,0));
//Rotate your first translation vector with the matrix
btVector3 invTrans(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ());
invTrans = invRot * invTrans;
//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();
//Translate back by rotated vector
btPhys->translate(-invTrans);
I'm not sure if the rotation shouldn't be with minus (I can't check it right now) but you can easily try both.
EDIT.
Ok, so you forgot to mention that you perform a continuous rotation instead of a single one. This procedure is correct for a single rotation around pivot (eg. 30 degrees rotation). I've looked into your code once more and I understand that you try to perform your first translation along local x and z-axis. However it is not what happens. In this line:
btVector3 axis = quat.getAxis();
the variable axis is a unit vector representing the axis around which your object is rotated. It is NOT its coordinate system. I haven't noticed this part before. Quaternions are tricky and you should read more about them because many people missuse them.
A solution that will work in a continuous case is to store the last translation (from center of mass to pivot - in my example it is represented by invTrans) in your object and use it to perform the first translation, then rotate it in the same way it is done, and use it to move to the right position.
The corrected code will look like this:
btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();
btQuaternion quat;
orn.getRotation(quat);
//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btPhys->offsetToPivot);
//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);
//Get rotation matrix
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(degreesToRads(-1))),btVector3(0,0,0));
//Rotate your first translation vector with the matrix
btVector3 invTrans = invRot * btPhys->offsetToPivot;
//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();
//Translate back by rotated vector
btPhys->translate(-invTrans);
btPhys->offsetToPivot = invTrans;
However before starting this whole procedure you have to set offsetToPivot into its position relative to the center of mass.
I have an impression that the main source of your problems is the lack of understanding of linear algebra and basic spatial transformations. If you are planning to continue in this field, I strongly recommend reading into this topic. Also drawing your problem on paper really helps.
EDIT2.
Ok, I've tried your code:
btVector3 temp = vec3(0,2,0);
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(-0.017453f)),btVector3(0,0,0));
temp = invRot * temp;
After this, temp is equal to {0.000000000, 1.99969542, -0.0349042267}.

In the below function, these transformations perform the three steps you've described:
int x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
int y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
namely:
Step 1:Transform the rigidbody to the origin.
initial.x - axisOfRotation.x
initial.y - axisOfRotation.y
Step 2:Rotate the rigidbody on its center of mass.
cos(angRads) * initial.x - sin(angRads) * initial.y
sin(angRads) * initial.x + cos(angRads) * initial.y
Step 3:Transform the rigidbody off of the origin.
+axisOfRotation.x;
+axisOfRotation.y;
Here is a recursive function that performs exactly what you need and returns
all the consecutively rotated points in a vector: (use it as a benchmark)
rotateCoordinate(vector<Point>& rotated, Point& axisOfRotation, Point initial,
float angRads, int numberOfRotations){
// base case: when all rotations performed return vector holding the rotated points
if(numberOfRotations <= 0) return;
else{
// apply transformation on the initial point
int x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
int y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
// save the result
rotated.push_back(Point(x, y));
// call the same function this time on the rotated point and decremented number of rotations
rotateCoordinate(rotated, axisOfRotation, Point(x,y), angRads, numberOfRotations -1);
}
}
where Point is:
struct Point {
int x, y;
Point(int xx, int yy) : x(xx), y(yy) { }
Point() :x(0), y(0) { }
};
For further reading that explains the math behind it here.

Related

Rotate object with keys [duplicate]

i am trying to implement functions, where i can rotate/ translate an object in local or global orientation, like in 3D modeling software, using glm. Something like this:
void Rotate(float x, float y, float z, bool localOrientation);
but I dont know how to get it working. Local rotation rotation should just be something like this(?):
m_Orientation *= glm::rotate(x, glm::vec3(1,0,0);
m_Orientation *= glm::rotate(y, glm::vec3(0,1,0);
m_Orientation *= glm::rotate(z, glm::vec3(0,0,1);
// (m_Orientation is glm::mat4)
But how to combine this with local orientation? Actually i need to rotate the rotation matrix in world orientation, right?
I hope you know what i mean with local and global oriented rotation/translation, like it is in 3D modeling programs. In most of them you have a button to switch between local and global.
And how would i calculating the forward/right/up vector then?
normally it should be something like this, right?:
forward = m_Orientation * glm::vec4(0,0,-1,0);
I tried global rotation with this:
m_GlobalOrientation = glm::rotate(m_GlobalRotation.x, glm::vec(1,0,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.y, glm::vec(0,1,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.z, glm::vec(0,0,1);
but then only x rotation is in global orientation, y and z rotation is in local orientation, since it is already rotated around x axis. So I need to rotate all 3 angles at once(?)
Translating local should just be adding translation values to current translation, and local translation should be glm::inverse(m_Orientation) * translationVector right?
Before I come to your question, let me explain some core concepts of matrices.
Assume that we have the following matrix:
wher T is a translation and R is a rotation matrix.
When we use this matrix to transform a vertex (or even mesh), there is one unique result. However, we can get to this result with the help of two interpretations:
Interpretation 1: Evaluate from right to left
If we evaluate the matrix from right to left, all transformations are performed in the global coordinate system. So if we transform a triangle that sits at the origin, we get the following result:
Interpretation 2: Evaluate from left to right
In the other case, all transformations are performed in the local coordinate system:
Of course, we get the same result.
So coming back to your question. If you store the position and orientation of the object as a matrix T. You can rotate this object in its local coordinate system by multiplying a rotation matrix to the right side of the current matrix. And in the global system by multiplying it at the left side. The same applies for translation:
void Rotate(float x, float y, float z, bool localOrientation)
{
auto rotationMatrix = glm::rotate(x, glm::vec3(1,0,0));
rotationMatrix *= glm::rotate(y, glm::vec3(0,1,0));
rotationMatrix *= glm::rotate(z, glm::vec3(0,0,1));
if(localOrientation)
this->T = this->T * rotationMatrix;
else
this->T = rotationMatrix * this->T;
}
The right / forward / up vectors are the column vectors of the matrix T. You can either read them directly or get them by multiplying the matrix with (1, 0, 0, 0) (right), (0, 1, 0, 0) (up), (0, 0, 1, 0) (for/backward) or (0, 0, 0, 1) (position).
If you want to read more about this, take a look at my blog article about matrices in DirectX. But it's for DirectX, which uses transposed matrices. Therefore the matrix order is reversed. Watch out for that when reading the article.

Problems rotating objects using eulers - quaternions

I'm having problems rotating GameObjects in my engine. I'm trying to rotate in 2 ways.
I'm using MathGeoLib to calculate maths in the engine.
First way: Rotates correctly around axis but if I want to rotate back, if I don't do it following the inverse order then rotation doesn't work properly.
e.g:
Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate Y axis -50 degrees, Rotate X axis -30 degrees. Works.
Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate X axis -50 degrees, Rotate Y axis -30 degrees. Doesn't.
Code:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
float3 diff = euler_rotation - editor_rotation;
editor_rotation = euler_rotation;
math::Quat mod = math::Quat::FromEulerXYZ(diff.x * DEGTORAD, diff.y * DEGTORAD, diff.z * DEGTORAD);
quat_rotation = quat_rotation * mod;
UpdateMatrix();
}
Second way: Starts rotating good around axis but after rotating some times, then it stops to rotate correctly around axis, but if I rotate it back regardless of the rotation order it works, not like the first way.
Code:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
editor_rotation = euler_rotation;
quat_rotation = math::Quat::FromEulerXYZ(euler_rotation.x * DEGTORAD, euler_rotation.y * DEGTORAD, euler_rotation.z * DEGTORAD);
UpdateMatrix();
}
Rest of code:
#define DEGTORAD 0.0174532925199432957f
void ComponentTransform::UpdateMatrix()
{
if (!this->GetGameObject()->IsParent())
{
//Get parent transform component
ComponentTransform* parent_transform = (ComponentTransform*)this->GetGameObject()->GetParent()->GetComponent(Component::CompTransform);
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//Multiply the object transform by parent transform
transform_matrix = parent_transform->transform_matrix * transform_matrix;
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
else
{
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
}
MathGeoLib:
Quat MUST_USE_RESULT Quat::FromEulerXYZ(float x, float y, float z) { return (Quat::RotateX(x) * Quat::RotateY(y) * Quat::RotateZ(z)).Normalized(); }
Quat MUST_USE_RESULT Quat::RotateX(float angle)
{
return Quat(float3(1,0,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateY(float angle)
{
return Quat(float3(0,1,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateZ(float angle)
{
return Quat(float3(0,0,1), angle);
}
Quat(const float3 &rotationAxis, float rotationAngleRadians) { SetFromAxisAngle(rotationAxis, rotationAngleRadians); }
void Quat::SetFromAxisAngle(const float3 &axis, float angle)
{
assume1(axis.IsNormalized(), axis);
assume1(MATH_NS::IsFinite(angle), angle);
float sinz, cosz;
SinCos(angle*0.5f, sinz, cosz);
x = axis.x * sinz;
y = axis.y * sinz;
z = axis.z * sinz;
w = cosz;
}
Any help?
Thanks.
Using Euler angles and or Quaternions adds some limitations as it creates singularities which if not handled correctly will make silly things. Sadly almost all new 3D games using it wrongly. You can detect those by the well known things like:
sometimes your view get to very different angle that should not be there
object can not rotate anymore in some direction
object start rotating around different axises than it should
view jumps around singularity pole
view is spinning or flipping until you move/turn again (not the one caused by optic mouse error)
I am using cumulative transform matrices instead:
Understanding 4x4 homogenous transform matrices
Read the whole stuff (especially difference between local and global rotations) then in last 3 links you got C++ examples of how to do this (also read all 3 especially the preserving accuracy ...).
The idea is to have matrix representing your object coordinate system. And when ever you rotate (by mouse, keyboard, NAV,AI,...) you rotate the matrix (incrementally). The same goes for movement. This way they are no limitations or singularities. But also this approach has its problems:
lose of accuracy with time (read the preserving accuracy example to deal with this)
no knowledge about the Euler angles (the angles can be computed from the matrix however)
Both are solvable relatively easily.
Now when you are rotating around local axises you need to take into account that with every rotation around some axis you change the other two. So if you want to get to the original state you need to reverse order of rotations because:
rotate around x by 30deg
rotate around y by 40deg
is not the same as:
rotate around y by 40deg
rotate around x by 30deg
With cumulative matrix if you want to get back you can either iteratively drive your ship until it faces desired directions or remember original matrix and compute the rotations needed to be done one axis at a time. Or convert the matrix difference into quaternion and iterate that single rotation...

Rotate and translate object in local and global orientation using glm

i am trying to implement functions, where i can rotate/ translate an object in local or global orientation, like in 3D modeling software, using glm. Something like this:
void Rotate(float x, float y, float z, bool localOrientation);
but I dont know how to get it working. Local rotation rotation should just be something like this(?):
m_Orientation *= glm::rotate(x, glm::vec3(1,0,0);
m_Orientation *= glm::rotate(y, glm::vec3(0,1,0);
m_Orientation *= glm::rotate(z, glm::vec3(0,0,1);
// (m_Orientation is glm::mat4)
But how to combine this with local orientation? Actually i need to rotate the rotation matrix in world orientation, right?
I hope you know what i mean with local and global oriented rotation/translation, like it is in 3D modeling programs. In most of them you have a button to switch between local and global.
And how would i calculating the forward/right/up vector then?
normally it should be something like this, right?:
forward = m_Orientation * glm::vec4(0,0,-1,0);
I tried global rotation with this:
m_GlobalOrientation = glm::rotate(m_GlobalRotation.x, glm::vec(1,0,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.y, glm::vec(0,1,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.z, glm::vec(0,0,1);
but then only x rotation is in global orientation, y and z rotation is in local orientation, since it is already rotated around x axis. So I need to rotate all 3 angles at once(?)
Translating local should just be adding translation values to current translation, and local translation should be glm::inverse(m_Orientation) * translationVector right?
Before I come to your question, let me explain some core concepts of matrices.
Assume that we have the following matrix:
wher T is a translation and R is a rotation matrix.
When we use this matrix to transform a vertex (or even mesh), there is one unique result. However, we can get to this result with the help of two interpretations:
Interpretation 1: Evaluate from right to left
If we evaluate the matrix from right to left, all transformations are performed in the global coordinate system. So if we transform a triangle that sits at the origin, we get the following result:
Interpretation 2: Evaluate from left to right
In the other case, all transformations are performed in the local coordinate system:
Of course, we get the same result.
So coming back to your question. If you store the position and orientation of the object as a matrix T. You can rotate this object in its local coordinate system by multiplying a rotation matrix to the right side of the current matrix. And in the global system by multiplying it at the left side. The same applies for translation:
void Rotate(float x, float y, float z, bool localOrientation)
{
auto rotationMatrix = glm::rotate(x, glm::vec3(1,0,0));
rotationMatrix *= glm::rotate(y, glm::vec3(0,1,0));
rotationMatrix *= glm::rotate(z, glm::vec3(0,0,1));
if(localOrientation)
this->T = this->T * rotationMatrix;
else
this->T = rotationMatrix * this->T;
}
The right / forward / up vectors are the column vectors of the matrix T. You can either read them directly or get them by multiplying the matrix with (1, 0, 0, 0) (right), (0, 1, 0, 0) (up), (0, 0, 1, 0) (for/backward) or (0, 0, 0, 1) (position).
If you want to read more about this, take a look at my blog article about matrices in DirectX. But it's for DirectX, which uses transposed matrices. Therefore the matrix order is reversed. Watch out for that when reading the article.

Rotation around camera's axis

I want to build proper camera transformations in OpenGL and I have a question.
I built a simple 3D engine with without OpenGL, just to see how hard it is to make simple projection of the world onto a screen.
In this engine I put wiewer on (0,0,0) with the eye sight on
(1,0,0).
ASDW make translation of objects.
Mouse make rotation
around (1,0,0),(0,1,0),(0,0,1).
I learned a lot with programing my own 3D engine. With that knowledge I started to build a proper 3D engine based on OpenGL.
For camera transformation in OpenGL, I made this code...
Camera's axis define where up,right,forward translation will go.
Rotation is made around global axis (I want to rotate objects around camera's axis) :(
glRotatef(camera_rot[0],1,0,0)
glRotatef(camera_rot[1],0,1,0)
glRotatef(camera_rot[2],0,0,1)
camera_rot[0]=0
camera_rot[1]=0
camera_rot[2]=0
glGetFloatv(GL_MODELVIEW_MATRIX, matrix)
glTranslatef(
matrix[0]*camera_tra[0],
matrix[4]*camera_tra[0],
matrix[8]*camera_tra[0])
glTranslatef(
matrix[1]*camera_tra[1],
matrix[5]*camera_tra[1],
matrix[9]*camera_tra[1])
glTranslatef(
matrix[2]*camera_tra[2],
matrix[6]*camera_tra[2],
matrix[10]*camera_tra[2])
camera_tra[0]=0
camera_tra[1]=0
camera_tra[2]=0
It's kinda confusing why OpenGL designers didn't put camera in (0,0,0) and make translation and rotation on object not camera.
I'm thinking to manually aplly transformation with out glTranslate or glRotation and put transformation logic from my 3D engine (that I made) to changing glVertexe cordinates.
So every time in draw function I will change glVertexes cordinates. But if I do that the vertextes will multiply after my transformation with identical matrix (ModelView matrix), and I don't want meaningless computation in my program.
So what should I do to fix this problem?
How can I make the best solution for rotation around camera's axis?
YEEEEEES I made it YAW,ROLL,PITCH rotation and translation for local camera axis :D
Down is my part of code that take care for transformation.
Here is my global constant:
float camera_drot[3]={0,0,0};
float camera_dtra[3]={0,0,0};
float camera_place[3]={0,0,10};
float camera_right[3]={1,0,0};
float camera_up[3]={0,1,0};
float camera_forvard[3]={0,0,-1};
Before i draw objects:
if(camera_drot[0]!=0){ //ROTATE CAMERA_UP AND CAMERA_FORVARD AROUND CAMERA_RIGHT
MYRotation(camera_right,camera_up,camera_drot[0]);
MYRotation(camera_right,camera_forvard,camera_drot[0]);
camera_drot[0]=0;}
if(camera_drot[1]!=0){ //ROTATE CAMERA_RIGHT AND CAMERA_FORVARD AROUND CAMERA_UP
MYRotation(camera_up,camera_right,camera_drot[1]);
MYRotation(camera_up,camera_forvard,camera_drot[1]);
camera_drot[1]=0;}
if(camera_drot[2]!=0){ //ROTATE CAMERA_RIGHT AND CAMERA_UP AROUND CAMERA_FORWARD
MYRotation(camera_forvard,camera_right,camera_drot[2]);
MYRotation(camera_forvard,camera_up,camera_drot[2]);
camera_drot[2]=0;}
if(camera_dtra[0]!=0){MYTranslation(camera_right,camera_dtra[0]);camera_dtra[0]=0;}
if(camera_dtra[1]!=0){MYTranslation(camera_up,camera_dtra[1]);camera_dtra[1]=0;}
if(camera_dtra[2]!=0){MYTranslation(camera_forvard,camera_dtra[2]);camera_dtra[2]=0;}
glLoadIdentity();
gluLookAt(
camera_place[0],camera_place[1],camera_place[2],
camera_place[0]+camera_forvard[0],camera_place[1]+camera_forvard[1],camera_place[2]+camera_forvard[2],
camera_up[0],camera_up[1],camera_up[2]);
}
And here is my function MYRotation and MYTranslation :)
static void MYRotation(float vector[3],float point[3],float fi){
//ROTATE POINT AROUND VECTOR
float i = point[0];
float j = point[1];
float k = point[2];
float x = vector[0];
float y = vector[1];
float z = vector[2];
point[0] = i*(x*x + (y*y + z*z)*cos(fi)) + k*(-x*z*(-1 + cos(fi)) + y*sin(fi)) + j*(-x*y*(-1 + cos(fi)) - z*sin(fi));
point[1] = j*(y*y + (x*x + z*z)*cos(fi)) + k*(-y*z*(-1 + cos(fi)) - x*sin(fi)) + i*(-x*y*(-1 + cos(fi)) + z*sin(fi));
point[2] = k*(z*z + (x*x + y*y)*cos(fi)) + j*(-y*z*(-1 + cos(fi)) + x*sin(fi)) + i*(- x*z*(-1 + cos(fi)) - y*sin(fi));
float normalize = pow(pow(point[0],2)+pow(point[1],2)+pow(point[2],2),-0.5);
point[0]=point[0]*normalize;
point[1]=point[1]*normalize;
point[2]=point[2]*normalize;
}
static void MYTranslation(float camera_axe[3],float camera_dtra){
camera_place[0]+=camera_axe[0]*camera_dtra;
camera_place[1]+=camera_axe[1]*camera_dtra;
camera_place[2]+=camera_axe[2]*camera_dtra;}
Oh and i forget to make vector corection before normalize in MYRotation. In program the calculation errors will not give ortogonal vectors. Its critical to make this correction!
I will poste the code after im done :)

OpenTK/OpenGL local axis rotation

Here is a scenario:
Object is described by:
Position
Scale
Rotation
First I apply model view (camera) from OpenGL, then Translation and Rotation using following matrix:
private Matrix4d AnglesToMatrix(Vector3d angles)
{
Vector3d left = Vector3d.UnitX;
Vector3d up = Vector3d.UnitY;
Vector3d forward = Vector3d.UnitZ;
AnglesToAxes(angles, ref left, ref up, ref forward);
return new Matrix4d(
new Vector4d(left.X, up.X, forward.X, 0),
new Vector4d(left.Y, up.Y, forward.Y, 0),
new Vector4d(left.Z, up.Z, forward.Z, 0),
new Vector4d(0, 0, 0, 1));
}
private void AnglesToAxes(Vector3d angles, ref Vector3d left, ref Vector3d up, ref Vector3d forward)
{
const double DEG2RAD = 0.0174532925;
double sx, sy, sz, cx, cy, cz, theta;
// rotation angle about X-axis (pitch)
theta = angles.X * DEG2RAD;
sx = Math.Sin(theta);
cx = Math.Cos(theta);
// rotation angle about Y-axis (yaw)
theta = angles.Y * DEG2RAD;
sy = Math.Sin(theta);
cy = Math.Cos(theta);
// rotation angle about Z-axis (roll)
theta = angles.Z * DEG2RAD;
sz = Math.Sin(theta);
cz = Math.Cos(theta);
// determine left axis
left.X = cy * cz;
left.Y = sx * sy * cz + cx * sz;
left.Z = -cx * sy * cz + sx * sz;
// determine up axis
up.X = -cy * sz;
up.Y = -sx * sy * sz + cx * cz;
up.Z = cx * sy * sz + sx * cz;
// determine forward axis
forward.X = sy;
forward.Y = -sx * cy;
forward.Z = cx * cy;
}
at, the end I apply scale. All looks great except rotation, which is based on global axis.
How to rotate objects using local axis?
To make question precise. When I rotate object by 45 degree on Y axis then X and Z axis are rotated with it and then applying another rotation use new axis.
To avoid punishment in form of minuses... I read all subjects related to rotation in 3D space, non of them gave me solution. Above code is a result of applying various attempts, but it produces result same as:
GL.Rotate(Rotation.X, Vector3d.UnitX);
GL.Rotate(Rotation.Y, Vector3d.UnitY);
GL.Rotate(Rotation.Z, Vector3d.UnitZ);
EDIT:
As it turned out, our designer had bad expectations about rotations of objects in 3D, but still the problem exist. As for language used, we write this in C#, but if you point me a solution in C or C++ I will handle it :D
We currently use (order can be configured):
GL.Rotate(Rotation.X, Vector3d.UnitX);
GL.Rotate(Rotation.Y, Vector3d.UnitY);
GL.Rotate(Rotation.Z, Vector3d.UnitZ);
But this rotates objects around world axis. What we want is to use local object axis like this assuming we have X-Y-Z axis rotation:
GL.Rotate(Rotation.X, Vector3d.UnitX);
GL.Rotate(Rotation.Y, newYaxis);
GL.Rotate(Rotation.Z, newZaxis);
or assuming we have Y-X-Z axis rotation
GL.Rotate(Rotation.Y, Vector3d.UnitY);
GL.Rotate(Rotation.X, newXaxis);
GL.Rotate(Rotation.Z, newZaxis);
Most efficient way would be to pre calculate rotation matrix, but still I'm wondering how to determine new axis after rotation. (it seams that I have to revisit trigonometry book). If someone have solution which would calculate rotation matrix really fast I would be grateful. For now I will try to use trigonometry calculations in each pass.
I'm not very good with opentk, but for C style opengl this is the difference between pre-multiplication and post-multiplication. Pre-multiplication occurs in world-coordinate space, post-multiplication occurs in local-coordinate space. See http://www.opengl.org/archives/resources/faq/technical/transformations.htm section 9.070.
If you can get a handle to the actual matrices to perform the matrix multiplication, this should work.
Can you say exactly what kind of situation you're envisioning here?
I don't believe your request makes any sense. With any rotation, you can either apply it before another operation (global axis), or after another operation (local axis). Therefore if you want to apply three rotations, one axis rotation must be first, another axis rotation must come second, and another axis must come third.
You're saying you want to apply three rotations, but you want each rotation to occur before the other two. They can't all be first.
If you put more description (images preferred) of what kind of rotation you're trying to achieve maybe we can clear up this misunderstanding, but what you're currently asking does not make sense to me in physical reality.