# Fundamentals of VRAD: Part 1

May 15, 2018

Continuing the topic of waging war on the Source Engine monolith, I’ll be looking at something a little larger for the forseeable future. I’ll preface this by saying that prior to investigation, I had practically 0 idea about how vrad worked, although I have used it extensively. It’s become clear very quickly that understanding the Input and Output data has 0 bearing on understanding just how vrad functions.

Vrad (Valve RADiosity simulator) is, simply put, a lighting pre-calculation tool that runs a radiosity simulation on a compiled Source Engine .bsp. It calculates lighting for any given surface, that is then stored inside the .bsp, such that lighting does not need to be calculated at runtime.

A clean breakdown of how vrad works has been written by x6herbius, and can be seen here: How VRAD works.md. For today, we’re just going to dive into the very first line:

`Load the BSP and other resources, eg. lights.rad`

Very well summarised, but not as simple as it appears. It is important to understand that the end goal is to obtain all the information required to generate a collection of C struct `winding_t` along with associated face information, from which `Patches` can be generated. Essentially, all other required structures are available in their current state as lumps. My current understanding is that a `winding_t` is what anyone familiar with graphics API’s can probably guess. `winding_t` represents an ordered collection of points from which you can construct a plane, as it represents a (optionally: clipped) infinite plane. Vertex order is important, as reading order affects derivable information, such as face normal. Reversing read order will reverse the normal direction, therefore the direction of the face too. This is commonly referred to as Winding Order. Winding in this context, according to x6herbius, is a leftover from Quake days, that Source was based off of.

Written in golang, `winding_t` looks like this:

``````const MAX_POINTS_ON_WINDING = 64

type Winding struct {
NumPoints int
Points [MAX_POINTS_ON_WINDING]mgl32.Vec3
MaxPoints int
Next *Winding
}``````

This could actually be simplified nicely in golang, as `NumPoints` can be derived from `Points`, if a slice was used over an array. Of course, there would be a performance hit, although simply using golang for this will incur a noticeable reduction, especially before optimisation.

Naturally the first thing to be done is to load in our bsp so relevant lump data can be obtained. Quite a few lumps are important: `Entdata, Planes, TexData, Vertexes, Visibility, Nodes, TexInfo, Faces, Leafs, LeafBrushes, Edges, SurfEdges, Models, Brushes, BrushSides, Areas, AreaPortals, MapFlags, VertNormals VertNormalIndices, and both TexDataStringTable+TexDataStringData`.
The relevance of a lot of these are quite basic. Since the more generic lumps that directly contain mesh data (e.g. vertexes, edges), many of the specialised lumped (AreaPortals, Leafs) are necessary to determine whether particular faces should be ignored. `MapFlags` and the `Tex*` lumps are useful for reducing our face count further by ignoring faces with flags/materials that should be fullbright (e.g. UnLitGeneric, nodraw etc). Calculating light on these surfaces doesn’t pose a problem, but the engine will ignore it so a bit of time is saved during calculation by testing during preparing the environment.
We can also discard faces with materials listed inside of the lights.rad files included by both the game we’re compiling against, and an optional custom file we can define as a command flag. Lights.rad just defines a list of materials that should be emissive, referred to by the source code as `TexLights`.
Lastly, its worth noting that brush entities will tend to be discarded, unless they have the property `vrad_brush_cast_shadows`, so we again need to ensure that some brush entities are included.