[Closed] align biped limb to object
Trying to align biped limbs to objects in scene and can’t seem to work. Here’s what I’ve tried.
EA = eulerAngles (coordsys world $Cylinder01.rotation.x_rotation) (coordsys world $Cylinder01.rotation.y_rotation) (coordsys world $Cylinder01.rotation.z_rotation)
tempQuatRot = eulerToQuat EA
biped.setTransform $'Bip01 L Thigh' #rotation (quat 0 0 0 1) True
biped.setTransform $'Bip01 L Thigh' #rotation (tempQuatRot) True
Added “quat 0 0 0 1” to reset quat value since sometimes would get odd rotations not corresponding with objects.
I use this method in my scripts.
in coordsys local ( rotate BipedObject ( BipedObject.transform.rotation * inverse TargetObject.transform.rotation ) )
Things that caught me out the first time I play with biped using MAXscript
-
Remember to start at the root node and work outwards when setting rotations.
-
Just setting positions for hands or feet doesn’t work very well. I the end I wrote my own limb solver to set rotations for the thigh and calf in order to put the foot where i wanted it.
Getting mixed results. What I have for scene is Spheres setup for joints and cylinders setup for limbs with ‘look at’ constraint pointed to joints. Am testing with upper legs first and your code works except have to offset y axis by -90 degrees. Can’t get my code to work with changing x,y,z degrees though. Have tried with changing local to world and that didn’t work either.
EA = eulerAngles (in coordsys local $RR_LeftUpLeg_Limb.rotation.x_rotation) ((in coordsys local $RR_LeftUpLeg_Limb.rotation.y_rotation) + 90) (in coordsys local $RR_LeftUpLeg_Limb.rotation.z_rotation)
in coordsys local ( rotate $'Bip01 L Thigh'( $'Bip01 L Thigh'.transform.rotation * inverse(eulerToQuat EA) ) )
How do you go about setting rotations for lower leg without throwing entire leg in a different direction?
Hi
You could try instead of using the biped.setTransform function, to directly apply the transformation matrix:
tempTM = BipedObject.transform
tempTM.rotation = newRotation
BipedObject.transform = tempTM
For this to work though, you need to have the Auto Key on.
Thanks that bit of advice helped simplify code. Found out that 90 degree y offfset is part of parent offset.
--resests values to 0
biped.setTransform $'Bip01 L Thigh' #rotation (quat 0 0 0 1) True
biped.setTransform $'Bip01 L Calf' #rotation (quat 0 0 0 1) True
biped.setTransform $'Bip01 L Foot' #rotation (quat 0 0 0 1) True
select (biped.getNode objBiped #Lleg link:1) --L Thigh
tempTM = $.transform
tempTM.rotation = $RR_LeftUpLeg_Limb.transform.rotation
$.transform = tempTM
in coordsys local ( rotate $ ( $.parent.transform.rotation ) )
Still having problems with getting lower leg (calf) to work correctly since it moves entire leg in order to conform to its rotations. Have even tried putting 0 in for xyz values and still moves entire leg.
You might want to calculate the transformation matrix before instead of moving the whole object.
Basically to get local/parent rotation/translation, you can easily change the Matrix to achieve this:
localTM = BipedObject.transform * inverse BipedObject.parent.transform
This way you get the local transformation of the BipedObject (works for any node…)
What you also might want to do is saving the position of the node if you just want to apply rotations.
Sometimes it happens, that the TM when applying rotations, changes the position too.
To prevent this, set the position afterwards back on:
So in your case it would be something like:
tempTM = $.transform
tempTM.rotation = $RR_LeftUpLeg_Limb.transform.rotation
tempTM.pos = $.transform.pos
$.transform = tempTM
What do you want to do with this?:
in coordsys local (rotate $ ( $.parent.transform.rotation ) )
Thanks. Code seems to working as next step but still trying to figure this one out. Parent code was working separate from temp rotation for some reason.
Another problem came up however and am trying to set length of each biped limb to cylinder length. Not sure if simply scaling it would result in accurate result though. Would scaling based on biped total height work? (ex. (thigh length) / (biped height) ). Have found a script online which will help with setting a box or object to biped limb sizes but not other way around.
I had the same problem before, where I wanted to align a finger node to a specific spot and I had a function to calculate the direction and scale length of the biped node for this.
function alignNode obj alignTM useScale:false =
(
if obj != undefined and alignTM != undefined then
(
case classOf alignTM of
(
Point3: alignTM = transMatrix alignTM
Matrix3: alignTM = alignTM
default:
(
case superClassOf alignTM of
(
GeometryClass: alignTM = alignTM.transform
helper: alignTM = alignTM.transform
default: return false
)
)
)
local newScale = [1,1,1]
if obj.parent != undefined then
(
if useScale == true then
(
local newParentTM = obj.parent.transform
local curParentTM = obj.parent.transform
local endPos = obj.transform.pos
local posOffset = alignTM.pos - curParentTM.pos
local scaleMul = (((distance curParentTM.pos alignTM.pos) * 100.0) / (distance curParentTM.pos endPos)) / 100.0
local scaleAxis = endPos * inverse curParentTM
local maxDist = 0.0
local scaleAxisID = 0
for i = 1 to 3 do
(
if scaleAxis[i] > maxDist then
(
maxDist = scaleAxis[i]
scaleAxisID = i
)
)
if scaleAxisID > 0 then
newScale[scaleAxisID] = scaleMul
--// generate new Matrix3 with the position at the offset and afterwards rotated to the origin to get the origin vector
newParentTM.pos = posOffset
newParentTM = rotate newParentTM (inverse newParentTM.rotation)
local upVec = [0,0,1] --// up vector = z
local dirVec = normalize newParentTM.pos --// direction vector
local xVec = dirVec --// x = direction vector
local yVec = normalize (cross upVec xVec) --// y = cross product of upVec and x
local zVec = (cross xVec yVec) --// z = cross product of x and y
--// apply values to identity Matrix
local rotateTM = matrix3 1
rotateTM.row1 = xVec
rotateTM.row2 = yVec
rotateTM.row3 = zVec
--// apply scale, rotation and position back on
newParentTM.scale = curParentTM.scale * newScale
newParentTM.rotation = rotateTM.rotation * curParentTM.rotation
newParentTM.pos = curParentTM.pos
)
)
local childArray = #()
undo "Align Node" on
(
if obj.classID[1] != 37157 then
(
for i = 1 to obj.children.count do
append childArray obj.children[i]
for i = 1 to childArray.count do
childArray[i].parent = undefined
)
if useScale == true then
(
--// biped of obj' parent
if obj.parent != undefined then
(
if obj.parent.classID[1] == 37157 then
(
--// get local scale
biped.setTransform obj.parent #scale ((biped.getTransform obj.parent #scale) * newScale) false
biped.setTransform obj.parent #rotation newParentTM.rotation false
biped.setTransform obj.parent #pos newParentTM.pos false
)
else
(
local tempNode = obj.parent
obj.parent = undefined
tempNode.transform = newParentTM
obj.parent = tempNode
)
)
)
--// biped of obj
if obj.classID[1] == 37157 then
(
biped.setTransform obj #rotation alignTM.rotation false
biped.setTransform obj #pos alignTM.pos false
)
else
(
obj.transform = alignTM
for i = 1 to childArray.count do
childArray[i].parent = obj
)
)
return true
)
return false
)
Example: alignNode $‘Bip01 R Finger01’ $helper1.transform useScale:true
I’m not sure if the thing is optimized at all, but it works for me.
Hope this helps a bit.
You could try defining the alignTM by using the end of the cylinder or projecting the height to the pivot.
For the width of the node, you basically do the same thing I did with the scaling with the BoundingBox (BBox) I would say.
Cheers