Author Topic: The previous rotation gets cleared when rotating around another axis  (Read 22627 times)

Offline ani.tumanyan

  • byte
  • *
  • Posts: 10
    • View Profile
Hi raft,

I'm using JPCT-AE to display my ninja object on an Android phone. What I'm trying to do it to rotate different joints of the ninja. Because of the human structure people can rotate their neck around different axis (twist, bend, flexion and extension). But because all the rotations are with the same joint, I'm getting a weird problem. Once I have twisted the neck by some angle, and I'm trying to add some blending rotation the neck gets to it's original position and everything starts from scratch. I'm not sure what I'm doing wrong here, as I don't have a lot of experience in this field. Here is the section of performing rotation:

Code: [Select]
private void rotateJoint(SkeletonPose pose, int jointIndex, SimpleVector bindPoseDirection, float angle, final float targetStrength) {
   
        final int parentIndex = pose.getSkeleton().getJoint(jointIndex).getParentIndex();

        final Matrix jointInverseBindPose = pose.getSkeleton().getJoint(jointIndex).getInverseBindPose();
        final Matrix jointBindPose = jointInverseBindPose.invert();
       
        // Get a vector representing forward direction in neck space, use inverse to take from world -> neck space.
        SimpleVector forwardDirection = new SimpleVector(bindPoseDirection);
        forwardDirection.rotate(jointInverseBindPose);

        // Calculate a rotation to go from one direction to the other and set that rotation on a blank transform.
        Quaternion quat = new Quaternion();       
        quat.fromAngleAxis(angle, forwardDirection);
        quat.slerp(Quaternion.IDENTITY, quat, targetStrength);

        final Matrix subGlobal = quat.getRotationMatrix();
       
        subGlobal.matMul(jointBindPose);
        subGlobal.matMul(pose.getSkeleton().getJoint(parentIndex).getInverseBindPose());
       
        // set that as the neck's transform
        pose.getLocal(jointIndex).setTo(subGlobal);
    }




Ani
« Last Edit: March 22, 2013, 09:20:00 pm by ani.tumanyan »

Offline raft

  • Moderator
  • quad
  • *****
  • Posts: 1993
    • View Profile
    • http://www.aptalkarga.com
Re: The previous rotation gets cleared when rotating around another axis
« Reply #1 on: March 22, 2013, 10:15:39 pm »
yes, that's expected. every time you call that method for a specific bone, you overwrite previous values for that bone. those modifications are not cumulative.

this is the part which actually modifies that specific bone's transformation
Code: [Select]
pose.getLocal(jointIndex).setTo(subGlobal);

Offline ani.tumanyan

  • byte
  • *
  • Posts: 10
    • View Profile
Re: The previous rotation gets cleared when rotating around another axis
« Reply #2 on: March 25, 2013, 04:35:09 pm »
Thanks raft!! But is it somehow feasible to combine the rotation modifications ?
« Last Edit: March 26, 2013, 12:26:50 am by ani.tumanyan »

Offline raft

  • Moderator
  • quad
  • *****
  • Posts: 1993
    • View Profile
    • http://www.aptalkarga.com
Re: The previous rotation gets cleared when rotating around another axis
« Reply #3 on: March 26, 2013, 07:52:44 am »
it depends on the application. if you are asking for performance, it's negligible. calculating and setting some transformation on a bone has almost no impact, expensive part is applying bone transformations to mesh.

Offline ani.tumanyan

  • byte
  • *
  • Posts: 10
    • View Profile
Re: The previous rotation gets cleared when rotating around another axis
« Reply #4 on: March 26, 2013, 11:23:12 pm »
Now I'm really confused :(. I know that my questions might be very basic but please bear with me. In your first reply you told me that those modifications are not cumulative. What I really want to know is how to add the previous position of the neck joint to the rotation matrix before doing this action
Code: [Select]
pose.getLocal(jointIndex).setTo(subGlobal);, so instead of resetting the joint to it's original position it will continue to rotate around ANOTHER axis.

Offline raft

  • Moderator
  • quad
  • *****
  • Posts: 1993
    • View Profile
    • http://www.aptalkarga.com
Re: The previous rotation gets cleared when rotating around another axis
« Reply #5 on: March 27, 2013, 08:12:34 am »
well just store that rotation (whatever it is) in a field of class and apply that cumulatively.

for example:

Code: [Select]
    Matrix rotation = new Matrix();
   
    private void targetJoint(SkeletonPose pose, int jointIndex, SimpleVector bindPoseDirection,
    SimpleVector targetPos, final float targetStrength) {
   
        // previous part is same in sample         

        if (jointIndex == 13) { // apply to only neck
        SimpleVector rotationAxis = new SimpleVector(0, 1, 0);
        rotation.rotateAxis(rotationAxis, 0.01f);
        quat.rotate(rotation);
        }
        final Matrix subGlobal = quat.getRotationMatrix();
       
        // now remove the global/world transform of the neck's parent bone, leaving us with just the local transform of
        // neck + rotation.
        subGlobal.matMul(jointBindPose);
        subGlobal.matMul(pose.getSkeleton().getJoint(parentIndex).getInverseBindPose());

        // set that as the neck's transform
        pose.getLocal(jointIndex).setTo(subGlobal);
    }

this actually turns the neck cumulatively, but since we used a bogus rotation axis, the results are also bogus

Offline ani.tumanyan

  • byte
  • *
  • Posts: 10
    • View Profile
Re: The previous rotation gets cleared when rotating around another axis
« Reply #6 on: March 27, 2013, 06:18:10 pm »
Thanks raft!!! It saved me lots of time and effort.

This is eventually what I ended up doing, in case someone else would be wondering:

Code: [Select]
private void rotateJoint(SkeletonPose pose, Matrix rotation, int jointIndex, SimpleVector bindPoseDirection, float angle, final float targetStrength) {
    final int parentIndex = pose.getSkeleton().getJoint(jointIndex).getParentIndex();

        // neckBindGlobalTransform is the neck bone -> model space transform. essentially, it is the world transform of
        // the neck bone in bind pose.
        final Matrix jointInverseBindPose = pose.getSkeleton().getJoint(jointIndex).getInverseBindPose();
        final Matrix jointBindPose = jointInverseBindPose.invert();
       
        // Get a vector representing forward direction in neck space, use inverse to take from world -> neck space.
        SimpleVector forwardDirection = new SimpleVector(bindPoseDirection);
        forwardDirection.rotate(jointInverseBindPose);

        // Calculate a rotation to go from one direction to the other and set that rotation on a blank transform.
        Quaternion quat = new Quaternion();
    rotation.rotateAxis(bindPoseDirection, angle);
    quat.rotate(rotation);

        final Matrix subGlobal = quat.getRotationMatrix();
       
        // now remove the global/world transform of the neck's parent bone, leaving us with just the local transform of
        // neck + rotation.
        subGlobal.matMul(jointBindPose);
        subGlobal.matMul(pose.getSkeleton().getJoint(parentIndex).getInverseBindPose());

        // set that as the neck's transform
        pose.getLocal(jointIndex).setTo(subGlobal);
    }