Apply rotation to Eigen::Affine3f - c++

I'm using an Eigen::Affine3f to represent a camera matrix. (I've already figured out how to setup the view matrix/Affine3f from an initial "lookAt" and "up" vector)
Now, I want to support change the camera's orientation. Simple question: what's the best way to apply rotations to this Affine3f, i.e. pitch, yaw, roll?

It's quite simple using the built in functionality. You can use an AxisAngle object to multiply the existing Affine3f. Just note that the axis needs to be normalized:
Vector3f rotationAxis;
rotationAxis.setRandom(); // I don't really care, you determine the axis
rotationAxis.normalize(); // This is important, don't forget it
Affine3f randomAffine3f, rotatedAffine;
// Whatever was left in memory in my case,
// whatever your transformation is in yours
std::cout << randomAffine3f.matrix() << std::endl;
// We'll now apply a rotation of 0.256*M_PI around the rotationAxis
rotatedAffine = (AngleAxisf(0.256*M_PI, rotationAxis) * randomAffine3f);
std::cout << rotatedAffine.matrix() << std::endl; // Ta dum!!

Related

Rotate around a point with a 3x3 matrix and a translation vector

I have a 3x3 matrix in openGL format and a translation vector. I get confused when rotating around a point because my rotate function does not consider translation.
For rotating I assumed I could simply change the 3x3 matrix into a quaternion, rotate it and then change it back. This does not work when rotating about anything other than the objects current position.
The normal procedure for 4x4 matrcies is:
translate point by -(point - position)
rotate
translate point by (point - position)
Optional advice:
Bullet physics uses this format and that is why I was considering using their data format to store transformations. I do eventually have to convert to a 4x4 matrix to do projection, but that does not matter all that much. Bullet's format is attractive because it removes the useless shear data. Is it worth the bother to keep transformations in bullet format or no?
Came up with one. Just do matrix math. Create the rotation matrix manually or from a quaternion and then just multiply them. Bullet does this for you but i'll show what bullet does internally too.
*psuedo code
void Transform::rotateAroundPoint(axis, angle, point)
{
Matrix3x3 mat = createMatrixFromQuaternion(axis, angle);
Transform rotateTransform;
trans.basis = mat; // <- 3x3 matrix
trans.origin = Vector3(0,0,0); // <- Translation vector
// "this" is a pointer to the transform being edited since I decided to write
// this as a member function.
Vector3 pos = this->origin;
this->origin = this->origin - point;
(*this) = rotateTransform * (*this);
this->origin = this->origin + point;
}
// Internally bullet does basically this
resultTransform.basis = t1.basis * t2.basis;
resultTransform.origin = t1.basis * t2.origin + t1.origin;
I have not tested the code yet. I am a little bit terrified that it may suffer from gimbal lock so in my actual code I am probably going to just do quaternion math when i have to multiply 2 basis matricies.

How to get local rotation from a reference 'zero' quaternion and the global rotation quaternion

