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