Mijin 100 Report post Posted March 27, 2006 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 0 Share this post Link to post Share on other sites
jyk 2094 Report post Posted March 28, 2006 Quote:Original post by MijinAlso, 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. 0 Share this post Link to post Share on other sites
Dmytry 1151 Report post Posted March 28, 2006 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_quat^{elapsed 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 errorsship_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? 0 Share this post Link to post Share on other sites
Mijin 100 Report post Posted March 29, 2006 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 momentumvoidMechanics::CalculateDelta (AxisAngle & out, const D3DXQUATERNION & currentRot, const D3DXQUATERNION & targetRot){ D3DXQUATERNION delta; D3DXQUATERNION conjugateOfCurrent; // TargetRot = quatDelta * currentRot so... // quatDelta = targetRot * currentRot-1 D3DXQuaternionConjugate(&conjugateOfCurrent, ¤tRot); 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 framevoidMechanics::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. :) 0 Share this post Link to post Share on other sites