-------------------------------------------------------------------------------------
--
-- 	Author:  Ben Bathen
--	Script to create a Morph target between two meshes that have the same 
--	vertex count but different index order.  This is acommplished by checking the angles of the edges using 
--	the dot product operation as opposed to the indeces of the vertices themselves.
-- 	
--
--------------------------------------------------------------------------------------



global morphBadIndecesUI

-- function to get the vertices that are connected to the currently selected vertex
fn getConnectedVerts MeshNode VertIndex=
(
	connectedEdges = (polyop.getEdgesUsingVert MeshNode VertIndex ) as array
	connectedVerts = (polyop.getVertsUsingEdge MeshNode connectedEdges) as array
	origVertIndex = findItem connectedVerts VertIndex
	connectedVerts = deleteItem connectedVerts origVertIndex
	return connectedVerts
)

fn getLastGoodVert dupMesh snappedVerts=
(
	--iterate through the array of verts we have already snapped
	for i = snappedVerts[5][1] to snappedVerts[1].count do
	(
		connVerts = getConnectedVerts dupMesh snappedVerts[1][i]
		for vert in connVerts do
		(
			if findItem snappedVerts[1] vert == 0 do
			(
				return i
			)
		)
	)
	return 0
)

-- vertex to snap one vertex to another in world space
fn snapVerts dupMesh dupMeshVert morphTarg morphTargVert = 
(
		worldPos = polyop.getvert morphTarg morphTargVert
		polyop.setVert dupMesh dupMeshVert worldPos
		completeRedraw()
)

fn getVectorDifference v1 v2 = 
(
	theAngle = dot (normalize v1) (normalize v2)
	return theAngle
)

fn getEdgeVector meshNode theEdge origVert=
(
	connectedVerts = (polyop.getVertsUsingEdge meshNode theEdge) as array
	connectedIndex = findItem connectedVerts origVert
	connectedVert = deleteItem connectedVerts connectedIndex
	position1 = polyop.getVert meshNode origVert
	position2 = polyop.getVert meshNode connectedVert[1]
	edgeVector = position2 - position1
)


-- iterate through the mesh using the grow selection method
fn snapConnectedVerts dupMesh dupVert morphTargNode morphTargVert snappedVerts = 
(
		locator = point()
		locator.pos = polyop.getVert dupMesh dupVert
		locator.centermarker = true
		locator.constantscreensize = true
		locator.size = 5
		locator.wireColor = [255,0,0]
	
		--clear the array of recently snapped vertices
		snappedVerts[3] = #()
		snappedVerts[4] = #()
	
		--get the edge vector of each of the connected edges on the duplicate mesh
		dupEdges = (polyop.getEdgesUsingVert dupMesh dupVert) as array
		morphEdges = (polyop.getEdgesUsingVert morphTargNode morphTargVert) as array
		
		--for every mesh attached to the originally selected vertex on duplicate mesh
		for dupEdge in dupEdges do
		(
				--find the vertex on the other end of the edge on the duplicate mesh
				dupEdgeVerts = (polyop.getVertsUsingEdge dupMesh dupEdge) as array
				dupCorrIndex = findItem dupEdgeVerts dupVert
				dupVertToSnap = deleteItem dupEdgeVerts dupCorrIndex
			
				-- if the vertex on the end of the edge hasn't already been snapped then...
				if findItem snappedVerts[1] dupVertToSnap[1] == 0 then
				(
					minDiff = -1
					correspEdge = undefined
					dupVector = getEdgeVector dupMesh dupEdge dupVert

					for morphEdge in morphEdges do
					(
						morphVector = getEdgeVector morphTargNode morphEdge morphTargVert
						vectorDifference = getVectorDifference dupVector morphVector

						if vectorDifference > minDiff do
						(
							minDiff = vectorDifference
							correspEdge = morphEdge
						)
					)
					
					--remove the corresponding edge from the list of attached edges so that it can't get assigned twice.
					edgeIndex = findItem morphEdges correspEdge
					if edgeIndex != 0 do
					(
						morphEdges = deleteItem morphEdges edgeIndex
					)

					--find the vertex on the other end of the edge on the morph Target
					correspVerts = (polyop.getVertsUsingEdge morphTargNode correspEdge) as array
					correspIndex = findItem correspVerts morphTargVert
					correspVert = deleteItem correspVerts correspIndex
					worldPos = polyop.getVert morphTargNode correspVert[1]
					
					append snappedVerts[1] dupVertToSnap[1]
					append snappedVerts[3] dupVertToSnap[1]
					append snappedVerts[2]  correspVert[1]			
					append snappedVerts[4] correspVert[1]

					--polyop.setVert dupMesh dupVertToSnap[1] worldPos
					completeRedraw()

				)
		)
	delete locator
	return snappedVerts 
)



