You've already forked Microtransactions64
mirror of
https://github.com/Print-and-Panic/Microtransactions64.git
synced 2026-01-21 10:17:19 -08:00
Culling improvements + GRAPH_RENDER_INVISIBLE check change (#590)
* Culling improvements + earlier GRAPH_RENDER_INVISIBLE check GRAPH_RENDER_INVISIBLE is now checked during geo_process_object before any uncessesary transformation is applied to the object, translation is still calculated for sound even if the object is invisible. Half fov is now computed during geo_process_perspective. Vertical culling has been added (only when bellow the screen to prevent shadow´s being culled). Emulators have basically infinite culling aspect ratio to prevent early culling with widescreen viewport hacks. Default culling radius is now a define. This was written by both me and Kaze Emanuar, he provided the suggestion to use absf and informed me of integer division being remarkably slow (although it´s only used once during geo_process_perspective). * Badly placed new line (major fix) * integer Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com> * parenthesis doesn´t affect order of operation, just for code quality Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com> * uncessary whitespace Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com> --------- Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
This commit is contained in:
@@ -133,3 +133,20 @@
|
||||
* NOTE: When this is enabled, The 49th hardcoded rectangle shadow will act as a regular circular shadow, due to Mario's shadow ID being 99 in vanilla.
|
||||
*/
|
||||
#define LEGACY_SHADOW_IDS
|
||||
|
||||
|
||||
/**
|
||||
* Limits the horizontal fov on emulator like on console. May break viewport widescreen hacks.
|
||||
*/
|
||||
//#define HORIZONTAL_CULLING_ON_EMULATOR
|
||||
|
||||
/**
|
||||
* Makes objects bellow the screen be culled.
|
||||
*/
|
||||
#define VERTICAL_CULLING
|
||||
|
||||
/**
|
||||
* If the first command of an object´s geolayout is not GEO_CULLING_RADIUS, DEFAULT_CULLING_RADIUS
|
||||
* will be used instead.
|
||||
*/
|
||||
#define DEFAULT_CULLING_RADIUS 300
|
||||
|
||||
@@ -261,7 +261,7 @@ void geo_layout_cmd_node_perspective(void) {
|
||||
gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT;
|
||||
}
|
||||
|
||||
graphNode = init_graph_node_perspective(gGraphNodePool, NULL, (f32) fov, near, far, frustumFunc, 0);
|
||||
graphNode = init_graph_node_perspective(gGraphNodePool, NULL, (f32) fov, near, far, frustumFunc);
|
||||
|
||||
register_scene_graph_node(&graphNode->fnNode.node);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ init_graph_node_ortho_projection(struct AllocOnlyPool *pool, struct GraphNodeOrt
|
||||
struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *pool,
|
||||
struct GraphNodePerspective *graphNode,
|
||||
f32 fov, u16 near, u16 far,
|
||||
GraphNodeFunc nodeFunc, s32 unused) {
|
||||
GraphNodeFunc nodeFunc) {
|
||||
if (pool != NULL) {
|
||||
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodePerspective));
|
||||
}
|
||||
@@ -83,7 +83,6 @@ struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *p
|
||||
graphNode->near = near;
|
||||
graphNode->far = far;
|
||||
graphNode->fnNode.func = nodeFunc;
|
||||
graphNode->unused = unused;
|
||||
|
||||
if (nodeFunc != NULL) {
|
||||
nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
|
||||
|
||||
@@ -131,10 +131,13 @@ struct GraphNodeOrthoProjection {
|
||||
*/
|
||||
struct GraphNodePerspective {
|
||||
/*0x00*/ struct FnGraphNode fnNode;
|
||||
/*0x18*/ s32 unused;
|
||||
/*0x1C*/ f32 fov; // horizontal field of view in degrees
|
||||
/*0x20*/ u16 near; // near clipping plane
|
||||
/*0x22*/ u16 far; // far clipping plane
|
||||
/*0x18*/ f32 fov; // horizontal field of view in degrees
|
||||
/*0x1C*/ u16 near; // near clipping plane
|
||||
/*0x1E*/ u16 far; // far clipping plane
|
||||
/*0x20*/ f32 halfFovHorizontal;
|
||||
#ifdef VERTICAL_CULLING
|
||||
/*0x24*/ f32 halfFovVertical;
|
||||
#endif
|
||||
};
|
||||
|
||||
/** An entry in the master list. It is a linked list of display lists
|
||||
@@ -369,7 +372,7 @@ void init_scene_graph_node_links(struct GraphNode *graphNode, s32 type);
|
||||
|
||||
struct GraphNodeRoot *init_graph_node_root (struct AllocOnlyPool *pool, struct GraphNodeRoot *graphNode, s16 areaIndex, s16 x, s16 y, s16 width, s16 height);
|
||||
struct GraphNodeOrthoProjection *init_graph_node_ortho_projection (struct AllocOnlyPool *pool, struct GraphNodeOrthoProjection *graphNode, f32 scale);
|
||||
struct GraphNodePerspective *init_graph_node_perspective (struct AllocOnlyPool *pool, struct GraphNodePerspective *graphNode, f32 fov, u16 near, u16 far, GraphNodeFunc nodeFunc, s32 unused);
|
||||
struct GraphNodePerspective *init_graph_node_perspective (struct AllocOnlyPool *pool, struct GraphNodePerspective *graphNode, f32 fov, u16 near, u16 far, GraphNodeFunc nodeFunc);
|
||||
struct GraphNodeStart *init_graph_node_start (struct AllocOnlyPool *pool, struct GraphNodeStart *graphNode);
|
||||
struct GraphNodeMasterList *init_graph_node_master_list (struct AllocOnlyPool *pool, struct GraphNodeMasterList *graphNode, s16 on);
|
||||
struct GraphNodeLevelOfDetail *init_graph_node_render_range (struct AllocOnlyPool *pool, struct GraphNodeLevelOfDetail *graphNode, s16 minDistance, s16 maxDistance);
|
||||
|
||||
@@ -566,6 +566,28 @@ void geo_process_perspective(struct GraphNodePerspective *node) {
|
||||
sAspectRatio = 4.0f / 3.0f; // 1.33333f
|
||||
#endif
|
||||
|
||||
// The reason this is not divided as an integer is to prevent an integer division.
|
||||
f32 vHalfFov = ( (f32) ((node->fov * 4096) + 8192) ) / 45.f;
|
||||
|
||||
// We need to account for aspect ratio changes by multiplying by the widescreen horizontal stretch
|
||||
// (normally 1.775).
|
||||
f32 hHalfFov = vHalfFov * sAspectRatio;
|
||||
|
||||
node->halfFovHorizontal = tans(hHalfFov);
|
||||
|
||||
#ifdef VERTICAL_CULLING
|
||||
node->halfFovVertical = tans(vHalfFov);
|
||||
#endif
|
||||
|
||||
#ifndef HORIZONTAL_CULLING_ON_EMULATOR
|
||||
// If an emulator is detected, use a large value for the half fov
|
||||
// horizontal value to account for viewport widescreen hacks.
|
||||
|
||||
if(!gIsConsole){
|
||||
node->halfFovHorizontal = 9999.0f;
|
||||
}
|
||||
#endif
|
||||
|
||||
guPerspective(mtx, &perspNorm, node->fov, sAspectRatio, node->near / WORLD_SCALE, node->far / WORLD_SCALE, 1.0f);
|
||||
gSPPerspNormalize(gDisplayListHead++, perspNorm);
|
||||
|
||||
@@ -1015,23 +1037,24 @@ void geo_process_shadow(struct GraphNodeShadow *node) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an object is in view to determine whether it should be drawn.
|
||||
* Check whether an object is in view to determine whether if it should be drawn.
|
||||
* This is known as frustum culling.
|
||||
* 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.
|
||||
*
|
||||
* It checks whether the object is far away, very close or behind the camera and
|
||||
* vertically or horizontally out of view.
|
||||
* The radius used is specified in DEFAULT_CULLING_RADIUS unless the object
|
||||
* has a culling radius node that specifies another value.
|
||||
*
|
||||
* 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
|
||||
* the slope of the lines of the frustum.
|
||||
* the slope of the lines of the frustum, these are done once during geo_process_perspective.
|
||||
*
|
||||
* z-
|
||||
*
|
||||
@@ -1046,10 +1069,6 @@ void geo_process_shadow(struct GraphNodeShadow *node) {
|
||||
* Since (0,0,0) is unaffected by rotation, columns 0, 1 and 2 are ignored.
|
||||
*/
|
||||
s32 obj_is_in_view(struct GraphNodeObject *node) {
|
||||
if (node->node.flags & GRAPH_RENDER_INVISIBLE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct GraphNode *geo = node->sharedChild;
|
||||
|
||||
s16 cullingRadius;
|
||||
@@ -1057,43 +1076,38 @@ s32 obj_is_in_view(struct GraphNodeObject *node) {
|
||||
if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) {
|
||||
cullingRadius = ((struct GraphNodeCullingRadius *) geo)->cullingRadius;
|
||||
} else {
|
||||
cullingRadius = 300;
|
||||
cullingRadius = DEFAULT_CULLING_RADIUS;
|
||||
}
|
||||
|
||||
// Don't render if the object is close to or behind the camera
|
||||
if (node->cameraToObject[2] > -100.0f + cullingRadius) {
|
||||
// Check whether the object is not too far away or too close / behind the camera.
|
||||
// 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.
|
||||
f32 cameraToObjectDepth = node->cameraToObject[2];
|
||||
|
||||
#define VALID_DEPTH_MIDDLE (-20100.f / 2.f)
|
||||
#define VALID_DEPTH_RANGE (19900 / 2.f)
|
||||
if (absf(cameraToObjectDepth - VALID_DEPTH_MIDDLE) >= VALID_DEPTH_RANGE + cullingRadius) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//! 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.
|
||||
if (node->cameraToObject[2] < -20000.0f - cullingRadius) {
|
||||
#ifdef VERTICAL_CULLING
|
||||
f32 vScreenEdge = -cameraToObjectDepth * gCurGraphNodeCamFrustum->halfFovVertical;
|
||||
|
||||
// Unlike with horizontal culling, we only check if the object is bellow the screen
|
||||
// to prevent shadows from being culled.
|
||||
if (node->cameraToObject[1] < -vScreenEdge - cullingRadius) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// half of the fov in in-game angle units instead of degrees
|
||||
s16 halfFov = (((((gCurGraphNodeCamFrustum->fov * sAspectRatio) / 2.0f) + 1.0f) * 32768.0f) / 180.0f) + 0.5f;
|
||||
#endif
|
||||
|
||||
f32 hScreenEdge = -cameraToObjectDepth * gCurGraphNodeCamFrustum->halfFovHorizontal;
|
||||
|
||||
f32 hScreenEdge = -node->cameraToObject[2] * tans(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.
|
||||
|
||||
// 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)
|
||||
// hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO;
|
||||
|
||||
// Check whether the object is horizontally in view
|
||||
if (node->cameraToObject[0] > hScreenEdge + cullingRadius) {
|
||||
if (absf(node->cameraToObject[0]) > hScreenEdge + cullingRadius) {
|
||||
return FALSE;
|
||||
}
|
||||
if (node->cameraToObject[0] < -hScreenEdge - cullingRadius) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1132,14 +1146,25 @@ void visualise_object_hitbox(struct Object *node) {
|
||||
*/
|
||||
void geo_process_object(struct Object *node) {
|
||||
if (node->header.gfx.areaIndex == gCurGraphNodeRoot->areaIndex) {
|
||||
if (node->header.gfx.throwMatrix != NULL) {
|
||||
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix, node->header.gfx.scale);
|
||||
} else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) {
|
||||
mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
|
||||
node->header.gfx.pos, node->header.gfx.scale, gCurGraphNodeCamera->roll);
|
||||
} else {
|
||||
mtxf_rotate_zxy_and_translate(gMatStack[gMatStackIndex + 1], node->header.gfx.pos, node->header.gfx.angle);
|
||||
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], node->header.gfx.scale);
|
||||
s32 isInvisible = (node->header.gfx.node.flags & GRAPH_RENDER_INVISIBLE);
|
||||
s32 noThrowMatrix = (node->header.gfx.throwMatrix == NULL);
|
||||
|
||||
// If the throw matrix is null and the object is invisible, there is no need
|
||||
// to update billboarding, scale, rotation, etc.
|
||||
// This still updates translation since it is needed for sound.
|
||||
if (isInvisible && noThrowMatrix) {
|
||||
mtxf_translate(gMatStack[gMatStackIndex + 1], node->header.gfx.pos);
|
||||
}
|
||||
else{
|
||||
if (!noThrowMatrix) {
|
||||
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix, node->header.gfx.scale);
|
||||
} else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) {
|
||||
mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
|
||||
node->header.gfx.pos, node->header.gfx.scale, gCurGraphNodeCamera->roll);
|
||||
} else {
|
||||
mtxf_rotate_zxy_and_translate(gMatStack[gMatStackIndex + 1], node->header.gfx.pos, node->header.gfx.angle);
|
||||
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], node->header.gfx.scale);
|
||||
}
|
||||
}
|
||||
|
||||
node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex];
|
||||
@@ -1149,7 +1174,8 @@ void geo_process_object(struct Object *node) {
|
||||
if (node->header.gfx.animInfo.curAnim != NULL) {
|
||||
geo_set_animation_globals(&node->header.gfx.animInfo, (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0);
|
||||
}
|
||||
if (obj_is_in_view(&node->header.gfx)) {
|
||||
|
||||
if (!isInvisible && obj_is_in_view(&node->header.gfx)) {
|
||||
gMatStackIndex--;
|
||||
inc_mat_stack();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user