**0**

# Align to velocity vector using Quaternions

###
#1
Members - Reputation: **100**

Posted 09 February 2010 - 03:44 AM

###
#2
Members - Reputation: **2073**

Posted 09 February 2010 - 10:13 AM

Quote:The question of whether the vector needs to be normalized should be easy enough to answer for yourself. What is the length of

Vec3D new_up = new Vec3D(0.0, 1.0, 0.0);

new_up.normalize(); // do I need to normalize this vector?

`new_up`after it's constructed? What does the 'normalize' operation do? Mathematically, would the 'normalize' operation change the vector in any way in this case? From that, you should be able to determine whether the normalization is needed.

Quote:For simply aligning an object with a direction vector (the velocity vector in this case), the position of the object doesn't matter.

Having a velocity vector and location vector how can I align my 3D object to face in the same direction as the velocity?

There are quite a few ways to align an object with a direction vector. The first thing you have to figure out is what direction the object 'points' in local space, so that you know what it is exactly that you need to align. For most objects, the local-space direction vector will be one of the cardinal axes (often z or x).

The next thing to realize is that there's an infinite number of orientations that will satisfy the specified constraints. Imagine an airplane flying straight up. The plane can roll around its local forward axis while still being aligned with its 'direction' vector. So one way or another, you need to narrow it down to a single orientation.

The 'fixed up' method you seem to be trying to use is one way to do that. It'll work fine so long as the direction vector isn't parallel or nearly parallel with the up vector. The full method would look something like this:

vector3 side = cross(up, forward);The above pseudocode performs the conversion by way of a matrix simply because 'quaternion from matrix' functions are fairly common, while 'quaternion from basis vectors' functions aren't so common (even though they're basically equivalent).

side.normalize();

up = cross(forward, side);

matrix m = matrix_from_basis_vectors(side, up, forward);

quaternion q = quaternion_from_matrix(m);

Another approach to aligning an object with a direction vector is to apply a relative rotation that rotates the object into alignment over the shortest arc. This can be done using an axis-angle computation, but there's also a nice quaternion-specific algorithm that does it a bit more elegantly.

###
#4
Members - Reputation: **100**

Posted 09 February 2010 - 10:38 PM

1. is there a way to avoid that? I can see that my object flip somethimes back and forward, is that the reason? if so can it be prevented?

2. will your pseudo code rotating using a quaternion fix the problem in 1?

Cheers

rS

###
#5
Members - Reputation: **2073**

Posted 10 February 2010 - 04:12 AM

Quote:The example I gave is susceptible to the problem I mentioned. The problem is here:

Original post by nardove

Hi jyk, so you said using my method "It'll work fine so long as the direction vector isn't parallel or nearly parallel with the up vector"

1. is there a way to avoid that? I can see that my object flip somethimes back and forward, is that the reason? if so can it be prevented?

2. will your pseudo code rotating using a quaternion fix the problem in 1?

// If 'up' and 'forward' are nearly parallel, the magnitude (length) of side will be small.From a conceptual standpoint, it makes sense that the algorithm doesn't work when the direction and up vectors are aligned. The purpose of the up vector is to provide additional information about how to orient the object, and if it's parallel to the direction vector, it provides no such information.

vector3 side = cross(up, forward);

// Normalizing a vector with small magnitude can have unpredictable results. Mathematically, you

// should be able to normalize any vector with non-zero length, but due to the limits of floating-point

// precision, normalization may fail even for vectors with very small magnitude (that is, you'll get

// undefined results, e.g. NaN's).

side.normalize();

// If normalizing the 'side' vector fails, everything from here on out will be invalid.

up = cross(forward, side);

matrix m = matrix_from_basis_vectors(side, up, forward);

quaternion q = quaternion_from_matrix(m);

If this case is likely to come up in your simulation, I'd recommend instead using relative rotations to align your object incrementally. The basic algorithm looks like this:

vector3 axis = cross(current_forward, desired_forward);That's off the top of my head, but I think I got it right.

float length = axis.length();

if (length > epsilon) {

axis /= length;

float angle = atan2(length, dot(current_forward, desired_forward));

quaternion q = quaternion_from_axis_angle(axis, angle);

orientation = q * orientation; // 'orientation' is a quaternion

orientation.normalize();

}

There's also a more elegant version of this algorithm that is specific to quaternions (the above is a general form of the algorithm that will work equally well with quaternions and matrices). I would try the above first and make sure it works, but if you want I can post the quaternion-specific version as well.

###
#6
Members - Reputation: **100**

Posted 10 February 2010 - 05:04 AM

-- INI CODE

Vec3D new_dir = new Vec3D(velocity);

new_dir.normalize();

Vec3D new_up = new Vec3D(0.0, 1.0, 0.0);

new_up.normalize(); // do I need to normalize this vector?

Vec3D new_side = new_dir.cross(new_up);

new_side.normalize();

float dotP = new_dir.dot(new_up);

float angle = new_dir.angleBetween(new_up, true);

pushMatrix();

