/** script to try and mimic Rune Spaans TrollHunter rigging/binding tool will epxlode a mesh into its constituent tris, then rigid bind these to closest bone **/ --Matt Rennie --April 2014 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- clearListener() filein "rockstar/export/settings.ms" -- This is fast -- Figure out the project theProjectRoot = RsConfigGetProjRootDir() theProject = RSConfigGetProjectName() theWildWest = RsConfigGetWildWestDir() theProjectConfig = RsConfigGetProjBinConfigDir() toolsRoot = RsConfigGetToolsRootDir() -- filein (RsConfigGetWildWestDir() + "script\\max\\Rockstar_North\\character\\Includes\\FN_Rigging.ms") filein (theWildWest + "script/3dsMax/_config_files/Wildwest_header.ms") filein (theWildWest + "script/3dsMax/_common_functions/FN_RSTA_Rigging.ms") filein (theWildWest + "script/3dsMax/_common_functions/FN_RSTA_UI.ms") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fn RSTA_runeBindMesh rootObj obj = ( --first of we need to build the hierarchy rootHierarchyArray = #() rootHierarchyArray = RSTA_getAllChildren rootObj arr:rootHierarchyArray#() --now we clone obj then explode it into tris if classof obj == Editable_Poly then ( disableSceneRedraw() max create mode local nnl = undefined maxOps.cloneNodes obj cloneType:#copy newNodes:&nnl select nnl nnl[1].name = (obj.name+"_RuneBind") local runeBindObj = nnl[1] local totalFaces = polyop.getNumFaces runeBindObj nnl = undefined maxOps.cloneNodes obj cloneType:#copy newNodes:&nnl select nnl nnl[1].name = (obj.name+"_RuneBind2") local runeBindObj2 = nnl[1] local totalFaces2 = polyop.getNumFaces runeBindObj2 initProgBar() for poly = totalFaces to 1 by -1 do ( polyop.detachFaces runeBindObj poly delete:true asNode:false progBar.prog.value = (100.*poly/totalFaces) ) format ("Faces detatched.\n") --ok we'll loop through again now and find the closest bone local faceToBoneArray = #() if totalFaces != totalFaces2 do ( format "FaceCount mismatch\n" break() ) local centrePoint = point pos:[0,0,0] name:"centrePoint" format ("Building detatched face data...\n") for poly = totalFaces2 to 1 by -1 do ( local startTime = timestamp() local dist = 100000.0 --set to a ludicroudsly high number local closestJoint = undefined local thisName = undefined format ("Building detached face data for face "+(poly as string)+"\n") -- runeBindObj.EditablePoly.SetSelection #Face #{poly} local newNode = undefined local thisName = (runeBindObj2.name+"_"+(poly as string)) polyop.detachFaces runeBindObj2 poly delete:false asNode:true name:thisName node:#(newNode) local thisFace = getNodeByName thisName CenterPivot thisFace --format (thisName+" generated.\n") for joint in rootHierArchyArray do ( --*** instead of jusing joint pivot we should use joint bbox centre centrePoint.transform = joint.transform local jointBBox = nodeGetBoundingBox joint joint.transform local mp1 = ((jointBBox[1][1] + jointBBox[2][1]) / 2) local mp2 = ((jointBBox[1][2] + jointBBox[2][2]) / 2) local mp3 = ((jointBBox[1][3] + jointBBox[2][3]) / 2) in coordsys joint centrePoint.position = [mp1,mp2,mp3] local jointVsFaceDist = (distance centrePoint thisFace) if jointVsFaceDist < dist do ( dist = jointVsFaceDist closestJoint = joint ) ) -- format ("Setting face "+thisFace.name+" to use joint "+closestJoint.name+"\n") delete thisFace local thisData = #( #(), #() ) append thisData[1] closestJoint --now we will get the vert numbers of this poly local faceVerts = polyop.getFaceVerts runeBindObj poly for fv in faceverts do ( append thisData[2] fv ) --format ((thisData as string)+"\n") append faceToBoneArray thisData --format (thisName+" data parsed.\n") local endTime = timestamp() local timeTaken = ((endTime - startTime) / 1000.0) if timeTaken > 0.21 do ( gc() ) format ("Processing this face took "+(timeTaken as string)+" seconds\n") progBar.prog.value = (100.*poly/totalFaces) ) format ("Face detach data created.\n") -- enableSceneRedraw() delete runeBindObj2 delete centrePoint format ("Starting skinning...\n") local Faces = polyop.getNumFaces runeBindObj --format ("Skin mesh has "+(faces as string)+" total faces...\n") --now we have got a mapping of faces to joints we can skin it all up max modify mode select runeBindObj modPanel.addModToSelection (Skin ()) ui:on -- addModifier runeBindObj (Skin()) local skinMod = runeBindObj.modifiers[#Skin] skinMod.bone_Limit = 4 skinMod.showNoEnvelopes = on for wbone = 1 to rootHierarchyArray.count do ( --print wbone try (skinOps.addBone skinMod rootHierarchyArray[wbone] 1) catch () ) local vertCount = polyop.getNumVerts runeBindObj --format ("Skin mesh has "+(vertCount as string)+" total verts...\n") --first off we'll blanket weight every vert to the base bone in the skin mod for v = 1 to vertCount do ( skinOps.SetVertexWeights skinMod v 1 1.0 ) for g = 1 to faceToBoneArray.count do ( local fb = faceToBoneArray[g] --format ("fb: "+fb as string+"\n") local joint = fb[1][1] --format ("Skinning "+(joint.name+"\n")) local verts = fb[2] local fbNo = findItem rootHierarchyArray joint for vertNo in verts do ( --format ("Attempting to skin joint "+joint.name+" to vert "+(vertNo as string)+"\n") skinOps.SetVertexWeights skinMod vertNo fbNo 1.0 ) -- for fbNo = 1 to rootHierarchyArray.count do -- ( -- thisJoint = skinOps.GetBoneName skinMod fbNo 1 -- -- if thisJOint == joint.name do -- ( -- --ok now we have found this bone we need to set all the verst to be weighted 100% to it -- -- for vertNo in verts do -- ( -- --format ("Attempting to skin joint "+joint.name+" to vert "+(vertNo as string)+"\n") -- skinOps.SetVertexWeights skinMod vertNo fbNo 1.0 -- ) -- ) -- ) progBar.prog.value = (100.*g/faceToBoneArray.count) --format (joint.name+" skinned. \n") ) format ("Skinning rough mesh.\n Beginning skinwrap to "+obj.name+"\n") max create mode destroyDialog progBar --now we'll skinwrap the original to this runeBind select obj addModifier obj (Skin_Wrap ()) local wrapMod = obj.modifiers[#Skin_Wrap] format ("Beginning skinWrap - this can take a while!\n") wrapMod.meshList = #(runeBindObj) wrapMod.falloff = 1 wrapMod.engine = 0 --face deformation wrapMod.threshold = 0.001 wrapMod.weightAllVerts = on wrapMod.ConvertToSkin true if obj.modifiers[#Skin] != undefined do ( format "Applying some default skin values\n" --toggle always deform off and on obj.modifiers[#Skin].always_deform = false obj.modifiers[#Skin].always_deform = true obj.modifiers[#Skin].cross_radius = 0.0834036 obj.modifiers[#Skin].mirrorEnabled = off obj.modifiers[#Skin].bone_Limit = 4 obj.modifiers[#Skin].clearZeroLimit = 0.0 skinOps.RemoveZeroWeights obj.modifiers[#Skin] obj.modifiers[#Skin].Filter_Vertices = on obj.modifiers[#Skin].shadeweights = on obj.modifiers[#Skin].showNoEnvelopes = on ) -- delete runeBindObj enableSceneRedraw() format ("runeBinding complete.\n") ) else ( format ("Please operate on Editable_Poly objects.\n") ) ) obj = $ rootObj = getNodeByName "Dummy01" if selection.count == 1 then ( RSTA_runeBindMesh rootObj obj ) else ( messagebox ("Please run with only one mesh selected.\n") )