Files
gtav-src/tools_ng/wildwest/script/3dsMax/Maps/HeightMapMesher.ms
T
2025-09-29 00:52:08 +02:00

2432 lines
61 KiB
Plaintext
Executable File

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:<string> row:<int> 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 <filename_string> [ #noPrompt ] [ selectedOnly:<boolean> ] [ using:<maxclass> ]
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()