• Runtimes
  • worldToLocalRotation() is inaccurate, expected angle ±offset

I imported skeleton in webgl runtime and apply the locatation/rotatation/scale of all bones with global(worldX) angles I parsed from other skeleton animation.

all bones are animated as I expected, except for one mischievous bone that rotates back and forth around the angle that it should be.

Here's the video of wrong bone movement :

And here's the video of correct bone movement(I animated Spine skeleton with the projected-from-3d-to-2d-plane coords values of joints of a 3d model in Blender. There's no error in these coords values.)

the angle calculated from bone.worldToLocalRotation() repeats two angles that are plus or minus by some error amount to the expected angle(in the video above, it should be between 05 degree, which is almost the average value of repeating values.

I tracked the a, b, c, d, rotation, shearX value of the bone when it starts to wiggle-wiggle.
'global rotation to apply' corresponds to the value I put as parameter of worldToLocalRotation().

frame : 16
 global rotation to apply : 59.71
 a : 0.5195181594435452
 b : -0.8210202594454984
 c : 0.7471284240399313
 d : 0.5708990572597284
 rotation : 178.35118927891028
 shearX : 0
 local rotation calculated by worldToLocalRotation() : 182.468538639338
frame : 17
 global rotation to apply : 59.7
 a : -0.13915575329380667
 b : 0.9887420882990631
 c : -0.9195301388890053
 d : -0.14962981930754288
 rotation : 182.468538639338
 shearX : 0
 local rotation calculated by worldToLocalRotation() : 342.16461794556807
frame : 18
 global rotation to apply : 59.52
 a : 0.8381920327735188
 b : -0.43323344171147493
 c : 0.40290708134133896
 d : 0.9012817456172239
 rotation : 342.16461794556807
 shearX : 0
 local rotation calculated by worldToLocalRotation() : 374.1158964445553
frame : 19
 global rotation to apply : 59.26
 a : -0.06153537736657333
 b : -0.9978085565182305
 c : 0.9279619589898894
 d : -0.06616709559142608
 rotation : 374.1158964445553
 shearX : 0
 local rotation calculated by worldToLocalRotation() : 341.4974280032252
frame : 20
 global rotation to apply : 59.08
 a : 0.8633436426801387
 b : -0.3717659613872986
 c : 0.34574232405620936
 d : 0.9283264888786582
 rotation : 341.4974280032252
 shearX : 0
 local rotation calculated by worldToLocalRotation() : 376.7702505160503
frame : 21
 global rotation to apply : 58.99
 a : -0.03848300771860522
 b : -0.9991434974966595
 c : 0.9292034535648959
 d : -0.04137960137727673
 rotation : 376.7702505160503
 shearX : 0
 local rotation calculated by worldToLocalRotation() : 345.2705974105002

And here's the inside of worldToLocalRotation() function in spine-webgl.js module.

Bone.prototype.worldToLocalRotation = function (worldRotation) {
   var sin = spine.MathUtils.sinDeg(worldRotation), cos = spine.MathUtils.cosDeg(worldRotation);
   return Math.atan2(this.a * sin - this.c * cos, this.d * cos - this.b * sin) * spine.MathUtils.radDeg + this.rotation - this.shearX;
};

The skeleton I loaded have all their IK or transform contraints disabled. I checked if the local Y scale of parent bone is repeating 1 or -1, but it's constantly 1 for whole animation. I did updateWorldTransform() everytime making changes to bone.

I suspect this is because of the calculation error in worldToLocalRotation() function.
Why is this happening? What could be reason for this, and what's the solution?

expected angle at frame 17 :

error angle at frame 17, 18 :

The numbers are pretty hard to follow. How are you posing the skeleton? Can you show the code? Are you just applying an animation? If so, does the problem occur in Skeleton Viewer?

It sound like you are manipulating bones via code? If so, can you show the code? If may be how you are calling Skeleton updateWorldTransform or Bone updateWorldTransform. If you are assembling a skeleton via code it may be that you need Skeleton updateCache.

Nate a écrit

The numbers are pretty hard to follow. How are you posing the skeleton? Can you show the code? Are you just applying an animation? If so, does the problem occur in Skeleton Viewer?

It sound like you are manipulating bones via code? If so, can you show the code? If may be how you are calling Skeleton updateWorldTransform or Bone updateWorldTransform. If you are assembling a skeleton via code it may be that you need Skeleton updateCache.

Thank you for your reply, Nate.

And Here's the code.
https://github.com/ShinhuPark/3d_to_2d_animating/blob/main/motion_capture.js

I didn't add or remove any bones, just manipulating the local scaleX, rotation, translation values of bones.

Thanks, I'll take a look.

vec.set(setupBone.worldX, setupBone.worldY);
setupBone.parent.worldToLocal(vec);

Not sure what the goal is here, but it looks like after this vec should always be the bone's local x,y. Since the skeleton has the setup pose, you don't need to store anything to get a bone's local setup position (or rotation). Use SkeletonData findBone then BoneData x, BoneData y, etc.

if(setupBone.rotation < 0){
        setupPose[spineBones[i]]["angle"] += 360;

Is this needed? Since you have an issue with rotation, code that does things with rotation is suspect.

Are you using negative scale in your animation or applied via code? One thing to note is the converting between world and local rotations may not always do what you think. Some operations are ambiguous. By this I mean there can be multiple ways to achieve a world transform. For example, -1,-1 local scale gives exactly the same world transform as 180 local rotation. Which operations were used to achieve a world transform is lost. When you convert from a world transform to a local transform, it isn't specified which you will get. For example, you could get -1,-1 local scale OR you could get 180 local rotation. Either one is correct, but it can be surprising if you don't get the same operation originally used to compute the world transform. Unfortunately there is no solution for this, it is just how the math works out.

Back to you code, it's quite a lot of code and not super easy for me to follow. I don't see anything obviously wrong, but I also don't understand what all the code wants to achieve. I suggest making a copy of the whole thing and simplifying it as much as possible while still showing the problem. Rip out literally everything single thing you can, do as little as possible, while making sure the problem still occurs after each change. Doing this will either uncover your problem, or make it easier for me to help.