macroScript eulerFix category:"Comet Cartoons" toolTip:"eulerFix: Fixes euler rotation interpolation so character animation doesn't interpolate oddly." Icon:#("CometCartoons",5) ( -- -- CometCartoons-EulerFix.mcr - Michael B. Comet - comet@comet-cartoons.com -- http://www.comet-cartoons.com/ -- Copyright ©2002 Michael B. Comet All Rights Reserved. -- -- -- How to work it: -- 1. Assign this script to a hotkey, or shelf button. -- 2. Select the objects that have an Euler_XYZ animated rotation -- controller -- -- 3. Run it. -- -- What it does: -- This does something similar to Maya's "Euler Filter" routine that -- will correct extra or bad interpolation of Euler Rotation keys. -- -- What this does is ADJUST KEYFRAME VALUES such that the end result -- POSES "look" the same, but such that the difference from the previous -- frame is less. This means values will be adjusted for your keys. -- -- If you find a problem area where you wanted it as it was before -- the fix ran...undo the eulerFix routine, and ADD an Extra Key -- frame inbetween the area that you want to keep. i.e.: Key an -- "inbetween" pose. Adding an inbetween (or if needed more than one) -- will cause eulerFix to not adjust that area and to leave it alone. -- -- Note you must have the X, Y and Z rotation keys ALL AT THE -- SAME FRAME for this to catch and adjust those keys. -- -- -------------------------------------------------------------------------- -- -------------------------------------------------------------------------- -- U.I. STUFF -- -------------------------------------------------------------------------- -- -------------------------------------------------------------------------- global eF_keyStatus; global oc, ot; rollout eF_keyStatus "Status" ( label lbl_status1 "Status: Ready." align:#left; label lbl_prog "Progress:" align:#left across:2 progressBar progBar value:0 width:200 offset:[-70,0]; ) -- -------------------------------------------------------------------------- -- FUNCTIONS -- -------------------------------------------------------------------------- -- -------------------------------------------------------------------------- /* * eF_progress() - Update progress % bar */ fn eF_progress val = ( eF_keyStatus.progBar.value = val; ival = val as integer; eF_keyStatus.lbl_prog.text = (""+(ival as string)+"%: "); ) -- -------------------------------------------------------------------------- /* * eF_feedback() - Nice output function */ fn eF_feedback str = ( format "-- eulerFix: %\n" str; eF_keyStatus.lbl_status1.text = ("Status: "+str+" "); ) -- -------------------------------------------------------------------------- /* * additiveKeys() - This will shift the value of all keys in a * key array from FROM index thru to the end by VALUE. */ fn additiveKeys keyArray indexStart value = ( if (keyArray == undefined) then return(); for i in indexStart to keyArray.count do keyArray[i].value += value; ) -- -------------------------------------------------------------------------- /* * multiplyKeys() - This will Multiply the value of all keys in a * key array from FROM index thru to the end by VALUE. */ fn multiplyKeys keyArray indexStart value = ( if (keyArray == undefined) then return(); for i in indexStart to keyArray.count do keyArray[i].value *= value; ) -- -------------------------------------------------------------------------- /* * fixEulerKeys() - Given 3 controllers that are bezier_float this will * go thru and do euler fixing on them. */ fn fixEulerKeys contX contY contZ = ( if (contX == undefined or contY == undefined or contZ == undefined) then return; sortKeys contX; sortKeys contY; sortKeys contZ; keysX = contX.keys; keysY = contY.keys; keysZ = contZ.keys; -- Since we only care about X and Z for checking to start...we'll -- just use Z keys... Hopefully user has all 3 keys set all the same -- time. cnt = keysZ.count; opct = (100.0*(oc - 1)/ot); -- what was prev obj % if (opct < 0) then opct = 0; npct = (100.0*(oc - 0)/ot); -- what will we be when done? -- Do this twice...for some reason it works right when we do it twice, vs. once. -- Also odd is once run if we were to do it only once, it would swap between two -- different solutions....heh. Weird but hey it works. for rep in 1 to 2 do ( -- Do main loop. Start with 2 so we can use prev frame for reference. -- for iz in 2 to cnt do ( -- how far are we for this object, count double rep too. -- this will be 0-1. if (rep == 2) then izc = cnt+iz; else izc = iz; subpct = (izc)/(cnt*2.0); newpct = opct + ((npct-opct)*subpct); eF_progress newpct; keyZ = keysZ[iz]; iy = getKeyIndex contY keyZ.time; ix = getKeyIndex contX keyZ.time; if (iy != 0 and ix != 0) then ( keyX = keysX[ix]; keyY = keysY[iy]; pkeyX = keysX[ix-1]; pkeyY = keysY[iy-1]; pkeyZ = keysZ[iz-1]; -- format "-- DEBUG % % % = %x %y %z\n" keyX.time keyY.time keyZ.time keyX.value keyY.value keyZ.value; -- At this point we have keys all at the same time...so we can do something now. -- And we have previous keys for reference as well -- Get differences from before. diffX = keyX.value - pkeyX.value; diffY = keyY.value - pkeyY.value; diffZ = keyZ.value - pkeyZ.value; -- format "DEBUG % % %\n" diffx diffy diffz; -- Now see if total change in X and Z added is > 90 -- If so...take everything + or - by 180. And then also -- multiply Y by -1. -- if ( (abs diffX)+(abs diffZ) > 90) then ( if (diffX > 0) then additiveKeys keysX ix -180; else additiveKeys keysX ix 180; if (diffY > 0) then additiveKeys keysY iy -180; else additiveKeys keysY iy 180; if (diffZ > 0) then additiveKeys keysZ iz -180; else additiveKeys keysZ iz 180; -- and now do mult by -1 oldDiffY = diffY; multiplyKeys keysY iy -1; /* diffY = keyY.value - pkeyY.value; if (abs diffY > abs oldDiffY) then multiplyKeys keysY iy -1; */ ) -- Finally now check for anything now over 360 and tweak those too. -- -- Get NEW differences now. diffX = keyX.value - pkeyX.value; diffY = keyY.value - pkeyY.value; diffZ = keyZ.value - pkeyZ.value; -- format "DEBUG % % %\n" diffx diffy diffz; if (diffX > 360 or diffX < -360) then ( amt = (integer)(diffX / 360.0); additiveKeys keysX ix (-360.0*amt); ) if (diffY > 360 or diffY < -360) then ( amt = (integer)(diffY / 360.0); additiveKeys keysY iy (-360.0*amt); ) if (diffZ > 360 or diffZ < -360) then ( amt = (integer)(diffZ / 360.0); additiveKeys keysZ iz (-360.0*amt); ) diffX = keyX.value - pkeyX.value; diffY = keyY.value - pkeyY.value; diffZ = keyZ.value - pkeyZ.value; -- format "DEBUG % % %\n\n" diffx diffy diffz; ) -- inner if we have all 3 keys, do it! ) -- main loop ) -- double repeat loop for some reason this makes it work better... ) -- -------------------------------------------------------------------------- /* * eulerFix() - Main Entry */ fn eulerFix = ( disableSceneRedraw() eF_floater = (newRolloutFloater "CometCartoons eulerFix: 1.00" 300 110) addRollout eF_keyStatus eF_floater eF_feedback("CometCartoons eulerFix started."); setWaitCursor(); objs = getCurrentSelection(); ot = objs.count; oc = 1; for o in objs do ( -- Only do objects that have Euler_XYZ controllers -- on them! if ((classOf o.rotation.controller) == Euler_XYZ) then ( contX = o.rotation.x_rotation.controller; contY = o.rotation.y_rotation.controller; contZ = o.rotation.z_rotation.controller; if ((classOf contX) == Bezier_Float and (classOf contY) == Bezier_Float and (classOf contZ) == Bezier_Float ) then ( eF_feedback(" >> "+(o.name)+" <<"); fixEulerKeys contX contY contZ; -- now call real worker proc. ) -- if it is all bez floats under it ) -- if it is euler xyz oc = oc + 1; ) eF_progress(100); setArrowCursor(); eF_feedback("CometCartoons eulerFix completed."); closeRolloutFloater eF_floater; enableSceneRedraw(); completeRedraw(); ) -- -------------------------------------------------------------------------- -- Now call it on startup! eulerFix(); ) -- end of eulerFix MACROScript