Notifications
Clear all

[Closed] Align biped to max bone rig?

Does anyone know of any scripts that will align a character studio biped to a rig made with Max bones?

11 Replies
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

what the hell is that? usually people align a custom bones to the biped.
is it anything about FBX to BIPED?
do you need to align THE config only OR whole animation? do you have any designated naming convention?

I鈥檝 made a script like you want for my company,
but it鈥檚 highly customize, the bone rig have to satisfy many rules for the script work.
I don鈥檛 know the goal your want to reach behind this. but if you rigging your bone freely without some limit like biped, it is useless to align biped to bone rig.

It鈥檚 mostly that I find fitting max bones to a character mesh much easier than doing the same with biped. So I want to set up a rig that fits inside my character properly, and then convert it over to a biped to load motion capture data onto.

So it鈥檚 only the configuration, not animation. And since I鈥檒l be making the rig specifically for the purpose of loading a biped onto, I figure it shouldn鈥檛 be a problem to set it up for a 1 to 1 conversion.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

interesting鈥?i do exactly opposite. i give to my artists the skeleton creation in the Biped and convert it to my custom rig after.

If anyone is interested, I鈥檝e come up with something that is even better (at least for me, since I usually create my rigs off of editable splines.)

Simply set up a series of lines describing the spine, head/neck, and left/right arms/legs, each with the appropriate number of vertices, and name them accordingly (note: fingers and toes are not currently supported).

Once that basic stick figure has been set up, create a biped in the scene.

At this point, simply run the script (if your biped is named something other than Bip01, change the baseName variable in the script first). You鈥檙e done!

I have not done any significant debugging on this yet, and am by no means a Maxscript master. If anyone finds any problems, or just sees away improve my code, please let me know!

