Jump to content
  • Advertisement
Sign in to follow this  

Scale quaternion rotation

This topic is 4745 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all, What I want is to store the angular velocity in a quaternion as follow: void addrotation(axis, angle) { thisrotation = quaternion.createfromaxis(axis, angle); accelerationquat = thisrotation * accelerationquat } And then: void update(delta_time) { objectorientation = accelerationquat * objectorientation. // Okey i skiped the angular velocity in this example.. } But I want to scale the accelerationquat with the delta_time. But nothing ive tried works. Ive tried the method of saving the angular acceleration in a 3d vector and then; spinquat = 0.5 * w * q where w = quat(angular.x, angular.y, angular.z, 0) and q is the current orientation quat. But I get some strange results. So saving the acceleration in a quat directly gives me corrent and smooth rotations.

Share this post


Link to post
Share on other sites
Advertisement
Ive tried that also, but it doesnt work.


I will explain some more on how I want the code to work.

I have a function called addRotation as shown.

This function takes a parameter that tells it if it is a local or world rotation.

So for an example. I have a camera that i want to drive with this code. (And no i dont want a basic camera, I want this to work :)


When draging the mouse up/down that node (camera) should rotate around local X and when draging left/right the node should rotate around world space Y.

So.
Doing this:

addRotation(1, 0, 0, DeltaY, Local);
addRotation(0, 1, 0, DeltaX, World);

Then in the addRotation function it looks kind of like this:


addRotation(Axis, angle, world) {
if (world)
invmatrix = absolutorientation.inverse();
invmatrix.applyto(Axis);
)



quaternion thisrotation.axis(axis, angle);
qacc = thisrotation * qacc;
}

Now the thing is that If I rotate Local X and World Y seperatly (That is, one frame rotate around x, and next frame rotate around Y it works, but when both rotations are added on the same frame it starts to flip.)



As the rotations shows, the X (sliding vector) should never be rotated on any other axis than around Y. So it should stay flat on the floor.

But when adding both rotations on the same frame it starts to rotate around Z, which ends up so that the sliding vector is pointing straight up which it is not suppose to do.

Everything ive tried to scale the acceleration quaternion give this result.
And if i dont scale it at all it works correct.

Any ideas?

Im in a bit hurry so ill check my post when i get back.

Cheers.

Share this post


Link to post
Share on other sites
Quaternions is good for rotations but bad for rotation velocities. Them just can not work really right because high rotation velocities will "wrap over".

You can store rotation velocity in single vector, as it is usually done in physics.
Let V is rotation velocity in world frame of reference, Q is quaternion you want to update, dt is time. Then,

float l=length(V);
if(l>0){
Q=AxisAngleToQuaternion(V*(1.0/l),l*dt)*Q;
}
will work perfectly.

What's good about it: you can add rotation velocities, for example. You can use inertia tensors, and so-on.
(Alternatively, you could use vector that specifies rotation axis and another value for rotation speed.)

Indeed,
AxisAngleToQuaternion
is not exactly fast (trigs), but it is not slower than quaternion exponentiation. As rule of thumb, if you do quaternion exponentiation it is quite likely that axisangle to quaternion could be more natural.

Share this post


Link to post
Share on other sites
I would store rotational velocities as Euler angles, or perhaps axis/angle.

To scale a quaternion rotation, you just slerp between the identity quaternion and the target quaternion, with the slerp factor being your scale weight. Most quaternion libraries already have a slerp function.

If you want to save time, you can actually use lerp() for each of the values, and re-normalize the quaternion. This isn't as accurate (in fact, may be very inaccurate if the target represents close to 180 rotation), but is faster.

Share this post


Link to post
Share on other sites
Thank you.

I have tried to save the velocity as both eular and axis before and then converting them to a quat as you pointed out but that still gave me the strange rotation of the x axis as I talked about earlier.

Im starting to think that the problem starts when transforming the rotation axis to local in the addrotation. But what confuses me is that it works if I multiply the quaternion directly. Insteed of increasing the acceleration as eular or axisangles. :/

Isnt this the right way?:

___________________________________________________________

addrotation(1, 0, 0, Angle, Local);
addrotation(0, 1, 0, Angle, World);
Update(delta_time);


void addrotation(x, y, z, Angle, State) {
if (state == World) {
Inverse = Current_Matrix.Inverse();
Inverse.Rotate(x, y, z);
}
AngularAcceleration += [x,y,z]*Angle;

}


void Update(delta_time) {

Angular_Velocity += AngularAcceleration * delta_time;
float Angle = Angular_Velocity.Magnitude();

Vector Axis = Angular_Velocity.Normalize();

SpinQuaternion.CreateFromAxis(Axis, Angle * delta_time);

MyOrientation = SpinQuaternion * MyOrientation;

MyOrientation.CreateMatrix(Current_Matrix);

// And then start all over again.
}


___________________________________________________________

I really hope its not correct cause it doesnt work :)

Share this post


Link to post
Share on other sites
I don't think your Euler angle math does what you think it should do. Adding to the "X" value of Euler angles won't necessarily make a thing rotate more around the X axis.

