I have some problems with a rotating marble.
I've tried it with Matrix.CreateFromYawPitchRoll and Matrix.CreateRotation but there were some problems, I think it's due to the Gimbal lock effect.

So, I've tried using quaternions instead, but nothing changed.
When moving on only an axis it works fine, but when the rotation occurs on two different axes the marble still rotates on wrong axes.

Here's my code:

// declarations
Vector3 Position = Vector3.Zero;
Vector3 Rotation = Vector3.Zero;
Quaternion qRotation = Quaternion.Identity;

AbsoluteBoneTransforms = new Matrix[Model.Bones.Count]; 

In the Update method:

Position += speed;
Rotation = speed * MathHelper.ToRadians(-1.5f);

Quaternion rot = Quaternion.CreateFromAxisAngle(Vector3.Right, Rotation.Z) *
                Quaternion.CreateFromAxisAngle(Vector3.Backward, Rotation.X);

qRotation *= rot;

And in the Draw method:

effect.World = AbsoluteBoneTransforms[mesh.ParentBone.Index] * 
  Matrix.CreateFromQuaternion(qRotation) * Matrix.CreateTranslation(Position);

What's wrong?


I've tried calculating directly the axis of rotation of my marble, instead of using combination of multiple axes:

angle += speed.Length() * angularVelocity;
qRotation = Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.Up), angle);

angle is a float that keeps track of the current movement. This solution doesn't seem to create Gimbal lock, but marble rotations aren't correct, it seems that the rotating speed is not constant, but became faster and slower over time, I can't understand why.

  • \$\begingroup\$ I'm gonna have to throw a remark at you: quaternion operations can also easily lead to gimbal lock! See this for an explanation opengl.org/discussion_boards/showthread.php/… . However, a gimbal lock occurs if and only if one axis moves rigidly with another (i.e. you really have a gimbal-like joint hierarchy). Your problem surely comes from how you compute your rot quaternion. To solve it, try and directly compute the axis of rotation and the angle, instead of composing two rotations... and rotates on wrong axes = in what way? \$\endgroup\$ – teodron Oct 7 '13 at 8:57
  • \$\begingroup\$ I've tried to compute directly the axis of rotation like this: Vector3 axis = Vector3.Cross(speed, Vector3.Up); but the rotation is still wrong. "Rotates on wrong axes" means that if I rotate on X and Z both it seems that my rotation involves Y, too. \$\endgroup\$ – pinckerman Oct 7 '13 at 10:51
  • \$\begingroup\$ related: gamedev.stackexchange.com/questions/23238/… \$\endgroup\$ – teodron Oct 7 '13 at 13:44
  • \$\begingroup\$ I've corrected a mistake (not updating the quaternion) and I've added a few remarks you should really consider to achieve a more synchronized effect \$\endgroup\$ – teodron Oct 7 '13 at 15:55

Although I don't know any XNA, this might help you "cheat" your way into achieving that rolling effect of the marble:

Position += speed;

angle = Magnitude(speed) * MathHelper.ToRadians(-1.5f);

Quaternion rot = Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.UP), angle);

qRotation *= rot;

So, instead of multiplying quaternions and achieving a chaotic wobbly effect, you should only update the angular position and use that to achieve the rolling effect.

Later edit:

What happens when your sphere moves from position A to position B:

  • the speed is used to translate it (you should consider adding a deltaTime to make it more FPS independent)
  • you know how much the ball moves: |AB| = |speed| (for your particular case) so you can determine how much it has rolled/rotated around the cross(speed, up) vector/axis. This amount is the angle |AB|/(2*pi*radius) - you should incorporate the marble radius to achieve believable roll with no sliding/slippage
  • lastly, qRotation is updated to reflect the sphere's current rotation w.r.t. to its starting pose

Of course, the correct way to achieve rolling behaviour from a physically accurate POV is to use the inertia tensor.

I implemented a short OpenGL demo using the following snippet to update the rolling sphere (and it worked):

  double radius = 0.1;
  static Quaternion qRot = identityq<double>();
  static double dTime = 0.002;

  Position += Velocity * dTime;
  double angle = length(Velocity) / radius * dTime;
  Vector3 axis = cross(Vector3(0,0,1), Velocity);

  qRot = quat_from_axis_angle<double>(axis, angle) * qRot;
  • \$\begingroup\$ I've already tried something like this, but maybe I'm doing something wrong. Your answer seems the right way, I'll try. \$\endgroup\$ – pinckerman Oct 7 '13 at 14:29
  • \$\begingroup\$ I'll update my question with what I did now. \$\endgroup\$ – pinckerman Oct 7 '13 at 15:27
  • \$\begingroup\$ I don't know why, but only if I overwrite qRotation (instead of multiplying qRotation *= rot) the Gimbal lock effect disappears. \$\endgroup\$ – pinckerman Oct 7 '13 at 16:14
  • \$\begingroup\$ That's really not gimbal lock. I forgot to modify the angle computation :(.. sorry. You're not supposed to accumulate angle offsets like in my initial code, you must directly use them to update the quaternion. The quaternion update will accumulate the correct rotation offset. \$\endgroup\$ – teodron Oct 7 '13 at 16:27
  • \$\begingroup\$ Ok, that's clear, anyway if I update the quaternion the resultant rotation still occurs on wrong axes :( \$\endgroup\$ – pinckerman Oct 7 '13 at 17:07

Your Answer

