Files
gtav-src/tools_ng/wildwest/script/3dsMax/Characters/Materials/pedDirtBaker.ms
T
2025-09-29 00:52:08 +02:00

529 lines
15 KiB
Plaintext
Executable File

try (destroyDialog RsPedDirtBakerRoll) catch ()
RSTA_LoadCommonFunction #("fn_RSTA_vertexColours.ms")
rollout RsPedDirtBakerRoll "Ped Dirt Baker [v1.01:FurtiveJar]" width:260
(
-- Version-phrase generated via: (filein (RsConfigGetWildWestDir() + "script/3dsmax/_config_files/wildwest_header.ms"); print (RsRandomPhrase count:100))
local dirtChan = 14
local rndSeed
local noiseContrast = 2.0
local noiseScale = 10
local dirtyRange = (dataPair min:4 max:32)
local dirtierRange = (dataPair min:15 max:110)
local dirtiestRange = (dataPair min:30 max:127)
local dirtierBonesString = "Calf"
local dirtiestBonesString = "Foot, Toe"
-- VertexPaint colour/size options:
local paintClrs = #((color 5 5 5 ), (color 127 127 127 ))
local brushSizes = #(0.02, 0.15)
-- CONTROLS --
dotNetControl RsBannerPanel "Panel" pos:[0,0] height:32 width:RsPedDirtBakerRoll.width
local bannerStruct = makeRsBanner dn_Panel:RsBannerPanel wiki:"PedDirtBaker" filename:(getThisScriptFilename())
button btnDoDirty "Apply Dirt to Selection" tooltip:"Apply Dirt data to vertex-colour channel 14" width:((RsPedDirtBakerRoll.width / 2) - 6) height:30 align:#left offset:[-8, 0] across:3
button btnShowDirty "Show Dirt" tooltip:"Show Dirt Channel (channel 14) on selection" width:((RsPedDirtBakerRoll.width / 4) - 4) height:30 align:#right offset:[25, 0]
button btnUnshowDirty "Unshow" tooltip:"Revert view to show materials" width:((RsPedDirtBakerRoll.width / 4) - 6) height:30 align:#right offset:[8, 0]
group "Dirt VertexPaint"
(
button btnAddVpaint "Add VertexPaint" tooltip:"Apply Dirt VertexPaint modifier to selection (set to channel 14)" height:30 align:#left
label lblPaintClr "Paint:" align:#left pos:(btnAddVpaint.pos + [110,1])
radiobuttons rdoPaintClr "" default:0 labels:#("Dark", "Light") align:#right offset:[0,-18]
label lblBrushSize "Size:" align:#left pos:(lblPaintClr.pos + [5,16])
radiobuttons rdoBrushSize "" default:0 labels:#("Fine", "Large") align:#right offset:[0,-18]
timer tmrVpaint interval:500 active:True
)
group "Dirtiness:"
(
spinner spnMinDirty "Default: Min:" range:[0, 127, 0] type:#integer fieldWidth:34 align:#right offset:[0,-3] across:2
spinner spnMaxDirty "Max:" range:[0, 127, 0] type:#integer fieldWidth:34 align:#right offset:[0,-3]
slider sldMinDirty "" range:[0, 127, 0] type:#integer ticks:0 align:#right offset:[10,-4] across:2
slider sldMaxDirty "" range:[0, 127, 0] type:#integer ticks:0 align:#left offset:[10,-4]
editText txtDirtier "Dirtier Bones:" fieldWidth:160 align:#right
spinner spnMinDirtier "Dirtier: Min:" range:[0, 127, 0] type:#integer fieldWidth:34 align:#right offset:[0,0] across:2
spinner spnMaxDirtier "Max:" range:[0, 127, 0] type:#integer fieldWidth:34 align:#right offset:[0,0]
slider sldMinDirtier "" range:[0, 127, 0] type:#integer ticks:0 align:#right offset:[10,-4] across:2
slider sldMaxDirtier "" range:[0, 127, 0] type:#integer ticks:0 align:#left offset:[10,-4]
editText txtDirtiest "Dirtiest Bones:" fieldWidth:160 align:#right
spinner spnMinDirtiest "Dirtiest: Min:" range:[0, 127, 0] type:#integer fieldWidth:34 align:#right offset:[0,0] across:2
spinner spnMaxDirtiest "Max:" range:[0, 127, 0] type:#integer fieldWidth:34 align:#right offset:[0,0]
slider sldMinDirtiest "" range:[0, 127, 0] type:#integer ticks:0 align:#right offset:[10,-4] across:2
slider sldMaxDirtiest "" range:[0, 127, 0] type:#integer ticks:0 align:#left offset:[10,-4]
)
group "Dirt Noise:"
(
spinner spnScale "Scale:" range:[0,100,0] scale:0.1 fieldWidth:50 align:#center offset:[0,-3] across:2
spinner spnContrast "Contrast:" range:[0,100,0] scale:0.1 fieldWidth:50 align:#center offset:[0,-3]
spinner spnSeed "Seed:" range:[0, 999999, 0] type:#integer fieldWidth:50 align:#center offset:[1,0] across:2
button btnRandom "RND" width:30 height:16 align:#right pos:(spnSeed.pos + [14,0])
)
-- FUNCTIONS --
fn updateCtrls =
(
txtDirtier.text = dirtierBonesString
txtDirtiest.text = dirtiestBonesString
spnMinDirty.value = dirtyRange.min
spnMaxDirty.value = dirtyRange.max
spnMinDirtier.value = dirtierRange.min
spnMaxDirtier.value = dirtierRange.max
spnMinDirtiest.value = dirtiestRange.min
spnMaxDirtiest.value = dirtiestRange.max
sldMinDirty.value = dirtyRange.min
sldMaxDirty.value = dirtyRange.max
sldMinDirtier.value = dirtierRange.min
sldMaxDirtier.value = dirtierRange.max
sldMinDirtiest.value = dirtiestRange.min
sldMaxDirtiest.value = dirtiestRange.max
spnScale.value = noiseScale
spnContrast.value = noiseContrast
spnSeed.value = rndSeed
)
fn showDirty unshow:False =
(
local objs = getCurrentSelection()
if unshow then
(
objs.showVertexColors = False
displayColor.shaded = #material
)
else
(
objs.showVertexColors = True
objs.vertexColorType = #map_channel
objs.vertexColorMapChannel = dirtChan
displayColor.shaded = #object
)
completeRedraw()
)
fn pedDirtBake objs:(getCurrentSelection()) =
(
-- Filter out invalid objs:
objs = for obj in objs where (isEditPolyMesh obj) collect obj
if (selection.count == 0) do return False
-- Generate noise-offset, based on random seed:
seed rndSeed
local noiseShift = random [-100,-100,-100] [100,100,100]
-- Get search-patterns for dirtier/dirtiest bone-names:
local dirtierBoneNames = for item in (filterString dirtierBonesString ", ") collect ("*" + item + "*")
local dirtiestBoneNames = for item in (filterString dirtiestBonesString ", ") collect ("*" + item + "*")
-- Process each object:
for obj in objs do
(
local skinMod = undefined
-- Get Skin modifier:
for thisMod in obj.modifiers while (skinMod == undefined) do
(
if (isKindOf thisMod skin) do
(
skinMod = thisMod
)
)
local vertDirts
if (skinMod == undefined) then
(
-- If object has no Skin modifier, use default dirt-ranges for all verts:
vertDirts = for n = 1 to obj.numVerts collect dirtyRange
)
else
(
-- Get verts used by specified bones:
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
modPanel.setCurrentObject skinMod
local skinBoneCount = skinOps.getNumberBones skinMod
local dirtierBoneNums = #{}
local dirtiestBoneNums = #{}
-- Find Dirtier/Dirtiest boneNums:
for boneNum = 1 to skinBoneCount do
(
local skinBoneName = skinOps.getBoneName skinMod boneNum 1
local keepLooking = True
for item in dirtierBoneNames while keepLooking do
(
if (matchPattern skinBoneName pattern:item) do
(
dirtierBoneNums[boneNum] = True
keepLooking = True
)
)
for item in dirtiestBoneNames while keepLooking do
(
if (matchPattern skinBoneName pattern:item) do
(
dirtiestBoneNums[boneNum] = True
keepLooking = True
)
)
)
vertDirts = for skinVertNum = 1 to (skinOps.getNumberVertices skinMod) collect
(
local dirtierWeights = #()
local dirtiestWeights = #()
for vertBoneNum = 1 to (skinOps.getVertexWeightCount skinMod skinVertNum) do
(
getBoneID = skinOps.GetVertexWeightBoneID skinMod skinVertNum vertBoneNum
local weightList = case of
(
(dirtierBoneNums[getBoneID]):dirtierWeights
(dirtiestBoneNums[getBoneID]):dirtiestWeights
Default:undefined
)
if (weightList != undefined) do
(
append weightList (skinOps.GetVertexWeight skinMod skinVertNum vertBoneNum)
)
)
local hasDirtier = (dirtierWeights.count != 0)
local hasDirtiest = (dirtiestWeights.count != 0)
local dirtyVals = case of
(
-- Find mix between dirtier/dirtiest weightings:
(hasDirtier and hasDirtiest):
(
local dirtierSum = 0.0
local dirtiestSum = 0.0
for val in dirtierWeights do
(
dirtierSum += val
)
for val in dirtiestWeights do
(
dirtiestSum += val
)
local totalSum = (dirtierSum + dirtiestSum)
local dirtierMult = (dirtierSum / totalSum)
local dirtiestMult = (dirtiestSum / totalSum)
local minVal = ((dirtierMult * dirtierRange.min) + (dirtiestMult * dirtiestRange.min))
local maxVal = ((dirtierMult * dirtierRange.max) + (dirtiestMult * dirtiestRange.max))
dataPair min:minVal max:maxVal
)
(hasDirtiest):(dirtiestRange)
(hasDirtier):
(
local dirtierMult = 0.0
for val in dirtierWeights do
(
dirtierMult += val
)
local dirtyMult = (1.0 - dirtierMult)
local minVal = ((dirtierMult * dirtierRange.min) + (dirtyMult * dirtyRange.min))
local maxVal = ((dirtierMult * dirtierRange.max) + (dirtyMult * dirtyRange.max))
dataPair min:minVal max:maxVal
)
(Default):(dirtyRange)
)
--format "%, % : %\n" hasDirtiest hasDirtier (dirtyVals as string)
dirtyVals
)
)
local tempObj = RsVertClr_makeTempObj obj copyChan:dirtChan
local objOp = RsMeshPolyOp tempObj
for vertNum = 1 to vertDirts.count do
(
-- Get vert's dirtiness:
local dirtRange = vertDirts[vertNum]
-- Get vertex position:
local vertPos = objOp.getVert tempObj vertNum
-- Generate dirtiness-multiplier for this position based on 3d noise values:
local noiseVal = noise3 (noiseScale * (vertPos + noiseShift))
-- Scale and clamp value:
noiseVal *= noiseContrast
noiseVal += 1
noiseVal /= 2
case of
(
(noiseVal < 0):(noiseVal = 0)
(noiseVal > 1):(noiseVal = 1)
)
-- Use noise-value to pick grey from vert's dirt-range:
local greyVal = dirtRange.min + (noiseVal * (dirtRange.max - dirtRange.min))
objOp.SetVertColor tempObj dirtChan vertNum [greyVal,greyVal,greyVal]
)
RsVertClr_applyTempData tempObj obj dirtChan modName:"DirtColours"
)
select objs
return OK
)
-- VERTEXPAINT MODIFIER BITS --
-- Get currently-active VertexPaint modifier, if shown:
local vertMod
local vertModEdit = VertexPaintTool()
fn getVertMod =
(
local getMod = modPanel.getCurrentObject()
if (getMod != vertMod) do
(
if (isKindOf getMod vertexPaint) then
(
vertMod = getMod
)
else
(
vertMod = undefined
)
)
return vertMod
)
-- Update controls to match current VertexPaint settings:
fn updatePaintCtrls =
(
getVertMod()
if (vertMod == undefined) then
(
rdoPaintClr.enabled = False
rdoBrushSize.enabled = False
rdoPaintClr.state = 0
rdoBrushSize.state = 0
)
else
(
rdoPaintClr.enabled = True
rdoBrushSize.enabled = True
-- Push buttons if they match relevant value:
local setState = 0
local toolPaintClr = (vertModEdit.paintColor as point3)
for ctrlNum = 1 to paintClrs.count while (setState == 0) do
(
if (distance toolPaintClr (paintClrs[ctrlNum] as point3) < 0.001) do
(
setState = ctrlNum
)
)
if (rdoPaintClr.state != setState) do (rdoPaintClr.state = setState)
local setState = 0
local toolBrushSize = vertModEdit.brushSize
for ctrlNum = 1 to brushSizes.count while (setState == 0) do
(
if (abs (toolBrushSize - brushSizes[ctrlNum]) < 0.001) do
(
setState = ctrlNum
)
)
if (rdoBrushSize.state != setState) do (rdoBrushSize.state = setState)
)
)
-- Use timer to keep control-states up-to-date with Vertex Paint Toolkit:
on tmrVpaint tick do
(
updatePaintCtrls()
)
-- Change VertexPaint colour:
fn pressPaintClrBtn num =
(
vertModEdit.paintColor = paintClrs[num]
vertModEdit.keepToolboxOpen = vertModEdit.keepToolboxOpen
updatePaintCtrls()
)
on rdoPaintClr changed state do (pressPaintClrBtn state)
-- Change VertexPaint brush-size:
fn pressBrushSizeBtn num =
(
vertModEdit.brushSize = brushSizes[num]
vertModEdit.keepToolboxOpen = vertModEdit.keepToolboxOpen
updatePaintCtrls()
)
on rdoBrushSize changed state do (pressBrushSizeBtn state)
-- Add new VertexPaint modifier to selection:
on btnAddVpaint pressed do
(
local objs = for obj in getCurrentSelection() where (isEditPolyMesh obj) collect obj
if (objs.count == 0) do
(
return False
)
showDirty()
local paintMod = vertexPaint mapChannel:dirtChan name:"Dirt VertexPaint"
addModifier objs paintMod
showDirty()
select objs
)
-- /VERTEXPAINT MODIFIER BITS --
on txtDirtier entered newText do
(
dirtierBonesString = newText
)
on txtDirtiest entered newText do
(
dirtiestBonesString = newText
)
fn minDirtyChanged val =
(
dirtyRange.min = val
if (val > dirtyRange.max) do (dirtyRange.max = val)
updateCtrls()
)
on spnMinDirty changed val do (minDirtyChanged val)
on sldMinDirty changed val do (minDirtyChanged val)
fn maxDirtyChanged val =
(
dirtyRange.max = val
if (val < dirtyRange.min) do (dirtyRange.min = val)
updateCtrls()
)
on spnMaxDirty changed val do (maxDirtyChanged val)
on sldMaxDirty changed val do (maxDirtyChanged val)
fn minDirtierChanged val =
(
dirtierRange.min = val
if (val > dirtierRange.max) do (dirtierRange.max = val)
updateCtrls()
)
on spnMinDirtier changed val do (minDirtierChanged val)
on sldMinDirtier changed val do (minDirtierChanged val)
fn maxDirtierChanged val =
(
dirtierRange.max = val
if (val < dirtierRange.min) do (dirtierRange.min = val)
updateCtrls()
)
on spnMaxDirtier changed val do (maxDirtierChanged val)
on sldMaxDirtier changed val do (maxDirtierChanged val)
fn minDirtiestChanged val =
(
dirtiestRange.min = val
if (val > dirtiestRange.max) do (dirtiestRange.max = val)
updateCtrls()
)
on spnMinDirtiest changed val do (minDirtiestChanged val)
on sldMinDirtiest changed val do (minDirtiestChanged val)
fn maxDirtiestChanged val =
(
dirtiestRange.max = val
if (val < dirtiestRange.min) do (dirtiestRange.min = val)
updateCtrls()
)
on spnMaxDirtiest changed val do (maxDirtiestChanged val)
on sldMaxDirtiest changed val do (maxDirtiestChanged val)
on btnRandom pressed do
(
rndSeed = (random spnSeed.range[1] spnSeed.range[2])
updateCtrls()
)
on spnSeed changed val do
(
rndSeed = val
)
on spnScale changed val do
(
noiseScale = val
)
on spnContrast changed val do
(
noiseContrast = val
)
on btnDoDirty pressed do
(
local objs = for obj in getCurrentSelection() where (isEditPolyMesh obj) collect obj
if (objs.count == 0) do
(
messageBox "No geometry is selected" title:"Error: Invalid Selection"
return False
)
showDirty()
pedDirtBake objs:objs
showDirty()
)
on btnShowDirty pressed do
(
showDirty()
)
on btnUnshowDirty pressed do
(
showDirty unshow:True
)
on RsPedDirtBakerRoll open do
(
-- Initialise tech-art banner:
bannerStruct.setup()
rndSeed = (random spnSeed.range[1] spnSeed.range[2])
updateCtrls()
)
)
createDialog RsPedDirtBakerRoll