304 lines
8.7 KiB
Plaintext
Executable File
304 lines
8.7 KiB
Plaintext
Executable File
/**
|
|
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")
|
|
) |