This forum has been archived. All content is frozen. Please use KDE Discuss instead.

aligning to arbitrary vector using quaternions

Tags: None
(comma "," separated)
hurley
Registered Member
Posts
6
Karma
0
Howdy,

I have a game object that I want to hug an arbitrary surface as it moves by aligning it's up vector to the surface normal. My code works ok on a flat surface but spins incorrectly when it hits a surface triangle with a different normal and has to rotate. Here's the code I'm using. Can anyone tell me what I'm doing wrong?

Code: Select all
Vector3f p(surfaceSpot);
Vector3f newUp(surfaceNormal);
Vector3f oldUp(pBody->component->worldTransform.linear().col(1));

Quaternionf tilt;
tilt.setFromTwoVectors(oldUp, newUp);

Quaternionf q( pBody->component->worldTransform.linear() );
q = q * tilt;

p = p + (halfExtents[1] * newUp);
pBody->component->worldTransform.translation() = p;
pBody->component->worldTransform.linear() = q.toRotationMatrix();
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
Why do you use quaternion for that purpose? You can directly assemble your world transTransform matrix by assigning the normal vector to the third column, and then apply two cross product to orthogonalize it.
hurley
Registered Member
Posts
6
Karma
0
ggael wrote:Why do you use quaternion for that purpose? You can directly assemble your world transTransform matrix by assigning the normal vector to the third column, and then apply two cross product to orthogonalize it.


I chose quaternion for this purpose because my object needs to maintain it's heading ( rotation around up ) as it travels along the surface mesh. I don't have much experience with quaternions butI thought using a quaternion would mean only the up vector would rotate... leaving the right and front vectors the same which is what I need

If I assemble the transform matrix using cross products, where do I get the vector to use in first cross product with the up vector? It has to be perpendicular to the up vector and there are an infinite number to choose from.

Thanks for the help!

hurley
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
They all have to change anyway, e.g.:
Code: Select all
new_up = normal;
new_right = front.cross(new_up);
new_front = new_up.cross(new_right);
linear() << new_right, new_front, new_up;
hurley
Registered Member
Posts
6
Karma
0
ggael wrote:They all have to change anyway, e.g.:
Code: Select all
new_up = normal;
new_right = front.cross(new_up);
new_front = new_up.cross(new_right);
linear() << new_right, new_front, new_up;


But this way I have to try to compute a new heading every time since the right and front are rotating around up. I've got it working with quaternions now with a little help from google. This code keeps the body sitting on the surface and maintains the same general heading without any extra work as it travels along

Code: Select all
// http://www.gamedev.net/topic/429507-finding-the-quaternion-betwee-two-vectors/
// http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/minorlogic.htm

Vector3f oldUp(pBody->component->worldTransform.linear().col(1).normalized());
Quaternionf q;
q.vec() = oldUp.cross(surfaceNormal);
q.w() = 1 + oldUp.dot(surfaceNormal);  // both vectors must be unit
q.normalize();
pBody->component->worldTransform.prerotate(q);
pBody->component->worldTransform.translation() = surfaceSpot + halfExtents[1] * surfaceNormal;
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
I guess that the best choice depends on your application. "my" solution minimize the rotation of the head vector, while "yours" balances the in-plane rotation between the head and right vectors.
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
btw, don't miss the built-in setFromTwoVectors() function: http://eigen.tuxfamily.org/dox/classEig ... cadf821452
hurley
Registered Member
Posts
6
Karma
0
btw, don't miss the built-in setFromTwoVectors() function

Thanks I started there but couldn't get it to work because I didn't know enough to use prerotate. :<

I'm using it now and it works great ... but I have a new problem. I need to be able to flip the object so it's top is aligned to the surface instead of it's bottom. I've tried using setFromTwoVectors() again using the surface normal and it's opposite vector. It works ok with some surface normals but becomes unstable over others. Here's a video of what's happening and the code I'm using. Can you tell me what I'm doing wrong?

Thanks for the help!

http://www.hurleyworks.com/media/flash/AlignToAxis/AlignToAxis.html

Code: Select all
Vector3f newUp(surfaceNormal);
Vector3f oldUp(pBody->component->worldTransform.linear().col(1));
Quaternionf q1;
q1.setFromTwoVectors(oldUp, newUp);
if( sense == Sense::Positive )
{
   Quaternionf q2;
   q2.setFromTwoVectors(surfaceNormal, surfaceNormal * -1.0f);
   pBody->component->worldTransform.prerotate(q1 * q2);
}
else
   pBody->component->worldTransform.prerotate(q1);
pBody->component->worldTransform.translation() = surfaceSpot + halfExtents[1] * newUp;
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
You should do the flip at end, not when computing the alignment rotation.
hurley
Registered Member
Posts
6
Karma
0
ggael wrote:You should do the flip at end, not when computing the alignment rotation.


I've tried that but get the same result though as in the video

Code: Select all
Vector3f newUp(surfaceNormal);
Vector3f oldUp(pBody->component->worldTransform.linear().col(1));
Quaternionf q1;
q1.setFromTwoVectors(oldUp, newUp);
pBody->component->worldTransform.prerotate(q1);
pBody->component->worldTransform.translation() = surfaceSpot + halfExtents[1] * newUp;

if( sense == Sense::Positive )
{
   Quaternionf q2;
   q2.setFromTwoVectors(surfaceNormal, surfaceNormal * -1.0f);
   pBody->component->worldTransform.prerotate(q2);
}
pBody->component->worldTransform.translation() = surfaceSpot + halfExtents[1] * newUp;
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
Oh, I did not look carefully your initial code, but the problem is:

q2.setFromTwoVectors(surfaceNormal, surfaceNormal * -1.0f);

for which there exist an infinity of solution. You should rather post-rotate with a rotation around the x or y axis depending on the expected behavior.
hurley
Registered Member
Posts
6
Karma
0
ggael wrote:Oh, I did not look carefully your initial code, but the problem is:

q2.setFromTwoVectors(surfaceNormal, surfaceNormal * -1.0f);

for which there exist an infinity of solution. You should rather post-rotate with a rotation around the x or y axis depending on the expected behavior.


I couldn't get that to work for me but I did find a simple elegant solution that works for all cases. ... I just flipped the newUp vector if I needed to flip the object. It works perfectly for me. Thanks very much for your help!

Code: Select all
Vector3f newUp(surfaceNormal);
Vector3f oldUp(pBody->component->worldTransform.linear().col(axis));
if( sense == Sense::Positive )
newUp *= -1.0f;

Quaternionf q1;
q1.setFromTwoVectors(oldUp, newUp);
pBody->component->worldTransform.prerotate(q1);
pBody->component->worldTransform.translation() = surfaceSpot +  halfExtents[axis] * surfaceNormal;


Bookmarks



Who is online

Registered users: Baidu [Spider], Bing [Bot], Google [Bot], rblackwell