Sign in to follow this  
Mijin

Quaternion components?

Recommended Posts

Mijin    100
I've been struggling to solve a quaternion problem now for days, if not weeks. Anyone who can help me with this will receive one of the following: money, sexual favours, eternal gratitude. 8) I'll start with as brief a problem description as I can and I'll clarify any unclear areas as necessary. ----- I'm working on a spaceship game. Each ship has an orientation described by a quaternion. Each frame a rotation difference (a 'delta') is applied to each ship's quaternion (new_orientation = old_orientation * (delta * elapsedTime)). While a ship's delta remains the same it will spin in the same direction at the same speed. It is the calculation of the delta that's the problem. The ship's mechanics need to have a degree of 'intelligence' to the force that is applied to angular momentum. I have a current rotation delta (Q_current), and a target rotation delta (Q_target) (both expressed as quaternions) and a constant; rotation acceleration (C_acceleration, scalar). I need to apply the following restrictions to Q_target before Q_target becomes the new delta (Q_new) for this frame: 1. The difference between Q_current and Q_target cannot exceed C_acceleration. Obviously the way I'm doing this by clamping against a dot product scaled by PI. 2. If Q_target is identity, then the C_acceleration force is used to 'cancel out' (dampen) Q_current. 3. If neither Q_current or Q_target is identity, then C_acceleration is still used to dampen Q_current. However, in this situation it only needs to dampen a component of the rotation. e.g. The axis of rotation for Q_current and Q_target is very similar, and they are in the same direction. Obviously here, we don't want to cancel out Q_current; we want to use C_acceleration to 'tip the axis' of rotation. 4. The C_acceleration force can be used to both dampen out Q_current, and apply a force in Q_target's direction, if necessary, in the same frame. 5. We don't want to overshoot Q_target. There is no requirement to use all of C_acceleration. --------- It is requirement 3 that is the main issue. I think I need to separate out the current delta into 'parallel' and 'remainder' components but I can't seem to come up with a working algorithm. Also, is the whole concept of representing a quaternion delta as a quaternion flawed? Should I be using differentials? Thanks in advance

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Original post by Mijin
Also, is the whole concept of representing a quaternion delta as a quaternion flawed? Should I be using differentials?
I'm not a physics guru, so take this for what it's worth. But I think it's more common to represent angular effects (acceleration, velocity, momentum, torque) as vectors. Torque is the derivative of angular momentum, and similarly angular acceleration is the derivative of angular velocity; these can be fed into an integrator in a straightforward manner.

It's also fairly straightforward to calculate the derivative of orientation in either matrix or quaternion form. I don't remember the quaternion form off the top of my head, but it's something like q' = .5*quat(w)*q, where w is angular velocity.

This is all nicely summed up here in Baraff's papers. I may be missing the point of your post, in which case you can ignore all this. But if you're just looking for more or less 'correct' behavior in the presence of external forces, this may be what you're looking for.

Share this post


Link to post
Share on other sites
Dmytry    1151
First,
new_orientation = old_orientation * (delta * elapsedTime)
is obviously incorrect : (A * B) * C = A * (B * C) is still true for quaternions, your elapsedTime simply scales whole thing. Correct formula would be old_orientation * rotation_speed_quatelapsed time but that has it's own problems, e.g. rotation speeds will flip ower.


Angular velocity is best represented as 3D vector with direction of vector giving "axis" and length of vector giving speed.
To compute the turn during time dt, you should do something like that:

double len=length(angular_velocity);
if(len>0){// you may put something very small there e.g. 1E-20
angle=len*dt;

// depending to in what frame angular velocity is defined you need to use one of the following
ship_orientation=ship_orientation*axis_angle_to_quaternion(angular_velocity*(1.0/len),angle);
//ship_orientation=axis_angle_to_quaternion(angular_velocity*(1.0/len),angle)*ship_orientation;

// in axis_angle_to_quaternion first parameter is normalized axis and second is angle

// to prevent roundoff errors
ship_orientation.Normalize();
}





If turn during single frame is small, you can use first order approximation (as in sin(x) = x for small x) that is very simple:

Quaternion turn;
turn.w=1.0;
turn.xyz=0.5*angular_velocity;
ship_orientation=ship_orientation*turn;// or other way around dependingto whever angular velocity is defined in ship's frame or world frame respectively.
// normalize orientation (necessary - turn is not exactly unit length)
ship_orientation.Normalize();





As for damping, controlling, etc. since angular velocity is a vector now (and angular acceleration too) it should be simple to figure out (e.g. damp it like you'd damp ship's velocity)
I'm is not quite sure wnat exactly you want, bot that track target?

Share this post


Link to post
Share on other sites
Mijin    100
Just thought I'd say thanks for the help guys. Basically, the suggestion of storing angular momentum as a vector was enough.

I'd already considered doing this but....when you've rewritten the same chunk of code many times you get reluctant to make changes until you're sure it will be an improvement this time [depressed].

Oh, and these are the core functions of the new code, in case anyone is interested. You won't be able to run them as is though, because they use my own struct AxisAngle and class Vector3, but you'll get the idea.



// Convert the Quaternion target into a target angular momentum
void
Mechanics::CalculateDelta (AxisAngle & out, const D3DXQUATERNION & currentRot, const D3DXQUATERNION & targetRot)
{
D3DXQUATERNION delta;
D3DXQUATERNION conjugateOfCurrent;

// TargetRot = quatDelta * currentRot so...
// quatDelta = targetRot * currentRot-1
D3DXQuaternionConjugate(&conjugateOfCurrent, &currentRot);
delta = targetRot * conjugateOfCurrent;

D3DXQuaternionToAxisAngle(&delta, (D3DXVECTOR3*)&out.axis, &out.angle);
out.axis.Normalise();
}


// Based on the desired angular momentum, the current angular momentum and the
// ship's rotate speed, calculate the angular momentum to set this frame
void
Mechanics::ConstrainDelta (AxisAngle & outDelta, float & inOutThrust, const AxisAngle & targetDelta, const AxisAngle & currentDelta)
{
AxisAngle difference;
Vector3 current;
Vector3 target;

// We need to calculate the axis and angle of rotation that will make currentDelta become targetDelta
// So we work backwards, calculating the difference required from subtracting
// current from target rot

target = targetDelta.axis * targetDelta.angle;
current = currentDelta.axis * currentDelta.angle;

difference.axis = target - current;
difference.Normalise();

if (difference.angle > inOutThrust)
{
difference.angle = inOutThrust;
}

// And the out variables can be written
outDelta.axis = (currentDelta.axis * currentDelta.angle) + (difference.axis * difference.angle);
outDelta.Normalise();

inOutThrust -= difference.angle;
}





Of the three prizes, you can have...my eternal gratitude. :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this