sm64pc/src/engine/graph_node.c

894 lines
30 KiB
C

#include <ultra64.h>
#include "sm64.h"
#include "game/level_update.h"
#include "math_util.h"
#include "game/memory.h"
#include "graph_node.h"
#include "game/rendering_graph_node.h"
#include "game/area.h"
#include "geo_layout.h"
// unused Mtx(s)
s16 identityMtx[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
s16 zeroMtx[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f };
Vec3s gVec3sZero = { 0, 0, 0 };
Vec3f gVec3fOne = { 1.0f, 1.0f, 1.0f };
UNUSED Vec3s gVec3sOne = { 1, 1, 1 };
/**
* Initialize a geo node with a given type. Sets all links such that there
* are no siblings, parent or children for this node.
*/
void init_scene_graph_node_links(struct GraphNode *graphNode, s32 type) {
graphNode->type = type;
graphNode->flags = GRAPH_RENDER_ACTIVE;
graphNode->prev = graphNode;
graphNode->next = graphNode;
graphNode->parent = NULL;
graphNode->children = NULL;
}
/**
* Allocated and returns a newly created root node
*/
struct GraphNodeRoot *init_graph_node_root(struct AllocOnlyPool *pool, struct GraphNodeRoot *graphNode,
s16 areaIndex, s16 x, s16 y, s16 width, s16 height) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRoot));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROOT);
graphNode->areaIndex = areaIndex;
graphNode->unk15 = 0;
graphNode->x = x;
graphNode->y = y;
graphNode->width = width;
graphNode->height = height;
graphNode->views = NULL;
graphNode->numViews = 0;
}
return graphNode;
}
/**
* Allocates and returns a newly created otrhographic projection node
*/
struct GraphNodeOrthoProjection *
init_graph_node_ortho_projection(struct AllocOnlyPool *pool, struct GraphNodeOrthoProjection *graphNode,
f32 scale) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeOrthoProjection));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ORTHO_PROJECTION);
graphNode->scale = scale;
}
return graphNode;
}
/**
* Allocates and returns a newly created perspective node
*/
struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *pool,
struct GraphNodePerspective *graphNode,
f32 fov, s16 near, s16 far,
GraphNodeFunc nodeFunc, s32 unused) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodePerspective));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_PERSPECTIVE);
graphNode->fov = fov;
graphNode->near = near;
graphNode->far = far;
graphNode->fnNode.func = nodeFunc;
graphNode->unused = unused;
if (nodeFunc != NULL) {
nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created start node
*/
struct GraphNodeStart *init_graph_node_start(struct AllocOnlyPool *pool,
struct GraphNodeStart *graphNode) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeStart));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_START);
}
return graphNode;
}
/**
* Allocates and returns a newly created master list node
*/
struct GraphNodeMasterList *init_graph_node_master_list(struct AllocOnlyPool *pool,
struct GraphNodeMasterList *graphNode, s16 on) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeMasterList));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_MASTER_LIST);
if (on) {
graphNode->node.flags |= GRAPH_RENDER_Z_BUFFER;
}
}
return graphNode;
}
/**
* Allocates and returns a newly created render range node
*/
struct GraphNodeLevelOfDetail *init_graph_node_render_range(struct AllocOnlyPool *pool,
struct GraphNodeLevelOfDetail *graphNode,
s16 minDistance, s16 maxDistance) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeLevelOfDetail));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_LEVEL_OF_DETAIL);
graphNode->minDistance = minDistance;
graphNode->maxDistance = maxDistance;
}
return graphNode;
}
/**
* Allocates and returns a newly created switch case node
*/
struct GraphNodeSwitchCase *init_graph_node_switch_case(struct AllocOnlyPool *pool,
struct GraphNodeSwitchCase *graphNode,
s16 numCases, s16 selectedCase,
GraphNodeFunc nodeFunc, s32 unused) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeSwitchCase));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_SWITCH_CASE);
graphNode->numCases = numCases;
graphNode->selectedCase = selectedCase;
graphNode->fnNode.func = nodeFunc;
graphNode->unused = unused;
if (nodeFunc != NULL) {
nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created camera node
*/
struct GraphNodeCamera *init_graph_node_camera(struct AllocOnlyPool *pool,
struct GraphNodeCamera *graphNode, f32 *fromPos,
f32 *toPos, GraphNodeFunc func, s32 preset) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCamera));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_CAMERA);
vec3f_copy(graphNode->from, fromPos);
vec3f_copy(graphNode->to, toPos);
graphNode->fnNode.func = func;
graphNode->config.preset = preset;
graphNode->roll = 0;
graphNode->rollScreen = 0;
if (func != NULL) {
func(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created translation rotation node
*/
struct GraphNodeTranslationRotation *
init_graph_node_translation_rotation(struct AllocOnlyPool *pool,
struct GraphNodeTranslationRotation *graphNode, s32 drawingLayer,
void *displayList, Vec3s translation, Vec3s rotation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslationRotation));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION_ROTATION);
vec3s_copy(graphNode->translation, translation);
vec3s_copy(graphNode->rotation, rotation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created translation node
*/
struct GraphNodeTranslation *init_graph_node_translation(struct AllocOnlyPool *pool,
struct GraphNodeTranslation *graphNode,
s32 drawingLayer, void *displayList,
Vec3s translation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslation));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created rotation node
*/
struct GraphNodeRotation *init_graph_node_rotation(struct AllocOnlyPool *pool,
struct GraphNodeRotation *graphNode,
s32 drawingLayer, void *displayList,
Vec3s rotation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRotation));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROTATION);
vec3s_copy(graphNode->rotation, rotation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created scaling node
*/
struct GraphNodeScale *init_graph_node_scale(struct AllocOnlyPool *pool,
struct GraphNodeScale *graphNode, s32 drawingLayer,
void *displayList, f32 scale) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeScale));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SCALE);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->scale = scale;
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created object node
*/
struct GraphNodeObject *init_graph_node_object(struct AllocOnlyPool *pool,
struct GraphNodeObject *graphNode,
struct GraphNode *sharedChild, Vec3f pos, Vec3s angle,
Vec3f scale) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObject));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT);
vec3f_copy(graphNode->pos, pos);
vec3f_copy(graphNode->scale, scale);
vec3s_copy(graphNode->angle, angle);
graphNode->sharedChild = sharedChild;
graphNode->throwMatrix = NULL;
graphNode->unk38.animID = 0;
graphNode->unk38.curAnim = NULL;
graphNode->unk38.animFrame = 0;
graphNode->unk38.animFrameAccelAssist = 0;
graphNode->unk38.animAccel = 0x10000;
graphNode->unk38.animTimer = 0;
graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
}
return graphNode;
}
/**
* Allocates and returns a newly created frustum culling radius node
*/
struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct AllocOnlyPool *pool,
struct GraphNodeCullingRadius *graphNode,
s16 radius) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCullingRadius));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_CULLING_RADIUS);
graphNode->cullingRadius = radius;
}
return graphNode;
}
/**
* Allocates and returns a newly created animated part node
*/
struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool *pool,
struct GraphNodeAnimatedPart *graphNode,
s32 drawingLayer, void *displayList,
Vec3s translation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeAnimatedPart));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ANIMATED_PART);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created billboard node
*/
struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool,
struct GraphNodeBillboard *graphNode,
s32 drawingLayer, void *displayList,
Vec3s translation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBillboard));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_BILLBOARD);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created displaylist node
*/
struct GraphNodeDisplayList *init_graph_node_display_list(struct AllocOnlyPool *pool,
struct GraphNodeDisplayList *graphNode,
s32 drawingLayer, void *displayList) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeDisplayList));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_DISPLAY_LIST);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created shadow node
*/
struct GraphNodeShadow *init_graph_node_shadow(struct AllocOnlyPool *pool,
struct GraphNodeShadow *graphNode, s16 shadowScale,
u8 shadowSolidity, u8 shadowType) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeShadow));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SHADOW);
graphNode->shadowScale = shadowScale;
graphNode->shadowSolidity = shadowSolidity;
graphNode->shadowType = shadowType;
}
return graphNode;
}
/**
* Allocates and returns a newly created object parent node
*/
struct GraphNodeObjectParent *init_graph_node_object_parent(struct AllocOnlyPool *pool,
struct GraphNodeObjectParent *graphNode,
struct GraphNode *sharedChild) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObjectParent));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT_PARENT);
graphNode->sharedChild = sharedChild;
}
return graphNode;
}
/**
* Allocates and returns a newly created generated node
*/
struct GraphNodeGenerated *init_graph_node_generated(struct AllocOnlyPool *pool,
struct GraphNodeGenerated *graphNode,
GraphNodeFunc gfxFunc, s32 parameter) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeGenerated));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_GENERATED_LIST);
graphNode->fnNode.func = gfxFunc;
graphNode->parameter = parameter;
if (gfxFunc != NULL) {
gfxFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created background node
*/
struct GraphNodeBackground *init_graph_node_background(struct AllocOnlyPool *pool,
struct GraphNodeBackground *graphNode,
u16 background, GraphNodeFunc backgroundFunc,
s32 zero) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBackground));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_BACKGROUND);
graphNode->background = (background << 16) | background;
graphNode->fnNode.func = backgroundFunc;
graphNode->unused = zero; // always 0, unused
if (backgroundFunc != NULL) {
backgroundFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created held object node
*/
struct GraphNodeHeldObject *init_graph_node_held_object(struct AllocOnlyPool *pool,
struct GraphNodeHeldObject *graphNode,
s32 objNode, Vec3s translation,
GraphNodeFunc nodeFunc, s32 unused) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeHeldObject));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_HELD_OBJ);
vec3s_copy(graphNode->translation, translation);
graphNode->objNode = (struct GraphNodeObject *) objNode; // assumed type
graphNode->fnNode.func = nodeFunc;
graphNode->unused = unused;
if (nodeFunc != NULL) {
nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Adds 'childNode' to the end of the list children from 'parent'
*/
struct GraphNode *geo_add_child(struct GraphNode *parent, struct GraphNode *childNode) {
struct GraphNode *parentFirstChild;
struct GraphNode *parentLastChild;
if (childNode != NULL) {
childNode->parent = parent;
parentFirstChild = parent->children;
if (parentFirstChild == NULL) {
parent->children = childNode;
childNode->prev = childNode;
childNode->next = childNode;
} else {
parentLastChild = parentFirstChild->prev;
childNode->prev = parentLastChild;
childNode->next = parentFirstChild;
parentFirstChild->prev = childNode;
parentLastChild->next = childNode;
}
}
return childNode;
}
/**
* Remove a node from the scene graph. It changes the links with its
* siblings and with its parent, it doesn't deallocate the memory
* since geo nodes are allocated in a pointer-bumping pool that
* gets thrown out when changing areas.
*/
struct GraphNode *geo_remove_child(struct GraphNode *graphNode) {
struct GraphNode *parent;
struct GraphNode **firstChild;
parent = graphNode->parent;
firstChild = &parent->children;
// Remove link with siblings
graphNode->prev->next = graphNode->next;
graphNode->next->prev = graphNode->prev;
// If this node was the first child, a new first child must be chosen
if (*firstChild == graphNode) {
// The list is circular, so this checks whether it was the only child
if (graphNode->next == graphNode) {
*firstChild = NULL; // Parent has no children anymore
} else {
*firstChild = graphNode->next; // Choose a new first child
}
}
return parent;
}
/**
* Reorders the given node so it's the first child of its parent.
* This is called on the Mario object when he is spawned. That's why Mario's
* object is always drawn before any other objects. (Note that the geo order
* is independent from processing group order, where Mario is not first.)
*/
struct GraphNode *geo_make_first_child(struct GraphNode *newFirstChild) {
struct GraphNode *lastSibling;
struct GraphNode *parent;
struct GraphNode **firstChild;
parent = newFirstChild->parent;
firstChild = &parent->children;
if (*firstChild != newFirstChild) {
if ((*firstChild)->prev != newFirstChild) {
newFirstChild->prev->next = newFirstChild->next;
newFirstChild->next->prev = newFirstChild->prev;
lastSibling = (*firstChild)->prev;
newFirstChild->prev = lastSibling;
newFirstChild->next = *firstChild;
(*firstChild)->prev = newFirstChild;
lastSibling->next = newFirstChild;
}
*firstChild = newFirstChild;
}
return parent;
}
/**
* Helper function for geo_call_global_function_nodes that recursively
* traverses the scene graph and calls the functions of global nodes.
*/
void geo_call_global_function_nodes_helper(struct GraphNode *graphNode, s32 callContext) {
struct GraphNode **globalPtr;
struct GraphNode *curNode;
struct FnGraphNode *asFnNode;
curNode = graphNode;
do {
asFnNode = (struct FnGraphNode *) curNode;
if (curNode->type & GRAPH_NODE_TYPE_FUNCTIONAL) {
if (asFnNode->func != NULL) {
asFnNode->func(callContext, curNode, NULL);
}
}
if (curNode->children != NULL) {
switch (curNode->type) {
case GRAPH_NODE_TYPE_MASTER_LIST:
globalPtr = (struct GraphNode **) &gCurGraphNodeMasterList;
break;
case GRAPH_NODE_TYPE_PERSPECTIVE:
globalPtr = (struct GraphNode **) &gCurGraphNodeCamFrustum;
break;
case GRAPH_NODE_TYPE_CAMERA:
globalPtr = (struct GraphNode **) &gCurGraphNodeCamera;
break;
case GRAPH_NODE_TYPE_OBJECT:
globalPtr = (struct GraphNode **) &gCurGraphNodeObject;
break;
default:
globalPtr = NULL;
break;
}
if (globalPtr != NULL) {
*globalPtr = curNode;
}
geo_call_global_function_nodes_helper(curNode->children, callContext);
if (globalPtr != NULL) {
*globalPtr = NULL;
}
}
} while ((curNode = curNode->next) != graphNode);
}
/**
* Call the update functions of geo nodes that are stored in global variables.
* These variables include gCurGraphNodeMasterList, gCurGraphNodeCamFrustum,
* gCurGraphNodeCamera and gCurGraphNodeObject.
* callContext is one of the GEO_CONTEXT_ defines.
* The graphNode argument should be of type GraphNodeRoot.
*/
void geo_call_global_function_nodes(struct GraphNode *graphNode, s32 callContext) {
if (graphNode->flags & GRAPH_RENDER_ACTIVE) {
gCurGraphNodeRoot = (struct GraphNodeRoot *) graphNode;
if (graphNode->children != NULL) {
geo_call_global_function_nodes_helper(graphNode->children, callContext);
}
gCurGraphNodeRoot = 0;
}
}
/**
* When objects are cleared, this is called on all object nodes (loaded or unloaded).
*/
void geo_reset_object_node(struct GraphNodeObject *graphNode) {
init_graph_node_object(NULL, graphNode, 0, gVec3fZero, gVec3sZero, gVec3fOne);
geo_add_child(&gObjParentGraphNode, &graphNode->node);
graphNode->node.flags &= ~GRAPH_RENDER_ACTIVE;
}
/**
* Initialize an object node using the given parameters
*/
void geo_obj_init(struct GraphNodeObject *graphNode, void *sharedChild, Vec3f pos, Vec3s angle) {
vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f);
vec3f_copy(graphNode->pos, pos);
vec3s_copy(graphNode->angle, angle);
graphNode->sharedChild = sharedChild;
graphNode->unk4C = 0;
graphNode->throwMatrix = NULL;
graphNode->unk38.curAnim = NULL;
graphNode->node.flags |= GRAPH_RENDER_ACTIVE;
graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE;
graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD;
}
/**
* Initialize and object node using the given SpawnInfo struct
*/
void geo_obj_init_spawninfo(struct GraphNodeObject *graphNode, struct SpawnInfo *spawn) {
vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f);
vec3s_copy(graphNode->angle, spawn->startAngle);
graphNode->pos[0] = (f32) spawn->startPos[0];
graphNode->pos[1] = (f32) spawn->startPos[1];
graphNode->pos[2] = (f32) spawn->startPos[2];
graphNode->unk18 = spawn->areaIndex;
graphNode->unk19 = spawn->activeAreaIndex;
graphNode->sharedChild = spawn->unk18;
graphNode->unk4C = spawn;
graphNode->throwMatrix = NULL;
graphNode->unk38.curAnim = 0;
graphNode->node.flags |= GRAPH_RENDER_ACTIVE;
graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE;
graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD;
}
/**
* Initialize the animation of an object node
*/
void geo_obj_init_animation(struct GraphNodeObject *graphNode, void *sp34) {
void **animSegmented = segmented_to_virtual(sp34);
struct Animation *anim = segmented_to_virtual(animSegmented[0]);
if (graphNode->unk38.curAnim != anim) {
graphNode->unk38.curAnim = anim;
graphNode->unk38.animFrame = (anim->unk04) + ((anim->flags & ANIM_FLAG_FORWARD) ? 1 : -1);
graphNode->unk38.animAccel = 0;
graphNode->unk38.animYTrans = 0;
}
}
/**
* Initialize the animation of an object node
*/
void geo_obj_init_animation_accel(struct GraphNodeObject *graphNode, void *sp34, u32 animAccel) {
void **animSegmented = segmented_to_virtual(sp34);
struct Animation *anim = segmented_to_virtual(animSegmented[0]);
if (graphNode->unk38.curAnim != anim) {
graphNode->unk38.curAnim = anim;
graphNode->unk38.animYTrans = 0;
graphNode->unk38.animFrameAccelAssist =
(anim->unk04 << 16) + ((anim->flags & ANIM_FLAG_FORWARD) ? animAccel : -animAccel);
graphNode->unk38.animFrame = graphNode->unk38.animFrameAccelAssist >> 16;
}
graphNode->unk38.animAccel = animAccel;
}
/**
* Retrieves an index into animation data based on the attribute pointer
* An attribute is an x-, y- or z-component of the translation / rotation for a part
* Each attribute is a pair of s16's, where the first s16 represents the maximum frame
* and the second s16 the actual index. This index can be used to index in the array
* with actual animation values.
*/
s32 retrieve_animation_index(s32 frame, u16 **attributes) {
s32 result;
if (frame < (*attributes)[0]) {
result = (*attributes)[1] + frame;
} else {
result = (*attributes)[1] + (*attributes)[0] - 1;
}
*attributes += 2;
return result;
}
/**
* Update the animation frame of an object. The animation flags determine
* whether it plays forwards or backwards, and whether it stops or loops at
* the end etc.
*/
s16 geo_update_animation_frame(struct GraphNodeObject_sub *obj, s32 *accelAssist) {
s32 result;
struct Animation *anim;
anim = obj->curAnim;
if (obj->animTimer == gAreaUpdateCounter || anim->flags & ANIM_FLAG_2) {
if (accelAssist != NULL) {
accelAssist[0] = obj->animFrameAccelAssist;
}
return obj->animFrame;
}
if (anim->flags & ANIM_FLAG_FORWARD) {
if (obj->animAccel) {
result = obj->animFrameAccelAssist - obj->animAccel;
} else {
result = (obj->animFrame - 1) << 16;
}
if (GET_HIGH_S16_OF_32(result) < anim->unk06) {
if (anim->flags & ANIM_FLAG_NOLOOP) {
SET_HIGH_S16_OF_32(result, anim->unk06);
} else {
SET_HIGH_S16_OF_32(result, anim->unk08 - 1);
}
}
} else {
if (obj->animAccel != 0) {
result = obj->animFrameAccelAssist + obj->animAccel;
} else {
result = (obj->animFrame + 1) << 16;
}
if (GET_HIGH_S16_OF_32(result) >= anim->unk08) {
if (anim->flags & ANIM_FLAG_NOLOOP) {
SET_HIGH_S16_OF_32(result, anim->unk08 - 1);
} else {
SET_HIGH_S16_OF_32(result, anim->unk06);
}
}
}
if (accelAssist != 0) {
accelAssist[0] = result;
}
return GET_HIGH_S16_OF_32(result);
}
/**
* Unused function to retrieve an object's current animation translation
* Assumes that it has x, y and z data in animations, which isn't always the
* case since some animation types only have vertical or lateral translation.
* This might have been used for positioning the shadow under an object, which
* currently happens in-line in geo_process_shadow where it also accounts for
* animations without lateral translation.
*/
void geo_retreive_animation_translation(struct GraphNodeObject *obj, Vec3f position) {
struct Animation *animation = obj->unk38.curAnim;
u16 *attribute;
s16 *values;
s16 frame;
if (animation != NULL) {
attribute = segmented_to_virtual(animation->index);
values = segmented_to_virtual(animation->values);
frame = obj->unk38.animFrame;
if (frame < 0) {
frame = 0;
}
if (1) // ? necessary to match
{
position[0] = (f32) values[retrieve_animation_index(frame, &attribute)];
position[1] = (f32) values[retrieve_animation_index(frame, &attribute)];
position[2] = (f32) values[retrieve_animation_index(frame, &attribute)];
}
} else {
vec3f_set(position, 0, 0, 0);
}
}
/**
* Unused function to find the root of the geo node tree, which should be a
* GraphNodeRoot. If it is not for some reason, null is returned.
*/
struct GraphNodeRoot *geo_find_root(struct GraphNode *graphNode) {
struct GraphNodeRoot *resGraphNode = NULL;
while (graphNode->parent != NULL) {
graphNode = graphNode->parent;
}
if (graphNode->type == GRAPH_NODE_TYPE_ROOT) {
resGraphNode = (struct GraphNodeRoot *) graphNode;
}
return resGraphNode;
}