*.map based levels with Blender 2.49 - advanced

Make *.map based levels with Blender

Blender 3D (in fact any modeling program) works in a fundamentally different way to Radiant, editor (or any BSP based level editor) used to make maps and levels for BSP based game engines (idtech).

This means a number of very important considerations need to be taken into account, essentially due to the way that 3D applications, used in the context of a level editor, work with 'flat planes' as opposed to level editing tools use of 'volumes'. This effects the outcome of the export process and can have a detrimental effect on the resulting map if not mindful of the following points.

Brushes As 'thin skin' volumes ^

Mesh polygons are converted and exported in one of two ways;

  • An 'closed' polygon cube (similar to the cube in Blenders default scene) is exported as a brush that's a solid volume.

  • An 'open' primitive, single or collection of polygons are exported as 'panels' - removing the top and bottom from a cube, for example, results in the remaining sides being exported as mitred panels.

Being able to build as much geometry as possible as 'closed' shapes ('sealed') would be ideal, but it's not always possible, especially if a levels architecture is relatively complex. The only option available in that case is to build the models using 'open' meshes and polygon planes.

Faces, polygons and 'open' shapes cause the most problems because in essence the way the script works is that it looks at a face and extrudes it backwards along the normal to create a 'volume', the depth of which is limited; in effect it's producing something commonly known as a "thin skin".

Generally speaking, if the converted model is left as is and 'structural' in nature, this 'skin' or 'shallow' brush building procedure results in poorly 'optimised' levels that have a tendency to cause compile problems; notwithstanding map 'leaks' due to gaps between brush volumes - brush accuracy problems occur where the process hasn't quite been able to create 'good' geometry (it's noted here that this may not necessarily be the problem of the script but the accuracy of the brush data format itself). The fix for this is to convert as much brushwork into "Detail" flagged volumes as is possible (see below).

Design note: Additionally it's worth noting that the 'thickness value' does not necessarily correspond to the Radiant's default grid settings, which will likely cause issues in of itself.

Issues relating to brush accuracy, which cause holes, gaps and leaks in compiled maps

Issues relating to brush accuracy, which cause holes, gaps and leaks in compiled maps due to their overly complex nature as 'solid' volumes

Brush joints are mitred ^

When exporting objects that are composed largely of flat faces/planes and 'open' primitives (a box without a top and bottom for example), due to the the way the export process creates volumes from flat polygon planes, it tends to try and mitre joints where brushes meet. However, it does so using a 'best fit' approach to mitring joints to 45° angles as much as possible rather than butting the brush volumes up to one another flat face to face - as most of the unused faces are covered in caulk it's not specifically necessary to mitre them where they join.

Exported brushwork is mitred at corners

Exported brushwork is mitred at corners

This causes a primary problem based on the way the brushes are written; if one is moved or manipulated there is a good change it will break or 'collapse' due to the lack of precision afforded to the exported brushwork; the snap to grid option available during export from Blender may fix this to a point in respect to polygon edges that were already 'snapped', anything else may show up as a misaligned edge or face in Radiant which will require further work to 'fix'.

There are technical reasons 'for' and 'against' mitring, however, from a practical point of view it can become time consuming to 'treat' edges in such a way that every volume is mitred to its immediate neighbours.

Maps are exported relative to being 'structural' brushwork ^

Due to the way the brushes are formed as part of the model to map process it means a map contains many, many more 'solid' brushes than is necessary for a functioning level. Now because this output is "Structural" in nature it means that brushes will block VIS(ibility), make 'cuts' in the BSP file to create 'portals' and be of a file size relative to the data that that generates (BSP needs to be under 2MB usage). Optimised levels, on the other hand, use as little "structural" brushwork as possible.

It's vitally important to understand this primary point because ignoring it will result in a map that won't compile or run in game, and more importantly, means rebuilding copious amounts of the level to correct problems that arise as a result of ignoring this particular point. A partial fix for this issue is to convert the exported brushes to 'Detail' flagged volumes (see below).

