g__DEBUG__ = false filein (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms") rsta_loadCommonFunction #("FN_RSTA_userSettings", "FN_RSTA_rageMat", "FN_Geometry") filein (RsConfigGetToolsDir() + "wildwest/script/3dsMax/Maps/Materials/rsta_mapIdStruct.ms") filein (RsConfigGetToolsDir() + "wildwest/script/3dsmax/maps/ultimateterrainmapper.ms") -- Set default vert/face-count limits, if they're not available from labelled tool-scripts yet: if (RsMaxCollVerts == undefined) do ( global RsMaxCollVerts = 32768 global RsMaxCollFaces = 100000 ) struct TileData ( position = [0,0], containerName = "", containerPath = "", meshes = #(), --Datapair mesh: LOD: colMeshes = #(), --DataPair drawMesh: LOD: ? textures = #(), --DataPair path:x:/blah/someting.bmp type:diffuse fn getRenderMesh LODName = ( if (this.meshes.count == 0) then ( return undefined ) else --find the level we after ( local LODMesh = #NOTFOUND for item in this.meshes where (item.LOD == LODName) do ( LODMesh = item.mesh ) return LODMesh ) ), fn setRenderMesh LODName newMesh = ( if (this.meshes.count == 0) then ( return false ) else ( for item in this.meshes where (item.LOD == LODName) do ( item.mesh = newMesh return true ) ) ), fn deleteMeshes = ( for item in this.meshes do ( delete item.mesh ) this.meshes = #() ) ) struct ContainerGridSetup ( --areaColourMap = Dictionary(), areas, areaDict = Dictionary(), areaGrids = Dictionary(), colourNames = Dictionary(), alphaGrid, layoutImage, --gridCorner = [-4096, -4096], old --gridCorner = [-2446.085, -4046.253], gridCorner, tileSize = 512.0, mapTileOffset = [0,0], utm = undefined, /*** ***/ fn init_ColourNames = ( for item in areas do ( local areaColour = (color item.Colour.R item.Colour.G item.Colour.B) colourNames.addKey areaColour item.Name ) ), /*** tilePos is 0-based ***/ fn getContainerWSPos tilePos = ( gridCorner + ((tilePos - 1) * tileSize) + (tileSize / 2.0) ), fn makeBoxContainer mapArea pos contName filePath = ( local offsetPos = getContainerWSPos pos local offsetPos3 = [offsetPos.x, offsetPos.y, 0] local bareBox = box name:contName pos:offsetPos3 --wirecolor:(areaColourMap.getValue mapArea) convertToPoly bareBox --add a basic rage material rageMat = Rage_Shader() RstSetShaderName rageMat "default.sps" barebox.material = rageMat --setAttrs local TXDIdx = getattrIndex "Gta Object" "TXD" setAttr barebox TXDIdx contName local mapContainer = Container() mapContainer.name = contName mapContainer.pos = offsetPos3 mapContainer.allowInPlaceEditing = true mapContainer.localDefinitionFilename = filePath mapContainer.displaylabel = true mapContainer.AddNodeToContent bareBox mapContainer.UnloadContainer() local success = mapContainer.SaveContainer false --return mapContainer ), fn getContainerDefsFromBitmap = ( for l=0 to (layoutImage.height - 1) do ( local y = (layoutImage.height - l) local pxLine = getPixels layoutImage [0, y] layoutImage.width for px=1 to pxLine.count do ( for item in areas do ( local areaColour = (color item.Colour.R item.Colour.G item.Colour.B) if (pxLine[px] == areaColour) then ( if (areaGrids.hasKey item.Name) then --exists so add to values ( local vals = areaGrids.getValue item.Name append vals [px, l] areaGrids.setValue item.Name vals ) else ( areaGrids.addKey item.Name #([px, l]) ) ) ) ) ) ), /*** Take a texture tile generated by world machine and return a container name ***/ fn resolveContainerTileFromWorldMachineTile wmName = ( local regex = dotnetclass "System.Text.RegularExpressions.Regex" local re_IgnoreCase = (dotNetClass "System.Text.RegularExpressions.RegexOptions").IgnoreCase local xString = regex.match wmName "_x([0-9]+)" re_IgnoreCase local xCoord = xString.groups.item[1].value as Float local yString = regex.match wmName "_y([0-9]+)" re_IgnoreCase local yCoord = yString.groups.item[1].value as Float --coords are 0-based, so add 1 local newCoords = [(xCoord + 1), (yCoord + 1)] local geoLoc = this.resolveCoordsToGeoLocation newCoords ), /*** Convert wm tile textures to container versions ***/ fn worldMachineTileConversion sourceDir targetDir prefix:"" suffix:"" srcExtension:"" targetExtension:"" mode:#copy = ( --get tif files from the sourceDir local srcFiles = getFiles (sourceDir + srcExtension) --copy to the targetDir if (srcFiles.count != 0) then ( for file in srcFiles do ( local newTileName = resolveContainerTileFromWorldMachineTile (getFilenameFile file) local targetFilePath = targetDir + prefix + newTileName + suffix + targetExtension print targetFilePath if (doesFileExist targetFilePath) then ( gRsPerforce.add_or_edit targetFilePath try ( copyFile file targetFilePath ) catch ( print "Problem copying file" ) ) else ( try ( gRsPerforce.add_or_edit #(targetFilePath) copyFile file targetFilePath ) catch ( print "Problem copying file" ) ) ) ) ), /*** pixel coords start form 0,0 so coords - 1 for getPixel ***/ fn resolveCoordsToGeoLocation coords = ( if (colourNames.keys()).count == 0 then ( init_ColourNames() ) --correct for map offset coords += this.mapTileOffset format "Coords: % \n" coords --get colour from bitmap local px = (getPixels layoutImage [(coords.x - 1), (layoutImage.height - 1 - (coords.y - 1))] 1)[1] --print px local geoAreaName = undefined local xCoord = alphaGrid[coords.x] local yCoord = (coords.y as Integer) as String local areaName = colourNames.getValue px local areaObj = areaDict.getValue areaName local areaNamePrefix = areaObj.ContainerPrefix geoAreaName = areaNamePrefix + xCoord + "_" + yCoord --return area and coords return (toUpper geoAreaName) ), /*** Take a location in the form of a column: row: and return the tile coords return a Point2 ***/ fn resolveGeoLocationToCoords column row = ( columnIdx = findItem alphaGrid (toUpper column) --return [columnIdx, row] ), fn makeContainerName _area _pos = ( local areaObj = areaDict.getValue _area local areaPrefix = areaObj.ContainerPrefix local retName = areaPrefix local yName = if _pos.y < 10 then "0" + ((_pos.y as Integer) as String) else ((_pos.y as Integer) as String) retName += alphaGrid[_pos.x] + "_" + yName retName ), fn renameTileToArea inName = ( local inNameBits = filterString inName "_" local coordsFromName = [(inNameBits[2] as Integer), (inNameBits[3] as Integer)] local geoLocName = resolveCoordsToGeoLocation coordsFromName print geoLocName ), fn makeFilePathName area name = ( area = trimleft area "_" local areaObj = areaDict.getValue area pathRoot = RsConfigGetArtDir() + "models/" + areaObj.ContainerFolderName + "/" pathRoot += name + ".maxc" ), fn getContainerPath containerName = ( local nameBits = filterString containerName "_" local areaPrefix = tolower nameBits[1] local newPath = "" for item in areas where ((trimright item.ContainerPrefix "_") == nameBits[1]) do ( newPath = makeFilePathName item.ContainerFolderName containerName ) newPath ), fn createContainers mapArea: = ( print "Creating containers yo" local tilePositions = #() if (mapArea == unsupplied) then --do them all ( for area in (areaGrids.keys()) do ( local areaPositions = areaGrids.getValue area join tilePositions areaPositions for pos in (areaGrids.getValue area) do ( if (pos.x > mapTileOffset.x) and (pos.x < (layoutImage.width - mapTileOffset.x)) and (pos.y > mapTileOffset.y) and (pos.y < (layoutImage.width - mapTileOffset.y)) then ( local name = makeContainerName area pos local filepath = makeFilePathName area name print name print filepath --makeBoxContainer mapArea pos name filepath ) ) ) ) else ( for pos in (areaGrids.getValue mapArea) do ( if (pos.x > mapTileOffset.x) and (pos.x < (layoutImage.width - mapTileOffset.x)) and (pos.y > mapTileOffset.y) and (pos.y < (layoutImage.width - mapTileOffset.y)) then ( local name = makeContainerName mapArea pos local filepath = makeFilePathName mapArea name print name print filepath --makeBoxContainer mapArea pos name filepath ) ) ) ), on create do ( ) ) /* struct HeightMapMesher ( ) */ --HMDiceAndMesh @"X:\gta5_liberty\art\dev\models\Working_files\Tim Flowers\World Machine Files\Master Outputs for Play area\Mainplay_HM.tif" 16 16 if (HeightMapMesherUI != undefined) do HeightMapMesherUI.dispose() struct HeightMapMesherUI ( -- LOCALS ------------------------------------------------ verNum = "1.02", verName = "Donkey Rhubarb", MainWindow, dataModel, UISettings = rsta_userSettings app:"HeightMapMesher", settingProps = #("DivisionsX", "DivisionsY", "LODLevel", "HeightMapStrength", "HeightMapPXScale", "WorldOriginX", "WorldOriginY", "mapOffsetX", "mapOffsetY", "mapOffsetZ", "OutputTexturePath", "MaterialIDPath"), attrExclusions = #(#activegrid, #HeightMapImagePath, #ReferenceImagePath, #maps), toolSettings, containerGridSettings, versionNum = 1.15, versionName = "Withering Slights", --numBlocksX = 8, --numBlocksY = 8, heightMapPath = "", heightMapBitmap = undefined, diffuseMapPath = "", diffuseMapBitmap = undefined, normalMapPath = "", normalMapBitmap = undefined, --textureOutputPath = "", texOverwrite = false, materialTexturePath = "", blockSizeX = undefined, blockSizeY = undefined, --LODLevel = 1, maxLODLevels = 2, LODSuffixes = #("_HD", "_LOD", "_LOD", "_LOD", "_LOD", "_LOD"), --heightMapPXScale = 3.662, --1 px == 1m --heightMapPXScale = 1.0, --1 px == 1m --heightMapStrength = 1600, --mapOffset = [0, 0, 0], blockArray = #(), newBlocks = #(), meshResX, meshResY, HMDisplaceWidth, HMDisplaceLength, blockSizeX, blockSizeY, HMDisplace, bm, nm, bmDirty = true, normalProxy, --mapUI activeCellList, activeCellListIt, activeRenderCells = #(), selectionPNGPath = (getDir #temp) + "/HeightMapMesherSelection.png", cancelOP = false, applyMaterials = false, defaultMaterial = Standardmaterial(), ContainerStruct,-- = ContainerGridSetup(), tileDataDict = dotNetObject "System.Collections.HashTable", tileDataPath = "c:/temp/gta5_liberty_tiles.xml", globalMultiSub = undefined, currentTile, /*** ***/ fn storeUISettings = ( if (dataModel != undefined) then ( for attr in (getPropNames dataModel) where ((findItem attrExclusions attr) == 0) do ( UISettings.setValue (attr as String) (getProperty dataModel attr) ) ) ), /*** ***/ fn restoreUISettings = ( for attr in (getPropNames dataModel) where ((findItem attrExclusions attr) == 0) do ( local val = UISettings.getValue (attr as String) if(val != undefined) then ( setProperty dataModel (attr as String) val ) ) ), /*** ***/ fn resetActiveRenderCells = ( activeRenderCells = #() activeRenderCells = for i=1 to this.dataModel.DivisionsX collect (for j=1 to this.dataModel.DivisionsY collect false) ), /*** ***/ fn createDebugPlane = ( local debugPlane = Plane name:"DebugPlane" typeInLength:4096.5 typeInWidth:4096.5 length:4096.5 width:4096.5 pos:[0,0,0] isSelected:on local mat = Standardmaterial() mat.diffuseMap = Bitmaptexture fileName:normalMapPath debugPlane.material = mat ), fn getGeoLocationFromTile coords = ( ContainerStruct.resolveCoordsToGeoLocation coords ), /*** get a Tile world space position ***/ fn getTilePosition x y = ( if (bm == undefined) then ( this.loadTerrainBitmaps() ) local xPos = (dataModel.mapOffsetX - ((bm.width * dataModel.heightMapPXScale) / 2.0)) - (blockSizeX / 2.0) + (x * blockSizeX) local halfImageOffsetY = ((bm.height * dataModel.heightMapPXScale) / 2.0) local originY = dataModel.mapOffsetY - halfImageOffsetY local invertY = dataModel.DivisionsY - y local yPos = originY + (blockSizeY / 2.0) + (invertY * blockSizeY) local zPos = dataModel.mapOffsetZ --return [xPos, yPos, zPos] ), /*** Parameters: size: Point2 LODLevel: integer ***/ fn createBaseTile size LODLevel = ( local planeMesh = Plane Length:size.x Width:size.y lengthsegs:1 widthsegs:1 mapCoords:true addModifier planeMesh (TurboSmooth()) --select planeMesh --max modify mode --planeMesh.modifiers[#Subdivide].showAllTriangles = true --planeMesh.modifiers[#Subdivide].size = dataModel.heightMapPXScale local iterationCount = (log meshResX) / (log 2.0) - (LODLevel - 1) planeMesh.Modifiers[#TurboSmooth].iterations = iterationCount --collapseStack planeMesh --retval return planeMesh ), /*** ***/ fn makeBlockXY x y LODLevel suppliedName: = ( --break() --plane only goes upto 1000 quads on a side so need subdivide to get to higher levels --local blockMesh = Plane Length:blockSizeX Width:blockSizeY lengthsegs:meshResY widthSegs:meshResX mapCoords:true local blockMesh = createBaseTile [blockSizeX, blockSizeY] LODLevel local pos = ContainerStruct.getContainerWSPos [x, y] pos = [pos.x, pos.y, dataModel.MapOffsetZ] blockMesh.pos = pos blockMesh.wirecolor = (color (random 0 255) (random 0 255) (random 0 255)) blockMesh.material = defaultMaterial local geoLocation = ContainerStruct.resolveCoordsToGeoLocation [x,y] --name the Plane if suppliedName != unsupplied then ( blockMesh.name = suppliedName ) else ( --blockMesh.name = "Tile_" + ((x as Integer) as String) + "_" + ((y as Integer) as String) + this.LODSuffixes[this.LODLevel] blockMesh.name = (getGeoLocationFromTile [x, y]) + LODSuffixes[LODLevel] ) --connect displace modifier bindSpaceWarp blockMesh HMDisplace print blockMesh.name --add the block to the array --append blockArray (DataPair tilePos:[x, y] mesh:blockMesh) --check if the TileData for this location exists already local thisLocationTileDataExists = false local locationTileData = undefined for item in blockArray do ( if(item.containerName == geoLocation) then ( thisLocationTileDataExists = true locationTileData = item ) ) if not thisLocationTileDataExists then ( append blockArray (TileData position:[x,y] containerName:geoLocation meshes:#((DataPair mesh:blockMesh LOD:LODSuffixes[LODLevel]))) ) else ( append locationTileData.meshes (DataPair mesh:blockMesh LOD:LODSuffixes[LODLevel]) ) ), /*** ***/ fn getNMapPixelFromVertexPos vPos = ( if (nm == undefined) do ( return undefined ) local ret = undefined --check bounds --if vPos.x > HMDisplace.width --find pixel location local dX = abs(((HMDisplaceWidth - 1.0) / 2.0) - vPos.x) local dy = abs(((HMDisplaceLength - 1.0) / 2.0) - vPos.y) local pX = abs((HMDisplaceWidth - 1.0) - ((dx / dataModel.heightMapPXScale) * ((HMDisplaceWidth - 1.0) / nm.width))) local pY = (dy / dataModel.heightMapPXScale) * ((HMDisplaceLength - 1.0) / nm.height) --format "pX: % pY: % \n" pX pY local colour = (getPixels nm [pX, pY] 1)[1] --0.00392157 = map colour fomr 0-255 to 0-1 ret = [(2.0 * (colour.r * 0.00392157) - 1.0), (2.0 * (colour.g * 0.00392157) - 1.0 ), (2.0 * (colour.b * 0.00392157) - 1.0)] --print ret --ret = [(colour.r * 0.00392157), 0.0, 1.0] --return ret ), /*** Uses the normal proxy object to sample a normal in the displace space to apply to a vertex. Returns the normal. ***/ fn getNormalFromVertexPos vPos = ( --move the normalProxy to the vert pos normalProxy.pos = vPos --local snapmesh = snapshotAsMesh normalProxy --get the normal for the centre vert(5) norm = getnormal normalProxy 5 --snapmesh = undefined --return norm ), /*** ***/ fn setBorderNormals blockMesh = ( --get the border edges --polyop.getOpenEdges blockMesh --convert to vertex selection local verts = (polyop.getVertsUsingEdge blockMesh (polyop.getOpenEdges blockMesh)) as Array local vertPosList = for vtx in verts collect polyop.getVert blockMesh vtx node:blockMesh --local verts = #{1..blockMesh.numverts} local normalMod = Edit_Normals() addModifier blockmesh normalMod normalMod.MakeExplicit selection:(verts as BitArray) --max modify mode modPanel.setCurrentObject normalMod --Check for an existing normalProxy, fail if its undefined if(normalProxy == undefined) then ( messageBox "Normal Proxy Not created!" title:"Error - Normal Proxy" return false ) --get the position of each vertex and get the normal from the normalmap --local vtxCounter = 0 --local aggTime = 0 with undo off ( for vtx=1 to verts.count do ( --if keyboard.escPressed then cancelOP = true if keyboard.escPressed then ( cancelOP = false return false ) else ( --local startTime = timeStamp() --get position local vPos = vertPosList[vtx] --get the normal local normalVec = getNormalFromVertexPos vPos --local normalVec = [0,0,1] --set the vertex normal to the normal vec local vtxNormals = #{} normalMod.ConvertVertexSelection #{verts[vtx]} vtxNormals vtxNormals = vtxNormals as array for idx = 1 to vtxNormals.count do ( normalMod.SetNormalExplicit vtxNormals[idx] normalMod.setNormal vtxNormals[idx] normalVec ) --aggTime += (timeStamp() - startTime) ) /* vtxCounter += 1 if vtxCounter == 100 then ( format "100 verts, time per vert: % \n" ((aggTime / 100.0) * 0.001) vtxCounter = 0 ) */ ) ) --collapse it collapseStack blockMesh OK ), /*** Set the resolution of the terrain tile to be generated. ***/ fn setMeshRes LODLevel = ( if (this.bm != undefined) then ( meshResX = this.bm.width / dataModel.DivisionsX / (2^(dataModel.LODLevel-1)) format "meshResX: %\n" meshResX meshResY = this.bm.height / dataModel.DivisionsY / (2^(dataModel.LODLevel-1)) ) ), /*** Load the bitmaps from the paths supplied ***/ fn loadTerrainBitmaps = ( if(bm == undefined) or (bmDirty) do ( --local mapImage = dataModel.HeightMapImagePath.AbsolutePath this.heightMapPath = dataModel.HeightMapImagePath.AbsolutePath this.bm = openBitmap this.heightMapPath --set vars that depend on bm HMDisplaceWidth = (bm.width * dataModel.heightMapPXScale) + dataModel.heightMapPXScale HMDisplaceLength = (bm.height * dataModel.heightMapPXScale) + dataModel.heightMapPXScale blockSizeX = (bm.width * dataModel.heightMapPXScale) / dataModel.DivisionsX blockSizeY = (bm.height * dataModel.heightMapPXScale) / dataModel.DivisionsY ) ), /*** chop up diffuse or normal textures texOverwrite is set by the overwrite ui checkbox ***/ fn diceTexture type singleTile: overwrite:dataModel.DoOverwriteTextures = ( --local overwrite = false local baseName = "Tile_" local LODName = LODSuffixes[dataModel.LODLevel] local bmTiles = #() local fullBM = case type of ( "D": ( diffuseMapBitmap ) "N": ( normalMapBitmap ) ) --check we dont have an undefined value for fullBM. --abort if we do if (fullBM == undefined) then return false local tilePixelsX = fullBM.width / dataModel.DivisionsX local tilePixelsY = fullBM.height / dataModel.DivisionsY if not (isDirectoryWriteable dataModel.OutputTexturePath) then ( if (querybox "Folder does not exist, create it?") then ( makedir dataModel.OutputTexturePath ) ) --Doing a tile at a time if (singleTile != unsupplied) then ( local texturePathExists = #{} local counter = 1 --local outputFileName = (textureOutputPath + "/" + baseName + ((singleTile.x as Integer) as String) + "_" + ((singleTile.y as Integer) as String) + LODName + "_" + type +".bmp") local geoLocationName = getGeoLocationFromTile [singleTile.x, singleTile.y] local outputFileName = (dataModel.OutputTexturePath + "/" + geoLocationName + LODName + "_" + type +".bmp") if (doesFileExist outputFileName) then ( texturePathExists[counter] = true ) --if the overwrite checkbox in the ui is not set but we find textures to overwrite anyway then prompt the user to action. if (texturePathExists.numberSet > 0) and not overwrite then ( case (yesNoCancelBox ("Some or all " + type + " textures already exist, do you want to overwrite them?")) of ( #yes: ( overwrite = true ) #no: ( return false ) #cancel: ( return false ) ) ) --chop out the tile from the main image. local tileBM = bitmap tilePixelsX tilePixelsY pasteBitmap fullBM tileBM (box2 ((singleTile.x - 1) * tilePixelsX) (fullBM.height - (singleTile.y * tilePixelsY)) tilePixelsX tilePixelsY) [0, 0] --set the filename tileBM.filename = outputFileName --Take action based on the texture already exists and the overwrite state. if texturePathExists[counter] and overwrite then ( try ( deleteFile outputFileName ) catch ( messagebox "There was a problem deleting that file can you try to do it manually?" ShellLaunch "Explorer.exe" outputFileName ) --save the bitmap save tileBM free tileBM ) else ( --save the bitmap save tileBM free tileBM ) counter += 1 ) else --make all the tiles ( --build a file path list to see if they need overwriting local texturePathExists = #{} local counter = 1 for x=0 to (this.dataModel.DivisionsX - 1) do ( for y=0 to (this.dataModel.DivisionsY - 1) do ( if (doesFileExist (dataModel.OutputTexturePath + "/" + baseName + (x as String) + "_" + (y as String) + LODName + "_" + type +".bmp")) then ( append texturePathExists counter ) counter += 1 ) ) if (texturePathExists.numberSet > 0) and not overwrite then ( case (yesNoCancelBox ("Some or all " + type + " textures already exist, do you want to overwrite them?")) of ( #yes: ( overwrite = true ) #no: ( return false ) #cancel: ( return false ) ) ) --create texture tiles local counter = 1 for x=0 to (this.dataModel.DivisionsX - 1) do ( for y=0 to (this.dataModel.DivisionsY - 1) do ( local tileBM = bitmap tilePixelsX tilePixelsY pasteBitmap fullBM tileBM (box2 (x * tilePixelsX) (fullBM.height - ((y+1) * tilePixelsY)) tilePixelsX tilePixelsY) [0, 0] --tile x+1 y+1 as the image is 0 based but mesh tiles are 1 based --append bmTiles (DataPair tile:[(x+1),(y+1)] bitmap:tileBM) local outputFileName = (dataModel.OutputTexturePath + "/" + baseName + ((x+1) as String) + "_" + ((y+1) as String) + LODName + "_" + type +".bmp") tileBM.filename = outputFileName if texturePathExists[counter] and overwrite then ( try ( deleteFile outputFileName ) catch ( messagebox "There was a problem deleting that file can you try to do it manually?" ShellLaunch "Explorer.exe" outputFileName ) --save the bitmap save tileBM free tileBM ) else ( --save the bitmap save tileBM free tileBM ) counter += 1 ) ) ) true ), /*** Create the Displacement SpaceWarp ***/ fn createSpaceWarp = ( HMDisplaceWidth = (bm.width * dataModel.heightMapPXScale) + dataModel.heightMapPXScale HMDisplaceLength = (bm.height * dataModel.heightMapPXScale) + dataModel.heightMapPXScale blockSizeX = (bm.width * dataModel.heightMapPXScale) / dataModel.DivisionsX blockSizeY = (bm.height * dataModel.heightMapPXScale) / dataModel.DivisionsY format "HMDisplaceWidth: % HMDisplaceHeight: % \n" HMDisplaceWidth HMDisplaceLength HMDisplace = Spacedisplace() HMDisplace.bitmap = bm HMDisplace.lumCenter = 0.0 HMDisplace.length = ((HMDisplaceWidth / 2.0) + 1.0) HMDisplace.width = ((HMDisplaceLength / 2.0) + 1.0) HMDisplace.strength = dataModel.heightMapStrength local posX = ContainerStruct.gridCorner.x + (bm.width * dataModel.heightMapPXScale) / 2.0 local posY = ContainerStruct.gridCorner.y + (bm.height * dataModel.heightMapPXScale) / 2.0 local posZ = dataModel.mapOffsetZ HMDisplace.pos = [posX, posY, posZ] --HMDisplace.pos = [(dataModel.mapOffsetX + (HMDisplaceWidth / 2.0)), (dataModel.mapOffsetY + (HMDisplaceLength / 2.0)), dataModel.mapOffsetZ] ), /*** ***/ fn processSetup = ( free this.blockArray --load bitmaps loadTerrainBitmaps() --set the container struct gridcorner from the world origin + map Offset ContainerStruct.gridCorner = [(dataModel.WorldOriginX + dataModel.MapOffsetX), (dataModel.WorldOriginY + dataModel.MapOffsetY)] --TODO --These values need to be 'discovered' rather than hard coded -- --ContainerStruct.tileSize = 512 --ContainerStruct.mapTileOffset = [4,4] --Set mesh resolution for the desired LOD level setMeshRes dataModel.LODLevel --for each block chop it out of the main image to a new bitmap, but add a border of 1 pixel --if one or two sides are on the edge then replicate the border pixels --create displace spacewarp createSpaceWarp() activeCellListIt = dataModel.ActiveGrid.Keys.GetEnumerator() /* while activeCellListIt.MoveNext() do ( local gridVal = activeCellListIt.Current --gridVal Y needs flipping due to grid coord system is inverse gridVal.Y = dataModel.DivisionsY - gridVal.Y + 1 format "x:% y:% \n" ((gridVal.X as Integer) as String) ((gridVal.Y as Integer) as String) makeBlockXY (gridVal.X as Integer) (gridVal.Y as Integer) ) */ storeUISettings() ), /*** ***/ fn processNextTile = ( print "processNextTile" if (activeCellListIt == undefined) then ( activeCellListIt = dataModel.ActiveGrid.Keys.GetEnumerator() ) if activeCellListIt.MoveNext() then ( print "next one" local gridVal = activeCellListIt.Current --gridVal Y needs flipping due to grid coord system is inverse gridVal.Y = dataModel.DivisionsY - gridVal.Y + 1 format "x:% y:% \n" ((gridVal.X as Integer) as String) ((gridVal.Y as Integer) as String) if dataModel.DoHD then ( makeBlockXY (gridVal.X as Integer) (gridVal.Y as Integer) dataModel.LODLevel ) --are we doing a LOD as well if dataModel.DoLODs then ( makeBlockXY (gridVal.X as Integer) (gridVal.Y as Integer) dataModel.SubLODLevel ) return true ) else --reached the end, reset the activeCellListIt ( print "all done" --activeCellListIt = undefined return false ) ), /*** ***/ fn HMDiceAndMesh = ( processSetup() local moreToGo = true while moreToGo do ( moreToGo = processNextTile() ) ), /*** ***/ fn createCollisionMesh inMesh = ( local givenName = inMesh.name + "_col" local outMesh = copy inMesh convertToMesh outMesh local savetrans = outMesh.transform newsubmat = RexBoundMtl() newsubmat.name = "Default" submatlist = #(newsubmat) newmat = Multimaterial() newmat.numsubs = submatlist.count newmat.materialList = submatlist outMesh.material = newmat --check the count for the mesh.If its bigger than allowed we need to chop it up. -- Split up mesh until it has fewer than the max number of verts/faces: local NewObjs = #(outMesh) local RedoLast = false local doSplit = false local NewObjNum = 0 while(NewObjNum < NewObjs.count) do ( if not RedoLast then ( NewObjNum += 1 ) local ThisNewObj = NewObjs[NewObjNum] local NumVerts = (GetNumVerts ThisNewObj.Mesh) local NumFaces = (GetNumFaces ThisNewObj.Mesh) local OverVerts = (NumVerts > RsMaxCollVerts) local OverFaces = (NumFaces > RsMaxCollFaces) doSplit = (OverVerts or OverFaces) --format "%%: Split:% Verts:(% > % = %) Faces:(% > % = %)\n" ObjPrintTabs ThisNewObj.Name doSplit NumVerts RsMaxCollVerts OverVerts NumFaces RsMaxCollFaces OverFaces if (doSplit) then ( RsMesh_SplitLongestAxis ThisNewObj &NewObjs selectionMode:true RedoLast = true ) else ( RedoLast = false ) ) -- Convert newly-split meshes to collision, and apply attributes to each object: for NewObj in NewObjs do ( mesh2col NewObj NewObj.parent = inMesh ) --set attrs gRsCollTypes.setObjCollTypeFlags NewObjs "Mover|Weapons|Horse|Vehicle|Cover" --) --mesh2col outMesh --outMesh.transform = savetrans --outMesh.name = givenName --return --outMesh ), /*** ***/ /* fn createMaterial blockName = ( --break() local rageMaterial = Rage_Shader() --assign normal shader RstSetShaderName rageMaterial "normal.sps" --get the textures and assign them to the slots --local blockNameBits = filterString blockName "_" --"TerrainTile_1_8_SLOD1_Diff.bmp" local tileDiffTextureName = blockName + "_D.bmp" --diffuseMap local diffusePath = pathConfig.appendPath materialTexturePath tileDiffTextureName if not (doesFileExist diffusePath) then ( messagebox "This texture path does not exist" title:"Bad Path" return undefined ) else ( RstSetVariable rageMaterial 1 diffusePath ) --local tileNormTextureName = "Norm" + tileName local tileNormTextureName = blockName + "_N.bmp" --normalMap local normalPath = pathConfig.appendPath materialTexturePath tileNormTextureName if not (doesFileExist normalPath) then ( messagebox "This texture path does not exist" title:"Bad Path" return undefined ) else ( RstSetVariable rageMaterial 9 normalPath ) --return the new material rageMaterial ), */ /*** Set the textures in the relevant slots for the material using the textures found in the path set in the UI ***/ fn setMaterialTextures theMaterial shaderType = ( --local theMaterial = theMesh.material --go through the material ID and for each RageMaterial --set the diffuse and normal maps for the currentTile local tintTexturePathRoot = (dataModel.GetMapDataByName "Tint Map").SourcePath local tintTexturePath = RsMakeSafeSlashes(tintTexturePathRoot + "/" + currentTile.containerName + "_T" + dataModel.DefaultTextureExtension ) local tintNormalPathRoot = (dataModel.GetMapDataByName "TintNormal Map").SourcePath local tintNormalTexturePath = RsMakeSafeSlashes( tintNormalPathRoot + "/" + currentTile.containerName + "_TN" + dataModel.DefaultTextureExtension ) --set textures dependent on shaderType case shaderType of ( #ttn: ( if (isKindOf theMaterial Multimaterial) then ( for subMat in theMaterial.materialList do ( rsta_rageMat.setVarByName subMat "Tint[rgb] + Tint blend[a]" tintTexturePath rsta_rageMat.setVarByName subMat "Tint bump" tintNormalTexturePath ) ) else if (isKindOf theMaterial Rage_Shader) then ( rsta_rageMat.setVarByName theMaterial "Tint[rgb] + Tint blend[a]" tintTexturePath rsta_rageMat.setVarByName theMaterial "Tint bump" tintNormalTexturePath ) ) #normal: ( if (isKindOf theMaterial Multimaterial) then ( for subMat in theMaterial.materialList do ( rsta_rageMat.setVarByName subMat "Diffuse Texture" tintTexturePath rsta_rageMat.setVarByName subMat "Bump Texture" tintNormalTexturePath ) ) else if (isKindOf theMaterial Rage_Shader) then ( rsta_rageMat.setVarByName theMaterial "Diffuse Texture" tintTexturePath rsta_rageMat.setVarByName theMaterial "Bump Texture" tintNormalTexturePath ) ) ) ), /*** ***/ fn setGtaAttributes inMesh tileData LODLevel = ( --getINISetting toolSettings.GtaAttrsINIPath local TXDAttrIdx = GetAttrIndex "Gta Object" "TXD" local LODDistIdx = GetAttrIndex "Gta Object" "LOD distance" --set attrs based on mesh lod if (LODLevel == #HD) then ( for item in toolSettings.GtaAttrs.HD do ( local attrName = (getpropnames item)[1] as String local attrQueryName = substituteString attrName "_" " " local attrIdx = GetAttrIndex "Gta Object" attrQueryName local attrVal = getproperty item attrName setAttr inMesh attrIdx attrVal ) --Specials --setAttr inMesh LODDistIdx 800 setAttr inMesh TXDAttrIdx tileData.ContainerName ) else if (LODLevel == #LOD) then ( for item in toolSettings.GtaAttrs.LOD do ( local attrName = (getpropnames item)[1] as String local attrQueryName = substituteString attrName "_" " " local attrIdx = GetAttrIndex "Gta Object" attrQueryName local attrVal = getproperty item attrName setAttr inMesh attrIdx attrVal ) --Specials --setAttr inMesh LODDistIdx 8000 setAttr inMesh TXDAttrIdx (tileData.ContainerName + "_LOD") ) ), /*** Create a final baked mesh from a passed in processed mesh ***/ fn createBakeMesh tile LODName = ( local cname = ContainerStruct.resolveCoordsToGeoLocation tile.position local originalMesh = tile.getRenderMesh LODName local newMesh = snapshot originalMesh convertTo newMesh PolyMeshObject originalMesh.name += "TRASH" newMesh.name = tile.containerName + LODName tile.containerPath = ContainerStruct.getContainerPath tile.containerName setBorderNormals newMesh CenterPivot newMesh ResetXForm newMesh collapseStack newMesh --add it to a collection --print newMesh --tile.meshes[1].mesh = newMesh tile.setRenderMesh LODName newMesh --delete the old one delete originalMesh --return successful? newMesh ), fn objExport theMesh = ( print(classOf theMesh) print "Attempting to export an obj mesh..." --local exportPath = toolSettings.objExportPath + theMesh.name + ".obj" local exportPath = dataModel.OBJOutputPath + "/" + theMesh.name + ".obj" --find if there one there already and get the datstamp so we can check if it got overwritten local depotPath = gRsPerforce.local2Depot exportPath local fStat = gRsPerforce.getFileStats depotPath if (fStat != undefined) and (fStat.count != 0) then --it exists already ( gRsPerforce.add_or_edit exportPath ) local fileHash = undefined if (doesFileExist exportPath) then ( fileHash = getHashValue (getFileModDate exportPath) 1 ) local objIni = ObjExp.getIniName() --exportFile [ #noPrompt ] [ selectedOnly: ] [ using: ] select theMesh local result = exportFile exportPath #noPrompt selectedOnly:true using:ObjExp print result /* if (doesFileExist exportPath) then ( local newFileHash = getHashValue (getFileModDate exportPath) 1 if (newFileHash != fileHash) then --it worked ( print "Successful save" return true ) else ( print "Fail save" return false ) ) else ( print "Fail save" return false ) */ result ), /*** ***/ fn decimateBlock theMesh LODLevel = ( local optMod = ProOptimizer() --What settings should we use addModifier theMesh optMod local appliedMod = theMesh.modifiers[#ProOptimizer] --max modify mode --modPanel.setCurrentObject appliedMod select theMesh appliedMod.Calculate = off appliedMod.Calculate = on --appliedMod.VertexPercent = toolSettings.proOptimizerVertexPercent case LODLevel of ( #HD:appliedMod.VertexPercent = dataModel.DecimateHDAmount #LOD:appliedMod.VertexPercent = dataModel.DecimateLODAmount ) --collapse it? format "optimised? % \n" appliedMod.Calculate as String if appliedMod.Calculate then collapseStack theMesh ok ), /*** ***/ fn gridSlicemesh theMesh = ( /* local gridSlice = RsGridSlice() gridSlice.loadPresets() local preset = gridSlice.presets.Item["Bob Terrain"].value gridSlice.activePreset = preset gridSlice.slice theMesh ok */ ), /*** ***/ fn findCollectionBounds contentNodes = ( local aMinBounds = #() local aMaxBounds = #() for item in contentNodes do ( local itemBounds = NodeGetBoundingBox item (matrix3 1) append aMinBounds itemBounds[1] append aMaxBounds itemBounds[2] ) local xBoundsMin = for val in aMinBounds collect val.x local xBoundsMax = for val in aMaxBounds collect val.x local yBoundsMin = for val in aMinBounds collect val.y local yBoundsMax = for val in aMaxBounds collect val.y local zBoundsMin = for val in aMinBounds collect val.z local zBoundsMax = for val in aMaxBounds collect val.z local minBounds = [(amin xBoundsMin), (amin yBoundsMin), (amin zBoundsMin)] local maxBounds = [(amax xBoundsMax), (amax yBoundsMax), (amax zBoundsMax)] return #(minBounds, maxBounds) ), /*** ***/ fn createContainer coords suppliedName: = ( --Create/add - load container to add the tile to local containerName = "" local tilePos if(suppliedName != unsupplied) then ( containerName = suppliedName local nameBits = filterString containerName "_" local column = nameBits[2] local row = nameBits[3] tilePos = ContainerStruct.resolveGeoLocationToCoords column row ) else ( containerName = ContainerStruct.resolveCoordsToGeoLocation coords tilePos = coords ) --what area does this container live local localContainerPath = ContainerStruct.getContainerPath containerName --does it exist in perforce local depotPath = gRsPerforce.local2Depot localContainerPath local fStat = gRsPerforce.getFileStats depotPath if (fStat != undefined) and (fStat.count != 0) then --it exists already ( gRsPerforce.add_or_edit localContainerPath --open the container local containerNode = Containers.CreateInheritedContainer localContainerPath select containerNode --edit in place actionMan.executeAction -1172021248 "13" --on create make sure container position matches the block centre position --local worldPos = getTilePosition tilePos.x tilePos.y local worldPos = ContainerStruct.getContainerWSPos [tilePos.x, tilePos.y] if(containerNode.pos != worldPos) then --adjust the position ( print "adjusting position" --remove the contained nodes whilst we move the container itself local contentNodes = #() containerNode.GetContentNodes false &contentNodes for item in contentNodes do ( containerNode.RemoveNodeFromContent item true ) --re-centrer the containerNode on the contentBounds local contentBounds = findCollectionBounds contentNodes local boundsCentre = contentBounds[1] + 0.5 * (contentBounds[2]-contentBounds[1]) setTransformLockFlags containerNode #none containerNode.pos = boundsCentre --Re-add the nodes -- for item in contentNodes do -- ( containerNode.AddNodesToContent contentNodes -- ) --Now set the containerNode to the bounds position containerNode.pos = [worldPos.x, worldPos.y, containerNode.pos.z] setTransformLockFlags containerNode #all ) --Prompt Whether to replace a mesh with the same name exists already in that container --save the container actionMan.executeAction -1172021248 "13" --add it to a container changelist ) else --Create a new one ( --get tileData --local data = tileDataDict.Item[containerName].value local mapArea = (filterString containerName "_")[1] local newContainer = ContainerStruct.makeBoxContainer mapArea tilePos containerName localContainerPath gRsPerforce.add_or_edit localContainerPath ) ), /*** Get the global multimaterial from a max file object currently Until we define it in a better way, depending on how shaders and presets get implemented ***/ fn getGlobalMaterial = ( local libertyMm = for item in (getClassInstances MultiMaterial) where (item.name == "Liberty_Master") collect item if (libertyMm.count == 0) then --its not loaded ( mergeMaxFile (RsConfigGetArtDir() + toolSettings.globalMultiSubPath) #select --get the material off it globalMultiSub = selection[1].material ) ), /*** ***/ fn getLODMaterial = ( local mat = Rage_Shader() --set shader type RstSetShaderName mat "normal.sps" setMaterialTextures mat #normal mat ), /*** ***/ fn ultimateMapMesh renderMesh = ( if (utm == undefined) then ( utm = UltimateTerrainMapperStruct() ) select renderMesh print selection[1] local utmsetting = toolSettings.UltimateTerrainMapper utm.MapObject renderMesh utmsetting.utm_channel1 utmsetting.utm_mapOp1_width utmsetting.utm_mapOp1_length utm.MapObject renderMesh utmsetting.utm_channel2 utmsetting.utm_mapOp2_width utmsetting.utm_mapOp2_length --convertToPoly renderMesh ), /*** Apply any other mapping required ***/ fn applyOtherMapping renderMesh = ( addModifier renderMesh (UVWMap()) renderMesh.modifiers[#UVWMap].Length = 512.0 renderMesh.modifiers[#UVWMap].Width = 512.0 renderMesh.modifiers[#UVWMap].mapChannel = 3 collapseStack renderMesh ), /*** ***/ fn projectMatIds renderMesh tileData = ( --get the texture path for the matId texture local matIDRootPath = (dataModel.GetMapDataByName "MatID Map").SourcePath local matIDTexturePath = RsMakeSafeSlashes (matIDRootPath + "/" + tileData.containerName + ".tif") if not (doesFileExist matIDTexturePath) then --try to sync it ( gRsPerforce.sync #(matIDTexturePath) if not (doesFileExist matIDTexturePath) then ( return false ) ) local mID = mapIDProjector() mID.project renderMesh matIDTexturePath if dataModel.DoMaterialIDBlending then ( if (classOf renderMesh != Editable_Poly) then ( convertToPoly renderMesh ) mID.blendVerts renderMesh #(2,4) ) return true ), /*** Mesh chopper ***/ fn meshChop obj chopSize:256 namePrefix:"test" = ( if (obj == undefined) then ( return false ) local meshBits = #() ConvertToPoly obj local chopBitCount = 0 chopSize = chopSize as Float local objBB = nodeGetBoundingBox obj (Matrix3 1) local bbWidth = objBB[2].x - objBB[1].x local bbHeight = objBB[2].y - objBB[1].y local widthRatio = bbWidth / chopSize local heightRatio = bbHeight / chopSize local halfWidthOffset = (bbWidth/2.0)+(chopSize/2.0) local halfHeightOffset = (bbHeight/2.0)+(chopSize/2.0) local columns = (ceil widthRatio) as integer local rows = (ceil heightRatio) as integer setCommandPanelTaskMode #modify modPanel.setCurrentObject obj CopyAttrs obj --setup modifiers on source obj local vs = volumeSelect() vs.level = 2 --face vs.type = 1 --crossing vs.method = 0 --replace vs.volume = 0 --box addModifier obj vs local gizmoScale = [(1.0 / widthRatio), (1.0 / heightRatio), 1] vs.Gizmo.scale = gizmoScale --move it for now vs.gizmo.pos = [(bbWidth * 2.0), (bbHeight * 2.0), 0] local epMod = Edit_Poly() epMod.useStackSelection = on addModifier obj epMod local newGizmoPos = [0,0,0] --store previously selected faces to check against local faceRegister = #{} -- format "rows: % \n" rows -- format "Columns: % \n" columns local gridSeq = spiralArraySequence rows columns for visitor in gridSeq do ( row = visitor.x col = visitor.y modPanel.setCurrentObject obj --newGizmoPos = [0, 0, 0] local newX = (col * chopSize) - halfWidthOffset local newY = (row * chopSize) - halfHeightOffset newGizmoPos = [newX, newY, 0] vs.gizmo.pos = newGizmoPos vs.method = 1 modPanel.setCurrentObject obj.modifiers[1] local facList = EditPolyMod.GetSelection epMod #Face if (facList.numberSet != 0) and (facList.numberSet != obj.numFaces) then ( local faceClash = faceRegister * facList local facesToChop = #{} if faceClash.numberSet > 0 then ( facesToChop = facList - faceClash ) else ( facesToChop = facList ) if (facesToChop.numberSet > 0) and (facesToChop.numberSet != obj.numFaces) then ( local newChop = copy obj collapseStack newChop chopBitCount += 1 newChop.name = namePrefix + "_" + (chopBitCount as string ) --apply the orignals Gta Attrs to the chopped pieces PasteAttrs newChop polyOp.DeleteFaces newChop -facesToChop centerPivot newChop append meshBits newChop faceRegister += facList ) ) ) --clean up delete obj --return the new pieces return meshBits ), /*** ***/ fn lockMesh theMesh = ( setTransformLockFlags theMesh #all ), /*** Append meshes to a container Warn about overwriteing ***/ fn appendMeshesToContainer overwrite:false = ( local containerName = currentTile.containerName --what area does this container live local localContainerPath if (currentTile.containerPath == undefined) then ( localContainerPath = ContainerStruct.getContainerPath containerName ) else ( localContainerPath = currentTile.containerPath ) --does it exist in perforce local depotPath = gRsPerforce.local2Depot localContainerPath local fStat = gRsPerforce.getFileStats depotPath if (fStat != undefined) and (fStat.count != 0) then --it exists already ( gRsPerforce.sync #(localContainerPath) gRsPerforce.add_or_edit localContainerPath --check for container lock file local lockFilePath = (getFilenamePath localContainerPath) + containerName + ".maxc.lock" if (doesFileExist lockFilePath) then ( try ( deleteFile lockFilePath ) catch ( format "Couldnt delete container lock file for container: % \n" lockFilePath gRsUlog.LogError ("Couldnt delete container lock file for container: " + lockFilePath) ) ) --open the container local containerNode = Containers.CreateInheritedContainer localContainerPath select containerNode --edit in place actionMan.executeAction -1172021248 "13" local contentNodes = #() containerNode.GetContentNodes false &contentNodes local contentNodeNames = for item in contentNodes collect item.name --see if any of the meshes already exist by name in the container nodes. local clashes = #() local meshes = for item in currentTile.meshes collect item.mesh for item in meshes do ( --if ((findItem contentNodes item) != 0) then -- if (containerNode.IsNodeInContent item false) then -- ( -- append clashes item -- ) if (findItem contentNodeNames item.name) != 0 then ( append clashes item ) ) --break() --got some clashing nodes, what we gonna do? if (clashes.count != 0) and not dataModel.DoOverWriteContainerContents then --we got some overwrite candidates. ( dataModel.DoOverWriteContainerContents = querybox "There are items of the same name already in this container.\nWant to overwrite them?" title:"Overwrite?" ) else --no clashes so add them ( containerNode.AddNodesToContent meshes ) if (clashes.count != 0) and dataModel.DoOverWriteContainerContents then ( format "dataModel.DoOverWriteContainerContents: % \n" dataModel.DoOverWriteContainerContents --delete the old ones first local clashnames = for item in clashes collect item.name for item in contentNodes do ( if (findItem clashnames item) != 0 then ( delete item --break() ) ) --now add the new ones containerNode.AddNodesToContent meshes ) --save the container actionMan.executeAction -1172021248 "13" --remove it from the scene delete containerNode ) else --create the container ( createContainer() ) ), /*** Main terrain processing macro script ***/ fn CreateTerrainTilesProcess = ( --Setup ulog gRsUlog.init "Create Terrain Tiles" appendToFile:false with undo off ( processSetup() --If no blocks have been done already then generate them if blockArray.count == 0 do ( -- messagebox "Need to process a heightmap first" error:"Missing Blocks" -- processMode = #ready -- return false processSetup() ) --what do we need to do local doLODs = dataModel.DoLODs local doCreateTextures = dataModel.DoTextures local doMaterials = dataModel.DoMaterials local doMaterialIDs = dataModel.DoMaterialIDProjection local doCollision = dataModel.DoCollision local doCreateContainers = dataModel.DoContainers --create the normal proxy sampler local normalProxy = Plane name:"NormalProxy" Length:4 Width:4 lengthsegs:2 widthSegs:2 convertToMesh normalProxy this.normalProxy = normalProxy --connect displace modifier createSpaceWarp() bindSpaceWarp normalProxy this.HMDisplace /*************************************** Block Container setup Macro **************************************/ --progressStart "Creating.." print "Creating.." /* BLOCK */ -- PROGRESS BAR prog = RSProgressWindow Title:"Building Tiles" prog.start() prog.StepProgressValue = 100.0 / (dataModel.ActiveGrid.count) --process each tile at a time and clean up before processing the next local moreToGo do ( moreToGo = processNextTile() format "moreToGo? %\n" moreToGo as String --if not moreToGo then continue if moreToGo and (blockArray.count > 0) then ( currentTile = blockArray[blockArray.count] format "CurrentTile: % \n" currentTile gRsUlog.LogMessage ("CurrentTile: " + currentTile.containerName) PushPrompt ("CurrentTile: " + currentTile.containerName) --Create the render mesh if dataModel.DoHD then ( gRsUlog.LogMessage "Doing HD Tile..." --get a reference to the inital process model so we can delete it after creating the rendermesh local processModel = currentTile.getRenderMesh "_HD" local renderMesh = createBakeMesh currentTile "_HD" format "renderMesh: % \n" renderMesh gRsUlog.LogMessage ("renderMesh: " + renderMesh.name) if dataModel.DoLODs then ( local renderLODMesh = createBakeMesh currentTile "_LOD" format "renderLODMesh: % \n" renderLODMesh gRsUlog.LogMessage ("Made renderLODMesh") ) --delete the process mesh --delete processModel --Export mesh to an OBJ for ZBRush/MudBox if dataModel.DoObjs then ( local success = objExport renderMesh if not success then ( messagebox "Obj Export Failed" Title:"OBJ Export Fail" gRsUlog.LogWarning "Obj Export Failed" ) ) --Decimate with ProOptimize if dataModel.DoDecimateHD then ( decimateBlock renderMesh #HD gRsUlog.LogMessage "Decimate HD mesh" ) /* MATERIALS */ if dataModel.DoMaterials then ( gRsUlog.LogMessage "Apply materials" if (globalMultiSub == undefined) then ( gRsUlog.LogMessage "Getting globale material" getGlobalMaterial() ) --Apply a copy of the global terrain multiSub material renderMesh.material = globalMultiSub --Set the shaders to use the chopped tint and tint_normal textures. setMaterialTextures renderMesh.material #ttn gRsUlog.LogMessage "Set textures on material" --Run the Ultimate Mapper ch1 6x6 ch2 100x100 ultimateMapMesh renderMesh gRsUlog.LogMessage "Ultimate mapper" --map channel3 on all 4 meshes to 512x512 applyOtherMapping renderMesh if dataModel.DoMaterialIDProjection then ( --Trigger the ID Projector for these meshes projectMatIds renderMesh currentTile gRsUlog.LogMessage "Projecting material IDs" ) ) --Set the Gta Attrs on these meshes including setGtaAttributes renderMesh currentTile #HD gRsUlog.LogMessage "Set Gta Attrs" --Run the mesh chopper local renderMeshBits = meshChop renderMesh chopSize:toolSettings.meshChopSize namePrefix:currentTile.containerName gRsUlog.LogMessage "Chop up the HD render mesh" for item in renderMeshBits do ( gRsUlog.LogMessage "Lock transforms on chopped HD render mesh" lockMesh item ) -- COLLISION if dataModel.DoCollision then ( gRsUlog.LogMessage "Doing Collision for HD mesh bits" for meshPiece in renderMeshBits do ( createCollisionMesh meshPiece --collisionmesh.parent = meshPiece ) ) ) -- LODs if dataModel.DoLODs then ( gRsUlog.LogMessage "Generating LODs" --get the LOD mesh for this tile local LODMesh = currentTile.getRenderMesh "_LOD" --Decimate with ProOptimize if dataModel.DoDecimateLOD then ( decimateBlock LODMesh #LOD gRsUlog.LogMessage "Decimated LOD mesh" ) --apply the material LODMesh.material = getLODMaterial() gRsUlog.LogMessage "Applied LOD material" --apply planar mapping addModifier LODMesh (Uvwmap()) --only if HD is done to link to if dataModel.DoHD then ( gRsUlog.LogMessage "Link LODs" if not (RsSceneLink.HasContainer LinkType_LOD LODMesh) then ( RsSceneLink.AddContainer LinkType_LOD LODMesh ) for item in renderMeshBits do ( RsSceneLink.SetParent LinkType_LOD item LODMesh ) ) setGtaAttributes LODMesh currentTile #LOD gRsUlog.LogMessage "LOD Gta Attrs set" collapseStack LODMesh lockMesh LODMesh ) /* CONTAINER */ if dataModel.DoContainers then ( gRsUlog.LogMessage "Append meshes to container" appendMeshesToContainer() /* if dataModel.DoLODs then ( local LODMesh = currentTile.getRenderMesh "_LOD" appendMeshesToContainer LODMesh currentTile ) */ --remove the container from the scene -- for obj in objects where (isKindOf obj Container) do -- ( -- delete obj -- ) --currentTile.deleteMeshes() ) --Clean up --delete renderMeshBits prog.PostProgressStep() ) ) while moreToGo and not keyboard.escPressed --progressEnd() print "Clean Up" delete normalProxy delete this.HMDisplace --delete meshes --delete globalMultiSub --free blockArray prog.End() ) gRsUlog.Validate() clearUndoBuffer() gc() (dotNetClass "System.Gc").Collect() ), /*** ***/ fn loadTileData = ( local xmlIO = rsta_xml_io() xmlIO.xmlFile = tileDataPath xmlIO.load() local tileNodes = xmlIO.root.ChildNodes for t=0 to (tileNodes.Count - 1) do ( local tile = tileNodes.Item t --create a tileData instance local data = TileData() local tileProperties = tile.ChildNodes for i=0 to (tileProperties.count - 1) do ( propNode = tileProperties.Item i case propNode.Name of ( "PositionX":data.position.column = propNode.InnerText "PositionY":data.position.row = propNode.InnerText "ContainerName":data.containerName = propNode.InnerText "ContainerPath":data.containerPath = propNode.InnerText --"ContainerPosition": --"Textures": ) ) local tileKey = data.containerName --add it to the TileData Dictionary tileDataDict.Add tileKey (dotNetMXSValue data) ) ), /*** Load Settings ***/ fn loadToolSettings = ( hmmSettingsAssembly = CSharp.CompileToMemory #(RsConfigGetWildWestDir() + "etc\config\maps\HMM_Tool_Settings.cs") local hmmSet = dotnetclass "HMMSettings" local ser = dotnetobject "System.Xml.Serialization.XmlSerializer" hmmSet stream = dotNetObject "System.IO.StreamReader" (RsConfigGetWildWestDir() + "etc\config\maps\HMM_Tool_Settings.xml") local allSettings = ser.Deserialize(stream); toolSettings = allSettings.items[1] containerGridSettings = allSettings.items[2] stream.close() --print settings ), /*** ***/ fn initContainerStruct = ( ContainerStruct = ContainerGridSetup() ContainerStruct.layoutImage = openBitMap containerGridSettings.LayoutImage ContainerStruct.alphaGrid = for item in containerGridSettings.GridAxes.Columns[1].Column collect item.value ContainerStruct.tileSize = containerGridSettings.TileSize ContainerStruct.areas = containerGridSettings.Areas for item in containerGridSettings.Areas do ( ContainerStruct.areaDict.addKey item.Name item ) local columnOffset = containerGridSettings.GridAxes.ColumnOffset local rowOffset = containerGridSettings.GridAxes.RowOffset ContainerStruct.mapTileOffset = [columnOffset, rowOffset] ContainerStruct.gridCorner = [(dataModel.WorldOriginX + dataModel.MapOffsetX), (dataModel.WorldOriginY + dataModel.MapOffsetY)] ContainerStruct.getContainerDefsFromBitmap() ), ----------------------------------------------------------------------------------- -- WINDOW FUNCTIONS --------------------------------------------- ----------------------------------------------------------------------------------- fn dispose = ( storeUISettings() if (MainWindow != undefined) do ( -- WINDOW POS UISettings.wpf_windowPos mainWindow #set -- CLOSE AND DISPOSE THE WINDOW MainWindow.Close() MainWindow = undefined ) execute ("HeightMapMesherUI = undefined") ), ------------------------------------------------------------------- fn init = ( -- LOAD ASSEMBLY local dllPath = (RsConfigGetToolsDir() + "techart/dcc/3dsMax/HeightMapMesher.dll") if not (doesFileExist dllPath) then ( messageBox "The HeightMapMesher.dll is missing. Exiting" title:"Error: Missing Dll" return false ) dotnet.loadAssembly dllPath -- MAKE MAIN WINDOW INSTANCE MainWindow = dotNetObject "HeightMapMesher.MainWindow" dn_window.setup MainWindow MainWindow.Tag = dotnetmxsvalue this -- PARENTS THE WINDOW UNDER MAX, STOPS IT FROM DROPPING BEHIND MainWindow.ChangeOwner (DotNetObject "System.IntPtr" (Windows.GetMAXHWND())) -- WINDOW POS UISettings.wpf_windowPos mainWindow #get -- SHOW THE WINDOW MainWindow.show() --set title MainWindow.Title = ("HeightMap Mesher | v" + this.verNum + " " + this.verName) loadToolSettings() --set the DataModel alias dataModel = MainWindow.DataModel restoreUISettings() initContainerStruct() ), -- EVENTS ----------------------------------------------------- on create do init() ) HeightMapMesherUI = HeightMapMesherUI()