Files
Microtransactions64/src/game/rendering_graph_node.c

1355 lines
56 KiB
C
Raw Normal View History

2020-06-02 12:44:34 -04:00
#include <PR/ultratypes.h>
2019-08-25 00:46:40 -04:00
#include "area.h"
2020-06-02 12:44:34 -04:00
#include "engine/math_util.h"
2020-04-03 14:57:26 -04:00
#include "game_init.h"
2020-06-02 12:44:34 -04:00
#include "gfx_dimensions.h"
#include "main.h"
#include "memory.h"
#include "print.h"
2019-08-25 00:46:40 -04:00
#include "rendering_graph_node.h"
2020-06-02 12:44:34 -04:00
#include "shadow.h"
#include "sm64.h"
#include "game_init.h"
#include "engine/extended_bounds.h"
2021-08-15 11:30:19 +01:00
#include "puppyprint.h"
#include "debug_box.h"
#include "level_update.h"
#include "config.h"
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*
*/
s16 gMatStackIndex;
Mat4 gMatStack[32];
Mtx *gMatStackFixed[32];
2021-05-09 22:34:22 +01:00
f32 aspect;
2021-08-25 19:01:54 +01:00
f32 gWorldScale = 1.0f;
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
struct GeoAnimState {
/*0x00*/ u8 type;
/*0x01*/ u8 enabled;
/*0x02*/ s16 frame;
/*0x04*/ f32 translationMultiplier;
/*0x08*/ u16 *attribute;
/*0x0C*/ s16 *data;
};
// 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;
f32 gCurAnimTranslationMultiplier;
u16 *gCurrAnimAttribute;
s16 *gCurAnimData;
struct AllocOnlyPool *gDisplayListHeap;
struct RenderModeContainer {
2021-09-18 15:55:57 -07:00
u32 modes[GFX_NUM_MASTER_LISTS];
2019-08-25 00:46:40 -04:00
};
2021-09-18 15:55:57 -07:00
#if SILHOUETTE
/* Rendermode settings for cycle 1 for all 13 layers. */
struct RenderModeContainer renderModeTable_1Cycle[2] = { { {
G_RM_OPA_SURF, // LAYER_FORCE
G_RM_AA_OPA_SURF, // LAYER_OPAQUE
G_RM_AA_OPA_SURF, // LAYER_OPAQUE_INTER
G_RM_AA_OPA_SURF, // LAYER_OPAQUE_DECAL
G_RM_AA_TEX_EDGE, // LAYER_ALPHA
G_RM_AA_TEX_EDGE | ZMODE_DEC, // LAYER_ALPHA_DECAL
G_RM_AA_OPA_SURF, // LAYER_SILHOUETTE_OPAQUE
G_RM_AA_TEX_EDGE, // LAYER_SILHOUETTE_ALPHA
G_RM_AA_OPA_SURF, // LAYER_OCCLUDE_SILHOUETTE_OPAQUE
G_RM_AA_TEX_EDGE, // LAYER_OCCLUDE_SILHOUETTE_ALPHA
G_RM_AA_XLU_SURF, // LAYER_TRANSPARENT_DECAL
G_RM_AA_XLU_SURF, // LAYER_TRANSPARENT
G_RM_AA_XLU_SURF, // LAYER_TRANSPARENT_INTER
} },
{ {
/* z-buffered */
G_RM_ZB_OPA_SURF, // LAYER_FORCE
G_RM_AA_ZB_OPA_SURF, // LAYER_OPAQUE
G_RM_AA_ZB_OPA_INTER, // LAYER_OPAQUE_INTER
G_RM_AA_ZB_OPA_DECAL, // LAYER_OPAQUE_DECAL
G_RM_AA_ZB_TEX_EDGE, // LAYER_ALPHA
G_RM_AA_ZB_TEX_EDGE | ZMODE_DEC, // LAYER_ALPHA_DECAL
G_RM_AA_ZB_OPA_SURF, // LAYER_SILHOUETTE_OPAQUE
G_RM_AA_ZB_TEX_EDGE, // LAYER_SILHOUETTE_ALPHA
G_RM_AA_ZB_OPA_SURF, // LAYER_OCCLUDE_SILHOUETTE_OPAQUE
G_RM_AA_ZB_TEX_EDGE, // LAYER_OCCLUDE_SILHOUETTE_ALPHA
G_RM_AA_ZB_XLU_DECAL, // LAYER_TRANSPARENT_DECAL
G_RM_AA_ZB_XLU_SURF, // LAYER_TRANSPARENT
G_RM_AA_ZB_XLU_INTER, // LAYER_TRANSPARENT_INTER
} } };
/* Rendermode settings for cycle 2 for all 13 layers. */
struct RenderModeContainer renderModeTable_2Cycle[2] = { { {
G_RM_OPA_SURF2, // LAYER_FORCE
G_RM_AA_OPA_SURF2, // LAYER_OPAQUE
G_RM_AA_OPA_SURF2, // LAYER_OPAQUE_INTER
G_RM_AA_OPA_SURF2, // LAYER_OPAQUE_DECAL
G_RM_AA_TEX_EDGE2, // LAYER_ALPHA
G_RM_AA_TEX_EDGE2 | ZMODE_DEC, // LAYER_ALPHA_DECAL
G_RM_AA_OPA_SURF2, // LAYER_SILHOUETTE_OPAQUE
G_RM_AA_TEX_EDGE2, // LAYER_SILHOUETTE_ALPHA
G_RM_AA_OPA_SURF2, // LAYER_OCCLUDE_SILHOUETTE_OPAQUE
G_RM_AA_TEX_EDGE2, // LAYER_OCCLUDE_SILHOUETTE_ALPHA
G_RM_AA_XLU_SURF2, // LAYER_TRANSPARENT_DECAL
G_RM_AA_XLU_SURF2, // LAYER_TRANSPARENT
G_RM_AA_XLU_SURF2, // LAYER_TRANSPARENT_INTER
} },
{ {
/* z-buffered */
G_RM_ZB_OPA_SURF2, // LAYER_FORCE
G_RM_AA_ZB_OPA_SURF2, // LAYER_OPAQUE
G_RM_AA_ZB_OPA_INTER2, // LAYER_OPAQUE_INTER
G_RM_AA_ZB_OPA_DECAL2, // LAYER_OPAQUE_DECAL
G_RM_AA_ZB_TEX_EDGE2, // LAYER_ALPHA
G_RM_AA_ZB_TEX_EDGE2 | ZMODE_DEC, // LAYER_ALPHA_DECAL
G_RM_AA_ZB_OPA_SURF2, // LAYER_SILHOUETTE_OPAQUE
G_RM_AA_ZB_TEX_EDGE2, // LAYER_SILHOUETTE_ALPHA
G_RM_AA_ZB_OPA_SURF2, // LAYER_OCCLUDE_SILHOUETTE_OPAQUE
G_RM_AA_ZB_TEX_EDGE2, // LAYER_OCCLUDE_SILHOUETTE_ALPHA
G_RM_AA_ZB_XLU_DECAL2, // LAYER_TRANSPARENT_DECAL
G_RM_AA_ZB_XLU_SURF2, // LAYER_TRANSPARENT
G_RM_AA_ZB_XLU_INTER2, // LAYER_TRANSPARENT_INTER
} } };
#else
2019-11-03 14:36:27 -05:00
/* Rendermode settings for cycle 1 for all 8 layers. */
2019-08-25 00:46:40 -04:00
struct RenderModeContainer renderModeTable_1Cycle[2] = { { {
2019-11-03 14:36:27 -05: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 00:46:40 -04:00
struct RenderModeContainer renderModeTable_2Cycle[2] = { { {
2019-11-03 14:36:27 -05: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,
} } };
2021-09-18 15:55:57 -07:00
#endif
2019-08-25 00:46:40 -04:00
2021-09-20 16:54:52 -07:00
struct GraphNodeRoot *gCurGraphNodeRoot = NULL;
struct GraphNodeMasterList *gCurGraphNodeMasterList = NULL;
2019-08-25 00:46:40 -04:00
struct GraphNodePerspective *gCurGraphNodeCamFrustum = NULL;
2021-09-20 16:54:52 -07:00
struct GraphNodeCamera *gCurGraphNodeCamera = NULL;
struct GraphNodeObject *gCurGraphNodeObject = NULL;
struct GraphNodeHeldObject *gCurGraphNodeHeldObject = NULL;
2019-08-25 00:46:40 -04:00
u16 gAreaUpdateCounter = 0;
#ifdef F3DEX_GBI_2
LookAt lookAt;
#endif
2021-09-18 15:55:57 -07:00
#if SILHOUETTE
#define SIL_CVG_THRESHOLD 0x3F // 32..255, 63 seems to give best results
#define SCHWA (AA_EN | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | CVG_X_ALPHA | FORCE_BL)
2021-09-18 15:55:57 -07:00
#define SET_SILHOUETTE_F3D(gfx) { \
gDPSetRenderMode( (gfx)++, (SCHWA | GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1MA)), \
(SCHWA | GBL_c2(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1MA))); \
gSPSetGeometryMode((gfx)++, G_FOG); /* Enable fog */ \
gSPFogPosition( (gfx)++, 0, 1 ); /* Fox position */ \
gDPSetFogColor( (gfx)++, 0, 0, 0, SILHOUETTE ); /* silhouette color & alpha */ \
gDPSetEnvColor( (gfx)++, 0, 0, 0, SIL_CVG_THRESHOLD); /* silhouette env transparency */ \
}
#define CLEAR_SILHOUETTE_F3D(gfx, i) { \
gSPClearGeometryMode((gfx)++, G_FOG ); /* Disable fog */ \
gDPSetEnvColor( (gfx)++, 255, 255, 255, 255); /* Reset env color & alpha */ \
gDPSetRenderMode( (gfx)++, (mode1List->modes[(i)] & ~IM_RD), \
(mode2List->modes[(i)] & ~IM_RD)); /* Use normal mode list, no AA */ \
}
#endif
2021-09-15 15:05:44 +01:00
u8 ucodeTestSwitch = 1;
void reset_clipping(void)
{
if (gMarioState->action == ACT_CREDITS_CUTSCENE)
make_viewport_clip_rect(&sEndCutsceneVp);
else
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, gBorderHeight, SCREEN_WIDTH, SCREEN_HEIGHT - gBorderHeight);
}
2019-09-01 15:50:50 -04:00
/**
2021-09-14 22:32:21 +01:00
* Process a master list node. This has been modified, so now it runs twice, for each microcode.
2021-09-14 22:41:47 +01:00
It iterates through the first 5 layers of if the first index using F3DLX2.Rej, then it switches
to F3DZEX and iterates through all layers, then switches back to F3DLX2.Rej and finishes the last
2021-09-14 22:32:21 +01:00
3. It does this, because layers 5-7 are non zbuffered, and just doing 0-7 of ZEX, then 0-7 of REJ
2021-09-14 22:41:47 +01:00
would make the ZEX 0-4 render on top of Rej's 5-7.
2019-08-25 00:46:40 -04:00
*/
static void geo_process_master_list_sub(struct GraphNodeMasterList *node) {
struct DisplayListNode *currList;
2021-09-18 15:55:57 -07:00
s32 startLayer, endLayer, currLayer = LAYER_FORCE;
s32 headsIndex = LIST_HEADS_REJ;
s32 renderPhase = RENDER_PHASE_REJ_ZB;
2019-08-25 00:46:40 -04:00
s32 enableZBuffer = (node->node.flags & GRAPH_RENDER_Z_BUFFER) != 0;
2021-09-18 15:55:57 -07:00
struct RenderModeContainer *mode1List = &renderModeTable_1Cycle[enableZBuffer];
2019-08-25 00:46:40 -04:00
struct RenderModeContainer *mode2List = &renderModeTable_2Cycle[enableZBuffer];
2021-09-18 15:02:53 -07: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.
#ifdef F3DEX_GBI_2
Mtx lMtx;
guLookAtReflect(&lMtx, &lookAt, 0, 0, 0, /* eye */ 0, 0, 1, /* at */ 1, 0, 0 /* up */);
#endif
2021-09-15 15:05:44 +01:00
//if (gPlayer1Controller->buttonPressed & L_TRIG)
// ucodeTestSwitch ^= 1;
//print_text_fmt_int(32,32,"%d",ucodeTestSwitch);
#ifdef F3DZEX_GBI_2
2021-09-14 22:32:21 +01:00
loopBegin:
2021-09-18 15:55:57 -07:00
switch (renderPhase) {
#if SILHOUETTE
case RENDER_PHASE_REJ_ZB: headsIndex = LIST_HEADS_REJ; startLayer = LAYER_FORCE; endLayer = LAYER_LAST_BEFORE_SILHOUETTE; break;
case RENDER_PHASE_ZEX_BEFORE_SILHOUETTE: headsIndex = LIST_HEADS_ZEX; startLayer = LAYER_FORCE; endLayer = LAYER_LAST_BEFORE_SILHOUETTE; break;
case RENDER_PHASE_REJ_SILHOUETTE: headsIndex = LIST_HEADS_REJ; startLayer = LAYER_SILHOUETTE_FIRST; endLayer = LAYER_SILHOUETTE_LAST; break;
case RENDER_PHASE_REJ_NON_SILHOUETTE: headsIndex = LIST_HEADS_REJ; startLayer = LAYER_SILHOUETTE_FIRST; endLayer = LAYER_SILHOUETTE_LAST; break;
case RENDER_PHASE_REJ_OCCLUDE_SILHOUETTE: headsIndex = LIST_HEADS_REJ; startLayer = LAYER_OCCLUDE_SILHOUETTE_FIRST; endLayer = LAYER_OCCLUDE_SILHOUETTE_LAST; break;
case RENDER_PHASE_ZEX_AFTER_SILHOUETTE: headsIndex = LIST_HEADS_ZEX; startLayer = LAYER_OCCLUDE_SILHOUETTE_FIRST; endLayer = LAYER_LAST_ALL; break;
case RENDER_PHASE_REJ_NON_ZB: headsIndex = LIST_HEADS_REJ; startLayer = LAYER_FIRST_NON_ZB; endLayer = LAYER_LAST_ALL; break;
#else
case RENDER_PHASE_REJ_ZB: headsIndex = LIST_HEADS_REJ; startLayer = LAYER_FORCE; endLayer = LAYER_ZB_LAST; break;
case RENDER_PHASE_ZEX_ALL: headsIndex = LIST_HEADS_ZEX; startLayer = LAYER_FORCE; endLayer = LAYER_LAST_ALL; break;
case RENDER_PHASE_REJ_NON_ZB: headsIndex = LIST_HEADS_REJ; startLayer = LAYER_FIRST_NON_ZB; endLayer = LAYER_LAST_ALL; break;
#endif
}
// Load rejection on pass 2. ZEX is loaded afterwards.
if (headsIndex == LIST_HEADS_REJ) {
2021-09-17 17:32:11 -07:00
if (gIsConsole) {
gSPLoadUcodeL(gDisplayListHead++, gspF3DLX2_Rej_fifo);
} else {
gSPLoadUcodeL(gDisplayListHead++, gspF3DEX2_Rej_fifo);
}
2021-09-16 23:14:53 +01:00
init_rcp(KEEP_ZBUFFER);
2021-09-14 22:41:47 +01:00
gSPClipRatio(gDisplayListHead++, FRUSTRATIO_2);
2021-09-18 15:55:57 -07:00
} else {
2021-09-14 22:41:47 +01:00
gSPLoadUcodeL(gDisplayListHead++, gspF3DZEX2_PosLight_fifo);
2021-09-16 23:14:53 +01:00
init_rcp(KEEP_ZBUFFER);
2021-09-14 22:41:47 +01:00
gSPClipRatio(gDisplayListHead++, FRUSTRATIO_1);
}
2021-09-18 15:02:53 -07:00
gSPLookAt(gDisplayListHead++, &lookAt);
reset_clipping();
2021-09-18 15:02:53 -07:00
#endif
2021-09-18 15:55:57 -07:00
if (enableZBuffer) {
2021-09-14 22:41:47 +01:00
gDPPipeSync(gDisplayListHead++);
gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER);
}
2021-09-18 15:55:57 -07:00
for (currLayer = startLayer; currLayer <= endLayer; currLayer++) {
currList = node->listHeads[headsIndex][currLayer];
while (currList != NULL) {
#if SILHOUETTE
if (renderPhase == RENDER_PHASE_REJ_SILHOUETTE) {
SET_SILHOUETTE_F3D(gDisplayListHead);
} else if (renderPhase == RENDER_PHASE_REJ_NON_SILHOUETTE) {
CLEAR_SILHOUETTE_F3D(gDisplayListHead, currLayer);
} else {
#endif
2021-09-18 15:55:57 -07:00
gDPSetRenderMode(gDisplayListHead++, mode1List->modes[currLayer], mode2List->modes[currLayer]);
#if SILHOUETTE
2019-08-25 00:46:40 -04:00
}
2021-09-18 15:55:57 -07:00
#endif
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transform), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
gSPDisplayList(gDisplayListHead++, currList->displayList);
currList = currList->next;
2019-08-25 00:46:40 -04:00
}
2021-09-14 22:32:21 +01:00
}
2021-09-18 15:55:57 -07:00
if (renderPhase < RENDER_PHASE_LAST) {
renderPhase++;
goto loopBegin;
2019-08-25 00:46:40 -04:00
}
2021-09-18 15:55:57 -07:00
#ifdef F3DZEX_GBI_2
if (enableZBuffer) {
2019-08-25 00:46:40 -04:00
gDPPipeSync(gDisplayListHead++);
gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER);
}
2021-09-17 14:28:24 +01:00
#ifdef VISUAL_DEBUG
2021-09-18 15:55:57 -07:00
if (hitboxView) {
2021-09-17 14:28:24 +01:00
render_debug_boxes(DEBUG_UCODE_REJ);
2021-09-18 15:55:57 -07:00
}
2021-09-17 14:28:24 +01:00
#endif
gSPLoadUcodeL(gDisplayListHead++, gspF3DZEX2_PosLight_fifo);
init_rcp(KEEP_ZBUFFER);
gSPClipRatio(gDisplayListHead++, FRUSTRATIO_1);
reset_clipping();
2021-09-17 14:28:24 +01:00
#endif
#ifdef VISUAL_DEBUG
2021-09-18 15:55:57 -07:00
if (hitboxView) {
2021-09-17 14:28:24 +01:00
render_debug_boxes(DEBUG_UCODE_DEFAULT | DEBUG_BOX_CLEAR);
2021-09-18 15:55:57 -07:00
}
if (surfaceView) {
2021-09-17 14:28:24 +01:00
visual_surface_loop();
2021-09-18 15:55:57 -07:00
}
2021-09-17 14:28:24 +01:00
#endif
2019-08-25 00:46:40 -04:00
}
2021-09-18 15:55:57 -07:00
#if SILHOUETTE
#undef SIL_CVG_THRESHOLD
#undef SCHWA
2021-09-21 12:25:56 -07:00
#undef SET_SILHOUETTE_F3D
#undef CLEAR_SILHOUETTE_F3D
2021-09-18 15:55:57 -07:00
#endif
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
2021-09-20 16:54:52 -07:00
static void geo_append_display_list(void *displayList, s32 layer) {
s32 index = 0;
2019-08-25 00:46:40 -04:00
#ifdef F3DEX_GBI_2
gSPLookAt(gDisplayListHead++, &lookAt);
#endif
2021-09-21 12:25:56 -07:00
#if defined(F3DZEX_GBI_2) || (SILHOUETTE > 0)
if (gCurGraphNodeObject != NULL) {
#ifdef F3DZEX_GBI_2
2021-09-21 12:25:56 -07:00
if (gCurGraphNodeObject->node.flags & GRAPH_RENDER_UCODE_REJ && ucodeTestSwitch) {
index = 1;
2021-09-21 12:25:56 -07:00
}
#endif
#if SILHOUETTE
if (gCurGraphNodeObject->node.flags & GRAPH_RENDER_SILHOUETTE) {
switch (layer) {
case LAYER_OPAQUE: layer = LAYER_SILHOUETTE_OPAQUE; break;
case LAYER_ALPHA: layer = LAYER_SILHOUETTE_ALPHA; break;
}
}
#endif
}
#endif
2021-09-20 16:54:52 -07:00
if (gCurGraphNodeMasterList != 0) {
struct DisplayListNode *listNode = alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode));
2019-08-25 00:46:40 -04:00
listNode->transform = gMatStackFixed[gMatStackIndex];
listNode->displayList = displayList;
listNode->next = 0;
2021-09-20 16:54:52 -07:00
if (gCurGraphNodeMasterList->listHeads[index][layer] == 0) {
gCurGraphNodeMasterList->listHeads[index][layer] = listNode;
2021-09-20 16:54:52 -07:00
} else {
gCurGraphNodeMasterList->listTails[index][layer]->next = listNode;
2019-09-01 15:50:50 -04:00
}
gCurGraphNodeMasterList->listTails[index][layer] = listNode;
2019-08-25 00:46:40 -04:00
}
}
2019-09-01 15:50:50 -04:00
/**
* Process the master list node.
2019-08-25 00:46:40 -04:00
*/
static void geo_process_master_list(struct GraphNodeMasterList *node) {
s32 i;
2021-09-20 16:54:52 -07:00
if (gCurGraphNodeMasterList == NULL && node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
gCurGraphNodeMasterList = node;
2021-09-20 16:54:52 -07:00
for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) {
2021-09-21 12:25:56 -07:00
node->listHeads[LIST_HEADS_ZEX][i] = NULL;
node->listHeads[LIST_HEADS_REJ][i] = NULL;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
geo_process_master_list_sub(gCurGraphNodeMasterList);
2019-08-25 00:46:40 -04:00
gCurGraphNodeMasterList = NULL;
}
}
2019-09-01 15:50:50 -04:00
/**
* Process an orthographic projection node.
2019-08-25 00:46:40 -04: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 14:36:27 -05:00
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
}
}
2019-09-01 15:50:50 -04:00
/**
* Process a perspective projection node.
2019-08-25 00:46:40 -04:00
*/
static void geo_process_perspective(struct GraphNodePerspective *node) {
2019-09-01 15:50:50 -04:00
if (node->fnNode.func != NULL) {
2019-08-25 00:46:40 -04:00
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
if (node->fnNode.node.children != NULL) {
u16 perspNorm;
Mtx *mtx = alloc_display_list(sizeof(*mtx));
#ifdef WIDE
2021-09-10 14:18:23 +01:00
if (gConfig.widescreen && (gCurrLevelNum != 0x01)){
2021-09-20 16:54:52 -07:00
aspect = (16.0f / 9.0f); // 1.775f
} else {
2021-09-20 16:54:52 -07:00
aspect = (4.0f / 3.0f); // 1.33333f
2021-05-09 22:34:22 +01:00
}
#else
2021-09-20 16:54:52 -07:00
aspect = (4.0f / 3.0f); // 1.33333f
#endif
2019-08-25 00:46:40 -04:00
2021-09-20 16:54:52 -07:00
if (gCamera) {
gWorldScale = ((sqr(gCamera->pos[0]) + sqr(gCamera->pos[1]) + sqr(gCamera->pos[2])) / sqr(0x2000));
gWorldScale = MAX(gWorldScale, 1.0f);
} else {
2021-08-25 19:01:54 +01:00
gWorldScale = 1.0f;
2021-09-20 16:54:52 -07:00
}
2021-08-25 19:01:54 +01:00
guPerspective(mtx, &perspNorm, node->fov, aspect, (node->far/300) / gWorldScale, node->far / gWorldScale, 1.0f);
2019-08-25 00:46:40 -04:00
gSPPerspNormalize(gDisplayListHead++, perspNorm);
2019-11-03 14:36:27 -05:00
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
2019-08-25 00:46:40 -04:00
gCurGraphNodeCamFrustum = node;
geo_process_node_and_siblings(node->fnNode.node.children);
gCurGraphNodeCamFrustum = NULL;
}
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) {
f32 distanceFromCam;
#ifdef AUTO_LOD
if (gIsConsole) {
distanceFromCam = -gMatStack[gMatStackIndex][3][2];
} else {
distanceFromCam = 50;
}
#else
distanceFromCam = -gMatStack[gMatStackIndex][3][2];
#endif
2021-08-15 17:26:38 +01:00
if ((f32)node->minDistance <= distanceFromCam && distanceFromCam < (f32)node->maxDistance) {
2019-09-01 15:50:50 -04:00
if (node->node.children != 0) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_switch(struct GraphNodeSwitchCase *node) {
struct GraphNode *selectedChild = node->fnNode.node.children;
s32 i;
2019-09-01 15:50:50 -04:00
if (node->fnNode.func != NULL) {
2019-08-25 00:46:40 -04:00
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
2019-09-01 15:50:50 -04:00
}
for (i = 0; selectedChild != NULL && node->selectedCase > i; i++) {
2019-08-25 00:46:40 -04:00
selectedChild = selectedChild->next;
2019-09-01 15:50:50 -04:00
}
if (selectedChild != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(selectedChild);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
static void make_roll_matrix(Mtx *mtx, s32 angle) {
Mat4 temp;
mtxf_identity(temp);
temp[0][0] = coss(angle);
temp[0][1] = sins(angle);
temp[1][0] = -temp[0][1];
temp[1][1] = temp[0][0];
guMtxF2L(temp, mtx);
}
2019-09-01 15:50:50 -04:00
/**
* Process a camera node.
2019-08-25 00:46:40 -04: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));
2019-09-01 15:50:50 -04:00
if (node->fnNode.func != NULL) {
2019-08-25 00:46:40 -04:00
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
2019-09-01 15:50:50 -04:00
}
make_roll_matrix(rollMtx, node->rollScreen);
2019-08-25 00:46:40 -04:00
2019-11-03 14:36:27 -05:00
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(rollMtx), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH);
2019-08-25 00:46:40 -04:00
2020-01-03 10:38:57 -05:00
mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll);
2019-08-25 00:46:40 -04:00
mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]);
gMatStackIndex++;
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
if (node->fnNode.node.children != 0) {
gCurGraphNodeCamera = node;
2020-07-04 11:18:55 -04:00
node->matrixPtr = &gMatStack[gMatStackIndex];
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->fnNode.node.children);
gCurGraphNodeCamera = NULL;
}
gMatStackIndex--;
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_translation_rotation(struct GraphNodeTranslationRotation *node) {
Mat4 mtxf;
Vec3f translation;
Mtx *mtx = alloc_display_list(sizeof(*mtx));
2021-09-23 18:56:14 -07:00
vec3_copy(translation, node->translation);
2019-08-25 00:46:40 -04:00
mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
gMatStackIndex++;
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
2019-09-01 15:50:50 -04:00
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2019-09-01 15:50:50 -04:00
}
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gMatStackIndex--;
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_translation(struct GraphNodeTranslation *node) {
Mat4 mtxf;
Vec3f translation;
Mtx *mtx = alloc_display_list(sizeof(*mtx));
2021-09-23 18:56:14 -07:00
vec3_copy(translation, node->translation);
2019-08-25 00:46:40 -04:00
mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
gMatStackIndex++;
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
2019-09-01 15:50:50 -04:00
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2019-09-01 15:50:50 -04:00
}
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gMatStackIndex--;
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_rotation(struct GraphNodeRotation *node) {
Mat4 mtxf;
Mtx *mtx = alloc_display_list(sizeof(*mtx));
mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
gMatStackIndex++;
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
2019-09-01 15:50:50 -04:00
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2019-09-01 15:50:50 -04:00
}
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gMatStackIndex--;
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_scale(struct GraphNodeScale *node) {
UNUSED Mat4 transform;
Vec3f scaleVec;
Mtx *mtx = alloc_display_list(sizeof(*mtx));
vec3f_set(scaleVec, node->scale, node->scale, node->scale);
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec);
gMatStackIndex++;
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
2019-09-01 15:50:50 -04:00
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2019-09-01 15:50:50 -04:00
}
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gMatStackIndex--;
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_billboard(struct GraphNodeBillboard *node) {
Vec3f translation;
Mtx *mtx = alloc_display_list(sizeof(*mtx));
gMatStackIndex++;
2021-09-23 18:56:14 -07:00
vec3_copy(translation, node->translation);
2019-08-25 00:46:40 -04:00
mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation,
gCurGraphNodeCamera->roll);
2019-09-01 15:50:50 -04:00
if (gCurGraphNodeHeldObject != NULL) {
2019-08-25 00:46:40 -04:00
mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex],
2019-11-03 14:36:27 -05:00
gCurGraphNodeHeldObject->objNode->header.gfx.scale);
2019-09-01 15:50:50 -04:00
} else if (gCurGraphNodeObject != NULL) {
2019-08-25 00:46:40 -04:00
mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex],
gCurGraphNodeObject->scale);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
2019-09-01 15:50:50 -04:00
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2019-09-01 15:50:50 -04:00
}
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gMatStackIndex--;
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_display_list(struct GraphNodeDisplayList *node) {
2019-09-01 15:50:50 -04:00
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2019-09-01 15:50:50 -04:00
}
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_generated_list(struct GraphNodeGenerated *node) {
if (node->fnNode.func != NULL) {
2019-11-03 14:36:27 -05:00
Gfx *list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node,
2019-08-25 00:46:40 -04:00
(struct AllocOnlyPool *) gMatStack[gMatStackIndex]);
2020-09-20 11:15:47 -04:00
if (list != NULL) {
geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), GET_GRAPH_NODE_LAYER(node->fnNode.node.flags));
2019-08-25 00:46:40 -04:00
}
}
2019-09-01 15:50:50 -04:00
if (node->fnNode.node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->fnNode.node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_background(struct GraphNodeBackground *node) {
2019-11-03 14:36:27 -05:00
Gfx *list = NULL;
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04:00
if (node->fnNode.func != NULL) {
2019-08-25 00:46:40 -04:00
list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node,
(struct AllocOnlyPool *) gMatStack[gMatStackIndex]);
2019-09-01 15:50:50 -04:00
}
2020-09-20 11:15:47 -04:00
if (list != NULL) {
geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), GET_GRAPH_NODE_LAYER(node->fnNode.node.flags));
2019-08-25 00:46:40 -04:00
} else if (gCurGraphNodeMasterList != NULL) {
2020-06-02 12:44:34 -04:00
#ifndef F3DEX_GBI_2E
2019-08-25 00:46:40 -04:00
Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7);
2020-06-02 12:44:34 -04:00
#else
Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 8);
#endif
2019-08-25 00:46:40 -04:00
Gfx *gfx = gfxStart;
gDPPipeSync(gfx++);
gDPSetCycleType(gfx++, G_CYC_FILL);
gDPSetFillColor(gfx++, node->background);
2021-07-12 17:10:44 +01:00
gDPFillRectangle(gfx++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), gBorderHeight,
GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - gBorderHeight - 1);
2019-08-25 00:46:40 -04:00
gDPPipeSync(gfx++);
gDPSetCycleType(gfx++, G_CYC_1CYCLE);
gSPEndDisplayList(gfx++);
geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(gfxStart), LAYER_FORCE);
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
if (node->fnNode.node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->fnNode.node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04: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.
2019-08-25 00:46:40 -04:00
*/
static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
Mat4 matrix;
Vec3s rotation;
Vec3f translation;
Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr));
vec3s_copy(rotation, gVec3sZero);
vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]);
if (gCurAnimType == ANIM_TYPE_TRANSLATION) {
translation[0] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
translation[1] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
translation[2] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurAnimType = ANIM_TYPE_ROTATION;
} else {
if (gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) {
translation[0] +=
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurrAnimAttribute += 2;
translation[2] +=
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurAnimType = ANIM_TYPE_ROTATION;
} else {
if (gCurAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) {
gCurrAnimAttribute += 2;
translation[1] +=
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurrAnimAttribute += 2;
gCurAnimType = ANIM_TYPE_ROTATION;
} else if (gCurAnimType == ANIM_TYPE_NO_TRANSLATION) {
gCurrAnimAttribute += 6;
gCurAnimType = ANIM_TYPE_ROTATION;
}
}
}
if (gCurAnimType == ANIM_TYPE_ROTATION) {
rotation[0] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
rotation[1] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
rotation[2] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
}
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
gMatStackIndex++;
mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = matrixPtr;
2019-09-01 15:50:50 -04:00
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2019-09-01 15:50:50 -04:00
}
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gMatStackIndex--;
}
2021-09-20 16:40:02 +01:00
/**
* Render an animated part that has an initial rotation value
*/
static void geo_process_bone(struct GraphNodeBone *node) {
Mat4 matrix;
Vec3s rotation;
Vec3f translation;
Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr));
vec3s_copy(rotation, node->rotation);
vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]);
if (gCurAnimType == ANIM_TYPE_TRANSLATION) {
translation[0] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
translation[1] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
translation[2] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurAnimType = ANIM_TYPE_ROTATION;
} else {
if (gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) {
translation[0] +=
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurrAnimAttribute += 2;
translation[2] +=
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurAnimType = ANIM_TYPE_ROTATION;
} else {
if (gCurAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) {
gCurrAnimAttribute += 2;
translation[1] +=
gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]
* gCurAnimTranslationMultiplier;
gCurrAnimAttribute += 2;
gCurAnimType = ANIM_TYPE_ROTATION;
} else if (gCurAnimType == ANIM_TYPE_NO_TRANSLATION) {
gCurrAnimAttribute += 6;
gCurAnimType = ANIM_TYPE_ROTATION;
}
}
}
if (gCurAnimType == ANIM_TYPE_ROTATION) {
rotation[0] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
rotation[1] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
rotation[2] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)];
}
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
gMatStackIndex++;
mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = matrixPtr;
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, GET_GRAPH_NODE_LAYER(node->node.flags));
2021-09-20 16:40:02 +01:00
}
if (node->node.children != NULL) {
geo_process_node_and_siblings(node->node.children);
}
gMatStackIndex--;
}
2019-09-01 15:50:50 -04:00
/**
* Initialize the animation-related global variables for the currently drawn
* object's animation.
2019-08-25 00:46:40 -04:00
*/
2020-09-20 11:15:47 -04:00
void geo_set_animation_globals(struct AnimInfo *node, s32 hasAnimation) {
2019-08-25 00:46:40 -04:00
struct Animation *anim = node->curAnim;
2020-09-20 11:15:47 -04:00
if (hasAnimation) {
2019-08-25 00:46:40 -04:00
node->animFrame = geo_update_animation_frame(node, &node->animFrameAccelAssist);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
node->animTimer = gAreaUpdateCounter;
2019-09-01 15:50:50 -04:00
if (anim->flags & ANIM_FLAG_HOR_TRANS) {
2019-08-25 00:46:40 -04:00
gCurAnimType = ANIM_TYPE_VERTICAL_TRANSLATION;
2019-09-01 15:50:50 -04:00
} else if (anim->flags & ANIM_FLAG_VERT_TRANS) {
2019-08-25 00:46:40 -04:00
gCurAnimType = ANIM_TYPE_LATERAL_TRANSLATION;
2021-09-20 16:25:58 -07:00
} else if (anim->flags & ANIM_FLAG_NO_TRANS) {
2019-08-25 00:46:40 -04:00
gCurAnimType = ANIM_TYPE_NO_TRANSLATION;
2019-09-01 15:50:50 -04:00
} else {
2019-08-25 00:46:40 -04:00
gCurAnimType = ANIM_TYPE_TRANSLATION;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gCurrAnimFrame = node->animFrame;
2021-09-20 16:25:58 -07:00
gCurAnimEnabled = (anim->flags & ANIM_FLAG_DISABLED) == 0;
2019-11-03 14:36:27 -05:00
gCurrAnimAttribute = segmented_to_virtual((void *) anim->index);
gCurAnimData = segmented_to_virtual((void *) anim->values);
2019-08-25 00:46:40 -04:00
2020-09-20 11:15:47 -04:00
if (anim->animYTransDivisor == 0) {
2019-08-25 00:46:40 -04:00
gCurAnimTranslationMultiplier = 1.0f;
2019-09-01 15:50:50 -04:00
} else {
2020-09-20 11:15:47 -04:00
gCurAnimTranslationMultiplier = (f32) node->animYTrans / (f32) anim->animYTransDivisor;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
static void geo_process_shadow(struct GraphNodeShadow *node) {
Gfx *shadowList;
Mat4 mtxf;
Vec3f shadowPos;
Vec3f animOffset;
f32 objScale;
f32 shadowScale;
f32 sinAng;
f32 cosAng;
struct GraphNode *geo;
Mtx *mtx;
if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) {
if (gCurGraphNodeHeldObject != NULL) {
get_pos_from_transform_mtx(shadowPos, gMatStack[gMatStackIndex],
2020-07-04 11:18:55 -04:00
*gCurGraphNodeCamera->matrixPtr);
2019-08-25 00:46:40 -04:00
shadowScale = node->shadowScale;
} else {
vec3f_copy(shadowPos, gCurGraphNodeObject->pos);
shadowScale = node->shadowScale * gCurGraphNodeObject->scale[0];
}
objScale = 1.0f;
2020-09-20 11:15:47 -04:00
if (gCurAnimEnabled) {
2019-08-25 00:46:40 -04:00
if (gCurAnimType == ANIM_TYPE_TRANSLATION
|| gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) {
geo = node->node.children;
2019-09-01 15:50:50 -04:00
if (geo != NULL && geo->type == GRAPH_NODE_TYPE_SCALE) {
2019-08-25 00:46:40 -04:00
objScale = ((struct GraphNodeScale *) geo)->scale;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04: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;
}
}
shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale,
node->shadowSolidity, node->shadowType);
if (shadowList != NULL) {
mtx = alloc_display_list(sizeof(*mtx));
gMatStackIndex++;
mtxf_translate(mtxf, shadowPos);
2020-07-04 11:18:55 -04:00
mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr);
2019-08-25 00:46:40 -04:00
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
2021-09-15 10:35:58 -07:00
geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), ((gShadowAboveWaterOrLava || gShadowAboveCustomWater || gMarioOnIceOrCarpet) ? LAYER_TRANSPARENT : LAYER_TRANSPARENT_DECAL));
2019-08-25 00:46:40 -04:00
gMatStackIndex--;
}
}
2019-09-01 15:50:50 -04:00
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
/**
* Check whether an object is in view to determine whether it should be drawn.
2020-06-02 12:44:34 -04:00
* This is known as frustum culling.
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*
2019-09-01 15:50:50 -04: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 12:44:34 -04:00
* the slope of the lines of the frustum.
2019-08-25 00:46:40 -04:00
*
2019-09-01 15:50:50 -04:00
* z-
2019-08-25 00:46:40 -04:00
*
2019-09-01 15:50:50 -04:00
* \ | /
* \ | /
* \ | /
* \ | /
* \ | /
* \|/
* C x+
2019-08-25 00:46:40 -04:00
*
2019-09-01 15:50:50 -04:00
* Since (0,0,0) is unaffected by rotation, columns 0, 1 and 2 are ignored.
2019-08-25 00:46:40 -04:00
*/
2020-09-20 11:15:47 -04:00
static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) {
s32 cullingRadius;
s32 halfFov; // half of the fov in in-game angle units instead of degrees
2019-08-25 00:46:40 -04:00
struct GraphNode *geo;
f32 hScreenEdge;
2019-09-01 15:50:50 -04:00
if (node->node.flags & GRAPH_RENDER_INVISIBLE) {
2019-08-25 00:46:40 -04:00
return FALSE;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
geo = node->sharedChild;
2021-09-25 09:31:45 -07:00
//! @bug The aspect ratio is not accounted for. When the fov value is 45,
2019-08-25 00:46:40 -04:00
// the horizontal effective fov is actually 60 degrees, so you can see objects
// visibly pop in or out at the edge of the screen.
2021-05-09 22:34:22 +01:00
halfFov = ((gCurGraphNodeCamFrustum->fov*aspect) / 2.0f + 1.0f) * 32768.0f / 180.0f + 0.5f;
2019-08-25 00:46:40 -04:00
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 12:44:34 -04:00
// This multiplication should really be performed on 4:3 as well,
// but the issue will be more apparent on widescreen.
// HackerSM64: This multiplication is done regardless of aspect ratio to fix object pop-in on the edges of the screen (which happens at 4:3 too)
2020-06-02 12:44:34 -04:00
hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO;
2019-09-01 15:50:50 -04:00
if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) {
2021-09-23 18:56:14 -07:00
cullingRadius = ((struct GraphNodeCullingRadius *) geo)->cullingRadius;
2019-09-01 15:50:50 -04:00
} else {
2019-08-25 00:46:40 -04:00
cullingRadius = 300;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
// Don't render if the object is close to or behind the camera
2019-09-01 15:50:50 -04:00
if (matrix[3][2] > -100.0f + cullingRadius) {
2019-08-25 00:46:40 -04:00
return FALSE;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04: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 15:50:50 -04:00
if (matrix[3][2] < -20000.0f - cullingRadius) {
2019-08-25 00:46:40 -04:00
return FALSE;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
// Check whether the object is horizontally in view
2019-09-01 15:50:50 -04:00
if (matrix[3][0] > hScreenEdge + cullingRadius) {
2019-08-25 00:46:40 -04:00
return FALSE;
2019-09-01 15:50:50 -04:00
}
if (matrix[3][0] < -hScreenEdge - cullingRadius) {
2019-08-25 00:46:40 -04:00
return FALSE;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
return TRUE;
}
2019-09-01 15:50:50 -04:00
/**
* Process an object node.
2019-08-25 00:46:40 -04:00
*/
static void geo_process_object(struct Object *node) {
Mat4 mtxf;
s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0;
2020-09-20 11:15:47 -04:00
if (node->header.gfx.areaIndex == gCurGraphNodeRoot->areaIndex) {
2019-08-25 00:46:40 -04:00
if (node->header.gfx.throwMatrix != NULL) {
2020-07-04 11:18:55 -04:00
mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix,
2019-08-25 00:46:40 -04:00
gMatStack[gMatStackIndex]);
2020-04-03 14:57:26 -04:00
} else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) {
2019-08-25 00:46:40 -04:00
mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
node->header.gfx.pos, gCurGraphNodeCamera->roll);
} else {
mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
}
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1],
node->header.gfx.scale);
2020-07-04 11:18:55 -04:00
node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex];
2019-08-25 00:46:40 -04: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
2020-09-20 11:15:47 -04:00
if (node->header.gfx.animInfo.curAnim != NULL) {
geo_set_animation_globals(&node->header.gfx.animInfo, hasAnimation);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) {
Mtx *mtx = alloc_display_list(sizeof(*mtx));
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
if (node->header.gfx.sharedChild != NULL) {
#ifdef VISUAL_DEBUG
if (hitboxView)
{
Vec3f bnds1;
Vec3f bnds2;
//This will create a cylinder that visualises their hitbox.
//If they do not have a hitbox, it will be a small white cube instead.
if (node->oIntangibleTimer != -1)
{
vec3f_set(bnds1, node->oPosX, node->oPosY - node->hitboxDownOffset, node->oPosZ);
vec3f_set(bnds2, node->hitboxRadius, node->hitboxHeight-node->hitboxDownOffset, node->hitboxRadius);
debug_box_color(0x800000FF);
2021-09-17 14:28:24 +01:00
debug_box(bnds1, bnds2, DEBUG_SHAPE_CYLINDER | DEBUG_UCODE_REJ);
vec3f_set(bnds1, node->oPosX, node->oPosY - node->hitboxDownOffset, node->oPosZ);
vec3f_set(bnds2, node->hurtboxRadius, node->hurtboxHeight, node->hurtboxRadius);
debug_box_color(0x8FF00000);
2021-09-17 14:28:24 +01:00
debug_box(bnds1, bnds2, DEBUG_SHAPE_CYLINDER | DEBUG_UCODE_REJ);
}
else
{
vec3f_set(bnds1, node->oPosX, node->oPosY - 15, node->oPosZ);
vec3f_set(bnds2, 30, 30, 30);
debug_box_color(0x80FFFFFF);
2021-09-17 14:28:24 +01:00
debug_box(bnds1, bnds2, DEBUG_SHAPE_BOX | DEBUG_UCODE_REJ);
}
}
#endif
2019-08-25 00:46:40 -04:00
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 15:50:50 -04:00
if (node->header.gfx.node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->header.gfx.node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
gMatStackIndex--;
gCurAnimType = ANIM_TYPE_NONE;
node->header.gfx.throwMatrix = NULL;
}
}
2019-09-01 15:50:50 -04: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 00:46:40 -04: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 15:50:50 -04:00
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
/**
* Process a held object node.
2019-08-25 00:46:40 -04:00
*/
void geo_process_held_object(struct GraphNodeHeldObject *node) {
Mat4 mat;
Vec3f translation;
Mtx *mtx = alloc_display_list(sizeof(*mtx));
#ifdef F3DEX_GBI_2
gSPLookAt(gDisplayListHead++, &lookAt);
#endif
2019-09-01 15:50:50 -04:00
if (node->fnNode.func != NULL) {
2019-08-25 00:46:40 -04:00
node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]);
2019-09-01 15:50:50 -04:00
}
2019-11-03 14:36:27 -05: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 00:46:40 -04:00
translation[0] = node->translation[0] / 4.0f;
translation[1] = node->translation[1] / 4.0f;
translation[2] = node->translation[2] / 4.0f;
mtxf_translate(mat, translation);
2020-07-04 11:18:55 -04:00
mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix);
2019-08-25 00:46:40 -04: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 14:36:27 -05:00
node->objNode->header.gfx.scale);
2019-09-01 15:50:50 -04:00
if (node->fnNode.func != NULL) {
2019-08-25 00:46:40 -04:00
node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node,
(struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gMatStackIndex++;
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = mtx;
gGeoTempState.type = gCurAnimType;
gGeoTempState.enabled = gCurAnimEnabled;
gGeoTempState.frame = gCurrAnimFrame;
gGeoTempState.translationMultiplier = gCurAnimTranslationMultiplier;
gGeoTempState.attribute = gCurrAnimAttribute;
gGeoTempState.data = gCurAnimData;
gCurAnimType = 0;
gCurGraphNodeHeldObject = (void *) node;
2020-09-20 11:15:47 -04:00
if (node->objNode->header.gfx.animInfo.curAnim != NULL) {
geo_set_animation_globals(&node->objNode->header.gfx.animInfo, hasAnimation);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
2019-11-03 14:36:27 -05:00
geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild);
2019-08-25 00:46:40 -04:00
gCurGraphNodeHeldObject = NULL;
gCurAnimType = gGeoTempState.type;
gCurAnimEnabled = gGeoTempState.enabled;
gCurrAnimFrame = gGeoTempState.frame;
gCurAnimTranslationMultiplier = gGeoTempState.translationMultiplier;
gCurrAnimAttribute = gGeoTempState.attribute;
gCurAnimData = gGeoTempState.data;
gMatStackIndex--;
}
2019-09-01 15:50:50 -04:00
if (node->fnNode.node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->fnNode.node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
/**
* Processes the children of the given GraphNode if it has any
2019-08-25 00:46:40 -04:00
*/
void geo_try_process_children(struct GraphNode *node) {
2019-09-01 15:50:50 -04:00
if (node->children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
void geo_process_node_and_siblings(struct GraphNode *firstNode) {
s32 iterateChildren = TRUE;
2019-08-25 00:46:40 -04:00
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 15:50:50 -04:00
if (parent != NULL) {
2019-08-25 00:46:40 -04:00
iterateChildren = (parent->type != GRAPH_NODE_TYPE_SWITCH_CASE);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
do {
if (curGraphNode->flags & GRAPH_RENDER_ACTIVE) {
if (curGraphNode->flags & GRAPH_RENDER_CHILDREN_FIRST) {
geo_try_process_children(curGraphNode);
} else {
switch (curGraphNode->type) {
2021-09-20 16:54:52 -07:00
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;
case GRAPH_NODE_TYPE_BONE: geo_process_bone ((struct GraphNodeBone *) curGraphNode); break;
default: geo_try_process_children ((struct GraphNode *) curGraphNode); break;
2019-08-25 00:46:40 -04:00
}
}
} else {
2019-09-01 15:50:50 -04:00
if (curGraphNode->type == GRAPH_NODE_TYPE_OBJECT) {
2019-08-25 00:46:40 -04:00
((struct GraphNodeObject *) curGraphNode)->throwMatrix = NULL;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
} while (iterateChildren && (curGraphNode = curGraphNode->next) != firstNode);
}
2019-09-01 15:50:50 -04: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 00:46:40 -04:00
*/
void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) {
#if PUPPYPRINT_DEBUG
2021-08-15 11:30:19 +01:00
OSTime first = osGetTime();
#endif
2019-08-25 00:46:40 -04:00
if (node->node.flags & GRAPH_RENDER_ACTIVE) {
Mtx *initialMatrix;
Vp *viewport = alloc_display_list(sizeof(*viewport));
2019-10-05 15:08:05 -04:00
gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool),
MEMORY_POOL_LEFT);
2019-08-25 00:46:40 -04: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);
make_viewport_clip_rect(b);
*viewport = *b;
}
else if (c != NULL) {
clear_frame_buffer(clearColor);
make_viewport_clip_rect(c);
}
mtxf_identity(gMatStack[gMatStackIndex]);
mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]);
gMatStackFixed[gMatStackIndex] = initialMatrix;
gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewport));
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]),
2019-11-03 14:36:27 -05:00
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
2019-08-25 00:46:40 -04:00
gCurGraphNodeRoot = node;
2019-09-01 15:50:50 -04:00
if (node->node.children != NULL) {
2019-08-25 00:46:40 -04:00
geo_process_node_and_siblings(node->node.children);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
gCurGraphNodeRoot = NULL;
2019-09-01 15:50:50 -04:00
if (gShowDebugText) {
2019-08-25 00:46:40 -04:00
print_text_fmt_int(180, 36, "MEM %d",
gDisplayListHeap->totalSpace - gDisplayListHeap->usedSpace);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
main_pool_free(gDisplayListHeap);
}
#if PUPPYPRINT_DEBUG
2021-08-15 11:30:19 +01:00
profiler_update(graphTime, first);
#endif
2019-08-25 00:46:40 -04:00
}