macroScript MeshAnimCopy category:"Tools" ToolTip:"Copy vertex animation from one mesh to another"\  

(

 tolerance = .035



 fn isOK obj = classof obj == Editable_mesh and obj.modifiers.count == 0

 

 messagebox "choose mesh to copy FROM:"

 oldmesh = pickobject message:"Choose animated (old) mesh" filter:isOK

 

 messagebox "chose mesh to copy TO:"

 newmesh = pickobject message:"Choose new mesh" filter:ISOK

  

 disablesceneredraw()



oldmeshkeys = oldmesh[4][1].keys

 

slidertime =  0



struct mvert (	

				index,  -- vertex index

				 home, -- rest position

				 matchv, -- matching vert in old mesh

				 matchf, -- matching face in old mesh

				 neighbor, -- neighbor in this mesh

				 offset -- vector from reference

				 )





WorkVerts = #() -- the array of moving verts



for i = 1 to newmesh.selectedverts.count do

	(

	animatevertex newmesh newmesh.selectedverts[i].index 

	append WorkVerts (	mvert \

						 index:newmesh.selectedverts[i].index \

						 home: (newmesh.selectedverts[i].pos * inverse newmesh.objecttransform)

						)

	)



-- get range of possible matches in old mesh, using 

-- bbox from newmesh selection

modpanel.AddModToSelection (VolumeSelect())

modtm = getModContextTm newmesh newmesh.modifiers[1]

bbmx = getModContextBBoxMax newmesh newmesh.modifiers[1]

wsctr = newmesh.modifiers[1].gizmo.pos * inverse modtm * newmesh.objecttransform



--- make a box to match the volumeselect bounding box 

--- a little larger than the original to get stray edge cases

xbox = box\

	width:(2.25 * abs (bbmx.x))\

	length:(2.25 * abs (bbmx.y))\

	height:(2.25 * abs (bbmx.z))

xbox.pivot = xbox.center



--- move the box to the same relative position 

xbox.pos = (wsctr * inverse newmesh.objecttransform) * oldmesh.objecttransform



--- add a volumeselect modifier

modpanel.setcurrentobject oldmesh

addmodifier oldmesh (VolumeSelect())

oldmesh .modifiers[1].volume = 3

oldmesh .modifiers[1].node = xbox

oldmesh .modifiers[1].level= 1



--- get the vertices selected by the volselect

UseOM =  oldmesh.selectedverts

RefVerts = #()

for items in UseOM do

	(

	append RefVerts items.index

	)



format "searching for matches among % verts\n" refverts.count

---- delete modifiers for cleanliness

deletemodifier oldmesh oldmesh.modifiers[1]

deletemodifier newmesh newmesh.modifiers[1]

delete xbox





progressstart "finding neighbors"

---- establish the nearest neighbor for each vert

for i = 1 to Workverts.count do

	(

	testdist = 999

	testnb = 0

	for j = 1 to Workverts.count do 

		(progressupdate ((j/ workverts.count as float) * 100)

		if j == i do continue

		homedist = distance workverts[i].home workverts[j].home

		if homedist < testdist do

			(

			testdist = homedist

			testnb = j

			)

		)

	workverts[i].neighbor = workverts[testnb].index

	workverts[i].offset = (workverts[testnb].home - workverts[i].home) 

	)

progressend()



--- look for exact matches



progressstart "Looking for exact matches"

for i = 1 to Workverts.count do

	(thisvert = workverts[i].home

	testdist = 999

	testnb = 0

	for j = 1 to RefVerts.count do

		(progressupdate ((j/ refverts.count as float) * 100)

		thatvert = oldmesh.verts[RefVerts[j]].pos * inverse oldmesh.objecttransform

		if abs (thisvert.z - thatvert.z) > tolerance do (continue)

		homedist = distance thisvert thatvert

		if homedist < testdist do

			(

			testdist = homedist

			testnb = refverts[j]

			)

		)

	if testdist < tolerance do

		(

		workverts[i].matchv = testnb

		)

	)

progressend()



--- look for normal-projected matches



projection = 1



for newverts in workverts where newverts.matchv == undefined do

	(

	norm = normalize (getnormal newmesh newverts.index)

	testray = ray ((newverts.home + norm) * oldmesh.objecttransform) (-1 * norm)

	probe = intersectrayex oldmesh testray

	if probe != undefined do 

		(

		format "vertex % projects onto face %\n" newverts.index probe[2]

		newverts.matchf = probe

		newverts.offset = (newverts.home - (probe[1].pos * inverse oldmesh.objecttransform) )

		)

	)





--- start iterating through keys

animate on





for w = 1 to oldmeshkeys.count  do

	(slidertime = oldmeshkeys[w].time

	progressstart ("Setting keys for time " + slidertime as string)

	for u = 1 to workverts.count do

		(progressupdate ((u / workverts.count as float) * 100)

		thisvert = workverts[u]

		addnewkey newmesh[4][1][thisvert.index] slidertime

		case of

			(

			(thisvert.matchv != undefined):	(

											format "x"

											newmesh[4][1][thisvert.index].value = \

												if (oldmesh[4][1][thisvert.matchv]).value == undefined then \

												thisvert.home else (oldmesh[4][1][thisvert.matchv]).value

											

											)

			(thisvert.matchf != undefined):	(format "o"

											matchface = getface oldmesh (thisvert.matchf[2])

												v1 = (getvert oldmesh (matchface.x)) * inverse oldmesh.objecttransform  

												v2 = (getvert oldmesh (matchface.y)) * inverse oldmesh.objecttransform

												v3 = (getvert oldmesh (matchface.z)) * inverse oldmesh.objecttransform

												newmesh[4][1][thisvert.index].value = (v1 * thisvert.matchf[3].x)\

														+ (v2 * thisvert.matchf[3].y)\

														+ (v3 * thisvert.matchf[3].z)\

														+ thisvert.offset

												

											)

			default: (

					format "n"

					)

			)

		

		)

	for nomatch in workverts where nomatch.matchv == undefined and nomatch.matchf == undefined do

		(

		newmesh[4][1][nomatch.index].value  == newmesh[4][1][nomatch.neighbor].value + nomatch.offset

		format "(%-%)" nomatch.index nomatch.neighbor

		)

	progressend()

	)

enablesceneredraw()

)
