1069 lines
28 KiB
Plaintext
Executable File
1069 lines
28 KiB
Plaintext
Executable File
/***
|
|
POM under IP removal tool
|
|
|
|
load the data maps for IP and combine them to get a 1-bit pixel map
|
|
marking where any IP is
|
|
apply to a world map sized plane and place in the scene
|
|
|
|
now for any container meshes of interest fire a ray from the face centre up into the IP plane
|
|
where we find a hit then the face needs to be markd for change
|
|
|
|
Once all the faces for a particular mesh are collected
|
|
swap the material for a non POM version.
|
|
Look at the surrounding faces. If any of them are POM materials, then we need to fade out that border
|
|
|
|
***/
|
|
|
|
|
|
filein (RsConfigGetWildWestDir() + "script/3dsmax/_config_files/wildwest_header.ms")
|
|
filein (RsConfigGetToolsDir() + "dcc/current/max2012/scripts/pipeline/helpers/terrain/terrain_funcs.ms")
|
|
|
|
|
|
|
|
struct POMUnderIP
|
|
(
|
|
__DEBUG__ = false,
|
|
IPMaskPath = (RsConfigGetWildWestDir() + "assets/image_files/IP_Distribution_FINAL.png"),
|
|
IPMask,
|
|
|
|
worldMapSize = [9150, 12450],
|
|
worldOffset = [4050, 4050],
|
|
worldBottomCorner = [-4050, -4050],
|
|
bitmapOrigin = [4050, 8400],
|
|
|
|
--selectionBounds = #([0, 0, 0], [0, 0, 0]),
|
|
|
|
xPixelPitch,
|
|
yPixelPitch,
|
|
|
|
pxmMapPath = ( RsConfigGetWildWestDir() + "script/3dsMax/maps/PxmMap.ini"),
|
|
|
|
cs_PolyClip = undefined,
|
|
coverageThreshold = 0.95,
|
|
|
|
--maskCache = #{},
|
|
maskCoverageValues= #(),
|
|
|
|
coverageList = #(),
|
|
pixelCoordsList = #(),
|
|
mapChannel = 4,
|
|
|
|
getVertsUsingFaceOp,
|
|
facesUsingVertOp,
|
|
|
|
mainProgress,
|
|
|
|
|
|
/***
|
|
return the mask value from a given pixel coord
|
|
black pixel is masked, white is not
|
|
***/
|
|
fn getMaskValue coords =
|
|
(
|
|
if __DEBUG__ then
|
|
(
|
|
format "pixelmask: % \n" coords
|
|
appendifunique pixelCoordsList coords
|
|
)
|
|
|
|
|
|
masked = false
|
|
|
|
--check coords are within image range
|
|
if (coords.x < 0) or (coords.x > IPMask.width) or (coords.y < 0) or (coords.y > IPMask.height) then
|
|
(
|
|
return undefined
|
|
)
|
|
|
|
local pixelValue = (getPixels IPMask coords 1)[1]
|
|
|
|
if (pixelValue == (color 0 0 0)) then
|
|
(
|
|
masked = true
|
|
appendifunique pixelCoordsList coords
|
|
)
|
|
|
|
--return
|
|
masked
|
|
|
|
),
|
|
|
|
/***
|
|
|
|
***/
|
|
fn getMaskCacheValue coords =
|
|
(
|
|
masked = false
|
|
|
|
--check coords are within image range
|
|
if (coords.x < 0) or (coords.x > IPMask.width) or (coords.y < 0) or (coords.y > IPMask.height) then
|
|
(
|
|
return undefined
|
|
)
|
|
|
|
--build coord lookup into maskCache
|
|
local cacheCoord = (coords.y * IPMask.width) + coords.x
|
|
|
|
--retval
|
|
maskCache[cacheCoord]
|
|
),
|
|
|
|
|
|
/***
|
|
Get the 2d bounds of the face using it vertex positions
|
|
return a datapair min:Point2 max:Point2
|
|
***/
|
|
fn get2DFaceBounds vertPosList =
|
|
(
|
|
local bounds = DataPair min:Point2 max:Point2
|
|
|
|
--initial values
|
|
local minX = worldMapSize.x
|
|
local minY = worldMapSize.y
|
|
local maxX = -worldOffset.x
|
|
local maxY = -worldOffset.y
|
|
|
|
for pos in vertPosList do
|
|
(
|
|
if pos.x < minX then minX = pos.x
|
|
if pos.x > maxX then maxX = pos.x
|
|
if pos.y < minY then minY = pos.y
|
|
if pos.y > maxY then maxY = pos.y
|
|
)
|
|
|
|
bounds.min = [minX, minY]
|
|
bounds.max = [maxX, maxY]
|
|
|
|
if __DEBUG__ do
|
|
(
|
|
box pos:[(minX + (0.5 * (maxX - minX))), (minY + (0.5 * (maxY- minY))), 0] length:(maxY - minY) width:(maxX - minX) height:10
|
|
)
|
|
|
|
--return
|
|
bounds
|
|
),
|
|
|
|
|
|
/***
|
|
|
|
***/
|
|
fn getWPosFromPixel pixelLoc debug:false =
|
|
(
|
|
--local x = worldBottomCorner.x + pixelLoc.x
|
|
local x = pixelLoc.x - bitmapOrigin.x
|
|
--local y = worldBottomCorner.y + pixelLoc.y
|
|
--local y = pixelLoc.y - worldBottomCorner.y
|
|
local y = worldBottomCorner.y + (worldMapSize.y - pixelLoc.y)
|
|
|
|
local transformPt = [x, y]
|
|
|
|
if __DEBUG__ then
|
|
(
|
|
box name:"WPosFromPixel" pos:[transformPt.x, transformPt.y, 0] length:1.0 width:1.0 height:1.0
|
|
)
|
|
|
|
--return
|
|
transformPt
|
|
),
|
|
|
|
|
|
/***
|
|
with a supplied DataPair min: max: object return the corresponding pixel bounds
|
|
***/
|
|
fn get2DPixelBounds faceBounds =
|
|
(
|
|
local bounds = DataPair min:Point2 max:Point2
|
|
|
|
/*
|
|
local minX = (worldOffset.x + faceBounds.min.x + 1.0) as Integer
|
|
local maxX = (worldOffset.x + faceBounds.max.x + 1.0) as Integer
|
|
|
|
local minY = (worldOffset.y + faceBounds.min.y + 1.0) as Integer
|
|
local maxY = (worldOffset.y + faceBounds.max.y + 1.0) as Integer
|
|
*/
|
|
|
|
local minX = (worldOffset.x + faceBounds.min.x) as Integer
|
|
local maxX = (worldOffset.x + faceBounds.max.x) as Integer
|
|
|
|
local minY = (worldMapSize.y - worldOffset.y - faceBounds.min.y) as Integer
|
|
local maxY = (worldMapSize.y - worldOffset.y - faceBounds.max.y) as Integer
|
|
|
|
if maxY < minY then
|
|
(
|
|
swap minY maxY
|
|
)
|
|
|
|
--Dilate the bounds by 1 pixel
|
|
minX -= 1
|
|
minY -= 1
|
|
maxX += 1
|
|
maxY += 1
|
|
|
|
|
|
bounds.min = [minX, minY]
|
|
bounds.max = [maxX, maxY]
|
|
|
|
if __DEBUG__ then
|
|
(
|
|
local boxPos = getWPosFromPixel [(minX + (0.5 * (maxX - minX))), (minY + (0.5 * (maxY- minY))), 0]
|
|
box name:"2DPixelBounds" pos:[boxPos.x, boxPos.y, 0] length:(maxY - minY) width:(maxX - minX) height:10
|
|
)
|
|
|
|
--return
|
|
bounds
|
|
),
|
|
|
|
|
|
/***
|
|
|
|
***/
|
|
fn getPixelFromWPos wPos =
|
|
(
|
|
[((bitmapOrigin.x + wPos.x) as Integer), ((bitmapOrigin.y - wPos.y) as Integer)]
|
|
--[((worldOffset.x + wPos.x) as Integer), ((worldOffset.y + wPos.y) as Integer)]
|
|
),
|
|
|
|
|
|
fn wedge a b =
|
|
(
|
|
(a.x as Double) * (b.y as Double) - (a.y as Double) * (b.x as Double)
|
|
),
|
|
|
|
|
|
fn getPolyArea2D pts =
|
|
(
|
|
local polyArea = 0.0
|
|
|
|
for i=1 to pts.count do
|
|
(
|
|
local nextPoint = if ((i+1) > pts.count) then pts[1] else pts[i+1]
|
|
polyArea += wedge pts[i] nextPoint
|
|
)
|
|
|
|
polyArea / 2.0
|
|
|
|
),
|
|
|
|
/***
|
|
__DEBUG__ function to build spline representation of clipped polygons
|
|
***/
|
|
fn makeDebugSpline centre pts =
|
|
(
|
|
debugSpline = splineShape pos:centre
|
|
addNewSpline debugSpline
|
|
for pt in pts do
|
|
(
|
|
addKnot debugSpline 1 #corner #line pt
|
|
)
|
|
|
|
updateShape debugSpline
|
|
),
|
|
|
|
/***
|
|
Work out which pixels the poly edge will pass through
|
|
***/
|
|
fn walkTheLine pixels start end =
|
|
(
|
|
local edgeLength = distance start end
|
|
|
|
local step = 0.05
|
|
local walk = 0.0
|
|
|
|
while (walk < (edgeLength + step)) do
|
|
(
|
|
local curPos = start + (walk * ((end - start) / (edgeLength + step)))
|
|
|
|
local pixelPos = getPixelFromWPos [curPos.x, curPos.y]
|
|
appendifunique pixels pixelPos
|
|
walk += step
|
|
)
|
|
|
|
|
|
if __DEBUG__ then
|
|
(
|
|
for item in pixels do
|
|
(
|
|
local debugPos = getWPosFromPixel item
|
|
--local debugPos = item
|
|
box name:"pixelPos" pos:[(debugPos.x + 0.5), (debugPos.y + 0.5), 0] length:1 width:1 height:1
|
|
)
|
|
)
|
|
|
|
--return
|
|
pixels
|
|
),
|
|
|
|
|
|
/***
|
|
determine how much of the face is covered by mask pixels using the face vertex positions
|
|
of the pixels it covers get the ones that are set to mask
|
|
get a ratio between the pixels that mask and those that dont and use that as a basis to decide
|
|
if the face should have a shader switch
|
|
***/
|
|
fn getMaskCoverage vPosList =
|
|
(
|
|
|
|
|
|
local coverage = 0.0
|
|
|
|
|
|
local faceBounds = get2DFaceBounds vPosList --debug:__DEBUG__
|
|
|
|
|
|
--we need the 2d face area
|
|
|
|
local faceArea = abs(getPolyArea2D vPosList)
|
|
|
|
if __DEBUG__ do format "faceArea: % \n" faceArea
|
|
|
|
--get pixels within bounds
|
|
|
|
local pixelBounds = get2DPixelBounds faceBounds --debug:__DEBUG__
|
|
|
|
|
|
--for poly edge testing later
|
|
local polyPoints = for vPos in vPosList collect (dotNetObject "System.Windows.Point" vPos.x vPos.y)
|
|
|
|
--find the poly edge pixel intersections
|
|
|
|
local polyEdgeSamples = #()
|
|
for pt=1 to vPosList.count do
|
|
(
|
|
local thisPoint = vPosList[pt]
|
|
local nextPoint = if ((pt+1) > vPosList.count) then vPosList[1] else vPosList[pt+1]
|
|
--local nextPixel = nextPoint
|
|
--format "thisPixel: % nextPixel: % \n" thisPixel nextPixel
|
|
|
|
local samples = #()
|
|
walkTheLine samples thisPoint nextPoint
|
|
join polyEdgeSamples samples
|
|
)
|
|
|
|
polyEdgeSamples = makeUniqueArray polyEdgeSamples
|
|
|
|
--format "polyEdgeSamples: % \n" polyEdgeSamples
|
|
|
|
if __DEBUG__ then
|
|
(
|
|
for samp in polyEdgeSamples do
|
|
(
|
|
local pos = getWPosFromPixel samp
|
|
box name:"edgesample" pos:[(pos.x + 0.5), (pos.y - 0.5), 0] length:1.0 width:1.0 height:1.0
|
|
)
|
|
)
|
|
|
|
|
|
--Process the pixels in the pixelBounds
|
|
|
|
local pixelBoundsX = if ((pixelBounds.max.x - pixelBounds.min.x) - 1) < 1 then 1 else ((pixelBounds.max.x - pixelBounds.min.x) - 1)
|
|
local pixelBoundsY = if ((pixelBounds.max.y - pixelBounds.min.y) - 1) < 1 then 1 else ((pixelBounds.max.y - pixelBounds.min.y) - 1)
|
|
for x=0 to pixelBoundsX do
|
|
(
|
|
for y=0 to pixelBoundsY do
|
|
(
|
|
local thisPixel = [(pixelBounds.min.x + x), (pixelBounds.min.y + y)]
|
|
|
|
--we can save time by only calculating coverage for mask pixels(black)
|
|
--assume the rest of the tri area is unmasked
|
|
--so get the pixel value first
|
|
|
|
local isMaskPixel = getMaskValue thisPixel
|
|
--local isMaskPixel = getMaskCacheValue thisPixel
|
|
--format "isMaskPixel: % pixel: % \n" isMaskPixel thisPixel
|
|
|
|
--Check if this pixel within the pixel bounds is already marked to be dealt with from the earlier polyEdgeSamples list
|
|
local isEdgePixel = if (findItem polyEdgeSamples thisPixel !=0) then true else false
|
|
|
|
--is the pixel outside the poly, partially, or completely inside
|
|
--test the pixel corners against the polygon.
|
|
--test the 4 corners of the pixel against the triangle
|
|
local pixelSamplesCovered = #{}
|
|
|
|
--corner coords built clockwise starting top left
|
|
local pixelSamples = #()
|
|
|
|
--corners
|
|
append pixelSamples [thisPixel.x, thisPixel.y, 0]
|
|
append pixelSamples [(thisPixel.x + 1), thisPixel.y, 0]
|
|
append pixelSamples [(thisPixel.x + 1), (thisPixel.y + 1), 0]
|
|
append pixelSamples [thisPixel.x, (thisPixel.y + 1), 0]
|
|
|
|
--convert sample pixel locations back into world coords
|
|
local samples = for pixel in pixelSamples collect getWPosFromPixel pixel --debug:__DEBUG__
|
|
|
|
/*
|
|
if __DEBUG__ then
|
|
(
|
|
for samp in samples do
|
|
(
|
|
Dummy pos:[(samp.x - 0.5), (samp.y - 0.5), 0] boxsize:[1,1,1]
|
|
)
|
|
)
|
|
*/
|
|
|
|
--Point name:"samplePoint" pos:[samples[5].x, samples[5].y, 0] size:1.0
|
|
--break()
|
|
|
|
|
|
--if its out, its not important
|
|
--if its partial calculate the partial coverage
|
|
--iff its full just add the full pixel are to the coverage
|
|
if isEdgePixel then
|
|
(
|
|
if __DEBUG__ do print "Edge Pixel"
|
|
|
|
local pixelPoints = #( (dotNetObject "System.Windows.Point" samples[1].x samples[1].y),
|
|
(dotNetObject "System.Windows.Point" samples[2].x samples[2].y),
|
|
(dotNetObject "System.Windows.Point" samples[3].x samples[3].y),
|
|
(dotNetObject "System.Windows.Point" samples[4].x samples[4].y)
|
|
)
|
|
|
|
local clippedPoly
|
|
local clippedPolyPts = #()
|
|
|
|
try
|
|
(
|
|
clippedPoly = cs_PolyClip.GetIntersectedPolygon pixelPoints polyPoints
|
|
clippedPolyPts = for pt in clippedPoly collect [(pt.x as Double), (pt.y as Double)]
|
|
|
|
)
|
|
catch
|
|
(
|
|
print "GetIntersectedPolygon Error"
|
|
|
|
print samples
|
|
|
|
if __DEBUG__ then
|
|
(
|
|
for item in samples do
|
|
(
|
|
Point name:"Error" pos:[item.x, item.y, 0] size:1
|
|
)
|
|
)
|
|
)
|
|
|
|
--__DEBUG__ draw the clipped polygons
|
|
if __DEBUG__ and clippedPolyPts.count != 0 then
|
|
(
|
|
local splinePts = #()
|
|
for pt=1 to clippedPolyPts.count do
|
|
(
|
|
local thisPoint = clippedPolyPts[pt]
|
|
local nextPoint = if ((pt+1) > clippedPolyPts.count) then clippedPolyPts[1] else clippedPolyPts[pt+1]
|
|
append splinePts [thisPoint.x, thisPoint.y, 0]
|
|
append splinePts [nextPoint.x, nextPoint.y, 0]
|
|
)
|
|
|
|
makeDebugSpline [0,0,0] splinePts
|
|
)
|
|
|
|
--Collect up the area that the poly gives
|
|
if clippedPolyPts.count != 0 then
|
|
(
|
|
local clippedPolyArea = abs(getPolyArea2D clippedPolyPts)
|
|
if __DEBUG__ do format "clippedPolyArea: % \n" clippedPolyArea
|
|
if isMaskPixel then
|
|
(
|
|
coverage += clippedPolyArea
|
|
)
|
|
)
|
|
)
|
|
else
|
|
(
|
|
--Check which pixels intersect the polygon
|
|
for pt=1 to samples.count do
|
|
(
|
|
local insidePoly = RSGeom_PointInPolygon samples[pt] vPosList
|
|
|
|
if insidePoly then
|
|
(
|
|
pixelSamplesCovered[pt] = true
|
|
)
|
|
)
|
|
|
|
if __DEBUG__ do format "pixelSamplesCovered: %\n" pixelSamplesCovered.numberSet
|
|
|
|
if (pixelSamplesCovered.numberSet == 0) then --this pixel contributes nothing
|
|
(
|
|
if __DEBUG__ do print "Excluded pixel"
|
|
--continue
|
|
)
|
|
else if (pixelSamplesCovered.numberSet == samples.count) and isMaskPixel then --its fully within the poly
|
|
(
|
|
if __DEBUG__ do print "Included pixel"
|
|
coverage += 1.0
|
|
--continue
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
|
|
--using what we've got work out how much of the face area is covered by mask pixels
|
|
--return
|
|
local retval = 0.0
|
|
if (coverage > 0.0) then
|
|
(
|
|
retval = (1.0 / faceArea) * coverage
|
|
)
|
|
|
|
if __DEBUG__ do format "Coverage: % \n" retVal
|
|
--format "Coverage: % \n" retVal
|
|
append coverageList retVal
|
|
--break()
|
|
|
|
|
|
retval
|
|
|
|
),
|
|
|
|
/***
|
|
Check the face against the IPMask to see if it needs a shader swap
|
|
***/
|
|
fn checkFaceIPMask theMesh face =
|
|
(
|
|
--print face
|
|
local faceVerts = polyop.getFaceVerts theMesh face
|
|
|
|
--check the positions of the verts in relation to the IPMask
|
|
local vPosList = for vert in faceVerts collect polyop.getvert theMesh vert
|
|
|
|
--local faceArea = polyop.getFaceArea theMesh face
|
|
|
|
--get the trinagle bounds in pixel space
|
|
--now test each pixel centre against the face
|
|
--for pixels in the face find any that are mask
|
|
local maskCoverage = getMaskCoverage vPosList --faceArea
|
|
|
|
--break()
|
|
--format "maskCoverage: % \n" maskCoverage
|
|
append maskCoverageValues maskCoverage
|
|
local needsSwap = if (maskCoverage > coverageThreshold) then true else false
|
|
|
|
--return
|
|
needsSwap
|
|
|
|
),
|
|
|
|
/***
|
|
Check for t-vert faces
|
|
***/
|
|
fn findTVertFaces obj =
|
|
(
|
|
local tVertFaces = #{}
|
|
|
|
for face in obj.faces where ((polyop.getFaceDeg $ face.index) > 3) do
|
|
(
|
|
|
|
--get the verts for the face
|
|
local faceVerts = polyop.getFaceVerts $ face.index
|
|
for vtx = 1 to faceVerts.count do
|
|
(
|
|
local thisVtx = faceVerts[vtx]
|
|
local midVtx = if (vtx+1) > faceVerts.count then faceVerts[1] else faceVerts[(vtx+1)]
|
|
local nextVtx = if (vtx+2) > faceVerts.count then faceVerts[2] else faceVerts[(vtx+2)]
|
|
|
|
--compare the dot between the two edges
|
|
local thisVertPos = polyop.getvert obj thisVtx
|
|
local midVertPos = polyop.getvert obj midVtx
|
|
local nextVertPos = polyop.getvert obj nextVtx
|
|
|
|
local diff = abs(dot (normalize(midVertPos - thisVertPos)) (normalize(nextVertPos - thisVertPos)))
|
|
--print diff
|
|
if diff > 0.99 then append tVertFaces face.index
|
|
)
|
|
|
|
)
|
|
|
|
obj.selectedFaces = tVertFaces
|
|
|
|
--return
|
|
(tVertFaces.numberSet > 0)
|
|
),
|
|
|
|
/***
|
|
|
|
***/
|
|
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 getMapVertLookup objNode =
|
|
(
|
|
local getMapFaceOp = RsGetMapFaceFunc objNode
|
|
local getFaceVerts = RsGetFaceFunc objNode
|
|
|
|
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
|
|
|
|
--NEED TO CHECK MAP SUPPORT FOR CHANNEL 4
|
|
local MapFaceVerts = getMapFaceOp objNode mapChannel FaceNum
|
|
|
|
|
|
for n = 1 to GeomFaceVerts.Count do
|
|
(
|
|
mapVertLookup[GeomFaceVerts[n]][MapFaceVerts[n]] = True
|
|
)
|
|
subProgress.PostProgressStep()
|
|
)
|
|
subProgress.End()
|
|
|
|
--return
|
|
mapVertLookup
|
|
),
|
|
|
|
|
|
|
|
/***
|
|
|
|
***/
|
|
fn paintOutPOMFaces objNode facesOfInterest =
|
|
(
|
|
getMapVertFunc = undefined
|
|
setMapVertFunc = undefined
|
|
getVertsUsingFaceOp = undefined
|
|
facesUsingVertOp = undefined
|
|
setFaceColorOp = undefined
|
|
faceMapFunc = meshop.getMapFace
|
|
if (classOf objNode == Editable_mesh) then
|
|
(
|
|
getMapVertFunc = meshop.getMapVert
|
|
setMapVertFunc = meshop.setMapVert
|
|
getVertsUsingFaceOp = meshop.getVertsUsingFace
|
|
setFaceColorOp = meshop.setFaceColor
|
|
facesUsingVertOp = meshop.getFacesUsingVert
|
|
--meshop.getMapSupport
|
|
)
|
|
else if (classof objNode == Editable_Poly) or (classOf objNode == PolyMeshObject) then
|
|
(
|
|
getMapVertFunc = polyop.getMapVert
|
|
setMapVertFunc = polyop.setMapVert
|
|
getVertsUsingFaceOp = polyop.getVertsUsingFace
|
|
setFaceColorOp = polyop.setFaceColor
|
|
faceMapFunc = polyop.getMapFace
|
|
facesUsingVertOp = polyop.getFacesUsingVert
|
|
--polyop.getMapSupport
|
|
)
|
|
|
|
|
|
/***
|
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! preserve green and flush red
|
|
***/
|
|
--flush with black
|
|
|
|
--polyop.setFaceColor objNode 4 (objNode.faces as BitArray) (color 0 0 0 )
|
|
|
|
--flip the face selection
|
|
--facesOfInterest = (objNode.faces as BitArray) - facesOfInterest
|
|
|
|
--paint those verts white on channel 4
|
|
--subProgress = mainProgress.StartSubProgress 0 meshPOMFaceList.count SubTitle:"Painting POM Faces.."
|
|
--progressStart ("Stage (2 of 2) " + objNode.name + "...")
|
|
|
|
with undo off
|
|
(
|
|
--first fast fill all the faces red channel only
|
|
subProgress = mainProgress.StartSubProgress 0 facesOfInterest.count SubTitle:"Painting POM Faces.."
|
|
subProgress.Start()
|
|
for face in facesOfInterest do
|
|
(
|
|
local faceVerts = faceMapFunc objNode mapChannel face
|
|
|
|
for mv in faceVerts do
|
|
(
|
|
local currentColour = getMapVertFunc objNode mapChannel mv
|
|
setMapVertFunc objNode mapChannel mv [1.0, currentColour.y, currentColour.z]
|
|
)
|
|
subProgress.PostProgressStep()
|
|
)
|
|
subProgress.End()
|
|
--setFaceColorOp objNode mapChannel facesOfInterest (color 255 255 255)
|
|
|
|
--then get the border ones and do the fade
|
|
local borderVerts = findBorderVerts objNode facesOfInterest
|
|
|
|
local mapVertLookup = getMapVertLookup objNode
|
|
|
|
local mapVerts = #()
|
|
|
|
for vtx in borderVerts do
|
|
(
|
|
join mapVerts mapVertLookup[vtx]
|
|
)
|
|
|
|
/*
|
|
subProgress = mainProgress.StartSubProgress 0 borderVerts.count SubTitle:"Collecting POM Faces Borders.."
|
|
subProgress.Start()
|
|
for vtx in borderVerts do
|
|
(
|
|
-- Get all faces using the vert as array
|
|
local faceArray = facesUsingVertOp objMesh #{vtx}
|
|
|
|
--iterate over the faces
|
|
for f in faceArray do
|
|
(
|
|
--Get geometry face
|
|
local geoFace = getFace objMesh f
|
|
local mapFace = meshop.getMapFace objMesh mapChannel f
|
|
|
|
-- Find order inside the mesh face - vertex (component) order will be the same in both
|
|
mapVert = case of
|
|
(
|
|
(geoFace.x == vtx): mapFace.x
|
|
(geoFace.y == vtx): mapFace.y
|
|
(geoFace.z == vtx): mapFace.z
|
|
)
|
|
|
|
--collect the mapVert values found
|
|
append mapVerts mapVert
|
|
)
|
|
|
|
subProgress.PostProgressStep()
|
|
)
|
|
subProgress.End()
|
|
*/
|
|
|
|
|
|
--set to white
|
|
/***
|
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ONLY SET RED CHANNEL
|
|
***/
|
|
subProgress = mainProgress.StartSubProgress 0 borderVerts.count SubTitle:"Painting POM Faces Borders.."
|
|
subProgress.Start()
|
|
local mapVertsDone = #{}
|
|
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]
|
|
|
|
mapVertsDone[mv] = true
|
|
|
|
subProgress.PostProgressStep()
|
|
)
|
|
subProgress.End()
|
|
|
|
update objNode
|
|
polyop.collapseDeadStructs objNode
|
|
)
|
|
|
|
--progressEnd()
|
|
),
|
|
|
|
/***
|
|
|
|
***/
|
|
fn smoothSelection obj faceSel =
|
|
(
|
|
obj.selectedFaces = faceSel
|
|
subobjectLevel = 4
|
|
obj.growSelection()
|
|
obj.shrinkSelection()
|
|
subobjectLevel = 0
|
|
),
|
|
|
|
/***
|
|
EXPERIMENTAL
|
|
***/
|
|
fn buildMaskCache =
|
|
(
|
|
--clear the cache
|
|
maskCache = #{}
|
|
|
|
--use selection bounds to get a pixelbounds
|
|
local pixelBounds = get2DPixelBounds (DataPair min:[selectionBounds[1].x, selectionBounds[1].y] max:[selectionBounds[2].x, selectionBounds[2].y])
|
|
|
|
--scan the ipmask and build a mask lookup using the pixelbounds
|
|
local x = pixelBounds.min.x
|
|
|
|
for y=0 to ((pixelBounds.max.y - pixelBounds.min.y) - 1) do --each line
|
|
(
|
|
local pxLine = getPixels IPMask [x, (pixelBounds.min.y + y)] (pixelBounds.max.x - pixelBounds.min.x)
|
|
for px = 1 to pxLine.count do
|
|
(
|
|
if (pxLine[px] == (color 0 0 0)) then
|
|
(
|
|
local offset = (pixelBounds.min.y * IPMask.width) + (y * IPMask.width) + px
|
|
maskCache[offset] = true
|
|
)
|
|
)
|
|
)
|
|
),
|
|
|
|
|
|
/***
|
|
Process the terrain shaded faces of the selected meshes
|
|
***/
|
|
fn processSelectedMeshes =
|
|
(
|
|
escapeEnable = true
|
|
|
|
--local st = timeStamp()
|
|
|
|
local meshes = for item in selection where (getAttrClass item == "Gta Object") collect item
|
|
|
|
|
|
mainProgress = RSProgressWindow Title:"Processing Meshes..." StartStep:1 EndStep:meshes.count
|
|
mainProgress.Start()
|
|
for item in meshes do
|
|
(
|
|
--find the POM shadeed faces
|
|
local meshShaders = RsGetShaderListForObjects item
|
|
local hasPOMShaders = false
|
|
for shd in meshShaders where ((matchPattern shd pattern:"*_pxm*.sps") == true) do
|
|
(
|
|
hasPOMShaders = true
|
|
)
|
|
|
|
if not hasPOMShaders then
|
|
(
|
|
print "no POM shaders on this object"
|
|
continue
|
|
)
|
|
|
|
--might be worth making a cache of the face vert position to speed up lookup time
|
|
--as there will be multiple hits on a vert per face
|
|
-- in fact cache the vert, position and IPMask hit
|
|
--only worth doing if there is a pxm shader to mess with, so check that first
|
|
|
|
local swapFaces = #{}
|
|
local meshPOMFaceList = #()
|
|
for shd in meshShaders where ((matchPattern shd pattern:"*_pxm*.sps") == true) do
|
|
(
|
|
local shdPOMFaceList = #()
|
|
RsGetObjFacesUsingShader item (getfilenamefile shd) shdPOMFaceList
|
|
join meshPOMFaceList shdPOMFaceList
|
|
)
|
|
|
|
--gather positions from POMFaces to check against the IPMask
|
|
item.selectedFaces = meshPOMFaceList
|
|
|
|
subProgress = mainProgress.StartSubProgress 0 meshPOMFaceList.count SubTitle:"Checking POM Faces.."
|
|
subProgress.Start()
|
|
|
|
for face in meshPOMFaceList do
|
|
(
|
|
local needsSwap = checkFaceIPMask item face
|
|
if needsSwap then
|
|
(
|
|
--format "face: % needsSwap\n" face
|
|
swapFaces[face] = true
|
|
)
|
|
|
|
subProgress.PostProgressStep()
|
|
windows.processPostedMessages()
|
|
)
|
|
subProgress.End()
|
|
|
|
|
|
item.selectedFaces = swapFaces
|
|
local avg = 0.0
|
|
for mc in maskCoverageValues do avg += mc
|
|
avg /= maskCoverageValues.count
|
|
|
|
format "maskCoverageValues: min: % max: % avg: % \n" (amin maskCoverageValues) (amax maskCoverageValues) avg
|
|
|
|
--smooth out the selection
|
|
smoothSelection item swapFaces
|
|
|
|
--paint the height weight blend
|
|
paintOutPOMFaces item swapFaces
|
|
|
|
swapFaces
|
|
|
|
mainProgress.PostProgressStep()
|
|
)
|
|
|
|
mainProgress.End()
|
|
|
|
true
|
|
|
|
),
|
|
|
|
|
|
/***
|
|
Process container meshes
|
|
|
|
iterate through all the meshes
|
|
|
|
identify POM shaded faces
|
|
|
|
use POM face position (multisample) to look for IP coverage
|
|
|
|
...
|
|
***/
|
|
fn processContainerMeshes =
|
|
(
|
|
escapeEnable = true
|
|
|
|
if (classOf selection[1] != Container) then
|
|
(
|
|
messageBox "Please Select a Container" title:"Selection Error"
|
|
return false
|
|
)
|
|
|
|
local meshes = for item in selection[1].children where (getAttrClass item == "Gta Object") collect item
|
|
|
|
clearSelection()
|
|
select meshes
|
|
|
|
processSelectedMeshes()
|
|
|
|
clearSelection()
|
|
|
|
true
|
|
),
|
|
|
|
|
|
/***
|
|
Initialise the mask and pixelPitch
|
|
***/
|
|
fn init =
|
|
(
|
|
if (doesFileExist IPMaskPath) then
|
|
(
|
|
IPMask = openBitmap IPMaskPath
|
|
xPixelPitch = worldMapSize.x / IPMask.width
|
|
yPixelPitch = worldMapSize.y / IPMask.height
|
|
)
|
|
else
|
|
(
|
|
format "Cant load IPMask: % \n" IPMaskPath
|
|
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
--/////////////////////////////////////////
|
|
-- UI
|
|
--/////////////////////////////////////////
|
|
--try(destroyDialog POMUnderIP_UI)catch()
|
|
rollout POMUnderIP_UI "Remove POM under IP" width:400 height:100
|
|
(
|
|
--/////////////////////////////////////////
|
|
-- VARIABLES
|
|
--/////////////////////////////////////////
|
|
local sPOMIP = POMUnderIP()
|
|
|
|
--/////////////////////////////////////////
|
|
-- CONTROLS
|
|
--/////////////////////////////////////////
|
|
--dotNetControl rsBannerPanel "Panel" pos:[0,0] height:32 width:(POMUnderIP_UI.width)
|
|
--local banner = makeRsBanner dn_Panel:rsBannerPanel wiki:"CHANGEME" versionNum:0.1 versionName:"Mumbo Jumbo" filename:(getThisScriptFilename())
|
|
timer ctrlState interval:5000 active:false
|
|
dotNetControl btnProcess "Button" text:"Process Container" width:(POMUnderIP_UI.width - 10) height:dnStyle.stdButtonHeight offset:[-10, 0]
|
|
dotNetControl btnSelMesh "Button" text:"Process Selected Meshes" width:(POMUnderIP_UI.width - 10) height:dnStyle.stdButtonHeight offset:[-10, 0]
|
|
|
|
|
|
--/////////////////////////////////////////
|
|
-- FUNCTIONS
|
|
--/////////////////////////////////////////
|
|
|
|
|
|
--/////////////////////////////////////////
|
|
-- EVENTS
|
|
--/////////////////////////////////////////
|
|
|
|
|
|
--/////////////////////////////////////////
|
|
--
|
|
--/////////////////////////////////////////
|
|
on btnSelMesh click do
|
|
(
|
|
local keepGoing = true
|
|
for item in selection where (isKindOf item EditablePolyMesh) do
|
|
(
|
|
if (querybox ("This object " + item.name + " has faces with T-Verts, this could give incorrect results,\n do you want to proceed?")) then
|
|
(
|
|
continue
|
|
)
|
|
else
|
|
(
|
|
return false
|
|
)
|
|
)
|
|
|
|
dnStyle.setExecuteStyle btnSelMesh
|
|
local success = sPOMIP.processSelectedMeshes()
|
|
ctrlState.active = true
|
|
if success then dnStyle.setSuccessStyle btnSelMesh else dnStyle.setErrorStyle btnSelMesh
|
|
)
|
|
|
|
--/////////////////////////////////////////
|
|
--
|
|
--/////////////////////////////////////////
|
|
on btnProcess click do
|
|
(
|
|
dnStyle.setExecuteStyle btnProcess
|
|
local success = sPOMIP.processContainerMeshes()
|
|
ctrlState.active = true
|
|
if success then dnStyle.setSuccessStyle btnProcess else dnStyle.setErrorStyle btnProcess
|
|
)
|
|
|
|
--/////////////////////////////////////////
|
|
--
|
|
--/////////////////////////////////////////
|
|
on ctrlState tick do
|
|
(
|
|
dnStyle.setDormantStyle btnProcess
|
|
dnStyle.setDormantStyle btnSelMesh
|
|
ctrlState.active = false
|
|
)
|
|
|
|
--/////////////////////////////////////////
|
|
--
|
|
--/////////////////////////////////////////
|
|
on POMUnderIP_UI open do
|
|
(
|
|
--banner.setup()
|
|
|
|
--make sure the mask is synced
|
|
--gRsPerforce.sync #(sPOMIP.IPMaskPath)
|
|
sPOMIP.init()
|
|
|
|
--build c# assembly
|
|
CSharp.CompileToMemory #(RsConfigGetWildWestDir() + "script/3dsMax/Maps/Materials/polyclip.cs")
|
|
sPOMIP.cs_PolyClip = dotNetClass "Sutherland.SutherlandHodgman"
|
|
|
|
--set styles
|
|
dnStyle.initButtonStyle btnProcess
|
|
dnStyle.initButtonStyle btnSelMesh
|
|
)
|
|
|
|
|
|
)
|
|
--createDialog POMUnderIP_UI
|
|
|
|
|
|
|