Import Heightmap for Nameshing with 3dsmax
Tools: 3ds Max 9, BF2 plugin for 3ds Max 9 (This is the last version that has plugins for 3ds Max), notepad or another text editor
This is an expansion to the original plugin 3ds max 9 tools for importing the heightmap of the map. This comes in very handy for adjusting navmeshes to the exact terrain as we all know that maps with smooth valleys and hills are often victims of the 'over-optimizing' in navmesh.exe, in which vehicles are abandoned in valleys and bots stand around there, or even game crashes.
You'll need 3DSMax and the appropriate BF2 toolset for your version of the program. A text editor is needed to modify the scripts.
The scripts are located in <3DSMax>/scripts/bf2
Ok, first open the 'BF2_UI.mcr' file with a texteditor and add the following lines (between two other menu entries) :
buttontext:"BF2 Import Heightmap"
category:"Game Tools BF2"
toolTip:"BF2 Import Heightmap"
I put those lines between BF2GameTools_LMs and BF2GameTools_Batch when using the PoE2 toolset in Max9.
Scroll further down in that file and add the line in the middle:
append ItemsTemp (menuMan.createActionItem
"BF2GameTools_LMs" "Game Tools BF2")
append ItemsTemp (menuMan.createActionItem "BF2Import_Heightmap" "Game Tools BF2")
append ItemsTemp (menuMan.createActionItem "BF2GameTools_Batch"
"Game Tools BF2")
Save the file and close it. Now we need to find the heightmap import function used for the lightmapping toolchain and clone it. Go to /scripts/bf2/levels/Heightmap_Import.ms and open it with your texteditor. Add the following code at the end. ( Rexman has left in a 'TEST' section at the end of the file , insert the code before
that section ) :
-- Creates the scaled terrain mesh with same scale as navmeshes
-- heightmap is flipped added by mschoeldgen aug 2008
fn BF2generatescaledHM worldSize:4096 values:#(1,2,3,4,5,6,7,8,9) =
local res = sqrt values.count
local evenRes = res - 1
local t = mesh width:evenRes length:evenRes lengthSegs:evenRes widthSegs:evenRes
format " res: %\n" res
-- set vertex positions
for y = 0 to evenRes do
tmpYOffset = y * res + 1
for x=0 to evenRes do
setVert t (tmpYOffset + x) [ x, y, values[ tmpYOffset + x ] ]
fn BF2_ImportscaledHeightmapRaw fname tScale:[1,1,1] s:1.0 =
format "HeightmapFile: %\n" fname
local f = fopen fname "rb"
if f == undefined then
format "ERROR! Could not open file: %\n" fname
fseek f 0 #seek_end
local theFileSize = ftell f
format " FileSize: %\n" theFileSize
fseek f 0 #seek_set
local hmRes = (sqrt (theFileSize / 2)) as integer
format " Heightmap res: %\n" hmRes
local hmVals = #()
for y=1 to hmRes do
for x=1 to hmRes do
append hmVals (readShort f #unsigned)
local t = BF2generatescaledHM worldsize:(hmRes * s) values:hmVals
if isValidNode t then
t.scale = tScale * s
fn bf2ImportScaledHeightmap fname:undefined s:1.0=
if fname == undefined then
fname = getOpenFileName types:"HeightData.con(*.con)|HeightData.con|All(*.*)|*.*|"
if fname == undefined then return false
local heightData = bf2parseConData_HeightData fname
local tScale = heightData
local tRawFile = heightData
if tScale == undefined then
format "ERROR! heightmap has no scale value!\n"
if tRawFile == undefined then
format "ERROR! heightmap has no .raw filename!\n"
local findPos = findString fname "\\levels\\"
if findPos == undefined then
format "Error Could not find Levels in path!\n"
local gamePath = substring fname 1 findPos
format "tScale.y: %\n" tScale.y
tScale = [ tScale.x, tScale.z , tScale ]
local t = BF2_ImportscaledHeightmapRaw (gamePath + tRawFile) tScale:tScale s:s
addModifier t (uvwmap())
t = convertToMesh t
t.wirecolor = brown
-- bf2 has the origin at the bottom center of the terrain
t.pos.x = -0.5 * t.max.x
t.pos.y = -0.5 * t.max.y
-- but the pivot is in the center
t.pivot = [0,0,0]
t.Mirror.mirror_axis = 1
t.Mirror.mirror_center.position = [0,0,0]
t.Mirror.mirror_center.position.x = 0
t.Mirror.mirror_center.position.y = 0
t.Mirror.mirror_center.position.z = 0
You'll probably note that most of these functiosn are nearly identical to the existing functions but with a different scale ( 1 instead of 10 ) and that the function adds a mirror modifier to the imported heightmap. This comes in handy later on.
- Save the file and quit the text editor.
After Max has been started you'll notice a new entry in the Battlefield 2 menu : 'BF2 Import Heightmap'
Now to import the navmesh you're currently working on:
- Go to the new menu entry and select it.
- Browse to the heightdata.con of your level and open it.
Heightmaps are mostly large objects so from that point on your Max will probably be a bit slower. Unfortunately, there's a bug in Maxscript which ignores setting the center of the mirror modifier, so we need to do that manually once the heightmap is imported.
- Select it (it's called 'Object01') and select the 'Mirror Center' property of its mirror modifier ( Step 1 in the image below ) .
- Select Max' 'Move' tool and find the three coordinates for the mirror center on the bottom status line .
- It currently says something like X = -1024.0 Y=-1024 Z=0 if you imported a 1024 scale 2 map.
- Enter a 0 ( zero, that is ) into the X and the Y coordinate and the heightmap is exactly where you want it.
- As the final step collapse the Mirror modifier on the heightmap mesh
Now edit your navmesh as usual.
For best results:
- ·vehicle navmeshes should be set between 0 and 1 meter above the ground
- ·infantry navmeshes should follow the terrain as close as possible.
- ·tesselating a polygon (set the 'explode' value to 0 before tesselating ) enables you to add detail in critical spots.