-- function to snap the vertices of the duplicate mesh to the morphTarget
fn runMorph origMesh origMeshVert morphTarg morphTargVert =
(
	origMeshNode = getNodeByName(origMesh)
	morphTargNode = getNodeByName(morphTarg)
	origMeshVert = origMeshVert as integer
	morphTargVert = morphTargVert as integer

	vertCount = origMeshNode.numverts
	
	-- duplicate the original mesh and move it to the position of the mesh with bade index order
	maxOps.CloneNodes origMeshNode cloneType: #copy  newNodes: &dupMeshArray
	dupMesh = dupMeshArray[1]
	dupMesh.transform = morphTargNode.transform
	
	max modify mode
	select dupMesh
	oldMorphMod = dupMesh.modifiers[#Morpher]
	if oldMorphMod != undefined do
	(
		deleteModifier dupMesh oldMorphMod
	)
	dupMesh.name = uniqueName(origMesh + "_new")
	
	
	--snap the original vertices 
	snapVerts dupMesh origMeshVert morphTargNode morphTargVert 
	
	--create a multiDimensional array to remember which vertex you snapped to which
	dupSnappedVerts = #()
	morphSnappedVerts = #()
	lastSnappeddupVerts = #()
	lastSnappedMorphVerts = #()
	lastUnconnectedVert = #(1)
	snappedVerts = #(dupSnappedVerts , morphSnappedVerts, lastSnappeddupVerts, lastSnappedMorphVerts, lastUnconnectedVert)
	
	append snappedVerts[1] origMeshVert
	append snappedVerts[2] morphTargVert
	append snappedVerts[3] origMeshVert
	append snappedVerts[4] morphTargVert
	
	rollout progressTest "Storing Corresponding Vertices"
	(
		progressbar doit_prog color:blue 
		label lab1 "storing corresponding vertices"
	)
	createDialog progressTest 300 60 
	
	while (getLastGoodVert dupMesh snappedVerts) != 0 do
	(
		percentDone = (snappedVerts[1].count as float ) / (vertCount as float) * 100.0 
		progressTest.doit_prog.value = percentDone 
		progressTest.lab1.text =  ("doing " + (snappedVerts[1].count as string ) + " of " + (vertCount as string))
		
		tempMultiArray = #(#(),#(),#(),#())
		if snappedVerts[3].count != 0 then
		(
			-- for all the vertices that just got snapped do...
			for i = 1 to snappedVerts[3].count do
			(
				if snappedVerts[3][i] != undefined then
				(
					--if there are vertices in the last snapped vert array snap the verts that are connected to them...
					tempMultiArray = snapConnectedVerts dupMesh snappedVerts[3][i]  morphTargNode snappedVerts[4][i] snappedVerts
					snappedVerts = tempMultiArray 
				)
			)
		)
		--otherwise go back through the list of vertices that we've snapped and find the first one that is attached to unsnapped vertices
		else
		(
			--go back through the array of snapped vertices until you find one that is connected to unsnapped verts
			goodVertIndex = getLastGoodVert dupMesh snappedVerts
			if goodVertIndex != 0 do
			(
				tempMultiArray = snapConnectedVerts dupMesh snappedVerts[1][goodVertIndex]  morphTargNode snappedVerts[2][goodVertIndex] snappedVerts
				snappedVerts = tempMultiArray 
			)
			snappedVerts[5][1] = goodVertIndex
		)
	)
	
	destroyDialog progressTest
	
	for i = 1 to snappedVerts[1].count do
	(
		snapVerts dupMesh snappedVerts[1][i] morphTargNode snappedVerts[2][i]
	)
	
	
	-- add the duplicate mesh as a blend target in the morpher modifier on the original mesh.
	morpherMod = origMeshNode.modifiers[#Morpher]
	if morpherMod == undefined then
	(	
		morpherMod = morpher()
		addModifier origMeshNode morpherMod
		WM3_MC_BuildFromNode morpherMod 1 dupMesh
	)
	else
	(
		i = 0
		while (WM3_MC_HasData morpherMod i) == true do
		(
			i = i + 1
		)
		WM3_MC_BuildFromNode morpherMod i dupMesh
	)
	morpherMod.Autoload_of_targets 
	--dupMesh.pos = dupMesh.pos + [10,0,0]
	WM3_RefreshChannelListUI morpherMod
	WM3_RefreshChannelParamsUI morpherMod
)


--function to verify the selected meshes before snapping them.
fn validateSelection origMeshTF origMeshVertTF morphTargTF morphTargVertTF = 
(
	origMeshNode = getNodeByName(origMeshTF)
	morphTargNode = getNodeByName(morphTargTF)
	origMeshVert = origMeshVertTF as integer
	morphTargVert = morphTargVertTF as integer
	
	isValid = true
	--check to make sure you didn't accidentally load the same mesh into the original and morph slots
	if origMeshTF == morphTargTF do
	(
		isValid = false
		rollout badSelection1 "Bad Selection" width: 200 height: 20
		(
			label lab1 "cannot morph between a mesh and itself"
		)
		createDialog badSelection1
	)
	if origMeshNode.numverts != morphTargNode.numverts do
	(
		isValid = false
		rollout badSelection2 "Bad Selection" width: 250 height: 20
		(
			label lab1 "Both meshes must have the same Vertex Count"
		)
		createDialog badSelection2
	)	
	if origMeshVertTF == "undefined" do
	(
		isValid = false
		rollout badSelection3 "Bad Selection" width: 250 height: 20
		(
			label lab1 "You must select the same vertex on each mesh"
		)
		createDialog badSelection3
	)
	if morphTargVertTF == "undefined" do
	(
		isValid = false
		rollout badSelection4 "Bad Selection" width: 250 height: 20
		(
			label lab1 "You must select the same vertex on each mesh"
		)
		createDialog badSelection4
	)
	if classOf origMeshNode.baseObject != Editable_Poly do
	(
		isValid = false
		rollout badSelection5 "Bad Selection" width: 250 height: 20
		(
			label lab1 "Meshes must be of type Editable_Poly"
		)
		createDialog badSelection5
	)
	if classOf morphTargNode.baseObject != Editable_Poly do
	(
		isValid = false
		rollout badSelection5 "Bad Selection" width: 250 height: 20
		(
			label lab1 "Meshes must be of type Editable_Poly"
		)
		createDialog badSelection5
	)
	return isValid
)

-- rollout controls
rollout morphBadIndeces "Morpher"
(
	editText origMeshTF "originalMesh"
	editText origMeshVertTF "originalVert"
	button loadMeshBtn "load original Mesh"
	editText morphTargTF "morphMesh"
	editText morphTargVertTF "morphVert"
	button loadMorphBtn "load morph target"
	button runBtn "Run" width: 100
	
	on loadMeshBtn pressed do
	(
		sel = selection as array
		if sel.count > 0 then
		(
			origMeshTF.text = sel[1].name
			origMeshNode = getNodeByName(origMeshTF.text)
			origMeshNode = origMeshNode.baseObject
			selectedVerts = (polyop.getVertSelection origMeshNode ) as array
			origMeshVertTF.text = selectedVerts[1] as string
		)
	)
	
	on loadMorphBtn pressed do
	(
		sel = selection as array
		if sel.count > 0 then
		(
			morphTargTF.text = sel[1].name
			selectedVerts = (polyop.getVertSelection sel[1] ) as array
			morphTargVertTF.text = selectedVerts[1] as string
		)
	)
	
	on runBtn pressed do
	(
		print localTime
		isValid = validateSelection origMeshTF.text origMeshVertTF.text morphTargTF.text morphTargVertTF.text
		if isValid == true do
		(
			runMorph origMeshTF.text origMeshVertTF.text morphTargTF.text morphTargVertTF.text
		)
		print localTime
	)
)

-- function to create the rollout
fn runMorphBadIndecesUI = 
(	
	try (closeRolloutFloater morphBadIndecesUI) catch()
	morphBadIndecesUI = (newRolloutFloater "Morph Bad Indeces" 200 203)
	addrollout morphBadIndeces morphBadIndecesUI
)



