The BSP format used by Q3 is internally constrained to lightmaps that are no larger than 128 x 128 per surface, unless ioquake3 upgrade its engine to remove that constrain there is no hack to go over 128 x 128.

What this trick does is to make q3map2 export all lightmaps as regular textures (lightmaps are textures). A shader script will be automatically generated by q3map2, don't edit it. Lightmaps will be exported as "lm_xxxx" in ".../maps/mapname" folder.

If you want all surfaces in your map to have high resolution lightmaps you'll have to write a shader script, with shaders for every texture used in your map that is going to be lightmapped.

Drawback: Texture compression does affect lightmaps, meaning that you'll see ugly artifacts if you enable it. There is no workaround for that, it's a trade memory space for increased fps. There are codecs suited for lightmap compression out there, but not every GPU supports them and the current engine has no support for such codecs at all.

Resolution vs scale[]

Suppose that there is a large, 2048 x 2048 (game units) flat surface in your map and the lightmap's resolution is 128 x 128 (pixels). The shadows on that surface are going to be so blurry and low def that you won't be able to tell the shape / silhouette of what is casting that shadow. Unless the shadow caster is super large too.

The reason for that is that a flat surface has to have one lightmap only, the lightmap can't be splitted in multiple 128 x 128 parts. One 128 x 128 lightmap stretched to fit a 2048 x 2048 surface is much like taking a photo and resizing it to 500%, it's going to be very blurry.

A scale of less than 1 is telling q3map2 to use up more space in a single 128 x 128 lightmap texture, so at best, one surface fills up one 128 x 128 texture alone. On the other side, a scale greater than 1 is telling q3map2 to use up less space in a single 128 x 128 texture, meaning that more surfaces can be fitted in there, thus, reducing the sharpness of shadows.

That's why you need higher resolution for large surfaces, it's the lightmap's resolution that puts a hard limit on how sharp shadows can be.

There is only one tradeoff possible if high res lightmaps can't be used: more triangles. And being more specific, more non planar triangles. Non planar because if you have a 1024 x 1024 flat ground and break it up in sixteen planar 256 x 256 pieces, they will all end up merged in one surface. But that is a terrible solution because you wouldn't want to waste time breaking up large surfaces in smaller ones for the sake of lightmaps, plus, bringing performance down due to even more triangles to be rendered.

The shader script[]



 qer_editorimage textures/my_dir/my_texture.jpg // JPG or TGA
 q3map_lightmapSize x x
 q3map_lightmapsamplesize 16
 q3map_lightmapBrightness 2
   tcgen lightmap
   map $lightmap
   rgbGen identity
   map textures/my_dir/my_texture.tga


Shader script documentation[]

Shader name: better use unique names to avoid conflicts with other textures or shaders which happen to have the same name but different properties.

lightmapsize: the engine is currently limited to 2048 x 2048 for any texture, so no resolution higher than that. For batching / texture atlas reasons it's better to have fewer high resolution lightmaps than a lot of low resolution lightmaps. But, older GPUs don't have much VRAM and have texture size limits lower than 2048 x 2048, so a map might be unplayable or run very sluggish on older machines if very high resolution lightmaps are used. The drawback is that q3map2 "atlasing algorithm" is not the best one, there is always some wasted space in the last lightmap page, meaning that at 2048 x 2048 you end up with the worst case of space wasted and memory occupied with black pixels that don't contain anything useful.

lightmapsamplesize: this is what controls the sharpness or blurriness of shadows (not lightmap's res!). A value of 1 provides the best quality at the expense of much more ram and much longer compiling times. A value of 16 is the default, low quality, fast compiling time. Lower values force q3map2 to use up large portions of a lightmap's texture for that specific surface, thus, will end up generating more lightmap pages. Values are integer only and scale exponentially.

lightmapbrightness: external lightmaps are rendered twice as dark, that's why there is this brightness multiplier. You can also lower it if you want to compensate for overbrightness in your map.

q3map_normalimage: if you want this everywhere in your map, on every surface, better forget about it. The pixel density required to achive sharp results is too high, requires lightmaps with ultra high resolution which the current engine doesn't support.

Workaround q3map2's safe_malloc error[]

If you have RAM but q3map2 crashes because light stage takes over 2GB of RAM to calculate light, use a 64-bit version of Q3Map2, such as the one that comes with NetRadiant-Custom.

If that is not an desirable option, but you are still using a 64-bit version of Windows, do this:

1. Download the CFF Explorer Suite

2. Open CFF Explorer and then open your q3map2.exe file.

3. Go to the "File Header" section (under Nt Headers)

4. click the "Click here" on the Characteristics row on the right hand

5. check the "App can handle >2gb addresses" box, then click OK

6. go to "Save as", and then rename your existing q3map2.exe (for backup)

7. Save the current version as q3map2.exe

External lightmap seams[]

Ordinarily, internal lightmaps do not have mipmaps generated for them, as this can cause seams. Due to the engine not knowing that external lightmaps are actually lightmaps, the external lightmaps will have mipmaps generated for them unless the material in the .shader file that uses it has the noMipMaps flag. Since textures are shared across multiple materials, whether a texture is mipmapped is dependent upon the first material loaded that uses the texture. This can be exploited by adding dummy materials with the flag and the lightmaps to the start of the list of materials in a BSP. This exploit is used in the version of Q3Map2 that comes with NetRadiant-Custom.[1]

Another workaround is to convert the TGA lightmaps to DDS files without mipmaps, which engines use as is, but the current version of ioQuake3 only loads DDS files if using the Rend2 renderer with r_ext_compressed_textures enabled, so this isn't a reliable solution.