Getting rotations from FFD cages

Originally posted on CG Talk, I’m putting my solution here as it may be useful others.

I’ve been trying to convert an animated FFD to a skinned bone based animation.

Creating a bone for each point and animating them to match the position of each control point is easy, but unless I handle rotation I get shearing.

In the end I decided to create a proxy object (either as a clone of of the original object or a new FFD cage style box mesh), deform that and read the transforms from that mesh. This meant the script would no longer be limited to FFDs, but to any max modifier. Again, getting the position of the verts of the proxy was easy, but getting the rotation of the verts isn’t so trivial. I had to learn some matrix maths, and thankfully David Bailey sits close to me and was a great help.

I have this working as part of the script, so I thought I’d paste the transform code for others.

I’m sending in a bonelist which is an array of bone objects, plus an proxy object that has the vertex deformations baked onto it.

This system is using a bone per vert, so the number of bones in the bone list match the number of bones in the proxymesh.

This loop is also inside an active time range loop.


for arrayIndex = 1 to bonelist.count do
(
					
		baseVertPos=(getVert proxyobj.mesh arrayIndex) * proxyobj.objectTransform 
		basevertexNormal = getNormal proxyobj.mesh arrayIndex	
					
		-- Pick a random edge connected to this vert
		allConnectedEdges = (polyop.getEdgesUsingVert proxyobj arrayIndex) as array
		firstConnectedEdge = allConnectedEdges[1]

		-- Stick the known vert into the vert list for that edge
		-- This is one I want to ignore
		connectedVertlist = #(arrayIndex)

		-- Find all verts on that edge, append if unique to connectedVertlist 
		allConnectedVerts = (polyop.getVertsUsingEdge proxyobj firstConnectedEdge) as array
		for v = 1 to allConnectedVerts.count do appendifunique connectedVertlist allConnectedVerts[v]
						
		-- the last vert in the new list is the connected vert, thants the one we want
		-- This will probably not be perpendicular, but it will give me a vector
		secondvert = connectedVertlist[connectedVertlist.count]
		secondvertpos = (getVert proxyobj.mesh secondvert)* proxyobj.objectTransform
		secondVector =  secondvertpos - baseVertPos
										
		-- I now have 2 vectors, these describe a plane, and I can get the cross product of that to get third vector and create another plane
		-- Then I can cross product that plane to get the 3rd 
		row1=basevertexNormal
		row2=normalize (cross row1 secondVector)
		row3=normalize (cross row1 row2)
		finalTransform = matrix3 row1 row2 row3 baseVertPos

		thebone = bonelist[arrayIndex]
		thebone.transform = finalTransform
)	-- end bone loop