-- Definitions
 ------------------------------------------------------------------------------
 leftLeg					= $leftLeg
 rightLeg				= $rightLeg
 spine						= $spine
 head						= $head
 leftArm					= $leftArm
 rightArm				= $rightArm
 
 pointLT					= getKnotPoint leftLeg 1 1
 pointRT					= getKnotPoint rightLeg 1 1
 
 targetPointLT		= getKnotPoint leftLeg 1 2
 targetPointLC 	= getKnotPoint leftLeg 1 3
 targetPointLF 	= getKnotPoint leftLeg 1 4
 
 targetPointRT		= getKnotPoint rightLeg 1 2
 targetPointRC	 = getKnotPoint rightLeg 1 3
 targetPointRF	 = getKnotPoint rightLeg 1 4
 
 spineArray			= for i = 1 to numKnots spine collect (getKnotPoint spine 1 i)
 headArray				= for i = 1 to numKnots head collect (getKnotPoint head 1 i)
 
 pointLS					= getKnotPoint leftArm 1 1
 targetPointLS		= getKnotPoint leftArm 1 2
 targetPointLUa	= getKnotPoint leftArm 1 3
 targetPointLFa	= getKnotPoint leftArm 1 4
 targetPointLH		= getKnotPoint leftArm 1 5
 
 pointRS					= getKnotPoint rightArm 1 1
 targetPointRS		= getKnotPoint rightArm 1 2
 targetPointRUa	= getKnotPoint rightArm 1 3
 targetPointRFa	= getKnotPoint rightArm 1 4
 targetPointRH		= getKnotPoint rightArm 1 5
 
 baseName				= "Bip01"
 
 bip							= execute ("$'" + basename)
 pelvis					= execute ("$'" + basename + " Pelvis'")
 leftThigh				= execute ("$'" + basename + " L Thigh'")
 leftCalf				= execute ("$'" + basename + " L Calf'")
 leftFoot				= execute ("$'" + basename + " L Foot'")
 rightThigh			= execute ("$'" + basename + " R Thigh'")
 rightCalf				= execute ("$'" + basename + " R Calf'")
 rightFoot				= execute ("$'" + basename + " R Foot'")
 
 spinePartArray	= #()
 
 for i = 1 to (numKnots spine - 1) do
 (
 	id = ""
 	
 	if i > 1 do id = (i - 1) as string
 	spinePart = execute ("$'" + basename + " Spine" + id + "'")
 	append spinePartArray spinePart
 )
 
 headPartArray		= #()
 
 for i = 1 to (numKnots head - 1) do
 (
 	id = ""
 	if i < (numKnots head - 1) then partName = " Neck" else partName = " Head"
 	
 	if i > 1 and i < (numKnots head - 1) do id = (i - 1) as string
 	headPart = execute ("$'" + basename + partName + id + "'")
 	append headPartArray headPart
 )
 
 leftShoulder		= execute ("$'" + basename + " L Clavicle'")
 leftUpperarm		= execute ("$'" + basename + " L Upperarm'")
 leftForearm			= execute ("$'" + basename + " L Forearm'")
 leftHand				= execute ("$'" + basename + " L Hand'")
 
 rightShoulder		= execute ("$'" + basename + " R Clavicle'") 
 rightUpperarm		= execute ("$'" + basename + " R Upperarm'")
 rightForearm		= execute ("$'" + basename + " R Forearm'")
 rightHand				= execute ("$'" + basename + " R Hand'")
 ------------------------------------------------------------------------------
 
 
 
 ----------------------------------------------------------
 -- Function to find the angle between two lines
 --
 -- Special thanks to prettyPixel
 ----------------------------------------------------------
 fn fourPointAngle pA pB pC pD =
 (
 	local vAB = pB - pA
 	local vCD = pD - pC
 	local angle = acos (dot (normalize vAB) (normalize vCD))
 	
 	if angle < 90.0 then angle else (180.0 - angle)
 )
 ----------------------------------------------------------
 
 
 
 -------------------------------------------------------------------------------------------------
 -- Functions to control transformations of biped parts
 -------------------------------------------------------------------------------------------------
 fn scaleToLength part p1 p2 =
 (
 	scalePart		= biped.getTransform part #scale
 	lengthPart	= distance p1 p2
 	biped.setTransform part #scale [lengthPart, scalePart.y, scalePart.z] false
 )
 
 fn rotateFromPoints part p1 p2 offset =
 (
 	theVector	= normalize (p1 - p2)
 	theMatrix	= matrixFromNormal theVector
 	preRotate theMatrix offset
 	biped.setTransform part #rotation theMatrix false
 )
 
 fn hipsAlign =
 (
 	hipsPos			= (pointLT + pointRT) / 2
 	biped.setTransform bip #pos hipsPos false
 	
 	scaleP			= biped.getTransform pelvis #scale
 	hipsWidth		= distance pointLT pointRT
 	biped.setTransform pelvis #scale [scaleP.x, scaleP.y, hipsWidth] false
 	
 	rotateFromPoints bip pointLT pointRT (eulerAngles -90 180 0)
 )
 
 fn proximalAlign part p2 p3 =
 (
 	p1 = biped.getTransform part #pos
 	scaleToLength part p1 p2
 
 	outVector		= normalize (p3 - p1)
 	partVector	= normalize (p2 - p1)
 	rightVector	= normalize (cross outVector partVector)
 	upVector		= normalize (cross rightVector partVector)
 	theMatrix		= matrix3 partVector upVector rightVector p1
 	biped.setTransform part #rotation theMatrix false
 )
 
 fn distalAlign part p1 p2 =
 (
 	scaleToLength part p1 p2
 
 	p3					= biped.getTransform part.children[1] #pos
 	partAngle		= fourPointAngle p1 p3 p1 p2
 	rotate part (angleAxis -partAngle [0,0,1])
 )
 
 fn footAlign foot p1 p2 =
 (
 	scaleFoot = biped.getTransform foot #scale
 	lengthX = p1.z - p2.z
 	lengthY = p1.y - p2.y
 	biped.setTransform foot #scale [lengthX, lengthY, scaleFoot.Z] false
 	
 	biped.setTransform foot #rotation (eulerAngles 180 90 0) false
 	heel			= [p1.x, p1.y, p2.z]
 	ball			= [p1.x, p2.y, p2.z]
 	footAngle	= fourPointAngle heel ball heel p2
 	if foot == rightFoot do footAngle = (-footAngle)
 	rotate foot (angleAxis footAngle [0,0,1])	
 )
 
 fn spineAlign =
 (
 	biped.setTransform spinePartArray[1] #pos spineArray[1] false
 	
 	for i = 1 to (numKnots spine - 1) do
 	(
 		scaleToLength spinePartArray[i] spineArray[i] spineArray[i + 1]
 		rotateFromPoints spinePartArray[i] spineArray[i] spineArray[i + 1] (eulerAngles 180 90 0)
 	)
 )
 
 fn headAlign =
 (
 	biped.setTransform headPartArray[1] #pos headArray[1] false
 
 	for i = 1 to (numKnots head - 1) do
 	(
 		scaleToLength headPartArray[i] headArray[i] headArray[i + 1]
 		rotateFromPoints headPartArray[i] headArray[i] headArray[i + 1] (eulerAngles 180 90 0)
 	)
 )
 
 fn shoulderAlign part p1 p2 =
 (
 	biped.setTransform part #pos p1 false
 	scaleToLength part p1 p2
 	
 	-- Clavicle Z-axis
 	baseVector	= (biped.getTransform spinePartArray[spinePartArray.count] #rotation as matrix3)[1]
 	goalVector	= normalize (p1 - p2)
 	rightVector	= normalize (cross baseVector goalVector)
 	crossVector	= normalize (cross rightVector baseVector)
 	theMatrix		= matrix3 crossVector rightVector baseVector p1
 	preRotate theMatrix (eulerAngles 0 0 180)
 	biped.setTransform part #rotation theMatrix false
 	
 	-- Clavice Y-axis
 	p3					= biped.getTransform part.children[1] #pos
 	partAngle		= fourPointAngle p1 p3 p1 p2
 	rotate part (angleAxis -partAngle [0,1,0])
 )
 
 fn handAlign part p1 p2 =
 (
 	scaleToLength part p1 p2
 	handAngle = -90
 	if part == rightHand do handAngle = -handAngle
 	rotateFromPoints part p1 p2 (eulerAngles handAngle 90 0)
 )
 -------------------------------------------------------------------------------------------------
 
 
 
 -- Execute transformations
 ------------------------------------------------------------------------------------------------------------
 if (numKnots spine) < 7
 	then 
 	(
 		bip.controller.spineLinks = (numKnots spine - 1)
 		
 		if numKnots head < 8
 			then
 			(
 				bip.controller.neckLinks = (numKnots head - 2)
 				
 				hipsAlign()
 				proximalAlign leftThigh targetPointLT targetPointLC
 				distalAlign leftCalf targetPointLT targetPointLC
 				footAlign leftFoot targetPointLC targetPointLF
 				
 				proximalAlign rightThigh targetPointRT targetPointRC
 				distalAlign rightCalf targetPointRT targetPointRC
 				footAlign rightFoot targetPointRC targetPointRF
 				
 				spineAlign()
 				headAlign()
 				
 				shoulderAlign leftShoulder pointLS targetPointLS
 				proximalAlign leftUpperarm targetPointLUa targetPointLFa
 				distalAlign leftForearm targetPointLUa targetPointLFa
 				handAlign leftHand targetPointLFa targetPointLH
 
 				shoulderAlign rightShoulder pointRS targetPointRS
 				proximalAlign rightUpperarm targetPointRUa targetPointRFa
 				distalAlign rightForearm targetPointRUa targetPointRFa
 				handAlign rightHand targetPointRFa targetPointRH
 			)
 			else (messageBox ("Please manually set the number of neck links to " + (numKnots head - 2) as string))
 	)
 	else (messageBox ("Please manually set the number of spine links to " + (numKnots spine - 1) as string))
 ------------------------------------------------------------------------------------------------------------

Well, no big surprise, it wasn鈥檛 hard to break. Getting the shoulders to line up properly was the biggest pain when I threw this together. Figures it鈥檚 the first thing to not work when I tried to apply it.

Okay, just鈥?wow.

I spent about 4 hours trying to nail down just what was going on with those clavicles. Sometimes they did what I wanted, sometimes they weren鈥檛 even close. I finally hit on a rather bizarre discovery: apparently, using biped.setTransform on the clavicles will ONLY have the desired results if the uppermost spine segment is perfectly vertical.

Knowing that, I made the following changes to my script, and it seems to be working fine now:

-- Definitions
------------------------------------------------------------------------------
leftLeg		= $leftLeg
rightLeg	= $rightLeg
spine		= $spine
head		= $head
leftArm		= $leftArm
rightArm	= $rightArm

pointLT		= getKnotPoint leftLeg 1 1
pointRT		= getKnotPoint rightLeg 1 1

targetPointLT	= getKnotPoint leftLeg 1 2
targetPointLC 	= getKnotPoint leftLeg 1 3
targetPointLF 	= getKnotPoint leftLeg 1 4

targetPointRT	= getKnotPoint rightLeg 1 2
targetPointRC	= getKnotPoint rightLeg 1 3
targetPointRF	= getKnotPoint rightLeg 1 4

spineArray	= for i = 1 to numKnots spine collect (getKnotPoint spine 1 i)
headArray	= for i = 1 to numKnots head collect (getKnotPoint head 1 i)

pointLS		= getKnotPoint leftArm 1 1
targetPointLS	= getKnotPoint leftArm 1 2
targetPointLUa	= getKnotPoint leftArm 1 3
targetPointLFa	= getKnotPoint leftArm 1 4
targetPointLH	= getKnotPoint leftArm 1 5

pointRS		= getKnotPoint rightArm 1 1
targetPointRS	= getKnotPoint rightArm 1 2
targetPointRUa	= getKnotPoint rightArm 1 3
targetPointRFa	= getKnotPoint rightArm 1 4
targetPointRH	= getKnotPoint rightArm 1 5

baseName	= "Bip01"

bip		= execute ("$'" + basename)
pelvis		= execute ("$'" + basename + " Pelvis'")
leftThigh	= execute ("$'" + basename + " L Thigh'")
leftCalf	= execute ("$'" + basename + " L Calf'")
leftFoot	= execute ("$'" + basename + " L Foot'")
rightThigh	= execute ("$'" + basename + " R Thigh'")
rightCalf	= execute ("$'" + basename + " R Calf'")
rightFoot	= execute ("$'" + basename + " R Foot'")

spinePartArray	= #()

for i = 1 to (numKnots spine - 1) do
(
	id = ""
	
	if i > 1 do id = (i - 1) as string
	spinePart = execute ("$'" + basename + " Spine" + id + "'")
	append spinePartArray spinePart
)

headPartArray		= #()

for i = 1 to (numKnots head - 1) do
(
	id = ""
	if i < (numKnots head - 1) then partName = " Neck" else partName = " Head"
	
	if i > 1 and i < (numKnots head - 1) do id = (i - 1) as string
	headPart = execute ("$'" + basename + partName + id + "'")
	append headPartArray headPart
)

leftShoulder	= execute ("$'" + basename + " L Clavicle'")
leftUpperarm	= execute ("$'" + basename + " L Upperarm'")
leftForearm	= execute ("$'" + basename + " L Forearm'")
leftHand	= execute ("$'" + basename + " L Hand'")

rightShoulder	= execute ("$'" + basename + " R Clavicle'") 
rightUpperarm	= execute ("$'" + basename + " R Upperarm'")
rightForearm	= execute ("$'" + basename + " R Forearm'")
rightHand	= execute ("$'" + basename + " R Hand'")
------------------------------------------------------------------------------


----------------------------------------------------------
-- Functions to find the angle between two lines
----------------------------------------------------------
fn fourPointAngle pA pB pC pD =
(
	local vAB = pB - pA
	local vCD = pD - pC
	local angle = acos (dot (normalize vAB) (normalize vCD))
	
	if angle < 90.0 then angle else (180.0 - angle)
)
----------------------------------------------------------
fn twoVectorAngle vAB vCD =
(
	local angle = acos (dot (normalize vAB) (normalize vCD))
	
	if angle < 90.0 then angle else (180.0 - angle)
)
----------------------------------------------------------

/*
--------------------------------------------------------------------------------------------
-- Function to align an axis to a plane using another axis
--------------------------------------------------------------------------------------------
-- Special thanks to Denis Trofimove
fn alignAxisToPlane node axis:#x pole: normal:[0,0,1] = 
(
	tm = node.transform

	vec = case axis of
	(
		#x: tm[1] 
		#y: tm[2] 
		#z: tm[3] 
	)
	vec = normalize vec
	normal = normalize normal
	
	if abs (dot vec normal) > 0.0 do
	(
		rot = if iskindof pole Point3 and abs (dot vec pole) < 1.0 then
		(
			pole = normalize pole
			normalize (cross pole normal)
		) else vec
		
		front = normalize (cross rot normal)
		side = normalize (cross normal front)
		if (dot vec side) < 0.0 do side = -side
		
		rtm = angleaxis (acos (dot vec side)) (normalize (cross side vec))
		node.transform = tm = translate (rotate (scalematrix tm.scale) (tm.rotation*rtm)) tm.pos
	)
	tm
)
--------------------------------------------------------------------------------------------
*/


fn rotateToAlign obj rotatedAxis rotationAxis planeNormal = 
(
format "rotateToAlign...
" 
		format "rotatedAxis: %
" rotatedAxis
		format "rotationAxis: %
" rotationAxis
		
if rotatedAxis != rotationAxis then 
	(

		if rotationAxis != planeNormal then
		(
			format "rotationAxis = %
" rotationAxis
			format "planeNormal = %
" planeNormal
			proj = cross (normalize rotationAxis) (normalize planeNormal)
			rot = acos (dot rotatedAxis (normalize proj))
		) else rot = 0.0
		if  rot>90.0 then abs (rot-=180)
		rotationAxis = normalize(cross rotatedAxis proj)
		rQuat = quat (rotationAxis.x * (sin(rot / 2))) (rotationAxis.y * (sin(rot / 2))) (rotationAxis.z * (sin(rot / 2))) (cos (rot / 2))
		rotate obj  rQuat

	)	
)



-------------------------------------------------------------------------------------------------
-- Functions to control transformations of biped parts
-------------------------------------------------------------------------------------------------
fn scaleToLength part p1 p2 =
(
	scalePart		= biped.getTransform part #scale
	lengthPart	= distance p1 p2
	biped.setTransform part #scale [lengthPart, scalePart.y, scalePart.z] false
)

fn rotateFromPoints part p1 p2 offset =
(
	theVector	= normalize (p1 - p2)
	theMatrix	= matrixFromNormal theVector
	preRotate theMatrix offset
	biped.setTransform part #rotation theMatrix false
)

fn hipsAlign =
(
	hipsPos		= (pointLT + pointRT) / 2
	biped.setTransform bip #pos hipsPos false
	
	scaleP		= biped.getTransform pelvis #scale
	hipsWidth	= distance pointLT pointRT
	biped.setTransform pelvis #scale [scaleP.x, scaleP.y, hipsWidth] false
	
	rotateFromPoints bip pointLT pointRT (eulerAngles -90 180 0)
)

fn proximalAlign part p2 p3 =
(
	p1 = biped.getTransform part #pos
	scaleToLength part p1 p2

	outVector	= normalize (p3 - p1)
	partVector	= normalize (p2 - p1)
	rightVector	= normalize (cross outVector partVector)
	upVector	= normalize (cross rightVector partVector)
	theMatrix	= matrix3 partVector upVector rightVector p1
	biped.setTransform part #rotation theMatrix false
)

fn distalAlign part p1 p2 =
(
	scaleToLength part p1 p2

	p3		= biped.getTransform part.children[1] #pos
	partAngle	= fourPointAngle p1 p3 p1 p2
	rotate part (angleAxis -partAngle [0,0,1])
)

fn footAlign foot p1 p2 =
(
	scaleFoot = biped.getTransform foot #scale
	lengthX = p1.z - p2.z
	lengthY = p1.y - p2.y
	biped.setTransform foot #scale [lengthX, lengthY, scaleFoot.Z] false
	
	biped.setTransform foot #rotation (eulerAngles 180 90 0) false
	heel		= [p1.x, p1.y, p2.z]
	ball		= [p1.x, p2.y, p2.z]
	footAngle	= fourPointAngle heel ball heel p2
	if foot == rightFoot do footAngle = (-footAngle)
	rotate foot (angleAxis footAngle [0,0,1])	
)

fn spineAlign =
(
	biped.setTransform spinePartArray[1] #pos spineArray[1] false
	
	for i = 1 to (numKnots spine - 1) do
	(
		scaleToLength spinePartArray[i] spineArray[i] spineArray[i + 1]
		rotateFromPoints spinePartArray[i] spineArray[i] spineArray[i + 1] (eulerAngles 180 90 0)
	)
)

fn headAlign =
(
	biped.setTransform headPartArray[1] #pos headArray[1] false

	for i = 1 to (numKnots head - 1) do
	(
		scaleToLength headPartArray[i] headArray[i] headArray[i + 1]
		rotateFromPoints headPartArray[i] headArray[i] headArray[i + 1] (eulerAngles 180 90 0)
	)
)

fn shoulderAlign part tgt =
(
	topSpine	= spinePartArray[spinePartArray.count]
	tgt.parent	= topSpine
	
	spineRot		= biped.getTransform topSpine #rotation
	biped.setTransform topSpine #rotation (quat -0.707107 0 -0.707107 0) false
	p1		= getKnotPoint tgt 1 1
	p2		= getKnotPoint tgt 1 2	
	
	biped.setTransform part #pos p1 false
	scaleToLength part p1 p2
	
	shoulderAngle = 0
	if part == rightShoulder do shoulderAngle = 180
	rotateFromPoints part p1 p2 (eulerAngles shoulderAngle 90 90)

	biped.setTransform topSpine #rotation spineRot false	
)

fn handAlign part p1 p2 =
(
	scaleToLength part p1 p2
	handAngle = -90
	if part == rightHand do handAngle = -handAngle
	rotateFromPoints part p1 p2 (eulerAngles handAngle 90 0)
)
-------------------------------------------------------------------------------------------------



-- Execute transformations
------------------------------------------------------------------------------------------------------------
if (numKnots spine) < 7
	then 
	(
		bip.controller.spineLinks = (numKnots spine - 1)
		
		if numKnots head < 8
			then
			(
				bip.controller.neckLinks = (numKnots head - 2)
				
				hipsAlign()
				proximalAlign leftThigh targetPointLT targetPointLC
				distalAlign leftCalf targetPointLT targetPointLC
				footAlign leftFoot targetPointLC targetPointLF
				
				proximalAlign rightThigh targetPointRT targetPointRC
				distalAlign rightCalf targetPointRT targetPointRC
				footAlign rightFoot targetPointRC targetPointRF
				
				spineAlign()
				headAlign()
				
				shoulderAlign leftShoulder leftArm
				proximalAlign leftUpperarm targetPointLUa targetPointLFa
				distalAlign leftForearm targetPointLUa targetPointLFa
				handAlign leftHand targetPointLFa targetPointLH

				shoulderAlign rightShoulder rightArm
				proximalAlign rightUpperarm targetPointRUa targetPointRFa
				distalAlign rightForearm targetPointRUa targetPointRFa
				handAlign rightHand targetPointRFa targetPointRH
			)
			else (messageBox ("Please manually set the number of neck links to " + (numKnots head - 2) as string))
	)
	else (messageBox ("Please manually set the number of spine links to " + (numKnots spine - 1) as string))
------------------------------------------------------------------------------------------------------------
 PEN

The clavicles just don鈥檛 work in biped. I found the same issue when I tried to write a tool for a client. It will work when figure mode is off but not when it is on.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

i remember this problem too.
i鈥檝e checked my old code i see that i did all structure change in the Figure Mode ON, aligned(transformed) biped bones in the Mode OFF, saved as a Bip file, went back to the Mode ON and applied the bip file.
Looks like i had a big reason doing it this way but i don鈥檛 remember why鈥?/p>

 PEN

Use getNodeByName instead of execute in you code.

Thanks for the tip on getNodeByName! Also, I should have mentioned that this script is for using with figure mode turned on. I had a lot of problems getting it to work, and ended up spending about 4 hours just trying to pinpoint the source of the problem. But as I said, as long as the uppermost spine segment is completely vertical, the clavicles seem to behave properly (at least they have for me so far.)