This is what I want:
What am I doing wrong with the following code. The output of the translation for the orbit rotation never occurs, it just ends up rotating all on the original axis.
void Renderer::UpdateAsteroid( Placement^ asteroidRotation, Placement^ sceneOrientation )
{
XMMATRIX selfRotation;
// Rotate the asteroid
selfRotation = XMMatrixRotationX( asteroidRotation->GetRotX() );
selfRotation = XMMatrixMultiply( selfRotation, XMMatrixRotationY( asteroidRotation->GetRotY() ) );
selfRotation = XMMatrixMultiply( selfRotation, XMMatrixRotationZ( asteroidRotation->GetRotZ() ) );
XMMATRIX translation;
// Move the asteroid to new location
translation = XMMatrixTranslation( sceneOrientation->GetPosX(), sceneOrientation->GetPosY(), sceneOrientation->GetPosZ() );
XMMATRIX orbitRotation;
// Rotate from moved origin
orbitRotation = XMMatrixRotationZ( sceneOrientation->GetRotZ() );
XMMATRIX outputMatrix = selfRotation * translation * orbitRotation;
// Store
XMStoreFloat4x4( &m_constantBufferData.model, XMMatrixTranspose( outputMatrix ) );
}
but it does not seem to work.. it just ends up rotating on the same point.
Matrix multiplication is not commutative, i.e. changing operands order changes result.
Typically you will want to calculate all of your matrces and then multiply together. Try this order first:
result = scale * selfRotation * translation * orbitRotation
If it's not what you want, just move matrices around to find right order.
Note, XMMath also have XMMatrixRotationRollPitchYaw() function to get rotation matrices in one call.
Related
Problem:
I have an object in 3D space that exists at a given orientation. I need to reorient the object to a new orientation. I'm currently representing the orientations as quaternions, though this is not strictly necessary.
I essentially need to determine the angular velocity needed to orient the body into the desired orientation.
What I'm currently working with looks something like the following:
Psuedocode:
// 4x4 Matrix containing rotation and translation
Matrix4 currentTransform = GetTransform();
// Grab the 3x3 matrix containing orientation only
Matrix3 currentOrientMtx = currentTransform.Get3x3();
// Build a quat based on the rotation matrix
Quaternion currentOrientation(currentOrientMtx);
currentOrientation.Normalize();
// Build a new matrix describing our desired orientation
Vector3f zAxis = desiredForward;
Vector3f yAxis = desiredUp;
Vector3f xAxis = yAxis.Cross(zAxis);
Matrix3 desiredOrientMtx(xAxis, yAxis, zAxis);
// Build a quat from our desired roation matrix
Quaternion desiredOrientation(desiredOrientMtx);
desiredOrientation.Normalize();
// Slerp from our current orientation to the new orientation based on our turn rate and time delta
Quaternion slerpedQuat = currentOrientation.Slerp(desiredOrientation, turnRate * deltaTime);
// Determine the axis and angle of rotation
Vector3f rotationAxis = slerpedQuat.GetAxis();
float rotationAngle = slerpedQuat.GetAngle();
// Determine angular displacement and angular velocity
Vector3f angularDisplacement = rotationAxis * rotationAngle;
Vector3f angularVelocity = angularDisplacement / deltaTime;
SetAngularVelocity(angularVelocity);
This essentially just sends my object spinning to oblivion. I have verified that the desiredOrientMtx I constructed via the axes is indeed the correct final rotation transformation. I feel like I'm missing something silly here.
Thoughts?
To calculate angular velocity, your turnRatealready provides the magnitude (rads/sec), so all you really need is the axis of rotation. That is just given by GetAxis( B * Inverse(A) ). GetAngle of that same quantity would give the total angle to travel between the two. See 'Difference' between two quaternions for further explanation.
SetAngularVelocity( Normalize( GetAxis( B * Inverse(A)) ) * turnRate )
You need to set the angular velocity to 0 at some point (when you reach your goal orientation). One way to do this is by using a quaternion distance. Another simpler way is by checking against the amount of time taken. Finally, you can check the angle between two quats (as discussed above) and check if that is close to 0.
float totalAngle = GetAngle( Normalize( endingPose * Inverse( startingPose ) ) );
if( fabs( totalAngle ) > 0.0001 ) // some epsilon
{
// your setting angular velocity code here
SetAngularVelocity(angularVelocity);
}
else
{
SetAngularVelocity( Vector3f(0) );
// Maybe, if you want high accuracy, call SetTransform here too
}
But, really, I don't see why you don't just use the Slerp to its fullest. Instead of relying on the physics integrator (which can be imprecise) and relying on knowing when you've reached your destination (which is somewhat awkward), you could just move the object frame-by-frame since you know the motion.
Quaternion startingPose;
Quaternion endingPose;
// As discussed earlier...
Quaternion totalAngle = Quaternion.AngleBetween( startingPose, endingPose );
// t is set to 0 whenever you start a new motion
t += deltaT;
float howFarIn = (turnRate * t) / totalAngle;
SetCurrentTransform( startingPose.Slerp( endingPose, howFarIn ) );
See Smooth rotation with quaternions for some discussion on that.
I'm working on a flight simulator. I've read a tutorial about quaternions, ( this one : http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/ ), so It it very new for me.
From what I've understand, quaternions should rotate an object using a direction vector and make the object's orientation match the direction, and rotate the quaternion should not move the vector, but just make him turn around himself. Am I true?
If yes, this is my code :
void Plane::Update()
{
m_matrix = GLM_GTX_quaternion::toMat4( GLM_GTX_quaternion::angleAxis( radians( m_angle ), normalize( m_planeDirection ) ) );
}
When my plane model's is pointing to x vector, my plane will rotate correctly around the x vector with the angle equal to 0, but if I change the angle, it will not rotate correctly. So how can I find the angle?
Yes, you are correct - Quaternion rotates an object around a directional vector. You should also use the glm::quat typedef when working with quaternions.
#include <glm/gtc/quaternion.hpp>
//...
glm::mat4 m = glm::mat4_cast(glm::angleAxis(glm::radians(m_angle), glm::normalize(m_planeDirection)));
glm::rotate function also works with quaternions
glm::mat4 m = glm::mat4_cast(glm::rotate(glm::quat(), 45.0f, glm::vec3(1.0f)));
I have two objects that I want to parent together so that Tri is a child of Torus. When I do so and multiply the matricies together by adding the parents modelView to the childs, the child jumps in space initially, over to the right and up by a few units. Where do I insert an offset into this, and how do I calculate it?
obj = make_shared<Object>(*this);
obj->rename("tri");
obj->type->val_s = "tri";
obj->t->val_3 = glm::vec3(-4.f, 1.5f, 0.f);
allObj.push_back(obj);
obj = make_shared<Object>(*this);
obj->rename("torus");
obj->type->val_s = "obj";
obj->t->val_3 = glm::vec3(3.f, 2.f, 0.f);
allObj.push_back(obj);
//Matrix
scaleM = glm::scale(glm::mat4(), s->val_3);
rotationM = glm::toMat4(r_quat);
glm::vec3 usablePivot = t->val_3 - pivot->val_3;
glm::mat4 localAxis1M = glm::translate(glm::mat4(), usablePivot);
glm::mat4 localAxis2M = glm::translate(glm::mat4(), -usablePivot);
translationM = glm::translate(glm::mat4(), t->val_3);
modelM = translationM * localAxis2M * rotationM * scaleM * localAxis1M;
//View
usableView = myGL->ViewM;
//Projection
usableProjection = myGL->ProjectionM;
//MVP
if (parent == "world") { MVP = usableProjection * usableView * modelM; }
else { MVP = usableProjection * usableView * parentTo->modelM * modelM; }
Matrix order in mine OpenGL apps:
sub-object matrices
the lowest in ownership hierarchy are first
they are local to their parents
their have offset to point (0,0,0) of parent space
if not then and they are local to world then this is the jump reason
in that case the sub-objects are not really sub objects
and handle them as normal object without parent
object to world
is transform matrix of objects without parent
they are local to your scene world
then goes camera view
it is inverse of camera space
where Z-axis is view direction
last is projection
like gluPerspective(...)
this one is usually stored in GL_PROJECTION matrix on fixed pipeline
instead of GL_MODELVIEW
Clipping is done by OpenGL separately via glViewport
(your usable view I think)
Look for more info here
you can check the content of your matrices
look at positions 12,13,14 where the offset vector is stored
do not forget that this vector is local to parent coordinate system!!!
so it is more likely rotated by it ...
also a good idea is to draw axis lines for each tested matrix in its parent space
to see if they are correct
I use red,gree,blue lines for x,y,z - axis
just extract origin of coordinate system [12,13,14]
and draw line from it to the same point + a*axis vector
a is line length (big enough so you see a line not a point)
axis vectors are at positions x=[0,1,2], y=[4,5,6], z=[8,9,10]
do not forget to set matrices to parent coordinate system !!!
if you handle matrices your self via GLSL then do not forget that
direction vectors like Normals are transformed without offset
so get the whole transform matrix (all multilicated together without projection and sometimes also camera)
set the offset to zero [12,13,14]=(0.0,0.0,0.0)
and multiply by this matrix
I have drawn a cube onto the screen and I want to both rotate and translate the scene:
// Translation
XMStoreFloat4x4( &m_constantBufferData.model, XMMatrixTranspose( XMMatrixTranslation( placement->GetPosX(), placement->GetPosY(), placement->GetPosZ() ) ) );
// Rotation
XMStoreFloat4x4( &m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationX( placement->GetRotX() ) ) );
XMStoreFloat4x4( &m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY( placement->GetRotY() ) ) );
XMStoreFloat4x4( &m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationZ( placement->GetRotZ() ) ) );
the problem is, only the translation is working... Do I have to set something somehow before doing the rotations too.
I have used the default Windows 8 Phone Direct3D C++ Project in Visual Studio 2012 Windows Phone.
I have passed in a few more variables and thanks to intellisense, found out there was a matrixtransaltion function
I added my positioning to this matrix and also hooked up the rotation to some custom variables too
The cube will move (translate) but I am guessing I need to save this movement somehow and THEN do the rotation.
Anything I can add to this to help solve the issue?
You are overwriting the contents of m_constantBufferData.model every time. You need to call XMMatrixMultiply on the four matrices to combine the transformations into a single matrix, then store the final result. For example:
// Rotation
XMMATRIX m = XMMatrixRotationX( placement->GetRotX() );
m = XMMatrixMultiply(m, XMMatrixRotationY( placement->GetRotY() );
m = XMMatrixMultiply(m, XMMatrixRotationZ( placement->GetRotZ() );
// Translation
m = XMMatrixMultiply(m, XMMatrixTranslation( placement->GetPosX(), placement->GetPosY(), placement->GetPosZ() ) );
XMStoreFloat4x4( &m_constantBufferData.model, XMMatrixTranspose(m) );
I'm currently implementing a C++ solution to track motion of multiple objects. In that I have tracked points of those objects in a frame sequences such that multiple points in each frame. As a result of that I have x, y, z coordinates of those points of the entire frame sequence. By studying an already generated model I understood it consists of a joints system which move relative to each other. Every joint has a parent and their movements are written relative to its parent in Quaternion format. Therefore, I want to convert my x,y,z coordinates, which are in 3D space relative to same origin, to quaternion format which are written as relative to its parent. I can then use the quaternions to animate them.
I don't understand how to calculate the angle that it requires. Can you please provide me a sample code (in c++) or any useful resources to overcome this problem.
So we have a system of connected joints and we want to find out the the relative delta rotation of the joints from one frame to another. I'll call the relative rotation the local rotation since the "relative rotation" on it's own doesn't tell us what it's relative to. (Is it relative to an object, to the center of the universe, etc?)
Assumptions
I'm going to assume a tree structure of joints so that each joint only has one parent and we only have one root joint (without parents). If you have several parents per joint you should be able to use the same solution, but you'll then each joint will one relative rotation per parent and you need to do the calcualtion once for each parent. If you have several joints without parents then each one can be thought of as a root in it's own tree made up of the connected joints.
I'll assume you have a quaternion math library that can; create from an axis and an angle, set to identity, inverse, and accumulate quaternions. If you don't you should find all the info you need to implement them from Wikipedia or Google.
Calulating the rotations
The code below first calculates the local rotations of the joint for the start and the end of the frame. It does this calculation using two vector; the vector from it's parent and the vector from the grandparent to the parent. Then in order to calculate the delta rotation it uses the inverted start rotation to "remove" the start rotation from the end rotation by applying it's inverse rotation. So we end up with the local delta rotation for that frame.
For the first two levels of the joint hierarchy we have special cases which we can solve directly.
Pseudocode
The out parameter is a multidimensional array named result.
NB: startPosition, endPosition, parentStartPosition, parentEndPosition, grandParentStartPosition, grandParentStartPosition all have to be updated for each iteration of the loops. That update is not shown in order to focus on the core of the problem.
for each frame {
for each joint {
if no parent {
// no parent means no local rotation
result[joint,frame] = identityQuaternion
}
else {
startLink = startPosition - parentStartPosition
endLink = endPosition - parentEndPosition
if no grandParent {
// no grand parent - we can calculate the local rotation directly
result[joint,frame] = QuaternionFromVectors( startLink, endLink )
}
else {
parentStartLink = parentStartPosition - grandParentStartPosition
parentEndLink = parentEndPosition - grandParentEndPosition
// calculate the local rotations
// = the difference in rotation between parent link and child link
startRotation = QuaternionFromVectors( parentStartLink, startLink )
endRotation = QuaternionFromVectors( parentEndLink, endLink )
// calculate the delta local rotation
// = the difference between start and end local rotations
invertedStartRotation = Inverse( startRotation )
deltaRotation = invertedStartRotation.Rotate( endRotation )
result[joint,frame] = deltaRotation
}
}
}
}
QuaternionFromVectors( fromVector, toVector )
{
axis = Normalize( fromVector.Cross( toVector ) )
angle = Acos( fromVector.Dot( toVector ) )
return Quaternion( axis, angle )
}
C++ implementation
Below is an untested recursive implementation in C++. For each frame we start at the root of our JointData tree and then traverse the tree by recursivly calling the JointData::calculateRotations() function.
In order to make the code easier to read I have an accessor from the joint tree nodes JointData to the FrameData. You probably don't want to have such a direct dependency in your implementation.
// Frame data holds the individual frame data for a joint
struct FrameData
{
Vector3 m_positionStart;
Vector3 m_positionEnd;
// this is our unknown
Quaternion m_localDeltaRotation;
}
class JointData
{
public:
...
JointData *getChild( int index );
int getNumberOfChildren();
FrameData *getFrame( int frameIndex );
void calculateDeltaRotation( int frameIndex, JointData *parent = NULL,
Vector3& parentV1 = Vector3(0),
Vector3& parentV2 = Vector3(0) );
...
}
void JointData::calculateDeltaRotation( int frameIndex, JointData *parent,
Vector3& parentV1, Vector3& parentV2 )
{
FrameData *frameData = getFrame( frameIndex );
if( !parent )
{
// this is the root, it has no local rotation
frameData->m_localDeltaRotation.setIdentity();
return;
}
FrameData *parentFrameData = parent->getFrame( frameIndex );
// calculate the vector from our parent
// for the start (v1) and the end (v2) of the frame
Vector3 v1 = frameData->m_positionStart - parentFrameData->m_positionStart;
Vector3 v2 = frameData->m_positionEnd - parentFrameData->m_positionEnd;
if( !getParent()->getParent() )
{
// child of the root is a special case,
// we can calculate it's rotation directly
frameData->m_localDeltaRotation = calculateQuaternion( v1, v2 );
}
else
{
// calculate start and end rotations
// apply inverse start rotation to end rotation
Quaternion startRotation = calculateQuaternion( parentV1, v1 );
Quaternion endRotation = calculateQuaternion( parentV2, v2 );
Quaternion invStartRot = startRotation.inverse();
frameData->m_localDeltaRotation = invStartRot.rotate( endRotation );
}
for( int i = 0; i < getNumberOfChildren(); ++i )
{
getChild( i )->calculateRotations( frameIndex, this, v1, v2 );
}
}
// helper function to calulate a quaternion from two vector
Quaternion calculateQuaternion( Vector3& fromVector, Vector3& toVector )
{
float angle = acos( fromVector.dot( toVector ) );
Vector3 axis = fromVector.cross( toVector );
axis.normalize();
return Quaternion( axis, angle );
}
The code is written for readability and not to be optimal.
Point3d Controller::calRelativeToParent(int parentID,Point3d point,int frameID){
if(parentID == 0){
QUATERNION temp = calChangeAxis(-1,parentID,frameID);
return getVect(multiplyTwoQuats(multiplyTwoQuats(temp,getQuat(point)),getConj(temp)));
}else{
Point3d ref = calRelativeToParent(originalRelativePointMap[parentID].parentID,point,frameID);
QUATERNION temp = calChangeAxis(originalRelativePointMap[parentID].parentID,parentID,frameID);
return getVect(multiplyTwoQuats(multiplyTwoQuats(temp,getQuat(ref)),getConj(temp)));
}}
QUATERNION Controller::calChangeAxis(int parentID,int qtcId,int frameID){ //currentid = id of the position of the orientation to be changed
if(parentID == -1){
QUATERNION out = multiplyTwoQuats(quatOrigin.toChange,originalRelativePointMap[qtcId].orientation);
return out;
}
else{
//QUATERNION temp = calChangeAxis(originalRelativePointMap[parentID].parentID,qtcId,frameID);
//return multiplyTwoQuats(finalQuatMap[frameID][parentID].toChange,temp);
return multiplyTwoQuats(finalQuatMap[frameID][parentID].toChange,originalRelativePointMap[qtcId].orientation);
}}
This is the algorithm I have used. I first calculated the relative vector of each frame wrt to it's parent. Here parent ID of root is 0. Then I calculated the relative vector in the model for each joint. This is called recursively.