I have a plane in a 3D-World and its orientation is saved in any way (e.g. pitch, yaw and roll). Now when I want the plane to turn left, but glRotatef doesn't do the job as it sticks to global coordinates and does not care about the rotation of the plane, and simply changing the yaw doesn't help either as this is also not relative to the planes actual rotation and would only mean "left" when the plane flies straight to the horizon. What I would need would be like this:
float pitch = 10 , yaw = 20, roll = 30; //some initial values
Turn(&pitch, &yaw, &roll , 5, 0 , 0 ) //calculate new pitch, yaw and roll as if
//the plane had turned 5 degrees to the right (relative to its current orientation and roll!)
//pitch, yaw and roll are updated to reflect the new orientation.
Many people suggest usage of Quaternions but I have no idea on how to implement them to a Turn function (one working example is Blitz3D, which has a "RotateEntity" function for global rotation like glRotatef and "TurnEntity" for rotation based on the orientation) I think the function internally works like this:
transform pitch, yaw, roll to a Quaternion like EulerToQuat in
OpenGL + SDL rotation around local axis
perform the local rotation using Quaternion mathematics (no source found)
transform Quaternion back to yaw, roll, pitch (no source found)
I finally solved the problem by switching to carrying a matrix for every ship around all the time. Pitch, yaw and roll are only calculated when needed, which is rarely the case. Finally, glRotatef does the job - you only have to apply it to the already rotated matrix - and save the result, so that the change is not dropped.
Following code is my implementation on a ship structure which carries x,y,z, Matrix[16], dx, dy, dz. (Note that all ship arrays have to be initialized with the IdentityMatrix):
//*************************** Turn Ship **********************************
void TurnShip(long nr,float yaw, float pitch=0,float roll=0) {
//Turns ship by pitch, yaw and roll degrees.
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&ship[nr].Matrix[0]);
glRotatef(yaw,0,1,0);
glGetFloatv(GL_MODELVIEW_MATRIX,&ship[nr].Matrix[0]);
glLoadMatrixf(&ship[nr].Matrix[0]);
glRotatef(pitch,1,0,0);
glGetFloatv(GL_MODELVIEW_MATRIX,&ship[nr].Matrix[0]);
glLoadMatrixf(&ship[nr].Matrix[0]);
glRotatef(roll,0,0,1);
glGetFloatv(GL_MODELVIEW_MATRIX,&ship[nr].Matrix[0]);
}
What the function does is loading the ships matrix stored in a simple float array, manipulate it and then save it back to the array. In the graphics section of the game, this array will be loaded with glLoadMatrixf and thus will be applied to the ship without any hassle or mathematics.
//*************************** Accelerate Ship relative to own orientation **
void AccelerateShip(long nr,float speedx,float speedy, float speedz)
{ //use speedz to move object "forward".
ship[nr].dx+= speedx*ship[nr].Matrix[0]; //accelerate sidewards (x-vector)
ship[nr].dy+= speedx*ship[nr].Matrix[1]; //accelerate sidewards (x-vector)
ship[nr].dz+= speedx*ship[nr].Matrix[2]; //accelerate sidewards (x-vector)
ship[nr].dx+= speedy*ship[nr].Matrix[4]; //accelerate upwards (y-vector)
ship[nr].dy+= speedy*ship[nr].Matrix[5]; //accelerate upwards (y-vector)
ship[nr].dz+= speedy*ship[nr].Matrix[6]; //accelerate upwards (y-vector)
ship[nr].dx+= speedz*ship[nr].Matrix[8]; //accelerate forward (z-vector)
ship[nr].dy+= speedz*ship[nr].Matrix[9]; //accelerate forward (z-vector)
ship[nr].dz+= speedz*ship[nr].Matrix[10]; //accelerate forward (z-vector)
}
This is the best part of what I learned today - the part the tutorials often don't tell you as they are all about maths - I can pull the vectors that point up, left and in front of my ship right out of the matrix and apply acceleration upon them so that my ship can strave left,right,up down, accelerate and brake - and glRotatef cares for them so they are always updated and no maths involved at our side at all :-)
Related
I'm trying to implement yaw pitch roll for visualisation camera yaw pitch roll more then 360 degree like in Maya, Blender, Unreal Engine, Unity etc. All this application can show angles in gui more then 360 degree and no have any Gimbal lock problems.
current psevdo code;
Quaternion rotation; //relative (local rotation)
Quaternion parentRotation;
//it is psevdo code where we calculate delta angle that user generate on current frame by mouse
void SetDeltaWorldRotationFromUserInput(Quaternion value)
{
SetWorldRotation(value);
}
void SetWorldRotation(Quaternion value)
{
SetLocalRotation(parentQuaternion.inverted() * value); //get/set local rotation
}
void SetLocalRotation(Quaternion value)
{
rotation = value; //this we save new local rotation but i want to save somehow more then 360 degree
auto eulersForGui = value.toEulers(); //it is current eulers but i want to accumulate and show in gui accumulate rotation. like currentRotation + delta or somehow
auto deltaEulers = (value * rotation.inverted()).toEulers(); //i can get delta eulers but i can't add it to euelers and don't know what i need to do with this value
}
I had left a link in the comment section to your question. However as a quick answer to your problem I think this quote from that webpage should point you in the right direction:
"A sequence of rotations can be represented by a series of quaternions multiplied together, producing a single resulting quaternion that encodes the combined rotations."
The quote above can refer to either multiple quaternions each rotating around a different axis to get the combined rotation in 3D, or multiple rotations about the same axis that would accumulate to a single rotation that is more than 360 degrees.
I'm currently in the process of finishing the implementation for a camera that functions in the same way as the camera in Maya. The part I'm stuck in the tumble functionality.
The problem is the following: the tumble feature works fine so long as the position of the camera is not parallel with the up vector (currently defined to be (0, 1, 0)). As soon as the camera becomes parallel with this vector (so it is looking straight up or down), the camera locks in place and will only rotate around the up vector instead of continuing to roll.
This question has already been asked here, unfortunately there is no actual solution to the problem. For reference, I also tried updating the up vector as I rotated the camera, but the resulting behaviour is not what I require (the view rolls as a result of the new orientation).
Here's the code for my camera:
using namespace glm;
// point is the position of the cursor in screen coordinates from GLFW
float deltaX = point.x - mImpl->lastPos.x;
float deltaY = point.y - mImpl->lastPos.y;
// Transform from screen coordinates into camera coordinates
Vector4 tumbleVector = Vector4(-deltaX, deltaY, 0, 0);
Matrix4 cameraMatrix = lookAt(mImpl->eye, mImpl->centre, mImpl->up);
Vector4 transformedTumble = inverse(cameraMatrix) * tumbleVector;
// Now compute the two vectors to determine the angle and axis of rotation.
Vector p1 = normalize(mImpl->eye - mImpl->centre);
Vector p2 = normalize((mImpl->eye + Vector(transformedTumble)) - mImpl->centre);
// Get the angle and axis
float theta = 0.1f * acos(dot(p1, p2));
Vector axis = cross(p1, p2);
// Rotate the eye.
mImpl->eye = Vector(rotate(Matrix4(1.0f), theta, axis) * Vector4(mImpl->eye, 0));
The vector library I'm using is GLM. Here's a quick reference on the custom types used here:
typedef glm::vec3 Vector;
typedef glm::vec4 Vector4;
typedef glm::mat4 Matrix4;
typedef glm::vec2 Point2;
mImpl is a PIMPL that contains the following members:
Vector eye, centre, up;
Point2 lastPoint;
Here is what I think. It has something to do with the gimbal lock, that occurs with euler angles (and thus spherical coordinates).
If you exceed your minimal(0, -zoom,0) or maxima(0, zoom,0) you have to toggle a boolean. This boolean will tell you if you must treat deltaY positive or not.
It could also just be caused by a singularity, therefore just limit your polar angle values between 89.99° and -89.99°.
Your problem could be solved like this.
So if your camera is exactly above (0, zoom,0) or beneath (0, -zoom,0) of your object, than the camera only rolls.
(I am also assuming your object is at (0,0,0) and the up-vector is set to (0,1,0).)
There might be some mathematical trick to resolve this, I would do it with linear algebra though.
You need to introduce a new right-vector. If you make a cross product, you will get the camera-vector. Camera-vector = up-vector x camera-vector. Imagine these vectors start at (0,0,0), then easily, to get your camera position just do this subtraction (0,0,0)-(camera-vector).
So if you get some deltaX, you rotate towards the right-vector(around the up-vector) and update it.
Any influence of deltaX should not change your up-vector.
If you get some deltaY you rotate towards the up-vector(around the right-vector) and update it. (This has no influence on the right-vector).
https://en.wikipedia.org/wiki/Rotation_matrix at Rotation matrix from axis and angle you can find a important formula.
You say u is your vector you want to rotate around and theta is the amount you want to pivot. The size of theta is proportional to deltaX/Y.
For example: We got an input from deltaX, so we rotate around the up-vector.
up-vector:= (0,1,0)
right-vector:= (0,0,-1)
cam-vector:= (0,1,0)
theta:=-1*30° // -1 due to the positive mathematical direction of rotation
R={[cos(-30°),0,-sin(-30°)],[0,1,0],[sin(-30°),0,cos(-30°)]}
new-cam-vector=R*cam-vector // normal matrix multiplication
One thing is left to be done: Update the right-vector.
right-vector=camera-vector x up-vector .
I've created an object that moves towards its destination with inertia. I am having alot of trouble getting the object to face its destination. My code is simple, it calculates the angle, converts it to degrees and passes that angle to the Matrix4 Rotate function, which adjusts the localTransform (scenegraph).
The problem is that the object spawns, and then just rotates endlessly. It slowly progresses towards its target, but just keeps spinning. I've tested it without translation, it spins regardless on the spot. All I need is for the object to face its destination. My Translate/Rotate functions work correctly, I've used it to rotate an object, have an object spawn with its parent's rotation and head in that direction. They provide 1:1 results with the GLM library.
I've tried swapping the order in aTan2, removing the degrees conversion, (though that does nothing, the Rotate function takes degrees) and swapping translation/rotation order.
localTransform is the combined rotation/scale/translation matrix. row[3]column[1] is Y. [3][0] is X.
float fAngle = atan2(v3Destination[1] - localTransform.data[3][1] , v3Destination[0] - localTransform.data[3][0]);
float fAngleDegrees = fAngle * 180 / PI;
localTransform = Matrix4::Rotate(localTransform, fAngleDegrees, Vector3(0.0f, 0.0f, 1.0f));
Vector3 Movement;
Movement[0] = v3Destination[0] - localTransform.data[3][0];
Movement[1] = v3Destination[1] - localTransform.data[3][1];
Movement = Movement * fSpeed * Application.GetTimeStep();
localTransform = Matrix4::Translate(localTransform, Movement);
Any advice on how to handle this? This is all in 2D coordinates, however the rotation is done on the Z-Axis.
Just a hunch, but is the localTransform matrix completely recomputed each time step?
Or could you be adding a rotation to a matrix that's already been rotated in the previous iteration.
This could explain the continuous rotation.
What exactly is the formula to needed to do this?
So if I have two separate sprites named player (a spaceship) and exhaust (an exhaust animation for the bottom of the spaceship)
What math is needed so that when player rotates, the exhaust moves and positions itself at the bottom of the ship which could be to the left at this point, if player has rotated 90 degrees right.
I know how to spin / rotate the sprites to face the right direction. What I need to know is how to move the exhaust so that it's positioned correctly at the bottom of the ship when the ship turns.
And please don't link any "Move a sprite around a point" threads and such since that's not what I'm looking for.
Assuming the ship is similar to the one in the game "Asteroids", the position of the tail of the ship (relative to the ship's center) will be described by a circle whose radius is the distance between the center of the ship and the ship's tail.
Given that, you can calculate the position of the tail of the ship like this:
#include <math.h>
[...]
double radius = ship_height/2.0;
double ship_heading = /* current angle of rotation of the ship, in radians, e.g. 0 if the ship is facing right, pi if it is facing left, etc */
double tail_heading = ship_heading + 3.14159; /* ship's tail faces the opposite direction from the nose! */
double x_offset = cos(tail_heading)*radius;
double y_offset = sin(tail_heading)*radius;
double exhaust_center_x = ship_center_x + x_offset;
double exhaust_center_y = ship_center_y + y_offset;
(and if you want the exhaust-graphic to appear a little farther away from the tail of the ship, just increase the radius value slightly)
I have to possitions, p1 and p2, p2 is attached to p1, not only to p1's position but also to it's rotation, so q1 is a quaternion which represents p1's rotation.
If q1 rotates, then p1's position must also rotate around p1 accordingly.
I only need to calculate p2's position, not it's rotation, I worked the rotation out already.
So basically is a spaceship docked to a station, I need to move and rotate the station around with the ship docked to it.
How do I do it?
the code i wrote for it works as long as the station is not rotated during the time of docking:
bool docked[100];
Quaternion quatTarget[100];
double distance_dock[100];
vector3 docking_position(int ship, int station)
{
if (!docked[ship])
{
docked[ship] = true;
distance_dock[ship] = distances(position[ship], position[station]);
vector3 direcc = normalized(position[station] - position[ship]);
quatTarget[ship] = vecToVecRotation(direcc, { 0, 0, 1 });
QuaternionNormalize(&quatTarget[ship], &quatTarget[ship]);
}
Quaternion orientation = total_rotation[station] * quatTarget[ship];
Matrix docking_place;
MatrixRotationQuaternion(&docking_place, &orientation);
vector3 axis_z = { docking_place(0, 2), docking_place(1, 2), docking_place(2, 2) };
return position[station] + -axis_z * distance_dock[ship];
}
What I do here is take an orientation quaternion from the ship to the station at the time of docking and then traslate the ship "distance_dock" units along the negative z axis of the orientation, so the ship will always move accordingly, but somehow if I dock the ship when the station is already rotated then I get the initial docking position wrong, though it still rotates perfectly along with the station.
If I understand you correctly, you have two objects that have a rigid transformation between them. The problem is that you want to calculate the pose (position + orientation) of one, given the pose of the other.
Let's say you have three frames; the Station frame "S", the Vehicle frame "V" and the Global frame "G" (I assume your graphics environment has a global 3D Cartesian frame).
The transformation between frames S and V is fully known (translation and orientation) and constant, and is denoted S_p_SV (the position of the Vehicle w.r.t the Station, expressed in the Station frame) and SV_q (the quaternion orientation of the Vehicle, expressed in the Station frame).
This will be confusing if you have not had experience in rigid-body mechanics, in which case you should read some introductory notes/slideshows on "Rigid-Body Mechanics" which are plentiful on Google results.
I have written the expression in LATEX but unfortunately StackOverflow does not support it, so I have attached it as an image. The original LATEX can be found here.
In my notation below, for example on the first line Sp_SV , is the position of the Vehicle w.r.t. the Station, expressed in the Station frame (of rotation).
The prefixed superscript indicates the rotation frame. For the quaternion G_Sq for example, this represents the orientation of the Station frame from the Ground Frame.
In terms of implementing this in C++, I am unsure of what library you are using for Quaternions, but you will need the following functions:
Convert Euler to Quaternions - If you are going to manually specify the rotation SVq (rotation of Vehicle w.r.t Station)
Convert Quaternion to DCM - For the first method in the LATEX
Quaternion Multiply - For the second method in the LATEX
Quaternion Conjugate - For the second method in the LATEX