Classic Battlefield Modding Wikia

Import Heightmap for Nameshing with 3dsmax

By Mschoeldgen

LEVEL:  Advanced

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

macroScript BF2Import_Heightmap

    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/ 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) =
      undo off
            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
            local tmpYOffset
            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 ] ]

      return t

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
            return false
      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)
      fclose f

      local t = BF2generatescaledHM worldsize:(hmRes * s) values:hmVals
      if isValidNode t then
            t.scale = tScale * s

      return t

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[2]
      local tRawFile = heightData[3]
      if tScale == undefined then
            format "ERROR! heightmap has no scale value!\n"
            return false
      if tRawFile == undefined then
            format "ERROR! heightmap has no .raw filename!\n"
            return false

      local findPos = findString fname "\\levels\\"
      if findPos == undefined then
            format "Error Could not find Levels in path!\n"
            return false
      local gamePath = substring fname 1 findPos

      format "tScale.y: %\n" tScale.y
      tScale = [ tScale.x, tScale.z , tScale[2] ]
      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]
    bf2AddMirrorMod t  
      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
      return t


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.