473 lines
13 KiB
Plaintext
Executable File
473 lines
13 KiB
Plaintext
Executable File
--
|
|
-- File:: rockstar/helpers/TerrainLodder.ms
|
|
-- Description:: Terrain Lodder
|
|
--
|
|
-- Author:: Stuart Macdonald <stuart.macdonald@rockstarnorth.com
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
-- HISTORY
|
|
--
|
|
--v1.1 (SM) Altered default vert percentage to 60 from 30
|
|
-- (SM) Added prefix or postfix option for LOD tag
|
|
|
|
--v1.2 (SM) Added checkbox for Maintain Base Vertices option of MultiRes modifier
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
filein "pipeline/util/meshutil.ms" -- Used for checking for verts in same position
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Rollouts
|
|
-----------------------------------------------------------------------------
|
|
|
|
rollout TerrainLodder_ProOptimize "Pro_Optimize"
|
|
(
|
|
--variables
|
|
local LODmat = undefined
|
|
|
|
group ""
|
|
(
|
|
radioButtons rbt_lod "Name Position:" width:118 height:30 labels:#("LOD_", "_LOD") default:2 columns:2 align:#left across:2
|
|
materialbutton picklodmtl "Pick LOD Mat" width:100 height:23 align:#right
|
|
)
|
|
|
|
group "Optimisation Options"
|
|
(
|
|
spinner optrange "Vertex %" range:[1,100,60] type:#integer width:104 height:16 across:2 align:#left
|
|
spinner pushrange "Push" range:[-10.0,10.0,-0.2] width:104 height:16 range:[0,100,0]
|
|
checkbox chkExcludeBorders "Exclude Borders:" checked:true tooltip:"On = Exclude, Off = Protect"
|
|
checkbox chkKeepMaterialBoundaries "Keep Material Boundaries:" checked:true
|
|
checkbox chkKeepVertexColours "Keep Vertex Colours:" checked:true
|
|
checkbox chkKeepTextures "Keep Textures:" checked:true
|
|
checkbox chkKeepUVBoundaries "Keep UV Boundaries:" checked:true
|
|
checkbox chkLockVertexPos "Lock Vertex Position:" checked:false
|
|
|
|
)
|
|
|
|
button lodit "LOD IT" width:243 height:24
|
|
--checkbox chbx_mverts "Keep Base Verts" checked:true tooltip:"keep this on for terrain" pos:[150,85] width:103 height:17
|
|
|
|
--select inner verts of polymesh
|
|
fn selInnerVerts n =
|
|
(
|
|
--Get exterior vertices
|
|
OpenEdges = polyOp.getOpenEdges n
|
|
SelVerts = polyOp.getVertsUsingEdge n OpenEdges
|
|
n.selectedVerts = -SelVerts
|
|
|
|
)
|
|
|
|
--select outer verts of polymesh
|
|
fn selOuterVerts n =
|
|
(
|
|
--Get exterior vertices
|
|
OpenEdges = polyOp.getOpenEdges n
|
|
SelVerts = polyOp.getVertsUsingEdge n OpenEdges
|
|
n.selectedVerts = SelVerts
|
|
|
|
)
|
|
|
|
--optimize function
|
|
fn optlod =
|
|
(
|
|
local proOptMod = ProOptimizer()
|
|
modPanel.addModToSelection proOptMod
|
|
|
|
proOptMod.VertexPercent = (optrange.value as Float)
|
|
proOptMod.OptimizationMode = if chkExcludeBorders.checked then 2 else 1
|
|
proOptMod.LockMat = chkKeepMaterialBoundaries.checked
|
|
proOptMod.KeepVC = chkKeepVertexColours.checked
|
|
proOptMod.KeepUV = chkKeepTextures.checked
|
|
proOptMod.LockUV = chkKeepUVBoundaries.checked
|
|
proOptMod.LockPoints = chkLockVertexPos.checked
|
|
proOptMod.Calculate = on
|
|
)
|
|
|
|
--push function
|
|
fn pushlod =
|
|
(
|
|
convertTo $ Editable_Poly
|
|
selInnerVerts $
|
|
subObjectLevel = 1
|
|
--Add the Push mod
|
|
pushmod = push()
|
|
pushmod.push_value = pushrange.value
|
|
modPanel.addModtoSelection( pushmod )
|
|
)
|
|
|
|
fn projectVertexMap source target channel:0 =
|
|
(
|
|
addModifier source (Turn_to_Mesh())
|
|
collapseStack source
|
|
local sourceMesh = source --needs to be editable mesh
|
|
|
|
--Check map support
|
|
--return if there is no map data on the source
|
|
if (meshop.getMapSupport source -2) == false then return false
|
|
|
|
local targetMesh = snapshotAsMesh target
|
|
|
|
if not ( meshop.getMapSupport sourceMesh channel ) then (
|
|
meshop.setMapSupport sourceMesh channel True
|
|
)
|
|
|
|
if not ( meshop.getMapSupport targetMesh channel ) then (
|
|
meshop.setMapSupport targetMesh channel True
|
|
)
|
|
|
|
--setup RayMeshGridIntersect
|
|
local rayMesh = RayMeshGridIntersect()
|
|
rayMesh.Initialize 10
|
|
rayMesh.addNode target
|
|
--print (rayMesh.nodelist())
|
|
raymesh.BuildGrid()
|
|
|
|
--raycast form source vert normal into the the target
|
|
for vtx=1 to (getNumVerts sourceMesh) do
|
|
(
|
|
--the Position
|
|
local position = in coordsys #world (meshop.getVert sourceMesh vtx node:source)
|
|
|
|
--get the normal
|
|
local normal = normalize(getnormal sourceMesh vtx)
|
|
--normal = [0,0,1]
|
|
|
|
--cast a ray
|
|
local hits = raymesh.intersectRay position normal true
|
|
|
|
--if we get a hit then....
|
|
if hits > 0 then
|
|
(
|
|
--get the face hit
|
|
local theIndex = rayMesh.getClosestHit() --get the index of the closest hit by the ray
|
|
local theFace = rayMesh.getHitFace theIndex
|
|
|
|
--get the barycentric coords of the face hit
|
|
local baryPt = rayMesh.getHitBary theIndex
|
|
|
|
--get the mapverts for the face hit
|
|
local faceMapVerts = meshop.getMapFace targetMesh channel theFace --will be in index order
|
|
--local faceMapVerts = (meshop.getMapVertsUsingMapFace targetMesh channel theFace) as Array
|
|
|
|
--get the mapvert values for the face hit
|
|
local mapVertValues = #((meshop.getMapVert targetMesh channel faceMapVerts.x),
|
|
(meshop.getMapVert targetMesh channel faceMapVerts.y),
|
|
(meshop.getMapVert targetMesh channel faceMapVerts.z))
|
|
|
|
--result is the barycentric offset weighted value of the mapvert values
|
|
local colour = (baryPt.x * mapVertValues[1]) + (baryPt.y * mapVertValues[2]) + (baryPt.z * mapVertValues[3])
|
|
|
|
--set the colour
|
|
local geoFaces = meshop.getFacesUsingVert sourceMesh vtx as Array
|
|
for f in geoFaces do
|
|
(
|
|
local geoFaceVerts = getFace sourceMesh f
|
|
local mapFaceVerts = meshop.getMapFace sourceMesh channel f --will be in index order
|
|
|
|
--match up indices
|
|
mapVert = case of
|
|
(
|
|
(geoFaceVerts.x == vtx): mapFaceVerts.x
|
|
(geoFaceVerts.y == vtx): mapFaceVerts.y
|
|
(geoFaceVerts.z == vtx): mapFaceVerts.z
|
|
)
|
|
|
|
meshop.setMapVert sourceMesh channel mapVert colour
|
|
)
|
|
)
|
|
)
|
|
update source
|
|
)
|
|
|
|
on picklodmtl picked mtl do
|
|
(
|
|
|
|
LODmat = mtl
|
|
picklodmtl.caption = mtl.name
|
|
|
|
)
|
|
|
|
on lodit pressed do
|
|
(
|
|
selset = getCurrentSelection()
|
|
lodset = #()
|
|
|
|
-- If it's not an editable mesh or poly, exit here rather than possibly after changing some
|
|
-- selected objects in the next loop.
|
|
for obj in selset do
|
|
(
|
|
if (( classOf obj != editable_Mesh) and ( classOf obj != editable_Poly)) then
|
|
(
|
|
|
|
messagebox "One of the selected objects is not an editable mesh or poly"
|
|
return false
|
|
)
|
|
)
|
|
|
|
|
|
undo "Create Terrain LOD" on
|
|
(
|
|
clearselection()
|
|
|
|
for n = 1 to selset.count do
|
|
(
|
|
-- Delete any faces with 0 area, as that causes a crash after Multires
|
|
-- whenever garbage collection is next done.
|
|
RSFindFacesWithZeroArea selset[n] deleteFaces:true
|
|
|
|
--check it is editable mesh, convert
|
|
if classOf selset[n] == editable_Mesh then convertTo selset[n] Editable_Poly
|
|
--check obj is editable poly
|
|
if classOf selset[n] == editable_Poly then
|
|
(
|
|
selOuterVerts selset[n]
|
|
newlod = copy selset[n]
|
|
highLodModel = selset[n]
|
|
|
|
--check whether prefix LOD_ or postfix _LOD
|
|
if rbt_lod.state == 1 then
|
|
(
|
|
newlod.name = "LOD_" + selset[n].name
|
|
) else newlod.name = selset[n].name + "_LOD"
|
|
|
|
-- Automatically link the LOD
|
|
if (newlod != undefined and highLodModel != undefined) do (
|
|
LinkType_LOD = 0
|
|
RsSceneLink.AddContainer LinkType_LOD selset[n]
|
|
RsSceneLink.SetParent LinkType_LOD selset[n] newlod
|
|
)
|
|
|
|
append lodset newlod
|
|
)
|
|
)
|
|
|
|
max modify mode
|
|
|
|
|
|
--run push on each object
|
|
for n = 1 to lodset.count do
|
|
(
|
|
select lodset[n]
|
|
|
|
optlod()
|
|
pushlod()
|
|
projectVertexMap lodset[n] selset[n] channel:-2
|
|
collapseStack lodset[n]
|
|
PolyToolsModeling.Quadrify false false
|
|
|
|
--Apply standard material if none picked
|
|
if LODmat == undefined then
|
|
(
|
|
lodset[n].material = standardMaterial()
|
|
lodset[n].material.name = "CHANGE_THIS"
|
|
) else lodset[n].material = LODmat
|
|
)
|
|
|
|
--select the lods
|
|
select lodset
|
|
selectMore selset
|
|
|
|
clearselection()
|
|
select lodset
|
|
)
|
|
)
|
|
)
|
|
|
|
rollout TerrainLodder_MultiRes "MultiRes"
|
|
(
|
|
--variables
|
|
local LODmat
|
|
|
|
button lodit "LOD IT" pos:[19,125] width:243 height:24
|
|
radioButtons rbt_lod "Name Position:" pos:[17,27] width:118 height:30 labels:#("LOD_", "_LOD") default:2 columns:2
|
|
materialbutton picklodmtl "Pick LOD Mat" pos:[149,36] width:100 height:23
|
|
spinner optrange "Vert" range:[1,100,60] type:#integer pos:[27,81] width:104 height:16
|
|
spinner pushrange "Push" range:[-10.0,10.0,-0.2] pos:[27,101] width:104 height:16 range:[0,100,0]
|
|
label lblVert "Percentage of verts left:" pos:[17,64] width:128 height:16
|
|
checkbox chbx_mverts "Keep Base Verts" checked:true tooltip:"keep this on for terrain" pos:[150,85] width:103 height:17
|
|
|
|
--select inner verts of polymesh
|
|
fn selInnerVerts n =
|
|
(
|
|
--Get exterior vertices
|
|
OpenEdges = polyOp.getOpenEdges n
|
|
SelVerts = polyOp.getVertsUsingEdge n OpenEdges
|
|
n.selectedVerts = -SelVerts
|
|
|
|
)
|
|
|
|
--select outer verts of polymesh
|
|
fn selOuterVerts n =
|
|
(
|
|
--Get exterior vertices
|
|
OpenEdges = polyOp.getOpenEdges n
|
|
SelVerts = polyOp.getVertsUsingEdge n OpenEdges
|
|
n.selectedVerts = SelVerts
|
|
|
|
)
|
|
|
|
--optimize function
|
|
fn optlod =
|
|
(
|
|
modPanel.addModToSelection (Multires())
|
|
|
|
$.modifiers[#MultiRes].resetParams = true
|
|
$.modifiers[#MultiRes].vertexPercent = optrange.value
|
|
$.modifiers[#MultiRes].baseVertices = chbx_mverts.state
|
|
$.modifiers[#MultiRes].reqGenerate = true
|
|
)
|
|
|
|
--push function
|
|
fn pushlod =
|
|
(
|
|
convertTo $ Editable_Poly
|
|
selInnerVerts $
|
|
subObjectLevel = 1
|
|
--Add the Push mod
|
|
pushmod = push()
|
|
pushmod.push_value = pushrange.value
|
|
modPanel.addModtoSelection( pushmod )
|
|
)
|
|
|
|
on picklodmtl picked mtl do
|
|
(
|
|
|
|
LODmat = mtl
|
|
picklodmtl.caption = mtl.name
|
|
|
|
)
|
|
|
|
on lodit pressed do
|
|
(
|
|
if ( false == querybox "Previous MAX crash with Multires has been avoided - do you want to continue? (Save recommended just incase)" ) do
|
|
(
|
|
return false
|
|
)
|
|
|
|
gc light:true
|
|
selset = getCurrentSelection()
|
|
lodset = #()
|
|
|
|
-- If it's not an editable mesh or poly, exit here rather than possibly after changing some
|
|
-- selected objects in the next loop.
|
|
for obj in selset do (
|
|
|
|
if (( classOf obj != editable_Mesh) and ( classOf obj != editable_Poly)) then (
|
|
|
|
messagebox "One of the selected objects is not an editable mesh or poly"
|
|
return false
|
|
)
|
|
)
|
|
|
|
clearselection()
|
|
|
|
for n = 1 to selset.count do
|
|
(
|
|
-- Delete any faces with 0 area, as that causes a crash after Multires
|
|
-- whenever garbage collection is next done.
|
|
RSFindFacesWithZeroArea selset[n] deleteFaces:true
|
|
|
|
--check it is editable mesh, convert
|
|
if classOf selset[n] == editable_Mesh then convertTo selset[n] Editable_Poly
|
|
--check obj is editable poly
|
|
if classOf selset[n] == editable_Poly then
|
|
(
|
|
selOuterVerts selset[n]
|
|
newlod = copy selset[n]
|
|
highLodModel = selset[n]
|
|
|
|
--check whether prefix LOD_ or postfix _LOD
|
|
if rbt_lod.state == 1 then
|
|
(
|
|
newlod.name = "LOD_" + selset[n].name
|
|
) else newlod.name = selset[n].name + "_LOD"
|
|
|
|
-- Automatically link the LOD
|
|
if (newlod != undefined and highLodModel != undefined) do (
|
|
LinkType_LOD = 0
|
|
RsSceneLink.AddContainer LinkType_LOD selset[n]
|
|
RsSceneLink.SetParent LinkType_LOD selset[n] newlod
|
|
)
|
|
|
|
append lodset newlod
|
|
)
|
|
)
|
|
|
|
max modify mode
|
|
|
|
|
|
--run push on each object
|
|
for n = 1 to lodset.count do
|
|
(
|
|
select lodset[n]
|
|
|
|
optlod()
|
|
pushlod()
|
|
convertTo lodset[n] Editable_Mesh
|
|
convertTo selset[n] Editable_Mesh
|
|
|
|
--Apply standard material if none picked
|
|
if LODmat == undefined then
|
|
(
|
|
lodset[n].material = standardMaterial()
|
|
lodset[n].material.name = "CHANGE_THIS"
|
|
) else lodset[n].material = LODmat
|
|
)
|
|
|
|
--select the lods
|
|
select lodset
|
|
selectMore selset
|
|
|
|
clearselection()
|
|
select lodset
|
|
)
|
|
)
|
|
|
|
rollout TerrainLodder_roll "Terrain Lodder"
|
|
(
|
|
|
|
--ui
|
|
hyperlink lnkHelp "Help?" address:"https://devstar.rockstargames.com/wiki/index.php/LOD_Toolkit#Terrain_Lodder" align:#right color:(color 0 0 255) hoverColor:(color 0 0 255) visitedColor:(color 0 0 255)
|
|
|
|
dropDownList ddlMode "Mode:" items:#("ProOptimize", "MultiRes")
|
|
|
|
subRollout lodMode "TerrainLodder_ProOptimize" width:280 height:380 offset:[-10, 0]
|
|
|
|
fn selectedMode arg =
|
|
(
|
|
case ddlMode.selected of
|
|
(
|
|
"ProOptimize":
|
|
(
|
|
RemoveSubRollout lodMode TerrainLodder_MultiRes
|
|
AddSubRollout lodMode TerrainLodder_ProOptimize
|
|
lodMode.height = 380
|
|
TerrainLodder_roll.height = 380
|
|
)
|
|
|
|
"MultiRes":
|
|
(
|
|
RemoveSubRollout lodMode TerrainLodder_ProOptimize
|
|
AddSubRollout lodMode TerrainLodder_MultiRes
|
|
lodMode.height = 250
|
|
TerrainLodder_roll.height = 250
|
|
)
|
|
)
|
|
)
|
|
|
|
on ddlMode selected arg do selectedMode arg
|
|
|
|
on TerrainLodder_roll open do
|
|
(
|
|
AddSubRollout lodMode TerrainLodder_ProOptimize
|
|
lodMode.height = 380
|
|
TerrainLodder_roll.height = 400
|
|
)
|
|
|
|
-- Save rolled-up state:
|
|
on TerrainLodder_roll rolledUp down do
|
|
(
|
|
RsSettingWrite "TerrainLodder_roll" "rollup" (not down)
|
|
)
|
|
)
|