Drawable and TFrag
At the highest level is the level file itself, which is a
bsp-header. It contains a
drawable-tree-array, which contains a small number of
drawable-trees. (~1 to 15?).
Each of these
drawable-trees is a different kind, distinguished by its type. Different types of trees go to different renderers. For example, there is a
drawable-tree-instance-tie, and even a
drawable-tree-actor. It is possible to have multiple trees of the same type, as some trees seem to have a maximum size - for example a
drawable-tree-instance-tie can have only 43 different prototypes, so it is common to see a few of these. These trees are the thing passed to the appropriate renderers. All the trees for all the levels get put in the
background-work structure, which is then read by the renderers.
drawable-tree-tfrag contains all the "normal" tfrag data. There are other trees like
lowres-trans, which are only present if the level actually has this type of geometry. As far as I can tell, the special ones follow the same format, but I haven't checked this in detail.
drawable-tree-tfrag contains a small number (1 to 6?) of
drawable-inline-array. They all refer to the same set of
tfragments (the actual things to draw), but have a different tree structure.
draw-node contains between 0 and 8 children. The children are represented by a pointer to an inline array of children. The children can be either
tfragments. All the children are always the same type.
drawable-inline-array in the
drawable-tree-tfrag is the full tree structure. It starts with some number of children that is smaller than 8. And from there on, all children are
draw-node (always 8 or fewer children) or a
tfragment directly. So this is the deepest possible tree. I believe the max depth seen is 6?
drawable-inline-array starts with a list of all nodes at with a depth of 1 from one of the top-level nodes of the first. So this has at most 64 entries. Like the previous tree, all the children from here on have 8 or fewer children.
This pattern continues. The n-th
drawable-inline-array is a list of all nodes at depth n in the "real" tree.
There are two tricks to this:
First, if the
draw-nodes, the type is actually
drawable-inline-array-node. Unlike a
draw-node, which can only contain 8 children, a
drawable-inline-array can contain a huge number of children. The final
drawable-inline-array is a list of a all children at the final depth, so it's always an array of
tfragment. In this case, the type of the
drawable-inline-array is a
drawable-inline-array-tfrag, and it's just a giant list of all the
tfragments in the level.
The second trick is that the
tfragments are stored only once, even if they appear in multiple
drawable-inline-arrays. They used the weird "node = length + pointer to inline array of children" format and sorted nodes by depth to enable this.
tfrag-methods.gc file has the methods of
drawable that call the actual "draw" implementations in
tfrag.gc. There is a near and "normal" version of the renderer. So far, it seems like trans/low-res, ice, dirt, etc don't have separate rendering code (or are part of the main
It looks possible for
tfragments to be drawn using the generic renderer, but so far I can't find the code where this happens.
Tfrag also uses the
adgif-shader idea. I believe the shaders are per-
tfragment (though some may share, or there may be tricks to avoid resending the shader if consecutive
tfragments use the same settings).
I don't know if the
adgif-shader is always 5 quadwords, like for sprite. It seems possible to use the
adgif-shader just to set up texturing, but also have some other stuff. I believe that we currently log in these shaders and link to textures, so we can probably learn something from inspecting these.
There are 4 sets of data in
tfragment: base, common, level0, level1. Each has its own DMA transfer (consisting of a address to data, plus a length). The details of which goes where is not clear yet. I think that sometimes not all 4 are valid. There are only 3 start addresses stored, and the three DMA chains may be the same, or overlap.
The DMA data itself seems to only be loading data. It uses
unpack (V4-16, V4-32, V3-32, V4-8) and
STCYCL to set up fancy unpacking tricks. No other VIFcodes have been found in any level.
Additionally, there are some color palettes that use the time-of-day system.