Materials, textures and orphaned shaders ^

The initial texturing that's assigned to brush faces is determined by the textures applied to the mesh in Blender - the name given to a shader in Radiant is the same the the actual file name of the image UVW mapped to the mesh. So areas (or whole maps) that use more than one texture - for example, a wall that has both a 'brick' and 'trim' texture UVW mapped to it - would have two distinct shaders created for each texture respectively, named the same as the applied images.

Shader names in Radiant are taken from image file names of textures applied to models

Shader names in Radiant are taken from image file names of textures applied to models

Additionally, unlike creating meshes that are exported to various game-ready model formats, when exporting a map from Blender it will use and process 'multi-materials' correctly; in other words, a model does not need to be broken down into sub-objects based on material/texture assignment.

Bear in mind also that if texture paths aren't correct as a model is being worked on in Blender, opening the resulting map in Radiant will show the 'orphaned' red and black checker editor image associated with missing or broken shaders. To remedy this it's best to work on the Blender file with a 'relative' folder structure that replicates the same hierarchy of the game being edited, for example;

  • \Root project folder\ - Blender files are stored in here
    • \textures\ - texture assets reference from here
    • \models\ - texture assets also referenced from here (where necessary)
    • \maps\ - exported map can be saved here

Failing that using Radiant's "find and replace" tool on these erroneous texture/shader assignment issues, it's possible to sort out the problem relatively quickly (as it can with any shader assignment work).

UVW mapping models in relation to exporting as maps ^

Whilst models can have very complex UVW maps applied to them, brushes can't, everything is 'planar' mapped - that is, 'flat projection' UVW mapping surfaces. So by default, the exporting of a model to a map will process face data largely ignoring any 'complex' UVW mapping that is applied to a mesh and simply 'planar map' all the faces it comes across.

There are limitations regarding what can be done with textures 'mapped' to brushes in this way, but most adjustments can be done using Radiant's surface tools ("Shift+S" and "S").

Export quads and not triangles ^

Because brushes are 'volumes' an unedited brush object is essentially the equivalent of a solid polygonal 'cube' (more akin to a block of stone, than an empty box). What this means is that they are largely composed of 'quadratic' polygons ('quads' for short) - faces that are composed of four corner vertexes - that tend to hold that shape whilst being edited and manipulated in Radiant. Knowing this effects the prepping of the model before export; All the faces need to be left as quads as much as possible in order for brush integrity to remain optimal.

Flat shaded view in EDIT mode showing the polygons as 'quads' that make up the mesh

Flat shaded view in EDIT mode showing the polygons as 'quads' that make up the mesh, it's important to make sure this is the case for as much of the mesh as is possible before exporting to the map format

Similar view in GTKRadiant of the Blender shot immediately above, 'quad' based brush volumes selected

Similar view in GTK Radiant of the Blender shot immediately above, 'quad' based brush volumes selected showing how the volumes are created as 'quads'

The main reason for this, aside from the way Radiant works with brush volumes, is that breaking a model down into triangles ('tris') results in a lot of 'accuracy errors' in brushes due to the way the script tries to 45° mitre the shapes and acute angles present in a triangulated mesh - remember that all adjoining brushes are mitred, this is carried out to the 'nth' degree and applied to all converted polygons which leads to the problem described in the following.

So, although the 'front' face of a brush may be aligned correctly to its immediate neighbour, the back and sides may not - if a brush is triangular and at an 'off grid' (none-axial) angle it more than likely will result in 'spiked' brushwork that have extended edges, faces, or points poking out beyond the expected bounds of the brush and potentially into 'good' space inside the level (see image below).

Design note: leaving a mesh with the polygons intact as quads avoids this problem to a great extent.

'Spiked' brushes poking out into 'good' space as a result of the exporter mitring all adjoining edges to other brushes

'Spiked' brushes poking out into 'good' space as a result of the exporter mitring all adjoining edges to other brushes

Scaling the model to the right size ^

When working on a level in Blender there are basically two ways to go about it;

  1. Work at Blenders default scale

  2. Use a reference object to work to scale (at actual size)

If neither of these is done then it means a process of trail and error will be necessary in order to get the scale correct. To avoid the problems associated with doing that, it's preferable to use reference objects and work relative to the size of those - so long as "Apply Scale and Rotation" is not used on re-sized objects, it should be relatively straightforward to work out what "Scale" setting should be used on export to get sizing correct.

Brushes as 'detail' flagged volumes ^

As mentioned above, a number of problems associated with using a map converted from a model in this way can be resolved by the use of 'detail' brushes. This is where brush volumes are 'flagged' (marked) as non-structural, which in turn means they have no effect on visibility in the map - they don't block the engines view of what can be rendered based on the players POV. In other words, standing behind a wall that's been 'detail' flagged won't prevent what's behind it from being rendered even though the player can't see it.

It's important to point out here that flagging brushes as 'detail' does not in of itself solve the problem of messy brushwork, all it does is prevents the excesses associated with compiling a level built ostensibly from the type of brushwork generated by the ('open' mesh) model to map conversion process, namely as that relates to BSP splits and portals. Select a brush or collection of brushes by "Shift+LMB" or drawing a brush volume over a selection and using the "Select » Select Inside" menu option. Once objects are selected, RMB click anywhere in the 2D view and from the pop-up menu select "Make Detail"; nothing will happen, but the selected brushes will be now be marked as 'detail'. To test this use "Ctrl+D" (or "View » Filter » Details") to toggle between "show / hide detail" of any brush volume flagged as detail; if the volume is marked 'detail' it will disappear from view.

The main reasons for going to all this trouble converting brushes from 'structural' to 'detail' volumes boils down to two main issues; BSP file size and bot navigation issues.

With brushes selected RMB and select "Make Detail" to convert brush volumes to non-structural objects

With brushes selected RMB and select "Make Detail" to convert brush volumes to non-structural objects

3D camera view showing the detail flagged brushwork

3D camera view showing the detail flagged brushwork

3D camera view showing the detail flagged brushes hidden from view and exposing the caulk hull underneath

3D camera view showing the detail flagged brushes hidden from view and exposing the caulk hull underneath

'Detail' flagging brushes, BSP file size & bot navigation ^

The compiling of a map is something called a 'recursive' process, i.e. space is cut and then check, then cut again and further checked until a point is reached where the compiler is mathematically satisfied there are no more divisions of space required. Each time one of these divisions is made in the BSP it uses up part of the allotted 2MB data limit - the more divisions, the more data is used - until the process runs out memory and displays one of the various MAX_MAP_ errors as a result. So, the more cuts the geometry causes in the BSP, the larger the file size.

In addition to this, these 'cuts', or 'portals' as they're correctly called, are used during the calculation of bot navigation and, as with everything to do with level design, the more complex the structure the more difficult it is for the game to use if it's not 'processed' or 'prepped' correctly beforehand.

A 'Portal' is in effect 'space' inside the map that's created as a result of the structural splits caused by brushwork. For example, if a simple box were compiled it would contain one 'space'. Now add a wall to divide the box into two with a gap for a door and the result is a box with three 'spaces' in it - one either side of the wall and one in the gap where the doorway is (the same thickness as the dividing wall).

Now the problem with using a converted model that's made from 'open' meshes 'as is' in this context, is that they tend to cause a lot of splits in the BSP (as mentioned above) which result in an equally excessive number of portals; these can sometimes be clustered together so that many small portal spaces are contained in a larger given area; or they can be at very odd and peculiar angles due to the way splits are generated along planes - imagine extending an edge of a curve until it hits something - it will more than likely slice through other areas and as a result create oddly shaped portal spaces.

The net result of having too many, small or oddly sized/shaped portals is poor bot navigation (amongst other things). So, to minimise this problem a map needs to have as few as possible and they need to be 'axial' in nature, i.e. be at 90° angles to each other where-ever possible. To do this the converted model needs to be extensively 'detail' flagged.

It's worth pointing out here that there are other reasons and issues "why" that can arise from BSP splits and portals but the two mentioned above are the most common problems that result from poor brushwork during level construction, regardless of the method used to generate said brushwork.

Many awkward, small and badly optimised BSP/Portal splits due to compiling a converted model 'as is'

Many awkward, small and badly optimised BSP/Portal splits due to compiling a converted model 'as is'

Same portal file as above but shown against the backdrop of a caulk hull

Same portal file as above but shown against the backdrop of a caulk hull

The same level after flagging brushwork as detail using the same caulk hull as above

The same level after flagging brushwork as detail using the same caulk hull as above

The same shot as above showing the optimised portals in relation to the caulk hull

The same shot as above showing the optimised portals in relation to the caulk hull

Caulk hull building ^

In order to make the best use of detail flagged brushwork it needs to sit inside a 'hull' made from basic blocks and shapes covered in 'caulk'; this is commonly referred to as the "caulk hull". Whereas detail brushes don't effect visibility or the BSP, a caulk hull does because the brush volumes are in their default 'structural' state - it's this hull that is used by the compile process to determine what can be seen from any given point of view.

Building a caulk hull is best done within GTK radiant (see below).

Depending on the architectural structure of the converted model the caulk hull needs to be as simple as possible but at the same time leave as little 'dead space' in the map - these are areas caused by gaps between the outer caulk hull walls and the inner contained detail brushes. Although there are ways to deal with specific problems that arise from such areas - such as using various 'clip' volumes, it's always best to try and remove them at this point without causing too many BSP splits - remember, structural brushes cut into the BSP so keeping the number of exposed edges to a minimum is ideal.

There are different schools of thought on exactly how brushes are joined when doing this - 'mitred' or 'butted' joints, but suffice to say, ideally the 'inside' of the hull should create as few splits in the BSP as possible, but be dependant on the architecture within given areas (see the note above regarding 'portals'). For instance in the image below using a simple box would suffice in this instance so the raised areas and 'pillars' aren't (absolutely) necessary; they are placed in the example to indicate that you can do so if/where required. The trick is to keep the hull as simple as possible without compromising visibility (what is drawn by the engine).

The essential caulkhull required when using detail flagged brushwork. Everything is contained and seal inside this 'hull'

The essential caulkhull required when using detail flagged brushwork. Everything is contained and seal inside this 'hull'

General level editing considerations ^

Some further considerations to keep in mind when building a model to be exported out as a map.

Caulk hull ^

Although, as discussed above the caulk hull is best built inside the Radiant editing tools, it is possible to build it as a model. However, in doing so the following needs to be kept in mind - caulk hulls tend to work best as solid blocks and volumes, so, only 'closed' cubes and 'solid' primitive shapes should be used to surround a model when building a caulk hull in Blender; using 'open' meshes and polygons won't produce the brush volumes required for caulk hulls, i.e. the process would be looking at the hull as individual (extruded) faces rather than solid blocks which would need to be replaced or edited so that only solid volumes remained - remember that 'dead space' is bad as it may create splits and portals that potentially could cause various problems. When building the hull as a model it's important that it's overall structure adheres to and corresponds to both the grid in Blender and the grid in Radiant; this means making sure that Blenders grid settings are correctly set up.

Basic caulkhull use

A basic caulk hull in Blender constructed around the brushwork the player will see and made from simple blocks and other primitive shapes

Blenders to Radiant grid spacing ^

Related to the above, in order to maintain a 1:1 relationship between the grid in Blender and Radiant it's best to work within Blender at scale and using grid settings that correspond to Radiants defaults. In Blender, open the "View Properties" panel - "View » View Properties" and change "Spacing" to "8", "Divisions" to "8" and "LMB+hold drag" "Clip End:…" as far as it will go ("80000.00"). This is also true of any modeling carried out that is intended for use in a game. For more details see the prepping models tutorial here.