I am displaying characters on a screen connected to a flystick (3D tracking object). My goal is to move the characters according to device input.
I noticed the 'zero' of the device (corresponding to the default position) is not corresponding to a not rotated quaternion of my characters. When moving the device, my character moves around some other axis (probably world's) than the device's. So I added a synchronising function which registers the quaternion rotation of my device when it should be at the default position, but now I have no idea how to combine this reference quaternion with the actual quaternion I receive from the device when it moves in order to rotate my character as I intend.
Here is how i am using the quaternions with OpenGL :
glm::quat rotation = QAccumulative.getQuat();
glm::mat4 matrix_rotation = glm::mat4_cast(rotation);
object_transform *= matrix_rotation;
Model *= object_transform;
glm::mat4 MVP = Projection * View * Model;
It works fine with keyboard and mouse if I rotate my objects with this method:
rotate(float angleX,float angleY,float angleZ) {
Quaternion worldRotationx( 1.0,0,0, angleZ);
Quaternion worldRotationy( 0,1.0,0, angleX);
Quaternion worldRotationz( 0,0,1.0, angleY);
QAccumulative = worldRotationx * worldRotationy * worldRotationz * QAccumulative;
QAccumulative.normalise();
}
Here is the main loop, where I compute the data from the tracking device:
void VRPN_CALLBACK handle_tracker(void* userData, const vrpn_TRACKERCB t ) {
Vrpn_tracker* current = Vrpn_tracker::current_vrpn_device;
if (current->calibrating == true) {
current->reference = Quaternion(t.quat[0],t.quat[1],t.quat[2],t.quat[3]);
current->reference.normalise();
} else {
// translation :
current->newPosition = sf::Vector3f(t.pos[0],t.pos[1],t.pos[2]);
current->world->current_object->translate(
current->newPosition.x - current->oldPosition.x,
current->newPosition.y - current->oldPosition.y,
current->newPosition.z - current->oldPosition.z);
current->oldPosition = current->newPosition;
// rotation:
current->newQuat = Quaternion(t.quat[0],t.quat[1],t.quat[2],t.quat[3]);
current->newQuat.normalise();
current->world->current_object->QAccumulative =
(current->reference.getConjugate() * current->newQuat);
Quaternion result = current->world->current_object->QAccumulative;
cout << "result? : " << result.x << result.y << result.z << result.w << endl;
// prints something like:
// x : -0.00207066 y : 0.00186546 z : -0.00165524 w : 0.999995
// when position of flystick = default position
}
}
The simple test I do to check the code: I start my program without moving the flystick, (ref quaternion is getting captured) so after the synchronisation the character should be in the default position (not moved) as I haven't touched the flystick.
I did try to multiply my reference by the quaternion received but it seems my character is moving according to local axis.
If someone could shed some light on how I can get the rotation in global axis from these two quaternions it would be great.
You have different calibrations possible for the flystick that may correspond to what you are expecting, however i suggest you read carefully the documentation about the flystick, your answer must be there.
If you are using ART tracking, page 124 of the User manual will help you.
Change multiplication order. If you rotate first, than do it last if last than do it first.
The hidden problem of your question, we don't know your transformation notation.
Your rotation can transform from World To Object frame or from Object to world. Depending of notation , exact transformation will be different.

Rotating the LookAt vector of gluLookAt

I'm a student new to opengl. Currently, I'm doing a project that creates a scene.
Right now, my team is using gluLookAt() for my camera. What I want to accomplish is to try and rotate the LookAt vector around a certain point, namely where the camera is looking at.
This accomplishes a sort of "swaying in a circle". I need this because I am making a dart game for the scene, and my camera stay still, but I need it to move in a circle, but still allow the user's mouse to influence it. I also need it to create a drunken movement. That is why I am not considering rotating the Up or Eye vectors.
Currently, my look at code is like this.
int deltax = x - mouse.mX;
int deltay = y - mouse.mY;
cameradart.mYaw -= ((deltax/360.0) * 3.142) * 0.5;
cameradart.mPitch -= deltay * 0.02;
mouse.mX = x;
mouse.mY = y;
cameradart.lookAt.x = sin (cameradart.mYaw);
cameradart.lookAt.y = cameradart.mPitch ;
cameradart.lookAt.z = cos (cameradart.mYaw);
gluLookAt (cameradart.eye.x, cameradart.eye.y, cameradart.eye.z,
cameradart.eye.x + cameradart.lookAt.x, cameradart.eye.y + cameradart.lookAt.y,
cameradart.eye.z + cameradart.lookAt.z,
cameradart.up.x, cameradart.up.y, cameradart.up.z);
I know that it could be done easier using a different camera, but I really don't want to mess with my team's code by not using gluLookAt().
There's a couple of solutions in my mind, I'll tell you the easiest to understand/implement as a new graphics student
Assuming at first you're looking at (0,0,1) -store that vector-:
Think of a point that's drawing a circle and you're looking at it,
-Do it first to turn right and left (2D on X & Z)
-Let HDiff be the horizontal difference between old mouse position and the new one
-Update the x = cos(HDiff)
-Update the z = sin(HDiff)
*I didn't try it but it should work :)
If you want to be able to manipulate the camera, using a camera matrix is a much more effective mechanism than working with gluLookat. GLM is a good library for matrix and vector math and includes a lookat mechanism you can use to initialize the matrix, or you can just initialize it with a series of operations. However, remember that lookat produces a view matrix, and the view matrix is the inverse of the camera matrix.
This piece of code has a demonstration of what I'm talking about. Specifically look at the player member variable and how it's manipulated
glm::mat4 player;
...
glm::vec3 playerPosition(0, eyeHeight, ipd * 4.0f);
player = glm::inverse(glm::lookAt(playerPosition, glm::vec3(0, eyeHeight, 0), GlUtils::Y_AXIS));
This approach lets you apply changes like rotation and translation directly to the player matrix
// Rotate on the Y axis
player = glm::rotate(player, angle, glm::vec3(0, 1, 0));
This is much more intuitive than manipulating the view matrix, since changes to the view matrix always have to be the inverse of what you'd do to the player matrix.
When you're ready to render you need to convert the player matrix to a view matrix by taking it's inverse. In my example it's done like this:
gl::Stacks::modelview().top() = riftOrientation * glm::inverse(player);
This is because I'm using an application based modelview matrix stack that gets applied to the Shader programs I'm running.
For an OpenGL 1.x program, you'd instead use LoadMatrix
glMatrixMode(GL_MODELVIEW);
glm::mat4 modelview = glm::inverse(player);
glLoadMatrixf(&modelview);

Quaternion-Based-Camera unwanted roll

I'm trying to implement a quaternion-based camera, but when moving around the X and Y axis, the camera produces an unwanted roll on the Z axis. I want to be able to look around freely on all axis.
I've read other topics about this problem (for example: http://www.flipcode.com/forums/thread/6525 ), but I'm not getting what is meant by "Fix this by continuously rebuilding the rotation matrix by rotating around the WORLD axis, i.e around <1,0,0>, <0,1,0>, <0,0,1>, not your local coordinates, whatever they might be."
//Camera.hpp
glm::quat rotation;
//Camera.cpp
void Camera::rotate(glm::vec3 vec)
{
glm::quat paramQuat = glm::quat(vec);
rotation = paramQuat * rotation;
}
I call the rotate function like this:
cam->rotate(glm::vec3(0, 0.5, 0));
This must have to do with local/world coordinates, right? I'm just not getting it, since I thought quaternions are always based on each other (thus a quaternion can't be in "world" or "local" space?).
Also, should i use more than one quaternion for a camera?
As far as I understand it, and from looking at the code you provided, what they mean is that you shouldn't store and apply the rotation incrementally by applying rotate on the rotation quat all the time, but instead keeping track of two quaternions for each axis (X and Y in world space) and calculating the rotation vector as the product of those.
[edit: some added (pseudo)code]
// Camera.cpp
void Camera::SetRotation(glm::quat value)
{
rotation = value;
}
// controller.cpp --> probably a place where you'd want to translate user input and store your rotational state
xAngle += deltaX;
yAngle += deltaY;
glm::quat rotationX = QuatAxisAngle(X_AXIS, xAngle);
glm::quat rotationY = QuatAxisAngle(Y_AXIS, yAngle);
camera.SetRotation(rotationX * rotationY);

