-- -- File:: pipeline/util/skinutils.ms -- Description:: Skin modifier utilities -- -- Author:: David Muir -- Date:: 7 July 2009 -- ----------------------------------------------------------------------------- -- Uses ----------------------------------------------------------------------------- --filein "pipeline/util/file.ms" -- loaded on startup by startup.ms ----------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------- -- -- utility: RsSkinUtils -- desc: Skin modifier utilities -- utility RsSkinUtils "RsSkinUtils" ( -- -- name: HasSkin -- desc: Return true if object has Skin modifier, false otherwise. -- fn HasSkin obj = ( ( obj.modifiers[#Skin] != undefined ) ) fn RsIsBoneRec obj = ( if undefined!=obj.parent and (RsIsBoneRec obj.parent) then return true else if (IsBone obj) then return true else return false ) fn IsBone obj = ( local skinrefs = #() skinrefs = for ref in (refs.dependents obj) where not isDeleted ref and Skin==(classof ref) collect ref if skinrefs.count>0 then ( return true ) return false ) -- -- name: BakeAndSaveWeights -- desc: Bake vertex weights and save to filename -- fn BakeAndSaveWeights obj filename = ( local result = false if ( HasSkin obj ) then ( setCommandPanelTaskMode #modify modPanel.setCurrentObject obj.modifiers[#Skin] subobjectLevel = 1 obj.modifiers[#Skin].Filter_Vertices = true local nSkinVerts = skinOps.getNumberVertices obj.modifiers[#Skin] skinOps.SelectVertices obj.modifiers[#Skin] #{1..nSkinVerts} skinOps.bakeSelectedVerts obj.modifiers[#Skin] RsMakeSurePathExists filename skinOps.saveEnvelope obj.modifiers[#Skin] filename subobjectlevel = 0 result = true ) result ) -- -- name: SkinAndLoadWeights -- desc: Create Skin modifier and load vertex weights from filename -- fn SkinAndLoadWeights obj filename bone_list match_by_name:false bone_limit:4 = ( setCommandPanelTaskMode #modify skinMod = Skin() skinMod.bone_Limit = bone_limit skinMod.showNoEnvelopes = true addModifier obj skinMod subobjectLevel = 1 modPanel.setCurrentObject obj.modifiers[#Skin] -- Prior to loading the weights, if we are matching by name then we -- install a handler to press the Match By Name button on the load -- dialog. if ( match_by_name ) then ( DialogMonitorOPS.RegisterNotification RsSkinUtils.__LoadEnvMatchByNameCallback ID:#SkinAndLoadWeightsCallback DialogMonitorOPS.Enabled = true ) for b in bone_list do try ( skinOps.addBone obj.modifiers[#Skin] b 1 ) catch () completeRedraw() skinOps.loadEnvelope obj.modifiers[#Skin] filename -- Uninstall the dialog callback. if ( match_by_name ) then ( DialogMonitorOPS.Enabled = false DialogMonitorOPS.UnregisterNotification ID:#SkinAndLoadWeightsCallback ) subobjectLevel = 0 true ) -- -- name: CollectBoneNamesInSkeleton -- desc: Collects all of the done names that are currently in the skeleton hierarchy -- starting at the given root bone and travelling down -- fn CollectBoneNamesInSkeleton rootBone boneNames = ( append boneNames rootBone.name boneChildren = rootBone.children for boneChild in boneChildren do ( CollectBoneNamesInSkeleton boneChild boneNames ) ) -- -- name: ValidateSkeletonForGeometry -- desc: Validates that the bones attached to the models -- skin modifier are all present in the skeleton hierarchy -- fn ValidateBonesInHierarchy obj messages = ( -- check to see if a skin modifier exists objectModifiers = obj.modifiers skinModifier = undefined for objModifier in objectModifiers do ( if classof objModifier == skin Then ( skinModifier = objModifier ) ) if skinModifier == undefined then ( -- Can export a prop without a skin modifier on it return true ) -- Create an array of the bones in the scene that are attached to the object sceneBone = for obj in objects where (refs.dependencyLoopTest skinModifier obj) collect obj if sceneBone.count == 0 Then ( if messages != undefined Then ( append messages "Couldn't find any bones in the scene that the skin modifier depends on." ) return false ) -- Pick any bone, find the root bone and then collect the names in the scene hierarchy starting at that root bone rootSkeletonBone = sceneBone[1] while rootSkeletonBone.parent != undefined do ( rootSkeletonBone = rootSkeletonBone.parent ) hierarchyBoneNames = #() CollectBoneNamesInSkeleton rootSkeletonBone hierarchyBoneNames -- make sure the skin modifier is selected and retrieve the bone count max modify mode modpanel.setCurrentObject skinModifier boneCount = skinOps.GetNumberBones skinModifier if boneCount == 0 Then ( if messages != undefined Then ( append messages "Skin modifier doesn't have any bones attached to it." ) return false ) -- Cycle through the skin bones and check them against the hierarchy errorFound = false for i = 1 to boneCount do ( boneName = (skinOps.GetBoneName skinModifier i 1) boneFound = false for hierarchyBoneName in hierarchyBoneNames do ( if boneName == hierarchyBoneName Then ( boneFound = true ) ) if boneFound == false Then ( if not errorFound Then ( if messages != undefined Then ( append messages "Unable to export due the fact that the following bones were not found in the hierarchy:" ) errorFound = true ) if messages != undefined Then ( append messages boneName ) ) ) if errorFound then ( return false ) true ) -- -- name: TestSkinObjectDuringMapTest -- desc: Checks a object that has a skin modifier to make sure it is -- valid for a map export -- fn TestSkinObjectDuringMapTest obj skinMod messages = ( local finish = false -- get local infomation local boneCount = 0 local boneNames = #() local rootSkeletonBone = undefined if finish == false and skinMod != undefined Then ( progressend() max modify mode setCommandPanelTaskMode #modify modpanel.setCurrentObject skinMod boneCount = skinOps.getNumberBones skinMod for i = 1 to boneCount do ( boneName = skinOps.GetBoneName skinMod i 1 append boneNames boneName ) progressStart "testing map objects" ) -- Make sure that there are bones in the object if finish == false and skinMod != undefined Then ( if ( ( Skin == ( classof skinMod ) ) and ( 0 == boneCount ) ) then ( messages = ( obj.name + " has skin modifier but no bones defined." ) return false ) ) -- Make sure that all the bones are located inside the node hierarchy if finish == false and skinMod != undefined Then ( local errorFound = false -- Create an array of the bones in the scene that are attached to the object and find the root sceneBone = for obj in objects where (refs.dependencyLoopTest skinMod obj) collect obj rootSkeletonBone = sceneBone[1] while rootSkeletonBone.parent != undefined do ( rootSkeletonBone = rootSkeletonBone.parent ) hierarchyBoneNames = #() CollectBoneNamesInSkeleton rootSkeletonBone hierarchyBoneNames -- Cycle through the skin bones and check them against the hierarchy for i = 1 to boneCount do ( boneName = boneNames[i] boneFound = false for hierarchyBoneName in hierarchyBoneNames do ( if boneName == hierarchyBoneName Then ( boneFound = true ) ) if boneFound == false Then ( if messages != undefined Then ( message = ("" + (obj.name) + " has a bone (" + boneName + ") attached to it that's not in the node hierarchy") append messages message errorFound = true ) ) ) if errorFound == true then ( return false ) ) -- Check to see if the pivot on the object and the root bone match if finish == false and skinMod != undefined and rootSkeletonBone != undefined Then ( rootBonePivot = rootSkeletonBone.pos objectPivot = obj.pos if not rootBonePivot == objectPivot Then ( message = ("" + (obj.name) + " pivot is not located in the same position as the root bone (" + rootSkeletonBone.name + ") pivot") append messages message ) rootBonePivot = rootSkeletonBone.rotation objectPivot = obj.rotation if not rootBonePivot == objectPivot Then ( message = ("" + (obj.name) + " pivot doesn't have the same rotation as the root bone (" + rootSkeletonBone.name + ") pivot") append messages message ) ) true ) ------------------------------------------------------------------------- -- Private (seriously, DO NOT USE) ------------------------------------------------------------------------- -- -- name: __LoadEnvMatchByNameCallback -- desc: dialog voodoo to press the "Match By Name" button on load env dialog. -- fn __LoadEnvMatchByNameCallback = ( WindowHandle = DialogMonitorOPS.GetWindowHandle() theDialogName = UIAccessor.GetWindowText WindowHandle if theDialogName != undefined and matchpattern theDialogName pattern:"*Load Envelopes*" do UIAccessor.PressButtonByName WindowHandle "Match by Name" if theDialogName != undefined and matchpattern theDialogName pattern:"*Load Envelopes*" do UIAccessor.PressButtonByName WindowHandle "OK" true ) ) -- pipeline/util/skinutils.ms