Making a *.map based Level in Blender: .map making extras (pt7)

Make a *.map based level in Blender

There are a number of additional extras that can help with the production of .map based levels or further our understanding of what we're doing using Blender in this capacity.

In this following final chapter we'll take a look at a varity of these as well as look at some of the more frequent and common problems that happen when designing with Blender.

Shader & texture path problems ^

When creating the materials assigned to the various mesh objects used to build the level, each was composed of three basic 'stages' or 'slots' - a "Material" stage, a "Texture" stage and an "Image" stage. For .map based level editing the image stage is of more importance than the other two because the data it contains is used to determine the "Shader" 'path' that's subsequently assigned to exported brush volumes.

When we add a bitmap image to a "Texture" slot it generates two pieces of data; 1) the images "Source:" data - this is the physical location of the bitmap asset and appears in the "Source:" input field[2] as either a 'full' or 'truncated' file path depending on how the asset is referenced, i.e. "F:\Models\TUTORIALS\test1.jpg" or "\\../test1.jpg" respectively. And 2) the "Image Name ID" of the datablock - this is usually taken from the assets file name (in this instance that's "test1.jpg"). It's also an independent block of data that identifies the image so different materials can use the same image but be referenced separately (different 'name', same 'image').

For the purposes of .map based design the file path present in the "Source:" datablock is important because the export process uses this as a means to generate the shader names assigned to brush volumes. It does this by stripping out the 'path' aspect of the source, "F:\Models\TUTORIALS\" for example, leaving just the file name, "test1.jpg". This is further stripped of the file extension, ".jpg" in this instance, the remainder, "test", being the temporary 'shader' applied to the exported map data.

Shader path details are derived from "Source:" path data

Shader references are determined in Blender from an images "Source:" data, which tends to produce 'broken' shader assignments. Character length restrictions mean not being able to add custom (correctly formatted) text to the "Image Name ID" datablock (above "Source:")[2]

This presents a problem however. Shaders are not physical bitmap assets, they are simply "name" references or "ID" tags. This means using Blenders image 'source' data to determine shader references will always produce broken paths because they are not formatted relative to what the .map environment is expecting. Below is a typical shader. It's 'ID' when assigned to a surface is "spkat5/gunmetal3"[1], whereas the actual texture asset is called "xgrid_c01.tga"[2&3].

textures/spkat5/genmetal3[1]
{
   surfaceparm nonsolid
   qer_editorimage textures/xlab_props/xgrid_c01.tga[2]
   q3map_forceMeta
   q3map_nonPlanar
   // q3map_shadeAngle 10
     {
       map textures/xlab_props/xgrid_c01.tga[3]
       rgbGen vertex
     }
}

We can work around this problem to a certain extent using placeholder textures named the same as the shader "ID" we want to use in game, "gunmetal3.tga" for example, instead of the assets actual name "xgrid_c01.tga". This at least makes it easier to figure out, after-the-fact, which shader we were actually referencing in Blender, allowing a quick 'find & replace' in the level editor. The alternative is to extract the contents of a particular game and use the assets 'as is' for visual reference, we then just match the shader to the texture seen in Blender.

Shaders applied to surfaces in Radiant

If we know the name of the shader we can use a placeholder similarly named in Blender so we know which shader we were actually referencing from a given game, in this instance "awall_m06" and "afloor_m01" (pulled from Return to Castle Wolfenstein, single player)

Checking the .map file data ^

Under most circumstances the objects we export from Blender are properly converted so the resulting file, if opened into a plain text editor, is not too dissimilar to the below. Occasionally there are problems that basically manifest themselves as 'invisible' maps - files open but contain no data. The typical cause of this is a missing 'shader' reference in the file[2]. Although coordinate data for a given element can be present (data that pertains to the physical dimensions or location of an object or element)[1], without a shader reference the data block is incomplete so the level editor interprets the file as being 'broken' when loaded, resulting in a 'blank' scene. We can fix this problem by re-exporting the level from Blender, making sure to check texture assignments beforehand. Or we use a text editor to 'find & replace' missing references.

// brush from cube
{
( 128.00012207 -632.00000000 -128.00000000 ) ( 127.99989319 -632.00000000 128.00000000 ) ( 63.99974823 -640.00000000 128.00000000 )[1] test1[2] 0 0 0 1 1 0 0 0[3]

( 64.00000000 -704.00000000 128.00000000 ) ( 127.99989319 -704.00000000 128.00000000 ) ( 128.00012207 -704.00000000 -128.00000000 ) NULL 0 0 0 1 1 0 0 0

( 64.00009918 -640.00000000 -128.00000000 ) ( 64.00000000 -704.00000000 -128.00000000 ) ( 128.00012207 -704.00000000 -128.00000000 ) NULL 0 0 0 1 1 0 0 0

( 127.99989319 -632.00000000 128.00000000 ) ( 128.00012207 -632.00000000 -128.00000000 ) ( 128.00012207 -704.00000000 -128.00000000 ) NULL 0 0 0 1 1 0 0 0

( 127.99989319 -632.00000000 128.00000000 ) ( 127.99989319 -704.00000000 128.00000000 ) ( 64.00000000 -704.00000000 128.00000000 ) NULL 0 0 0 1 1 0 0 0

( 63.99974823 -640.00000000 128.00000000 ) ( 64.00000000 -704.00000000 128.00000000 ) ( 64.00000000 -704.00000000 -128.00000000 ) NULL 0 0 0 1 1 0 0 0
}

Small details and models ^

Making .map based levels necessitates working within certain size limitations we shouldn't typically go under for generally placed architectural structures, the smallest objects being "8" unit steps or stairs. What happens if we have or want to place objects that are smaller than this? Objects that are relatively complex? Or other items we may want to distribute frequently around a level?. Under these circumstances we rely on normal models exported to an editor/game friendly format. It's beyond the scope of this tutorial to discuss making models specifically, suffice to say here that we have two options for getting such content out of Blender and into a level; we can export all the smaller details and objects as a single joined mesh, or rely on a single instance and its subsequent replication and placement in the level editor.

Export small details as models

Small details or objects that are used frequently should ideally be exported as models and not brush volumes. They can be processed as a larger group (all items joined together into a larger object), or as a single item which is re-placed in the level editor [see *.blend "44"]

In practice using a joined group, where all the models placed in the level are joined together into one much larger object (or joined together based on object types), means we can do much of the detailing work in Blender, everything then being exported as a single entity that just needs to be dropped into place in the level editor. The downside is based on the fact that models are processed based on their "bounding box", the 'area' the model occupies. If the bounding box itself can be seen, but not necessarily the model it belongs to, then the entire object is rendered irrespective. For large models this presents a performance problem due to the increased number of polygons being rendered as a result of the grouping.

Small detailed objects exported as a single model

Small detailed objects can be joined together as a single object and exported as a normal model. However, as they are rendered to screen based on their overall bounding box that potentially means a lot of polygonal data being rendered even though much of it may not be visible to the player [see *.blend "44a"]

The alternative, and generally preferable route, is to specifically export one of each type of object so the overall model only contains a single instance of the actual mesh that's needed. In the level editor this individual model can then duplicated and placed around the map as per the original layout design in Blender - we can still use the grouped, larger mesh as a temporary guide, we just need to make sure to remove it before the level is finalised. The advantage here is that only the units seen at a given moment are rendered to screen thus saving on over-draw (drawing too much, or more than is necessary to screen).

Exporting detailed objects as a single model

Small detailed objects are placed in editor as individual instances of the same core model rather than being exported as a whole. Note that models are processed and drawn based on their "bounding box" (the white outline), if that can be seen the entire object is drawn [see *.blend "44b"]

Planar & coplanar surfaces ^

Ideally we should not be using single sided surfaces, either "planes" or "faces", to construct a .map based level. The reason for this is due to the way the export process generates brush volumes by extruding individual polygons along their normals. This creates two problems; 1) because the extrusion of surfaces is limited in depth it results in what's commonly referred to as a "thin skin" mesh. And 2) extruding surfaces in this way generates a disproportionate number of "unique surfaces". Both problems typically mean there are a lot of individual triangles that have to be culled from use or merged together to form larger, more efficient "coplanar" ("co-planar") surfaces. This is one of the primary reasons for building with simple mesh primitives or blocks because it affords greater control over their arrangement and how they can be better made to align more favourably into groups along their major and incidental surfaces.

In practice this means we need to manipulate shapes so surfaces and objects are aligned along the same "plane". With the arch for example, leaving the outer edge as it was would have meant an object with four distinct 'planes' forming the curve[1-4], composed are they are of two triangles each. In reshaping the mesh to form a block, the count is reduced to two 'planes' - one on 'top' and one down the 'back', each composed of four triangles. This reduction constitutes a much better optimisation of surfaces, producing a more efficient map file that avoids the generation of max_map_ errors.

Mesh surfaces are seen as individual 'polygons' and 'planes'

A collection of individual faces form "coplanar surfaces" - elements that orientate along the same 'plane'. Ideally there should be as few of these as possible. No.'s [1] through [6] are individual 'planes' - [1]-[4] are both 'planes' and 'surfaces' because each is orientated to itself. Whereas [5] and [6] are 'coplanar' surfaces composed of individual elements aligned to form a single 'plane' [see *.blend "45"]

Optimising an object to reduce the number of coplanar surfaces

Reshaping the outer edge of the curve reduces the number of unique planes (the top and back edges are no 'coplanar' surfaces) and helps optimise the level by reducing the 'unique surface count' [see *.blend "46"]

Mitring corners, or not ^

As a general rule-of-thumb a "mitre" doesn't in of itself provide any qualitative advantage over other types of joint or corner when used on "structural" brush volumes. During compile every surface within a level is traced until progress is blocked or ends, usually at a volumes outer edge or an intersection of some kind with another brush. Using a "butt" or "mitred" joint typically has the apex of the connection between brushes function as the termination point because the trace can no longer travel along the face being analysed. With "overlapped" brushes the process continues past the apex until it hits an edge, which may be some distance away depending on the size of the brush, before stopping. This is done to all surfaces to calculate what needs to be rendered to screen and what needs to be removed or "culled" from the level as 'waste'.

Mitred corners are not absolutely necessary

The two types of 'corner' typically employed when making a level - "Mitred" or "Butted" corners, although a "mitre" may seem a logical technique to use on brushwork it does in fact come with a number of caveats to it use that can have a detrimental affect on compilation [see *.blend "47"]

There's a secondary issue here to do with the way coplanar surfaces are calculated. The logic of this means overlapping and mitred brush volumes are potentially problematic because they have varying degrees of overrun that project beyond each surface being traced. This can result in brush volumes receiving additional cuts and splits as they cross or intersect surface planes belonging to other objects, if we're not careful this can cause a lot of extra and miscellaneous brush work to be formed, which then needs to be (re)calculated to determine if they should be retraced and/or culled. This obviously needs to be avoided. By using "butt" joints where ever possible we facilitate the proper (or "best fit") projection and culling of surfaces being done in an efficient manner, keeping both map data and file size to a minimum as a result.

Design note: other not so incidental reasons for using "butt" joints is the amount of time it takes to mitre brush volumes on large scale projects. It's also not always possible for joints to be complete; any exposed portions are typically non-axial, meaning the angle of the mitre can than cause further issues in such instances, especially if there is a lot of it.

Overlapped brushes typically require more calculation time to determine proper structure

Overlapping brush volumes typically require more calculation time to properly determine the structure of a corner. On a large level this can be significant in both the time that's taken and the amount of brush data that needs to be culled (corner section and back faces of a brush) [see *.blend "48"]

Objects and features flagged as "detail" don't undergo the same surface culling procedure as "structural" volumes/surfaces so should be mitred and joined properly - there should be no, or very little overlap on these types of structures.

Misc. activating the MAP AddOn ^

The "Quake MAP" export script is an "AddOn" that's not active by default. It has to be enabled before it can be used. We can do this on a 'per-project', or 'default' basis based on how frequently it is required or how convenient it is for the user to have the option available on start-up, or not.

To enable the AddOn, with Blender open click "File" and then "User Preferences", or press "Ctrl+Alt+U" ("File » User Preferences"), the "Blender User Preferences" window will appear. From here click "AddOns" (option along the top) then on "Import/Export". A list will appear showing all Blenders available scripts. Scroll down to "Quake MAP Format" and then click the small checkbox on the right of the listing to enable the script. We can then do one of two things, either close the window and have the script enabled for that session, or we can click the "Save As Default" button bottom-left to have the script load every time Blender is opened. Once done close the window to return back to the main interface.

Activating the MAP export AddOn in Blender

The "Quake MAP" AddOn needs to be enabled before Blender is able to export any .map based levels. From the "File" menu select "User Preferences" then in the window that opens select "Import/Export"; active "Quake Map Format" and close the view

Misc. map entity kit ^

If doing a lot of level deign inside Blender, it's worth using a set of generic 'helpers' that mark entity placements within a map. A pre-made set of entity helpers can be downloaded from here, but if we needed a specific set of objects per the duplication of a particular game set, it would mean creating them. Ideally this should be done in an external file, the contents of which can then be loaded in to a scene using "Append" ("Shift+F1") - this allows individual objects or groups of items to be loaded into the scene as new additions either physically - a copy is loaded into the scene, or referentially - a copy is loaded as an 'instance' that's linked to the external file containing the actual objects. Although each has it's 'Pros & Cons', because they are common elements the idea is not to remake them every time a new project is started.

Design note: if the latter option is chosen remember not to edit the files as changes may propagate into the original linked file which may adversely affect any other level using the same objects in the same way.

Map entity kit for Blender

Generic entity helper kit containing the basic entity classes that can be placed into a level as 'placeholders'; these are not converted into entities on export so need to be removed/replaced in a level editor (GTK Radiant et-al)

Appending "Object" data to the scene from an external file

Appending external data to the level using "File » Append" and selecting the objects to add - items can be 'linked' or 'loaded' into a scene

^