An
AllocBlock (Allocation Block) is a type of texture atlas the GoldSrc engine uses internally to paint the lighting for each face. A texture atlas is one (or several) texture sheet(s) that contain smaller individual textures (in this case lighting textures or luxels) that maps to one or more faces (always 1:1 for allocblocks).
Because the lighting data (
lightmaps) is less dense than texture data but inversely unique to each and every face, the engine consolidates them into a texture sheet of 128x128 lighting pixels (luxels) and there can be up to 64 of them at once [
note 1]. This means there's a hard limit on the total texture detailing on the map and texture scaling contributes to the total.
The allocation process happens at map load, and for qualified faces [
note 2] regardless of lightmap data being present (unlit pitch black faces consumes no lighting data).
The size allocated for lightmap is calculated as follows:
- Starting with the vertices of the face, its extents is calculated by projecting the vertices onto the same plane as the textures, inheriting the texture's scale, and then taking the rectangular extents that fit those vertices.
- The extents is divided by 16 (value fixed by the engine) and rounded to expand the area (rounded down for the minimums, rounded up for the maximums.)
- 1 luxel is added to each side for the right-most and bottom-most row of luxels.
Texture projection
Scaling the face's texture changes the face's extents
Then the allocation process for each face can be summarized as follows:
- On a block, find the highest (closest to the top) rectangle in the unallocated region that can fit the face's lightmap size
- If such region exists, assign the rectangle, aligned to the left
- If no such region that fit exists for this block, try the next block and repeat the process (max. 64 times)
Visualizing AllocBlock allocation
Since face extents is rectangular, the resulting lightmap is also rectangular. Logically, this means some wastage happens when the face has diagonal edges. A rectangle cut diagonally into two triangles (viz. ◩) uses twice the AllocBlock as an intact rectangle. The most waste however happen on thin diagonal faces.
Face polygon (in green) over face extents (rectangular, in red). The areas shaded red are wasted.
The allocation algorithm is also not very efficient. Larger faces tend to waste a fraction more allocation area than needed as it buries the nooks and crannies that could've fit a few smaller faces. No tool yet exists that could reorder faces in the BSP in order to fill such gaps.
From the above viz. Areas marked red on the right (the allocblock) are wasted.
While the AllocBlocks are purely a construct inside the engine, the impact is notable enough that modern compile tools like VHLT simulates allocating faces to AllocBlocks to measure the number of AllocBlocks a map would use if the engine loads it.
Notes
- Svengine (Sven Co-Op's engine) has increased this limit.
-
aaatrigger
, sky
, !
-prefixed liquid textures, and faces marked as special, are exempted and thus appear fullbright if at all.
AllocBlock:Full
The dreaded AllocBlock:Full error occurs when the engine runs out of these 64 AllocBlocks, and there's still faces left over. Simply put, it's because there's too many texture pixelage in your map. It can happen accidentally, like having scale lock on while resizing down brushes, or transforming brushes that result in textures perpendicular to the face, in which case you should go fix it. When accidental tiny scaled faces are eliminated but AllocBlocks are still over budget, then texture scale should start getting budgeted, by increasing the scale on certain faces which reduces the total number of texels (and the corresponding luxels). The brushwork should also be optimized to reduce cutting diagonal edges, which as previously discussed waste allocation space.