PDA

View Full Version : Symmetrically Copying Bone motion?



nickej
04-27-2005, 10:36 AM
I'm trying to animate a perfectly symmetrical winglike motion, using about twelve bones per wing. I've got one limb moving just about right, and I was wondering if there's a convenient way to mirror the motions of the hierarchy on one side over to the other. I'm going to be controlling the motion with Cyclist later on, if that makes any difference.
Using 7.5d on Win2K.
Thanks in advance!

faulknermano
04-27-2005, 07:07 PM
try this, it's a generic script.
select two items: first item is the "target", the second is the "source".

unfortunately, for now, i've only made it possible to do it one pair of items at a time. if you can change the code below, or if someone here can do it for you, by all means go ahead.

else, you'd have to wait until i get enough time to make changes.



@warnings


activePosRot: val
{
return(val);
}

generic
{

mirrorPos = integer(recall("MIRRORPOS",1));
mirrorx = integer(recall("MIRRORX",0));
mirrory = integer(recall("MIRRORY",0));
mirrorz = integer(recall("MIRRORZ",0));

mirrorRot = integer(recall("MIRRORROT",1));
mirrorh = integer(recall("MIRRORH",0));
mirrorp = integer(recall("MIRRORP",0));
mirrorb = integer(recall("MIRRORB",0));
s = Scene().getSelect();

if(!s)
error("<br>Select two items.");

if(s.size() < 2)
error("<br>Select two items only.");

target = s[1];
source = s[2];


reqbegin("Mirror Channel Keys");

ctlsep();
b0 = ctlcheckbox("Use Position",mirrorPos);
ctlsep();
b1 = ctlchoice("X",mirrorx,@"Mirror X","Copy X","Bypass X"@);
b2 = ctlchoice("Y",mirrory,@"Mirror Y","Copy Y","Bypass Y"@);
b3 = ctlchoice("Z",mirrorz,@"Mirror Z","Copy Z","Bypass Z"@);

ctlsep();
c0 = ctlcheckbox("Use Rotation",mirrorRot);
ctlsep();
c1 = ctlchoice("H",mirrorh,@"Mirror H","Copy H","Bypass H"@);
c2 = ctlchoice("P",mirrorp,@"Mirror P","Copy P","Bypass P"@);
c3 = ctlchoice("B",mirrorb,@"Mirror B","Copy B","Bypass B"@);

ctlsep();
ctltext("[email protected]","");

ctlactive(b0,"activePosRot",b1,b2,b3);
ctlactive(c0,"activePosRot",c1,c2,c3);

return if(!reqpost());

mirrorPos = getvalue(b0);
mirrorx = getvalue(b1);
mirrory = getvalue(b2);
mirrorz = getvalue(b3);

mirrorRot = getvalue(c0);
mirrorh = getvalue(c1);
mirrorp = getvalue(c2);
mirrorb = getvalue(c3);


store("MIRRORPOS",mirrorPos);
store("MIRRORROT",mirrorRot);
store("MIRRORX",mirrorx);
store("MIRRORY",mirrory);
store("MIRRORZ",mirrorz);

store("MIRRORH",mirrorh);
store("MIRRORP",mirrorp);
store("MIRRORB",mirrorb);


reqend();

// get source channels

tc = target.firstChannel();
while(tc)
{
if(tc.name == "Position.X")
tx = tc;
if(tc.name == "Position.Y")
ty = tc;
if(tc.name == "Position.Z")
tz = tc;

if(tc.name == "Rotation.H")
th = tc;
if(tc.name == "Rotation.P")
tp = tc;
if(tc.name == "Rotation.B")
tb = tc;

tc = target.nextChannel();
}

sc = source.firstChannel();
while(sc)
{
if(sc.name == "Position.X")
sx = sc;
if(sc.name == "Position.Y")
sy = sc;
if(sc.name == "Position.Z")
sz = sc;

if(sc.name == "Rotation.H")
sh = sc;
if(sc.name == "Rotation.P")
sp = sc;
if(sc.name == "Rotation.B")
sb = sc;

sc = source.nextChannel();
}

// get info frmo source


for(i=1;i<=6;i++) // per channel
{
if(i == 1)
{
if(!mirrorPos)
continue;
if(mirrorx == 3)
continue;
chan = sx;
tchan = tx;
if(mirrorx == 1)
multiplier = -(1.0);
else
multiplier = 1.0;
}
if(i == 2)
{
if(!mirrorPos)
continue;
if(mirrory == 3)
continue;

chan = sy;
tchan = ty;
if(mirrory == 1)
multiplier = -(1.0);
else
multiplier = 1.0;
}
if(i == 3)
{
if(!mirrorPos)
continue;
if(mirrorz == 3)
continue;

chan = sz;
tchan = tz;
if(mirrorz == 1)
multiplier = -(1.0);
else
multiplier = 1.0;
}
if(i == 4)
{
if(!mirrorRot)
continue;
if(mirrorh == 3)
continue;

chan = sh;
tchan = th;
if(mirrorh == 1)
multiplier = -(1.0);
else
multiplier = 1.0;
}
if(i == 5)
{
if(!mirrorRot)
continue;
if(mirrorp == 3)
continue;

chan = sp;
tchan = tp;
if(mirrorp == 1)
multiplier = -(1.0);
else
multiplier = 1.0;
}
if(i == 6)
{
if(!mirrorRot)
continue;
if(mirrorb == 3)
continue;

chan = sb;
tchan = tb;
if(mirrorb == 1)
multiplier = -(1.0);
else
multiplier = 1.0;
}

kcount = chan.keyCount;
for(j=1;j<=kcount;j++) // per key
{
kf = chan.keys[j];
kfTime = chan.getKeyTime(kf);
kfValue = chan.getKeyValue(kf) * multiplier;
newKey = tchan.createKey(kfTime,kfValue);
if(string(chan.getKeyCurve(kf)) != "1" && string(chan.getKeyCurve(kf)) != "2" && string(chan.getKeyCurve(kf)) != "4" && string(chan.getKeyCurve(kf)) != "5" && string(chan.getKeyCurve(kf)) != "6")
{
kfCurve = nil;
}
if(string(chan.getKeyCurve(kf)) == "0")
{
kfCurve = nil;
}
else
kfCurve = chan.getKeyCurve(kf);

if(kfCurve == CHAN_TCB)
{
t = chan.getKeyTension(kf);
c = chan.getKeyContinuity(kf);
b = chan.getKeyBias(kf);
tchan.setKeyCurve(newKey,CHAN_TCB);
tchan.setKeyTension(newKey,t);
tchan.setKeyContinuity(newKey,c);
tchan.setKeyBias(newKey,b);
continue;
}
if(kfCurve == CHAN_HERMITE)
{
(h1,h2,h3,h4) = chan.getKeyHermite(kf);
tchan.setKeyCurve(newKey,CHAN_HERMITE);
tchan.setKeyHermite(newKey,h1*multiplier,h2*multip lier,h3*multiplier,h4*multiplier);
continue;
}
if(kfCurve == CHAN_LINEAR)
{
tchan.setKeyCurve(newKey,CHAN_LINEAR);
continue;
}

if(kfCurve == CHAN_STEPPED)
{
tchan.setKeyCurve(newKey,CHAN_STEPPED);
continue;
}
if(kfCurve == nil)
{
tchan.setKeyCurve(newKey,CHAN_BEZIER);
(h1,h2,h3,h4) = chan.getKeyHermite(kf);
tchan.setKeyHermite(newKey,h1,h2*multiplier,h3,h4* multiplier);
}

}
}
}

nickej
04-27-2005, 08:40 PM
Oh. My. God.

*Ahem*Thanks! That's a pretty amazing script-thingie you got there. It's very pretty and all those indentations and parentheses are very nicely balanced-looking. Not too dark or too pale or anything. Unfortunately, I come to 3D from painting. As in oil. Smeared around with pig-fur on a stick. While I'm acquainted with Java and ActionScript (and even a little MEL), I realized a long time ago that while I might master the syntax of a particular script-style, I would never, ever, ever understand logic or mathematics to the extent that I could make them do what I want, rather than the reverse. So....all I can say is thank you from the bottom of my heart for the effort, but it would actually be faster and easier for me to manually adjust the bones than it would be to deploy a script.

faulknermano
04-27-2005, 08:50 PM
but it would actually be faster and easier for me to manually adjust the bones than it would be to deploy a script.

well, it's a generic script, so it's pretty much two clicks to select your objects, and one-click to run the script. of course, you'll have to specify your options. thats where it gets hairy :)

however, use whatever works for your requirements. :)

all the best...

MonroePoteet
04-28-2005, 10:02 AM
I've done symmetric movement by copying the motion from the keyframed elements to their symmetric elements, and then scaling the X channel by -1. You can copy the motion either using the Graph Editor, the Scene Editor or using the Save/Load Motion under the File menu. The Scene Editor allows multiple object manipulation, and would avoid a bone-by-bone copy/paste/scale. Scaling is done in the Graph Editor using the "H" shortcut and entering -1 for the Value Scale Factor.

BTW, if you're using rotations, you'd need to scale the Heading by -1 as well.

mTp

jhopfenblatt
04-28-2005, 10:40 AM
else, you'd have to wait until i get enough time to make changes.

Been programming much lately? Heheheh. I dunno. I found the phrasing in that sentence strangely amusing. :D