1
\$\begingroup\$

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]; 
            Model.CopyAbsoluteBoneTransformsTo(AbsoluteBoneTransforms); 

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?

EDIT

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);
qRotation.Normalize();

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.

\$\endgroup\$
4
  • \$\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, 2013 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, 2013 at 10:51
  • \$\begingroup\$ related: gamedev.stackexchange.com/questions/23238/… \$\endgroup\$
    – teodron
    Oct 7, 2013 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, 2013 at 15:55

1 Answer 1

1
\$\begingroup\$

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);
  normalize(axis);

  qRot = quat_from_axis_angle<double>(axis, angle) * qRot;
\$\endgroup\$
6
  • \$\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, 2013 at 14:29
  • \$\begingroup\$ I'll update my question with what I did now. \$\endgroup\$
    – pinckerman
    Oct 7, 2013 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, 2013 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, 2013 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, 2013 at 17:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .