sm64

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

graph_node.c (31305B)


      1 #include <ultra64.h>
      2 #include "sm64.h"
      3 
      4 #include "game/level_update.h"
      5 #include "math_util.h"
      6 #include "game/memory.h"
      7 #include "graph_node.h"
      8 #include "game/rendering_graph_node.h"
      9 #include "game/area.h"
     10 #include "geo_layout.h"
     11 
     12 // unused Mtx(s)
     13 s16 identityMtx[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
     14 s16 zeroMtx[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
     15 
     16 Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f };
     17 Vec3s gVec3sZero = { 0, 0, 0 };
     18 Vec3f gVec3fOne = { 1.0f, 1.0f, 1.0f };
     19 UNUSED Vec3s gVec3sOne = { 1, 1, 1 };
     20 
     21 /**
     22  * Initialize a geo node with a given type. Sets all links such that there
     23  * are no siblings, parent or children for this node.
     24  */
     25 void init_scene_graph_node_links(struct GraphNode *graphNode, s32 type) {
     26     graphNode->type = type;
     27     graphNode->flags = GRAPH_RENDER_ACTIVE;
     28     graphNode->prev = graphNode;
     29     graphNode->next = graphNode;
     30     graphNode->parent = NULL;
     31     graphNode->children = NULL;
     32 }
     33 
     34 /**
     35  * Allocated and returns a newly created root node
     36  */
     37 struct GraphNodeRoot *init_graph_node_root(struct AllocOnlyPool *pool, struct GraphNodeRoot *graphNode,
     38                                            s16 areaIndex, s16 x, s16 y, s16 width, s16 height) {
     39     if (pool != NULL) {
     40         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRoot));
     41     }
     42 
     43     if (graphNode != NULL) {
     44         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROOT);
     45 
     46         graphNode->areaIndex = areaIndex;
     47         graphNode->unk15 = 0;
     48         graphNode->x = x;
     49         graphNode->y = y;
     50         graphNode->width = width;
     51         graphNode->height = height;
     52         graphNode->views = NULL;
     53         graphNode->numViews = 0;
     54     }
     55 
     56     return graphNode;
     57 }
     58 
     59 /**
     60  * Allocates and returns a newly created otrhographic projection node
     61  */
     62 struct GraphNodeOrthoProjection *
     63 init_graph_node_ortho_projection(struct AllocOnlyPool *pool, struct GraphNodeOrthoProjection *graphNode,
     64                                  f32 scale) {
     65     if (pool != NULL) {
     66         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeOrthoProjection));
     67     }
     68 
     69     if (graphNode != NULL) {
     70         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ORTHO_PROJECTION);
     71         graphNode->scale = scale;
     72     }
     73 
     74     return graphNode;
     75 }
     76 
     77 /**
     78  * Allocates and returns a newly created perspective node
     79  */
     80 struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *pool,
     81                                                          struct GraphNodePerspective *graphNode,
     82                                                          f32 fov, s16 near, s16 far,
     83                                                          GraphNodeFunc nodeFunc, s32 unused) {
     84     if (pool != NULL) {
     85         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodePerspective));
     86     }
     87 
     88     if (graphNode != NULL) {
     89         init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_PERSPECTIVE);
     90 
     91         graphNode->fov = fov;
     92         graphNode->near = near;
     93         graphNode->far = far;
     94         graphNode->fnNode.func = nodeFunc;
     95         graphNode->unused = unused;
     96 
     97         if (nodeFunc != NULL) {
     98             nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
     99         }
    100     }
    101 
    102     return graphNode;
    103 }
    104 
    105 /**
    106  * Allocates and returns a newly created start node
    107  */
    108 struct GraphNodeStart *init_graph_node_start(struct AllocOnlyPool *pool,
    109                                              struct GraphNodeStart *graphNode) {
    110     if (pool != NULL) {
    111         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeStart));
    112     }
    113 
    114     if (graphNode != NULL) {
    115         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_START);
    116     }
    117 
    118     return graphNode;
    119 }
    120 
    121 /**
    122  * Allocates and returns a newly created master list node
    123  */
    124 struct GraphNodeMasterList *init_graph_node_master_list(struct AllocOnlyPool *pool,
    125                                                         struct GraphNodeMasterList *graphNode, s16 on) {
    126     if (pool != NULL) {
    127         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeMasterList));
    128     }
    129 
    130     if (graphNode != NULL) {
    131         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_MASTER_LIST);
    132 
    133         if (on) {
    134             graphNode->node.flags |= GRAPH_RENDER_Z_BUFFER;
    135         }
    136     }
    137 
    138     return graphNode;
    139 }
    140 
    141 /**
    142  * Allocates and returns a newly created render range node
    143  */
    144 struct GraphNodeLevelOfDetail *init_graph_node_render_range(struct AllocOnlyPool *pool,
    145                                                             struct GraphNodeLevelOfDetail *graphNode,
    146                                                             s16 minDistance, s16 maxDistance) {
    147     if (pool != NULL) {
    148         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeLevelOfDetail));
    149     }
    150 
    151     if (graphNode != NULL) {
    152         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_LEVEL_OF_DETAIL);
    153         graphNode->minDistance = minDistance;
    154         graphNode->maxDistance = maxDistance;
    155     }
    156 
    157     return graphNode;
    158 }
    159 
    160 /**
    161  * Allocates and returns a newly created switch case node
    162  */
    163 struct GraphNodeSwitchCase *init_graph_node_switch_case(struct AllocOnlyPool *pool,
    164                                                         struct GraphNodeSwitchCase *graphNode,
    165                                                         s16 numCases, s16 selectedCase,
    166                                                         GraphNodeFunc nodeFunc, s32 unused) {
    167     if (pool != NULL) {
    168         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeSwitchCase));
    169     }
    170 
    171     if (graphNode != NULL) {
    172         init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_SWITCH_CASE);
    173         graphNode->numCases = numCases;
    174         graphNode->selectedCase = selectedCase;
    175         graphNode->fnNode.func = nodeFunc;
    176         graphNode->unused = unused;
    177 
    178         if (nodeFunc != NULL) {
    179             nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
    180         }
    181     }
    182 
    183     return graphNode;
    184 }
    185 
    186 /**
    187  * Allocates and returns a newly created camera node
    188  */
    189 struct GraphNodeCamera *init_graph_node_camera(struct AllocOnlyPool *pool,
    190                                                struct GraphNodeCamera *graphNode, f32 *pos,
    191                                                f32 *focus, GraphNodeFunc func, s32 mode) {
    192     if (pool != NULL) {
    193         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCamera));
    194     }
    195 
    196     if (graphNode != NULL) {
    197         init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_CAMERA);
    198         vec3f_copy(graphNode->pos, pos);
    199         vec3f_copy(graphNode->focus, focus);
    200         graphNode->fnNode.func = func;
    201         graphNode->config.mode = mode;
    202         graphNode->roll = 0;
    203         graphNode->rollScreen = 0;
    204 
    205         if (func != NULL) {
    206             func(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
    207         }
    208     }
    209 
    210     return graphNode;
    211 }
    212 
    213 /**
    214  * Allocates and returns a newly created translation rotation node
    215  */
    216 struct GraphNodeTranslationRotation *
    217 init_graph_node_translation_rotation(struct AllocOnlyPool *pool,
    218                                      struct GraphNodeTranslationRotation *graphNode, s32 drawingLayer,
    219                                      void *displayList, Vec3s translation, Vec3s rotation) {
    220     if (pool != NULL) {
    221         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslationRotation));
    222     }
    223 
    224     if (graphNode != NULL) {
    225         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION_ROTATION);
    226 
    227         vec3s_copy(graphNode->translation, translation);
    228         vec3s_copy(graphNode->rotation, rotation);
    229         graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
    230         graphNode->displayList = displayList;
    231     }
    232 
    233     return graphNode;
    234 }
    235 
    236 /**
    237  * Allocates and returns a newly created translation node
    238  */
    239 struct GraphNodeTranslation *init_graph_node_translation(struct AllocOnlyPool *pool,
    240                                                          struct GraphNodeTranslation *graphNode,
    241                                                          s32 drawingLayer, void *displayList,
    242                                                          Vec3s translation) {
    243     if (pool != NULL) {
    244         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslation));
    245     }
    246 
    247     if (graphNode != NULL) {
    248         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION);
    249 
    250         vec3s_copy(graphNode->translation, translation);
    251         graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
    252         graphNode->displayList = displayList;
    253     }
    254 
    255     return graphNode;
    256 }
    257 
    258 /**
    259  * Allocates and returns a newly created rotation node
    260  */
    261 struct GraphNodeRotation *init_graph_node_rotation(struct AllocOnlyPool *pool,
    262                                                    struct GraphNodeRotation *graphNode,
    263                                                    s32 drawingLayer, void *displayList,
    264                                                    Vec3s rotation) {
    265     if (pool != NULL) {
    266         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRotation));
    267     }
    268 
    269     if (graphNode != NULL) {
    270         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROTATION);
    271         vec3s_copy(graphNode->rotation, rotation);
    272         graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
    273         graphNode->displayList = displayList;
    274     }
    275 
    276     return graphNode;
    277 }
    278 
    279 /**
    280  * Allocates and returns a newly created scaling node
    281  */
    282 struct GraphNodeScale *init_graph_node_scale(struct AllocOnlyPool *pool,
    283                                              struct GraphNodeScale *graphNode, s32 drawingLayer,
    284                                              void *displayList, f32 scale) {
    285     if (pool != NULL) {
    286         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeScale));
    287     }
    288 
    289     if (graphNode != NULL) {
    290         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SCALE);
    291         graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
    292         graphNode->scale = scale;
    293         graphNode->displayList = displayList;
    294     }
    295 
    296     return graphNode;
    297 }
    298 
    299 /**
    300  * Allocates and returns a newly created object node
    301  */
    302 struct GraphNodeObject *init_graph_node_object(struct AllocOnlyPool *pool,
    303                                                struct GraphNodeObject *graphNode,
    304                                                struct GraphNode *sharedChild, Vec3f pos, Vec3s angle,
    305                                                Vec3f scale) {
    306     if (pool != NULL) {
    307         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObject));
    308     }
    309 
    310     if (graphNode != NULL) {
    311         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT);
    312         vec3f_copy(graphNode->pos, pos);
    313         vec3f_copy(graphNode->scale, scale);
    314         vec3s_copy(graphNode->angle, angle);
    315         graphNode->sharedChild = sharedChild;
    316         graphNode->throwMatrix = NULL;
    317         graphNode->animInfo.animID = 0;
    318         graphNode->animInfo.curAnim = NULL;
    319         graphNode->animInfo.animFrame = 0;
    320         graphNode->animInfo.animFrameAccelAssist = 0;
    321         graphNode->animInfo.animAccel = 0x10000;
    322         graphNode->animInfo.animTimer = 0;
    323         graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
    324     }
    325 
    326     return graphNode;
    327 }
    328 
    329 /**
    330  * Allocates and returns a newly created frustum culling radius node
    331  */
    332 struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct AllocOnlyPool *pool,
    333                                                               struct GraphNodeCullingRadius *graphNode,
    334                                                               s16 radius) {
    335     if (pool != NULL) {
    336         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCullingRadius));
    337     }
    338 
    339     if (graphNode != NULL) {
    340         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_CULLING_RADIUS);
    341         graphNode->cullingRadius = radius;
    342     }
    343 
    344     return graphNode;
    345 }
    346 
    347 /**
    348  * Allocates and returns a newly created animated part node
    349  */
    350 struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool *pool,
    351                                                             struct GraphNodeAnimatedPart *graphNode,
    352                                                             s32 drawingLayer, void *displayList,
    353                                                             Vec3s translation) {
    354     if (pool != NULL) {
    355         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeAnimatedPart));
    356     }
    357 
    358     if (graphNode != NULL) {
    359         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ANIMATED_PART);
    360         vec3s_copy(graphNode->translation, translation);
    361         graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
    362         graphNode->displayList = displayList;
    363     }
    364 
    365     return graphNode;
    366 }
    367 
    368 /**
    369  * Allocates and returns a newly created billboard node
    370  */
    371 struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool,
    372                                                      struct GraphNodeBillboard *graphNode,
    373                                                      s32 drawingLayer, void *displayList,
    374                                                      Vec3s translation) {
    375     if (pool != NULL) {
    376         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBillboard));
    377     }
    378 
    379     if (graphNode != NULL) {
    380         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_BILLBOARD);
    381         vec3s_copy(graphNode->translation, translation);
    382         graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
    383         graphNode->displayList = displayList;
    384     }
    385 
    386     return graphNode;
    387 }
    388 
    389 /**
    390  * Allocates and returns a newly created displaylist node
    391  */
    392 struct GraphNodeDisplayList *init_graph_node_display_list(struct AllocOnlyPool *pool,
    393                                                           struct GraphNodeDisplayList *graphNode,
    394                                                           s32 drawingLayer, void *displayList) {
    395     if (pool != NULL) {
    396         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeDisplayList));
    397     }
    398 
    399     if (graphNode != NULL) {
    400         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_DISPLAY_LIST);
    401         graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
    402         graphNode->displayList = displayList;
    403     }
    404 
    405     return graphNode;
    406 }
    407 
    408 /**
    409  * Allocates and returns a newly created shadow node
    410  */
    411 struct GraphNodeShadow *init_graph_node_shadow(struct AllocOnlyPool *pool,
    412                                                struct GraphNodeShadow *graphNode, s16 shadowScale,
    413                                                u8 shadowSolidity, u8 shadowType) {
    414     if (pool != NULL) {
    415         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeShadow));
    416     }
    417 
    418     if (graphNode != NULL) {
    419         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SHADOW);
    420         graphNode->shadowScale = shadowScale;
    421         graphNode->shadowSolidity = shadowSolidity;
    422         graphNode->shadowType = shadowType;
    423     }
    424 
    425     return graphNode;
    426 }
    427 
    428 /**
    429  * Allocates and returns a newly created object parent node
    430  */
    431 struct GraphNodeObjectParent *init_graph_node_object_parent(struct AllocOnlyPool *pool,
    432                                                             struct GraphNodeObjectParent *graphNode,
    433                                                             struct GraphNode *sharedChild) {
    434     if (pool != NULL) {
    435         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObjectParent));
    436     }
    437 
    438     if (graphNode != NULL) {
    439         init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT_PARENT);
    440         graphNode->sharedChild = sharedChild;
    441     }
    442 
    443     return graphNode;
    444 }
    445 
    446 /**
    447  * Allocates and returns a newly created generated node
    448  */
    449 struct GraphNodeGenerated *init_graph_node_generated(struct AllocOnlyPool *pool,
    450                                                      struct GraphNodeGenerated *graphNode,
    451                                                      GraphNodeFunc gfxFunc, s32 parameter) {
    452     if (pool != NULL) {
    453         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeGenerated));
    454     }
    455 
    456     if (graphNode != NULL) {
    457         init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_GENERATED_LIST);
    458         graphNode->fnNode.func = gfxFunc;
    459         graphNode->parameter = parameter;
    460 
    461         if (gfxFunc != NULL) {
    462             gfxFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
    463         }
    464     }
    465 
    466     return graphNode;
    467 }
    468 
    469 /**
    470  * Allocates and returns a newly created background node
    471  */
    472 struct GraphNodeBackground *init_graph_node_background(struct AllocOnlyPool *pool,
    473                                                        struct GraphNodeBackground *graphNode,
    474                                                        u16 background, GraphNodeFunc backgroundFunc,
    475                                                        s32 zero) {
    476     if (pool != NULL) {
    477         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBackground));
    478     }
    479 
    480     if (graphNode != NULL) {
    481         init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_BACKGROUND);
    482 
    483         graphNode->background = (background << 16) | background;
    484         graphNode->fnNode.func = backgroundFunc;
    485         graphNode->unused = zero; // always 0, unused
    486 
    487         if (backgroundFunc != NULL) {
    488             backgroundFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
    489         }
    490     }
    491 
    492     return graphNode;
    493 }
    494 
    495 /**
    496  * Allocates and returns a newly created held object node
    497  */
    498 struct GraphNodeHeldObject *init_graph_node_held_object(struct AllocOnlyPool *pool,
    499                                                         struct GraphNodeHeldObject *graphNode,
    500                                                         struct Object *objNode,
    501                                                         Vec3s translation,
    502                                                         GraphNodeFunc nodeFunc, s32 playerIndex) {
    503     if (pool != NULL) {
    504         graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeHeldObject));
    505     }
    506 
    507     if (graphNode != NULL) {
    508         init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_HELD_OBJ);
    509         vec3s_copy(graphNode->translation, translation);
    510         graphNode->objNode = objNode;
    511         graphNode->fnNode.func = nodeFunc;
    512         graphNode->playerIndex = playerIndex;
    513 
    514         if (nodeFunc != NULL) {
    515             nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
    516         }
    517     }
    518 
    519     return graphNode;
    520 }
    521 
    522 /**
    523  * Adds 'childNode' to the end of the list children from 'parent'
    524  */
    525 struct GraphNode *geo_add_child(struct GraphNode *parent, struct GraphNode *childNode) {
    526     struct GraphNode *parentFirstChild;
    527     struct GraphNode *parentLastChild;
    528 
    529     if (childNode != NULL) {
    530         childNode->parent = parent;
    531         parentFirstChild = parent->children;
    532 
    533         if (parentFirstChild == NULL) {
    534             parent->children = childNode;
    535             childNode->prev = childNode;
    536             childNode->next = childNode;
    537         } else {
    538             parentLastChild = parentFirstChild->prev;
    539             childNode->prev = parentLastChild;
    540             childNode->next = parentFirstChild;
    541             parentFirstChild->prev = childNode;
    542             parentLastChild->next = childNode;
    543         }
    544     }
    545 
    546     return childNode;
    547 }
    548 
    549 /**
    550  * Remove a node from the scene graph. It changes the links with its
    551  * siblings and with its parent, it doesn't deallocate the memory
    552  * since geo nodes are allocated in a pointer-bumping pool that
    553  * gets thrown out when changing areas.
    554  */
    555 struct GraphNode *geo_remove_child(struct GraphNode *graphNode) {
    556     struct GraphNode *parent;
    557     struct GraphNode **firstChild;
    558 
    559     parent = graphNode->parent;
    560     firstChild = &parent->children;
    561 
    562     // Remove link with siblings
    563     graphNode->prev->next = graphNode->next;
    564     graphNode->next->prev = graphNode->prev;
    565 
    566     // If this node was the first child, a new first child must be chosen
    567     if (*firstChild == graphNode) {
    568         // The list is circular, so this checks whether it was the only child
    569         if (graphNode->next == graphNode) {
    570             *firstChild = NULL; // Parent has no children anymore
    571         } else {
    572             *firstChild = graphNode->next; // Choose a new first child
    573         }
    574     }
    575 
    576     return parent;
    577 }
    578 
    579 /**
    580  * Reorders the given node so it's the first child of its parent.
    581  * This is called on the Mario object when he is spawned. That's why Mario's
    582  * object is always drawn before any other objects. (Note that the geo order
    583  * is independent from processing group order, where Mario is not first.)
    584  */
    585 struct GraphNode *geo_make_first_child(struct GraphNode *newFirstChild) {
    586     struct GraphNode *lastSibling;
    587     struct GraphNode *parent;
    588     struct GraphNode **firstChild;
    589 
    590     parent = newFirstChild->parent;
    591     firstChild = &parent->children;
    592 
    593     if (*firstChild != newFirstChild) {
    594         if ((*firstChild)->prev != newFirstChild) {
    595             newFirstChild->prev->next = newFirstChild->next;
    596             newFirstChild->next->prev = newFirstChild->prev;
    597             lastSibling = (*firstChild)->prev;
    598             newFirstChild->prev = lastSibling;
    599             newFirstChild->next = *firstChild;
    600             (*firstChild)->prev = newFirstChild;
    601             lastSibling->next = newFirstChild;
    602         }
    603         *firstChild = newFirstChild;
    604     }
    605 
    606     return parent;
    607 }
    608 
    609 /**
    610  * Helper function for geo_call_global_function_nodes that recursively
    611  * traverses the scene graph and calls the functions of global nodes.
    612  */
    613 void geo_call_global_function_nodes_helper(struct GraphNode *graphNode, s32 callContext) {
    614     struct GraphNode **globalPtr;
    615     struct GraphNode *curNode;
    616     struct FnGraphNode *asFnNode;
    617 
    618     curNode = graphNode;
    619 
    620     do {
    621         asFnNode = (struct FnGraphNode *) curNode;
    622 
    623         if (curNode->type & GRAPH_NODE_TYPE_FUNCTIONAL) {
    624             if (asFnNode->func != NULL) {
    625                 asFnNode->func(callContext, curNode, NULL);
    626             }
    627         }
    628 
    629         if (curNode->children != NULL) {
    630             switch (curNode->type) {
    631                 case GRAPH_NODE_TYPE_MASTER_LIST:
    632                     globalPtr = (struct GraphNode **) &gCurGraphNodeMasterList;
    633                     break;
    634                 case GRAPH_NODE_TYPE_PERSPECTIVE:
    635                     globalPtr = (struct GraphNode **) &gCurGraphNodeCamFrustum;
    636                     break;
    637                 case GRAPH_NODE_TYPE_CAMERA:
    638                     globalPtr = (struct GraphNode **) &gCurGraphNodeCamera;
    639                     break;
    640                 case GRAPH_NODE_TYPE_OBJECT:
    641                     globalPtr = (struct GraphNode **) &gCurGraphNodeObject;
    642                     break;
    643                 default:
    644                     globalPtr = NULL;
    645                     break;
    646             }
    647 
    648             if (globalPtr != NULL) {
    649                 *globalPtr = curNode;
    650             }
    651 
    652             geo_call_global_function_nodes_helper(curNode->children, callContext);
    653 
    654             if (globalPtr != NULL) {
    655                 *globalPtr = NULL;
    656             }
    657         }
    658     } while ((curNode = curNode->next) != graphNode);
    659 }
    660 
    661 /**
    662  * Call the update functions of geo nodes that are stored in global variables.
    663  * These variables include gCurGraphNodeMasterList, gCurGraphNodeCamFrustum,
    664  * gCurGraphNodeCamera and gCurGraphNodeObject.
    665  * callContext is one of the GEO_CONTEXT_ defines.
    666  * The graphNode argument should be of type GraphNodeRoot.
    667  */
    668 void geo_call_global_function_nodes(struct GraphNode *graphNode, s32 callContext) {
    669     if (graphNode->flags & GRAPH_RENDER_ACTIVE) {
    670         gCurGraphNodeRoot = (struct GraphNodeRoot *) graphNode;
    671 
    672         if (graphNode->children != NULL) {
    673             geo_call_global_function_nodes_helper(graphNode->children, callContext);
    674         }
    675 
    676         gCurGraphNodeRoot = 0;
    677     }
    678 }
    679 
    680 /**
    681  * When objects are cleared, this is called on all object nodes (loaded or unloaded).
    682  */
    683 void geo_reset_object_node(struct GraphNodeObject *graphNode) {
    684     init_graph_node_object(NULL, graphNode, 0, gVec3fZero, gVec3sZero, gVec3fOne);
    685 
    686     geo_add_child(&gObjParentGraphNode, &graphNode->node);
    687     graphNode->node.flags &= ~GRAPH_RENDER_ACTIVE;
    688 }
    689 
    690 /**
    691  * Initialize an object node using the given parameters
    692  */
    693 void geo_obj_init(struct GraphNodeObject *graphNode, void *sharedChild, Vec3f pos, Vec3s angle) {
    694     vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f);
    695     vec3f_copy(graphNode->pos, pos);
    696     vec3s_copy(graphNode->angle, angle);
    697 
    698     graphNode->sharedChild = sharedChild;
    699     graphNode->unk4C = 0;
    700     graphNode->throwMatrix = NULL;
    701     graphNode->animInfo.curAnim = NULL;
    702 
    703     graphNode->node.flags |= GRAPH_RENDER_ACTIVE;
    704     graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE;
    705     graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
    706     graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD;
    707 }
    708 
    709 /**
    710  * Initialize and object node using the given SpawnInfo struct
    711  */
    712 void geo_obj_init_spawninfo(struct GraphNodeObject *graphNode, struct SpawnInfo *spawn) {
    713     vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f);
    714     vec3s_copy(graphNode->angle, spawn->startAngle);
    715 
    716     graphNode->pos[0] = (f32) spawn->startPos[0];
    717     graphNode->pos[1] = (f32) spawn->startPos[1];
    718     graphNode->pos[2] = (f32) spawn->startPos[2];
    719 
    720     graphNode->areaIndex = spawn->areaIndex;
    721     graphNode->activeAreaIndex = spawn->activeAreaIndex;
    722     graphNode->sharedChild = spawn->model;
    723     graphNode->unk4C = spawn;
    724     graphNode->throwMatrix = NULL;
    725     graphNode->animInfo.curAnim = 0;
    726 
    727     graphNode->node.flags |= GRAPH_RENDER_ACTIVE;
    728     graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE;
    729     graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
    730     graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD;
    731 }
    732 
    733 /**
    734  * Initialize the animation of an object node
    735  */
    736 void geo_obj_init_animation(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr) {
    737     struct Animation **animSegmented = segmented_to_virtual(animPtrAddr);
    738     struct Animation *anim = segmented_to_virtual(*animSegmented);
    739 
    740     if (graphNode->animInfo.curAnim != anim) {
    741         graphNode->animInfo.curAnim = anim;
    742         graphNode->animInfo.animFrame = anim->startFrame + ((anim->flags & ANIM_FLAG_BACKWARD) ? 1 : -1);
    743         graphNode->animInfo.animAccel = 0;
    744         graphNode->animInfo.animYTrans = 0;
    745     }
    746 }
    747 
    748 /**
    749  * Initialize the animation of an object node
    750  */
    751 void geo_obj_init_animation_accel(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr, u32 animAccel) {
    752     struct Animation **animSegmented = segmented_to_virtual(animPtrAddr);
    753     struct Animation *anim = segmented_to_virtual(*animSegmented);
    754 
    755     if (graphNode->animInfo.curAnim != anim) {
    756         graphNode->animInfo.curAnim = anim;
    757         graphNode->animInfo.animYTrans = 0;
    758         graphNode->animInfo.animFrameAccelAssist =
    759             (anim->startFrame << 16) + ((anim->flags & ANIM_FLAG_BACKWARD) ? animAccel : -animAccel);
    760         graphNode->animInfo.animFrame = graphNode->animInfo.animFrameAccelAssist >> 16;
    761     }
    762 
    763     graphNode->animInfo.animAccel = animAccel;
    764 }
    765 
    766 /**
    767  * Retrieves an index into animation data based on the attribute pointer
    768  * An attribute is an x-, y- or z-component of the translation / rotation for a part
    769  * Each attribute is a pair of s16's, where the first s16 represents the maximum frame
    770  * and the second s16 the actual index. This index can be used to index in the array
    771  * with actual animation values.
    772  */
    773 s32 retrieve_animation_index(s32 frame, u16 **attributes) {
    774     s32 result;
    775 
    776     if (frame < (*attributes)[0]) {
    777         result = (*attributes)[1] + frame;
    778     } else {
    779         result = (*attributes)[1] + (*attributes)[0] - 1;
    780     }
    781 
    782     *attributes += 2;
    783 
    784     return result;
    785 }
    786 
    787 /**
    788  * Update the animation frame of an object. The animation flags determine
    789  * whether it plays forwards or backwards, and whether it stops or loops at
    790  * the end etc.
    791  */
    792 s16 geo_update_animation_frame(struct AnimInfo *obj, s32 *accelAssist) {
    793     s32 result;
    794     struct Animation *anim = obj->curAnim;
    795 
    796     if (obj->animTimer == gAreaUpdateCounter || anim->flags & ANIM_FLAG_2) {
    797         if (accelAssist != NULL) {
    798             accelAssist[0] = obj->animFrameAccelAssist;
    799         }
    800 
    801         return obj->animFrame;
    802     }
    803 
    804     if (anim->flags & ANIM_FLAG_BACKWARD) {
    805         if (obj->animAccel != 0) {
    806             result = obj->animFrameAccelAssist - obj->animAccel;
    807         } else {
    808             result = (obj->animFrame - 1) << 16;
    809         }
    810 
    811         if (GET_HIGH_S16_OF_32(result) < anim->loopStart) {
    812             if (anim->flags & ANIM_FLAG_NOLOOP) {
    813                 SET_HIGH_S16_OF_32(result, anim->loopStart);
    814             } else {
    815                 SET_HIGH_S16_OF_32(result, anim->loopEnd - 1);
    816             }
    817         }
    818     } else {
    819         if (obj->animAccel != 0) {
    820             result = obj->animFrameAccelAssist + obj->animAccel;
    821         } else {
    822             result = (obj->animFrame + 1) << 16;
    823         }
    824 
    825         if (GET_HIGH_S16_OF_32(result) >= anim->loopEnd) {
    826             if (anim->flags & ANIM_FLAG_NOLOOP) {
    827                 SET_HIGH_S16_OF_32(result, anim->loopEnd - 1);
    828             } else {
    829                 SET_HIGH_S16_OF_32(result, anim->loopStart);
    830             }
    831         }
    832     }
    833 
    834     if (accelAssist != 0) {
    835         accelAssist[0] = result;
    836     }
    837 
    838     return GET_HIGH_S16_OF_32(result);
    839 }
    840 
    841 /**
    842  * Unused function to retrieve an object's current animation translation
    843  * Assumes that it has x, y and z data in animations, which isn't always the
    844  * case since some animation types only have vertical or lateral translation.
    845  * This might have been used for positioning the shadow under an object, which
    846  * currently happens in-line in geo_process_shadow where it also accounts for
    847  * animations without lateral translation.
    848  */
    849 void geo_retreive_animation_translation(struct GraphNodeObject *obj, Vec3f position) {
    850     struct Animation *animation = obj->animInfo.curAnim;
    851 
    852     if (animation != NULL) {
    853         u16 *attribute = segmented_to_virtual((void *) animation->index);
    854         s16 *values = segmented_to_virtual((void *) animation->values);
    855 
    856         s16 frame = obj->animInfo.animFrame;
    857 
    858         if (frame < 0) {
    859             frame = 0;
    860         }
    861 
    862         position[0] = (f32) values[retrieve_animation_index(frame, &attribute)];
    863         position[1] = (f32) values[retrieve_animation_index(frame, &attribute)];
    864         position[2] = (f32) values[retrieve_animation_index(frame, &attribute)];
    865     } else {
    866         vec3f_set(position, 0, 0, 0);
    867     }
    868 }
    869 
    870 /**
    871  * Unused function to find the root of the geo node tree, which should be a
    872  * GraphNodeRoot. If it is not for some reason, null is returned.
    873  */
    874 struct GraphNodeRoot *geo_find_root(struct GraphNode *graphNode) {
    875     struct GraphNodeRoot *resGraphNode = NULL;
    876 
    877     while (graphNode->parent != NULL) {
    878         graphNode = graphNode->parent;
    879     }
    880 
    881     if (graphNode->type == GRAPH_NODE_TYPE_ROOT) {
    882         resGraphNode = (struct GraphNodeRoot *) graphNode;
    883     }
    884 
    885     return resGraphNode;
    886 }