1062 lines
33 KiB
Plaintext
Executable File
1062 lines
33 KiB
Plaintext
Executable File
try (destroyDialog RSpedDmgMapperRoll) catch ()
|
|
callbacks.removeScripts id:#RSdmgHelperMat
|
|
|
|
--gc()
|
|
--resumeediting()
|
|
--popprompt()
|
|
|
|
rollout RSpedDmgMapperRoll "Ped Damage-Mapper [v1.18:Stiff Engine]" width:320 --height:188
|
|
(
|
|
dotNetControl RsBannerPanel "Panel" pos:[0,0] height:32 width:RSpedDmgMapperRoll.width
|
|
local bannerStruct = makeRsBanner dn_Panel:RsBannerPanel wiki:"PedDamageMapper" versionNum:1.18 versionName:"Stiff Engine" filename:(getThisScriptFilename())
|
|
|
|
-- UV-channel used for damage:
|
|
local damChan = 2
|
|
|
|
-- Data for setting up different damage-zones on body:
|
|
struct sectionStruct
|
|
(
|
|
num, Name, Enabled = True, Colour, VOffset = 0.0, CylRot = 0.0, CylHeight = 1.0, FlipSection = False, BoneNames = #(),
|
|
WorkingBoneNames = #(), Verts = #{}, GeomFaces = #{}, MapFaces = #{}, RootPos = [0,0,0], MapDir = [0,0,0]
|
|
)
|
|
local sectionsList =
|
|
#(
|
|
sectionStruct name:"Torso" enabled:True colour:red vOffset:0.8 cylRot:95 CylHeight:0.9 \
|
|
boneNames:#("SKEL_Spine0", "SKEL_Pelvis", "CTRLRIG_Tail_03_NUB", "SKEL_Spine*", "SKEL_?_Clavicle", "*_Suit_*", "SM_?_Pouches*", "SM_LifeSaver*", "SKEL_Tail*", "SPR_Gonads", "SM_CollarStraps*", "CTRLRIG_Spine*", "CTRLRIG_Spine1", "SKEL_Neck_1"),
|
|
sectionStruct name:"Head" enabled:True colour:green VOffset:2.55 CylRot:180 CylHeight:0.37 \
|
|
boneNames:#("SKEL_Neck_1", "CTRLRIG_Head", "RB_Neck_*", "FB_*", "FACIAL_*", "SPR_?_Ear", "SKEL_Head_NUB", "CTRLRIG_Head_NUB", "SKEL_Head"),
|
|
sectionStruct name:"LArm" enabled:True colour:yellow vOffset:5.31 cylRot:-85 CylHeight:0.9 flipSection:True \
|
|
boneNames:#("SKEL_L_UpperArm", "SKEL_L_Clavicle", "RB_L_*ArmRoll", "MH_L_Elbow", "SKEL_L_ForeArm", "SKEL_L_Finger*", "*_L_Hand", "SM_L_Cuff", "CTRLRIG_L_Clavicle_NUB", "SKEL_L_Finger00_NUB", "SKEL_L_Finger2_NUB"),
|
|
sectionStruct name:"RArm" enabled:True colour:blue vOffset:7.31 cylRot:85 CylHeight:0.9 flipSection:True \
|
|
boneNames:#("SKEL_R_UpperArm", "SKEL_R_Clavicle", "RB_R_*ArmRoll", "MH_R_Elbow", "SKEL_R_ForeArm", "SKEL_R_Finger*", "*_R_Hand", "SM_L_Cuff", "CTRLRIG_R_Clavicle_NUB", "SKEL_R_Finger00_NUB", "SKEL_R_Finger2_NUB"),
|
|
sectionStruct name:"LLeg" enabled:True colour:(red + blue) vOffset:9.45 cylRot:-75 flipSection:True \
|
|
boneNames:#("SKEL_L_Thigh", "SM_L_Flipper*", "*_L_Bum*", "MH_L_Knee", "*_L_Calf*", "*_L_Thigh*", "L_PantHem*", "RB_L_ThighRoll", "SKEL_R_Thigh_NUB", "SKEL_L_Calf", "SKEL_L_Toe0", "SKEL_L_Toe0_NUB", "SKEL_L_Foot"),
|
|
sectionStruct name:"RLeg" enabled:True colour:(green + blue) vOffset:11.45 cylRot:75 flipSection:True \
|
|
boneNames:#("SKEL_R_Thigh", "SM_R_Flipper*", "*_R_Bum*", "MH_R_Knee", "*_R_Calf*", "*_R_Thigh*", "R_PantHem*", "RB_R_ThighRoll", "SKEL_R_Thigh_NUB", "SKEL_R_Calf", "SKEL_R_Toe0", "SKEL_R_Toe0_NUB", "SKEL_R_Foot")
|
|
)
|
|
|
|
-- Faces assigned to these bones will be exluded from Damage Mapping:
|
|
local excludeBoneNames = #("*_Eye_*", "*Eyeball*")
|
|
-- Only down-facing faces for these bones will be excluded:
|
|
local underExcludeBoneNames = #("*Foot*", "*Toe*")
|
|
|
|
|
|
group "Show debug texture:"
|
|
(
|
|
checkbutton showHelperGridMatBtn "Helper-grid" width:140 checked:False offset:[0,-3] across:2
|
|
tooltip:"Show selected objects with material showing the mapping on UV-channel 2, with damage-segment zone outlined on the texture"
|
|
checkbutton showHelperAreaMatBtn "Limb-colours" width:140 checked:True offset:[0,-3]
|
|
tooltip:"Show selected objects with material showing the mapping on UV-channel 2, showing limb-mapping colours"
|
|
)
|
|
|
|
button btnDmgMapper "Apply Damage Mapping" width:200 height:30 offset:[1,1]
|
|
tooltip:"Apply damage-mapping modifiers to selected skinned objects, generating mapping-cylinders from bone objects"
|
|
checkButton btnMapAllLimbs "Allow modifiers for full body" width:200 height:16 offset:[1,-4]
|
|
tooltip:"By default, \"Apply\" doesn't add modifiers for parts that don't appear to be used by a mesh.\n\nOverriding this behaviour will slow down the Apply process."
|
|
button btnAddExclusion "Add Damage-Exclude Modifiers" width:200 offset:[1,0]
|
|
tooltip:"Add modifiers to exclude selected faces from damage-mapping"
|
|
button btnCollapseToSkin "Collapse to Skin" width:200 offset:[0,0]
|
|
tooltip:"Collapse selected objects' modifiers below their Skin modifiers"
|
|
|
|
progressBar progressCtrl
|
|
|
|
|
|
-- Generate handy helper-texture materials, for showing how the damage-map has been set up:
|
|
fn genDmgGridHelperMat =
|
|
(
|
|
-- Adds a handy helper-texture material to material-editor slot 7, set up to show how the damage-map has been set up --
|
|
local texMap = bitmaptexture filename:( ::RsConfigGetWildwestDir() + "script/3dsmax/characters/materials/damage_helperA.bmp" )
|
|
texMap.coords.mapChannel = 2
|
|
texMap.coords.U_Offset = texMap.coords.V_Offset = -0.5
|
|
texMap.coords.U_Tiling = texMap.coords.V_Tiling = 0.5
|
|
local dmgmat = standardmaterial name:"DamageHelperGridMat" diffusemap:texMap showInViewport:true
|
|
|
|
dmgmat
|
|
)
|
|
fn genDmgAreaHelperMat =
|
|
(
|
|
local colourCount = sectionsList.count
|
|
|
|
local texMap = bitmaptexture filename:( ::RsConfigGetWildwestDir() + "script/3dsmax/characters/materials/damage_helperB.bmp" )
|
|
texMap.coords.mapChannel = 2
|
|
texMap.coords.U_Offset = (1.0)
|
|
texMap.coords.U_Tiling = (0.5)
|
|
texMap.coords.V_Offset = (colourCount)
|
|
texMap.coords.V_Tiling = (0.5 / colourCount)
|
|
local dmgmat = standardmaterial name:"DamageHelperAreaMat" diffusemap:texMap showInViewport:true
|
|
|
|
dmgmat
|
|
)
|
|
|
|
local dmgGridHelperMat = genDmgGridHelperMat()
|
|
local dmgAreaHelperMat = genDmgAreaHelperMat()
|
|
local matObjs = #()
|
|
local origMats = #()
|
|
|
|
fn changeObjMatting newObjs =
|
|
(
|
|
-- Set to Object subobject-level, as Face mode will mess up materials on apply:
|
|
if (subobjectLevel != 0) do
|
|
(
|
|
subobjectLevel = 0
|
|
)
|
|
|
|
local newMatObjs = #()
|
|
local newOrigMats = #()
|
|
|
|
local reusedObjs = #{}
|
|
reusedObjs.count = matObjs.count
|
|
|
|
-- Build new holding-lists for object-materials:
|
|
local findNum
|
|
for obj in newObjs do
|
|
(
|
|
append newMatObjs obj
|
|
|
|
findNum = findItem matObjs obj
|
|
if (findNum == 0) then
|
|
(
|
|
append newOrigMats obj.material
|
|
)
|
|
else
|
|
(
|
|
append newOrigMats origMats[findNum]
|
|
reusedObjs[findNum] = true
|
|
)
|
|
)
|
|
|
|
-- Restore materials for objects that aren't in newObjs:
|
|
for n = -reusedObjs do
|
|
(
|
|
matObjs[n].material = origMats[n]
|
|
)
|
|
|
|
matObjs = newMatObjs
|
|
origMats = newOrigMats
|
|
|
|
-- Set objects to use helper-material:
|
|
matObjs.material = if showHelperAreaMatBtn.checked then dmgAreaHelperMat else dmgGridHelperMat
|
|
)
|
|
|
|
fn setSelHelperMats =
|
|
(
|
|
changeObjMatting (getCurrentSelection())
|
|
)
|
|
|
|
fn startShowHelperMats =
|
|
(
|
|
setSelHelperMats()
|
|
|
|
-- Update whenever the selection changes:
|
|
callbacks.addScript #selectionSetChanged ("RSpedDmgMapperRoll.setSelHelperMats()") id:#RSdmgHelperMat
|
|
|
|
-- Clear temp materials before saving:
|
|
callbacks.addScript #filePreSaveProcess ("RSpedDmgMapperRoll.changeObjMatting #()") id:#RSdmgHelperMat
|
|
callbacks.addScript #filePostSaveProcess ("RSpedDmgMapperRoll.setSelHelperMats()") id:#RSdmgHelperMat
|
|
)
|
|
|
|
fn clearHelperMats deactivate:True =
|
|
(
|
|
callbacks.removeScripts id:#RSdmgHelperMat
|
|
changeObjMatting #()
|
|
|
|
if deactivate do
|
|
(
|
|
showHelperGridMatBtn.checked = false
|
|
showHelperAreaMatBtn.checked = false
|
|
)
|
|
)
|
|
|
|
on showHelperGridMatBtn changed activated do
|
|
(
|
|
if showHelperAreaMatBtn.checked do
|
|
(
|
|
showHelperAreaMatBtn.checked = False
|
|
clearHelperMats()
|
|
showHelperGridMatBtn.checked = activated
|
|
)
|
|
|
|
if activated then
|
|
(
|
|
startShowHelperMats()
|
|
)
|
|
else
|
|
(
|
|
clearHelperMats()
|
|
)
|
|
)
|
|
|
|
on showHelperAreaMatBtn changed activated do
|
|
(
|
|
if showHelperGridMatBtn.checked do
|
|
(
|
|
showHelperGridMatBtn.checked = False
|
|
clearHelperMats()
|
|
showHelperAreaMatBtn.checked = activated
|
|
)
|
|
|
|
if activated then
|
|
(
|
|
startShowHelperMats()
|
|
)
|
|
else
|
|
(
|
|
clearHelperMats()
|
|
)
|
|
)
|
|
|
|
on RSpedDmgMapperRoll open do
|
|
(
|
|
-- Initialise tech-art banner:
|
|
bannerStruct.setup()
|
|
|
|
if (showHelperGridMatBtn.checked or showHelperAreaMatBtn.checked) do startShowHelperMats()
|
|
)
|
|
|
|
on RSpedDmgMapperRoll close do
|
|
(
|
|
clearHelperMats()
|
|
)
|
|
|
|
-- Get index for object's Skin modifier, if it has one:
|
|
fn getSkinIdx obj =
|
|
(
|
|
local retVal = 0
|
|
|
|
for n = 1 to obj.modifiers.count while (retVal == 0) do
|
|
(
|
|
if (isKindOf obj.modifiers[n] Skin) do
|
|
(
|
|
retVal = n
|
|
)
|
|
)
|
|
|
|
return retVal
|
|
)
|
|
|
|
-- Sets modifier-stack to show under a given modifier:
|
|
fn showUnderMod obj thisMod =
|
|
(
|
|
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
|
|
|
|
-- Get modifier below thisMod (or the object-node, if none)
|
|
local thisModIdx = modPanel.getModifierIndex obj thisMod
|
|
local underModIdx = (thisModIdx + 1)
|
|
local underMod = if (underModIdx > obj.modifiers.count) then obj else obj.modifiers[underModIdx]
|
|
|
|
if (modPanel.getCurrentObject() != underMod) do
|
|
(
|
|
modPanel.setCurrentObject underMod
|
|
)
|
|
|
|
modPanel.setCurrentObject underMod
|
|
if (subobjectlevel != 0) do (subobjectlevel = 0)
|
|
|
|
return underMod
|
|
)
|
|
|
|
-- Gets Dmg "Edit Normals" modifier and selection-clearer modifier (or adds it below Skin and all current Dmg modifiers, if it doesn't already exist)
|
|
-- These modifiers stops normals and selection from being messed up by other modifiers
|
|
-- Returns the modifier/baseObject from below selection-clearer.
|
|
fn showUnderSkin obj =
|
|
(
|
|
local keepNormsName = "Dmg.KeepNormals"
|
|
local safeModName = "Dmg.Sel.Clear"
|
|
|
|
local skinIdx = (getSkinIdx obj)
|
|
|
|
local skinMod = if (skinIdx == 0) then undefined else obj.modifiers[skinIdx]
|
|
local normMod = undefined
|
|
local safeMod = undefined
|
|
local lastDmgMod = undefined
|
|
|
|
-- Find existing safety-modifiers:
|
|
for modIdx = (skinIdx + 1) to obj.modifiers.count while (normMod == undefined) or (safeMod == undefined) do
|
|
(
|
|
local thisMod = obj.modifiers[modIdx]
|
|
|
|
case of
|
|
(
|
|
((normMod == undefined) and (matchPattern thisMod.name pattern:keepNormsName)):
|
|
(
|
|
normMod = thisMod
|
|
)
|
|
((safeMod == undefined) and (matchPattern thisMod.name pattern:safeModName)):
|
|
(
|
|
safeMod = thisMod
|
|
)
|
|
(matchPattern thisMod.name pattern:"Dmg.*"):
|
|
(
|
|
lastDmgMod = thisMod
|
|
)
|
|
)
|
|
)
|
|
|
|
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
|
|
|
|
-- Deal with various modifier-combinations:
|
|
case of
|
|
(
|
|
-- If we have no "Dmg.KeepNormals", add it and a selection-clearer (a new one, if one already exists...)
|
|
(normMod == undefined):
|
|
(
|
|
case of
|
|
(
|
|
-- Add under the last Dmg modifier, if found:
|
|
(lastDmgMod != undefined):(showUnderMod obj lastDmgMod)
|
|
|
|
-- Otherwise, add under Skin, if found: (no reason why that should be though?)
|
|
(skinMod != undefined):(showUnderMod obj skinMod)
|
|
|
|
-- No modifiers?
|
|
(obj.modifiers.count == 0):(modPanel.setCurrentObject obj)
|
|
|
|
-- Go to top of any other modifiers:
|
|
Default:(modPanel.setCurrentObject obj.modifiers[1])
|
|
)
|
|
|
|
-- Add "Edit Normals" modifier first, to ensure that normals are retained:
|
|
local normMod = (Edit_Normals name:(keepNormsName) Display_Length:0.01)
|
|
modPanel.addModToSelection normMod
|
|
|
|
-- Add selection-clearer modifier below the Edit Normals
|
|
showUnderMod obj normMod
|
|
local safeMod = (Poly_Select name:(safeModName))
|
|
modPanel.addModToSelection safeMod
|
|
)
|
|
|
|
-- If we have "Dmg.KeepNormals", but not "Dmg.Sel.Clear", add selection-modifier below normals-modifier
|
|
(safeMod == undefined):
|
|
(
|
|
showUnderMod obj normMod
|
|
|
|
safeMod = (Poly_Select name:(safeModName))
|
|
modPanel.addModToSelection safeMod
|
|
)
|
|
)
|
|
|
|
-- Set stack to below selection-clearer modifier:
|
|
showUnderMod obj safeMod
|
|
|
|
return #(normMod, safeMod)
|
|
)
|
|
|
|
fn findNamedMods obj namePattern:"Dmg.*" =
|
|
(
|
|
local findMods = #()
|
|
|
|
for thisMod in obj.modifiers do
|
|
(
|
|
-- Remove whitespace from name...
|
|
local trimmedName = trimleft thisMod.name
|
|
|
|
if (matchPattern trimmedName pattern:namePattern) do
|
|
(
|
|
append findMods thisMod
|
|
)
|
|
)
|
|
|
|
return findMods
|
|
)
|
|
|
|
fn deleteTheseMods obj delMods keepMods:#() =
|
|
(
|
|
local objMods = obj.modifiers
|
|
|
|
delMods = for thisMod in delMods where (findItem objMods thisMod != 0) and (findItem keepMods thisMod == 0) collect thisMod
|
|
|
|
if (delMods.count != 0) do
|
|
(
|
|
pushPrompt ("Removing old modifiers from: " + obj.name)
|
|
|
|
suspendEditing()
|
|
for thisMod in delMods do
|
|
(
|
|
deleteModifier obj thisMod
|
|
)
|
|
resumeEditing()
|
|
|
|
popPrompt()
|
|
|
|
delMods.count = 0
|
|
)
|
|
)
|
|
|
|
-- Find User-Excluded faces, mapped to [0,0,0]:
|
|
fn getUserExcludedFaces obj testPolyMod: deleteMods:#() =
|
|
(
|
|
local userExcludeFaces = #{}
|
|
|
|
-- Was a test-modifier supplied?
|
|
local addTestMod = (testPolyMod == unsupplied)
|
|
|
|
if addTestMod do
|
|
(
|
|
testPolyMod = Edit_Poly name:"Dmg.Poly.Testing..."
|
|
modPanel.addModToSelection testPolyMod
|
|
)
|
|
|
|
pushPrompt "Finding excluded faces..."
|
|
|
|
local faceCount = (testPolyMod.GetNumFaces())
|
|
userExcludeFaces.count = faceCount
|
|
|
|
if (testPolyMod.GetMapChannelActive damChan) do
|
|
(
|
|
local vertsExcluded = #()
|
|
vertsExcluded.count = testPolyMod.GetNumMapVertices damChan
|
|
|
|
for faceNum = 1 to faceCount do
|
|
(
|
|
-- Only need to bother checking face's first vert...
|
|
local vertNum = (testPolyMod.GetMapFaceVert damChan faceNum 1)
|
|
|
|
local faceExcluded = vertsExcluded[vertNum]
|
|
|
|
-- Face is considered user-excluded if this vert is at [0,0]
|
|
if (faceExcluded == undefined) do
|
|
(
|
|
local vertUv = (testPolyMod.GetMapVertex damChan vertNum)
|
|
vertUv.Z = 0
|
|
|
|
faceExcluded = (distance vertUv [0,0,0] < 0.001)
|
|
vertsExcluded[vertNum] = faceExcluded
|
|
)
|
|
|
|
userExcludeFaces[faceNum] = faceExcluded
|
|
)
|
|
)
|
|
|
|
popPrompt()
|
|
|
|
if addTestMod do
|
|
(
|
|
append deleteMods testPolyMod
|
|
)
|
|
|
|
return userExcludeFaces
|
|
)
|
|
|
|
fn addUserExcludes obj userExcludeFaces deleteMods:#() =
|
|
(
|
|
pushPrompt ("Adding User Excludes: " + obj.name)
|
|
|
|
showUnderSkin obj
|
|
|
|
local polyMod = Edit_Poly name:"Dmg.Setting...UserNone"
|
|
local selectMod = Poly_Select name:"Dmg.Sel.UserNone"
|
|
|
|
-- Add new poly-select modifier:
|
|
modPanel.addModToSelection polyMod
|
|
polyMod.setSelection #face userExcludeFaces
|
|
subobjectlevel = 4
|
|
|
|
modPanel.addModToSelection selectMod
|
|
subobjectlevel = 4
|
|
|
|
append deleteMods polyMod
|
|
|
|
-- Add uv-mapper, which maps faces to [0,0,0]:
|
|
local uvMapper = UVWMap name:(" Dmg.Map.UserNone") maptype:1 length:0.1 width:0.1 height:0.1 utile:0 vtile:0 mapChannel:damChan
|
|
modPanel.addModToSelection uvMapper
|
|
uvMapper.gizmo.scale = [0,0,0]
|
|
|
|
popPrompt()
|
|
|
|
return selectMod
|
|
)
|
|
|
|
fn genDmgMapping obj applyAll:False =
|
|
(
|
|
local skinIdx = getSkinIdx obj
|
|
|
|
if (skinIdx == 0) do return false
|
|
|
|
local skinMod = obj.modifiers[skinIdx]
|
|
|
|
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
|
|
local keepMods = showUnderSkin obj
|
|
|
|
-- These modifiers will be deleted at end of process:
|
|
local deleteMods = #()
|
|
|
|
-- Add modifier used to probe object's faces at this point in stack, and to convert selections from vert to face:
|
|
local testPolyMod = Edit_Poly name:"Dmg.Poly.Testing..."
|
|
modPanel.addModToSelection testPolyMod
|
|
append deleteMods testPolyMod
|
|
|
|
-- Find faces mapped to [0,0,0]
|
|
local userExcludeFaces = getUserExcludedFaces obj testPolyMod:testPolyMod deleteMods:deleteMods
|
|
|
|
-- Remove existing "Dmg" modifiers (apart from temp test-modifier)
|
|
deleteTheseMods obj (findNamedMods obj) keepMods:(#(testPolyMod) + keepMods)
|
|
|
|
-- Get working bone-names list, and tweak it for alternative skeletons:
|
|
for sectionData in sectionsList do (sectionData.WorkingBoneNames = deepCopy sectionData.boneNames)
|
|
sectionsList.enabled = True
|
|
|
|
-- If scene contains clavicle-nubs, it's probably a shark - don't include clavicles as part of torso:
|
|
if (($SKEL_Clavicle_NUB*).count != 0) do
|
|
(
|
|
sectionsList[1].WorkingBoneNames = for boneName in sectionsList[1].WorkingBoneNames where (not matchPattern boneName pattern:"*Clavicle*") collect boneName
|
|
)
|
|
|
|
-- Mapping-cylinders will be placed at the position of the first bones on the bone-lists, and pointed at the last bone on that list:
|
|
local secNum = 0
|
|
for sectionData in sectionsList where sectionData.Enabled do
|
|
(
|
|
sectionData.Num = (secNum += 1)
|
|
sectionData.Verts = #{}
|
|
sectionData.GeomFaces = #{}
|
|
sectionData.MapFaces = #{}
|
|
|
|
local boneNames = sectionData.WorkingBoneNames
|
|
|
|
local boneObjs = for boneName in boneNames collect
|
|
(
|
|
local obj = getNodeByName boneName
|
|
if (isValidNode obj) then obj else dontCollect
|
|
)
|
|
|
|
local rootPos, mapDir
|
|
|
|
if (boneObjs.count > 1) then
|
|
(
|
|
local rootPos = boneObjs[1].pos
|
|
local endPos = boneObjs[boneObjs.count].pos
|
|
|
|
-- Mapping-direction is from first bone to last:
|
|
local mapDir = endPos - rootPos
|
|
)
|
|
else
|
|
(
|
|
sectionData.Enabled = False
|
|
)
|
|
|
|
sectionData.rootPos = rootPos
|
|
sectionData.mapDir = mapDir
|
|
)
|
|
|
|
-- Set view to Skin modifier:
|
|
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
|
|
modPanel.setCurrentObject skinMod
|
|
skinOps.closeWeightTool skinMod
|
|
|
|
local useBones = #{}
|
|
local boneSectionNums = #()
|
|
|
|
local excludeBones = #{}
|
|
local underExcludeBones = #{}
|
|
|
|
-- Match skin-modifier's bone-names against the body-section bone-name lists:
|
|
(
|
|
local skinBoneCount = skinOps.getNumberBones skinMod
|
|
local allMentionedBones = #{}
|
|
|
|
local boneNum, boneSectionNum
|
|
for boneNum = 1 to skinBoneCount do
|
|
(
|
|
boneSectionNum = undefined
|
|
skinBoneName = skinOps.getBoneName skinMod boneNum 1
|
|
|
|
-- Is this on the exclude-bones list?
|
|
for boneName in excludeBoneNames while (boneSectionNum == undefined) do
|
|
(
|
|
if (matchPattern skinBoneName pattern:boneName) do
|
|
(
|
|
excludeBones[boneNum] = True
|
|
allMentionedBones[boneNum] = True
|
|
boneSectionNum = False
|
|
)
|
|
)
|
|
|
|
-- Is this on the underside-exclude bones list?
|
|
if (boneSectionNum != False) do
|
|
(
|
|
for boneName in underExcludeBoneNames while (not underExcludeBones[boneNum]) do
|
|
(
|
|
if (matchPattern skinBoneName pattern:boneName) do
|
|
(
|
|
underExcludeBones[boneNum] = True
|
|
allMentionedBones[boneNum] = True
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Is this on any of the main section-bones lists?
|
|
for sectionNum = 1 to sectionsList.count while (boneSectionNum == undefined) do
|
|
(
|
|
for boneName in sectionsList[sectionNum].WorkingBoneNames while (boneSectionNum == undefined) do
|
|
(
|
|
if (matchPattern skinBoneName pattern:boneName) do
|
|
(
|
|
boneSectionNum = sectionNum
|
|
)
|
|
)
|
|
)
|
|
|
|
if (isKindOf boneSectionNum integer) do
|
|
(
|
|
useBones[boneNum] = True
|
|
allMentionedBones[boneNum] = True
|
|
)
|
|
|
|
append boneSectionNums boneSectionNum
|
|
)
|
|
)
|
|
|
|
local excludeVerts = #{}
|
|
local underExcludeVerts = #{}
|
|
|
|
-- Verts used by bones for multiple sections:
|
|
local overlapVerts = #{}
|
|
|
|
-- Find body-section with highest bone-weight for each vertex
|
|
for skinVertNum = 1 to (skinOps.getNumberVertices skinMod) do
|
|
(
|
|
local vertHighWeight = 0.0
|
|
local vertHighSectionNum = 0
|
|
|
|
local vertBoneCount = (skinOps.getVertexWeightCount skinMod skinVertNum)
|
|
|
|
local vertExcludes = #{}
|
|
local vertUnderExcludes = #{}
|
|
|
|
for vertBoneNum = 1 to vertBoneCount do
|
|
(
|
|
local getBoneID = skinOps.GetVertexWeightBoneID skinMod skinVertNum vertBoneNum
|
|
local getBoneWeight = skinOps.GetVertexWeight skinMod skinVertNum vertBoneNum
|
|
local hasWeight = (getBoneWeight != 0)
|
|
|
|
if hasWeight and useBones[getBoneID] do
|
|
(
|
|
-- Is this vert used by more than one body-segment?
|
|
if (vertHighWeight > 0) and (vertHighSectionNum != 0) and (vertHighSectionNum != boneSectionNums[getBoneID]) do
|
|
(
|
|
overlapVerts[skinVertNum] = true
|
|
)
|
|
|
|
if (getBoneWeight > vertHighWeight) do
|
|
(
|
|
vertHighWeight = getBoneWeight
|
|
|
|
vertHighSectionNum = boneSectionNums[getBoneID]
|
|
)
|
|
)
|
|
|
|
if excludeBones[getBoneID] or (not hasWeight) do
|
|
(
|
|
vertExcludes[vertBoneNum] = True
|
|
)
|
|
if underExcludeBones[getBoneID] or (not hasWeight) do
|
|
(
|
|
vertUnderExcludes[vertBoneNum] = True
|
|
)
|
|
)
|
|
|
|
-- Only flag this vert for exclusion if it it only used by excludey or non-weighted bones:
|
|
if (vertExcludes.numberSet == vertBoneCount) do
|
|
(
|
|
excludeVerts[skinVertNum] = True
|
|
)
|
|
if (vertUnderExcludes.numberSet == vertBoneCount) do
|
|
(
|
|
underExcludeVerts[skinVertNum] = True
|
|
)
|
|
|
|
if (vertHighSectionNum != 0) do sectionsList[vertHighSectionNum].Verts[skinVertNum] = true
|
|
)
|
|
|
|
fn getFaces polyMod verts =
|
|
(
|
|
polyMod.setSelection #vertex #{}
|
|
subobjectlevel = 1
|
|
polyMod.Select #vertex verts
|
|
polyMod.convertSelection #Vertex #Face
|
|
subobjectlevel = 4
|
|
polyMod.GetSelection #Face
|
|
)
|
|
|
|
-- Convert overlap-verts to faces:
|
|
modPanel.setCurrentObject testPolyMod
|
|
testPolyMod.ButtonOp #UnhideAllFace
|
|
testPolyMod.ButtonOp #UnhideAllVertex
|
|
|
|
local overlapFaces = getFaces testPolyMod overlapVerts
|
|
|
|
-- Find faces used by verts:
|
|
for sectionData in sectionsList where sectionData.Enabled and (applyAll or (sectionData.Verts.count != 0)) do
|
|
(
|
|
sectionData.GeomFaces = getFaces testPolyMod sectionData.Verts
|
|
)
|
|
|
|
local excludeFaces = getFaces testPolyMod excludeVerts
|
|
local underExcludeFaces = getFaces testPolyMod underExcludeVerts
|
|
|
|
-- Work out which face-elements in UV-channel 1 belong to which body-section, based on non-overlapping verts:
|
|
(
|
|
local testUvMap = Unwrap_UVW name:("Dmg.UV.Testing...")
|
|
testUvMap.setApplyToWholeObject true
|
|
addModifier obj testUvMap -- Add modifier to top of stack:
|
|
testUvMap.setTVSubObjectMode 3
|
|
append deleteMods testUvMap
|
|
|
|
for sectionData in sectionsList where sectionData.Enabled and (applyAll or (sectionData.geomFaces.numberSet != 0)) do
|
|
(
|
|
-- Select the segment's uncontested faces:
|
|
testUvMap.selectFaces (sectionData.geomFaces - overlapFaces)
|
|
|
|
-- Expand selection to get full mapping-element:
|
|
local lastSelCount
|
|
local curSelCount = 0
|
|
while (lastSelCount != curSelCount) do
|
|
(
|
|
lastSelCount = curSelCount
|
|
|
|
testUvMap.expandSelection()
|
|
|
|
curSelCount = (testUvMap.getSelectedFaces()).numberSet
|
|
)
|
|
|
|
-- Get faces that are on the same mapping-element as faces that are only on this body-segment, are aren't on multiple segments:
|
|
sectionData.mapFaces = testUvMap.getSelectedFaces()
|
|
)
|
|
)
|
|
|
|
-- Delete all modifiers that have been marked for removal so far:
|
|
deleteTheseMods obj deleteMods keepMods:keepMods
|
|
|
|
-- Make list of UV-faces that are connected to multiple body-segments, and shouldn't be used to decide assignments:
|
|
local UVOverlaps = #{}
|
|
for sectionData in sectionsList do
|
|
(
|
|
local secMapFaces = sectionData.mapFaces
|
|
|
|
for otherSecNum = (sectionData.Num + 1) to sectionsList.count do
|
|
(
|
|
UVOverlaps += sectionsList[otherSecNum].mapFaces * secMapFaces
|
|
)
|
|
|
|
sectionData.mapFaces -= UVOverlaps
|
|
)
|
|
|
|
-- Assign remaining segment-UV faces to their assigned segment, and remove them from the others:
|
|
for sectionData in sectionsList do
|
|
(
|
|
sectionData.GeomFaces += sectionData.mapFaces
|
|
|
|
for otherSectionData in sectionsList where (otherSectionData != sectionData) do
|
|
(
|
|
otherSectionData.geomFaces -= sectionData.mapFaces
|
|
)
|
|
)
|
|
|
|
-- Add modifiers to bottom of current stack...
|
|
modPanel.setCurrentObject obj
|
|
|
|
for sectionData in sectionsList where sectionData.Enabled and (applyAll or (sectionData.GeomFaces.numberSet != 0)) do
|
|
(
|
|
local sectionName = sectionData.Name
|
|
local polyMod = Edit_Poly name:("Dmg.New." + sectionName)
|
|
local selectMod = Poly_Select name:("Dmg.Sel." + sectionName)
|
|
local uvMapper = UVWMap name:(" Dmg.Map." + sectionName) maptype:1 length:1 width:1 height:sectionData.CylHeight mapChannel:damChan
|
|
|
|
local flipVal = if sectionData.FlipSection then 1 else 0
|
|
local uvXformer = UVW_Xform name:(" Dmg.uvxfrm." + sectionName) map_channel:damChan u_offset:1 v_offset:sectionData.VOffset u_flip:flipVal v_flip:flipVal
|
|
|
|
-- Add temporary selection-setup modifier:
|
|
(
|
|
modPanel.addModToSelection polyMod
|
|
append deleteMods polyMod
|
|
)
|
|
|
|
-- Unhide faces. Clear subobject-selection, to ensure that uvMapper gizmo is centred on object pivot:
|
|
subobjectlevel = 4
|
|
polyMod.ButtonOp #UnhideAllVertex
|
|
polyMod.ButtonOp #UnhideAllFace
|
|
polyMod.setSelection #face #{}
|
|
|
|
-- Add uvmapper while selection is blank:
|
|
modPanel.addModToSelection uvMapper
|
|
modPanel.addModToSelection uvXformer
|
|
|
|
-- Set Edit Poly modifier's selection:
|
|
modPanel.setCurrentObject polyMod
|
|
polyMod.Select #Face sectionData.geomFaces
|
|
|
|
-- Convert Edit to Select:
|
|
modPanel.addModToSelection selectMod
|
|
subobjectlevel = 4
|
|
|
|
modPanel.setCurrentObject uvXformer
|
|
|
|
(
|
|
-- Add temp object to work out gizmo direction:
|
|
local tempDirObj = box name:(uniqueName "Temp_dmgMapDirDummy") \
|
|
width:0.1 height:0.1 length:0.1 --parent:obj
|
|
|
|
-- Point temp-box in the right direction:
|
|
tempDirObj.dir = sectionData.mapDir
|
|
local getRot = (tempDirObj.rotation) as eulerAngles
|
|
getRot.Z = 0
|
|
tempDirObj.rotation = getRot
|
|
|
|
-- Apply cylinder-rotation if required:
|
|
if sectionData.CylRot != 0 do
|
|
(
|
|
in coordSys local rotate tempDirObj (eulerangles 0 0 sectionData.CylRot)
|
|
)
|
|
|
|
tempDirObj.pos = in coordSys obj sectionData.rootPos
|
|
|
|
-- Pass object's transform to gizmo, delete temp object:
|
|
local newDirXform = tempDirObj.transform * (inverse obj.objectTransform)
|
|
delete tempDirObj
|
|
uvMapper.gizmo.transform = newDirXform
|
|
)
|
|
|
|
-- Clear per-section data-arrays:
|
|
sectionData.Verts = #{}
|
|
sectionData.GeomFaces = #{}
|
|
sectionData.MapFaces = #{}
|
|
sectionData.WorkingBoneNames = #()
|
|
)
|
|
|
|
-- Add modifiers to auto-exclude faces from damage:
|
|
(
|
|
-- Only set down-facing "underExcludeFaces" as excluded:
|
|
for faceNum in underExcludeFaces do
|
|
(
|
|
local faceNorm = (polyop.getFaceNormal obj faceNum)
|
|
|
|
if (faceNorm.Z < -0.5) do
|
|
(
|
|
excludeFaces[faceNum] = True
|
|
)
|
|
)
|
|
|
|
if (excludeFaces.numberSet != 0) do
|
|
(
|
|
local polyMod = Edit_Poly name:"Dmg.Setting...AutoNone"
|
|
local selectMod = Poly_Select name:"Dmg.Sel.AutoNone"
|
|
|
|
-- Add new poly-select modifier:
|
|
(
|
|
modPanel.addModToSelection polyMod
|
|
append deleteMods polyMod
|
|
)
|
|
|
|
polyMod.setSelection #face excludeFaces
|
|
subobjectlevel = 4
|
|
modPanel.addModToSelection selectMod
|
|
subobjectlevel = 4
|
|
|
|
-- Add uv-mapper, which maps faces to [0,0,0]:
|
|
local uvMapper = UVWMap name:(" Dmg.Map.AutoNone") maptype:1 length:0.1 width:0.1 height:0.1 utile:0 vtile:0 mapChannel:damChan
|
|
modPanel.addModToSelection uvMapper
|
|
uvMapper.gizmo.scale = [0,0,0]
|
|
|
|
-- Shift auto-excluded UVs up to [0,1,0] to keep them separate from manually-excluded ones:
|
|
local uvXformer = UVW_Xform name:(" Dmg.uvxfrm.AutoNone") map_channel:damChan v_offset:1
|
|
modPanel.addModToSelection uvXformer
|
|
|
|
-- Delete all modifiers that have been marked for removal so far:
|
|
deleteTheseMods obj deleteMods keepMods:keepMods
|
|
)
|
|
)
|
|
|
|
-- Add manual-exclude modifiers:
|
|
(
|
|
addUserExcludes obj userExcludeFaces deleteMods:deleteMods
|
|
)
|
|
|
|
-- Delete all modifiers that have been marked for removal:
|
|
deleteTheseMods obj deleteMods keepMods:keepMods
|
|
|
|
OK
|
|
)
|
|
|
|
on btnDmgMapper pressed do
|
|
(
|
|
local timeStart = timeStamp()
|
|
|
|
progressCtrl.value = 0
|
|
|
|
local selObjs = getCurrentSelection()
|
|
local useObjs = for obj in selObjs where (isEditPolyMesh obj) collect obj
|
|
|
|
if (useObjs.count != 0) do
|
|
(
|
|
clearHelperMats deactivate:False
|
|
|
|
undo "apply Damage Mapping" on with redraw off
|
|
(
|
|
local currentObj = (modPanel.getCurrentObject())
|
|
local panelMode = (getCommandPanelTaskMode())
|
|
local viewSave = (RSrefFuncs.viewportsToWire())
|
|
|
|
-- Reset radiosity models, to avoid getting the "The Radiosity Solution has been Invalidated" message:
|
|
if (isKindOf SceneRadiosity.Radiosity Radiosity) do
|
|
(
|
|
SceneRadiosity.Radiosity.Reset True False
|
|
)
|
|
|
|
-- Process objects:
|
|
for objNum = 1 to useObjs.count while (not keyboard.escPressed) do
|
|
(
|
|
local obj = useObjs[objNum]
|
|
local promptMsg = stringstream ""
|
|
format "Applying Damage Mapping to:% [%/%]" obj.name objNum useObjs.count to:promptMsg
|
|
pushPrompt (promptMsg as string)
|
|
|
|
genDmgMapping obj applyAll:btnMapAllLimbs.checked
|
|
|
|
popPrompt()
|
|
progressCtrl.value = 100 * (float objNum / useObjs.count)
|
|
)
|
|
|
|
if (getCommandPanelTaskMode() != panelMode) do (setCommandPanelTaskMode panelMode)
|
|
|
|
if (currentObj == undefined) then
|
|
(
|
|
select selObjs
|
|
)
|
|
else
|
|
(
|
|
modPanel.setCurrentObject currentObj
|
|
)
|
|
|
|
RSrefFuncs.restoreViewports viewSave
|
|
)
|
|
|
|
startShowHelperMats()
|
|
)
|
|
|
|
progressCtrl.value = 100
|
|
|
|
format "Time taken to apply Damage Mapping: %s\n" ((timeStamp() - timeStart) / 1000.0)
|
|
)
|
|
|
|
on btnAddExclusion pressed do
|
|
(
|
|
local timeStart = timeStamp()
|
|
|
|
local selObjs = getCurrentSelection()
|
|
local useObjs = for obj in selObjs where (isEditPolyMesh obj) collect obj
|
|
|
|
progressCtrl.value = 0
|
|
|
|
if (useObjs.count != 0) do
|
|
(
|
|
clearHelperMats deactivate:False
|
|
|
|
undo "add damage-exclusion" on
|
|
(
|
|
local selObjs = getCurrentSelection()
|
|
local currentObj = (modPanel.getCurrentObject())
|
|
local panelMode = (getCommandPanelTaskMode())
|
|
local viewSave = (RSrefFuncs.viewportsToWire())
|
|
|
|
-- Reset radiosity models, to avoid getting the "The Radiosity Solution has been Invalidated" message:
|
|
if (isKindOf SceneRadiosity.Radiosity Radiosity) do
|
|
(
|
|
SceneRadiosity.Radiosity.Reset True False
|
|
)
|
|
|
|
-- Set panel to Modify tab:
|
|
if (panelMode != #modify) do (setCommandPanelTaskMode #modify)
|
|
|
|
local selMods = #()
|
|
|
|
for objNum = 1 to useObjs.count while (not keyboard.escPressed) do
|
|
(
|
|
local obj = useObjs[objNum]
|
|
local promptMsg = stringstream ""
|
|
format "Applying Damage Exclusion to:% [%/%]" obj.name objNum useObjs.count to:promptMsg
|
|
pushPrompt (promptMsg as string)
|
|
|
|
-- Show modifier-stack below Skin and its safety-modifiers:
|
|
local keepMods = showUnderSkin obj
|
|
|
|
local deleteMods = #()
|
|
|
|
-- Find faces mapped to [0,0,0]
|
|
local userExcludeFaces = getUserExcludedFaces obj deleteMods:deleteMods
|
|
|
|
-- Delete existing User-exclude modifiers before re-adding:
|
|
join deleteMods (findNamedMods obj namePattern:"Dmg.*.UserNone")
|
|
|
|
-- Add new exclude-modifiers:
|
|
local selMod = addUserExcludes obj userExcludeFaces deleteMods:deleteMods
|
|
append selMods selMod
|
|
|
|
deleteTheseMods obj deleteMods keepMods:keepMods
|
|
|
|
popPrompt()
|
|
progressCtrl.value = 100 * (float objNum / useObjs.count)
|
|
)
|
|
|
|
case of
|
|
(
|
|
(selMods.count == 1):
|
|
(
|
|
modPanel.setCurrentObject selMods[1]
|
|
subobjectlevel = 4
|
|
showEndResult = True
|
|
)
|
|
(currentObj == undefined):
|
|
(
|
|
select selObjs
|
|
)
|
|
Default:
|
|
(
|
|
modPanel.setCurrentObject currentObj
|
|
)
|
|
)
|
|
|
|
RSrefFuncs.restoreViewports viewSave
|
|
)
|
|
|
|
startShowHelperMats()
|
|
)
|
|
|
|
progressCtrl.value = 100
|
|
|
|
format "Time taken to apply Damage Exclusion: %s\n" ((timeStamp() - timeStart) / 1000.0)
|
|
)
|
|
|
|
on btnCollapseToSkin pressed do
|
|
(
|
|
clearHelperMats deactivate:False
|
|
|
|
undo "collapse to Skin" on
|
|
(
|
|
local selObjs = getCurrentSelection()
|
|
local currentObj = modPanel.getCurrentObject()
|
|
|
|
for obj in selObjs do
|
|
(
|
|
local skinIdx = getSkinIdx obj
|
|
|
|
if (skinIdx != 0) do
|
|
(
|
|
modPanel.SetCurrentObject obj.modifiers[skinIdx]
|
|
maxOps.CollapseNodeTo obj (skinIdx + 1) True
|
|
)
|
|
)
|
|
|
|
if (currentObj == undefined) then
|
|
(
|
|
select selObjs
|
|
)
|
|
else
|
|
(
|
|
modPanel.setCurrentObject currentObj
|
|
)
|
|
)
|
|
|
|
startShowHelperMats()
|
|
)
|
|
)
|
|
createDialog RSpedDmgMapperRoll
|