From cbcba7b6f975c8510eef31a0bc2286067a9fc0a7 Mon Sep 17 00:00:00 2001 From: Reonu Date: Tue, 27 Apr 2021 11:54:51 +0100 Subject: [PATCH] applied falcobuster's long render distance fix --- enhancements/long-render-patch.diff | 118 ++++++++++++++++++++++++++++ src/engine/math_util.c | 26 +++--- src/engine/math_util.h | 8 ++ src/game/rendering_graph_node.c | 28 ++++--- 4 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 enhancements/long-render-patch.diff diff --git a/enhancements/long-render-patch.diff b/enhancements/long-render-patch.diff new file mode 100644 index 00000000..dd72a4eb --- /dev/null +++ b/enhancements/long-render-patch.diff @@ -0,0 +1,118 @@ +diff --git a/src/engine/math_util.c b/src/engine/math_util.c +index e5d59d8b5..75cd43a09 100644 +--- a/src/engine/math_util.c ++++ b/src/engine/math_util.c +@@ -564,23 +564,17 @@ void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) { + * and no crashes occur. + */ + void mtxf_to_mtx(Mtx *dest, Mat4 src) { +-#ifdef AVOID_UB +- // Avoid type-casting which is technically UB by calling the equivalent +- // guMtxF2L function. This helps little-endian systems, as well. +- guMtxF2L(src, dest); +-#else +- s32 asFixedPoint; +- register s32 i; +- register s16 *a3 = (s16 *) dest; // all integer parts stored in first 16 bytes +- register s16 *t0 = (s16 *) dest + 16; // all fraction parts stored in last 16 bytes +- register f32 *t1 = (f32 *) src; +- +- for (i = 0; i < 16; i++) { +- asFixedPoint = *t1++ * (1 << 16); //! float-to-integer conversion responsible for PU crashes +- *a3++ = GET_HIGH_S16_OF_32(asFixedPoint); // integer part +- *t0++ = GET_LOW_S16_OF_32(asFixedPoint); // fraction part +- } +-#endif ++ Mat4 temp; ++ register s32 i, j; ++ ++ for( i = 0; i < 4; i++ ) { ++ for( j = 0; j < 3; j++ ) { ++ temp[i][j] = src[i][j] / WORLD_SCALE; ++ } ++ temp[i][3] = src[i][3]; ++ } ++ ++ guMtxF2L( temp, dest ); + } + + /** +diff --git a/src/engine/math_util.h b/src/engine/math_util.h +index cb37d52d1..0826a8407 100644 +--- a/src/engine/math_util.h ++++ b/src/engine/math_util.h +@@ -5,6 +5,14 @@ + + #include "types.h" + ++/* Scales the world down by this factor, increasing how far you can render on ++ * console in exchange for a slight loss in precision. ++ * ++ * For double extended boundary hacks, a value of 1.5f or 2.0f is good. ++ * For quadruple extended bounds, use 3.f or 4.f ++ */ ++#define WORLD_SCALE 2.f ++ + /* + * The sine and cosine tables overlap, but "#define gCosineTable (gSineTable + + * 0x400)" doesn't give expected codegen; gSineTable and gCosineTable need to +diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c +index 58238e83f..150cff68b 100644 +--- a/src/game/rendering_graph_node.c ++++ b/src/game/rendering_graph_node.c +@@ -249,7 +249,7 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { + f32 aspect = (f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height; + #endif + +- guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f); ++ guPerspective(mtx, &perspNorm, node->fov, aspect, node->near / WORLD_SCALE, node->far / WORLD_SCALE, 1.0f); + gSPPerspNormalize(gDisplayListHead++, perspNorm); + + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); +@@ -267,17 +267,8 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { + * range of this node. + */ + static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) { +-#ifdef GBI_FLOATS +- Mtx *mtx = gMatStackFixed[gMatStackIndex]; +- s16 distanceFromCam = (s32) -mtx->m[3][2]; // z-component of the translation column +-#else +- // The fixed point Mtx type is defined as 16 longs, but it's actually 16 +- // shorts for the integer parts followed by 16 shorts for the fraction parts +- Mtx *mtx = gMatStackFixed[gMatStackIndex]; +- s16 distanceFromCam = -GET_HIGH_S16_OF_32(mtx->m[1][3]); // z-component of the translation column +-#endif +- +- if (node->minDistance <= distanceFromCam && distanceFromCam < node->maxDistance) { ++ f32 distanceFromCam = -gMatStack[gMatStackIndex][3][2]; ++ if ((f32)node->minDistance <= distanceFromCam && distanceFromCam < (f32)node->maxDistance) { + if (node->node.children != 0) { + geo_process_node_and_siblings(node->node.children); + } +@@ -304,6 +295,17 @@ static void geo_process_switch(struct GraphNodeSwitchCase *node) { + } + } + ++static void make_roll_matrix(Mtx *mtx, s16 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); ++} ++ + /** + * Process a camera node. + */ +@@ -315,7 +317,7 @@ static void geo_process_camera(struct GraphNodeCamera *node) { + if (node->fnNode.func != NULL) { + node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); + } +- mtxf_rotate_xy(rollMtx, node->rollScreen); ++ make_roll_matrix(rollMtx, node->rollScreen); + + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(rollMtx), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH); + diff --git a/src/engine/math_util.c b/src/engine/math_util.c index e5d59d8b..75cd43a0 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -564,23 +564,17 @@ void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) { * and no crashes occur. */ void mtxf_to_mtx(Mtx *dest, Mat4 src) { -#ifdef AVOID_UB - // Avoid type-casting which is technically UB by calling the equivalent - // guMtxF2L function. This helps little-endian systems, as well. - guMtxF2L(src, dest); -#else - s32 asFixedPoint; - register s32 i; - register s16 *a3 = (s16 *) dest; // all integer parts stored in first 16 bytes - register s16 *t0 = (s16 *) dest + 16; // all fraction parts stored in last 16 bytes - register f32 *t1 = (f32 *) src; + Mat4 temp; + register s32 i, j; + + for( i = 0; i < 4; i++ ) { + for( j = 0; j < 3; j++ ) { + temp[i][j] = src[i][j] / WORLD_SCALE; + } + temp[i][3] = src[i][3]; + } - for (i = 0; i < 16; i++) { - asFixedPoint = *t1++ * (1 << 16); //! float-to-integer conversion responsible for PU crashes - *a3++ = GET_HIGH_S16_OF_32(asFixedPoint); // integer part - *t0++ = GET_LOW_S16_OF_32(asFixedPoint); // fraction part - } -#endif + guMtxF2L( temp, dest ); } /** diff --git a/src/engine/math_util.h b/src/engine/math_util.h index cb37d52d..0826a840 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -5,6 +5,14 @@ #include "types.h" +/* Scales the world down by this factor, increasing how far you can render on + * console in exchange for a slight loss in precision. + * + * For double extended boundary hacks, a value of 1.5f or 2.0f is good. + * For quadruple extended bounds, use 3.f or 4.f + */ +#define WORLD_SCALE 2.f + /* * The sine and cosine tables overlap, but "#define gCosineTable (gSineTable + * 0x400)" doesn't give expected codegen; gSineTable and gCosineTable need to diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 00f1313c..4626a34d 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -249,7 +249,7 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { f32 aspect = (f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height; #endif - guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f); + guPerspective(mtx, &perspNorm, node->fov, aspect, node->near / WORLD_SCALE, node->far / WORLD_SCALE, 1.0f); gSPPerspNormalize(gDisplayListHead++, perspNorm); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); @@ -267,17 +267,8 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { * range of this node. */ static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) { -#ifdef GBI_FLOATS - Mtx *mtx = gMatStackFixed[gMatStackIndex]; - s16 distanceFromCam = (s32) -mtx->m[3][2]; // z-component of the translation column -#else - // The fixed point Mtx type is defined as 16 longs, but it's actually 16 - // shorts for the integer parts followed by 16 shorts for the fraction parts - Mtx *mtx = gMatStackFixed[gMatStackIndex]; - s16 distanceFromCam = -GET_HIGH_S16_OF_32(mtx->m[1][3]); // z-component of the translation column -#endif - - if (node->minDistance <= distanceFromCam && distanceFromCam < node->maxDistance) { + f32 distanceFromCam = -gMatStack[gMatStackIndex][3][2]; + if ((f32)node->minDistance <= distanceFromCam && distanceFromCam < (f32)node->maxDistance) { if (node->node.children != 0) { geo_process_node_and_siblings(node->node.children); } @@ -304,6 +295,17 @@ static void geo_process_switch(struct GraphNodeSwitchCase *node) { } } +static void make_roll_matrix(Mtx *mtx, s16 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); +} + /** * Process a camera node. */ @@ -315,7 +317,7 @@ static void geo_process_camera(struct GraphNodeCamera *node) { if (node->fnNode.func != NULL) { node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); } - mtxf_rotate_xy(rollMtx, node->rollScreen); + make_roll_matrix(rollMtx, node->rollScreen); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(rollMtx), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH);