An animator at my studio asked me to put together a quick tool to copy and paste Tran,Rot,Sca data in worldspace selectively from one key to another. Scripting it was pretty easy. It just grabs and banks the data based upon user input via checkboxes. I ended up making it do local as well since that wasn’t much more work. The tool seemed to work fine because of the way I was working, but when I went to show the animator, it was rather embarrassing.
The basis of the script is copy executes a “bankpos=$.position” and paste executes “$.position=bankpos”. Rather simple. Here’s where it seems to be getting weird. As an example, I click on a controller and go frame 5 and copy the position. I then slide the time to frame 10 and hit paste. Sometimes the controller’s new position will be really close to correct value, and other times I’ve had it fly across the screen. Now if I deselect the controller and reselect it before hitting “Paste” it executes correctly. If copy and paste the data without deselecting the controller in between, it seems to get it wrong. I’ve tried scripting a deselect and reselect both after the copy execution and before the paste, but it doesn’t fix the problem. It only works if I deselect the object in the viewport and reselect it manually.
I can throw the code up if it’ll help, but it isn’t anything special. I was just wondering if anyone has encountered similar issues and, if so, what you did to get around it. It appears to be some sort of bug.
edit: Just found out that If I hit “Paste” twice, the second time the controller will pop into place correctly.
sometimes when you change controller values
Max screws up where the previous animation data should have put things
When the pasting onto controllers goes to wildly wrong places
does scrubbing the timeline to a previous key frame and back snap them into the right place?
If you’re dealing only with world space and objects, and not pasting values into the tracks directly, it would be better to get/set the $.transform instead of $.position, as the later sometimes isn’t the accurate world space position, while .transform is always the object’s world transform.
In setting .transform you would first get .transform matrix from your source object, then adjust it based on your script’s options, then set target object’s .transform to the adjusted matrix.
It working on multiple clicks sounds like it may be a hierarchy vs selection order problem, i.e.: if you set the child’s transform before setting the parent’s, the child’s transform will be changed once you set its parent’s transform.
I managed to get it working by having it assign rotation before transform, so it looks like it was the rotation that was causing the issue. I like the idea of using the full matrix and may rewrite it later to use that. Altering the position is easy enough because its just row4 in the matrix, and I know I can extract the quat out of the matrix, but I haven’t found any good tutorials on how to rebuild the matrix afterwords, depending on whether I want to alter scale or rotation data. I saw a couple of good link in older posts here, but unfortunately they were dead.
Would a potential workaround be to bank the target object’s scale info, paste the scale and rotation data combined, then paste the original scale data bank on?
Oh, transform matrices are actually much easier than messing with quats: .row1, .row2, and .row3 are the vectors of the X, Y and Z axis of the object respectively, the length of each vector is the scale along that axis.
See this:
fn matchTransform tm tmTarget position:true rotation:true scale:true =
(
if position then tm.row4 = tmTarget.row4
if rotation AND scale then
(
tm.row1 = tmTarget.row1
tm.row2 = tmTarget.row2
tm.row3 = tmTarget.row3
)
else if rotation AND NOT scale then
(
tm.row1 = (length tm.row1) * normalize tmTarget.row1
tm.row2 = (length tm.row2) * normalize tmTarget.row2
tm.row3 = (length tm.row3) * normalize tmTarget.row3
)
else if scale AND NOT rotation then
(
tm.row1 = (normalize tm.row1) * length tmTarget.row1
tm.row2 = (normalize tm.row2) * length tmTarget.row2
tm.row3 = (normalize tm.row3) * length tmTarget.row3
)
RETURN tm
)
$[1].transform = matchTransform $[1].transform $[2].transform position:true rotation:true scale:false
If you wanted to match the rotation of only specific axis of the object it would be a little more complicated, because you’d have to orthogonalize the rows using cross products. But that’s likely a different topic.
For a tutorial: if you can obtain it somehow, I’d definitely recommend Bobo’s “The Matrix: Explained” DVD. I’m sure you would find it very very useful, as did I.
Max script has a $.transfrom.position property you can use to get the world pos of any object
Matching the whole transform is easy
tm= objA.transform
objB.transform= tm
Matching a subset of the transform (eg just pos and rot) is harder
because Transform.position or rotation etc is read only.
So you have to create a new transform
With the values you want and then apply it
Like Zhalkis’ code demonstrates
+1 to Bobo’s Matrix Explained DVD.
CG Academy appears defunct (and has bad reputation towards its contributors)
You can find them on you Tubenow days.
unscrupulous folks might try bit torrent, I hear.