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