There are a couple non-obvious things about creating static meshes and having them appear correctly in the engine. Ideally you shouldn’t be able to spot the difference between BSP and static meshes when playing the map.
A few issues I ran into:
1. Exporting the mesh from Radiant
Make sure the brushes comprising the object are on the grid. Also make sure no brushes are sticking into each other (no brush overlap). Before exporting, move the object to grid center (0 0 0) which is going to be its origin later on. Position the object so the origin is roughly at its center of mass and cannot be buried in surrounding brushwork later on.
You can export brushwork either directly via Radiant’s brushexport plugin (to *.obj, this plugin comes with Netradiant) or by using q3map2 (*.bsp to *.ase).
2. UV unwrapping and texture scale
When UVmapping the object in Blender, an approach that often works with blocky level geometry is selecting all faces pointing into the same direction and doing “project from view”. In orthogonal top/front/side view of course. Do this from all three views and you should end up with something useable. It won’t work so well with curves though (it may be better to mark seams there).
Remove all faces that aren’t visible anyway.
Here’s an important point; if you keep your entire UV map inside the image space (512×512 in my case), the model’s texture will appear much lower res in the game than the adjacent brushwork. That’s because textures on brushwork tile, and may even be scaled down if it’s a high res texture (I typically have to downscale textures by as much as 0.125).
Now it may not be obvious that textures on a model tile as well, but only if the UV map is scaled up to several times the space of the image. As shown here. When you scale up the UV map in Blender, you can see the effect in realtime – the model’s texture appears to be tiled and scaled down. If you get the scaling of the UV map just right, the texture resolution on the model will become indistinguishable from surrounding brushwork.
3. Smoothing groups and splitting the mesh
There is some debate if you should need to split the mesh to create smoothing groups. A smoothing group is basically a group of faces that are lit as if they were a coherent surface. Example wise, with a cylinder, all the faces except the end caps should be shaded smoothly to make it appear rounded.
Different engines and modeling suites seem to have different ideas of how to go about this. It turns out that with q3map2, when you bake a static mesh without splitting it into smoothing groups, you get the entire object smoothed over like in the image above. Basically you lose your hard edges.
If you split the mesh (Y key in Blender) along every hard edge (meaning for example, every stair surface becomes its own mesh) the result looks a lot more like what you would see on brushwork:
Grouping is done by selecting the face/s in between hard edges and splitting the mesh so the vertices (and probably edges too) are duplicated in position. What’s going on behind the scenes is that each smoothing group gets its own vertex normals.
This is how it looks while splitting the mesh.
Notice that I set the grid subdivisions to 8 instead of 10 to match Radiant’s grid. I also have face normals visible because I had to flip a couple of them after importing the model from Radiant. With the normals displayed, it’s very easy to see which faces need to be flipped.
4. Exporting the mesh from Blender
You need to triangulate the mesh (simply select all and do “Triangulate Faces”). It makes sense to do this after all the other stuff. When exporting, make sure the scale comes out correctly (I had to hack the ASE export script to keep it from upscaling the model by a factor of 16, grrr). Also make sure the objects’s position and rotation are 0 0 0 (“apply location/rotation”) and that it is still on the grid. I tend to do all this by hand instead of letting the exporter fix it – better make sure…
5. Placing the object in Radiant and compiling
Radiant and q3map2 use a dummy entity called misc_model to import static meshes. This entity must a) be listed in your entity .def file and b) your mod shouldn’t use misc_model for any other purposes.
misc_model has a few important spawnflags; spawnflag 2 adds collision and spawnflag 4 essentially makes your model have a lightmap. Collision impacts performance somewhat; if you have a purely decorative object that only needs very rough collision, it’s better to manually cover it in a clip brush or two. For something like these stairs, using the modelclip spawnflag is probably justified though.
q3map2 will bake misc_models directly into the BSP; it is not necessary to package the *.ase files with your mod for this reason. The static meshes use shaders just like brushes do; you need to add materials in Blender for the export to work correctly, but you can later open the *.ase file in a text editor and change the line that reads “BITMAP” to a shader path that fits your game.
You can use multiple materials, but each must be applied to a different mesh (split mesh if necessary).
Blender ASE exporter (2.63, this is the one I had to fix to avoid upscaling)