[Closed] Axis order and IK: how do I make them cooperate?
I’ve got a bit of a puzzler here. I have a function that I want to use to change the axis order of a bone, without changing the keyframe transforms of the existing animation. The functions works fine for FK bones, but as soon as you throw an IK chain into the mix all bets are off.
I should clarify that I am trying to use the function on an IK chain that has IK disabled, so I had hoped that would make it behave like an FK chain, but no dice. The same thing happens when I try to do this manually: FK bones can be aligned after their axis order has been changed using the align tool, but a disabled IK bone will not.
Is there anything I can add or change in the following script that will make it work with the lines between the /****/ markers un-commented?
----
-- FUNCTIONS TO MAKE THE OTHER STUFF WORK
----
fn getKnotPoints obj s:1 k: =(
case(classOf k)of(
array: try(for i in k collect(getKnotPoint obj s i))catch()
integer: #(getKnotPoint obj s k)
unsuppliedClass: for i=1 to(numKnots obj s)collect(getKnotPoint obj s i)
)
)
fn getAxis p1 p2 p3=(x1=normalize(p2-p1);x2=normalize(p3-p2);normalize(cross x1 x2))
fn makeTipBone b=(
local tip=boneSys.createBone b.transform.translation(b.transform.translation-6)b.dir
tip.name=b.name+"_Tip";tip.parent=b;tip.taper=100;tip.transform=b.transform;tip.wireColor=b.wireColor;tip.backFin=tip.frontFin=tip.sideFins=false;tip.height=tip.length=tip.width=aMin #(b.width,b.height)
in coordsys local move tip [b.length,0,0]
setTransformLockFlags tip #all;tip
)
fn BCFL lin=(--Bone Chain From Line
local boneChain=#(),LP=getKnotPoints lin,segCount=(numKnots lin 1)- 1
for i=1 to segCount do(
local p1=LP[i],p2=LP[i+1]
local a=(if LP.count>2 then(if i==1 then getAxis LP[3]LP[2]LP[1]else getAxis LP[i-1]LP[i]LP[i+1])else(matrixFromNormal(normalize(p1-p2)))[2])
local b=boneSys.createBone p1 p2 a;append boneChain b;if i!=1 then b.parent=boneChain[i-1]
)
for b in boneChain do(b.height=b.width=(b.length*.2);b.backFin=true;b.frontFin=b.sideFins=false;b.backFinSize=b.height/4;b.taper=50)
append boneChain(tip=makeTipBone boneChain[boneChain.count])
)
fn collectObjKeys a sel:false cf: =(--collect Object Keys
local fa=#()
fn getKeys &fa t sel:sel=(
if isController t.controller do(
if sel==true then join fa(for k in t.controller.keys where k.selected collect k.time.frame as integer)else join fa(for k in t.controller.keys collect k.time.frame as integer)
fa=sort(makeUniqueArray fa);for i=1 to t.numSubs do try(getKeys &fa t[i]sel:sel)catch()
)
)
if(not classOf a==array)and(not classOf a==objectSet)do a=#(a);
for n in a do (getKeys &fa n sel:sel;for i=1 to n.baseObject.custAttributes.count do if n.baseObject.custAttributes[i].name=="IKFK" do getKeys &fa n.baseObject.custAttributes[i][1] sel:sel)
fa
)
----
fn setAxisOrder obj ao =
(
-- Collect keys and object transforms at keys
objKeys = collectObjKeys obj
keyTMs = for k in objKeys collect at time k obj.transform
-- Switch axis order
try(obj.rotation.controller.axisOrder=ao)catch(obj[3][2][2].controller.axisorder=ao)
-- Fix rotations
for k = 1 to objKeys.count do
with animate on at time objKeys[k] obj.Transform = keyTMs[k]
)
delete objects
-- Random bone chains
----
pointArray = (for p = 1 to 6 collect random [0,0,0] [200,200,200])
lin = SplineShape pos:pointArray[1]; addNewSpline lin; for p in pointArray do addKnot lin 1 #corner #line p; updateShape lin
bc1 = BCFL lin
bc2 = BCFL lin
for bc in #(bc1, bc2) do (wc = color (random 0 255) (random 0 255) (random 0 255); for b in bc do b.wireColor = wc)
----
/*/
-- IKHI=IKSys.ikChain bc1[1]bc1[3]"IKHISolver"
-- IKHI.controller.enabled = 0
/*/
-- Random animation
----
keys = #(0) + (sort (for k = 1 to 5 collect random 0 100))
for k = 2 to 5 do with animate on at time keys[k]
(
for b = 1 to 4 do
(
rot = (eulerAngles (random -180 180) (random -180 180) (random -180 180))
rotate bc1[b] rot
rotate bc2[b] rot
)
)
----
for b = 1 to 2 do setAxisOrder bc1[b] (random 2 9)
- Bake the IK controller/solver keys on to a point.
- delete the curent IK.
- Change bone axis order.
- re-create the IK.
- bake the keys from the point to the IK control.
That is what I think I have done in the past.
I was looking at something along those lines for a possible solution. Unfortunately, when working with an IK-disabled IK chain, matching transformations directly using x.transform = y.transform, or even the “align selection” tool, simply does not work.
I have something that works for FK bones but not IK-disabled IK bones. I checked this a bunch of times to make sure I wasn’t going crazy, and finally realized that the problem only occurs with an axis order between 2 and 6. (Things work fine with XYZ, XYX, YZY, and ZXZ; the “default” and “two axis” configurations.)
To demonstrate, the bone is this scene named $2_1 will not match up with the desired transformations, and fails to even hold the correct bone length:
fn getKnotPoints obj s:1 k: =(
case(classOf k)of(
array: try(for i in k collect(getKnotPoint obj s i))catch()
integer: #(getKnotPoint obj s k)
unsuppliedClass: for i=1 to(numKnots obj s)collect(getKnotPoint obj s i)
)
)
fn getAxis p1 p2 p3=(x1=normalize(p2-p1);x2=normalize(p3-p2);normalize(cross x1 x2))
fn makeTipBone b=(
local tip=boneSys.createBone b.transform.translation(b.transform.translation-6)b.dir
tip.name=b.name+"_Tip";tip.parent=b;tip.taper=100;tip.transform=b.transform;tip.wireColor=b.wireColor;tip.backFin=tip.frontFin=tip.sideFins=false;tip.height=tip.length=tip.width=aMin #(b.width,b.height)
in coordsys local move tip [b.length,0,0]
setTransformLockFlags tip #all;tip
)
fn BCFL lin=(--Bone Chain From Line
local boneChain=#(),LP=getKnotPoints lin,segCount=(numKnots lin 1)- 1
for i=1 to segCount do(
local p1=LP[i],p2=LP[i+1]
local a=(if LP.count>2 then(if i==1 then getAxis LP[3]LP[2]LP[1]else getAxis LP[i-1]LP[i]LP[i+1])else(matrixFromNormal(normalize(p1-p2)))[2])
local b=boneSys.createBone p1 p2 a;append boneChain b;if i!=1 then b.parent=boneChain[i-1]
)
for b in boneChain do(b.height=b.width=(b.length*.2);b.backFin=true;b.frontFin=b.sideFins=false;b.backFinSize=b.height/4;b.taper=50)
append boneChain(tip=makeTipBone boneChain[boneChain.count])
)
-- Set up scene
----
sliderTime = 0
delete objects
pointArray = #(random [-200,-200,-200] [200,200,200])
for i = 1 to 2 do append pointArray (pointArray[pointArray.count] + random [-200,-200,-200] [200,200,200])
lin = SplineShape pos:pointArray[1]; addNewSpline lin; for p in pointArray do addKnot lin 1 #corner #line p; updateShape lin
bc1 = BCFL lin; bc2 = BCFL lin
bc3 = BCFL lin; move bc3[1] [300,300,300]; bc4 = BCFL lin; move bc4[1] [300,300,300]
bcArray = #(bc1, bc2, bc3, bc4)
for a = 1 to 4 do
(
bc = bcArray[a]
wc = color (random 0 255) (random 0 255) (random 0 255); for b in bc where isValidNode b do b.wireColor = wc
for b = 1 to bc.count do
(
bc[b].name = a as string + "_" + b as string
)
)
bc2_Chain = IKSys.ikChain bc2[1] bc2[3] "IKHISolver"; bc2_Chain.controller.enabled = 0
bc4_Chain = IKSys.ikChain bc4[1] bc4[3] "IKHISolver"; bc4_Chain.controller.enabled = 0
----
-- Switch axis order
bc2[1].rotation.controller.axisOrder = bc3[1].rotation.controller.axisOrder = random 2 6
-- Randomized animation
----
keys = (sort (for k = 1 to 5 collect random 0 100))
for k = 1 to 5 do with animate on at time keys[k]
(
for b = 1 to 2 do--2 do
(
rot = (eulerAngles (random -180 180) (random -180 180) (random -180 180))
rotate bc1[b] rot; rotate bc4[b] rot
bc2[b].transform = bc1[b].transform
bc3[b].transform = bc4[b].transform
)
bc2_Chain.pos = bc2[3].pos; bc4_Chain.pos = bc4[3].pos
)
----
I’m thinking it /might/ be possible to get the desired effect with IK enabled? (Through the positioning of the goal and use of a vh target.) I don’t even know if that would work, and even if it did, what happens if IK is subsequently disabled?
i don’t understand why you need to change axis order at all. i’ve never changed it… never in many years of my rigging practice.
Hi Denis,
If you look at chains 3 and 4 in my scene, you can see how much of a difference the axis order can make. A limb can be posed exactly how I want it on a series of keyframes, but on the in between frames be behaving very differently. I’ve had luck with switching to ZXZ in the past (which is fortunately not one of the setups that causes problems for this sort of thing), though that can also lead to some of its own problems. Beyond that, I guess I just like having options available?
there is no axis order that magically solves all problems. there is no really difference.
for me the changing axis order as animation option is the same as change a car from left-wheel to right on the road.
And it LOOKS like it might really just be as simple as adding this line:
for k = 1 to 5 do with animate on at time keys[k]
(
for b = 1 to 2 do
(
rot = (eulerAngles (random -180 180) (random -180 180) (random -180 180))
rotate bc1[b] rot; rotate bc4[b] rot
bc2[b].transform = bc1[b].transform
bc2[b].transform = bc2[b].objectTransform
bc3[b].transform = bc4[b].transform
)
bc2_Chain.pos = bc2[3].pos; bc4_Chain.pos = bc4[3].pos
)
…or not?
I must have tested it a couple dozen times earlier and it was working. Now it isn’t. :surprised
To clarify, it still works if the axisorder is 6, but apparently only then. I could have sworn that earlier it was working for everything, but apparently I’m on crack.
in coordSys local $2_1.rotation == (quat 0 0 0 1)
false
And that seems to be at the heart of the problem.