Once all the fundamental data types have been laid out, it is time to move one notch higher in the abstraction scale and concentrate on how we will deliver the geometry stream to the graphics subsystem. It is essential to understand the different geometry packing methods because performance will largely depend on them. The same engine can achieve x2 performance, just by packing the geometry in an optimal way. Additionally, understanding geometry formats will yield lower memory footprints, which is a must for most games.
As mentioned earlier, our geometry stream will consist of five data types: vertices, normals, texture coordinates, colors, and optionally, indices to them to avoid repetition. Generally, I'll describe the geometry formats in an encoded way, similar to that used in chemistry. As a first example, the following format means three floats per vertex, two floats per texture coordinate, three floats per normal, and three floats per color:
V3f T2f N3f C3f
This takes up 132 bytes per triangle, which is quite a lot. You can choose to pre-illuminate vertices, and thus skip the normal information. This eliminates the option of dynamic lighting, but saves lots of space: A V3f T2f N0 C3f format takes 96 bytes per triangle.
Other options allow us to reduce memory footprint even more. We can quantize the different data values until we reach this aggressive configuration, which takes only 18 bytes per vertex:
V3b T2b N0 C1b
Notice how vertices and texture coordinates have been quantized, normals have been skipped completely, and we have stored the color information in bytes, using the luminance channel only.
Also notice how indexing can significantly reduce these memory footprints due to vertex sharing. An indexed mesh usually takes between 40 and 60 percent of the space used by the original data set, so it always pays off in terms of performance and memory usage.