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

594 lines
16 KiB
Plaintext
Executable File

filein (RsConfigGetWildWestDir() + "script/3dsmax/_config_files/wildwest_header.ms")
struct FaceStruct
(
idx,
pos,
normal
)
struct PxmDecalStruct
(
mapChannel = 4,
rayMesh,
getVertsUsingFaceOp,
facesUsingVertOp,
rayMesh = undefined,
processItems = Dictionary(),
fn SelectHiDetailObjects objset =
(
local returnList = #()
local meshAttr = getattrindex "Gta Object" "Mesh"
for object = 1 to objset.count do
(
if (getattrclass objset[object]) == "Gta Collision" then
(
continue
)
if (isRefObj objset[object]) then
(
continue
)
local found = (findstring (tolower objset[object].name) "lod")
if (found != undefined) then
(
continue
)
if (RsGetObjLodDistance idxLodDistance) > 300 then
(
continue
)
--check the attributes to make sure they're not reflection proxies or somesuch
if ((getattrclass objset[object]) == "Gta Object") and (GetAttr objset[object] meshAttr == false) then
(
continue
)
else
(
append returnList objset[object]
)
if (objset[object].children != undefined) then
(
join returnList (SelectHiDetailObjects objset[object].children)
)
)
returnList
),
/***
***/
fn addDebugTiming name start end =
(
if _DEBUG_ then
(
append timings (DataPair id:name time:(end - start))
)
),
/***
***/
fn findBorderVerts obj faces =
(
local faceBitArray = faces as BitArray
local faceVerts = getVertsUsingFaceOp obj faceBitArray
local borderVerts = #()
for vert in faceVerts do
(
vertFaces = facesUsingVertOp obj vert
--if all the faces are not in selected faces then its a border vert
local diff = (faceBitArray * vertFaces).numberSet
if diff < vertFaces.numberSet then
(
append borderVerts vert
)
)
--return
borderVerts
),
/***
***/
fn PxmFadeUnderDecals =
(
--timings = #()
gRsUlog.init "Fade Under Decals"
--check for any meshes without collapsed stacks
for item in selection where (item.modifiers.count > 0) do
(
messageBox (item.name + " has an uncollapsed stack, it will be ignored.\nCollapse it and run the tool again for it to be included")
)
--find all SELECTED meshes with decal shaders applied
local meshes = for item in selection where ((ClassOf item == Editable_mesh) or (classOf item == Editable_poly)) collect item
local decalMeshes = #()
for item in meshes do
(
local meshShaders = RsGetShaderListForObjects item
local decalShaders = #()
for shd in meshShaders where ((matchPattern shd pattern:"*decal_*pxm*.sps") == true) do
(
appendifunique decalMeshes (DataPair mesh:item shaders:meshShaders)
)
)
local Meshes_and_Faces = Dictionary()
--for each mesh go through the faces and look for the mesh beneath it
escapeenable = true
--format "decal mesh count: % \n" decalMeshes.count
counter = 1.0
mainProgress = RSProgressWindow Title:"[Stage 1]: Processing Decal Meshes..." StartStep:1 EndStep:decalMeshes.count
mainProgress.Start()
for dm in decalMeshes do
(
--find any other mesh BBox that intersect this one to ray test faces of
--this is to cut down the number of objects checked.
local localNodesToRayTest = for item in objects where (item != dm.mesh) and
(GetAttrClass item == "Gta Object") and
(intersects item dm.mesh) and
(classOf item == Editable_mesh) or (classof item == Editable_Poly) collect item
--Find the HD meshes that we want to effect
local localHDNodes = SelectHiDetailObjects localNodesToRayTest
local faceFunc = undefined
local faceNormFunc = undefined
local meshType = undefined
if (classOf dm.mesh == Editable_mesh) then
(
meshType = #mesh
faceFunc = meshop.getFaceCenter
faceNormFunc = getFaceNormal
)
else if (classOf dm.mesh == Editable_Poly) then
(
meshType = #poly
faceFunc = polyop.getFaceCenter
faceNormFunc = polyop.getFaceNormal
)
--get the faces with the decal shaders applied
local faceCollection = #()
for shd in dm.shaders where (matchPattern shd pattern:"*decal_*pxm*.sps") do
(
local facelist = #()
RsGetObjFacesUsingShader dm.mesh (getfilenamefile shd) faceList
join faceCollection facelist
)
subProgress = mainProgress.StartSubProgress 0 faceCollection.count SubTitle:"Find occluded meshes.."
subProgress.Start()
for face in faceCollection do
(
--find the closest mesh to the facePos
--local sFace = FaceStruct()
local sFace = DataPair pos:undefined normal:undefined
sFace.normal = faceNormFunc dm.mesh face
sFace.pos = faceFunc dm.mesh face node:dm.mesh
--push the position away from the face a little to get more ray intersection hits
sFace.pos += (0.2 * sFace.normal)
local results = for node in localHDNodes where
(
local bounds = nodeGetBoundingBox node (matrix3 1)
(sFace.pos.x > bounds[1].x) and (sFace.pos.x < bounds[2].x) and
(sFace.pos.y > bounds[1].y) and (sFace.pos.y < bounds[2].y) and
(sFace.pos.z > bounds[1].z) and (sFace.pos.z < bounds[2].z)
)
collect node
--Find the HD meshes that we want to effect
--so we need to get all the faces realted to each node the face intersects and get thme in terms of the nodes
for node in results do
(
if (processItems.hasKey node) then
(
local processFaces = processItems.getValue node
append processFaces sFace
processItems.setValue node processFaces
)
else
(
processItems.addKey node #(sFace)
)
)
subProgress.PostProgressStep()
)
subProgress.End()
mainProgress.PostProgressStep()
)
mainProgress.End()
--Could be multiple hits, say if there is overlapping mesh geo.
--Find the faces in each that are not decal shaders
--Also check that the underlying shader on the face is a pxm type
mainProgress = RSProgressWindow Title:"[Stage 2]: Test occluded meshes ....." StartStep:1 EndStep:(processItems.keys()).count
mainProgress.Start()
rayMesh = RayMeshGridIntersect()
for node in processItems.keys() do
(
rayMesh.Initialize 10
rayMesh.addNode node
rayMesh.BuildGrid()
local triPolyLookup
local nodeType
if (classOf node == Editable_Poly) or (classOf node == PolyMeshObject) then
(
nodeType = #poly
triPolyLookup = RsMakeTriToPolyList node
)
else if (classOf node == Editable_mesh) then
(
nodeType = #mesh
)
faceProgress = mainProgress.StartSubProgress 0 (processItems.getValue node).count SubTitle:(node.name + " Faces..")
faceProgress.Start()
for face in (processItems.getValue node) do
(
--fire a ray
local hits = rayMesh.intersectRay face.pos (-face.normal) false
--local hits = rayMesh.closestFace face.pos
local faceIdx = 0
if (hits > 0) then
(
--get the face hit
local theIndex = rayMesh.getClosestHit() --get the index of the closest hit by the ray
faceIdx = rayMesh.getHitFace theIndex
local matID = 0
if nodeType == #poly then
(
faceIdx = triPolyLookup[faceIdx]
matID = polyop.getfacematid node faceIdx
)
else if nodeType == #mesh then
(
matID = getFaceMatID node faceIdx
)
--matID == 0 check
if (matID != 0) then
(
--get the shader for the face material
--check the material on the face
--if its a decal type we neednt bother with it, continue to the next in the loop
--local faceShader = undefined
local validShader = true
local shaderName = undefined
if (classof node.material) == MultiMaterial then
(
--check its a RageShader
if (classOf node.material.materialList[matID] == Rage_Shader) then
(
shaderName = RstGetShaderName node.material.materialList[matID]
)
)
else if (classof node.material == Rage_Shader) then
(
shaderName = RstGetShaderName node.material
)
else
(
validShader = false
)
if (shaderName != undefined) then
(
if (matchPattern shaderName pattern:"*decal_*pxm*.sps") then
(
validShader = false
)
else if (matchPattern shaderName pattern:"*pxm*.sps") == false then
(
validShader = false
)
)
--save to dictionary
if validShader then
(
local key = node.name
if (Meshes_and_Faces.hasKey key) then
(
local currentVal = Meshes_and_Faces.getValue key
append currentVal faceIdx
Meshes_and_Faces.setValue key currentVal
)
else
(
Meshes_and_Faces.addKey key #{faceIDx}
)
)
)
)
faceProgress.PostProgressStep()
)
faceProgress.End()
rayMesh.free()
mainProgress.PostProgressStep()
)
--reset the dictionary
processItems = Dictionary()
mainProgress.End()
--/////////////////////////
--STAGE 2
--/////////////////////////
--Now process the gathered data and paint out the faces
mainProgress = RSProgressWindow Title:"[Stage 3]: Modify POM Strength..." StartStep:0 EndStep:(Meshes_and_Faces.keys()).count
mainProgress.Start()
for obj in Meshes_and_Faces.keys() do
(
--progressStart (obj + " (2/2) ...")
objNode = (getNodeByName obj)
if objNode == undefined then
(
print "undefined objNode!"
continue
)
local facesOfInterest = Meshes_and_Faces.getValue obj
objNode.selectedFaces = facesOfInterest --as bitArray
local setMapVertFunc = undefined
local setFaceColorOp = undefined
local faceMapFunc = meshop.getMapFace
local getMapFaceOp = RsGetMapFaceFunc objNode
local getFaceVerts = RsGetFaceFunc objNode
local getMapSupportOP = undefined
if (classOf objNode == Editable_mesh) then
(
--getFaceVerts = getface
getMapVertFunc = meshop.getMapVert
setMapVertFunc = meshop.setMapVert
getVertsUsingFaceOp = meshop.getVertsUsingFace
setFaceColorOp = meshop.setFaceColor
--getMapFaceOp = meshop.getMapVertsUsingMapFace
facesUsingVertOp = meshop.getFacesUsingVert
getVertsUsingFaceOp = meshop.getVertsUsingFace
getMapSupportOP = meshop.getMapSupport
)
else if (classof objNode == Editable_Poly) or (classof objNode == PolyMeshObject) then
(
--getFaceVerts = polyop.getFaceVerts
getMapVertFunc = polyop.getMapVert
setMapVertFunc = polyop.setMapVert
getVertsUsingFaceOp = polyop.getVertsUsingFace
setFaceColorOp = polyop.setFaceColor
--getMapFaceOp = polyop.getMapFace
facesUsingVertOp = polyop.getFacesUsingVert
getVertsUsingFaceOp = polyop.getVertsUsingFace
getMapSupportOP = polyop.getMapSupport
)
--NEED TO CHECK MAP SUPPORT FOR CHANNEL 4
if (getMapSupportOP objNode mapChannel) == false then
(
format "Object: % missing % \n" objNode mapChannel
gRsUlog.logWarning ("Object: " + objNode.name +" is missing map channel 4, no fade applied")
mainProgress.PostProgressStep()
continue
)
local mapVertLookup = for n = 1 to objNode.verts.count collect #{}
-- Build geometry-to-UV vertex-lookup list, to allow us to find equivalent indexes quickly...
subProgress = mainProgress.StartSubProgress 0 objNode.faces.count SubTitle:"Build vertex lookup.."
subProgress.Start()
for FaceNum = 1 to objNode.Faces.count do
(
--local faceIdx = objNode.selectedFaces[FaceNum].index
local GeomFaceVerts = getFaceVerts objNode FaceNum
local MapFaceVerts = getMapFaceOp objNode mapChannel FaceNum
for n = 1 to GeomFaceVerts.Count do
(
mapVertLookup[GeomFaceVerts[n]][MapFaceVerts[n]] = True
)
subProgress.PostProgressStep()
)
subProgress.End()
local mapVertsDone = #{}
with undo off
(
--first fill all the faces with red
subProgress = mainProgress.StartSubProgress 0 facesOfInterest.count SubTitle:"Paint Faces.."
subProgress.Start()
for face in facesOfInterest do
(
for fv in (getFaceVerts objNode face) do
(
local mapVerts = mapVertLookup[fv]
for mv in mapVerts where (not mapVertsDone[mv]) do
(
local currentColour = getMapVertFunc objNode mapChannel mv
setMapVertFunc objNode mapChannel mv [1.0, currentColour.y, currentColour.z]
)
)
subProgress.PostProgressStep()
)
subProgress.End()
--then get the border ones and do the fade
local borderVerts = findBorderVerts objNode facesOfInterest
local mapVerts = #()
local vtxCounter = 1
for vtx in borderVerts do
(
join mapVerts mapVertLookup[vtx]
vtxCounter += 1
)
--set to red
subProgress = mainProgress.StartSubProgress 0 mapVerts.count SubTitle:"Paint Border.."
subProgress.Start()
mapVertsDone = #{}
mapVerts = makeUniqueArray mapVerts
for mv in mapVerts where (not mapVertsDone[mv]) do
(
--get the current colour value, we only want to adjust the red
local currentColour = getMapVertFunc objNode mapChannel mv
setMapVertFunc objNode mapChannel mv [1.0, currentColour.y, currentColour.z]
mapVertsDone[mv] = true
subProgress.PostProgressStep()
)
subProgress.End()
update objNode
if (classof objNode == Editable_Poly) or (classof objNode == PolyMeshObject) then
(
polyop.collapseDeadStructs objNode
)
)
mainProgress.PostProgressStep()
)
mainProgress.End()
Meshes_and_Faces = undefined
gc light:true
ForceCompleteRedraw()
gRsUlog.Validate()
true
)
)
--/////////////////////////////////////////
-- UI
--/////////////////////////////////////////
try(destroyDialog PxmFadeUNderDecalsUI)catch()
rollout PxmFadeUNderDecalsUI "Fade Pxm Under Decals" width:250 height:70
(
--/////////////////////////////////////////
-- VARIABLES
--/////////////////////////////////////////
local _DEBUG_ = false
local timings = #()
local mapChannel = 4
local pxmDecalTool = PxmDecalStruct()
--/////////////////////////////////////////
-- CONTROLS
--/////////////////////////////////////////
--dotNetControl rsBannerPanel "Panel" pos:[0,0] height:32 width:(PxmFadeUNderDecalsUI.width)
--local banner = makeRsBanner dn_Panel:rsBannerPanel versionName:"Pumpkin Trump" versionNum:0.14 wiki:"POM Fade Under Decals" filename:(getThisScriptFilename())
timer ctrlState interval:5000 active:false
dotNetControl btnProcessSelected "Button" text:"Process Selected" width:(PxmFadeUNderDecalsUI.width - 10) height:dnStyle.stdButtonHeight offset:[-10, 0]
--/////////////////////////////////////////
-- FUNCTIONS
--/////////////////////////////////////////
--/////////////////////////////////////////
-- EVENTS
--/////////////////////////////////////////
/***
***/
on btnProcessSelected click do
(
dnStyle.setExecuteStyle btnProcessSelected
local success = pxmDecalTool.PxmFadeUnderDecals()
ctrlState.active = true
if success then dnStyle.setSuccessStyle btnProcessSelected else dnStyle.setErrorStyle btnProcessSelected
)
/***
switch state timer
***/
on ctrlState tick do
(
dnStyle.setDormantStyle btnProcessSelected
ctrlState.active = false
)
--/////////////////////////////////////////
--
--/////////////////////////////////////////
on PxmFadeUNderDecalsUI open do
(
--banner.setup()
dnStyle.initButtonStyle btnProcessSelected
)
)
--createDialog PxmFadeUnderDecalsUI