Sorry for the delay, I meant to look into this deeper quite a while ago.
IanM đã viếtSpine isn't actually altering the local transform, is it? That's all staying stable behind the scenes and the constraints are just applying a temporary shift that's "non canonical" as it were, just written into the outputted, visible skeleton but not stored in any way on the local transform of the real skeleton. Then, when something higher in the hierarchy is updated in the constraint, it's all wiped as the updater walks down the graph and recomputes from the base, unaltered local transforms.
You wouldn't want applying constraints to mutate the local transforms. The local transforms make up the skeleton's pose, so for example, if your skeleton was just standing there, each frame you apply constraints and the pose would change. To apply constraints more than once you'd have to change the local transforms back to how they were originally.
To solve that, bones have a second local transform called the "applied transform". A constraint either changes the applied transform or it changes the world transform and marks the applied transform as invalid. Before the applied transform can be used, for example by a constraint, first Bone updateAppliedTransform
needs to be called if the applied transform is invalid. This work is avoided if a constraint changes the world transform and nothing else needs the applied transform values.
Note this is low level stuff. It is rare for most users to need to worry about the applied transform.
I reviewed your project again and created a simpler version:
http://n4te.com/x/1823-simpler.spine
I agree it seems odd the changes made by the transform constraint are lost, even when the IK constraint mix is 0. In that case the IK constraint code is bypassed, so it isn't changing any bones. The problem is with the skeleton "update cache", which is the order bones and constraints are updated. The update cache is sorted so parent bones are updated before children, constraint target and constrained bones are updated before constraints are applied, and the children of constrained bones are updated after a constraint is applied.
Here is the update cache for the skeleton in the project I linked:
root-bone
ik-parent-bone
ik-child-bone
transform-target-bone
transform-constrained-bone
transform-constraint
ik-target-bone
ik-constraint
transform-target-bone
transform-constrained-bone
First the root bone is updated, then the IK parent and child bones. After that is the transform constraint target and constrained bones, then the transform constraint is applied. Good so far. Next we have the IK target bone and then the IK constraint is applied. This still makes sense.
Lastly the transform constraint target and constrained bones are updated again. They are updated because their parent bone, ik-child-bone
may have been modified by the IK constraint (the update cache doesn't know what the mix is, so they are updated even if the mix is zero). Bones in the update cache are updated by calling Bone updateWorldTransform
with no arguments, which uses its local transform, not the applied transform. That is where the changes made by the transform constraint are lost.
We could improve this by first setting the applied transforms for all bones, then bones in the update cache would always use their applied transform, not their original local transform.
One problem with this is the way we mark the applied transform as invalid to defer recomputing it until just before it is needed. In the update cache above, the applied transform for transform-constrained-bone
is invalidated when transform-constraint
is applied and then recomputed the second time transform-constrained-bone
is updated. At that point its parent bone has already been moved and/or rotated by the IK constraint. The recomputed applied transform will use the parent bone's current world transform, which results in the transform-constrained-bone
not moving with its parent! To fix that the applied transform needs to be recomputed before the parent bone's world transform changes. For example, instead of deferral, we could just recompute the applied transform. This is a little unfortunate if it is not needed by anything else (the cost is a few trig calls more than updating a bone). I'm not sure there is a better way though.
TLDR; we could fix the issue by setting the applied transforms first, then updating bones. Also, constraints that modify the world transform must recompute the applied transform to keep it in sync, which requires slightly more effort than we currently spend.
To follow up, we've decided it is best to implement the changes as discussed above. The applied transform is now always kept in sync with changes made to the world transform. This improves the behavior of constraints and should fixed the problem you initially reported.
The commit is in the 4.0-beta branch, here:
https://github.com/EsotericSoftware/spine-runtimes/commit/4f73fbbb396f406954646af76f167d8238802171