// update location

translate(location.x, location.y, location.z);

// orientation to velocity

rotate(-angle, new_side.x, new_side.y, new_side.z);

/* draw happens here */

popMatrix();

-- END CODE

You said things will go wrong when new_side.normalize(); fails, and from there I have to check

float length = new_side.length();

if (length > EPSILON) {

new_side /= length;

....

from there you lost me, will it be possible provide a pseudocode using my logic to rotate instead of quaternions?

Thanks a lot this post has been great source of information and understanding

rS

###
#7
Members - Reputation: **2073**

Posted 10 February 2010 - 07:20 AM

Quote:Unfortunately I don't think I can provide a working version of your method, because I don't quite understand what it's doing (or trying to do). Among other things, there's some context missing; it's not really clear from your example (to me at least) how the transforms are being handled, and what the 'push' and 'pop' calls are supposed to do.

from there you lost me, will it be possible provide a pseudocode using my logic to rotate instead of quaternions?

Perhaps you could describe in a bit more detail what type of behavior you're after. Are you trying to get the object to rotate incrementally each update so as to align with the velocity vector? Or are you trying to build a transform 'from scratch' that will align the object with the velocity vector?

###
#8
Members - Reputation: **100**

Posted 10 February 2010 - 07:40 PM

"The pushMatrix() function saves the current coordinate system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in conjuction with the other transformation methods and may be embedded to control the scope of the transformations."

very similar to opengl gl.glPushMatrix() and gl.glPopMatrix()

Using this code I calculate my angle and axis of rotation

Vec3D new_dir = new Vec3D(velocity);

new_dir.normalize();

Vec3D new_up = new Vec3D(0.0, 1.0, 0.0);

new_up.normalize(); // do I need to normalize this vector?

Vec3D new_side = new_dir.cross(new_up);

new_side.normalize();

float dotP = new_dir.dot(new_up);

float angle = new_dir.angleBetween(new_up, true);

So angle = angle of rotation and new_side = axis

This code I do the matrix transformations

pushMatrix();

// update location

translate(location.x, location.y, location.z);

// orientation to velocity

rotate(-angle, new_side.x, new_side.y, new_side.z);

/* draw happens here */

popMatrix();

Here is a link to my work in progress, so you can see the problem, and you can also look at the code, all the prev code is in the Boids Class in the display() method

http://nardove.com/p5/swimming_jellie/swimming_jellie_test_2/

If you click and drag the jelly will follow the mouse position and if you are on top an drag to the sides you can see the jump in the rotation, if you let it wander it becomes more clear the sudden jump in the orientation

Cheers

rS

###
#9
Members - Reputation: **2073**

Posted 11 February 2010 - 06:26 AM

I'm sure I could figure it out if I examined your code carefully enough, but it would be easier if the example were a little more clear.

Actually, just answering this question from my previous post:

Quote:Would probably help.

Perhaps you could describe in a bit more detail what type of behavior you're after. Are you trying to get the object to rotate incrementally each update so as to align with the velocity vector? Or are you trying to build a transform 'from scratch' that will align the object with the velocity vector?

###
#10
Members - Reputation: **100**

Posted 11 February 2010 - 11:15 AM

and plug those 2 in the rotate method like this rotate(angle, croosp.x, crossp.y, crossp.z);

I can see that some times there is a flip of the rotation making the object to face the opposite direction and also if the object goes up it will do a 360 rotation int the y axis I belive.

This concept of 3D orientation is new to me, and from that it is dificult for me to explain in more detail.

I have a wander steering behavior going on, and I want the objects to face the same direction as their velocity, at the moment it is working but like I said there are moments were they go nuts.

Sorry if I cant explain it in more detail you see I am a rooky in the 3D programming stuff.

All your help its very much appreciated!

rS

###
#11
Members - Reputation: **2073**

Posted 11 February 2010 - 11:40 AM

The little 'spins' that it does are actually kind of neat, but I'm guessing they're not intentional :) Anyway, for this type of motion I would definitely recommend accumulated relative rotations. To keep things simple, I'll skip the quaternion-specific version of the algorithm and just describe the straightforward version. In pseudocode, it looks something like this (typed directly into the post, and not tested in any way):

// Somewhere during initialization:I think that's about the best advice I can offer at this point. If you have questions about the above pseudocode though, feel free to ask.

quaternion q = whatever_you_want_the_initial_orientation_to_be();

// For each update:

matrix m = matrix_from_quaternion(q);

vector3 forward = m.get_third_basis_vector(); // Ask if you're not sure what this means

vector3 desired_forward = normalize(velocity);

vector3 axis = cross(forward, desired_forward);

float length = axis.length();

if (length > epsilon) {

axis /= length;

float angle = atan2(length, dot(forward, desired_forward));

quaternion rotation = quaternion_from_axis_angle(axis, angle);

q = rotation * q;

q.normalize();

}

###
#12
Members - Reputation: **100**

Posted 11 February 2010 - 11:35 PM

So I try to translate your pseudocode to this

