354 lines
9.7 KiB
Plaintext
Executable File
354 lines
9.7 KiB
Plaintext
Executable File
--
|
|
-- File:: pipeline/util/skinutils.ms
|
|
-- Description:: Skin modifier utilities
|
|
--
|
|
-- Author:: David Muir <david.muir@rockstarnorth.com>
|
|
-- 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
|