----------------------------------------------------------------------------------- -- 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