Search code examples
c#mathrotationoffsetquaternions

C# and XNA rotate an object around another object keeping the offset (or HOW TO SKIN A RANDOM POINT AGAINS A RANDOM BONE)


I have an object X, positioned in the World space, represented by its quaternion, lets call the latter X_Base. I have another object Y, offset from object X and presented by its quaternion matrix called Y_Base.

That's their positions at time zero, at time 1 they change their positions. Object Y rotates around it's axis for some angle and I know it's new quaternion it is Y_New. X is rotated relatively to Y so to keep the offset held at time 0. What I need is basically X_New.

In English, I am trying to manually skin a model. I have a mesh that is offset from the bone for some distance and I need it to keep this offset when the bone rotates. Somewhy I can't find a clear answer as to what formula I need to use.

Would be grateful for any advise.

A bit more clarification:

imagine a solar system, Earth rotates around the Sun and around it's axis. Let's put it so that Moon does not rotate around Earth but it is offset from Earth and keeps the offset no matter how you transform Earth's position. What I need is to find out where the Moon rests at time1, while knowing where it was at time0 and where Earth was at time0 and is at time1.


Solution

  • I was able to skin my model. Here is what I did:

    1. "Sun" is my model's bone. It can translate (in world space) and rotate. "Earth" is a custom point on a model that moves rotated/translated with the model as my goal implies that I can't just pick any point and skin it, I need the latter to move with the model.

    2. I have two methods in my app: one that launches once (Initialize) and another one that launches every frame (Update). In Initialize method my model is always at T-Pose.

    3. In Initialize method I read the position of the Sun and Earth as 2 XNA Matrix.

    4. In Initialize I also compute

      Vector3 Difference = TPoseEarth.Translation - TPoseSun.Translation;
      
    5. Finally in Initialize I decompose Sun's Matrix to gain pure Sun's rotation matrix (without translation). I do so because my model's bone Sun apparently has a non-identity matrix assigned to it even at T-Pose. like so:

      Vector3 scale;
      Quaternion rotation;
      Vector3 tra;
      TPoseSun.Decompose(out scale, out rotation, out tra);
      
      BaseSunRot = Matrix.CreateFromQuaternion(rotation);
      
    6. In Update method I get the current World position of Earth and Sun. I decompose the Sun's Matrix to get the pure rotation (again!).

      Vector3 scale;
      Quaternion rotation;
      Vector3 tra;
      Sun.Decompose(out scale, out rotation, out tra);
      
      Matrix SunRot = Matrix.CreateFromQuaternion(rotation);
      
    7. I compute the unique bone-Sun rotation by multiplying the inverse of my base rotation by my new rotation:

      Matrix UniqueRot = Matrix.Invert(BaseSunRot) * SunRot;
      
    8. I set my Earth's Translation equal to the Difference (Between Sun's T-Pose position and Earth's T-Pose position):

      Matrix Earth = Matrix.Identity;
      Earth.Translation = Difference;
      
    9. I multiply Earth matrix by the unique bone's (Sun's) rotation:

      Earth *= UniqueRot;
      
    10. I add Sun's CURRENT position to provide for possible translations between frames:

      Earth += Sun.Translation;
      
    11. The End. Earth matrix contains all the necessary info about the skinned vertex.

    I am not 100% sure but I feel like this is basically a manual on how to skin ANY given point agains ANY bone in the world (i.e rotate/translate keeping the initial offset). The problem is, the code is not optimized :). If anyone have any ideas on how to optimize it I would HUGELY appreciate it.

    Especially I want to know if I can skip the double matrix decomposition and simply multiply the full ScaleRotationTranslation matrices? I am trying it but without any success.