"Looking At" an object with a Quaternion

So I am currently trying to create a function that will take two 3D points A and B, and provide me with the quaternion representing the rotation required of point A to be "looking at" point B (such that point A's local Z axis passes through point B, if you will).
I originally found this post, the top answer of which seemed to provide me with a good starting point. I went on to implement the following code; instead of assuming a default (0, 0, -1) orientation, as the original answer suggests, I try to extract a unit vector representing the actual orientation of the camera.
void Camera::LookAt(sf::Vector3<float> Target)
{
///Derived from pseudocode found here:
///https://stackoverflow.com/questions/13014973/quaternion-rotate-to
//Get the normalized vector from the camera position to Target
sf::Vector3<float> VectorTo(Target.x - m_Position.x,
Target.y - m_Position.y,
Target.z - m_Position.z);
//Get the length of VectorTo
float VectorLength = sqrt(VectorTo.x*VectorTo.x +
VectorTo.y*VectorTo.y +
VectorTo.z*VectorTo.z);
//Normalize VectorTo
VectorTo.x /= VectorLength;
VectorTo.y /= VectorLength;
VectorTo.z /= VectorLength;
//Straight-ahead vector
sf::Vector3<float> LocalVector = m_Orientation.MultVect(sf::Vector3<float>(0, 0, -1));
//Get the cross product as the axis of rotation
sf::Vector3<float> Axis(VectorTo.y*LocalVector.z - VectorTo.z*LocalVector.y,
VectorTo.z*LocalVector.x - VectorTo.x*LocalVector.z,
VectorTo.x*LocalVector.y - VectorTo.y*LocalVector.x);
//Get the dot product to find the angle
float Angle = acos(VectorTo.x*LocalVector.x +
VectorTo.y*LocalVector.y +
VectorTo.z*LocalVector.z);
//Determine whether or not the angle is positive
//Get the cross product of the axis and the local vector
sf::Vector3<float> ThirdVect(Axis.y*LocalVector.z - Axis.z*LocalVector.y,
Axis.z*LocalVector.x - Axis.x*LocalVector.z,
Axis.x*LocalVector.y - Axis.y*LocalVector.x);
//If the dot product of that and the local vector is negative, so is the angle
if (ThirdVect.x*VectorTo.x + ThirdVect.y*VectorTo.y + ThirdVect.z*VectorTo.z < 0)
{
Angle = -Angle;
}
//Finally, create a quaternion
Quaternion AxisAngle;
AxisAngle.FromAxisAngle(Angle, Axis.x, Axis.y, Axis.z);
//And multiply it into the current orientation
m_Orientation = AxisAngle * m_Orientation;
}
This almost works. What happens is that the camera seems to rotate half the distance towards the Target point. If I attempt the rotation again, it performs half the remaining rotation, ad infinitum, such that if I hold down the "Look-At-Button", the camera's orientation gets closer and closer to looking directly at the target, but is also constantly slowing down in its rotation, such that it never quite gets there.
Note that I don't want to resort to gluLookAt(), as I will also eventually need this code to point objects other than the camera at one another, and my objects already use quaternions for their orientations. For example, I might want to create an eyeball that tracks the position of something moving around in front of it, or a projectile that updates its orientation to seek out its target.
Normalize Axis vector before passing it to FromAxisAngle.
Why are you using a quaternion? You're just making things more complex and requiring more computation in this instance. To set up a matrix:-
calculate vector from observer to observed (which you're doing already)
normalise it (again, doing it already) = at
cross product this with the observer's up direction = right
normalise right
cross product at and right to get up
and you're done. The right, up and at vectors are the first, second and third row (or column, depending on how you set things up) of your matrix. The final row/column is the objects position.
But it looks like you want to transform an existing matrix to this new matrix over several frames. SLERPs do this to matricies as well as quaternions (which isn't surprising when you look into the maths). For the transformation, store the initial and target matricies and then SLERP between them, changing the amount to SLERP by each frame (e.g. 0, 0.25, 0.5, 0.75, 1.0 - although a non-linear progression would look nicer).
Don't forget that you're converting a quaternion back into a matrix in order to pass it to the rendering pipeline (unless there's some new features in the shaders to handle quaternions natively). So any efficencies due to quaternion use has to take into account the conversion process as well.