sm64

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

geo_layout.c (24068B)


      1 #include <ultra64.h>
      2 #include "sm64.h"
      3 
      4 #include "geo_layout.h"
      5 #include "math_util.h"
      6 #include "game/memory.h"
      7 #include "graph_node.h"
      8 
      9 typedef void (*GeoLayoutCommandProc)(void);
     10 
     11 GeoLayoutCommandProc GeoLayoutJumpTable[] = {
     12     geo_layout_cmd_branch_and_link,
     13     geo_layout_cmd_end,
     14     geo_layout_cmd_branch,
     15     geo_layout_cmd_return,
     16     geo_layout_cmd_open_node,
     17     geo_layout_cmd_close_node,
     18     geo_layout_cmd_assign_as_view,
     19     geo_layout_cmd_update_node_flags,
     20     geo_layout_cmd_node_root,
     21     geo_layout_cmd_node_ortho_projection,
     22     geo_layout_cmd_node_perspective,
     23     geo_layout_cmd_node_start,
     24     geo_layout_cmd_node_master_list,
     25     geo_layout_cmd_node_level_of_detail,
     26     geo_layout_cmd_node_switch_case,
     27     geo_layout_cmd_node_camera,
     28     geo_layout_cmd_node_translation_rotation,
     29     geo_layout_cmd_node_translation,
     30     geo_layout_cmd_node_rotation,
     31     geo_layout_cmd_node_animated_part,
     32     geo_layout_cmd_node_billboard,
     33     geo_layout_cmd_node_display_list,
     34     geo_layout_cmd_node_shadow,
     35     geo_layout_cmd_node_object_parent,
     36     geo_layout_cmd_node_generated,
     37     geo_layout_cmd_node_background,
     38     geo_layout_cmd_nop,
     39     geo_layout_cmd_copy_view,
     40     geo_layout_cmd_node_held_obj,
     41     geo_layout_cmd_node_scale,
     42     geo_layout_cmd_nop2,
     43     geo_layout_cmd_nop3,
     44     geo_layout_cmd_node_culling_radius,
     45 };
     46 
     47 struct GraphNode gObjParentGraphNode;
     48 struct AllocOnlyPool *gGraphNodePool;
     49 struct GraphNode *gCurRootGraphNode;
     50 
     51 UNUSED s32 D_8038BCA8;
     52 
     53 /* The gGeoViews array is a mysterious one. Some background:
     54  *
     55  * If there are e.g. multiple Goombas, the multiple Goomba objects share one
     56  * Geo node tree describing the goomba 3D model. Since every node has a single
     57  * parent field and not a parent array, the parent is dynamically rebinded to
     58  * each goomba instance just before rendering and set to null afterwards.
     59  * The same happens for ObjectParentNode, which has as his sharedChild a group
     60  * of all 240 object nodes. Why does the ObjectParentNode exist at all, if its
     61  * only purpose is to temporarily bind the actual group with objects? This might
     62  * be another remnant to Luigi.
     63  *
     64  * When creating a root node, room for (2 + cmd+0x02) pointers is allocated in
     65  * gGeoViews. Except for the title screen, cmd+0x02 is 10. The 2 default ones
     66  * might be for Mario and Luigi, and the other 10 could be different cameras for
     67  * different rooms / boss fights. An area might be structured like this:
     68  *
     69  * geo_camera mode_player //Mario cam
     70  * geo_open_node
     71  *   geo_render_obj
     72  *   geo_assign_as_view 1   // currently unused geo command
     73  * geo_close_node
     74  *
     75  * geo_camera mode_player //Luigi cam
     76  * geo_open_node
     77  *   geo_render_obj
     78  *   geo_copy_view 1        // currently unused geo command
     79  *   geo_assign_as_view 2
     80  * geo_close_node
     81  *
     82  * geo_camera mode_boss //boss fight cam
     83  * geo_assign_as_view 3
     84  * ...
     85  *
     86  * There might also be specific geo nodes for Mario or Luigi only. Or a fixed camera
     87  * might not have display list nodes of parts of the level that are out of view.
     88  * In the end Luigi got scrapped and the multiple-camera design did not pan out,
     89  * so everything was reduced to a single ObjectParent with a single group, and
     90  * camera switching was all done in one node. End of speculation.
     91  */
     92 struct GraphNode **gGeoViews;
     93 u16 gGeoNumViews; // length of gGeoViews array
     94 
     95 uintptr_t gGeoLayoutStack[16];
     96 struct GraphNode *gCurGraphNodeList[32];
     97 s16 gCurGraphNodeIndex;
     98 s16 gGeoLayoutStackIndex; // similar to SP register in MIPS
     99 UNUSED s16 D_8038BD7C;
    100 s16 gGeoLayoutReturnIndex; // similar to RA register in MIPS
    101 u8 *gGeoLayoutCommand;
    102 
    103 u32 unused_8038B894[3] = { 0 };
    104 
    105 /*
    106   0x00: Branch and store return address
    107    cmd+0x04: void *branchTarget
    108 */
    109 void geo_layout_cmd_branch_and_link(void) {
    110     gGeoLayoutStack[gGeoLayoutStackIndex++] = (uintptr_t) (gGeoLayoutCommand + CMD_PROCESS_OFFSET(8));
    111     gGeoLayoutStack[gGeoLayoutStackIndex++] = (gCurGraphNodeIndex << 16) + gGeoLayoutReturnIndex;
    112     gGeoLayoutReturnIndex = gGeoLayoutStackIndex;
    113     gGeoLayoutCommand = segmented_to_virtual(cur_geo_cmd_ptr(0x04));
    114 }
    115 
    116 // 0x01: Terminate geo layout
    117 void geo_layout_cmd_end(void) {
    118     gGeoLayoutStackIndex = gGeoLayoutReturnIndex;
    119     gGeoLayoutReturnIndex = gGeoLayoutStack[--gGeoLayoutStackIndex] & 0xFFFF;
    120     gCurGraphNodeIndex = gGeoLayoutStack[gGeoLayoutStackIndex] >> 16;
    121     gGeoLayoutCommand = (u8 *) gGeoLayoutStack[--gGeoLayoutStackIndex];
    122 }
    123 
    124 /*
    125   0x02: Branch
    126    cmd+0x04: void *branchTarget
    127 */
    128 void geo_layout_cmd_branch(void) {
    129     if (cur_geo_cmd_u8(0x01) == 1) {
    130         gGeoLayoutStack[gGeoLayoutStackIndex++] = (uintptr_t) (gGeoLayoutCommand + CMD_PROCESS_OFFSET(8));
    131     }
    132 
    133     gGeoLayoutCommand = segmented_to_virtual(cur_geo_cmd_ptr(0x04));
    134 }
    135 
    136 // 0x03: Return from branch
    137 void geo_layout_cmd_return(void) {
    138     gGeoLayoutCommand = (u8 *) gGeoLayoutStack[--gGeoLayoutStackIndex];
    139 }
    140 
    141 // 0x04: Open node
    142 void geo_layout_cmd_open_node(void) {
    143     gCurGraphNodeList[gCurGraphNodeIndex + 1] = gCurGraphNodeList[gCurGraphNodeIndex];
    144     gCurGraphNodeIndex++;
    145     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    146 }
    147 
    148 // 0x05: Close node
    149 void geo_layout_cmd_close_node(void) {
    150     gCurGraphNodeIndex--;
    151     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    152 }
    153 
    154 /*
    155   0x06: Register the current node as a view
    156    cmd+0x02: index
    157 
    158   Register the current node in the gGeoViews array at the given index
    159 */
    160 void geo_layout_cmd_assign_as_view(void) {
    161     u16 index = cur_geo_cmd_s16(0x02);
    162 
    163     if (index < gGeoNumViews) {
    164         gGeoViews[index] = gCurGraphNodeList[gCurGraphNodeIndex];
    165     }
    166 
    167     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    168 }
    169 
    170 /*
    171   0x07: Update current scene graph node flags
    172    cmd+0x01: u8 operation (0 = reset, 1 = set, 2 = clear)
    173    cmd+0x02: s16 bits
    174 */
    175 void geo_layout_cmd_update_node_flags(void) {
    176     u16 operation = cur_geo_cmd_u8(0x01);
    177     u16 flagBits = cur_geo_cmd_s16(0x02);
    178 
    179     switch (operation) {
    180         case GEO_CMD_FLAGS_RESET:
    181             gCurGraphNodeList[gCurGraphNodeIndex]->flags = flagBits;
    182             break;
    183         case GEO_CMD_FLAGS_SET:
    184             gCurGraphNodeList[gCurGraphNodeIndex]->flags |= flagBits;
    185             break;
    186         case GEO_CMD_FLAGS_CLEAR:
    187             gCurGraphNodeList[gCurGraphNodeIndex]->flags &= ~flagBits;
    188             break;
    189     }
    190 
    191     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    192 }
    193 
    194 /*
    195   0x08: Create a scene graph root node that specifies the viewport
    196    cmd+0x02: s16 num entries (+2) to allocate for gGeoViews
    197    cmd+0x04: s16 x
    198    cmd+0x06: s16 y
    199    cmd+0x08: s16 width
    200    cmd+0x0A: s16 height
    201 */
    202 void geo_layout_cmd_node_root(void) {
    203     s32 i;
    204     struct GraphNodeRoot *graphNode;
    205 
    206     s16 x = cur_geo_cmd_s16(0x04);
    207     s16 y = cur_geo_cmd_s16(0x06);
    208     s16 width = cur_geo_cmd_s16(0x08);
    209     s16 height = cur_geo_cmd_s16(0x0A);
    210 
    211     // number of entries to allocate for gGeoViews array
    212     // at least 2 are allocated by default
    213     // cmd+0x02 = 0x00: Mario face, 0x0A: all other levels
    214     gGeoNumViews = cur_geo_cmd_s16(0x02) + 2;
    215 
    216     graphNode = init_graph_node_root(gGraphNodePool, NULL, 0, x, y, width, height);
    217 
    218     // TODO: check type
    219     gGeoViews = alloc_only_pool_alloc(gGraphNodePool, gGeoNumViews * sizeof(struct GraphNode *));
    220 
    221     graphNode->views = gGeoViews;
    222     graphNode->numViews = gGeoNumViews;
    223 
    224     for (i = 0; i < gGeoNumViews; i++) {
    225         gGeoViews[i] = NULL;
    226     }
    227 
    228     register_scene_graph_node(&graphNode->node);
    229 
    230     gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT;
    231 }
    232 
    233 /*
    234   0x09: Create orthographic projection scene graph node
    235    cmd+0x02: s16 scale as a percentage (usually it's 100)
    236 */
    237 void geo_layout_cmd_node_ortho_projection(void) {
    238     struct GraphNodeOrthoProjection *graphNode;
    239     f32 scale = (f32) cur_geo_cmd_s16(0x02) / 100.0f;
    240 
    241     graphNode = init_graph_node_ortho_projection(gGraphNodePool, NULL, scale);
    242 
    243     register_scene_graph_node(&graphNode->node);
    244 
    245     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    246 }
    247 
    248 /*
    249   0x0A: Create camera frustum scene graph node
    250    cmd+0x01: u8  if nonzero, enable frustumFunc field
    251    cmd+0x02: s16 field of view
    252    cmd+0x04: s16 near
    253    cmd+0x06: s16 far
    254    [cmd+0x08: GraphNodeFunc frustumFunc]
    255 */
    256 void geo_layout_cmd_node_perspective(void) {
    257     struct GraphNodePerspective *graphNode;
    258     GraphNodeFunc frustumFunc = NULL;
    259     s16 fov = cur_geo_cmd_s16(0x02);
    260     s16 near = cur_geo_cmd_s16(0x04);
    261     s16 far = cur_geo_cmd_s16(0x06);
    262 
    263     if (cur_geo_cmd_u8(0x01) != 0) {
    264         // optional asm function
    265         frustumFunc = (GraphNodeFunc) cur_geo_cmd_ptr(0x08);
    266         gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT;
    267     }
    268 
    269     graphNode = init_graph_node_perspective(gGraphNodePool, NULL, (f32) fov, near, far, frustumFunc, 0);
    270 
    271     register_scene_graph_node(&graphNode->fnNode.node);
    272 
    273     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    274 }
    275 
    276 /*
    277   0x0B: Create a scene graph node that groups other nodes without any
    278   additional functionality
    279 */
    280 void geo_layout_cmd_node_start(void) {
    281     struct GraphNodeStart *graphNode;
    282 
    283     graphNode = init_graph_node_start(gGraphNodePool, NULL);
    284 
    285     register_scene_graph_node(&graphNode->node);
    286 
    287     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    288 }
    289 
    290 // 0x1F: No operation
    291 void geo_layout_cmd_nop3(void) {
    292     gGeoLayoutCommand += 0x10 << CMD_SIZE_SHIFT;
    293 }
    294 
    295 /*
    296   0x0C: Create z-buffer-toggling scene graph node
    297    cmd+0x01: u8 enableZBuffer (1 = on, 0 = off)
    298 */
    299 void geo_layout_cmd_node_master_list(void) {
    300     struct GraphNodeMasterList *graphNode;
    301 
    302     graphNode = init_graph_node_master_list(gGraphNodePool, NULL, cur_geo_cmd_u8(0x01));
    303 
    304     register_scene_graph_node(&graphNode->node);
    305 
    306     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    307 }
    308 
    309 /*
    310   0x0D: Create a level of detail graph node, which only renders at a certain
    311   distance interval from the camera.
    312    cmd+0x04: s16 minDistance
    313    cmd+0x06: s16 maxDistance
    314 */
    315 void geo_layout_cmd_node_level_of_detail(void) {
    316     struct GraphNodeLevelOfDetail *graphNode;
    317     s16 minDistance = cur_geo_cmd_s16(0x04);
    318     s16 maxDistance = cur_geo_cmd_s16(0x06);
    319 
    320     graphNode = init_graph_node_render_range(gGraphNodePool, NULL, minDistance, maxDistance);
    321 
    322     register_scene_graph_node(&graphNode->node);
    323 
    324     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    325 }
    326 
    327 /*
    328   0x0E: Create switch-case scene graph node
    329    cmd+0x02: s16 initialSelectedCase
    330    cmd+0x04: GraphNodeFunc caseSelectorFunc
    331 
    332   caseSelectorFunc returns an index which is used to select the child node to render.
    333   Used for animating coins, blinking, color selection, etc.
    334 */
    335 void geo_layout_cmd_node_switch_case(void) {
    336     struct GraphNodeSwitchCase *graphNode;
    337 
    338     graphNode =
    339         init_graph_node_switch_case(gGraphNodePool, NULL,
    340                                     cur_geo_cmd_s16(0x02), // case which is initially selected
    341                                     0,
    342                                     (GraphNodeFunc) cur_geo_cmd_ptr(0x04), // case update function
    343                                     0);
    344 
    345     register_scene_graph_node(&graphNode->fnNode.node);
    346 
    347     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    348 }
    349 
    350 /*
    351  0x0F: Create a camera scene graph node (GraphNodeCamera). The focus sets the Camera's areaCen position.
    352   cmd+0x02: s16 camera type (changes from course to course)
    353   cmd+0x04: s16 posX
    354   cmd+0x06: s16 posY
    355   cmd+0x08: s16 posZ
    356   cmd+0x0A: s16 focusX
    357   cmd+0x0C: s16 focusY
    358   cmd+0x0E: s16 focusZ
    359   cmd+0x10: GraphNodeFunc func
    360 */
    361 void geo_layout_cmd_node_camera(void) {
    362     struct GraphNodeCamera *graphNode;
    363     s16 *cmdPos = (s16 *) &gGeoLayoutCommand[4];
    364 
    365     Vec3f pos, focus;
    366 
    367     cmdPos = read_vec3s_to_vec3f(pos, cmdPos);
    368     cmdPos = read_vec3s_to_vec3f(focus, cmdPos);
    369 
    370     graphNode = init_graph_node_camera(gGraphNodePool, NULL, pos, focus,
    371                                        (GraphNodeFunc) cur_geo_cmd_ptr(0x10), cur_geo_cmd_s16(0x02));
    372 
    373     register_scene_graph_node(&graphNode->fnNode.node);
    374 
    375     gGeoViews[0] = &graphNode->fnNode.node;
    376 
    377     gGeoLayoutCommand += 0x14 << CMD_SIZE_SHIFT;
    378 }
    379 
    380 /*
    381   0x10: Create translation & rotation scene graph node with optional display list
    382    cmd+0x01: u8 params
    383      (params & 0x80): if set, enable displayList field and drawingLayer
    384      ((params & 0x70)>>4): fieldLayout
    385      (params & 0x0F): drawingLayer
    386 
    387    fieldLayout == 0:
    388     cmd+0x04: s16 xTranslation
    389     cmd+0x06: s16 yTranslation
    390     cmd+0x08: s16 zTranslation
    391     cmd+0x0A: s16 xRotation
    392     cmd+0x0C: s16 yRotation
    393     cmd+0x0E: s16 zRotation
    394 
    395    fieldLayout == 1:
    396     cmd+0x02: s16 xTranslation
    397     cmd+0x04: s16 yTranslation
    398     cmd+0x06: s16 zTranslation
    399     (rotation gets copied from gVec3sZero)
    400 
    401    fieldLayout == 2:
    402     cmd+0x02: s16 xRotation
    403     cmd+0x04: s16 yRotation
    404     cmd+0x06: s16 zRotation
    405     (translation gets copied from gVec3sZero)
    406 
    407    fieldLayout == 3:
    408     cmd+0x02: s16 yRotation
    409     (translation gets copied from gVec3sZero)
    410     (x and z translation are set to 0)
    411 
    412    [cmd+var: void *displayList]
    413 */
    414 void geo_layout_cmd_node_translation_rotation(void) {
    415     struct GraphNodeTranslationRotation *graphNode;
    416 
    417     Vec3s translation, rotation;
    418 
    419     void *displayList = NULL;
    420     s16 drawingLayer = 0;
    421 
    422     s16 params = cur_geo_cmd_u8(0x01);
    423     s16 *cmdPos = (s16 *) gGeoLayoutCommand;
    424 
    425     switch ((params & 0x70) >> 4) {
    426         case 0:
    427             cmdPos = read_vec3s(translation, &cmdPos[2]);
    428             cmdPos = read_vec3s_angle(rotation, cmdPos);
    429             break;
    430         case 1:
    431             cmdPos = read_vec3s(translation, &cmdPos[1]);
    432             vec3s_copy(rotation, gVec3sZero);
    433             break;
    434         case 2:
    435             cmdPos = read_vec3s_angle(rotation, &cmdPos[1]);
    436             vec3s_copy(translation, gVec3sZero);
    437             break;
    438         case 3:
    439             vec3s_copy(translation, gVec3sZero);
    440             vec3s_set(rotation, 0, (cmdPos[1] << 15) / 180, 0);
    441             cmdPos += 2 << CMD_SIZE_SHIFT;
    442             break;
    443     }
    444 
    445     if (params & 0x80) {
    446         displayList = *(void **) &cmdPos[0];
    447         drawingLayer = params & 0x0F;
    448         cmdPos += 2 << CMD_SIZE_SHIFT;
    449     }
    450 
    451     graphNode = init_graph_node_translation_rotation(gGraphNodePool, NULL, drawingLayer, displayList,
    452                                                      translation, rotation);
    453     register_scene_graph_node(&graphNode->node);
    454 
    455     gGeoLayoutCommand = (u8 *) cmdPos;
    456 }
    457 
    458 /*
    459   0x11: Create translation scene graph node with optional display list
    460    cmd+0x01: u8 params
    461      (params & 0x80): if set, enable displayList field and drawingLayer
    462      (params & 0x0F): drawingLayer
    463    cmd+0x02: s16 xTranslation
    464    cmd+0x04: s16 yTranslation
    465    cmd+0x06: s16 zTranslation
    466   [cmd+0x08: void *displayList]
    467 */
    468 void geo_layout_cmd_node_translation(void) {
    469     struct GraphNodeTranslation *graphNode;
    470 
    471     Vec3s translation;
    472 
    473     s16 drawingLayer = 0;
    474     s16 params = cur_geo_cmd_u8(0x01);
    475     s16 *cmdPos = (s16 *) gGeoLayoutCommand;
    476     void *displayList = NULL;
    477 
    478     cmdPos = read_vec3s(translation, &cmdPos[1]);
    479 
    480     if (params & 0x80) {
    481         displayList = *(void **) &cmdPos[0];
    482         drawingLayer = params & 0x0F;
    483         cmdPos += 2 << CMD_SIZE_SHIFT;
    484     }
    485 
    486     graphNode =
    487         init_graph_node_translation(gGraphNodePool, NULL, drawingLayer, displayList, translation);
    488 
    489     register_scene_graph_node(&graphNode->node);
    490 
    491     gGeoLayoutCommand = (u8 *) cmdPos;
    492 }
    493 
    494 /*
    495   0x12: Create ? scene graph node
    496    cmd+0x01: u8 params
    497      (params & 0x80): if set, enable displayList field and drawingLayer
    498      (params & 0x0F): drawingLayer
    499    cmd+0x02: s16 unkX
    500    cmd+0x04: s16 unkY
    501    cmd+0x06: s16 unkZ
    502   [cmd+0x08: void *displayList]
    503 */
    504 void geo_layout_cmd_node_rotation(void) {
    505     struct GraphNodeRotation *graphNode;
    506 
    507     Vec3s sp2c;
    508 
    509     s16 drawingLayer = 0;
    510     s16 params = cur_geo_cmd_u8(0x01);
    511     s16 *cmdPos = (s16 *) gGeoLayoutCommand;
    512     void *displayList = NULL;
    513 
    514     cmdPos = read_vec3s_angle(sp2c, &cmdPos[1]);
    515 
    516     if (params & 0x80) {
    517         displayList = *(void **) &cmdPos[0];
    518         drawingLayer = params & 0x0F;
    519         cmdPos += 2 << CMD_SIZE_SHIFT;
    520     }
    521 
    522     graphNode = init_graph_node_rotation(gGraphNodePool, NULL, drawingLayer, displayList, sp2c);
    523 
    524     register_scene_graph_node(&graphNode->node);
    525 
    526     gGeoLayoutCommand = (u8 *) cmdPos;
    527 }
    528 
    529 /*
    530   0x1D: Create scale scene graph node with optional display list
    531    cmd+0x01: u8 params
    532      (params & 0x80): if set, enable displayList field and drawingLayer
    533      (params & 0x0F): drawingLayer
    534    cmd+0x04: u32 scale (0x10000 = 1.0)
    535   [cmd+0x08: void *displayList]
    536 */
    537 void geo_layout_cmd_node_scale(void) {
    538     struct GraphNodeScale *graphNode;
    539 
    540     s16 drawingLayer = 0;
    541     s16 params = cur_geo_cmd_u8(0x01);
    542     f32 scale = cur_geo_cmd_u32(0x04) / 65536.0f;
    543     void *displayList = NULL;
    544 
    545     if (params & 0x80) {
    546         displayList = cur_geo_cmd_ptr(0x08);
    547         drawingLayer = params & 0x0F;
    548         gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT;
    549     }
    550 
    551     graphNode = init_graph_node_scale(gGraphNodePool, NULL, drawingLayer, displayList, scale);
    552 
    553     register_scene_graph_node(&graphNode->node);
    554 
    555     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    556 }
    557 
    558 // 0x1E: No operation
    559 void geo_layout_cmd_nop2(void) {
    560     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    561 }
    562 
    563 /*
    564   0x13: Create a scene graph node that is rotated by the object's animation.
    565    cmd+0x01: u8 drawingLayer
    566    cmd+0x02: s16 xTranslation
    567    cmd+0x04: s16 yTranslation
    568    cmd+0x06: s16 zTranslation
    569    cmd+0x08: void *displayList
    570 */
    571 void geo_layout_cmd_node_animated_part(void) {
    572     struct GraphNodeAnimatedPart *graphNode;
    573     Vec3s translation;
    574     s32 drawingLayer = cur_geo_cmd_u8(0x01);
    575     void *displayList = cur_geo_cmd_ptr(0x08);
    576     s16 *cmdPos = (s16 *) gGeoLayoutCommand;
    577 
    578     read_vec3s(translation, &cmdPos[1]);
    579 
    580     graphNode =
    581         init_graph_node_animated_part(gGraphNodePool, NULL, drawingLayer, displayList, translation);
    582 
    583     register_scene_graph_node(&graphNode->node);
    584 
    585     gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT;
    586 }
    587 
    588 /*
    589   0x14: Create billboarding node with optional display list
    590    cmd+0x01: u8 params
    591      (params & 0x80): if set, enable displayList field and drawingLayer
    592      (params & 0x0F): drawingLayer
    593    cmd+0x02: s16 xTranslation
    594    cmd+0x04: s16 yTranslation
    595    cmd+0x06: s16 zTranslation
    596   [cmd+0x08: void *displayList]
    597 */
    598 void geo_layout_cmd_node_billboard(void) {
    599     struct GraphNodeBillboard *graphNode;
    600     Vec3s translation;
    601     s16 drawingLayer = 0;
    602     s16 params = cur_geo_cmd_u8(0x01);
    603     s16 *cmdPos = (s16 *) gGeoLayoutCommand;
    604     void *displayList = NULL;
    605 
    606     cmdPos = read_vec3s(translation, &cmdPos[1]);
    607 
    608     if (params & 0x80) {
    609         displayList = *(void **) &cmdPos[0];
    610         drawingLayer = params & 0x0F;
    611         cmdPos += 2 << CMD_SIZE_SHIFT;
    612     }
    613 
    614     graphNode = init_graph_node_billboard(gGraphNodePool, NULL, drawingLayer, displayList, translation);
    615 
    616     register_scene_graph_node(&graphNode->node);
    617 
    618     gGeoLayoutCommand = (u8 *) cmdPos;
    619 }
    620 
    621 /*
    622   0x15: Create plain display list scene graph node
    623    cmd+0x01: u8 drawingLayer
    624    cmd+0x04: void *displayList
    625 */
    626 void geo_layout_cmd_node_display_list(void) {
    627     struct GraphNodeDisplayList *graphNode;
    628     s32 drawingLayer = cur_geo_cmd_u8(0x01);
    629     void *displayList = cur_geo_cmd_ptr(0x04);
    630 
    631     graphNode = init_graph_node_display_list(gGraphNodePool, NULL, drawingLayer, displayList);
    632 
    633     register_scene_graph_node(&graphNode->node);
    634 
    635     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    636 }
    637 
    638 /*
    639   0x16: Create shadow scene graph node
    640    cmd+0x02: s16 shadowType
    641    cmd+0x04: s16 shadowSolidity
    642    cmd+0x06: s16 shadowScale
    643 */
    644 void geo_layout_cmd_node_shadow(void) {
    645     struct GraphNodeShadow *graphNode;
    646     u8 shadowType = cur_geo_cmd_s16(0x02);
    647     u8 shadowSolidity = cur_geo_cmd_s16(0x04);
    648     s16 shadowScale = cur_geo_cmd_s16(0x06);
    649 
    650     graphNode = init_graph_node_shadow(gGraphNodePool, NULL, shadowScale, shadowSolidity, shadowType);
    651 
    652     register_scene_graph_node(&graphNode->node);
    653 
    654     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    655 }
    656 
    657 // 0x17: Create scene graph node that manages the group of all object nodes
    658 void geo_layout_cmd_node_object_parent(void) {
    659     struct GraphNodeObjectParent *graphNode;
    660 
    661     graphNode = init_graph_node_object_parent(gGraphNodePool, NULL, &gObjParentGraphNode);
    662 
    663     register_scene_graph_node(&graphNode->node);
    664 
    665     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    666 }
    667 
    668 /*
    669   0x18: Create dynamically generated displaylist scene graph node
    670    cmd+0x02: s16 parameter
    671    cmd+0x04: GraphNodeFunc func
    672 */
    673 void geo_layout_cmd_node_generated(void) {
    674     struct GraphNodeGenerated *graphNode;
    675 
    676     graphNode = init_graph_node_generated(gGraphNodePool, NULL,
    677                                           (GraphNodeFunc) cur_geo_cmd_ptr(0x04), // asm function
    678                                           cur_geo_cmd_s16(0x02));                // parameter
    679 
    680     register_scene_graph_node(&graphNode->fnNode.node);
    681 
    682     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    683 }
    684 
    685 /*
    686   0x19: Create background scene graph node
    687    cmd+0x02: s16 background // background ID, or RGBA5551 color if backgroundFunc is null
    688    cmd+0x04: GraphNodeFunc backgroundFunc
    689 */
    690 void geo_layout_cmd_node_background(void) {
    691     struct GraphNodeBackground *graphNode;
    692 
    693     graphNode = init_graph_node_background(
    694         gGraphNodePool, NULL,
    695         cur_geo_cmd_s16(0x02), // background ID, or RGBA5551 color if asm function is null
    696         (GraphNodeFunc) cur_geo_cmd_ptr(0x04), // asm function
    697         0);
    698 
    699     register_scene_graph_node(&graphNode->fnNode.node);
    700 
    701     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    702 }
    703 
    704 // 0x1A: No operation
    705 void geo_layout_cmd_nop(void) {
    706     gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
    707 }
    708 
    709 /*
    710   0x1B: Copy the shared children from the object parent from a specific view
    711   to a newly created object parent node.
    712    cmd+0x02: s16 index (of gGeoViews)
    713 */
    714 void geo_layout_cmd_copy_view(void) {
    715     struct GraphNodeObjectParent *graphNode;
    716     struct GraphNode *node = NULL;
    717     s16 index = cur_geo_cmd_s16(0x02);
    718 
    719     if (index >= 0) {
    720         node = gGeoViews[index];
    721 
    722         if (node->type == GRAPH_NODE_TYPE_OBJECT_PARENT) {
    723             node = ((struct GraphNodeObjectParent *) node)->sharedChild;
    724         } else {
    725             node = NULL;
    726         }
    727     }
    728 
    729     graphNode = init_graph_node_object_parent(gGraphNodePool, NULL, node);
    730 
    731     register_scene_graph_node(&graphNode->node);
    732 
    733     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    734 }
    735 
    736 /*
    737   0x1C: Create a held object scene graph node
    738    cmd+0x01: u8 unused
    739    cmd+0x02: s16 offsetX
    740    cmd+0x04: s16 offsetY
    741    cmd+0x06: s16 offsetZ
    742    cmd+0x08: GraphNodeFunc nodeFunc
    743 */
    744 void geo_layout_cmd_node_held_obj(void) {
    745     struct GraphNodeHeldObject *graphNode;
    746     Vec3s offset;
    747 
    748     read_vec3s(offset, (s16 *) &gGeoLayoutCommand[0x02]);
    749 
    750     graphNode = init_graph_node_held_object(
    751         gGraphNodePool, NULL, NULL, offset, (GraphNodeFunc) cur_geo_cmd_ptr(0x08), cur_geo_cmd_u8(0x01));
    752 
    753     register_scene_graph_node(&graphNode->fnNode.node);
    754 
    755     gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT;
    756 }
    757 
    758 /*
    759   0x20: Create a scene graph node that specifies for an object the radius that
    760    is used for frustum culling.
    761    cmd+0x02: s16 cullingRadius
    762 */
    763 void geo_layout_cmd_node_culling_radius(void) {
    764     struct GraphNodeCullingRadius *graphNode;
    765     graphNode = init_graph_node_culling_radius(gGraphNodePool, NULL, cur_geo_cmd_s16(0x02));
    766     register_scene_graph_node(&graphNode->node);
    767     gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
    768 }
    769 
    770 struct GraphNode *process_geo_layout(struct AllocOnlyPool *pool, void *segptr) {
    771     // set by register_scene_graph_node when gCurGraphNodeIndex is 0
    772     // and gCurRootGraphNode is NULL
    773     gCurRootGraphNode = NULL;
    774 
    775     gGeoNumViews = 0; // number of entries in gGeoViews
    776 
    777     gCurGraphNodeList[0] = 0;
    778     gCurGraphNodeIndex = 0; // incremented by cmd_open_node, decremented by cmd_close_node
    779 
    780     gGeoLayoutStackIndex = 2;
    781     gGeoLayoutReturnIndex = 2; // stack index is often copied here?
    782 
    783     gGeoLayoutCommand = segmented_to_virtual(segptr);
    784 
    785     gGraphNodePool = pool;
    786 
    787     gGeoLayoutStack[0] = 0;
    788     gGeoLayoutStack[1] = 0;
    789 
    790     while (gGeoLayoutCommand != NULL) {
    791         GeoLayoutJumpTable[gGeoLayoutCommand[0x00]]();
    792     }
    793 
    794     return gCurRootGraphNode;
    795 }