339 lines
12 KiB
Plaintext
Executable File
339 lines
12 KiB
Plaintext
Executable File
/*###########
|
|
|
|
Vertex Density Visualizer
|
|
|
|
Description:
|
|
--Used for analyzing a mesh to see if areas are too vertex dense
|
|
|
|
History:
|
|
--Kevin Ala-Pantti - January, 2014
|
|
--Created
|
|
--Kevin Ala-Pantti - April 2014
|
|
--Adding auto calculations for segment counts, vert limits
|
|
|
|
Wiki:
|
|
--https://devstar.rockstargames.com/wiki/index.php/Vert_Density_Visualizer
|
|
|
|
###########*/
|
|
|
|
try destroyDialog(RSVertDensityVisualizer)catch()
|
|
|
|
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RsTa_createVisualizerPlane
|
|
Creates a plane over top of the selected mesh that matches the min/max X and Y points
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------*/
|
|
fn RsTa_createVisualizerPlane obj wSpinner hSpinner autoCalcSegs:true=
|
|
(
|
|
-- Get min and max values of the object to analyze
|
|
local minX = obj.min.x
|
|
local maxX = obj.max.x
|
|
local minY = obj.min.y
|
|
local maxY = obj.max.y
|
|
local maxZ = obj.max.z
|
|
|
|
-- Get distance between min and max to use for length / height of plane
|
|
local xDist = minX - maxX
|
|
if ( xDist < 0 ) then xDist = ( xDist * ( -1 ) )
|
|
|
|
local yDist = minY - maxY
|
|
if ( yDist < 0 ) then yDist = ( yDist * ( -1 ) )
|
|
|
|
-- Make the plane
|
|
local visualPlane = plane()
|
|
visualPlane.name = "Density Visualizer"
|
|
visualPlane.width = xDist
|
|
visualPlane.length = yDist
|
|
if ( autoCalcSegs ) then
|
|
(
|
|
hSpinner.value = ( ( yDist / 10 ) as integer )
|
|
visualPlane.lengthsegs = hSpinner.value
|
|
wSpinner.value = ( ( xDist / 10 ) as integer )
|
|
visualPlane.widthsegs = wSpinner.value
|
|
)
|
|
else
|
|
(
|
|
visualPlane.lengthsegs = hSpinner.value
|
|
visualPlane.widthsegs = wSpinner.value
|
|
)
|
|
visualPlane.pivot.x = visualPlane.min.x
|
|
visualPlane.pivot.y = visualPlane.min.y
|
|
visualPlane.pos = [minX, minY, maxZ]
|
|
visualPlane.showVertexColors = true
|
|
|
|
convertToPoly visualPlane
|
|
|
|
visualPlane
|
|
)
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RsTa_faceRayTest
|
|
Shoots a ray from the center of each face of the visual plane down towards the mesh being analyzed
|
|
If the ray doesn't hit, the plane face is deleted
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------*/
|
|
fn RsTa_faceRayTest obj visualPlane=
|
|
(
|
|
-- Array of faces to delete
|
|
local delFaces = #()
|
|
|
|
local faceNum = polyOp.getNumFaces visualPlane
|
|
for i = 1 to faceNum do
|
|
(
|
|
-- Get pos of face center, create a ray from there, shoot it at the analyzed mesh
|
|
local faceLoc = polyOp.getFaceCenter visualPlane i
|
|
local testRay = ray faceLoc [0,0,-1]
|
|
local nodeMaxZ = obj.max.z
|
|
testRay.pos.z = nodeMaxZ + 0.0001 * abs nodeMaxZ
|
|
local rayHit = intersectRay obj testRay
|
|
|
|
-- If the ray doesn't hit anything, add that face to the array to delete
|
|
if rayHit == undefined then append delFaces i
|
|
)
|
|
|
|
if delFaces.count > 0 then polyOp.deleteFaces visualPlane delFaces delIsoVerts:true
|
|
)
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RsTa_buildMinMaxArrays
|
|
Gets the min/max X and Y values for each face of the visualizer plane
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------*/
|
|
fn RsTa_buildMinMaxArrays visualPlane &faceMinMaxArray =
|
|
(
|
|
local numFaces = polyOp.getNumFaces visualPlane
|
|
for face = 1 to numFaces do
|
|
(
|
|
-- Get verts for each face; compare those verts to find min/max values for that face
|
|
local vertBits = polyOp.getVertsUsingFace visualPlane face
|
|
|
|
vertBits = ( vertBits as array )
|
|
|
|
local vert = polyOp.getVert visualPlane vertBits[1]
|
|
local minMax = #( vert.x,vert.x,vert.y,vert.y )
|
|
|
|
faceMinMaxArray[face] = minMax
|
|
|
|
for j = 2 to 4 do
|
|
(
|
|
vert = polyOp.getVert visualPlane vertBits[j]
|
|
|
|
if vert.x < faceMinMaxArray[face][1] then faceMinMaxArray[face][1] = vert.x
|
|
if vert.x > faceMinMaxArray[face][2] then faceMinMaxArray[face][2] = vert.x
|
|
if vert.y < faceMinMaxArray[face][3] then faceMinMaxArray[face][3] = vert.y
|
|
if vert.y > faceMinMaxArray[face][4] then faceMinMaxArray[face][4] = vert.y
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RsTa_findMatches
|
|
Compares each vert of the analyzed mesh to each min/max value of the visualizer plane until a match is found
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------*/
|
|
fn RsTa_findMatches obj faceMinMaxArray &foundCountArray=
|
|
(
|
|
local numVerts = polyOp.getNumVerts obj
|
|
for i = 1 to numVerts do
|
|
(
|
|
local vert = polyOp.getVert obj i
|
|
|
|
-- Loop through the faces of the visualizer plane until a match is found
|
|
local found = false
|
|
local arrIndex = 1
|
|
while found == false do
|
|
(
|
|
if ( vert.x > faceMinMaxArray[arrIndex][1] and vert.x < faceMinMaxArray[arrIndex][2] and vert.y > faceMinMaxArray[arrIndex][3] and vert.y < faceMinMaxArray[arrIndex][4] ) then
|
|
(
|
|
found = true
|
|
if ( foundCountArray[arrIndex] == undefined ) then foundCountArray[arrIndex] = 1
|
|
else foundCountArray[arrIndex] += 1
|
|
)
|
|
arrIndex += 1
|
|
if ( arrIndex > faceMinMaxArray.count ) then
|
|
(
|
|
found = true
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RsTa_colourFaces
|
|
Colours the faces of the visualizer plane based on how many verts were found inisde its bounds
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------*/
|
|
fn RsTa_colourFaces visualPlane foundCountArray threshold=
|
|
(
|
|
local numFaces = polyOp.getNumFaces visualPlane
|
|
for i = 1 to numFaces do
|
|
(
|
|
local clrPercent = 255
|
|
-- Blue colour to use if the face had no verts inside
|
|
local faceClr = color 0 0 125
|
|
|
|
-- Calculate the colour to use for the face; colour is a percentage of allowed verts found in that face's bounds
|
|
if ( foundCountArray[i] != undefined ) then
|
|
(
|
|
clrPercent = (((foundCountArray[i] as float) / threshold) * 255.0)
|
|
faceClr = color clrPercent clrPercent clrPercent
|
|
)
|
|
|
|
-- Use red if the face had more than the allowed # verts
|
|
if ( clrPercent > 255 ) then faceClr = red
|
|
|
|
polyop.setFaceSelection visualPlane i
|
|
|
|
visualPlane.SetFaceColor faceClr 0
|
|
)
|
|
)
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RsTa_calcSegmentArea
|
|
Calculates the area of a segment to use in formula for getting vert limit
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------*/
|
|
fn RsTa_calcSegmentArea visualPlane =
|
|
(
|
|
local faceVerts = polyop.getVertsUsingFace visualPlane 1
|
|
faceVerts = ( faceVerts as array )
|
|
|
|
local v1 = polyOp.getVert visualPlane faceVerts[1]
|
|
local v2 = polyOp.getVert visualPlane faceVerts[2]
|
|
local v3 = polyOp.getVert visualPlane faceVerts[3]
|
|
|
|
local xLen = v1.x - v2.x
|
|
if xLen < 0 then xLen = (xLen * (-1))
|
|
local yLen = v1.y - v3.y
|
|
if yLen < 0 then yLen = (yLen * (-1))
|
|
|
|
format "xLen: %\n" xLen
|
|
format "yLen: %\n" yLen
|
|
|
|
local faceArea = xLen * yLen
|
|
)
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
RsTa_calcVertsPerSeg
|
|
Calculates max allowed verts per segment
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------*/
|
|
fn RsTa_calcVertsPerSeg obj visualPlane segSpinner =
|
|
(
|
|
local segArea = RsTa_calcSegmentArea visualPlane
|
|
|
|
local idxLODDist = GetAttrIndex "Gta Object" "LOD distance"
|
|
local objLODDist = GetAttr obj idxLODDist
|
|
|
|
local vertsPerSeg = ( ( 100 * segArea ) / objLODDist )
|
|
segSpinner.value = ( vertsPerSeg as integer )
|
|
format "segArea: %\n" segArea
|
|
format "vertsPerSeg: %\n" vertsPerSeg
|
|
)
|
|
|
|
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
rollout RSVertDensityVisualizer "Vert Density Visualizer"
|
|
(
|
|
local visualPlane = undefined
|
|
local foundCountArray = #()
|
|
|
|
dotNetControl rsBannerPanel "Panel" pos:[0,0] height:32 width:(RSVertDensityVisualizer.width)
|
|
local banner = makeRsBanner dn_Panel:rsBannerPanel wiki:"Vert_Density_Visualizer" filename:(getThisScriptFilename())
|
|
|
|
checkbox cbx_autoCalcVerts "Auto-Calculate Vertex Limits" checked:true
|
|
spinner spn_wSegments "Number of Width Segments:" range:[2,150,30] type:#integer fieldWidth:40 align:#right enabled:false
|
|
spinner spn_hSegments "Number of Height Segments:" range:[2,150,30] type:#integer fieldWidth:40 align:#right enabled:false
|
|
spinner spn_numAllowedVerts "Target # Verts Per Segment" range:[1,10000,1000] type:#integer fieldWidth:40 align:#right enabled:false
|
|
button btn_createPlane "Create / Update Visualizer Plane" width:200 height:30 align:#center
|
|
checkBox cbx_autoUpdate "Auto Update On # Verts Change" align:#left tooltip:"Warning: This can be slow."
|
|
checkBox cbx_cullBounds "Cull Outlying Segments" align:#left tooltip:"Warning: This checks from the face centre, so can be inaccurate."
|
|
|
|
|
|
on btn_createPlane pressed do
|
|
(
|
|
if ( visualPlane != undefined and isValidNode visualPlane ) then delete visualPlane
|
|
|
|
local curSel = getCurrentSelection()
|
|
|
|
if ( curSel.count != 1 ) then messageBox "Please select one editable_poly object." title:"Error"
|
|
else
|
|
(
|
|
curSel = curSel[1]
|
|
if ( classOf curSel == Editable_Poly ) then
|
|
(
|
|
visualPlane = RsTa_createVisualizerPlane curSel spn_wSegments spn_hSegments autoCalcSegs:cbx_autoCalcVerts.checked
|
|
|
|
if ( cbx_cullBounds.checked ) then RsTa_faceRayTest curSel visualPlane
|
|
|
|
local faceMinMaxArray = #()
|
|
RsTa_buildMinMaxArrays visualPlane &faceMinMaxArray
|
|
|
|
foundCountArray = #()
|
|
RsTa_findMatches curSel faceMinMaxArray &foundCountArray
|
|
|
|
if ( cbx_autoCalcVerts.checked ) then RsTa_calcVertsPerSeg curSel visualPlane spn_numAllowedVerts
|
|
local threshold = spn_numAllowedVerts.value
|
|
RsTa_colourFaces visualPlane foundCountArray threshold
|
|
)
|
|
)
|
|
)
|
|
|
|
on cbx_autoCalcVerts changed state do
|
|
(
|
|
if ( state ) then
|
|
(
|
|
spn_wSegments.enabled = false
|
|
spn_hSegments.enabled = false
|
|
spn_numAllowedVerts.enabled = false
|
|
)
|
|
else
|
|
(
|
|
spn_wSegments.enabled = true
|
|
spn_hSegments.enabled = true
|
|
spn_numAllowedVerts.enabled = true
|
|
)
|
|
)
|
|
|
|
on spn_numAllowedVerts changed verts do
|
|
(
|
|
if ( cbx_autoUpdate.checked ) then
|
|
(
|
|
if ( visualPlane != undefined and isValidNode visualPlane ) then RsTa_colourFaces visualPlane foundCountArray verts
|
|
)
|
|
)
|
|
|
|
on RSVertDensityVisualizer open do
|
|
(
|
|
banner.setup()
|
|
)
|
|
|
|
on RSVertDensityVisualizer close do
|
|
(
|
|
if ( visualPlane != undefined and isValidNode visualPlane ) then delete visualPlane
|
|
)
|
|
)
|
|
createDialog RSVertDensityVisualizer 220 200 style:#(#style_titlebar, #style_toolwindow, #style_sysmenu) |