2020-06-02 16:44:34 +00:00
|
|
|
#include <PR/ultratypes.h>
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "area.h"
|
|
|
|
#include "engine/math_util.h"
|
|
|
|
#include "game_init.h"
|
2020-05-07 18:21:22 +00:00
|
|
|
#include "gfx_dimensions.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
#include "main.h"
|
|
|
|
#include "memory.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "print.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
#include "rendering_graph_node.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "shadow.h"
|
|
|
|
#include "sm64.h"
|
2021-09-05 21:17:20 +00:00
|
|
|
#include "game/level_update.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* This file contains the code that processes the scene graph for rendering.
|
|
|
|
* The scene graph is responsible for drawing everything except the HUD / text boxes.
|
|
|
|
* First the root of the scene graph is processed when geo_process_root
|
|
|
|
* is called from level_script.c. The rest of the tree is traversed recursively
|
|
|
|
* using the function geo_process_node_and_siblings, which switches over all
|
|
|
|
* geo node types and calls a specialized function accordingly.
|
|
|
|
* The types are defined in engine/graph_node.h
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* The scene graph typically looks like:
|
|
|
|
* - Root (viewport)
|
|
|
|
* - Master list
|
|
|
|
* - Ortho projection
|
|
|
|
* - Background (skybox)
|
|
|
|
* - Master list
|
|
|
|
* - Perspective
|
|
|
|
* - Camera
|
|
|
|
* - <area-specific display lists>
|
|
|
|
* - Object parent
|
|
|
|
* - <group with 240 object nodes>
|
|
|
|
* - Master list
|
|
|
|
* - Script node (Cannon overlay)
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
s16 gMatStackIndex;
|
|
|
|
Mat4 gMatStack[32];
|
|
|
|
Mtx *gMatStackFixed[32];
|
2020-07-29 01:28:12 +00:00
|
|
|
Mat4 gMatStackInterpolated[32];
|
|
|
|
Mtx *gMatStackInterpolatedFixed[32];
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Animation nodes have state in global variables, so this struct captures
|
|
|
|
* the animation state so a 'context switch' can be made when rendering the
|
|
|
|
* held object.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
struct GeoAnimState {
|
|
|
|
/*0x00*/ u8 type;
|
|
|
|
/*0x01*/ u8 enabled;
|
|
|
|
/*0x02*/ s16 frame;
|
|
|
|
/*0x04*/ f32 translationMultiplier;
|
|
|
|
/*0x08*/ u16 *attribute;
|
|
|
|
/*0x0C*/ s16 *data;
|
2020-07-29 01:28:12 +00:00
|
|
|
s16 prevFrame;
|
2019-08-25 04:46:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// For some reason, this is a GeoAnimState struct, but the current state consists
|
|
|
|
// of separate global variables. It won't match EU otherwise.
|
|
|
|
struct GeoAnimState gGeoTempState;
|
|
|
|
|
|
|
|
u8 gCurAnimType;
|
|
|
|
u8 gCurAnimEnabled;
|
|
|
|
s16 gCurrAnimFrame;
|
2020-07-29 01:28:12 +00:00
|
|
|
s16 gPrevAnimFrame;
|
2019-08-25 04:46:40 +00:00
|
|
|
f32 gCurAnimTranslationMultiplier;
|
|
|
|
u16 *gCurrAnimAttribute;
|
|
|
|
s16 *gCurAnimData;
|
|
|
|
|
|
|
|
struct AllocOnlyPool *gDisplayListHeap;
|
|
|
|
|
|
|
|
struct RenderModeContainer {
|
|
|
|
u32 modes[8];
|
|
|
|
};
|
|
|
|
|
2019-11-03 19:36:27 +00:00
|
|
|
/* Rendermode settings for cycle 1 for all 8 layers. */
|
2019-08-25 04:46:40 +00:00
|
|
|
struct RenderModeContainer renderModeTable_1Cycle[2] = { { {
|
2019-11-03 19:36:27 +00:00
|
|
|
G_RM_OPA_SURF,
|
|
|
|
G_RM_AA_OPA_SURF,
|
|
|
|
G_RM_AA_OPA_SURF,
|
|
|
|
G_RM_AA_OPA_SURF,
|
|
|
|
G_RM_AA_TEX_EDGE,
|
|
|
|
G_RM_AA_XLU_SURF,
|
|
|
|
G_RM_AA_XLU_SURF,
|
|
|
|
G_RM_AA_XLU_SURF,
|
|
|
|
} },
|
|
|
|
{ {
|
|
|
|
/* z-buffered */
|
|
|
|
G_RM_ZB_OPA_SURF,
|
|
|
|
G_RM_AA_ZB_OPA_SURF,
|
|
|
|
G_RM_AA_ZB_OPA_DECAL,
|
|
|
|
G_RM_AA_ZB_OPA_INTER,
|
|
|
|
G_RM_AA_ZB_TEX_EDGE,
|
|
|
|
G_RM_AA_ZB_XLU_SURF,
|
|
|
|
G_RM_AA_ZB_XLU_DECAL,
|
|
|
|
G_RM_AA_ZB_XLU_INTER,
|
|
|
|
} } };
|
|
|
|
|
|
|
|
/* Rendermode settings for cycle 2 for all 8 layers. */
|
2019-08-25 04:46:40 +00:00
|
|
|
struct RenderModeContainer renderModeTable_2Cycle[2] = { { {
|
2019-11-03 19:36:27 +00:00
|
|
|
G_RM_OPA_SURF2,
|
|
|
|
G_RM_AA_OPA_SURF2,
|
|
|
|
G_RM_AA_OPA_SURF2,
|
|
|
|
G_RM_AA_OPA_SURF2,
|
|
|
|
G_RM_AA_TEX_EDGE2,
|
|
|
|
G_RM_AA_XLU_SURF2,
|
|
|
|
G_RM_AA_XLU_SURF2,
|
|
|
|
G_RM_AA_XLU_SURF2,
|
|
|
|
} },
|
|
|
|
{ {
|
|
|
|
/* z-buffered */
|
|
|
|
G_RM_ZB_OPA_SURF2,
|
|
|
|
G_RM_AA_ZB_OPA_SURF2,
|
|
|
|
G_RM_AA_ZB_OPA_DECAL2,
|
|
|
|
G_RM_AA_ZB_OPA_INTER2,
|
|
|
|
G_RM_AA_ZB_TEX_EDGE2,
|
|
|
|
G_RM_AA_ZB_XLU_SURF2,
|
|
|
|
G_RM_AA_ZB_XLU_DECAL2,
|
|
|
|
G_RM_AA_ZB_XLU_INTER2,
|
|
|
|
} } };
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
struct GraphNodeRoot *gCurGraphNodeRoot = NULL;
|
|
|
|
struct GraphNodeMasterList *gCurGraphNodeMasterList = NULL;
|
|
|
|
struct GraphNodePerspective *gCurGraphNodeCamFrustum = NULL;
|
|
|
|
struct GraphNodeCamera *gCurGraphNodeCamera = NULL;
|
|
|
|
struct GraphNodeObject *gCurGraphNodeObject = NULL;
|
|
|
|
struct GraphNodeHeldObject *gCurGraphNodeHeldObject = NULL;
|
|
|
|
u16 gAreaUpdateCounter = 0;
|
|
|
|
|
|
|
|
#ifdef F3DEX_GBI_2
|
|
|
|
LookAt lookAt;
|
|
|
|
#endif
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
static Gfx *sPerspectivePos;
|
|
|
|
static Mtx *sPerspectiveMtx;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
Gfx *pos;
|
|
|
|
void *mtx;
|
|
|
|
void *displayList;
|
|
|
|
} gMtxTbl[6400];
|
|
|
|
s32 gMtxTblSize;
|
|
|
|
|
|
|
|
static Gfx *sViewportPos;
|
|
|
|
static Vp sPrevViewport;
|
|
|
|
|
2020-09-06 20:54:01 +00:00
|
|
|
struct Object* gCurGraphNodeProcessingObject;
|
2021-09-05 21:17:20 +00:00
|
|
|
struct MarioState* gCurGraphNodeMarioState;
|
2020-09-06 20:54:01 +00:00
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
void mtx_patch_interpolated(void) {
|
|
|
|
s32 i;
|
|
|
|
|
|
|
|
if (sPerspectivePos != NULL) {
|
|
|
|
gSPMatrix(sPerspectivePos, VIRTUAL_TO_PHYSICAL(sPerspectiveMtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < gMtxTblSize; i++) {
|
|
|
|
Gfx *pos = gMtxTbl[i].pos;
|
|
|
|
gSPMatrix(pos++, VIRTUAL_TO_PHYSICAL(gMtxTbl[i].mtx),
|
|
|
|
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
|
|
|
|
gSPDisplayList(pos++, gMtxTbl[i].displayList);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sViewportPos != NULL) {
|
|
|
|
Gfx *saved = gDisplayListHead;
|
|
|
|
gDisplayListHead = sViewportPos;
|
|
|
|
make_viewport_clip_rect(&sPrevViewport);
|
|
|
|
gSPViewport(gDisplayListHead, VIRTUAL_TO_PHYSICAL(&sPrevViewport));
|
|
|
|
gDisplayListHead = saved;
|
|
|
|
}
|
|
|
|
|
|
|
|
gMtxTblSize = 0;
|
|
|
|
sPerspectivePos = NULL;
|
|
|
|
sViewportPos = NULL;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a master list node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_master_list_sub(struct GraphNodeMasterList *node) {
|
|
|
|
struct DisplayListNode *currList;
|
|
|
|
s32 i;
|
|
|
|
s32 enableZBuffer = (node->node.flags & GRAPH_RENDER_Z_BUFFER) != 0;
|
|
|
|
struct RenderModeContainer *modeList = &renderModeTable_1Cycle[enableZBuffer];
|
|
|
|
struct RenderModeContainer *mode2List = &renderModeTable_2Cycle[enableZBuffer];
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
// @bug This is where the LookAt values should be calculated but aren't.
|
|
|
|
// As a result, environment mapping is broken on Fast3DEX2 without the
|
|
|
|
// changes below.
|
2019-08-25 04:46:40 +00:00
|
|
|
#ifdef F3DEX_GBI_2
|
|
|
|
Mtx lMtx;
|
|
|
|
guLookAtReflect(&lMtx, &lookAt, 0, 0, 0, /* eye */ 0, 0, 1, /* at */ 1, 0, 0 /* up */);
|
|
|
|
#endif
|
2019-09-01 19:50:50 +00:00
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
if (enableZBuffer != 0) {
|
|
|
|
gDPPipeSync(gDisplayListHead++);
|
|
|
|
gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) {
|
|
|
|
if ((currList = node->listHeads[i]) != NULL) {
|
|
|
|
gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]);
|
|
|
|
while (currList != NULL) {
|
2020-07-29 01:28:12 +00:00
|
|
|
if ((u32) gMtxTblSize < sizeof(gMtxTbl) / sizeof(gMtxTbl[0])) {
|
|
|
|
gMtxTbl[gMtxTblSize].pos = gDisplayListHead;
|
|
|
|
gMtxTbl[gMtxTblSize].mtx = currList->transform;
|
|
|
|
gMtxTbl[gMtxTblSize++].displayList = currList->displayList;
|
|
|
|
}
|
|
|
|
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformInterpolated),
|
2020-04-03 18:57:26 +00:00
|
|
|
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
|
2020-07-29 01:28:12 +00:00
|
|
|
gSPDisplayList(gDisplayListHead++, currList->displayListInterpolated);
|
2019-08-25 04:46:40 +00:00
|
|
|
currList = currList->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (enableZBuffer != 0) {
|
|
|
|
gDPPipeSync(gDisplayListHead++);
|
|
|
|
gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Appends the display list to one of the master lists based on the layer
|
|
|
|
* parameter. Look at the RenderModeContainer struct to see the corresponding
|
|
|
|
* render modes of layers.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
2020-07-29 01:28:12 +00:00
|
|
|
static void geo_append_display_list2(void *displayList, void *displayListInterpolated, s16 layer) {
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
#ifdef F3DEX_GBI_2
|
|
|
|
gSPLookAt(gDisplayListHead++, &lookAt);
|
|
|
|
#endif
|
|
|
|
if (gCurGraphNodeMasterList != 0) {
|
|
|
|
struct DisplayListNode *listNode =
|
|
|
|
alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode));
|
|
|
|
|
|
|
|
listNode->transform = gMatStackFixed[gMatStackIndex];
|
2020-07-29 01:28:12 +00:00
|
|
|
listNode->transformInterpolated = gMatStackInterpolatedFixed[gMatStackIndex];
|
2019-08-25 04:46:40 +00:00
|
|
|
listNode->displayList = displayList;
|
2020-07-29 01:28:12 +00:00
|
|
|
listNode->displayListInterpolated = displayListInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
listNode->next = 0;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gCurGraphNodeMasterList->listHeads[layer] == 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurGraphNodeMasterList->listHeads[layer] = listNode;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurGraphNodeMasterList->listTails[layer]->next = listNode;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurGraphNodeMasterList->listTails[layer] = listNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
static void geo_append_display_list(void *displayList, s16 layer) {
|
|
|
|
geo_append_display_list2(displayList, displayList, layer);
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process the master list node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_master_list(struct GraphNodeMasterList *node) {
|
|
|
|
s32 i;
|
|
|
|
UNUSED s32 sp1C;
|
|
|
|
|
|
|
|
if (gCurGraphNodeMasterList == NULL && node->node.children != NULL) {
|
|
|
|
gCurGraphNodeMasterList = node;
|
2019-09-01 19:50:50 +00:00
|
|
|
for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) {
|
2019-08-25 04:46:40 +00:00
|
|
|
node->listHeads[i] = NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
|
|
|
geo_process_master_list_sub(node);
|
|
|
|
gCurGraphNodeMasterList = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process an orthographic projection node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_ortho_projection(struct GraphNodeOrthoProjection *node) {
|
|
|
|
if (node->node.children != NULL) {
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
|
|
|
f32 left = (gCurGraphNodeRoot->x - gCurGraphNodeRoot->width) / 2.0f * node->scale;
|
|
|
|
f32 right = (gCurGraphNodeRoot->x + gCurGraphNodeRoot->width) / 2.0f * node->scale;
|
|
|
|
f32 top = (gCurGraphNodeRoot->y - gCurGraphNodeRoot->height) / 2.0f * node->scale;
|
|
|
|
f32 bottom = (gCurGraphNodeRoot->y + gCurGraphNodeRoot->height) / 2.0f * node->scale;
|
|
|
|
|
|
|
|
guOrtho(mtx, left, right, bottom, top, -2.0f, 2.0f, 1.0f);
|
|
|
|
gSPPerspNormalize(gDisplayListHead++, 0xFFFF);
|
2019-11-03 19:36:27 +00:00
|
|
|
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
geo_process_node_and_siblings(node->node.children);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a perspective projection node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_perspective(struct GraphNodePerspective *node) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.func != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
if (node->fnNode.node.children != NULL) {
|
|
|
|
u16 perspNorm;
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
f32 fovInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
#ifdef VERSION_EU
|
|
|
|
f32 aspect = ((f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height) * 1.1f;
|
|
|
|
#else
|
|
|
|
f32 aspect = (f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f);
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
|
|
|
|
|
|
|
fovInterpolated = (node->prevFov + node->fov) / 2.0f;
|
|
|
|
guPerspective(mtxInterpolated, &perspNorm, fovInterpolated, aspect, node->near, node->far, 1.0f);
|
|
|
|
gSPPerspNormalize(gDisplayListHead++, perspNorm);
|
|
|
|
|
|
|
|
sPerspectivePos = gDisplayListHead;
|
|
|
|
sPerspectiveMtx = mtx;
|
|
|
|
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtxInterpolated),
|
|
|
|
G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
|
|
|
|
} else {
|
|
|
|
gSPPerspNormalize(gDisplayListHead++, perspNorm);
|
|
|
|
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
|
|
|
|
}
|
|
|
|
node->prevFov = node->fov;
|
|
|
|
node->prevTimestamp = gGlobalTimer;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
gCurGraphNodeCamFrustum = node;
|
|
|
|
geo_process_node_and_siblings(node->fnNode.node.children);
|
|
|
|
gCurGraphNodeCamFrustum = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a level of detail node. From the current transformation matrix,
|
|
|
|
* the perpendicular distance to the camera is extracted and the children
|
|
|
|
* of this node are only processed if that distance is within the render
|
|
|
|
* range of this node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) {
|
2020-07-04 20:18:17 +00:00
|
|
|
// We assume modern hardware is powerful enough to draw the most detailed variant
|
2020-07-05 11:42:26 +00:00
|
|
|
s16 distanceFromCam = 0;
|
2020-07-04 20:18:17 +00:00
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
if (node->minDistance <= distanceFromCam && distanceFromCam < node->maxDistance) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->node.children != 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a switch case node. The node's selection function is called
|
|
|
|
* if it is 0, and among the node's children, only the selected child is
|
|
|
|
* processed next.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_switch(struct GraphNodeSwitchCase *node) {
|
|
|
|
struct GraphNode *selectedChild = node->fnNode.node.children;
|
|
|
|
s32 i;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.func != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
for (i = 0; selectedChild != NULL && node->selectedCase > i; i++) {
|
2019-08-25 04:46:40 +00:00
|
|
|
selectedChild = selectedChild->next;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (selectedChild != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(selectedChild);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b) {
|
|
|
|
res[0] = (a[0] + b[0]) / 2.0f;
|
|
|
|
res[1] = (a[1] + b[1]) / 2.0f;
|
|
|
|
res[2] = (a[2] + b[2]) / 2.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b) {
|
|
|
|
res[0] = (a[0] + b[0]) / 2;
|
|
|
|
res[1] = (a[1] + b[1]) / 2;
|
|
|
|
res[2] = (a[2] + b[2]) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static s16 interpolate_angle(s16 a, s16 b) {
|
|
|
|
s32 absDiff = b - a;
|
|
|
|
if (absDiff < 0) {
|
|
|
|
absDiff = -absDiff;
|
|
|
|
}
|
|
|
|
if (absDiff >= 0x4000 && absDiff <= 0xC000) {
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
if (absDiff <= 0x8000) {
|
|
|
|
return (a + b) / 2;
|
|
|
|
} else {
|
|
|
|
return (a + b) / 2 + 0x8000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void interpolate_angles(Vec3s res, Vec3s a, Vec3s b) {
|
|
|
|
res[0] = interpolate_angle(a[0], b[0]);
|
|
|
|
res[1] = interpolate_angle(a[1], b[1]);
|
|
|
|
res[2] = interpolate_angle(a[2], b[2]);
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a camera node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_camera(struct GraphNodeCamera *node) {
|
|
|
|
Mat4 cameraTransform;
|
|
|
|
Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx));
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
|
|
|
Vec3f posInterpolated;
|
|
|
|
Vec3f focusInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.func != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_rotate_xy(rollMtx, node->rollScreen);
|
|
|
|
|
2019-11-03 19:36:27 +00:00
|
|
|
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(rollMtx), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-01-03 15:38:57 +00:00
|
|
|
mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll);
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
|
|
|
|
if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
|
|
|
interpolate_vectors(posInterpolated, node->prevPos, node->pos);
|
|
|
|
interpolate_vectors(focusInterpolated, node->prevFocus, node->focus);
|
|
|
|
float magnitude = 0;
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
float diff = node->pos[i] - node->prevPos[i];
|
|
|
|
magnitude += diff * diff;
|
|
|
|
}
|
|
|
|
if (magnitude > 500000) {
|
|
|
|
// Observed ~479000 in BBH when toggling R camera
|
|
|
|
// Can get over 3 million in VCUTM though...
|
|
|
|
vec3f_copy(posInterpolated, node->pos);
|
|
|
|
vec3f_copy(focusInterpolated, node->focus);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vec3f_copy(posInterpolated, node->pos);
|
|
|
|
vec3f_copy(focusInterpolated, node->focus);
|
|
|
|
}
|
|
|
|
vec3f_copy(node->prevPos, node->pos);
|
|
|
|
vec3f_copy(node->prevFocus, node->focus);
|
|
|
|
node->prevTimestamp = gGlobalTimer;
|
|
|
|
mtxf_lookat(cameraTransform, posInterpolated, focusInterpolated, node->roll);
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], cameraTransform, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
if (node->fnNode.node.children != 0) {
|
|
|
|
gCurGraphNodeCamera = node;
|
2020-07-04 15:18:55 +00:00
|
|
|
node->matrixPtr = &gMatStack[gMatStackIndex];
|
2020-07-29 01:28:12 +00:00
|
|
|
node->matrixPtrInterpolated = &gMatStackInterpolated[gMatStackIndex];
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->fnNode.node.children);
|
|
|
|
gCurGraphNodeCamera = NULL;
|
|
|
|
}
|
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a translation / rotation node. A transformation matrix based
|
|
|
|
* on the node's translation and rotation is created and pushed on both
|
|
|
|
* the float and fixed point matrix stacks.
|
|
|
|
* For the rest it acts as a normal display list node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_translation_rotation(struct GraphNodeTranslationRotation *node) {
|
|
|
|
Mat4 mtxf;
|
|
|
|
Vec3f translation;
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
vec3s_to_vec3f(translation, node->translation);
|
|
|
|
mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation);
|
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]);
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->displayList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_append_display_list(node->displayList, node->node.flags >> 8);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a translation node. A transformation matrix based on the node's
|
|
|
|
* translation is created and pushed on both the float and fixed point matrix stacks.
|
|
|
|
* For the rest it acts as a normal display list node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_translation(struct GraphNodeTranslation *node) {
|
|
|
|
Mat4 mtxf;
|
|
|
|
Vec3f translation;
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
vec3s_to_vec3f(translation, node->translation);
|
|
|
|
mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero);
|
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]);
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->displayList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_append_display_list(node->displayList, node->node.flags >> 8);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a rotation node. A transformation matrix based on the node's
|
|
|
|
* rotation is created and pushed on both the float and fixed point matrix stacks.
|
|
|
|
* For the rest it acts as a normal display list node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_rotation(struct GraphNodeRotation *node) {
|
|
|
|
Mat4 mtxf;
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
|
|
|
Vec3s rotationInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation);
|
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
if (gGlobalTimer == node->prevTimestamp + 1) {
|
|
|
|
interpolate_angles(rotationInterpolated, node->prevRotation, node->rotation);
|
|
|
|
mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, rotationInterpolated);
|
|
|
|
}
|
|
|
|
vec3s_copy(node->prevRotation, node->rotation);
|
|
|
|
node->prevTimestamp = gGlobalTimer;
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]);
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->displayList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_append_display_list(node->displayList, node->node.flags >> 8);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a scaling node. A transformation matrix based on the node's
|
|
|
|
* scale is created and pushed on both the float and fixed point matrix stacks.
|
|
|
|
* For the rest it acts as a normal display list node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_scale(struct GraphNodeScale *node) {
|
|
|
|
UNUSED Mat4 transform;
|
|
|
|
Vec3f scaleVec;
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
vec3f_set(scaleVec, node->scale, node->scale, node->scale);
|
|
|
|
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], scaleVec);
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->displayList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_append_display_list(node->displayList, node->node.flags >> 8);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a billboard node. A transformation matrix is created that makes its
|
|
|
|
* children face the camera, and it is pushed on the floating point and fixed
|
|
|
|
* point matrix stacks.
|
|
|
|
* For the rest it acts as a normal display list node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_billboard(struct GraphNodeBillboard *node) {
|
|
|
|
Vec3f translation;
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
gMatStackIndex++;
|
|
|
|
vec3s_to_vec3f(translation, node->translation);
|
|
|
|
mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation,
|
|
|
|
gCurGraphNodeCamera->roll);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_billboard(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex - 1], translation,
|
|
|
|
gCurGraphNodeCamera->roll);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gCurGraphNodeHeldObject != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex],
|
2019-11-03 19:36:27 +00:00
|
|
|
gCurGraphNodeHeldObject->objNode->header.gfx.scale);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex],
|
|
|
|
gCurGraphNodeHeldObject->objNode->header.gfx.scale);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (gCurGraphNodeObject != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex],
|
|
|
|
gCurGraphNodeObject->scale);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex],
|
|
|
|
gCurGraphNodeObject->scale);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->displayList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_append_display_list(node->displayList, node->node.flags >> 8);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a display list node. It draws a display list without first pushing
|
|
|
|
* a transformation on the stack, so all transformations are inherited from the
|
|
|
|
* parent node. It processes its children if it has them.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_display_list(struct GraphNodeDisplayList *node) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->displayList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_append_display_list(node->displayList, node->node.flags >> 8);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a generated list. Instead of storing a pointer to a display list,
|
|
|
|
* the list is generated on the fly by a function.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_generated_list(struct GraphNodeGenerated *node) {
|
|
|
|
if (node->fnNode.func != NULL) {
|
2019-11-03 19:36:27 +00:00
|
|
|
Gfx *list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node,
|
2019-08-25 04:46:40 +00:00
|
|
|
(struct AllocOnlyPool *) gMatStack[gMatStackIndex]);
|
|
|
|
|
|
|
|
if (list != 0) {
|
|
|
|
geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8);
|
|
|
|
}
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->fnNode.node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a background node. Tries to retrieve a background display list from
|
|
|
|
* the function of the node. If that function is null or returns null, a black
|
|
|
|
* rectangle is drawn instead.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_background(struct GraphNodeBackground *node) {
|
2019-11-03 19:36:27 +00:00
|
|
|
Gfx *list = NULL;
|
2020-07-29 01:28:12 +00:00
|
|
|
Gfx *listInterpolated = NULL;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.func != NULL) {
|
2020-07-29 01:28:12 +00:00
|
|
|
Vec3f posCopy;
|
|
|
|
Vec3f focusCopy;
|
|
|
|
Vec3f posInterpolated;
|
|
|
|
Vec3f focusInterpolated;
|
|
|
|
|
|
|
|
if (gGlobalTimer == node->prevCameraTimestamp + 1 &&
|
|
|
|
gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
|
|
|
interpolate_vectors(posInterpolated, node->prevCameraPos, gLakituState.pos);
|
|
|
|
interpolate_vectors(focusInterpolated, node->prevCameraFocus, gLakituState.focus);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(posInterpolated, gLakituState.pos);
|
|
|
|
vec3f_copy(focusInterpolated, gLakituState.focus);
|
|
|
|
}
|
|
|
|
vec3f_copy(node->prevCameraPos, gLakituState.pos);
|
|
|
|
vec3f_copy(node->prevCameraFocus, gLakituState.focus);
|
|
|
|
node->prevCameraTimestamp = gGlobalTimer;
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node,
|
|
|
|
(struct AllocOnlyPool *) gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
vec3f_copy(posCopy, gLakituState.pos);
|
|
|
|
vec3f_copy(focusCopy, gLakituState.focus);
|
|
|
|
vec3f_copy(gLakituState.pos, posInterpolated);
|
|
|
|
vec3f_copy(gLakituState.focus, focusInterpolated);
|
|
|
|
listInterpolated = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, NULL);
|
|
|
|
vec3f_copy(gLakituState.pos, posCopy);
|
|
|
|
vec3f_copy(gLakituState.focus, focusCopy);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
if (list != 0) {
|
2020-07-29 01:28:12 +00:00
|
|
|
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(list),
|
|
|
|
(void *) VIRTUAL_TO_PHYSICAL(listInterpolated), node->fnNode.node.flags >> 8);
|
2019-08-25 04:46:40 +00:00
|
|
|
} else if (gCurGraphNodeMasterList != NULL) {
|
2020-06-02 16:44:34 +00:00
|
|
|
#ifndef F3DEX_GBI_2E
|
|
|
|
Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7);
|
|
|
|
#else
|
2020-05-07 18:21:22 +00:00
|
|
|
Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 8);
|
2020-06-02 16:44:34 +00:00
|
|
|
#endif
|
2019-08-25 04:46:40 +00:00
|
|
|
Gfx *gfx = gfxStart;
|
|
|
|
|
|
|
|
gDPPipeSync(gfx++);
|
|
|
|
gDPSetCycleType(gfx++, G_CYC_FILL);
|
|
|
|
gDPSetFillColor(gfx++, node->background);
|
2020-05-07 18:21:22 +00:00
|
|
|
gDPFillRectangle(gfx++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), BORDER_HEIGHT,
|
|
|
|
GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - BORDER_HEIGHT - 1);
|
2019-08-25 04:46:40 +00:00
|
|
|
gDPPipeSync(gfx++);
|
|
|
|
gDPSetCycleType(gfx++, G_CYC_1CYCLE);
|
|
|
|
gSPEndDisplayList(gfx++);
|
|
|
|
|
|
|
|
geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(gfxStart), 0);
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->fnNode.node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 animFrame, u16 **animAttribute) {
|
|
|
|
if (*animType == ANIM_TYPE_TRANSLATION) {
|
|
|
|
translation[0] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]
|
2019-08-25 04:46:40 +00:00
|
|
|
* gCurAnimTranslationMultiplier;
|
2020-07-29 01:28:12 +00:00
|
|
|
translation[1] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]
|
2019-08-25 04:46:40 +00:00
|
|
|
* gCurAnimTranslationMultiplier;
|
2020-07-29 01:28:12 +00:00
|
|
|
translation[2] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]
|
2019-08-25 04:46:40 +00:00
|
|
|
* gCurAnimTranslationMultiplier;
|
2020-07-29 01:28:12 +00:00
|
|
|
*animType = ANIM_TYPE_ROTATION;
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
2020-07-29 01:28:12 +00:00
|
|
|
if (*animType == ANIM_TYPE_LATERAL_TRANSLATION) {
|
2019-08-25 04:46:40 +00:00
|
|
|
translation[0] +=
|
2020-07-29 01:28:12 +00:00
|
|
|
gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]
|
2019-08-25 04:46:40 +00:00
|
|
|
* gCurAnimTranslationMultiplier;
|
2020-07-29 01:28:12 +00:00
|
|
|
*animAttribute += 2;
|
2019-08-25 04:46:40 +00:00
|
|
|
translation[2] +=
|
2020-07-29 01:28:12 +00:00
|
|
|
gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]
|
2019-08-25 04:46:40 +00:00
|
|
|
* gCurAnimTranslationMultiplier;
|
2020-07-29 01:28:12 +00:00
|
|
|
*animType = ANIM_TYPE_ROTATION;
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
2020-07-29 01:28:12 +00:00
|
|
|
if (*animType == ANIM_TYPE_VERTICAL_TRANSLATION) {
|
|
|
|
*animAttribute += 2;
|
2019-08-25 04:46:40 +00:00
|
|
|
translation[1] +=
|
2020-07-29 01:28:12 +00:00
|
|
|
gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]
|
2019-08-25 04:46:40 +00:00
|
|
|
* gCurAnimTranslationMultiplier;
|
2020-07-29 01:28:12 +00:00
|
|
|
*animAttribute += 2;
|
|
|
|
*animType = ANIM_TYPE_ROTATION;
|
|
|
|
} else if (*animType == ANIM_TYPE_NO_TRANSLATION) {
|
|
|
|
*animAttribute += 6;
|
|
|
|
*animType = ANIM_TYPE_ROTATION;
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
if (*animType == ANIM_TYPE_ROTATION) {
|
|
|
|
rotation[0] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)];
|
|
|
|
rotation[1] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)];
|
|
|
|
rotation[2] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)];
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
2020-07-29 01:28:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render an animated part. The current animation state is not part of the node
|
|
|
|
* but set in global variables. If an animated part is skipped, everything afterwards desyncs.
|
|
|
|
*/
|
|
|
|
static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
|
|
|
|
Mat4 matrix;
|
|
|
|
Vec3s rotation;
|
|
|
|
Vec3f translation;
|
|
|
|
Vec3s rotationInterpolated;
|
|
|
|
Vec3f translationInterpolated;
|
|
|
|
Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr));
|
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
|
|
|
u16 *animAttribute = gCurrAnimAttribute;
|
|
|
|
u8 animType = gCurAnimType;
|
|
|
|
|
|
|
|
vec3s_copy(rotation, gVec3sZero);
|
|
|
|
vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]);
|
|
|
|
vec3s_copy(rotationInterpolated, rotation);
|
|
|
|
vec3f_copy(translationInterpolated, translation);
|
|
|
|
|
|
|
|
anim_process(translationInterpolated, rotationInterpolated, &animType, gPrevAnimFrame, &animAttribute);
|
|
|
|
anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
|
|
|
|
interpolate_vectors(translationInterpolated, translationInterpolated, translation);
|
|
|
|
interpolate_angles(rotationInterpolated, rotationInterpolated, rotation);
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
|
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_rotate_xyz_and_translate(matrix, translationInterpolated, rotationInterpolated);
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], matrix, gMatStackInterpolated[gMatStackIndex]);
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
|
|
|
mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = matrixPtr;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2021-09-05 21:17:20 +00:00
|
|
|
|
|
|
|
if (gCurGraphNodeMarioState != NULL) {
|
|
|
|
Vec3f translated = { 0 };
|
|
|
|
get_pos_from_transform_mtx(translated, gMatStack[gMatStackIndex], *gCurGraphNodeCamera->matrixPtr);
|
|
|
|
gCurGraphNodeMarioState->minimumBoneY = fmin(gCurGraphNodeMarioState->minimumBoneY, translated[1] - gCurGraphNodeMarioState->marioObj->header.gfx.pos[1]);
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->displayList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_append_display_list(node->displayList, node->node.flags >> 8);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Initialize the animation-related global variables for the currently drawn
|
|
|
|
* object's animation.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void geo_set_animation_globals(struct GraphNodeObject_sub *node, s32 hasAnimation) {
|
|
|
|
struct Animation *anim = node->curAnim;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (hasAnimation != 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
node->animFrame = geo_update_animation_frame(node, &node->animFrameAccelAssist);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
node->animTimer = gAreaUpdateCounter;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (anim->flags & ANIM_FLAG_HOR_TRANS) {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimType = ANIM_TYPE_VERTICAL_TRANSLATION;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (anim->flags & ANIM_FLAG_VERT_TRANS) {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimType = ANIM_TYPE_LATERAL_TRANSLATION;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (anim->flags & ANIM_FLAG_6) {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimType = ANIM_TYPE_NO_TRANSLATION;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimType = ANIM_TYPE_TRANSLATION;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
gCurrAnimFrame = node->animFrame;
|
2020-07-29 01:28:12 +00:00
|
|
|
if (node->prevAnimPtr == anim && node->prevAnimID == node->animID &&
|
|
|
|
gGlobalTimer == node->prevAnimFrameTimestamp + 1) {
|
|
|
|
gPrevAnimFrame = node->prevAnimFrame;
|
|
|
|
} else {
|
|
|
|
gPrevAnimFrame = node->animFrame;
|
|
|
|
}
|
|
|
|
node->prevAnimPtr = anim;
|
|
|
|
node->prevAnimID = node->animID;
|
|
|
|
node->prevAnimFrame = node->animFrame;
|
|
|
|
node->prevAnimFrameTimestamp = gGlobalTimer;
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimEnabled = (anim->flags & ANIM_FLAG_5) == 0;
|
2019-11-03 19:36:27 +00:00
|
|
|
gCurrAnimAttribute = segmented_to_virtual((void *) anim->index);
|
|
|
|
gCurAnimData = segmented_to_virtual((void *) anim->values);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (anim->unk02 == 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimTranslationMultiplier = 1.0f;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimTranslationMultiplier = (f32) node->animYTrans / (f32) anim->unk02;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a shadow node. Renders a shadow under an object offset by the
|
|
|
|
* translation of the first animated component and rotated according to
|
|
|
|
* the floor below it.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_shadow(struct GraphNodeShadow *node) {
|
|
|
|
Gfx *shadowList;
|
2020-07-29 01:28:12 +00:00
|
|
|
Gfx *shadowListInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
Mat4 mtxf;
|
|
|
|
Vec3f shadowPos;
|
2020-07-29 01:28:12 +00:00
|
|
|
Vec3f shadowPosInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
Vec3f animOffset;
|
|
|
|
f32 objScale;
|
|
|
|
f32 shadowScale;
|
|
|
|
f32 sinAng;
|
|
|
|
f32 cosAng;
|
|
|
|
struct GraphNode *geo;
|
|
|
|
Mtx *mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) {
|
|
|
|
if (gCurGraphNodeHeldObject != NULL) {
|
|
|
|
get_pos_from_transform_mtx(shadowPos, gMatStack[gMatStackIndex],
|
2020-07-04 15:18:55 +00:00
|
|
|
*gCurGraphNodeCamera->matrixPtr);
|
2019-08-25 04:46:40 +00:00
|
|
|
shadowScale = node->shadowScale;
|
|
|
|
} else {
|
|
|
|
vec3f_copy(shadowPos, gCurGraphNodeObject->pos);
|
|
|
|
shadowScale = node->shadowScale * gCurGraphNodeObject->scale[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
objScale = 1.0f;
|
|
|
|
if (gCurAnimEnabled != 0) {
|
|
|
|
if (gCurAnimType == ANIM_TYPE_TRANSLATION
|
|
|
|
|| gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) {
|
|
|
|
geo = node->node.children;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (geo != NULL && geo->type == GRAPH_NODE_TYPE_SCALE) {
|
2019-08-25 04:46:40 +00:00
|
|
|
objScale = ((struct GraphNodeScale *) geo)->scale;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
animOffset[0] =
|
|
|
|
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
|
|
|
|
* gCurAnimTranslationMultiplier * objScale;
|
|
|
|
animOffset[1] = 0.0f;
|
|
|
|
gCurrAnimAttribute += 2;
|
|
|
|
animOffset[2] =
|
|
|
|
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
|
|
|
|
* gCurAnimTranslationMultiplier * objScale;
|
|
|
|
gCurrAnimAttribute -= 6;
|
|
|
|
|
|
|
|
// simple matrix rotation so the shadow offset rotates along with the object
|
|
|
|
sinAng = sins(gCurGraphNodeObject->angle[1]);
|
|
|
|
cosAng = coss(gCurGraphNodeObject->angle[1]);
|
|
|
|
|
|
|
|
shadowPos[0] += animOffset[0] * cosAng + animOffset[2] * sinAng;
|
|
|
|
shadowPos[2] += -animOffset[0] * sinAng + animOffset[2] * cosAng;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
if (gCurGraphNodeHeldObject != NULL) {
|
|
|
|
if (gGlobalTimer == gCurGraphNodeHeldObject->prevShadowPosTimestamp + 1) {
|
|
|
|
interpolate_vectors(shadowPosInterpolated, gCurGraphNodeHeldObject->prevShadowPos, shadowPos);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(shadowPosInterpolated, shadowPos);
|
|
|
|
}
|
|
|
|
vec3f_copy(gCurGraphNodeHeldObject->prevShadowPos, shadowPos);
|
|
|
|
gCurGraphNodeHeldObject->prevShadowPosTimestamp = gGlobalTimer;
|
|
|
|
} else {
|
|
|
|
if (gGlobalTimer == gCurGraphNodeObject->prevShadowPosTimestamp + 1 &&
|
2020-09-27 05:00:34 +00:00
|
|
|
gGlobalTimer != gCurGraphNodeObject->skipInterpolationTimestamp &&
|
|
|
|
gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
2020-07-29 01:28:12 +00:00
|
|
|
interpolate_vectors(shadowPosInterpolated, gCurGraphNodeObject->prevShadowPos, shadowPos);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(shadowPosInterpolated, shadowPos);
|
|
|
|
}
|
|
|
|
vec3f_copy(gCurGraphNodeObject->prevShadowPos, shadowPos);
|
|
|
|
gCurGraphNodeObject->prevShadowPosTimestamp = gGlobalTimer;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern u8 gInterpolatingSurfaces;
|
|
|
|
gInterpolatingSurfaces = TRUE;
|
|
|
|
shadowListInterpolated = create_shadow_below_xyz(shadowPosInterpolated[0], shadowPosInterpolated[1],
|
|
|
|
shadowPosInterpolated[2], shadowScale,
|
|
|
|
node->shadowSolidity, node->shadowType);
|
|
|
|
gInterpolatingSurfaces = FALSE;
|
2019-08-25 04:46:40 +00:00
|
|
|
shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale,
|
|
|
|
node->shadowSolidity, node->shadowType);
|
2020-07-29 01:28:12 +00:00
|
|
|
if (shadowListInterpolated != NULL && shadowList != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
2020-07-29 01:28:12 +00:00
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_translate(mtxf, shadowPos);
|
2020-07-04 15:18:55 +00:00
|
|
|
mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr);
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
|
|
|
|
mtxf_translate(mtxf, shadowPosInterpolated);
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtrInterpolated);
|
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gShadowAboveWaterOrLava == 1) {
|
2020-07-29 01:28:12 +00:00
|
|
|
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList),
|
|
|
|
(void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 4);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (gMarioOnIceOrCarpet == 1) {
|
2020-07-29 01:28:12 +00:00
|
|
|
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList),
|
|
|
|
(void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 5);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2020-07-29 01:28:12 +00:00
|
|
|
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList),
|
|
|
|
(void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 6);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Check whether an object is in view to determine whether it should be drawn.
|
2020-06-02 16:44:34 +00:00
|
|
|
* This is known as frustum culling.
|
2019-09-01 19:50:50 +00:00
|
|
|
* It checks whether the object is far away, very close / behind the camera,
|
|
|
|
* or horizontally out of view. It does not check whether it is vertically
|
|
|
|
* out of view. It assumes a sphere of 300 units around the object's position
|
|
|
|
* unless the object has a culling radius node that specifies otherwise.
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* The matrix parameter should be the top of the matrix stack, which is the
|
|
|
|
* object's transformation matrix times the camera 'look-at' matrix. The math
|
|
|
|
* is counter-intuitive, but it checks column 3 (translation vector) of this
|
|
|
|
* matrix to determine where the origin (0,0,0) in object space will be once
|
|
|
|
* transformed to camera space (x+ = right, y+ = up, z = 'coming out the screen').
|
|
|
|
* In 3D graphics, you typically model the world as being moved in front of a
|
|
|
|
* static camera instead of a moving camera through a static world, which in
|
|
|
|
* this case simplifies calculations. Note that the perspective matrix is not
|
|
|
|
* on the matrix stack, so there are still calculations with the fov to compute
|
2020-06-02 16:44:34 +00:00
|
|
|
* the slope of the lines of the frustum.
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* z-
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* \ | /
|
|
|
|
* \ | /
|
|
|
|
* \ | /
|
|
|
|
* \ | /
|
|
|
|
* \ | /
|
|
|
|
* \|/
|
|
|
|
* C x+
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* Since (0,0,0) is unaffected by rotation, columns 0, 1 and 2 are ignored.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static int obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) {
|
|
|
|
s16 cullingRadius;
|
|
|
|
s16 halfFov; // half of the fov in in-game angle units instead of degrees
|
|
|
|
struct GraphNode *geo;
|
|
|
|
f32 hScreenEdge;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->node.flags & GRAPH_RENDER_INVISIBLE) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
geo = node->sharedChild;
|
|
|
|
|
|
|
|
// ! @bug The aspect ratio is not accounted for. When the fov value is 45,
|
|
|
|
// the horizontal effective fov is actually 60 degrees, so you can see objects
|
|
|
|
// visibly pop in or out at the edge of the screen.
|
|
|
|
halfFov = (gCurGraphNodeCamFrustum->fov / 2.0f + 1.0f) * 32768.0f / 180.0f + 0.5f;
|
|
|
|
|
|
|
|
hScreenEdge = -matrix[3][2] * sins(halfFov) / coss(halfFov);
|
|
|
|
// -matrix[3][2] is the depth, which gets multiplied by tan(halfFov) to get
|
|
|
|
// the amount of units between the center of the screen and the horizontal edge
|
|
|
|
// given the distance from the object to the camera.
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// This multiplication should really be performed on 4:3 as well,
|
|
|
|
// but the issue will be more apparent on widescreen.
|
2020-05-07 18:21:22 +00:00
|
|
|
hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) {
|
2019-08-25 04:46:40 +00:00
|
|
|
cullingRadius =
|
|
|
|
(f32)((struct GraphNodeCullingRadius *) geo)->cullingRadius; //! Why is there a f32 cast?
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
cullingRadius = 300;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
// Don't render if the object is close to or behind the camera
|
2019-09-01 19:50:50 +00:00
|
|
|
if (matrix[3][2] > -100.0f + cullingRadius) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
//! This makes the HOLP not update when the camera is far away, and it
|
|
|
|
// makes PU travel safe when the camera is locked on the main map.
|
|
|
|
// If Mario were rendered with a depth over 65536 it would cause overflow
|
|
|
|
// when converting the transformation matrix to a fixed point matrix.
|
2019-09-01 19:50:50 +00:00
|
|
|
if (matrix[3][2] < -20000.0f - cullingRadius) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
// Check whether the object is horizontally in view
|
2019-09-01 19:50:50 +00:00
|
|
|
if (matrix[3][0] > hScreenEdge + cullingRadius) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (matrix[3][0] < -hScreenEdge - cullingRadius) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
static void interpolate_matrix(Mat4 result, Mat4 a, Mat4 b) {
|
|
|
|
s32 i, j;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
|
|
result[i][j] = (a[i][j] + b[i][j]) / 2.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process an object node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_object(struct Object *node) {
|
2020-10-10 04:36:15 +00:00
|
|
|
struct Object* lastProcessingObject = gCurGraphNodeProcessingObject;
|
2021-09-05 21:17:20 +00:00
|
|
|
struct MarioState* lastMarioState = gCurGraphNodeMarioState;
|
2020-09-06 20:54:01 +00:00
|
|
|
gCurGraphNodeProcessingObject = node;
|
2019-08-25 04:46:40 +00:00
|
|
|
Mat4 mtxf;
|
|
|
|
s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0;
|
2020-07-29 01:28:12 +00:00
|
|
|
Vec3f scaleInterpolated;
|
2021-09-05 21:17:20 +00:00
|
|
|
if (node->header.gfx.node.flags & GRAPH_RENDER_PLAYER) {
|
|
|
|
gCurGraphNodeMarioState = NULL;
|
|
|
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
|
|
|
if (gMarioStates[i].marioObj == node) {
|
|
|
|
gCurGraphNodeMarioState = &gMarioStates[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (gCurGraphNodeMarioState != NULL) {
|
|
|
|
gCurGraphNodeMarioState->minimumBoneY = 999;
|
|
|
|
}
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
if (node->header.gfx.unk18 == gCurGraphNodeRoot->areaIndex) {
|
|
|
|
if (node->header.gfx.throwMatrix != NULL) {
|
2020-07-04 15:18:55 +00:00
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix,
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
if (gGlobalTimer == node->header.gfx.prevThrowMatrixTimestamp + 1 &&
|
2020-09-27 05:00:34 +00:00
|
|
|
gGlobalTimer != node->header.gfx.skipInterpolationTimestamp &&
|
|
|
|
gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
2020-07-29 01:28:12 +00:00
|
|
|
interpolate_matrix(mtxf, *node->header.gfx.throwMatrix, node->header.gfx.prevThrowMatrix);
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf,
|
|
|
|
gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
} else {
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix,
|
|
|
|
gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
}
|
|
|
|
mtxf_copy(node->header.gfx.prevThrowMatrix, *node->header.gfx.throwMatrix);
|
|
|
|
node->header.gfx.prevThrowMatrixTimestamp = gGlobalTimer;
|
2020-05-15 01:47:55 +00:00
|
|
|
} else if (node->header.gfx.node.flags & GRAPH_RENDER_CYLBOARD) {
|
2020-07-29 01:28:12 +00:00
|
|
|
Vec3f posInterpolated;
|
|
|
|
if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 &&
|
2020-09-27 05:00:34 +00:00
|
|
|
gGlobalTimer != node->header.gfx.skipInterpolationTimestamp &&
|
|
|
|
gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
2020-07-29 01:28:12 +00:00
|
|
|
interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(posInterpolated, node->header.gfx.pos);
|
|
|
|
}
|
|
|
|
vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos);
|
|
|
|
node->header.gfx.prevTimestamp = gGlobalTimer;
|
2020-05-15 01:47:55 +00:00
|
|
|
mtxf_cylboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
|
|
|
|
node->header.gfx.pos, gCurGraphNodeCamera->roll);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_cylboard(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex],
|
|
|
|
posInterpolated, gCurGraphNodeCamera->roll);
|
2020-04-03 18:57:26 +00:00
|
|
|
} else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) {
|
2020-07-29 01:28:12 +00:00
|
|
|
Vec3f posInterpolated;
|
|
|
|
if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 &&
|
2020-09-27 05:00:34 +00:00
|
|
|
gGlobalTimer != node->header.gfx.skipInterpolationTimestamp &&
|
|
|
|
gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
2020-07-29 01:28:12 +00:00
|
|
|
interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(posInterpolated, node->header.gfx.pos);
|
|
|
|
}
|
|
|
|
vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos);
|
|
|
|
node->header.gfx.prevTimestamp = gGlobalTimer;
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
|
|
|
|
node->header.gfx.pos, gCurGraphNodeCamera->roll);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_billboard(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex],
|
|
|
|
posInterpolated, gCurGraphNodeCamera->roll);
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
2020-07-29 01:28:12 +00:00
|
|
|
Vec3f posInterpolated;
|
|
|
|
Vec3s angleInterpolated;
|
|
|
|
if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 &&
|
2020-09-27 05:00:34 +00:00
|
|
|
gGlobalTimer != node->header.gfx.skipInterpolationTimestamp &&
|
|
|
|
gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
2020-07-29 01:28:12 +00:00
|
|
|
interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos);
|
|
|
|
interpolate_angles(angleInterpolated, node->header.gfx.prevAngle, node->header.gfx.angle);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(posInterpolated, node->header.gfx.pos);
|
|
|
|
vec3s_copy(angleInterpolated, node->header.gfx.angle);
|
|
|
|
}
|
|
|
|
vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos);
|
|
|
|
vec3s_copy(node->header.gfx.prevAngle, node->header.gfx.angle);
|
|
|
|
node->header.gfx.prevTimestamp = gGlobalTimer;
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle);
|
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_rotate_zxy_and_translate(mtxf, posInterpolated, angleInterpolated);
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gGlobalTimer == node->header.gfx.prevScaleTimestamp + 1 &&
|
2020-09-27 05:00:34 +00:00
|
|
|
gGlobalTimer != node->header.gfx.skipInterpolationTimestamp &&
|
|
|
|
gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) {
|
2020-07-29 01:28:12 +00:00
|
|
|
interpolate_vectors(scaleInterpolated, node->header.gfx.prevScale, node->header.gfx.scale);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(scaleInterpolated, node->header.gfx.scale);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
2020-07-29 01:28:12 +00:00
|
|
|
vec3f_copy(node->header.gfx.prevScale, node->header.gfx.scale);
|
|
|
|
node->header.gfx.prevScaleTimestamp = gGlobalTimer;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1],
|
|
|
|
node->header.gfx.scale);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1],
|
|
|
|
scaleInterpolated);
|
2020-07-04 15:18:55 +00:00
|
|
|
node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex];
|
2020-07-29 01:28:12 +00:00
|
|
|
node->header.gfx.throwMatrixInterpolated = &gMatStackInterpolated[gMatStackIndex];
|
2019-08-25 04:46:40 +00:00
|
|
|
node->header.gfx.cameraToObject[0] = gMatStack[gMatStackIndex][3][0];
|
|
|
|
node->header.gfx.cameraToObject[1] = gMatStack[gMatStackIndex][3][1];
|
|
|
|
node->header.gfx.cameraToObject[2] = gMatStack[gMatStackIndex][3][2];
|
|
|
|
|
|
|
|
// FIXME: correct types
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->header.gfx.unk38.curAnim != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_set_animation_globals(&node->header.gfx.unk38, hasAnimation);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) {
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
if (node->header.gfx.sharedChild != NULL) {
|
|
|
|
gCurGraphNodeObject = (struct GraphNodeObject *) node;
|
|
|
|
node->header.gfx.sharedChild->parent = &node->header.gfx.node;
|
|
|
|
geo_process_node_and_siblings(node->header.gfx.sharedChild);
|
|
|
|
node->header.gfx.sharedChild->parent = NULL;
|
|
|
|
gCurGraphNodeObject = NULL;
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->header.gfx.node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->header.gfx.node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2020-07-29 01:28:12 +00:00
|
|
|
} else {
|
|
|
|
node->header.gfx.prevThrowMatrixTimestamp = 0;
|
|
|
|
node->header.gfx.prevTimestamp = 0;
|
|
|
|
node->header.gfx.prevScaleTimestamp = 0;
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gMatStackIndex--;
|
|
|
|
gCurAnimType = ANIM_TYPE_NONE;
|
|
|
|
node->header.gfx.throwMatrix = NULL;
|
2020-07-29 01:28:12 +00:00
|
|
|
node->header.gfx.throwMatrixInterpolated = NULL;
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
2020-10-10 04:36:15 +00:00
|
|
|
gCurGraphNodeProcessingObject = lastProcessingObject;
|
2021-09-05 21:17:20 +00:00
|
|
|
gCurGraphNodeMarioState = lastMarioState;
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process an object parent node. Temporarily assigns itself as the parent of
|
|
|
|
* the subtree rooted at 'sharedChild' and processes the subtree, after which the
|
|
|
|
* actual children are be processed. (in practice they are null though)
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
static void geo_process_object_parent(struct GraphNodeObjectParent *node) {
|
|
|
|
if (node->sharedChild != NULL) {
|
|
|
|
node->sharedChild->parent = (struct GraphNode *) node;
|
|
|
|
geo_process_node_and_siblings(node->sharedChild);
|
|
|
|
node->sharedChild->parent = NULL;
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a held object node.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void geo_process_held_object(struct GraphNodeHeldObject *node) {
|
|
|
|
Mat4 mat;
|
|
|
|
Vec3f translation;
|
|
|
|
Mtx *mtx = alloc_display_list(sizeof(*mtx));
|
2020-07-29 01:28:12 +00:00
|
|
|
Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated));
|
|
|
|
Vec3f scaleInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
#ifdef F3DEX_GBI_2
|
|
|
|
gSPLookAt(gDisplayListHead++, &lookAt);
|
|
|
|
#endif
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.func != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-11-03 19:36:27 +00:00
|
|
|
if (node->objNode != NULL && node->objNode->header.gfx.sharedChild != NULL) {
|
|
|
|
s32 hasAnimation = (node->objNode->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
translation[0] = node->translation[0] / 4.0f;
|
|
|
|
translation[1] = node->translation[1] / 4.0f;
|
|
|
|
translation[2] = node->translation[2] / 4.0f;
|
|
|
|
|
2020-07-29 01:28:12 +00:00
|
|
|
if (gGlobalTimer == node->objNode->header.gfx.prevScaleTimestamp + 1) {
|
|
|
|
interpolate_vectors(scaleInterpolated, node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale);
|
|
|
|
} else {
|
|
|
|
vec3f_copy(scaleInterpolated, node->objNode->header.gfx.scale);
|
|
|
|
}
|
|
|
|
vec3f_copy(node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale);
|
|
|
|
node->objNode->header.gfx.prevScaleTimestamp = gGlobalTimer;
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
mtxf_translate(mat, translation);
|
2020-07-04 15:18:55 +00:00
|
|
|
mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix);
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStack[gMatStackIndex + 1][3][0] = gMatStack[gMatStackIndex][3][0];
|
|
|
|
gMatStack[gMatStackIndex + 1][3][1] = gMatStack[gMatStackIndex][3][1];
|
|
|
|
gMatStack[gMatStackIndex + 1][3][2] = gMatStack[gMatStackIndex][3][2];
|
|
|
|
mtxf_mul(gMatStack[gMatStackIndex + 1], mat, gMatStack[gMatStackIndex + 1]);
|
|
|
|
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1],
|
2019-11-03 19:36:27 +00:00
|
|
|
node->objNode->header.gfx.scale);
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_copy(gMatStackInterpolated[gMatStackIndex + 1], (void *) gCurGraphNodeObject->throwMatrixInterpolated);
|
|
|
|
gMatStackInterpolated[gMatStackIndex + 1][3][0] = gMatStackInterpolated[gMatStackIndex][3][0];
|
|
|
|
gMatStackInterpolated[gMatStackIndex + 1][3][1] = gMatStackInterpolated[gMatStackIndex][3][1];
|
|
|
|
gMatStackInterpolated[gMatStackIndex + 1][3][2] = gMatStackInterpolated[gMatStackIndex][3][2];
|
|
|
|
mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mat, gMatStackInterpolated[gMatStackIndex + 1]);
|
|
|
|
mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1],
|
|
|
|
scaleInterpolated);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.func != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node,
|
|
|
|
(struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex++;
|
|
|
|
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = mtx;
|
2020-07-29 01:28:12 +00:00
|
|
|
mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated;
|
2019-08-25 04:46:40 +00:00
|
|
|
gGeoTempState.type = gCurAnimType;
|
|
|
|
gGeoTempState.enabled = gCurAnimEnabled;
|
|
|
|
gGeoTempState.frame = gCurrAnimFrame;
|
|
|
|
gGeoTempState.translationMultiplier = gCurAnimTranslationMultiplier;
|
|
|
|
gGeoTempState.attribute = gCurrAnimAttribute;
|
|
|
|
gGeoTempState.data = gCurAnimData;
|
2020-07-29 01:28:12 +00:00
|
|
|
gGeoTempState.prevFrame = gPrevAnimFrame;
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurAnimType = 0;
|
|
|
|
gCurGraphNodeHeldObject = (void *) node;
|
2019-11-03 19:36:27 +00:00
|
|
|
if (node->objNode->header.gfx.unk38.curAnim != NULL) {
|
|
|
|
geo_set_animation_globals(&node->objNode->header.gfx.unk38, hasAnimation);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-11-03 19:36:27 +00:00
|
|
|
geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild);
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurGraphNodeHeldObject = NULL;
|
|
|
|
gCurAnimType = gGeoTempState.type;
|
|
|
|
gCurAnimEnabled = gGeoTempState.enabled;
|
|
|
|
gCurrAnimFrame = gGeoTempState.frame;
|
|
|
|
gCurAnimTranslationMultiplier = gGeoTempState.translationMultiplier;
|
|
|
|
gCurrAnimAttribute = gGeoTempState.attribute;
|
|
|
|
gCurAnimData = gGeoTempState.data;
|
2020-07-29 01:28:12 +00:00
|
|
|
gPrevAnimFrame = gGeoTempState.prevFrame;
|
2019-08-25 04:46:40 +00:00
|
|
|
gMatStackIndex--;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->fnNode.node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->fnNode.node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Processes the children of the given GraphNode if it has any
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void geo_try_process_children(struct GraphNode *node) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a generic geo node and its siblings.
|
|
|
|
* The first argument is the start node, and all its siblings will
|
|
|
|
* be iterated over.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void geo_process_node_and_siblings(struct GraphNode *firstNode) {
|
|
|
|
s16 iterateChildren = TRUE;
|
|
|
|
struct GraphNode *curGraphNode = firstNode;
|
|
|
|
struct GraphNode *parent = curGraphNode->parent;
|
|
|
|
|
|
|
|
// In the case of a switch node, exactly one of the children of the node is
|
|
|
|
// processed instead of all children like usual
|
2019-09-01 19:50:50 +00:00
|
|
|
if (parent != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
iterateChildren = (parent->type != GRAPH_NODE_TYPE_SWITCH_CASE);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2022-02-15 02:15:54 +00:00
|
|
|
// sanity check
|
|
|
|
if (firstNode == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
do {
|
|
|
|
if (curGraphNode->flags & GRAPH_RENDER_ACTIVE) {
|
|
|
|
if (curGraphNode->flags & GRAPH_RENDER_CHILDREN_FIRST) {
|
|
|
|
geo_try_process_children(curGraphNode);
|
|
|
|
} else {
|
|
|
|
switch (curGraphNode->type) {
|
|
|
|
case GRAPH_NODE_TYPE_ORTHO_PROJECTION:
|
|
|
|
geo_process_ortho_projection((struct GraphNodeOrthoProjection *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_PERSPECTIVE:
|
|
|
|
geo_process_perspective((struct GraphNodePerspective *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_MASTER_LIST:
|
|
|
|
geo_process_master_list((struct GraphNodeMasterList *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_LEVEL_OF_DETAIL:
|
|
|
|
geo_process_level_of_detail((struct GraphNodeLevelOfDetail *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_SWITCH_CASE:
|
|
|
|
geo_process_switch((struct GraphNodeSwitchCase *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_CAMERA:
|
|
|
|
geo_process_camera((struct GraphNodeCamera *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_TRANSLATION_ROTATION:
|
|
|
|
geo_process_translation_rotation(
|
|
|
|
(struct GraphNodeTranslationRotation *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_TRANSLATION:
|
|
|
|
geo_process_translation((struct GraphNodeTranslation *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_ROTATION:
|
|
|
|
geo_process_rotation((struct GraphNodeRotation *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_OBJECT:
|
|
|
|
geo_process_object((struct Object *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_ANIMATED_PART:
|
|
|
|
geo_process_animated_part((struct GraphNodeAnimatedPart *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_BILLBOARD:
|
|
|
|
geo_process_billboard((struct GraphNodeBillboard *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_DISPLAY_LIST:
|
|
|
|
geo_process_display_list((struct GraphNodeDisplayList *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_SCALE:
|
|
|
|
geo_process_scale((struct GraphNodeScale *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_SHADOW:
|
|
|
|
geo_process_shadow((struct GraphNodeShadow *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_OBJECT_PARENT:
|
|
|
|
geo_process_object_parent((struct GraphNodeObjectParent *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_GENERATED_LIST:
|
|
|
|
geo_process_generated_list((struct GraphNodeGenerated *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_BACKGROUND:
|
|
|
|
geo_process_background((struct GraphNodeBackground *) curGraphNode);
|
|
|
|
break;
|
|
|
|
case GRAPH_NODE_TYPE_HELD_OBJ:
|
|
|
|
geo_process_held_object((struct GraphNodeHeldObject *) curGraphNode);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
geo_try_process_children((struct GraphNode *) curGraphNode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (curGraphNode->type == GRAPH_NODE_TYPE_OBJECT) {
|
2019-08-25 04:46:40 +00:00
|
|
|
((struct GraphNodeObject *) curGraphNode)->throwMatrix = NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
} while (iterateChildren && (curGraphNode = curGraphNode->next) != firstNode);
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Process a root node. This is the entry point for processing the scene graph.
|
|
|
|
* The root node itself sets up the viewport, then all its children are processed
|
|
|
|
* to set up the projection and draw display lists.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) {
|
|
|
|
UNUSED s32 unused;
|
|
|
|
|
|
|
|
if (node->node.flags & GRAPH_RENDER_ACTIVE) {
|
|
|
|
Mtx *initialMatrix;
|
|
|
|
Vp *viewport = alloc_display_list(sizeof(*viewport));
|
2020-07-29 01:28:12 +00:00
|
|
|
Vp *viewportInterpolated = viewport;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool),
|
|
|
|
MEMORY_POOL_LEFT);
|
2019-08-25 04:46:40 +00:00
|
|
|
initialMatrix = alloc_display_list(sizeof(*initialMatrix));
|
|
|
|
gMatStackIndex = 0;
|
|
|
|
gCurAnimType = 0;
|
|
|
|
vec3s_set(viewport->vp.vtrans, node->x * 4, node->y * 4, 511);
|
|
|
|
vec3s_set(viewport->vp.vscale, node->width * 4, node->height * 4, 511);
|
|
|
|
if (b != NULL) {
|
|
|
|
clear_frame_buffer(clearColor);
|
2020-07-29 01:28:12 +00:00
|
|
|
viewportInterpolated = alloc_display_list(sizeof(*viewportInterpolated));
|
|
|
|
interpolate_vectors_s16(viewportInterpolated->vp.vtrans, sPrevViewport.vp.vtrans, b->vp.vtrans);
|
|
|
|
interpolate_vectors_s16(viewportInterpolated->vp.vscale, sPrevViewport.vp.vscale, b->vp.vscale);
|
|
|
|
|
|
|
|
sViewportPos = gDisplayListHead;
|
|
|
|
make_viewport_clip_rect(viewportInterpolated);
|
2019-08-25 04:46:40 +00:00
|
|
|
*viewport = *b;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (c != NULL) {
|
|
|
|
clear_frame_buffer(clearColor);
|
|
|
|
make_viewport_clip_rect(c);
|
|
|
|
}
|
2020-07-29 01:28:12 +00:00
|
|
|
sPrevViewport = *viewport;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
mtxf_identity(gMatStack[gMatStackIndex]);
|
|
|
|
mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]);
|
|
|
|
gMatStackFixed[gMatStackIndex] = initialMatrix;
|
2020-07-29 01:28:12 +00:00
|
|
|
|
|
|
|
mtxf_identity(gMatStackInterpolated[gMatStackIndex]);
|
|
|
|
gMatStackInterpolatedFixed[gMatStackIndex] = initialMatrix;
|
|
|
|
|
|
|
|
gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewportInterpolated));
|
2019-08-25 04:46:40 +00:00
|
|
|
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]),
|
2019-11-03 19:36:27 +00:00
|
|
|
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurGraphNodeRoot = node;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (node->node.children != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
geo_process_node_and_siblings(node->node.children);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gCurGraphNodeRoot = NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gShowDebugText) {
|
2019-08-25 04:46:40 +00:00
|
|
|
print_text_fmt_int(180, 36, "MEM %d",
|
|
|
|
gDisplayListHeap->totalSpace - gDisplayListHeap->usedSpace);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
main_pool_free(gDisplayListHeap);
|
|
|
|
}
|
|
|
|
}
|