I have an animated character, and I want to clone and reverse the bone animations in such a way that the bones overlap the originals but are in reverse order.
So far I am working on a four bone system, where the bone chain comes down a bicep, forearm, hand, and finishes at a dummy in the hand with no animation. I then made a script which creates a similar bone chain which goes from the hand dummy, back up the arm chain in reverse order.
I then made a simple script which points the bones in the correct direction, but something is not quite right. I worked out that if a bone needs to point the other direction, then its base/pivot point needs to be the end point of the previous bone. So for instance, the pivot of newHand in relation to the oldHand is backward in such a way that the pivot is at the fingertips, rather than the wrist, with the target facing being the wrist, not the fingertips. The same was true for the entire arm structure.
In my calculations to determine which rotation I needed, I couldn't figure out how to get an exact inverse that I needed out of the rotational quat, or even the euler angle. It seems like I need 360-x 360-y 360-z and build a new quat from such a eulerangle. But that didn't work. So I used a vector system, in which I find the vector from the pivot to the target, create a matrix to give me such rotation and then apply it. That works to an extent, but the bone is often rotated 180 degrees on what looks like the z axis. I have read that using quats has this issue, but I found that using angleaxis rotation on all three parts of the eulerangle, separately, gives about the same error.
What I want is for the bones to not only point toward the correct target position, but also retain the Z-axis rotation which was present on the original bone.
I don't know if there is a technical term for what I am trying to do, so I don't even know how to research this. Inverse, reverse, and mirror used as search terms give me no results which fit my needs. It also seems like there should be a simple quat reversal function to do exactly this, giving me the perfect opposite of the angle I need in 720 space, but I have no clue what that is.
Below is the code I used so far:
fn getMostDistantVertFromOrigin obj = (
local vD = 0
local vSel = 0
local v = 0
for v = 1 to (getNumVerts obj) do (
local vert = getVert obj v
local d = distance vert obj.position
if (d>vD) then (
vD = d
vSel = v
)
)
vSel
)
fn makeChildBonesReverse obj depthToCopy = (
if (depthToCopy<=0) then return undefined
if (obj.parent!=undefined) then (
local nonDummies = 0
local child = obj.parent
if ((classOf child)!= Dummy) then (
nonDummies+=1
local newBone = copy $Bone00
newBone.position = obj.position
--get vector from position to child position
local p = child.position
local v = normalize (p - obj.position)
local r = (matrixFromNormal v) as eulerangles
--rotate the new bone to point toward the child position
rotate newBone (angleAxis r.x [1,0,0])
rotate newBone (angleAxis r.y [0,1,0])
rotate newBone (angleAxis r.z [0,0,1])
--find the vert most distant from
local vSel = getMostDistantVertFromOrigin newBone
if (vSel!=0) then (
setVert newbone vSel child.position
)
--if this child has children, work on those
local childBone = makeChildBonesReverse child (depthToCopy-1)
if (childBone!=undefined) then (
childBone.parent = newBone
)
return newBone
)
if (((classOf obj) != Dummy) and (nonDummies==0)) then (
local newBone = copy $Box00
newBone.position = obj.position
return newBone
)
) else (
--put a knob on the position to make sure we know there
--is a bone here, but that it has no children
if ((classOf obj) != Dummy) then (
local newBone = copy $Box00
newBone.position = obj.position
return newBone
)
)
)
fn getAncestor obj depth = (
if (depth<1) then return undefined
local r = obj
for n = 1 to depth do (
r = r.parent
)
return r
)
fn getFirstChild obj depth = (
if (depth<1) then return undefined
local r = obj
for n = 1 to depth do (
r = r.children[1]
)
return r
)
fn forAllKeysPointBoneAtObject obj target keySource = (
--get the keys on keysource
for key in keySource.rotation.controller.keys do (
--for t = 9 to 8000 do (
local t = key.time
--first reset the rotation on the object
--then animate it
with animate on at time t (
in coordsys world (
local origP = obj.position
obj.position = [0,0,0]
--obj.rotation = (quat 0 0 0 1)
obj.position = origP
)
in coordsys obj (
--point obj at target position
local p1 = target.position
local p2 = obj.position
local v = normalize (p1-p2)
local r = (matrixFromNormal v) as eulerangles
obj.rotation = (quat 0 0 0 1)
--rotate obj (angleAxis r.x [1,0,0])
--rotate obj (angleAxis r.y [0,1,0])
--rotate obj (angleAxis r.z [0,0,1])
rotate obj r
--local q1 = key.value -- rotation quat
--local q2 = inverse q1
--obj.rotation = q2
--local e1=r
--local e2=((e1 as quat) as eulerangles)
--local e1=q1 as eulerangles
--local e2=q2 as eulerangles
--format "%: % %" t e1 e2
)
)
)
)
fn bakeRotationIntoChain obj = (
local r = in coordsys obj obj.rotation
local child = obj.children[1]
--zero rotation here
in coordsys world (
p = obj.position
obj.position = [0,0,0]
obj.rotation = quat 0 0 0 1
obj.position = p
)
if (child!=undefined) do (
--move the rotation down the chain
in coordsys child (
child.rotation+=r
)
--repeat for children in the chain
bakeRotationIntoChain child
)
)
fn reverseBoneKinetics obj depthToCopy = (
--copy the structure backward
local newBone = makeChildBonesReverse obj depthToCopy
--parent the new structure to the selected object
newBone.parent = obj
--bake it
bakeRotationIntoChain newBone
--reverse the animation rotations
--first build a table for bone to bone cloning
local targetBones=#()
local newBones=#()
targetBones[1]=obj
for n = 1 to depthToCopy do (
targetBones[n+1]=getAncestor obj n
newbones[n]=getFirstChild obj n
--then process the bone pairs
--point the current bone at its counterpart child using the keys system from the counterpart
forAllKeysPointBoneAtObject newBones[n] targetBones[n+1] targetBones[n]
)
)
reverseBoneKinetics $rhand 3 --hand forearm bicep
Replies
You want a clone of the arm with an inverted hierarchy and with the same animation?
You could clone the arm, use constraints (position and orient) to the original arm, and then collapse the controllers with a key per frame. No script needed. (Unless you wanted to script that process).
I know the request itself is superficially un-useful, but I'm making weapons for a game where you don't have access to previous bones backward from the hand node, and so I have to rely on local copies of the bones, except in reverse order. My goal is to create a hand-based object on my character which creates a skinned fire appearance all the way up to the shoulder. To do this, it needs to somehow have access to the forearm and bicep positions, as well as their localized z-rotations.
I've tried simply copying the localized z-rotations from the "target" bone to the clone bone, but that doesn't seem to work as expected.
If my grasp of 3D coordinate systems was just a bit better, I could probably get this in 2 seconds.
I understand that the whole point-toward scripting I have above can be generally simplified into a two part process, using only x and y rotations returned from the matrixFromNormal call. One moving the facing up off the local x/y plane, and one moving the rotation around the local z-axis so that the facing vector is perfect. But the math I used sometimes gives a third number in the z-axis. Not all the time though, so I don't understand that part.
I still think it is just as simple as copying the z-rotation from bone to bone after I get the facing correct, as long as I do that in the correct coordinate system. But something in my code is wrong, or I am completely mistaken.