2432 lines
61 KiB
Plaintext
Executable File
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()
|
|
|