first initialize the variables

Quaternion quat = new Quaternion(1.0, 0.0, 0.0, 0.0);

Quaternion rotationQuat;

Matrix4x4 mat;

for the update

// INI CODE

mat = quat.getMatrix();

Vec3D forward = new Vec3D((float)mat.matrix[0][2],

(float)mat.matrix[1][2],

(float)mat.matrix[2][2]);

Vec3D desired_forward = forward.normalize();

Vec3D axis = forward.cross(desired_forward);

float leng = axis.magnitude();

if (leng > EPSILON) {

axis.scaleSelf(1.0 / leng);

float angle = atan2(leng, forward.dot(desired_forward));

rotationQuat = new Quaternion();

rotationQuat.createFromAxisAngle(axis, angle);

quat = quat.multiply(rotationQuat);

quat.normalize();

}

// perform transformation

pushMatrix();

// update location

translate(location.x, location.y, location.z);

// orientation to velocity

rotate(rotationQuat.w, rotationQuat.x, rotationQuat.y, rotationQuat.z);

agent.display();

popMatrix();

// END CODE

But I get rotationQuat = NULL

Any ideas?

Cheers

rS

###
#13
Members - Reputation: **2073**

Posted 12 February 2010 - 04:22 AM

Vec3D forward = new Vec3D((float)mat.matrix[0][2],Is probably right, but just be sure that your matrices are actually column-basis matrices (as opposed to having the basis vectors in the rows). Next, take a look at this line:

(float)mat.matrix[1][2],

(float)mat.matrix[2][2]);

Vec3D desired_forward = forward.normalize();And see if you can spot any difference between it and the corresponding line in the pseudocode I posted earlier.

Lastly, this:

rotate(rotationQuat.w, rotationQuat.x, rotationQuat.y, rotationQuat.z);Looks a little suspicious. I don't know what your rotate() function does, but keep in mind that although a quaternion

*encodes*an axis-angle rotation, under normal circumstances its elements cannot directly be interpreted as an axis-angle pair. (Typically, what you would do here is convert the quaternion to a matrix, and then upload the matrix to the graphics API.)

###
#14
Members - Reputation: **100**

Posted 12 February 2010 - 04:44 AM

Vec3D desired_forward = velocity.normalize();

It is way better this way, sorry for the old ones

Now when I print the rotationQuat instead of Null I get

**axis: [0.0,0.0,0.0], w: 1.0**constantly any ideas?

And I think you are right after I get the rotationQuat I will need to gt a rotation matrix, bu tI need to fix the logic first

Cheers

rS

###
#16
Members - Reputation: **2073**

Posted 12 February 2010 - 05:10 AM

2. Does atan2() return the angle in radians or degrees?

3. What is the value of EPSILON?

4. What do you see if you also print out the values of 'axis' and 'angle'?

[Edit: If there's something wrong with createFromAxisAngle(), that could explain it. If you have access to the code for that function, you might post it.]

###
#17
Members - Reputation: **100**

Posted 12 February 2010 - 06:11 AM

Everything is radians

EPSILON = 1.0E-4

...

length: 0.8714546

angle: 1.05816

axis: {x:1.6277998E-8, y:0.9906332, z:0.13654976}

rotationQuat: {axis: [0.0,0.0,0.0], w: 1.0}

length: 0.86895746

angle: 1.0530915

axis: {x:1.6521371E-8, y:0.99034965, z:0.13859132}

rotationQuat: {axis: [0.0,0.0,0.0], w: 1.0}

...

The library is a .jar file not sure how/if is possible to look inside

I am still testing and throwing arrows like crazy

Cheers

rS

###
#18
Members - Reputation: **2073**

Posted 12 February 2010 - 06:44 AM

Quote:With those axis-angle values, you shouldn't be getting an identity quaternion. What math library are you using? Can you post a link to the documentation?

length: 0.8714546

angle: 1.05816

axis: {x:1.6277998E-8, y:0.9906332, z:0.13654976}

rotationQuat: {axis: [0.0,0.0,0.0], w: 1.0}

length: 0.86895746

angle: 1.0530915

axis: {x:1.6521371E-8, y:0.99034965, z:0.13859132}

rotationQuat: {axis: [0.0,0.0,0.0], w: 1.0}

(Also, as a side note, it's a bit inaccurate to label the the x, y, and z elements of a quaternion as the 'axis'. When a quaternion is used to represent a rotation, these elements are related to the axis of rotation, but they are not equivalent to the axis of rotation.)

###
#20
Members - Reputation: **2073**

Posted 12 February 2010 - 07:40 AM

Quote:For URLs, use the HTML 'a' element along with the 'href' attribute to create clickable links.

Original post by nardove

Documentation can be found herehttp://toxiclibs.org/docs/core/

Whate are the [ tags ] for a url?

rS

It looks to me like createFromAxisAngle() is a static member function that returns a new quaternion, but it doesn't look like you're doing anything with the return value. I imagine this is why your quaternion is coming out as identity.