Setting grid properties to match GtkRadiant

Changing Blender View Properties to match GtkRadiants grid settings: Spacing and Divisions in particular

Snapped to the grid ^

When modeling in Blender with the intention of exporting the results out as a map it's important to make sure, as much as is possible, that work is carried out whilst snapped to the grid. In other words, major structures should be made and manipulated so that they follow Blenders grid. The specific reason for this is related to the accuracy that's possible from the *.map format itself, and the way the Radiant level editor treats objects, faces and edges that are 'offgrid' or 'none-axial' (at odd angles to the grid); in effect Radiant depends largely on objects being sat on the grid to properly maintain an objects integrity.

Setting Blenders grid properties to match GtkRadiant

With Blenders grid properties correct set-up, a 64 x 64 (Blender) Unit block matches a 64 x 64 (Radiant) unit block in the map editor

Object size and distances match between Blender and Radiant

A block 64 x 64 units in Radiant is the same in Blender when grid properties are correct set

Texturing ^

Although the export process ignores 'complex' UVW mapping in favour of a more simple 'planar' UVW map, it's best to do some UVW unwrapping and texture/material assignment as this is where the scripts gets shader assignments from. The upside of this it that despite the liberal appearance of the 'missing' shader in such instances, there will at least be correct shader application on brushwork making the job of texture adjustments and/or find and replace much less time consuming to fix.

Lights and game entities ^

Exporting a model to the map format does export lights, however, beyond positional (and colour) information little else is carried over which means that lights will need further editing once in Radiant. This is also true of other game specific entities such as player_start entities, weapon markers, ammo boxes and so on, so it's best to use Blender to build the structure of the level and leave entity set-up to Radiant. Primitive objects can be used in Blender as place-holder position markers, but they will simply be exported as brush geometry and have to be removed once in Radiant (brush geometry can't for those types of entities).

Using Lamps as light entities

Setting Lamp "Distance" properties in Blender

Using Lamps as light entities

Blender Lamp objects can be used as 'light' entities in map creation

Lamps are converted into 'Light' Entities

During export to *.map Blenders 'Lamp' objects are converted to 'Light' entities (show here in GtkRadiant)

Lamp colouration can also be converted

Colouration can also be used to tint Lamps which are then converted - colour variations may occur setting the property in Blender rather than Radiant

Conclusions ^

As with level design generally a certain amount of preplanning is ideal, especially when working this way so as to ensure that as few problems occur as possible; most issues that arise usually relate to scale and grid problems - brushwork being too small/large and gaps/overlaps as a result of incomplete or approximate conversion of the models polygon faces into brush volumes.

It's also worth noting that the *.map format is relative cross platform from the point of view of using the same source model to create levels in more than one game engine; the same model can be exported out to a *.map and used in Quake 3, Doom 3, Quake 4, Prey and Quake Wars as they all fundamentally use the same data structure and/or can read in Quake 3 map data and 'convert' that into the appropriate format for use (there are some organisation differences between map generations associated with the different game engine generations).

As this tutorial has shown, it is more than possible to make a level in Blender 3D as a model and export that out as a map. And although the use of a 3D modeling application means more physical flexibility, that is still best done using the same, similar or approximate considerations that would be applicable when building the same map with the traditional level editing tools, GTK radiant (and QeRadiant).

FOOTNOTE: converting models to maps from 3DS Max and other 3D applications ^

It's probably dawned on none Blender users reading this tutorial that yes, it should be possible to use this process as a go-between to convert a model made in 3DS Max (or any 3rd party 3D application - Maya, XSi, Lightwave and so on) into a map; all that's required is that the source model be exported out to a format that Blender can import intact. Once there some tidying up may be required but so long as everything imported correctly Blender should then be able to export the model back out and directly into the *.map format.