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

1061 lines
32 KiB
Plaintext
Executable File

--try (destroydialog RsLodModellingRoll) catch ()
rollout RsLodModellingRoll "LOD Modelling" width:300
(
local railShapeLabels = #("^", "+", "X", "<>","--", "|")
local geoDoubleDefaults = #{1..4}
local textureDir = (RsConfigGetTextureSourceDir() + "LOD/Fizzy/")
local texDiffFilename = (textureDir + "railing_white.bmp")
local texAlphaFilename = (textureDir + "Rail_Basic_A.bmp")
local tintChan = gRsMeshTintFuncs.tintChannel
local uvChan = 1
local artifAOChan = -1
group "Railing Lodder [v1.11: Upset Juice]" -- Name generated via: (filein (RsConfigGetWildWestDir() + "script/3dsmax/_config_files/wildwest_header.ms"); print (RsRandomPhrase count:100))
(
groupBox grpRailFinder "Shape-Finder Settings:" width:(RsLodModellingRoll.width - 26) height:42
spinner spnClusterSize "Max Vertex-Cluster Size:" range:[0,1,0.16] scale:0.01 width:100 offset:[4, (-grpRailFinder.height + 12)]
tooltip:"Source-verts closer than this will be combined to generate a single spline-node"
label lblDummyA "" pos:grpRailFinder.pos height:grpRailFinder.height visible:False
groupBox grpRailMesh "Rail LOD Mesh:" width:(grpRailFinder.width) height:295
radioButtons rdoRailShape "Top-Bar Shape:" labels:railShapeLabels offset:[0, (-grpRailMesh.height + 12)] default:6
tooltip:"Top-bars of generated railings will have this cross-section"
checkBox chkUpShape "Same shape for Uprights" align:#center checked:True pos:(rdoRailShape.pos + [0, 15])
tooltip:"Give upright-bars the same cross-section as top-bars, instead of the default +"
radioButtons rdoDoubleType "Double-sided:" labels:#("Geometry", "Material") offset:[-29,-2] default:(if geoDoubleDefaults[rdoRailShape.state] then 1 else 2)
tooltip:"Either doubles-up faces (not on angled/diamond cross-sections) or sets material as two-sided"
spinner spnRailingSize "Set Thickness:" range:[0,3,0.06] scale:0.01 width:86 enabled:False offset:[-14,2]
tooltip:"Set all railings to this thickness"
spinner spnRailingRatio "Height/Width Ratio" range:[0,4,1.0] scale:0.01 width:86 offset:[-3,-3]
tooltip:"Alters generated railing-height to match this ratio"
checkBox chkAutoSize "Auto Thickness" offset:[-13,-5] align:#center checked:True
tooltip:"Automatically use average railing-thickness found on source-mesh"
spinner spnIncreaseSize "+ Size%" offset:[30,-4] range:[0,100,25] align:#center tooltip:"" width:62 type:#integer
tooltip:"Increase auto-thickness by this percentage, to account for rail-lod texture's alpha-fringe"
checkBox chkAddMat "Add Default Material" offset:[0,-3] align:#center checked:True
tooltip:"Adds material with default textures to generated meshes"
checkBox chkCopyTint "Copy Source's MeshTint" pos:(chkAddMat.pos + [8,16]) checked:False enabled:chkAddMat.checked
tooltip:"Copy MeshTint from source, if used."
checkBox chkDiffuseTints "Texture-colours => MeshTint" pos:(chkCopyTint.pos + [0,16]) checked:False enabled:chkAddMat.checked
tooltip:"Gets diffuse-colour from source-object's textures (plus any meshtinting) and applies that as mesh-tint to generated meshes"
checkBox chkDrawableLod "Set as Drawable LOD" offset:[1,-2] align:#center checked:False
tooltip:"Automatically set generated meshes as Drawable LODs for their source-meshes"
checkBox chkVertNorms "Vertical Normals on Cross-Polys" pos:(chkDrawableLod.pos + [0,16]) checked:True
tooltip:"Sets normals of crossed polys to point upwards (if used)"
button btnGenRailLodMesh "Generate Railing LOD Meshes" width:160 height:30
tooltip:"Generate LOD-meshes for selected railing-objects"
label lblDummyB "" pos:grpRailMesh.pos height:grpRailMesh.height visible:False
groupBox grpRailSpline "Rail Spline:" pos:[grpRailMesh.pos.x, grpRailMesh.pos.y + grpRailMesh.height + 4] width:grpRailMesh.width height:56
button btnGenRailSplines "Generate Railing Splines" width:160 height:30 offset:[0, (-grpRailSpline.height + 14)]
tooltip:"Generate splines from selected railing-objects"
label lblDummyC "" pos:grpRailSpline.pos height:(grpRailSpline.height + 3) visible:False
)
group ""
(
button btnComboLodder "ComboLodder" width:160 height:24 offset:[0,-4]
)
fn generateMat lodName:"LodObj" doTint:True doubleMat:False =
(
local shaderName = "cutout"
if doTint do (append shaderName "_tnt")
local objSubMat = Rage_Shader name:(lodName + "_mat")
RstSetShaderName objSubMat shaderName
RstSetIsTwoSided objSubMat doubleMat
-- Find HardAlpha index, set value to zero:
local hardAlphaIdx
for i = 1 to (RstGetVariableCount objSubMat) while (hardAlphaIdx == undefined) do
(
if (RstGetVariableName objSubMat i) == "Hard Alpha" do
(
hardAlphaIdx = i
RstSetVariable objSubMat hardAlphaIdx 0.0
)
)
local subTexMap = bitmapTexture()
subTexMap.filename = texDiffFilename
setSubTexmap objSubMat 1 subTexMap
local alphaIdxOffset = (getNumSubTexmaps objSubMat) / 2
local subTexMap = bitmapTexture()
subTexMap.filename = texAlphaFilename
setSubTexmap objSubMat (1 + alphaIdxOffset) subTexMap
local objMat = MultiMaterial name:(lodName + "_multiMat") numsubs:1
objMat.materialList[1] = objSubMat
return objMat
)
fn getCrossSect railShape radius doDouble:False =
(
local crossSect = case railShape of
(
#angle:
(
doDouble = False
local triBaseHeight = radius * sin 30
local triBaseRadius = radius * cos 30
#(#([triBaseRadius,-triBaseHeight,0], [0,radius,0], [-triBaseRadius,-triBaseHeight,0]))
)
#plusCross:
(
#(#([0,-radius,0], [0,radius,0]), #([radius,0,0], [-radius,0,0]))
)
#xCross:
(
local diag = sqrt (0.5 * (radius ^ 2))
#(#([diag,diag,0], [-diag,-diag,0]), #([diag,-diag,0], [-diag,diag,0]))
)
#diamond:
(
doDouble = False
#(#([0,-radius,0], [radius,0,0], [0,radius,0], [-radius,0,0], [0,-radius,0]))
)
#horizontal:
(
#(#([radius,0,0], [-radius,0,0]))
)
#vertical:
(
#(#([0,-radius,0], [0,radius,0]))
)
)
-- Double-back the lines:
if doDouble do
(
for lineNum = 1 to crossSect.count do
(
local lineVerts = crossSect[lineNum]
local addVerts = for vertNum = (lineVerts.count - 1) to 1 by -1 collect lineVerts[vertNum]
join lineVerts addVerts
)
)
return crossSect
)
fn genRailingLodMesh obj maxClusterSize:0.16 setRailingSize: railShape:#angle upShape:#plusCross doubleGeom:True doubleMat:False sizeMult:1.25 heightMult:1.0 \
copyTint:True tintFromDiffuse:True setAsDrawable:False addMat:True verticalNorms:True returnSpline:False =
(
if not ((isKindOf obj Editable_Mesh) or (isKindOf obj Editable_Poly)) do return Undefined
local setRailingRadius = if (isKindOf setRailingSize Number) then (setRailingSize * 0.5) else undefined
local doTint = tintFromDiffuse
local objOp = RsMeshPolyOp obj
local objGetFace = RsGetFaceFunc obj
local objGetVert = RsGetVertFunc obj
local objGetMapVert = RsGetMapFaceFunc obj
local doneVertNums = #{}
local doneFaceNums = #{}
-- Get face-selection:
local selType = RsGetSubObjLevelName()
local selFaces = if (selType == #Face) then (getFaceSelection obj) else
(
local faceCount = obj.numFaces
if (faceCount == 0) then #{} else #{1..faceCount}
)
struct vertCluster (num, pos, horizPos, radius, verts = #{}, adjVerts = #{}, adjClusters = #(), doLine = True, canFollow = True, meshTint = [1,1,1])
local clusterNum = 0
local railingLines = #()
local uprightLines = #()
-- Get all vert-positions:
local vertPosList = for vertNum = 1 to obj.numVerts collect
(
objGetVert obj vertNum
)
local geomFaces = #()
local uvMapFaces = #()
local uvMapVerts = #()
local uvMapFaceCentres = #()
local tintMapFaces = #()
local tintMapVertVals = #()
-- Does this object have a tint-channel?
local hasTintChan = (not returnSpline) and (objOp.getmapsupport obj tintChan)
local tintedFaces = #{}
-- Get materials used on object, and find texturemap faces per material:
local objMats = #()
local faceBitmaps = #()
if (doTint or (copyTint and hasTintChan)) do
(
local objMatFaces = #()
RsGetMaterialsOnObjFaces obj materials:objMats faceLists:objMatFaces
-- If object has tint-channel, does it actually use any tint-shaders?
if hasTintChan do
(
hasTintChan = False
for matNum = 1 to objMats.count do
(
local mat = objMats[matNum]
if (isKindOf mat Rage_Shader) and (matchPattern (RstGetShaderName mat) pattern:"*_tnt*") do
(
tintedFaces += objMatFaces[matNum]
hasTintChan = True
)
)
)
if hasTintChan then
(
doTint = True
)
else
(
copyTint = False
)
if tintFromDiffuse do
(
faceBitmaps.count = obj.numFaces
-- Collect diffuse-maps per material used on object:
local objMatDiffuses = for mat in objMats collect
(
local diffuseBitmap = undefined
if (isKindOf mat Rage_Shader) do
(
for n = 1 to (RstGetVariableCount mat) while (diffuseBitmap == undefined) do
(
if RsIsDiffuseMap (RstGetVariableName mat n) do
(
local texMap = (getSubTexmap mat n)
if (texMap != undefined) and (doesFileExist texMap.filename) do
(
diffuseBitmap = texMap.bitmap
)
)
)
)
diffuseBitmap
)
-- Set up faceBitmaps: a list of per-face diffuse bitmaps:
for matIdx = 1 to objMats.count do
(
local faceDiffuseBmp = objMatDiffuses[matIdx]
if (faceDiffuseBmp != undefined) do
(
for faceNum = objMatFaces[matIdx] do
(
faceBitmaps[faceNum] = faceDiffuseBmp
)
)
)
)
)
-- Step through each selected face-element:
local success = True
progressStart "Collecting rail-lines"
local selCount = selFaces.numberSet
for faceNum = selFaces where (not doneFaceNums[faceNum]) while (success = progressUpdate (100.0 * (doneFaceNums * selFaces).numberSet / selCount)) do
(
-- Get all faces on selected face's element:
local elemFaces = objOp.getElementsUsingFace obj #{faceNum}
doneFaceNums += elemFaces
-- Collect verts per face:
local faceVerts = #()
faceVerts.count = obj.numFaces
for faceNum = elemFaces do
(
faceVerts[faceNum] = objOp.getVertsUsingFace obj faceNum
)
-- Filter element-faces to selected faces:
local selElemFaces = (elemFaces * selFaces)
local selElemVerts = #{}
for faceNum in selElemFaces do
(
selElemVerts += faceVerts[faceNum]
)
-- Find selected faces' vertex clusters:
local vertClusters = for vertNum = selElemVerts where (not doneVertNums[vertNum]) collect
(
doneVertNums[vertNum] = True
local thisVertPos = vertPosList[vertNum]
local clusterVertNums = #{vertNum}
for thatVertNum = selElemVerts where (not doneVertNums[thatVertNum]) do
(
if ((distance thisVertPos vertPosList[thatVertNum]) < maxClusterSize) do
(
doneVertNums[thatVertNum] = True
clusterVertNums[thatVertNum] = True
)
)
local clusterPosList = for vertNum in clusterVertNums collect vertPosList[vertNum]
local clusterSphere = RsFindMinSphere clusterPosList showProgress:false
local clusterMeshTints = #()
local clusterAdjVerts = #{}
for vertNum in clusterVertNums do
(
local vertFaces = selElemFaces * (objOp.getFacesUsingVert obj vertNum)
for faceNum in vertFaces do
(
-- Collect meshtint colours for cluster:
if doTint do
(
-- Get geom/map face-data, to get the matching mapping-vert index for this geom-vert:
if (geomFaces[faceNum] == undefined) do
(
geomFaces[faceNum] = objGetFace obj faceNum
uvMapFaces[faceNum] = objGetMapVert obj uvChan faceNum
local facePos = [0,0,0]
uvMapVerts[faceNum] = for uvVertNum in uvMapFaces[faceNum] collect
(
local val = objOp.getMapVert obj uvChan uvVertNum
facePos += val
val
)
facePos /= uvMapVerts[faceNum].count
uvMapFaceCentres[faceNum] = facePos
if hasTintChan then
(
tintMapFaces[faceNum] = objGetMapVert obj tintChan faceNum
)
else
(
tintMapFaces[faceNum] = (deepCopy uvMapFaces[faceNum])
)
)
-- Get mapping-vert index:
local vertIdx = findItem geomFaces[faceNum] vertNum
local tintVertNum = tintMapFaces[faceNum][vertIdx]
-- Get tint-colour (from cache-array, if it's already been found)
if (tintMapVertVals[tintVertNum] == undefined) do
(
local vertTintVal = [1,1,1]
local vertBmpClr = [1,1,1]
-- Get tint-colour (for faces with tint-shader)
if copyTint and tintedFaces[faceNum] do
(
vertTintVal = (objOp.getMapVert obj tintChan tintVertNum)
)
-- Get bitmap-colour from texturemap, if collected/used:
local faceBitmap = faceBitmaps[faceNum]
if (faceBitmap != undefined) do
(
local bmpMaxX = (faceBitmap.width - 1)
local bmpMaxY = (faceBitmap.height - 1)
local vertUvPos = uvMapVerts[faceNum][vertIdx]
local faceCentre = uvMapFaceCentres[faceNum]
-- Get uv-positions on line from vert to face-centre (a quarter and halfway to there)
local centreOffset = (faceCentre - vertUvPos)
local uvPositions = #(vertUvPos, vertUvPos + (0.5 * centreOffset), vertUvPos + (0.25 * centreOffset))
-- Collect bitmap-coordinates for line of UVs:
local bmpPosList = for uvPos in uvPositions collect
(
-- Convert UV-coords to texture-coords:
[integer (abs (mod uvPos.x 1.0) * bmpMaxX), integer ((1 - (abs (mod uvPos.y 1.0))) * bmpMaxY)]
)
bmpPosList = makeUniqueArray bmpPosList
local totalClr = [0,0,0]
for bmpPos in bmpPosList do
(
-- Get colour from texture:
totalClr += ((getPixels faceBitmap bmpPos 1)[1] as point3)
)
vertBmpClr = totalClr / (bmpPosList.count * 255)
)
-- Combine tint and bitmap colours:
tintMapVertVals[tintVertNum] = (vertTintVal * vertBmpClr)
)
append clusterMeshTints tintMapVertVals[tintVertNum]
)
clusterAdjVerts += faceVerts[faceNum]
)
-- Don't include cluster's verts, for easier eyeballing:
clusterAdjVerts -= clusterVertNums
)
local meshTint = [1,1,1]
if doTint do
(
-- Find average of cluster's vert-colours:
local tintTotal = [0,0,0]
for clr in clusterMeshTints do
(
tintTotal += clr
)
meshTint = tintTotal / clusterMeshTints.count
)
-- Collect cluster-data struct:
local midPos = clusterSphere.pos
-- Bump radius up by multiplier, to push out alpha'd portion of generated faces:
local clusterRadius = (clusterSphere.radius * sizeMult)
vertCluster pos:midPos radius:clusterRadius horizPos:[midPos.x, midPos.y] verts:clusterVertNums adjVerts:clusterAdjVerts meshTint:meshTint
)
-- Set up node-links between clusters:
for n = 1 to vertClusters.count do
(
local thisCluster = vertClusters[n]
local adjVerts = thisCluster.adjVerts
for m = (n + 1) to vertClusters.count do
(
local thatCluster = vertClusters[m]
if ((thatCluster.verts * adjVerts).numberSet != 0) do
(
append thisCluster.adjClusters thatCluster
append thatCluster.adjClusters thisCluster
)
)
)
fn sortAdjClusters v1 v2 pos: =
(
local distA = distance pos v1.horizPos
local distB = distance pos v1.horizPos
case of
(
(distA > distB):-1
(distA < distB):1
Default:0
)
)
-- Sort adjacent-cluster links by horizontal distance, so that uprights will be found last if there's a choice:
for thisCluster in vertClusters do
(
qsort thisCluster.adjClusters sortAdjClusters pos:thisCluster.horizPos
)
-- Order clusters by connection-count, so we process lines with lots of offshoots first:
qsort vertClusters (fn sorter v1 v2 = (v2.adjClusters.count - v1.adjClusters.count))
-- Apply index-numbers to clusters:
for clusterNum = 1 to vertClusters.count do
(
vertClusters[clusterNum].num = clusterNum
)
-- Function orders two clusters by position, to make it easier to link ends up:
fn orderClustersByPos v1 v2 =
(
local posDiff = 100 * (v1.pos - v2.pos)
local retVal = 0
for n = 1 to 3 while (retVal == 0) do
(
retVal = (posDiff[n] as integer)
)
return retVal
)
for clusterNum = 1 to vertClusters.count do
(
local thisCluster = vertClusters[clusterNum]
for thatCluster in thisCluster.adjClusters where (thatCluster.num < clusterNum) do
(
local linePair = #(thisCluster, thatCluster)
qsort linePair orderClustersByPos
-- Lines are separated off if they're not tilted from vertical by a minimum angle (around four or five degrees)
local isUpright = False
if (not returnSpline) do
(
local clusterDiffNorm = normalize (thisCluster.pos - thatCluster.pos)
local tiltRatio = length [clusterDiffNorm.X, clusterDiffNorm.Y]
isUpright = (tiltRatio < 0.08)
)
-- Should this line go in the uprights-list, or the rails list?
if isUpright then
(
append uprightLines linePair
)
else
(
append railingLines linePair
)
)
)
)
progressEnd()
-- If previous step was cancelled...
if not success do return false
-- Connect up the individual upright/rail lines:
local toDoCount = (railingLines.count + uprightLines.count)
local thisNum = 0
progressStart "Connecting rail-lines"
for lineArray in #(railingLines, uprightLines) while success do
(
local doLoop = True
local lineArrayCount = lineArray.count
while doLoop and success do
(
doLoop = False
for n = 1 to lineArray.count where (lineArray[n] != undefined) while (success = progressUpdate (100.0 * (thisNum += 1) / toDoCount)) do
(
if success do
(
local thisLine = lineArray[n]
local thisLineEnd = thisLine[thisLine.count]
for m = 1 to lineArray.count where (lineArray[m] != undefined) and (n != m) do
(
local thatLine = lineArray[m]
-- Does start of that line match end of this one?
local isMatch = (thatLine[1] == thisLineEnd)
-- If the end of that line matches, use a reversed version as a match:
if (not isMatch) and (thatLine[thatLine.count] == thisLineEnd) do
(
isMatch = True
thatLine = (for n = thatLine.count to 1 by -1 collect thatLine[n])
)
if isMatch do
(
thisLine.count = (thisLine.count - 1)
join thisLine thatLine
thisLineEnd = thisLine[thisLine.count]
lineArray[m] = undefined
if not doLoop do
(
toDoCount += lineArrayCount
doLoop = True
)
)
)
)
)
)
)
progressEnd()
-- If previous step was cancelled...
if not success do return false
-- Filter out undefined values:
railingLines = for item in railingLines where (item != undefined) collect item
uprightLines = for item in uprightLines where (item != undefined) collect item
local railingRadius = 0.0
local uprightRadius = 0.0
if (setRailingRadius != undefined) then
(
railingRadius = setRailingRadius
uprightRadius = setRailingRadius
)
else
(
-- Get average railing/upright vert-cluster radii:
if (railingLines.count != 0) do
(
local itemCount = 0
for lineList in railingLines do
(
for item in lineList do
(
itemCount += 1
railingRadius += item.radius
)
)
railingRadius /= itemCount
)
-- Get average upright vert-cluster radius:
if (uprightLines.count != 0) do
(
local itemCount = 0
for lineList in uprightLines do
(
for item in lineList do
(
itemCount += 1
uprightRadius += item.radius
)
)
uprightRadius /= itemCount
)
)
-- Optimise out clusters that don't change line's direction:
(
fn optimiseLine clusterList =
(
local newClusterList = #(clusterList[1])
local prevPos = clusterList[1].pos
local clusterCount = clusterList.count
for n = 2 to clusterCount do
(
if (n == clusterCount) then
(
append newClusterList clusterList[n]
)
else
(
local thisCluster = clusterList[n]
local nextCluster = clusterList[n + 1]
local thisDir = normalize (thisCluster.pos - prevPos)
local nextDir = normalize (nextCluster.pos - prevPos)
-- Only add clusters if the next one is in a different direction:
if ((distance thisDir nextDir) > 0.001) do
(
append newClusterList thisCluster
prevPos = thisCluster.pos
)
)
)
return newClusterList
)
railingLines = for clusterList in railingLines collect (optimiseLine clusterList)
uprightLines = for clusterList in uprightLines collect (optimiseLine clusterList)
)
local newLodObj
-- Generate Lod-object:
pushPrompt "Generating object..."
if (railingLines.count != 0) or (uprightLines.count != 0) do
(
local lodName = obj.name + "_LOD"
if ((getNodeByName lodName) != undefined) do
(
lodName = uniqueName lodName
)
-- These cross-poly faces will be given vertical normals, to fix their lighting:
local crossPolys = #{}
local shapesList = #()
append shapesList (dataPair lines:railingLines crossSectVals:(dataPair name:railShape radius:railingRadius))
append shapesList (dataPair lines:uprightLines crossSectVals:(dataPair name:upShape radius:uprightRadius))
for shapeNum = 1 to shapesList.count where (shapesList[shapeNum].lines.count != 0) do
(
local crossSectName
local isRailShape = (shapeNum == 1)
local splineList = shapesList[shapeNum]
local tempSplineObj = SplineShape pos:obj.pos name:(lodName + "_TEMP")
local lineNum = 0
for thisLine in splineList.lines where (thisLine.count > 1) do
(
addNewSpline tempSplineObj
lineNum += 1
for cluster in thisLine do
(
addKnot tempSplineObj lineNum #corner #line cluster.pos
)
if (thisLine.count > 3) and (thisLine[1].pos == thisLine[thisLine.count].pos) do
(
close tempSplineObj lineNum
)
)
updateShape tempSplineObj
-- Add cross-section and use it with Sweep modifier:
if not returnSpline do
(
crossSectName = splineList.crossSectVals.name
local sectionSpline = SplineShape()
(
local crossSectLines = getCrossSect crossSectName splineList.crossSectVals.radius doDouble:doubleGeom
local sectionMult = if isRailShape then [1,heightMult,1] else [1,1,1]
for splineNum = 1 to crossSectLines.count do
(
addNewSpline sectionSpline
local thisSpline = crossSectLines[splineNum]
for pos in thisSpline do
(
addKnot sectionSpline splineNum #corner #line (pos * sectionMult)
)
if (thisSpline.count > 3) and (thisSpline[1] == thisSpline[thisSpline.count]) do
(
close sectionSpline splineNum
)
)
updateShape sectionSpline
)
-- Apply sweep-modifier:
(
local sweepMod = sweep CustomShape:1 SmoothPath:False SmoothSection:False GenMatIDs:False GenerateMappingCoords:True Banking:False
sweepMod.Shapes[1] = sectionSpline
addModifier tempSplineObj sweepMod
delete sectionSpline
)
-- Rotate UVs to horizontal:
(
local uvXformMod = UVW_Xform Rotation_Angle:90 Rotation_Center:1
local doubledFaces = doubleGeom and not (matchPattern crossSectName pattern:"*angle*")
if doubledFaces do
(
uvXformMod.V_Tile = 2.0
)
addModifier tempSplineObj uvXformMod
)
-- Convert extruded spline-object to Poly:
convertToPoly tempSplineObj
-- Set Artificial Ambient Occlusion (vertex-illumination) channel to black:
polyOp.setVertColor tempSplineObj artifAOChan #all Black
if doTint do
(
-- Activate tint-channel:
polyOp.setFaceColor tempSplineObj tintChan #all White
local clusterList = #()
for item in splineList.lines do (join clusterList item)
clusterList = (makeUniqueArray clusterList)
local clusterPosList = for cluster in clusterList collect cluster.pos
-- Apply cluster-tints to verts:
for vertNum = 1 to tempSplineObj.numVerts do
(
-- Find closest cluster:
local vertPos = polyOp.getVert tempSplineObj vertNum
local clusterDists = for clusterPos in clusterPosList collect (distance vertPos clusterPos)
local closestIdx = findItem clusterDists (aMin clusterDists)
-- Get vertex-colour from closest cluster:
polyOp.setMapVert tempSplineObj tintChan vertNum clusterList[closestIdx].meshTint
)
)
)
local firstAddedFace = 1
-- Make first temp-object be lod-object:
if (newLodObj == undefined) then
(
newLodObj = tempSplineObj
newLodObj.name = lodName
if returnSpline do
(
newLodObj.name = (lodName + "_spline")
)
)
else
(
firstAddedFace += newLodObj.numFaces
-- Attach temp-object into lod-object:
polyOp.attach newLodObj tempSplineObj
)
-- Make note of cross-poly faces:
if (crossSectName != undefined) and (matchPattern crossSectName pattern:"*cross*") do
(
crossPolys += #{firstAddedFace..newLodObj.numFaces}
)
)
-- Final object-setup:
if not returnSpline do
(
-- Point normals for cross-polys upwards:
if verticalNorms and (crossPolys.numberSet != 0) do
(
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
local normalMod = Edit_Normals selectBy:3
addModifier newLodObj normalMod
modPanel.setCurrentObject normalMod ui:True
-- Convert face-list to normals-list:
local selNorms = #{}
normalMod.ConvertFaceSelection crossPolys selNorms
for n = selNorms do
(
normalMod.SetNormalExplicit n
normalMod.SetNormal n [0,0,1]
)
)
collapseStack newLodObj
if doubleGeom do
(
newLodObj.backFaceCull = True
)
)
)
if (isValidNode newLodObj) do
(
-- Add new object to prop-objects' container:
local objCont = RsGetObjContainer obj
if (objCont != undefined) do
(
objCont.addNodeToContent newLodObj
)
if not returnSpline do
(
-- Set up object's material:
if addMat do
(
newLodObj.material = (generateMat lodName:lodName dotint:dotint doubleMat:doubleMat)
)
-- Set new mesh as Drawable LOD:
if setAsDrawable do
(
RsLodDrawable_SetLowerDetailModel obj newLodObj
)
)
)
popPrompt()
return newLodObj
)
on rdoRailShape changed val do
(
-- Set default doubled-faces value for selected cross-section:
rdoDoubleType.state = if geoDoubleDefaults[val] then 1 else 2
)
on chkAutoSize changed state do
(
spnRailingSize.enabled = not state
spnIncreaseSize.enabled = state
)
on chkAddMat changed state do
(
chkCopyTint.enabled = state
chkDiffuseTints.enabled = state
--chkDiffuseTints.checked = state
)
fn useObjs =
(
local retVal = for obj in selection where ((isKindOf obj Editable_Mesh) or (isKindOf obj Editable_Poly)) collect obj
if (retVal.count == 0) do
(
messageBox "No Editable Poly/Mesh objects are selected, nothing to do!\n\n(Rail Lodder only works on these object-classes)" title:"Rail Lodder: Nothing to do"
)
return retVal
)
-- Generate railing lod-meshes:
on btnGenRailLodMesh pressed do
(
local selObjs = useObjs()
if (selObjs.count == 0) do (return False)
local clusterSize = spnClusterSize.value
local railSize = if chkAutoSize.checked then undefined else spnRailingSize.value
local railSizeMult = ((spnIncreaseSize.value + 100) * 0.01)
local heightMult = spnRailingRatio.value
local setDrawable = chkDrawableLod.state
local addMat = chkAddMat.checked
local copyTint = chkCopyTint.checked
local tintFromDiffuse = chkDiffuseTints.checked
local doubleGeom = (rdoDoubleType.state == 1)
local doubleMat = (rdoDoubleType.state == 2)
local verticalNorms = chkVertNorms.checked
local railShape = case rdoRailShape.state of
(
1:#angle
2:#plusCross
3:#xCross
4:#diamond
5:#horizontal
6:#vertical
)
local upShape = if chkUpShape.checked then railShape else #plusCross
undo "Generate Railing LODs" on
(
setWaitCursor()
local success = True
local newObjs = #()
for obj in selObjs while success do
(
local newObj = genRailingLodMesh obj maxClusterSize:clusterSize setRailingSize:railSize sizeMult:railSizeMult heightMult:heightMult \
railShape:railShape upShape:upShape setAsDrawable:setDrawable addMat:addMat copyTint:copyTint tintFromDiffuse:tintFromDiffuse \
doubleGeom:doubleGeom doubleMat:doubleMat verticalNorms:verticalNorms
if (newObj == False) then
(
success = False
)
else
(
append newObjs newObj
)
)
newObjs = for obj in newObjs where (isValidNode obj) collect obj
if success then
(
clearSelection()
select newObjs
)
else
(
delete newObjs
)
setArrowCursor()
completeRedraw()
)
)
-- Generate railing-splines:
on btnGenRailSplines pressed do
(
local selObjs = useObjs()
if (selObjs.count == 0) do (return False)
local clusterSize = spnClusterSize.value
undo "Generate Railing Splines" on
(
setWaitCursor()
local success = True
local newObjs = #()
for obj in selObjs while success do
(
local newObj = (genRailingLodMesh obj maxClusterSize:clusterSize setRailingSize:0 returnSpline:True)
if (newObj == False) then
(
success = False
)
else
(
append newObjs newObj
)
)
newObjs = for obj in newObjs where (isValidNode obj) collect obj
if success then
(
clearSelection()
select newObjs
)
else
(
delete newObjs
)
setArrowCursor()
completeRedraw()
)
)
-- Run ComboLodder script:
on btnComboLodder pressed do
(
filein (::RsConfigGetWildwestDir() + "Script/3dsMax/Maps/lodCombinerTool_UI.ms")
)
)
-- TEST BITS --
if FALSE do
(
local obj = $
local newobj = RsLodModellingRoll.genRailingLodMesh obj railShape:#plusCross --returnSpline:True
--obj.isHidden = true
--select newobj
)
--createDialog RsLodModellingRoll