Also, your functions and quantities have confusing names. A function named "addrotation" (which seems like it should affect the actual orientation directly) updates a value named "acceleration" -- from acceleration, you integrate to (angular) velocity, from which you integrate again to actual rotation. It's likely that you actually mean "addRotationalVelocity" for the function name, and "velocity" (not acceleration) for the updated value. Typically, this will be expressed as "torque" and divided by the mass of the object, rather than expressed in actual delta-velocity, too.

The quantity you call "velocity" seems to be the actual rotation.

I suggest that you get your unit/quantities straight first, then compare your rotation code with that of a trustworthy quaternion library. Also, there are a number of books on game physics that might help, although they're likely pretty heavy on the math.

Share this post


Link to post
Share on other sites
If you store rotational velocity in a 3D vector (as required by a typical physics engine: rotVel = rotMomentum scaled by inverseRotInertia (rotVel and rotM in world frame)), you can support arbitrarily high rotational velocities, and with a good integrator, you can support proper precession at higher angular velocities.

Two example methods below. The first method is useful for higher order integrators (such as Runge-Kutta). The second method is effectively equivalent to the first method when using an Euler style integrator (however, it will not handle precession very well. Unless you are simulating spinning tops (or similar objects) or are not concerned about precession behavior, the second method can work fine). To be clear, the two methods presented below are effectively equivalent. To make the first version provide better precession behavior, it must be used with a higher order integrator (RK, etc.).

Quote:

/*

Two methods to update rotation with velocity:

(currentRot is a Quat4, currentVel is in world coordinates).

1:

Quat4 adr = .5f*mulQuat4BwIs0(currentRot,currentVel); // Note w is 0.f.
currentRot += adr*dt; // Add derivative.
currentRot.normalize();

2:

flt hdt = .5f*dt;
Quat4 rdr(hdt*currentVel.x,hdt*currentVel.y,hdt*currentVel.z,1.f); // Note w is 1.f.
currentRot = rdr ^ currentRot; // Rotate by delta transform. ^ = quat transform
currentRot.normalize();

*/


Code snippet from my Runge-Kutta (RK4) integrator using the first method (See David Baraff's paper(s) for more information, derivations, etc.):
(Plus symbols are being filtered during posts (forum bug?), escape sequence 43 does not appear to work in SOURCE blocks).

Quote:

struct RBState {
RigidBody * rb; // Pointer to rigid body associated with this state.
// State Variables at time t:
Vec3 center; // x : Center of rotation and center of mass.
Quat4 rot; // R : Rotational position.
Vec3 linM; // P : Linear momentum.
Vec3 rotM; // L : Rotional momentum

RBState() : rb(0) {}
RBState(RigidBody * irb,const Vec3 & c,const Quat4 & r,const Vec3 & lm,const Vec3 & rm) : rb(irb), center(c),rot(r),linM(lm),rotM(rm) {}

RBState derivative(flt currentTime);
};

inline RBState operator * (const RBState & s,flt n) {
return RBState(s.rb,s.center*n,s.rot*n,s.linM*n,s.rotM*n);
}
inline RBState operator * (flt n,const RBState & s) {
return RBState(s.rb,s.center*n,s.rot*n,s.linM*n,s.rotM*n);
}
inline RBState operator + (const RBState & a,const RBState & b) {
return RBState(a.rb,a.center + b.center,a.rot + b.rot,a.linM + b.linM,a.rotM + b.rotM);
}

inline void RigidBody::updateDerived(RBState & s) {
mrot = Mat3(s.rot.normalize());
rotVel = scaleLocal(s.rotM,rotMassIdealInv,mrot);
linVel = s.linM*linMassInv;
}// RigidBody::updateDerived

inline RBState RBState::derivative(flt currentTime) {
rb->clearForceAccum();
rb->computeForces(currentTime);
rb->updateDerived(*this);
Quat4 dRot = .5f*mulQuat4BwIs0(rot,rb->rotVel);
return RBState(rb,rb->linVel,dRot,rb->linForce,rb->rotForce);
} // RBState::derivative

// Runge-Kutta 4
template <class T>
class RK4ODEI : public TGODEI<T> {
flt h2,h6;
T k1,k2,k3,k4;
public:
RK4ODEI() {}
void setTime(flt deltaTime,flt currentTime=0) {
h = deltaTime;
h2 = deltaTime*.5f;
h6 = deltaTime*(1.f/6.f);
time = currentTime;
} // setStepSize
void update(void) {
flt time2 = time + h2;
k1 = current->derivative(time);
k2 = (*current + k1*h2).derivative(time2);
k3 = (*current + k2*h2).derivative(time2);
k4 = (*current + k3*h).derivative(time);
*current = *current + h6*(k1 + 2.f*(k2 + k3) + k4);
} // update
};


An energy accurate physics demo (circa 2000) using these principles here (will run continuously for days (perhaps weeks) without appreciable energy drift). Also shows energy accurate simultaneous contact handling.

Share this post


Link to post
Share on other sites
Thank you, and I know its not a correct representation and its not how everyone else is doing it. But I really want to know whats wrong and why its not working.

I might get this problem some other day :)


Edit:

After a couple of hours last night I saw why it couldnt work. That kind of rotation can not be done with just a single velocity.
Scaling that rotation will force that vector to slip.


[Edited by - Dies_Irae on May 26, 2005 11:58:55 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!