Notifications
Clear all

[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.

7 Replies

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

  1. Remember to start at the root node and work outwards when setting rotations.

  2. 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