Files
2025-09-29 00:52:08 +02:00

260 lines
9.7 KiB
Plaintext
Executable File

-----------------------------------------------------------------------------------
-- Parallax Occlusion Map Curvature Masker tool
--
-- Masks out parts of selected meshes that have highly
-- convex/concave normals, as curved areas don't POM well.
--
-- James O'Hare (R* Leeds) 01/2014
-----------------------------------------------------------------------------------
try(destroyDialog RsPOMCurvatureMaskerUI)catch()
--/////////////////////////////////////////
-- UI
--/////////////////////////////////////////
rollout RsPOMCurvatureMaskerUI "POM Curvature Masker" width:300 height:150
(
--/////////////////////////////////////////
-- CONTROLS
--/////////////////////////////////////////
--dotNetControl rsBannerPanel "Panel" pos:[0,0] height:32 width:RsPOMCurvatureMaskerUI.width
--local banner = makeRsBanner dn_Panel:rsBannerPanel wiki:"POM Curvature Masker" versionNum:1.04 versionName:"Nostalgic Curtain" filename:(getThisScriptFilename())
timer ctrlState interval:5000 active:false
group "POM Curvature Masker"
(
dotnetControl btnMaskPOM "Button" text:"Apply POM Mask to Selection" height:dnStyle.stdButtonHeight --offset:[-10, 0]
)
group "Options"
(
checkbox preserveExisting "Preserve Existing Mask" align:#left offset:[0, -2]
checkbox maskMaterialSeams "Mask POM/Non-POM Material Joins" align:#left offset:[0, -2] checked:True \
Tooltip:"Add POM-scale mask (on red channel) to fade parallax effect at seams between POM/non-POM materials"
)
-- If preserveOriginal == true, then set the blend mode to additive. Otherwise overwrite entirely.
-- If maskJoins == true, then set all verts at a POM - nonPOM material boundary to white.
fn maskPOM preserveOriginal:false maskJoins:false =
(
if (tension == undefined) do
(
messageBox "Please install the 'Tension' modifier plugin to run this tool.\n\nSee wiki for details (click [i] icon on banner)" title:"Error: Plugin Missing" beep:false
return False
)
-- Collect selected Editable Poly/Mesh or PolyMeshObject objects:
local myobjs = for obj in selection where (isEditPolyMesh obj) collect obj
if (myobjs.count == 0) do
(
messageBox "No mesh objects are selected" title:"Error: Invalid Selection" beep:false
return False
)
-- Do any objects have modifiers?
local HasModifiers = False
for obj in myobjs while (not HasModifiers) do
(
HasModifiers = (obj.Modifiers.Count != 0)
)
if HasModifiers and (not queryBox "A selected object has modifiers applied.\n\nThis tool will collapse its modifier-stack, do you want to continue?" title:"Warning: Object has modifiers") do
(
return False
)
-- Channel 4 is what we're using for the POM masker. This might vary depending on the material (sometimes channel 3?) but I think it's mostly 4.
local vchannel = 4
-- Cache refs to oft-used functions.
local refGetMapVert = polyop.getMapVert
local refSetMapVert = polyop.setMapVert
local refGetFacesUsingVert = polyop.getFacesUsingVert
local refGetFaceMatID = polyop.getFaceMatID
local refGetFaceVerts = polyOp.getFaceVerts
local refGetMapFace = polyOp.getMapFace
local convscale = 1.0 -- Convex scale.
local convexp = 1.0 -- Convex exponent.
local concscale = 1.0 -- Concave scale. Plugin calls it delta.
local concexp = 1.0 -- Concave exponent.
local blurStrength = 1.0 -- Blur strength.
local blurAmount = 0 -- Blur amount - integer value.
undo "Apply POM Curvature Mask" on
(
for myobj in myobjs do
(
if (not isKindOf myobj Editable_Poly) then
(
-- The modifier works with Editable Mesh, although it's slower as it internally converts it to Editable Poly anyway.
convertToPoly(myobj)
)
else
(
collapseStack myobj
)
-- Apply Tension modifier to generate vertcolours, and collapse to bake data into model:
local tensionmodifier = tension mappingChannel:vchannel mappingChannelConvex:vchannel doExpansionGreen:on doCompressRed:on doConvex:on convexIsComparitive:off doNormalisedConvex:off deltaScale:concscale deltaExponent:concexp convexgreenscale:convscale concaveExponent:convexp convblurValue:blurStrength convexblur:blurAmount additiveComp:preserveOriginal compositeOp:1
addModifier myobj tensionmodifier
collapseStack myobj
-- Tidy away dead structs that might confuse vert/face-counts:
polyOp.collapseDeadStructs myobj
local NumMapVerts = (polyop.getNumMapVerts myobj vchannel)
local FaceCount = (polyOp.getNumFaces myobj)
-- Get materials on this shader then loop through verts and mask any that are on joins between POM and non-POM materials.
-- Checks material type for each material ID, and stores an array of true/false values for each material ID to be checked later on.
local SeamMapVerts = #{}
SeamMapVerts.Count = NumMapVerts
if maskJoins do
(
-- Get list of materials used on object, and bitarrays of the faces they're used on:
local ObjMats = #()
local MatFaces = #()
RsGetMaterialsOnObjFaces myobj materials:ObjMats faceLists:MatFaces
-- No need to mask joins if object only uses one material...
if (ObjMats.Count > 1) do
(
-- Make blank bitarrays for recording POM/non-POM faces:
local PomFaces = #{}
local NonPomFaces = #{}
PomFaces.Count = FaceCount
NonPomFaces.Count = FaceCount
-- Find out which materials have a POM shader, and take note of their faces:
for n = 1 to ObjMats.Count do
(
local ThisMat = ObjMats[n]
local ThisMatFaces = MatFaces[n]
local isPOM = False
-- Is this a POM shader?
if (isKindOf ThisMat Rage_Shader) do
(
local ShaderName = RstGetShaderName ThisMat
isPOM = (matchPattern ShaderName pattern:"*pxm*")
)
-- Add material face-list to relevant bitarray:
if isPOM then
(
PomFaces += ThisMatFaces
)
else
(
NonPomFaces += ThisMatFaces
)
)
-- If both lists are non-zero, then we have a mixture of POM/non-POM faces:
if (PomFaces.NumberSet != 0) and (NonPomFaces.NumberSet != 0) do
(
local VertCount = polyop.getNumVerts myobj
-- This will list bitarrays of map-verts corresponding to each geometry-vert:
local mapVertLookup = for n = 1 to VertCount collect #{}
-- Build geometry-to-UV vertex-lookup list, to allow us to find equivalent indexes quickly...
for FaceNum = 1 to FaceCount do
(
local GeomFaceVerts = refGetFaceVerts myobj FaceNum
local MapFaceVerts = refGetMapFace myobj vchannel FaceNum
for n = 1 to GeomFaceVerts.Count do
(
mapVertLookup[GeomFaceVerts[n]][MapFaceVerts[n]] = True
)
)
-- Find verts on border of POM/non-POM faces...
for VertNum = 1 to VertCount do
(
local FacesUsingVert = refGetFacesUsingVert myobj VertNum
-- Find union of POM-faces and this vert's faces:
local VertPomFaces = (FacesUsingVert * PomFaces)
local VertPomFaceCount = VertPomFaces.NumberSet
-- Does this vert's faces include a mixture of POM/non-POM materials?
if (VertPomFaceCount != 0) and (VertPomFaceCount != FacesUsingVert.NumberSet) do
(
-- If this is a POM-seam geometry-vert, add its map-vert(s) to the to-mask list:
SeamMapVerts += mapVertLookup[VertNum]
)
)
)
)
)
for MapVert = 1 to NumMapVerts do
(
-- Get baked-in vertex-colour, and start blank vertcolour for making reformatted version:
local VertClr = (refGetMapVert myobj vchannel MapVert)
local EditVertClr = [0,0,0]
-- The settings of the curvature map mean it's applied in 0 - 1 range from concave - convex in the blue channel, with 0.5 being "flat".
-- So I'll expand it out to -1 - 1 range and get the absolute value to force it into 0 - 1 range with 0 being "flat" and 1 being "non-flat".
-- Then create a new point3 and fill G with the curvature value and assign that back to the vertex.
EditVertClr.y = amax (amin (abs ((VertClr.z * 2) - 1)) 1) 0 -- GREEN: Curvature Mask
-- RED: POM Scale, used to fade POM effect between POM/non-POM materials (0-1:On-Off)
if maskJoins and SeamMapVerts[MapVert] do
(
EditVertClr.x = 1.0
)
-- Don't bother applying colour if it isn't changing:
if (EditVertClr != VertClr) do
(
-- Set vertex to edited colour:
refSetMapVert myobj vchannel MapVert EditVertClr
)
)
)
)
return true
)
--/////////////////////////////////////////
-- EVENTS
--/////////////////////////////////////////
on btnMaskPOM click do
(
dnStyle.setExecuteStyle btnMaskPOM
local success = maskPOM preserveOriginal:preserveExisting.state maskJoins:maskMaterialSeams.state
ctrlState.active = true
if success then dnStyle.setSuccessStyle btnMaskPOM else dnStyle.setErrorStyle btnMaskPOM
)
--/////////////////////////////////////////
-- Switch control state on timer trigger
--/////////////////////////////////////////
on ctrlState tick do
(
dnStyle.setDormantStyle btnMaskPOM
ctrlState.active = false
)
--/////////////////////////////////////////
-- INITIALISATION
--/////////////////////////////////////////
on RsPOMCurvatureMaskerUI open do
(
--banner.setup()
dnStyle.initButtonStyle btnMaskPOM
windows.processPostedMessages()
)
)
--createDialog RsPOMCurvatureMaskerUI