sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

rendering_graph_node.c (42688B)


      1 #include <PR/ultratypes.h>
      2 
      3 #include "area.h"
      4 #include "engine/math_util.h"
      5 #include "game_init.h"
      6 #include "gfx_dimensions.h"
      7 #include "main.h"
      8 #include "memory.h"
      9 #include "print.h"
     10 #include "rendering_graph_node.h"
     11 #include "shadow.h"
     12 #include "sm64.h"
     13 
     14 /**
     15  * This file contains the code that processes the scene graph for rendering.
     16  * The scene graph is responsible for drawing everything except the HUD / text boxes.
     17  * First the root of the scene graph is processed when geo_process_root
     18  * is called from level_script.c. The rest of the tree is traversed recursively
     19  * using the function geo_process_node_and_siblings, which switches over all
     20  * geo node types and calls a specialized function accordingly.
     21  * The types are defined in engine/graph_node.h
     22  *
     23  * The scene graph typically looks like:
     24  * - Root (viewport)
     25  *  - Master list
     26  *   - Ortho projection
     27  *    - Background (skybox)
     28  *  - Master list
     29  *   - Perspective
     30  *    - Camera
     31  *     - <area-specific display lists>
     32  *     - Object parent
     33  *      - <group with 240 object nodes>
     34  *  - Master list
     35  *   - Script node (Cannon overlay)
     36  *
     37  */
     38 
     39 s16 gMatStackIndex;
     40 Mat4 gMatStack[32];
     41 Mtx *gMatStackFixed[32];
     42 
     43 /**
     44  * Animation nodes have state in global variables, so this struct captures
     45  * the animation state so a 'context switch' can be made when rendering the
     46  * held object.
     47  */
     48 struct GeoAnimState {
     49     /*0x00*/ u8 type;
     50     /*0x01*/ u8 enabled;
     51     /*0x02*/ s16 frame;
     52     /*0x04*/ f32 translationMultiplier;
     53     /*0x08*/ u16 *attribute;
     54     /*0x0C*/ s16 *data;
     55 };
     56 
     57 // For some reason, this is a GeoAnimState struct, but the current state consists
     58 // of separate global variables. It won't match EU otherwise.
     59 struct GeoAnimState gGeoTempState;
     60 
     61 u8 gCurrAnimType;
     62 u8 gCurrAnimEnabled;
     63 s16 gCurrAnimFrame;
     64 f32 gCurrAnimTranslationMultiplier;
     65 u16 *gCurrAnimAttribute;
     66 s16 *gCurrAnimData;
     67 
     68 struct AllocOnlyPool *gDisplayListHeap;
     69 
     70 struct RenderModeContainer {
     71     u32 modes[8];
     72 };
     73 
     74 /* Rendermode settings for cycle 1 for all 8 layers. */
     75 struct RenderModeContainer renderModeTable_1Cycle[2] = { { {
     76     G_RM_OPA_SURF,
     77     G_RM_AA_OPA_SURF,
     78     G_RM_AA_OPA_SURF,
     79     G_RM_AA_OPA_SURF,
     80     G_RM_AA_TEX_EDGE,
     81     G_RM_AA_XLU_SURF,
     82     G_RM_AA_XLU_SURF,
     83     G_RM_AA_XLU_SURF,
     84     } },
     85     { {
     86     /* z-buffered */
     87     G_RM_ZB_OPA_SURF,
     88     G_RM_AA_ZB_OPA_SURF,
     89     G_RM_AA_ZB_OPA_DECAL,
     90     G_RM_AA_ZB_OPA_INTER,
     91     G_RM_AA_ZB_TEX_EDGE,
     92     G_RM_AA_ZB_XLU_SURF,
     93     G_RM_AA_ZB_XLU_DECAL,
     94     G_RM_AA_ZB_XLU_INTER,
     95     } } };
     96 
     97 /* Rendermode settings for cycle 2 for all 8 layers. */
     98 struct RenderModeContainer renderModeTable_2Cycle[2] = { { {
     99     G_RM_OPA_SURF2,
    100     G_RM_AA_OPA_SURF2,
    101     G_RM_AA_OPA_SURF2,
    102     G_RM_AA_OPA_SURF2,
    103     G_RM_AA_TEX_EDGE2,
    104     G_RM_AA_XLU_SURF2,
    105     G_RM_AA_XLU_SURF2,
    106     G_RM_AA_XLU_SURF2,
    107     } },
    108     { {
    109     /* z-buffered */
    110     G_RM_ZB_OPA_SURF2,
    111     G_RM_AA_ZB_OPA_SURF2,
    112     G_RM_AA_ZB_OPA_DECAL2,
    113     G_RM_AA_ZB_OPA_INTER2,
    114     G_RM_AA_ZB_TEX_EDGE2,
    115     G_RM_AA_ZB_XLU_SURF2,
    116     G_RM_AA_ZB_XLU_DECAL2,
    117     G_RM_AA_ZB_XLU_INTER2,
    118     } } };
    119 
    120 struct GraphNodeRoot *gCurGraphNodeRoot = NULL;
    121 struct GraphNodeMasterList *gCurGraphNodeMasterList = NULL;
    122 struct GraphNodePerspective *gCurGraphNodeCamFrustum = NULL;
    123 struct GraphNodeCamera *gCurGraphNodeCamera = NULL;
    124 struct GraphNodeObject *gCurGraphNodeObject = NULL;
    125 struct GraphNodeHeldObject *gCurGraphNodeHeldObject = NULL;
    126 u16 gAreaUpdateCounter = 0;
    127 
    128 #ifdef F3DEX_GBI_2
    129 LookAt lookAt;
    130 #endif
    131 
    132 /**
    133  * Process a master list node.
    134  */
    135 static void geo_process_master_list_sub(struct GraphNodeMasterList *node) {
    136     struct DisplayListNode *currList;
    137     s32 i;
    138     s32 enableZBuffer = (node->node.flags & GRAPH_RENDER_Z_BUFFER) != 0;
    139     struct RenderModeContainer *modeList = &renderModeTable_1Cycle[enableZBuffer];
    140     struct RenderModeContainer *mode2List = &renderModeTable_2Cycle[enableZBuffer];
    141 
    142     // @bug This is where the LookAt values should be calculated but aren't.
    143     // As a result, environment mapping is broken on Fast3DEX2 without the
    144     // changes below.
    145 #ifdef F3DEX_GBI_2
    146     Mtx lMtx;
    147     guLookAtReflect(&lMtx, &lookAt, 0, 0, 0, /* eye */ 0, 0, 1, /* at */ 1, 0, 0 /* up */);
    148 #endif
    149 
    150     if (enableZBuffer != 0) {
    151         gDPPipeSync(gDisplayListHead++);
    152         gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER);
    153     }
    154 
    155     for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) {
    156         if ((currList = node->listHeads[i]) != NULL) {
    157             gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]);
    158             while (currList != NULL) {
    159                 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transform),
    160                           G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
    161                 gSPDisplayList(gDisplayListHead++, currList->displayList);
    162                 currList = currList->next;
    163             }
    164         }
    165     }
    166     if (enableZBuffer != 0) {
    167         gDPPipeSync(gDisplayListHead++);
    168         gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER);
    169     }
    170 }
    171 
    172 /**
    173  * Appends the display list to one of the master lists based on the layer
    174  * parameter. Look at the RenderModeContainer struct to see the corresponding
    175  * render modes of layers.
    176  */
    177 static void geo_append_display_list(void *displayList, s16 layer) {
    178 
    179 #ifdef F3DEX_GBI_2
    180     gSPLookAt(gDisplayListHead++, &lookAt);
    181 #endif
    182     if (gCurGraphNodeMasterList != 0) {
    183         struct DisplayListNode *listNode =
    184             alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode));
    185 
    186         listNode->transform = gMatStackFixed[gMatStackIndex];
    187         listNode->displayList = displayList;
    188         listNode->next = 0;
    189         if (gCurGraphNodeMasterList->listHeads[layer] == 0) {
    190             gCurGraphNodeMasterList->listHeads[layer] = listNode;
    191         } else {
    192             gCurGraphNodeMasterList->listTails[layer]->next = listNode;
    193         }
    194         gCurGraphNodeMasterList->listTails[layer] = listNode;
    195     }
    196 }
    197 
    198 /**
    199  * Process the master list node.
    200  */
    201 static void geo_process_master_list(struct GraphNodeMasterList *node) {
    202     s32 i;
    203     UNUSED u8 filler[4];
    204 
    205     if (gCurGraphNodeMasterList == NULL && node->node.children != NULL) {
    206         gCurGraphNodeMasterList = node;
    207         for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) {
    208             node->listHeads[i] = NULL;
    209         }
    210         geo_process_node_and_siblings(node->node.children);
    211         geo_process_master_list_sub(node);
    212         gCurGraphNodeMasterList = NULL;
    213     }
    214 }
    215 
    216 /**
    217  * Process an orthographic projection node.
    218  */
    219 static void geo_process_ortho_projection(struct GraphNodeOrthoProjection *node) {
    220     if (node->node.children != NULL) {
    221         Mtx *mtx = alloc_display_list(sizeof(*mtx));
    222         f32 left = (gCurGraphNodeRoot->x - gCurGraphNodeRoot->width) / 2.0f * node->scale;
    223         f32 right = (gCurGraphNodeRoot->x + gCurGraphNodeRoot->width) / 2.0f * node->scale;
    224         f32 top = (gCurGraphNodeRoot->y - gCurGraphNodeRoot->height) / 2.0f * node->scale;
    225         f32 bottom = (gCurGraphNodeRoot->y + gCurGraphNodeRoot->height) / 2.0f * node->scale;
    226 
    227         guOrtho(mtx, left, right, bottom, top, -2.0f, 2.0f, 1.0f);
    228         gSPPerspNormalize(gDisplayListHead++, 0xFFFF);
    229         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
    230 
    231         geo_process_node_and_siblings(node->node.children);
    232     }
    233 }
    234 
    235 /**
    236  * Process a perspective projection node.
    237  */
    238 static void geo_process_perspective(struct GraphNodePerspective *node) {
    239     if (node->fnNode.func != NULL) {
    240         node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
    241     }
    242     if (node->fnNode.node.children != NULL) {
    243         u16 perspNorm;
    244         Mtx *mtx = alloc_display_list(sizeof(*mtx));
    245 
    246 #ifdef VERSION_EU
    247         f32 aspect = ((f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height) * 1.1f;
    248 #else
    249         f32 aspect = (f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height;
    250 #endif
    251 
    252         guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f);
    253         gSPPerspNormalize(gDisplayListHead++, perspNorm);
    254 
    255         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
    256 
    257         gCurGraphNodeCamFrustum = node;
    258         geo_process_node_and_siblings(node->fnNode.node.children);
    259         gCurGraphNodeCamFrustum = NULL;
    260     }
    261 }
    262 
    263 /**
    264  * Process a level of detail node. From the current transformation matrix,
    265  * the perpendicular distance to the camera is extracted and the children
    266  * of this node are only processed if that distance is within the render
    267  * range of this node.
    268  */
    269 static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) {
    270 #ifdef GBI_FLOATS
    271     Mtx *mtx = gMatStackFixed[gMatStackIndex];
    272     s16 distanceFromCam = (s32) -mtx->m[3][2]; // z-component of the translation column
    273 #else
    274     // The fixed point Mtx type is defined as 16 longs, but it's actually 16
    275     // shorts for the integer parts followed by 16 shorts for the fraction parts
    276     Mtx *mtx = gMatStackFixed[gMatStackIndex];
    277     s16 distanceFromCam = -GET_HIGH_S16_OF_32(mtx->m[1][3]); // z-component of the translation column
    278 #endif
    279 
    280     if (node->minDistance <= distanceFromCam && distanceFromCam < node->maxDistance) {
    281         if (node->node.children != 0) {
    282             geo_process_node_and_siblings(node->node.children);
    283         }
    284     }
    285 }
    286 
    287 /**
    288  * Process a switch case node. The node's selection function is called
    289  * if it is 0, and among the node's children, only the selected child is
    290  * processed next.
    291  */
    292 static void geo_process_switch(struct GraphNodeSwitchCase *node) {
    293     struct GraphNode *selectedChild = node->fnNode.node.children;
    294     s32 i;
    295 
    296     if (node->fnNode.func != NULL) {
    297         node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
    298     }
    299     for (i = 0; selectedChild != NULL && node->selectedCase > i; i++) {
    300         selectedChild = selectedChild->next;
    301     }
    302     if (selectedChild != NULL) {
    303         geo_process_node_and_siblings(selectedChild);
    304     }
    305 }
    306 
    307 /**
    308  * Process a camera node.
    309  */
    310 static void geo_process_camera(struct GraphNodeCamera *node) {
    311     Mat4 cameraTransform;
    312     Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx));
    313     Mtx *mtx = alloc_display_list(sizeof(*mtx));
    314 
    315     if (node->fnNode.func != NULL) {
    316         node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
    317     }
    318     mtxf_rotate_xy(rollMtx, node->rollScreen);
    319 
    320     gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(rollMtx), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH);
    321 
    322     mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll);
    323     mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]);
    324     gMatStackIndex++;
    325     mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    326     gMatStackFixed[gMatStackIndex] = mtx;
    327     if (node->fnNode.node.children != 0) {
    328         gCurGraphNodeCamera = node;
    329         node->matrixPtr = &gMatStack[gMatStackIndex];
    330         geo_process_node_and_siblings(node->fnNode.node.children);
    331         gCurGraphNodeCamera = NULL;
    332     }
    333     gMatStackIndex--;
    334 }
    335 
    336 /**
    337  * Process a translation / rotation node. A transformation matrix based
    338  * on the node's translation and rotation is created and pushed on both
    339  * the float and fixed point matrix stacks.
    340  * For the rest it acts as a normal display list node.
    341  */
    342 static void geo_process_translation_rotation(struct GraphNodeTranslationRotation *node) {
    343     Mat4 mtxf;
    344     Vec3f translation;
    345     Mtx *mtx = alloc_display_list(sizeof(*mtx));
    346 
    347     vec3s_to_vec3f(translation, node->translation);
    348     mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation);
    349     mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
    350     gMatStackIndex++;
    351     mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    352     gMatStackFixed[gMatStackIndex] = mtx;
    353     if (node->displayList != NULL) {
    354         geo_append_display_list(node->displayList, node->node.flags >> 8);
    355     }
    356     if (node->node.children != NULL) {
    357         geo_process_node_and_siblings(node->node.children);
    358     }
    359     gMatStackIndex--;
    360 }
    361 
    362 /**
    363  * Process a translation node. A transformation matrix based on the node's
    364  * translation is created and pushed on both the float and fixed point matrix stacks.
    365  * For the rest it acts as a normal display list node.
    366  */
    367 static void geo_process_translation(struct GraphNodeTranslation *node) {
    368     Mat4 mtxf;
    369     Vec3f translation;
    370     Mtx *mtx = alloc_display_list(sizeof(*mtx));
    371 
    372     vec3s_to_vec3f(translation, node->translation);
    373     mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero);
    374     mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
    375     gMatStackIndex++;
    376     mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    377     gMatStackFixed[gMatStackIndex] = mtx;
    378     if (node->displayList != NULL) {
    379         geo_append_display_list(node->displayList, node->node.flags >> 8);
    380     }
    381     if (node->node.children != NULL) {
    382         geo_process_node_and_siblings(node->node.children);
    383     }
    384     gMatStackIndex--;
    385 }
    386 
    387 /**
    388  * Process a rotation node. A transformation matrix based on the node's
    389  * rotation is created and pushed on both the float and fixed point matrix stacks.
    390  * For the rest it acts as a normal display list node.
    391  */
    392 static void geo_process_rotation(struct GraphNodeRotation *node) {
    393     Mat4 mtxf;
    394     Mtx *mtx = alloc_display_list(sizeof(*mtx));
    395 
    396     mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation);
    397     mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
    398     gMatStackIndex++;
    399     mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    400     gMatStackFixed[gMatStackIndex] = mtx;
    401     if (node->displayList != NULL) {
    402         geo_append_display_list(node->displayList, node->node.flags >> 8);
    403     }
    404     if (node->node.children != NULL) {
    405         geo_process_node_and_siblings(node->node.children);
    406     }
    407     gMatStackIndex--;
    408 }
    409 
    410 /**
    411  * Process a scaling node. A transformation matrix based on the node's
    412  * scale is created and pushed on both the float and fixed point matrix stacks.
    413  * For the rest it acts as a normal display list node.
    414  */
    415 static void geo_process_scale(struct GraphNodeScale *node) {
    416     UNUSED Mat4 transform;
    417     Vec3f scaleVec;
    418     Mtx *mtx = alloc_display_list(sizeof(*mtx));
    419 
    420     vec3f_set(scaleVec, node->scale, node->scale, node->scale);
    421     mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec);
    422     gMatStackIndex++;
    423     mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    424     gMatStackFixed[gMatStackIndex] = mtx;
    425     if (node->displayList != NULL) {
    426         geo_append_display_list(node->displayList, node->node.flags >> 8);
    427     }
    428     if (node->node.children != NULL) {
    429         geo_process_node_and_siblings(node->node.children);
    430     }
    431     gMatStackIndex--;
    432 }
    433 
    434 /**
    435  * Process a billboard node. A transformation matrix is created that makes its
    436  * children face the camera, and it is pushed on the floating point and fixed
    437  * point matrix stacks.
    438  * For the rest it acts as a normal display list node.
    439  */
    440 static void geo_process_billboard(struct GraphNodeBillboard *node) {
    441     Vec3f translation;
    442     Mtx *mtx = alloc_display_list(sizeof(*mtx));
    443 
    444     gMatStackIndex++;
    445     vec3s_to_vec3f(translation, node->translation);
    446     mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation,
    447                    gCurGraphNodeCamera->roll);
    448     if (gCurGraphNodeHeldObject != NULL) {
    449         mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex],
    450                          gCurGraphNodeHeldObject->objNode->header.gfx.scale);
    451     } else if (gCurGraphNodeObject != NULL) {
    452         mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex],
    453                          gCurGraphNodeObject->scale);
    454     }
    455 
    456     mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    457     gMatStackFixed[gMatStackIndex] = mtx;
    458 
    459     if (node->displayList != NULL) {
    460         geo_append_display_list(node->displayList, node->node.flags >> 8);
    461     }
    462 
    463     if (node->node.children != NULL) {
    464         geo_process_node_and_siblings(node->node.children);
    465     }
    466 
    467     gMatStackIndex--;
    468 }
    469 
    470 /**
    471  * Process a display list node. It draws a display list without first pushing
    472  * a transformation on the stack, so all transformations are inherited from the
    473  * parent node. It processes its children if it has them.
    474  */
    475 static void geo_process_display_list(struct GraphNodeDisplayList *node) {
    476     if (node->displayList != NULL) {
    477         geo_append_display_list(node->displayList, node->node.flags >> 8);
    478     }
    479     if (node->node.children != NULL) {
    480         geo_process_node_and_siblings(node->node.children);
    481     }
    482 }
    483 
    484 /**
    485  * Process a generated list. Instead of storing a pointer to a display list,
    486  * the list is generated on the fly by a function.
    487  */
    488 static void geo_process_generated_list(struct GraphNodeGenerated *node) {
    489     if (node->fnNode.func != NULL) {
    490         Gfx *list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node,
    491                                      (struct AllocOnlyPool *) gMatStack[gMatStackIndex]);
    492 
    493         if (list != NULL) {
    494             geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8);
    495         }
    496     }
    497     if (node->fnNode.node.children != NULL) {
    498         geo_process_node_and_siblings(node->fnNode.node.children);
    499     }
    500 }
    501 
    502 /**
    503  * Process a background node. Tries to retrieve a background display list from
    504  * the function of the node. If that function is null or returns null, a black
    505  * rectangle is drawn instead.
    506  */
    507 static void geo_process_background(struct GraphNodeBackground *node) {
    508     Gfx *list = NULL;
    509 
    510     if (node->fnNode.func != NULL) {
    511         list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node,
    512                                  (struct AllocOnlyPool *) gMatStack[gMatStackIndex]);
    513     }
    514     if (list != NULL) {
    515         geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8);
    516     } else if (gCurGraphNodeMasterList != NULL) {
    517 #ifndef F3DEX_GBI_2E
    518         Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7);
    519 #else
    520         Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 8);
    521 #endif
    522         Gfx *gfx = gfxStart;
    523 
    524         gDPPipeSync(gfx++);
    525         gDPSetCycleType(gfx++, G_CYC_FILL);
    526         gDPSetFillColor(gfx++, node->background);
    527         gDPFillRectangle(gfx++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), BORDER_HEIGHT,
    528         GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - BORDER_HEIGHT - 1);
    529         gDPPipeSync(gfx++);
    530         gDPSetCycleType(gfx++, G_CYC_1CYCLE);
    531         gSPEndDisplayList(gfx++);
    532 
    533         geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(gfxStart), 0);
    534     }
    535     if (node->fnNode.node.children != NULL) {
    536         geo_process_node_and_siblings(node->fnNode.node.children);
    537     }
    538 }
    539 
    540 /**
    541  * Render an animated part. The current animation state is not part of the node
    542  * but set in global variables. If an animated part is skipped, everything afterwards desyncs.
    543  */
    544 static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
    545     Mat4 matrix;
    546     Vec3s rotation;
    547     Vec3f translation;
    548     Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr));
    549 
    550     vec3s_copy(rotation, gVec3sZero);
    551     vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]);
    552     if (gCurrAnimType == ANIM_TYPE_TRANSLATION) {
    553         translation[0] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    554                           * gCurrAnimTranslationMultiplier;
    555         translation[1] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    556                           * gCurrAnimTranslationMultiplier;
    557         translation[2] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    558                           * gCurrAnimTranslationMultiplier;
    559         gCurrAnimType = ANIM_TYPE_ROTATION;
    560     } else {
    561         if (gCurrAnimType == ANIM_TYPE_LATERAL_TRANSLATION) {
    562             translation[0] +=
    563                 gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    564                 * gCurrAnimTranslationMultiplier;
    565             gCurrAnimAttribute += 2;
    566             translation[2] +=
    567                 gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    568                 * gCurrAnimTranslationMultiplier;
    569             gCurrAnimType = ANIM_TYPE_ROTATION;
    570         } else {
    571             if (gCurrAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) {
    572                 gCurrAnimAttribute += 2;
    573                 translation[1] +=
    574                     gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    575                     * gCurrAnimTranslationMultiplier;
    576                 gCurrAnimAttribute += 2;
    577                 gCurrAnimType = ANIM_TYPE_ROTATION;
    578             } else if (gCurrAnimType == ANIM_TYPE_NO_TRANSLATION) {
    579                 gCurrAnimAttribute += 6;
    580                 gCurrAnimType = ANIM_TYPE_ROTATION;
    581             }
    582         }
    583     }
    584 
    585     if (gCurrAnimType == ANIM_TYPE_ROTATION) {
    586         rotation[0] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
    587         rotation[1] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
    588         rotation[2] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
    589     }
    590     mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
    591     mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
    592     gMatStackIndex++;
    593     mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]);
    594     gMatStackFixed[gMatStackIndex] = matrixPtr;
    595     if (node->displayList != NULL) {
    596         geo_append_display_list(node->displayList, node->node.flags >> 8);
    597     }
    598     if (node->node.children != NULL) {
    599         geo_process_node_and_siblings(node->node.children);
    600     }
    601     gMatStackIndex--;
    602 }
    603 
    604 /**
    605  * Initialize the animation-related global variables for the currently drawn
    606  * object's animation.
    607  */
    608 void geo_set_animation_globals(struct AnimInfo *node, s32 hasAnimation) {
    609     struct Animation *anim = node->curAnim;
    610 
    611     if (hasAnimation) {
    612         node->animFrame = geo_update_animation_frame(node, &node->animFrameAccelAssist);
    613     }
    614     node->animTimer = gAreaUpdateCounter;
    615     if (anim->flags & ANIM_FLAG_HOR_TRANS) {
    616         gCurrAnimType = ANIM_TYPE_VERTICAL_TRANSLATION;
    617     } else if (anim->flags & ANIM_FLAG_VERT_TRANS) {
    618         gCurrAnimType = ANIM_TYPE_LATERAL_TRANSLATION;
    619     } else if (anim->flags & ANIM_FLAG_6) {
    620         gCurrAnimType = ANIM_TYPE_NO_TRANSLATION;
    621     } else {
    622         gCurrAnimType = ANIM_TYPE_TRANSLATION;
    623     }
    624 
    625     gCurrAnimFrame = node->animFrame;
    626     gCurrAnimEnabled = (anim->flags & ANIM_FLAG_5) == 0;
    627     gCurrAnimAttribute = segmented_to_virtual((void *) anim->index);
    628     gCurrAnimData = segmented_to_virtual((void *) anim->values);
    629 
    630     if (anim->animYTransDivisor == 0) {
    631         gCurrAnimTranslationMultiplier = 1.0f;
    632     } else {
    633         gCurrAnimTranslationMultiplier = (f32) node->animYTrans / (f32) anim->animYTransDivisor;
    634     }
    635 }
    636 
    637 /**
    638  * Process a shadow node. Renders a shadow under an object offset by the
    639  * translation of the first animated component and rotated according to
    640  * the floor below it.
    641  */
    642 static void geo_process_shadow(struct GraphNodeShadow *node) {
    643     Gfx *shadowList;
    644     Mat4 mtxf;
    645     Vec3f shadowPos;
    646     Vec3f animOffset;
    647     f32 objScale;
    648     f32 shadowScale;
    649     f32 sinAng;
    650     f32 cosAng;
    651     struct GraphNode *geo;
    652     Mtx *mtx;
    653 
    654     if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) {
    655         if (gCurGraphNodeHeldObject != NULL) {
    656             get_pos_from_transform_mtx(shadowPos, gMatStack[gMatStackIndex],
    657                                        *gCurGraphNodeCamera->matrixPtr);
    658             shadowScale = node->shadowScale;
    659         } else {
    660             vec3f_copy(shadowPos, gCurGraphNodeObject->pos);
    661             shadowScale = node->shadowScale * gCurGraphNodeObject->scale[0];
    662         }
    663 
    664         objScale = 1.0f;
    665         if (gCurrAnimEnabled) {
    666             if (gCurrAnimType == ANIM_TYPE_TRANSLATION
    667                 || gCurrAnimType == ANIM_TYPE_LATERAL_TRANSLATION) {
    668                 geo = node->node.children;
    669                 if (geo != NULL && geo->type == GRAPH_NODE_TYPE_SCALE) {
    670                     objScale = ((struct GraphNodeScale *) geo)->scale;
    671                 }
    672                 animOffset[0] =
    673                     gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    674                     * gCurrAnimTranslationMultiplier * objScale;
    675                 animOffset[1] = 0.0f;
    676                 gCurrAnimAttribute += 2;
    677                 animOffset[2] =
    678                     gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
    679                     * gCurrAnimTranslationMultiplier * objScale;
    680                 gCurrAnimAttribute -= 6;
    681 
    682                 // simple matrix rotation so the shadow offset rotates along with the object
    683                 sinAng = sins(gCurGraphNodeObject->angle[1]);
    684                 cosAng = coss(gCurGraphNodeObject->angle[1]);
    685 
    686                 shadowPos[0] += animOffset[0] * cosAng + animOffset[2] * sinAng;
    687                 shadowPos[2] += -animOffset[0] * sinAng + animOffset[2] * cosAng;
    688             }
    689         }
    690 
    691         shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale,
    692                                              node->shadowSolidity, node->shadowType);
    693         if (shadowList != NULL) {
    694             mtx = alloc_display_list(sizeof(*mtx));
    695             gMatStackIndex++;
    696             mtxf_translate(mtxf, shadowPos);
    697             mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr);
    698             mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    699             gMatStackFixed[gMatStackIndex] = mtx;
    700             if (gShadowAboveWaterOrLava == TRUE) {
    701                 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 4);
    702             } else if (gMarioOnIceOrCarpet == 1) {
    703                 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 5);
    704             } else {
    705                 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 6);
    706             }
    707             gMatStackIndex--;
    708         }
    709     }
    710 
    711     if (node->node.children != NULL) {
    712         geo_process_node_and_siblings(node->node.children);
    713     }
    714 }
    715 
    716 /**
    717  * Check whether an object is in view to determine whether it should be drawn.
    718  * This is known as frustum culling.
    719  * It checks whether the object is far away, very close / behind the camera,
    720  * or horizontally out of view. It does not check whether it is vertically
    721  * out of view. It assumes a sphere of 300 units around the object's position
    722  * unless the object has a culling radius node that specifies otherwise.
    723  *
    724  * The matrix parameter should be the top of the matrix stack, which is the
    725  * object's transformation matrix times the camera 'look-at' matrix. The math
    726  * is counter-intuitive, but it checks column 3 (translation vector) of this
    727  * matrix to determine where the origin (0,0,0) in object space will be once
    728  * transformed to camera space (x+ = right, y+ = up, z = 'coming out the screen').
    729  * In 3D graphics, you typically model the world as being moved in front of a
    730  * static camera instead of a moving camera through a static world, which in
    731  * this case simplifies calculations. Note that the perspective matrix is not
    732  * on the matrix stack, so there are still calculations with the fov to compute
    733  * the slope of the lines of the frustum.
    734  *
    735  *        z-
    736  *
    737  *  \     |     /
    738  *   \    |    /
    739  *    \   |   /
    740  *     \  |  /
    741  *      \ | /
    742  *       \|/
    743  *        C       x+
    744  *
    745  * Since (0,0,0) is unaffected by rotation, columns 0, 1 and 2 are ignored.
    746  */
    747 static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) {
    748     s16 cullingRadius;
    749     s16 halfFov; // half of the fov in in-game angle units instead of degrees
    750     struct GraphNode *geo;
    751     f32 hScreenEdge;
    752 
    753     if (node->node.flags & GRAPH_RENDER_INVISIBLE) {
    754         return FALSE;
    755     }
    756 
    757     geo = node->sharedChild;
    758 
    759     // ! @bug The aspect ratio is not accounted for. When the fov value is 45,
    760     // the horizontal effective fov is actually 60 degrees, so you can see objects
    761     // visibly pop in or out at the edge of the screen.
    762     halfFov = (gCurGraphNodeCamFrustum->fov / 2.0f + 1.0f) * 32768.0f / 180.0f + 0.5f;
    763 
    764     hScreenEdge = -matrix[3][2] * sins(halfFov) / coss(halfFov);
    765     // -matrix[3][2] is the depth, which gets multiplied by tan(halfFov) to get
    766     // the amount of units between the center of the screen and the horizontal edge
    767     // given the distance from the object to the camera.
    768 
    769 #ifdef WIDESCREEN
    770     // This multiplication should really be performed on 4:3 as well,
    771     // but the issue will be more apparent on widescreen.
    772     hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO;
    773 #endif
    774 
    775     if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) {
    776         cullingRadius =
    777             (f32)((struct GraphNodeCullingRadius *) geo)->cullingRadius; //! Why is there a f32 cast?
    778     } else {
    779         cullingRadius = 300;
    780     }
    781 
    782     // Don't render if the object is close to or behind the camera
    783     if (matrix[3][2] > -100.0f + cullingRadius) {
    784         return FALSE;
    785     }
    786 
    787     //! This makes the HOLP not update when the camera is far away, and it
    788     //  makes PU travel safe when the camera is locked on the main map.
    789     //  If Mario were rendered with a depth over 65536 it would cause overflow
    790     //  when converting the transformation matrix to a fixed point matrix.
    791     if (matrix[3][2] < -20000.0f - cullingRadius) {
    792         return FALSE;
    793     }
    794 
    795     // Check whether the object is horizontally in view
    796     if (matrix[3][0] > hScreenEdge + cullingRadius) {
    797         return FALSE;
    798     }
    799     if (matrix[3][0] < -hScreenEdge - cullingRadius) {
    800         return FALSE;
    801     }
    802     return TRUE;
    803 }
    804 
    805 /**
    806  * Process an object node.
    807  */
    808 static void geo_process_object(struct Object *node) {
    809     Mat4 mtxf;
    810     s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0;
    811 
    812     if (node->header.gfx.areaIndex == gCurGraphNodeRoot->areaIndex) {
    813         if (node->header.gfx.throwMatrix != NULL) {
    814             mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix,
    815                      gMatStack[gMatStackIndex]);
    816         } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) {
    817             mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
    818                            node->header.gfx.pos, gCurGraphNodeCamera->roll);
    819         } else {
    820             mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle);
    821             mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
    822         }
    823 
    824         mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1],
    825                          node->header.gfx.scale);
    826         node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex];
    827         node->header.gfx.cameraToObject[0] = gMatStack[gMatStackIndex][3][0];
    828         node->header.gfx.cameraToObject[1] = gMatStack[gMatStackIndex][3][1];
    829         node->header.gfx.cameraToObject[2] = gMatStack[gMatStackIndex][3][2];
    830 
    831         // FIXME: correct types
    832         if (node->header.gfx.animInfo.curAnim != NULL) {
    833             geo_set_animation_globals(&node->header.gfx.animInfo, hasAnimation);
    834         }
    835         if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) {
    836             Mtx *mtx = alloc_display_list(sizeof(*mtx));
    837 
    838             mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    839             gMatStackFixed[gMatStackIndex] = mtx;
    840             if (node->header.gfx.sharedChild != NULL) {
    841                 gCurGraphNodeObject = (struct GraphNodeObject *) node;
    842                 node->header.gfx.sharedChild->parent = &node->header.gfx.node;
    843                 geo_process_node_and_siblings(node->header.gfx.sharedChild);
    844                 node->header.gfx.sharedChild->parent = NULL;
    845                 gCurGraphNodeObject = NULL;
    846             }
    847             if (node->header.gfx.node.children != NULL) {
    848                 geo_process_node_and_siblings(node->header.gfx.node.children);
    849             }
    850         }
    851 
    852         gMatStackIndex--;
    853         gCurrAnimType = ANIM_TYPE_NONE;
    854         node->header.gfx.throwMatrix = NULL;
    855     }
    856 }
    857 
    858 /**
    859  * Process an object parent node. Temporarily assigns itself as the parent of
    860  * the subtree rooted at 'sharedChild' and processes the subtree, after which the
    861  * actual children are be processed. (in practice they are null though)
    862  */
    863 static void geo_process_object_parent(struct GraphNodeObjectParent *node) {
    864     if (node->sharedChild != NULL) {
    865         node->sharedChild->parent = (struct GraphNode *) node;
    866         geo_process_node_and_siblings(node->sharedChild);
    867         node->sharedChild->parent = NULL;
    868     }
    869     if (node->node.children != NULL) {
    870         geo_process_node_and_siblings(node->node.children);
    871     }
    872 }
    873 
    874 /**
    875  * Process a held object node.
    876  */
    877 void geo_process_held_object(struct GraphNodeHeldObject *node) {
    878     Mat4 mat;
    879     Vec3f translation;
    880     Mtx *mtx = alloc_display_list(sizeof(*mtx));
    881 
    882 #ifdef F3DEX_GBI_2
    883     gSPLookAt(gDisplayListHead++, &lookAt);
    884 #endif
    885 
    886     if (node->fnNode.func != NULL) {
    887         node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
    888     }
    889     if (node->objNode != NULL && node->objNode->header.gfx.sharedChild != NULL) {
    890         s32 hasAnimation = (node->objNode->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0;
    891 
    892         translation[0] = node->translation[0] / 4.0f;
    893         translation[1] = node->translation[1] / 4.0f;
    894         translation[2] = node->translation[2] / 4.0f;
    895 
    896         mtxf_translate(mat, translation);
    897         mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix);
    898         gMatStack[gMatStackIndex + 1][3][0] = gMatStack[gMatStackIndex][3][0];
    899         gMatStack[gMatStackIndex + 1][3][1] = gMatStack[gMatStackIndex][3][1];
    900         gMatStack[gMatStackIndex + 1][3][2] = gMatStack[gMatStackIndex][3][2];
    901         mtxf_mul(gMatStack[gMatStackIndex + 1], mat, gMatStack[gMatStackIndex + 1]);
    902         mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1],
    903                          node->objNode->header.gfx.scale);
    904         if (node->fnNode.func != NULL) {
    905             node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node,
    906                               (struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]);
    907         }
    908         gMatStackIndex++;
    909         mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
    910         gMatStackFixed[gMatStackIndex] = mtx;
    911         gGeoTempState.type = gCurrAnimType;
    912         gGeoTempState.enabled = gCurrAnimEnabled;
    913         gGeoTempState.frame = gCurrAnimFrame;
    914         gGeoTempState.translationMultiplier = gCurrAnimTranslationMultiplier;
    915         gGeoTempState.attribute = gCurrAnimAttribute;
    916         gGeoTempState.data = gCurrAnimData;
    917         gCurrAnimType = 0;
    918         gCurGraphNodeHeldObject = (void *) node;
    919         if (node->objNode->header.gfx.animInfo.curAnim != NULL) {
    920             geo_set_animation_globals(&node->objNode->header.gfx.animInfo, hasAnimation);
    921         }
    922 
    923         geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild);
    924         gCurGraphNodeHeldObject = NULL;
    925         gCurrAnimType = gGeoTempState.type;
    926         gCurrAnimEnabled = gGeoTempState.enabled;
    927         gCurrAnimFrame = gGeoTempState.frame;
    928         gCurrAnimTranslationMultiplier = gGeoTempState.translationMultiplier;
    929         gCurrAnimAttribute = gGeoTempState.attribute;
    930         gCurrAnimData = gGeoTempState.data;
    931         gMatStackIndex--;
    932     }
    933 
    934     if (node->fnNode.node.children != NULL) {
    935         geo_process_node_and_siblings(node->fnNode.node.children);
    936     }
    937 }
    938 
    939 /**
    940  * Processes the children of the given GraphNode if it has any
    941  */
    942 void geo_try_process_children(struct GraphNode *node) {
    943     if (node->children != NULL) {
    944         geo_process_node_and_siblings(node->children);
    945     }
    946 }
    947 
    948 /**
    949  * Process a generic geo node and its siblings.
    950  * The first argument is the start node, and all its siblings will
    951  * be iterated over.
    952  */
    953 void geo_process_node_and_siblings(struct GraphNode *firstNode) {
    954     s16 iterateChildren = TRUE;
    955     struct GraphNode *curGraphNode = firstNode;
    956     struct GraphNode *parent = curGraphNode->parent;
    957 
    958     // In the case of a switch node, exactly one of the children of the node is
    959     // processed instead of all children like usual
    960     if (parent != NULL) {
    961         iterateChildren = (parent->type != GRAPH_NODE_TYPE_SWITCH_CASE);
    962     }
    963 
    964     do {
    965         if (curGraphNode->flags & GRAPH_RENDER_ACTIVE) {
    966             if (curGraphNode->flags & GRAPH_RENDER_CHILDREN_FIRST) {
    967                 geo_try_process_children(curGraphNode);
    968             } else {
    969                 switch (curGraphNode->type) {
    970                     case GRAPH_NODE_TYPE_ORTHO_PROJECTION:
    971                         geo_process_ortho_projection((struct GraphNodeOrthoProjection *) curGraphNode);
    972                         break;
    973                     case GRAPH_NODE_TYPE_PERSPECTIVE:
    974                         geo_process_perspective((struct GraphNodePerspective *) curGraphNode);
    975                         break;
    976                     case GRAPH_NODE_TYPE_MASTER_LIST:
    977                         geo_process_master_list((struct GraphNodeMasterList *) curGraphNode);
    978                         break;
    979                     case GRAPH_NODE_TYPE_LEVEL_OF_DETAIL:
    980                         geo_process_level_of_detail((struct GraphNodeLevelOfDetail *) curGraphNode);
    981                         break;
    982                     case GRAPH_NODE_TYPE_SWITCH_CASE:
    983                         geo_process_switch((struct GraphNodeSwitchCase *) curGraphNode);
    984                         break;
    985                     case GRAPH_NODE_TYPE_CAMERA:
    986                         geo_process_camera((struct GraphNodeCamera *) curGraphNode);
    987                         break;
    988                     case GRAPH_NODE_TYPE_TRANSLATION_ROTATION:
    989                         geo_process_translation_rotation(
    990                             (struct GraphNodeTranslationRotation *) curGraphNode);
    991                         break;
    992                     case GRAPH_NODE_TYPE_TRANSLATION:
    993                         geo_process_translation((struct GraphNodeTranslation *) curGraphNode);
    994                         break;
    995                     case GRAPH_NODE_TYPE_ROTATION:
    996                         geo_process_rotation((struct GraphNodeRotation *) curGraphNode);
    997                         break;
    998                     case GRAPH_NODE_TYPE_OBJECT:
    999                         geo_process_object((struct Object *) curGraphNode);
   1000                         break;
   1001                     case GRAPH_NODE_TYPE_ANIMATED_PART:
   1002                         geo_process_animated_part((struct GraphNodeAnimatedPart *) curGraphNode);
   1003                         break;
   1004                     case GRAPH_NODE_TYPE_BILLBOARD:
   1005                         geo_process_billboard((struct GraphNodeBillboard *) curGraphNode);
   1006                         break;
   1007                     case GRAPH_NODE_TYPE_DISPLAY_LIST:
   1008                         geo_process_display_list((struct GraphNodeDisplayList *) curGraphNode);
   1009                         break;
   1010                     case GRAPH_NODE_TYPE_SCALE:
   1011                         geo_process_scale((struct GraphNodeScale *) curGraphNode);
   1012                         break;
   1013                     case GRAPH_NODE_TYPE_SHADOW:
   1014                         geo_process_shadow((struct GraphNodeShadow *) curGraphNode);
   1015                         break;
   1016                     case GRAPH_NODE_TYPE_OBJECT_PARENT:
   1017                         geo_process_object_parent((struct GraphNodeObjectParent *) curGraphNode);
   1018                         break;
   1019                     case GRAPH_NODE_TYPE_GENERATED_LIST:
   1020                         geo_process_generated_list((struct GraphNodeGenerated *) curGraphNode);
   1021                         break;
   1022                     case GRAPH_NODE_TYPE_BACKGROUND:
   1023                         geo_process_background((struct GraphNodeBackground *) curGraphNode);
   1024                         break;
   1025                     case GRAPH_NODE_TYPE_HELD_OBJ:
   1026                         geo_process_held_object((struct GraphNodeHeldObject *) curGraphNode);
   1027                         break;
   1028                     default:
   1029                         geo_try_process_children((struct GraphNode *) curGraphNode);
   1030                         break;
   1031                 }
   1032             }
   1033         } else {
   1034             if (curGraphNode->type == GRAPH_NODE_TYPE_OBJECT) {
   1035                 ((struct GraphNodeObject *) curGraphNode)->throwMatrix = NULL;
   1036             }
   1037         }
   1038     } while (iterateChildren && (curGraphNode = curGraphNode->next) != firstNode);
   1039 }
   1040 
   1041 /**
   1042  * Process a root node. This is the entry point for processing the scene graph.
   1043  * The root node itself sets up the viewport, then all its children are processed
   1044  * to set up the projection and draw display lists.
   1045  */
   1046 void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) {
   1047     UNUSED u8 filler[4];
   1048 
   1049     if (node->node.flags & GRAPH_RENDER_ACTIVE) {
   1050         Mtx *initialMatrix;
   1051         Vp *viewport = alloc_display_list(sizeof(*viewport));
   1052 
   1053         gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool),
   1054                                                 MEMORY_POOL_LEFT);
   1055         initialMatrix = alloc_display_list(sizeof(*initialMatrix));
   1056         gMatStackIndex = 0;
   1057         gCurrAnimType = 0;
   1058         vec3s_set(viewport->vp.vtrans, node->x * 4, node->y * 4, 511);
   1059         vec3s_set(viewport->vp.vscale, node->width * 4, node->height * 4, 511);
   1060         if (b != NULL) {
   1061             clear_framebuffer(clearColor);
   1062             make_viewport_clip_rect(b);
   1063             *viewport = *b;
   1064         }
   1065 
   1066         else if (c != NULL) {
   1067             clear_framebuffer(clearColor);
   1068             make_viewport_clip_rect(c);
   1069         }
   1070 
   1071         mtxf_identity(gMatStack[gMatStackIndex]);
   1072         mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]);
   1073         gMatStackFixed[gMatStackIndex] = initialMatrix;
   1074         gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewport));
   1075         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]),
   1076                   G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
   1077         gCurGraphNodeRoot = node;
   1078         if (node->node.children != NULL) {
   1079             geo_process_node_and_siblings(node->node.children);
   1080         }
   1081         gCurGraphNodeRoot = NULL;
   1082         if (gShowDebugText) {
   1083             print_text_fmt_int(180, 36, "MEM %d",
   1084                                gDisplayListHeap->totalSpace - gDisplayListHeap->usedSpace);
   1085         }
   1086         main_pool_free(gDisplayListHeap);
   1087     }
   1088 }