Massive refactor

This commit is contained in:
jaburns
2020-10-27 11:07:42 -06:00
parent 48ab192e17
commit fcb6b5b431
85 changed files with 207 additions and 168 deletions
+797
View File
@@ -0,0 +1,797 @@
#include "../include/sm64.h"
#include "geo_layout.h"
#include "math_util.h"
#include "../memory.h"
#include "graph_node.h"
#include "../shim.h"
static Vec3s gVec3sZero = { 0, 0, 0 };
typedef void (*GeoLayoutCommandProc)(void);
GeoLayoutCommandProc GeoLayoutJumpTable[] = {
geo_layout_cmd_branch_and_link,
geo_layout_cmd_end,
geo_layout_cmd_branch,
geo_layout_cmd_return,
geo_layout_cmd_open_node,
geo_layout_cmd_close_node,
geo_layout_cmd_assign_as_view,
geo_layout_cmd_update_node_flags,
geo_layout_cmd_node_root,
geo_layout_cmd_node_ortho_projection,
geo_layout_cmd_node_perspective,
geo_layout_cmd_node_start,
geo_layout_cmd_node_master_list,
geo_layout_cmd_node_level_of_detail,
geo_layout_cmd_node_switch_case,
geo_layout_cmd_node_camera,
geo_layout_cmd_node_translation_rotation,
geo_layout_cmd_node_translation,
geo_layout_cmd_node_rotation,
geo_layout_cmd_node_animated_part,
geo_layout_cmd_node_billboard,
geo_layout_cmd_node_display_list,
geo_layout_cmd_node_shadow,
geo_layout_cmd_node_object_parent,
geo_layout_cmd_node_generated,
geo_layout_cmd_node_background,
geo_layout_cmd_nop,
geo_layout_cmd_copy_view,
geo_layout_cmd_node_held_obj,
geo_layout_cmd_node_scale,
geo_layout_cmd_nop2,
geo_layout_cmd_nop3,
geo_layout_cmd_node_culling_radius,
};
struct GraphNode gObjParentGraphNode;
struct AllocOnlyPool *gGraphNodePool;
struct GraphNode *gCurRootGraphNode;
UNUSED s32 D_8038BCA8;
/* The gGeoViews array is a mysterious one. Some background:
*
* If there are e.g. multiple Goombas, the multiple Goomba objects share one
* Geo node tree describing the goomba 3D model. Since every node has a single
* parent field and not a parent array, the parent is dynamically rebinded to
* each goomba instance just before rendering and set to null afterwards.
* The same happens for ObjectParentNode, which has as his sharedChild a group
* of all 240 object nodes. Why does the ObjectParentNode exist at all, if its
* only purpose is to temporarily bind the actual group with objects? This might
* be another remnant to Luigi.
*
* When creating a root node, room for (2 + cmd+0x02) pointers is allocated in
* gGeoViews. Except for the title screen, cmd+0x02 is 10. The 2 default ones
* might be for Mario and Luigi, and the other 10 could be different cameras for
* different rooms / boss fights. An area might be structured like this:
*
* geo_camera mode_player //Mario cam
* geo_open_node
* geo_render_obj
* geo_assign_as_view 1 // currently unused geo command
* geo_close_node
*
* geo_camera mode_player //Luigi cam
* geo_open_node
* geo_render_obj
* geo_copy_view 1 // currently unused geo command
* geo_assign_as_view 2
* geo_close_node
*
* geo_camera mode_boss //boss fight cam
* geo_assign_as_view 3
* ...
*
* There might also be specific geo nodes for Mario or Luigi only. Or a fixed camera
* might not have display list nodes of parts of the level that are out of view.
* In the end Luigi got scrapped and the multiple-camera design did not pan out,
* so everything was reduced to a single ObjectParent with a single group, and
* camera switching was all done in one node. End of speculation.
*/
struct GraphNode **gGeoViews;
u16 gGeoNumViews; // length of gGeoViews array
uintptr_t gGeoLayoutStack[16];
struct GraphNode *gCurGraphNodeList[32];
s16 gCurGraphNodeIndex;
s16 gGeoLayoutStackIndex; // similar to SP register in MIPS
UNUSED s16 D_8038BD7C;
s16 gGeoLayoutReturnIndex; // similar to RA register in MIPS
u8 *gGeoLayoutCommand;
u32 unused_8038B894[3] = { 0 };
/*
0x00: Branch and store return address
cmd+0x04: void *branchTarget
*/
void geo_layout_cmd_branch_and_link(void) {
gGeoLayoutStack[gGeoLayoutStackIndex++] = (uintptr_t) (gGeoLayoutCommand + CMD_PROCESS_OFFSET(8));
gGeoLayoutStack[gGeoLayoutStackIndex++] = (gCurGraphNodeIndex << 16) + gGeoLayoutReturnIndex;
gGeoLayoutReturnIndex = gGeoLayoutStackIndex;
gGeoLayoutCommand = segmented_to_virtual(cur_geo_cmd_ptr(0x04));
}
// 0x01: Terminate geo layout
void geo_layout_cmd_end(void) {
gGeoLayoutStackIndex = gGeoLayoutReturnIndex;
gGeoLayoutReturnIndex = gGeoLayoutStack[--gGeoLayoutStackIndex] & 0xFFFF;
gCurGraphNodeIndex = gGeoLayoutStack[gGeoLayoutStackIndex] >> 16;
gGeoLayoutCommand = (u8 *) gGeoLayoutStack[--gGeoLayoutStackIndex];
}
/*
0x02: Branch
cmd+0x04: void *branchTarget
*/
void geo_layout_cmd_branch(void) {
if (cur_geo_cmd_u8(0x01) == 1) {
gGeoLayoutStack[gGeoLayoutStackIndex++] = (uintptr_t) (gGeoLayoutCommand + CMD_PROCESS_OFFSET(8));
}
gGeoLayoutCommand = segmented_to_virtual(cur_geo_cmd_ptr(0x04));
}
// 0x03: Return from branch
void geo_layout_cmd_return(void) {
gGeoLayoutCommand = (u8 *) gGeoLayoutStack[--gGeoLayoutStackIndex];
}
// 0x04: Open node
void geo_layout_cmd_open_node(void) {
gCurGraphNodeList[gCurGraphNodeIndex + 1] = gCurGraphNodeList[gCurGraphNodeIndex];
gCurGraphNodeIndex++;
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
// 0x05: Close node
void geo_layout_cmd_close_node(void) {
gCurGraphNodeIndex--;
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
/*
0x06: Register the current node as a view
cmd+0x02: index
Register the current node in the gGeoViews array at the given index
*/
void geo_layout_cmd_assign_as_view(void) {
u16 index = cur_geo_cmd_s16(0x02);
if (index < gGeoNumViews) {
gGeoViews[index] = gCurGraphNodeList[gCurGraphNodeIndex];
}
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
/*
0x07: Update current scene graph node flags
cmd+0x01: u8 operation (0 = reset, 1 = set, 2 = clear)
cmd+0x02: s16 bits
*/
void geo_layout_cmd_update_node_flags(void) {
u16 operation = cur_geo_cmd_u8(0x01);
u16 flagBits = cur_geo_cmd_s16(0x02);
switch (operation) {
case GEO_CMD_FLAGS_RESET:
gCurGraphNodeList[gCurGraphNodeIndex]->flags = flagBits;
break;
case GEO_CMD_FLAGS_SET:
gCurGraphNodeList[gCurGraphNodeIndex]->flags |= flagBits;
break;
case GEO_CMD_FLAGS_CLEAR:
gCurGraphNodeList[gCurGraphNodeIndex]->flags &= ~flagBits;
break;
}
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
/*
0x08: Create a scene graph root node that specifies the viewport
cmd+0x02: s16 num entries (+2) to allocate for gGeoViews
cmd+0x04: s16 x
cmd+0x06: s16 y
cmd+0x08: s16 width
cmd+0x0A: s16 height
*/
void geo_layout_cmd_node_root(void) {
s32 i;
struct GraphNodeRoot *graphNode;
s16 x = cur_geo_cmd_s16(0x04);
s16 y = cur_geo_cmd_s16(0x06);
s16 width = cur_geo_cmd_s16(0x08);
s16 height = cur_geo_cmd_s16(0x0A);
// number of entries to allocate for gGeoViews array
// at least 2 are allocated by default
// cmd+0x02 = 0x00: Mario face, 0x0A: all other levels
gGeoNumViews = cur_geo_cmd_s16(0x02) + 2;
graphNode = init_graph_node_root(gGraphNodePool, NULL, 0, x, y, width, height);
// TODO: check type
gGeoViews = alloc_only_pool_alloc(gGraphNodePool, gGeoNumViews * sizeof(struct GraphNode *));
graphNode->views = gGeoViews;
graphNode->numViews = gGeoNumViews;
for (i = 0; i < gGeoNumViews; i++) {
gGeoViews[i] = NULL;
}
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT;
}
/*
0x09: Create orthographic projection scene graph node
cmd+0x02: s16 scale as a percentage (usually it's 100)
*/
void geo_layout_cmd_node_ortho_projection(void) {
struct GraphNodeOrthoProjection *graphNode;
f32 scale = (f32) cur_geo_cmd_s16(0x02) / 100.0f;
graphNode = init_graph_node_ortho_projection(gGraphNodePool, NULL, scale);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
/*
0x0A: Create camera frustum scene graph node
cmd+0x01: u8 if nonzero, enable frustumFunc field
cmd+0x02: s16 field of view
cmd+0x04: s16 near
cmd+0x06: s16 far
[cmd+0x08: GraphNodeFunc frustumFunc]
*/
void geo_layout_cmd_node_perspective(void) {
struct GraphNodePerspective *graphNode;
GraphNodeFunc frustumFunc = NULL;
s16 fov = cur_geo_cmd_s16(0x02);
s16 near = cur_geo_cmd_s16(0x04);
s16 far = cur_geo_cmd_s16(0x06);
if (cur_geo_cmd_u8(0x01) != 0) {
// optional asm function
frustumFunc = (GraphNodeFunc) cur_geo_cmd_ptr(0x08);
gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT;
}
graphNode = init_graph_node_perspective(gGraphNodePool, NULL, (f32) fov, near, far, frustumFunc, 0);
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
/*
0x0B: Create a scene graph node that groups other nodes without any
additional functionality
*/
void geo_layout_cmd_node_start(void) {
struct GraphNodeStart *graphNode;
graphNode = init_graph_node_start(gGraphNodePool, NULL);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
// 0x1F: No operation
void geo_layout_cmd_nop3(void) {
gGeoLayoutCommand += 0x10 << CMD_SIZE_SHIFT;
}
/*
0x0C: Create zbuffer-toggling scene graph node
cmd+0x01: u8 enableZBuffer (1 = on, 0 = off)
*/
void geo_layout_cmd_node_master_list(void) {
struct GraphNodeMasterList *graphNode;
graphNode = init_graph_node_master_list(gGraphNodePool, NULL, cur_geo_cmd_u8(0x01));
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
/*
0x0D: Create a level of detail graph node, which only renders at a certain
distance interval from the camera.
cmd+0x04: s16 minDistance
cmd+0x06: s16 maxDistance
*/
void geo_layout_cmd_node_level_of_detail(void) {
struct GraphNodeLevelOfDetail *graphNode;
s16 minDistance = cur_geo_cmd_s16(0x04);
s16 maxDistance = cur_geo_cmd_s16(0x06);
graphNode = init_graph_node_render_range(gGraphNodePool, NULL, minDistance, maxDistance);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
/*
0x0E: Create switch-case scene graph node
cmd+0x02: s16 initialSelectedCase
cmd+0x04: GraphNodeFunc caseSelectorFunc
caseSelectorFunc returns an index which is used to select the child node to render.
Used for animating coins, blinking, color selection, etc.
*/
void geo_layout_cmd_node_switch_case(void) {
struct GraphNodeSwitchCase *graphNode;
graphNode =
init_graph_node_switch_case(gGraphNodePool, NULL,
cur_geo_cmd_s16(0x02), // case which is initially selected
0,
(GraphNodeFunc) cur_geo_cmd_ptr(0x04), // case update function
0);
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
/*
0x0F: Create a camera scene graph node (GraphNodeCamera). The focus sets the Camera's areaCen position.
cmd+0x02: s16 camera type (changes from course to course)
cmd+0x04: s16 posX
cmd+0x06: s16 posY
cmd+0x08: s16 posZ
cmd+0x0A: s16 focusX
cmd+0x0C: s16 focusY
cmd+0x0E: s16 focusZ
cmd+0x10: GraphNodeFunc func
*/
void geo_layout_cmd_node_camera(void) {
struct GraphNodeCamera *graphNode;
s16 *cmdPos = (s16 *) &gGeoLayoutCommand[4];
Vec3f pos, focus;
cmdPos = read_vec3s_to_vec3f(pos, cmdPos);
cmdPos = read_vec3s_to_vec3f(focus, cmdPos);
graphNode = init_graph_node_camera(gGraphNodePool, NULL, pos, focus,
(GraphNodeFunc) cur_geo_cmd_ptr(0x10), cur_geo_cmd_s16(0x02));
register_scene_graph_node(&graphNode->fnNode.node);
gGeoViews[0] = &graphNode->fnNode.node;
gGeoLayoutCommand += 0x14 << CMD_SIZE_SHIFT;
}
/*
0x10: Create translation & rotation scene graph node with optional display list
cmd+0x01: u8 params
(params & 0x80): if set, enable displayList field and drawingLayer
((params & 0x70)>>4): fieldLayout
(params & 0x0F): drawingLayer
fieldLayout == 0:
cmd+0x04: s16 xTranslation
cmd+0x06: s16 yTranslation
cmd+0x08: s16 zTranslation
cmd+0x0A: s16 xRotation
cmd+0x0C: s16 yRotation
cmd+0x0E: s16 zRotation
fieldLayout == 1:
cmd+0x02: s16 xTranslation
cmd+0x04: s16 yTranslation
cmd+0x06: s16 zTranslation
(rotation gets copied from gVec3sZero)
fieldLayout == 2:
cmd+0x02: s16 xRotation
cmd+0x04: s16 yRotation
cmd+0x06: s16 zRotation
(translation gets copied from gVec3sZero)
fieldLayout == 3:
cmd+0x02: s16 yRotation
(translation gets copied from gVec3sZero)
(x and z translation are set to 0)
[cmd+var: void *displayList]
*/
void geo_layout_cmd_node_translation_rotation(void) {
struct GraphNodeTranslationRotation *graphNode;
Vec3s translation, rotation;
void *displayList = NULL;
s16 drawingLayer = 0;
s16 params = cur_geo_cmd_u8(0x01);
s16 *cmdPos = (s16 *) gGeoLayoutCommand;
switch ((params & 0x70) >> 4) {
case 0:
cmdPos = read_vec3s(translation, &cmdPos[2]);
cmdPos = read_vec3s_angle(rotation, cmdPos);
break;
case 1:
cmdPos = read_vec3s(translation, &cmdPos[1]);
vec3s_copy(rotation, gVec3sZero);
break;
case 2:
cmdPos = read_vec3s_angle(rotation, &cmdPos[1]);
vec3s_copy(translation, gVec3sZero);
break;
case 3:
vec3s_copy(translation, gVec3sZero);
vec3s_set(rotation, 0, (cmdPos[1] << 15) / 180, 0);
cmdPos += 2 << CMD_SIZE_SHIFT;
break;
}
if (params & 0x80) {
displayList = *(void **) &cmdPos[0];
drawingLayer = params & 0x0F;
cmdPos += 2 << CMD_SIZE_SHIFT;
}
graphNode = init_graph_node_translation_rotation(gGraphNodePool, NULL, drawingLayer, displayList,
translation, rotation);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand = (u8 *) cmdPos;
}
/*
0x11: Create translation scene graph node with optional display list
cmd+0x01: u8 params
(params & 0x80): if set, enable displayList field and drawingLayer
(params & 0x0F): drawingLayer
cmd+0x02: s16 xTranslation
cmd+0x04: s16 yTranslation
cmd+0x06: s16 zTranslation
[cmd+0x08: void *displayList]
*/
void geo_layout_cmd_node_translation(void) {
struct GraphNodeTranslation *graphNode;
Vec3s translation;
s16 drawingLayer = 0;
s16 params = cur_geo_cmd_u8(0x01);
s16 *cmdPos = (s16 *) gGeoLayoutCommand;
void *displayList = NULL;
cmdPos = read_vec3s(translation, &cmdPos[1]);
if (params & 0x80) {
displayList = *(void **) &cmdPos[0];
drawingLayer = params & 0x0F;
cmdPos += 2 << CMD_SIZE_SHIFT;
}
graphNode =
init_graph_node_translation(gGraphNodePool, NULL, drawingLayer, displayList, translation);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand = (u8 *) cmdPos;
}
/*
0x12: Create ? scene graph node
cmd+0x01: u8 params
(params & 0x80): if set, enable displayList field and drawingLayer
(params & 0x0F): drawingLayer
cmd+0x02: s16 unkX
cmd+0x04: s16 unkY
cmd+0x06: s16 unkZ
[cmd+0x08: void *displayList]
*/
void geo_layout_cmd_node_rotation(void) {
struct GraphNodeRotation *graphNode;
Vec3s sp2c;
s16 drawingLayer = 0;
s16 params = cur_geo_cmd_u8(0x01);
s16 *cmdPos = (s16 *) gGeoLayoutCommand;
void *displayList = NULL;
cmdPos = read_vec3s_angle(sp2c, &cmdPos[1]);
if (params & 0x80) {
displayList = *(void **) &cmdPos[0];
drawingLayer = params & 0x0F;
cmdPos += 2 << CMD_SIZE_SHIFT;
}
graphNode = init_graph_node_rotation(gGraphNodePool, NULL, drawingLayer, displayList, sp2c);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand = (u8 *) cmdPos;
}
/*
0x1D: Create scale scene graph node with optional display list
cmd+0x01: u8 params
(params & 0x80): if set, enable displayList field and drawingLayer
(params & 0x0F): drawingLayer
cmd+0x04: u32 scale (0x10000 = 1.0)
[cmd+0x08: void *displayList]
*/
void geo_layout_cmd_node_scale(void) {
struct GraphNodeScale *graphNode;
s16 drawingLayer = 0;
s16 params = cur_geo_cmd_u8(0x01);
f32 scale = cur_geo_cmd_u32(0x04) / 65536.0f;
void *displayList = NULL;
if (params & 0x80) {
displayList = cur_geo_cmd_ptr(0x08);
drawingLayer = params & 0x0F;
gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT;
}
graphNode = init_graph_node_scale(gGraphNodePool, NULL, drawingLayer, displayList, scale);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
// 0x1E: No operation
void geo_layout_cmd_nop2(void) {
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
/*
0x13: Create a scene graph node that is rotated by the object's animation.
cmd+0x01: u8 drawingLayer
cmd+0x02: s16 xTranslation
cmd+0x04: s16 yTranslation
cmd+0x06: s16 zTranslation
cmd+0x08: void *displayList
*/
void geo_layout_cmd_node_animated_part(void) {
struct GraphNodeAnimatedPart *graphNode;
Vec3s translation;
s32 drawingLayer = cur_geo_cmd_u8(0x01);
void *displayList = cur_geo_cmd_ptr(0x08);
s16 *cmdPos = (s16 *) gGeoLayoutCommand;
read_vec3s(translation, &cmdPos[1]);
graphNode =
init_graph_node_animated_part(gGraphNodePool, NULL, drawingLayer, displayList, translation);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT;
}
/*
0x14: Create billboarding node with optional display list
cmd+0x01: u8 params
(params & 0x80): if set, enable displayList field and drawingLayer
(params & 0x0F): drawingLayer
cmd+0x02: s16 xTranslation
cmd+0x04: s16 yTranslation
cmd+0x06: s16 zTranslation
[cmd+0x08: void *displayList]
*/
void geo_layout_cmd_node_billboard(void) {
struct GraphNodeBillboard *graphNode;
Vec3s translation;
s16 drawingLayer = 0;
s16 params = cur_geo_cmd_u8(0x01);
s16 *cmdPos = (s16 *) gGeoLayoutCommand;
void *displayList = NULL;
cmdPos = read_vec3s(translation, &cmdPos[1]);
if (params & 0x80) {
displayList = *(void **) &cmdPos[0];
drawingLayer = params & 0x0F;
cmdPos += 2 << CMD_SIZE_SHIFT;
}
graphNode = init_graph_node_billboard(gGraphNodePool, NULL, drawingLayer, displayList, translation);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand = (u8 *) cmdPos;
}
/*
0x15: Create plain display list scene graph node
cmd+0x01: u8 drawingLayer
cmd+0x04: void *displayList
*/
void geo_layout_cmd_node_display_list(void) {
struct GraphNodeDisplayList *graphNode;
s32 drawingLayer = cur_geo_cmd_u8(0x01);
void *displayList = cur_geo_cmd_ptr(0x04);
graphNode = init_graph_node_display_list(gGraphNodePool, NULL, drawingLayer, displayList);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
/*
0x16: Create shadow scene graph node
cmd+0x02: s16 shadowType
cmd+0x04: s16 shadowSolidity
cmd+0x06: s16 shadowScale
*/
void geo_layout_cmd_node_shadow(void) {
struct GraphNodeShadow *graphNode;
u8 shadowType = cur_geo_cmd_s16(0x02);
u8 shadowSolidity = cur_geo_cmd_s16(0x04);
s16 shadowScale = cur_geo_cmd_s16(0x06);
graphNode = init_graph_node_shadow(gGraphNodePool, NULL, shadowScale, shadowSolidity, shadowType);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
// 0x17: Create scene graph node that manages the group of all object nodes
void geo_layout_cmd_node_object_parent(void) {
struct GraphNodeObjectParent *graphNode;
graphNode = init_graph_node_object_parent(gGraphNodePool, NULL, &gObjParentGraphNode);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
/*
0x18: Create dynamically generated displaylist scene graph node
cmd+0x02: s16 parameter
cmd+0x04: GraphNodeFunc func
*/
void geo_layout_cmd_node_generated(void) {
struct GraphNodeGenerated *graphNode;
graphNode = init_graph_node_generated(gGraphNodePool, NULL,
(GraphNodeFunc) cur_geo_cmd_ptr(0x04), // asm function
cur_geo_cmd_s16(0x02)); // parameter
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
/*
0x19: Create background scene graph node
cmd+0x02: s16 background // background ID, or RGBA5551 color if backgroundFunc is null
cmd+0x04: GraphNodeFunc backgroundFunc
*/
void geo_layout_cmd_node_background(void) {
struct GraphNodeBackground *graphNode;
graphNode = init_graph_node_background(
gGraphNodePool, NULL,
cur_geo_cmd_s16(0x02), // background ID, or RGBA5551 color if asm function is null
(GraphNodeFunc) cur_geo_cmd_ptr(0x04), // asm function
0);
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
// 0x1A: No operation
void geo_layout_cmd_nop(void) {
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
/*
0x1B: Copy the shared children from the object parent from a specific view
to a newly created object parent node.
cmd+0x02: s16 index (of gGeoViews)
*/
void geo_layout_cmd_copy_view(void) {
struct GraphNodeObjectParent *graphNode;
struct GraphNode *node = NULL;
s16 index = cur_geo_cmd_s16(0x02);
if (index >= 0) {
node = gGeoViews[index];
if (node->type == GRAPH_NODE_TYPE_OBJECT_PARENT) {
node = ((struct GraphNodeObjectParent *) node)->sharedChild;
} else {
node = NULL;
}
}
graphNode = init_graph_node_object_parent(gGraphNodePool, NULL, node);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
/*
0x1C: Create a held object scene graph node
cmd+0x01: u8 unused
cmd+0x02: s16 offsetX
cmd+0x04: s16 offsetY
cmd+0x06: s16 offsetZ
cmd+0x08: GraphNodeFunc nodeFunc
*/
void geo_layout_cmd_node_held_obj(void) {
struct GraphNodeHeldObject *graphNode;
Vec3s offset;
read_vec3s(offset, (s16 *) &gGeoLayoutCommand[0x02]);
graphNode = init_graph_node_held_object(
gGraphNodePool, NULL, NULL, offset, (GraphNodeFunc) cur_geo_cmd_ptr(0x08), cur_geo_cmd_u8(0x01));
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT;
}
/*
0x20: Create a scene graph node that specifies for an object the radius that
is used for frustum culling.
cmd+0x02: s16 cullingRadius
*/
void geo_layout_cmd_node_culling_radius(void) {
struct GraphNodeCullingRadius *graphNode;
graphNode = init_graph_node_culling_radius(gGraphNodePool, NULL, cur_geo_cmd_s16(0x02));
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
struct GraphNode *process_geo_layout(struct AllocOnlyPool *pool, void *segptr) {
// set by register_scene_graph_node when gCurGraphNodeIndex is 0
// and gCurRootGraphNode is NULL
gCurRootGraphNode = NULL;
gGeoNumViews = 0; // number of entries in gGeoViews
gCurGraphNodeList[0] = 0;
gCurGraphNodeIndex = 0; // incremented by cmd_open_node, decremented by cmd_close_node
gGeoLayoutStackIndex = 2;
gGeoLayoutReturnIndex = 2; // stack index is often copied here?
gGeoLayoutCommand = segmented_to_virtual(segptr);
gGraphNodePool = pool;
gGeoLayoutStack[0] = 0;
gGeoLayoutStack[1] = 0;
while (gGeoLayoutCommand != NULL) {
GeoLayoutJumpTable[gGeoLayoutCommand[0x00]]();
}
return gCurRootGraphNode;
}
+87
View File
@@ -0,0 +1,87 @@
#ifndef GEO_LAYOUT_H
#define GEO_LAYOUT_H
#include "../include/PR/ultratypes.h"
#include "../memory.h"
#include "../include/macros.h"
#include "../include/types.h"
#define GEO_CMD_FLAGS_RESET 0
#define GEO_CMD_FLAGS_SET 1
#define GEO_CMD_FLAGS_CLEAR 2
#define CMD_SIZE_SHIFT (sizeof(void *) >> 3)
#define CMD_PROCESS_OFFSET(offset) (((offset) & 3) | (((offset) & ~3) << CMD_SIZE_SHIFT))
#define cur_geo_cmd_u8(offset) \
(gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)])
#define cur_geo_cmd_s16(offset) \
(*(s16 *) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)])
#define cur_geo_cmd_s32(offset) \
(*(s32 *) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)])
#define cur_geo_cmd_u32(offset) \
(*(u32 *) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)])
#define cur_geo_cmd_ptr(offset) \
(*(void **) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)])
extern struct AllocOnlyPool *gGraphNodePool;
extern struct GraphNode *gCurRootGraphNode;
extern UNUSED s32 D_8038BCA8;
extern struct GraphNode **gGeoViews;
extern u16 gGeoNumViews;
extern uintptr_t gGeoLayoutStack[];
extern struct GraphNode *gCurGraphNodeList[];
extern s16 gCurGraphNodeIndex;
extern s16 gGeoLayoutStackIndex;
extern UNUSED s16 D_8038BD7C;
extern s16 gGeoLayoutReturnIndex;
extern u8 *gGeoLayoutCommand;
extern struct GraphNode gObjParentGraphNode;
extern struct AllocOnlyPool *D_8038BCA0;
extern struct GraphNode *D_8038BCA4;
extern s16 D_8038BD78;
extern struct GraphNode *D_8038BCF8[];
void geo_layout_cmd_branch_and_link(void);
void geo_layout_cmd_end(void);
void geo_layout_cmd_branch(void);
void geo_layout_cmd_return(void);
void geo_layout_cmd_open_node(void);
void geo_layout_cmd_close_node(void);
void geo_layout_cmd_assign_as_view(void);
void geo_layout_cmd_update_node_flags(void);
void geo_layout_cmd_node_root(void);
void geo_layout_cmd_node_ortho_projection(void);
void geo_layout_cmd_node_perspective(void);
void geo_layout_cmd_node_start(void);
void geo_layout_cmd_nop3(void);
void geo_layout_cmd_node_master_list(void);
void geo_layout_cmd_node_level_of_detail(void);
void geo_layout_cmd_node_switch_case(void);
void geo_layout_cmd_node_camera(void);
void geo_layout_cmd_node_translation_rotation(void);
void geo_layout_cmd_node_translation(void);
void geo_layout_cmd_node_rotation(void);
void geo_layout_cmd_node_scale(void);
void geo_layout_cmd_nop2(void);
void geo_layout_cmd_node_animated_part(void);
void geo_layout_cmd_node_billboard(void);
void geo_layout_cmd_node_display_list(void);
void geo_layout_cmd_node_shadow(void);
void geo_layout_cmd_node_object_parent(void);
void geo_layout_cmd_node_generated(void);
void geo_layout_cmd_node_background(void);
void geo_layout_cmd_nop(void);
void geo_layout_cmd_copy_view(void);
void geo_layout_cmd_node_held_obj(void);
void geo_layout_cmd_node_culling_radius(void);
struct GraphNode *process_geo_layout(struct AllocOnlyPool *a0, void *segptr);
#endif // GEO_LAYOUT_H
+894
View File
@@ -0,0 +1,894 @@
#include "../include/sm64.h"
#include "../game/level_update.h"
#include "math_util.h"
//#include "game/memory.h"
#include "graph_node.h"
#include "../game/rendering_graph_node.h"
#include "../game/area.h"
#include "geo_layout.h"
#include "../shim.h"
// unused Mtx(s)
static Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f };
static Vec3s gVec3sZero = { 0, 0, 0 };
static Vec3f gVec3fOne = { 1.0f, 1.0f, 1.0f };
/**
* Initialize a geo node with a given type. Sets all links such that there
* are no siblings, parent or children for this node.
*/
void init_scene_graph_node_links(struct GraphNode *graphNode, s32 type) {
graphNode->type = type;
graphNode->flags = GRAPH_RENDER_ACTIVE;
graphNode->prev = graphNode;
graphNode->next = graphNode;
graphNode->parent = NULL;
graphNode->children = NULL;
}
/**
* Allocated and returns a newly created root node
*/
struct GraphNodeRoot *init_graph_node_root(struct AllocOnlyPool *pool, struct GraphNodeRoot *graphNode,
s16 areaIndex, s16 x, s16 y, s16 width, s16 height) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRoot));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROOT);
graphNode->areaIndex = areaIndex;
graphNode->unk15 = 0;
graphNode->x = x;
graphNode->y = y;
graphNode->width = width;
graphNode->height = height;
graphNode->views = NULL;
graphNode->numViews = 0;
}
return graphNode;
}
/**
* Allocates and returns a newly created otrhographic projection node
*/
struct GraphNodeOrthoProjection *
init_graph_node_ortho_projection(struct AllocOnlyPool *pool, struct GraphNodeOrthoProjection *graphNode,
f32 scale) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeOrthoProjection));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ORTHO_PROJECTION);
graphNode->scale = scale;
}
return graphNode;
}
/**
* Allocates and returns a newly created perspective node
*/
struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *pool,
struct GraphNodePerspective *graphNode,
f32 fov, s16 near, s16 far,
GraphNodeFunc nodeFunc, s32 unused) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodePerspective));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_PERSPECTIVE);
graphNode->fov = fov;
graphNode->near = near;
graphNode->far = far;
graphNode->fnNode.func = nodeFunc;
graphNode->unused = unused;
if (nodeFunc != NULL) {
nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created start node
*/
struct GraphNodeStart *init_graph_node_start(struct AllocOnlyPool *pool,
struct GraphNodeStart *graphNode) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeStart));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_START);
}
return graphNode;
}
/**
* Allocates and returns a newly created master list node
*/
struct GraphNodeMasterList *init_graph_node_master_list(struct AllocOnlyPool *pool,
struct GraphNodeMasterList *graphNode, s16 on) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeMasterList));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_MASTER_LIST);
if (on) {
graphNode->node.flags |= GRAPH_RENDER_Z_BUFFER;
}
}
return graphNode;
}
/**
* Allocates and returns a newly created render range node
*/
struct GraphNodeLevelOfDetail *init_graph_node_render_range(struct AllocOnlyPool *pool,
struct GraphNodeLevelOfDetail *graphNode,
s16 minDistance, s16 maxDistance) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeLevelOfDetail));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_LEVEL_OF_DETAIL);
graphNode->minDistance = minDistance;
graphNode->maxDistance = maxDistance;
}
return graphNode;
}
/**
* Allocates and returns a newly created switch case node
*/
struct GraphNodeSwitchCase *init_graph_node_switch_case(struct AllocOnlyPool *pool,
struct GraphNodeSwitchCase *graphNode,
s16 numCases, s16 selectedCase,
GraphNodeFunc nodeFunc, s32 unused) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeSwitchCase));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_SWITCH_CASE);
graphNode->numCases = numCases;
graphNode->selectedCase = selectedCase;
graphNode->fnNode.func = nodeFunc;
graphNode->unused = unused;
if (nodeFunc != NULL) {
nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created camera node
*/
struct GraphNodeCamera *init_graph_node_camera(struct AllocOnlyPool *pool,
struct GraphNodeCamera *graphNode, f32 *pos,
f32 *focus, GraphNodeFunc func, s32 mode) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCamera));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_CAMERA);
vec3f_copy(graphNode->pos, pos);
vec3f_copy(graphNode->focus, focus);
graphNode->fnNode.func = func;
graphNode->config.mode = mode;
graphNode->roll = 0;
graphNode->rollScreen = 0;
if (func != NULL) {
func(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created translation rotation node
*/
struct GraphNodeTranslationRotation *
init_graph_node_translation_rotation(struct AllocOnlyPool *pool,
struct GraphNodeTranslationRotation *graphNode, s32 drawingLayer,
void *displayList, Vec3s translation, Vec3s rotation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslationRotation));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION_ROTATION);
vec3s_copy(graphNode->translation, translation);
vec3s_copy(graphNode->rotation, rotation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created translation node
*/
struct GraphNodeTranslation *init_graph_node_translation(struct AllocOnlyPool *pool,
struct GraphNodeTranslation *graphNode,
s32 drawingLayer, void *displayList,
Vec3s translation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslation));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created rotation node
*/
struct GraphNodeRotation *init_graph_node_rotation(struct AllocOnlyPool *pool,
struct GraphNodeRotation *graphNode,
s32 drawingLayer, void *displayList,
Vec3s rotation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRotation));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROTATION);
vec3s_copy(graphNode->rotation, rotation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created scaling node
*/
struct GraphNodeScale *init_graph_node_scale(struct AllocOnlyPool *pool,
struct GraphNodeScale *graphNode, s32 drawingLayer,
void *displayList, f32 scale) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeScale));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SCALE);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->scale = scale;
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created object node
*/
struct GraphNodeObject *init_graph_node_object(struct AllocOnlyPool *pool,
struct GraphNodeObject *graphNode,
struct GraphNode *sharedChild, Vec3f pos, Vec3s angle,
Vec3f scale) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObject));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT);
vec3f_copy(graphNode->pos, pos);
vec3f_copy(graphNode->scale, scale);
vec3s_copy(graphNode->angle, angle);
#ifdef USE_SYSTEM_MALLOC
// To avoid uninitialised memory usage in audio code
vec3f_copy(graphNode->cameraToObject, gVec3fZero);
#endif
graphNode->sharedChild = sharedChild;
graphNode->throwMatrix = NULL;
graphNode->animInfo.animID = 0;
graphNode->animInfo.curAnim = NULL;
graphNode->animInfo.animFrame = 0;
graphNode->animInfo.animFrameAccelAssist = 0;
graphNode->animInfo.animAccel = 0x10000;
graphNode->animInfo.animTimer = 0;
graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
}
return graphNode;
}
/**
* Allocates and returns a newly created frustum culling radius node
*/
struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct AllocOnlyPool *pool,
struct GraphNodeCullingRadius *graphNode,
s16 radius) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCullingRadius));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_CULLING_RADIUS);
graphNode->cullingRadius = radius;
}
return graphNode;
}
/**
* Allocates and returns a newly created animated part node
*/
struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool *pool,
struct GraphNodeAnimatedPart *graphNode,
s32 drawingLayer, void *displayList,
Vec3s translation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeAnimatedPart));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ANIMATED_PART);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created billboard node
*/
struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool,
struct GraphNodeBillboard *graphNode,
s32 drawingLayer, void *displayList,
Vec3s translation) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBillboard));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_BILLBOARD);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created displaylist node
*/
struct GraphNodeDisplayList *init_graph_node_display_list(struct AllocOnlyPool *pool,
struct GraphNodeDisplayList *graphNode,
s32 drawingLayer, void *displayList) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeDisplayList));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_DISPLAY_LIST);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
}
return graphNode;
}
/**
* Allocates and returns a newly created shadow node
*/
struct GraphNodeShadow *init_graph_node_shadow(struct AllocOnlyPool *pool,
struct GraphNodeShadow *graphNode, s16 shadowScale,
u8 shadowSolidity, u8 shadowType) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeShadow));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SHADOW);
graphNode->shadowScale = shadowScale;
graphNode->shadowSolidity = shadowSolidity;
graphNode->shadowType = shadowType;
}
return graphNode;
}
/**
* Allocates and returns a newly created object parent node
*/
struct GraphNodeObjectParent *init_graph_node_object_parent(struct AllocOnlyPool *pool,
struct GraphNodeObjectParent *graphNode,
struct GraphNode *sharedChild) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObjectParent));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT_PARENT);
graphNode->sharedChild = sharedChild;
}
return graphNode;
}
/**
* Allocates and returns a newly created generated node
*/
struct GraphNodeGenerated *init_graph_node_generated(struct AllocOnlyPool *pool,
struct GraphNodeGenerated *graphNode,
GraphNodeFunc gfxFunc, s32 parameter) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeGenerated));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_GENERATED_LIST);
graphNode->fnNode.func = gfxFunc;
graphNode->parameter = parameter;
if (gfxFunc != NULL) {
gfxFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created background node
*/
struct GraphNodeBackground *init_graph_node_background(struct AllocOnlyPool *pool,
struct GraphNodeBackground *graphNode,
u16 background, GraphNodeFunc backgroundFunc,
s32 zero) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBackground));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_BACKGROUND);
graphNode->background = (background << 16) | background;
graphNode->fnNode.func = backgroundFunc;
graphNode->unused = zero; // always 0, unused
if (backgroundFunc != NULL) {
backgroundFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Allocates and returns a newly created held object node
*/
struct GraphNodeHeldObject *init_graph_node_held_object(struct AllocOnlyPool *pool,
struct GraphNodeHeldObject *graphNode,
struct Object *objNode,
Vec3s translation,
GraphNodeFunc nodeFunc, s32 playerIndex) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeHeldObject));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_HELD_OBJ);
vec3s_copy(graphNode->translation, translation);
graphNode->objNode = objNode;
graphNode->fnNode.func = nodeFunc;
graphNode->playerIndex = playerIndex;
if (nodeFunc != NULL) {
nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool);
}
}
return graphNode;
}
/**
* Adds 'childNode' to the end of the list children from 'parent'
*/
struct GraphNode *geo_add_child(struct GraphNode *parent, struct GraphNode *childNode) {
struct GraphNode *parentFirstChild;
struct GraphNode *parentLastChild;
if (childNode != NULL) {
childNode->parent = parent;
parentFirstChild = parent->children;
if (parentFirstChild == NULL) {
parent->children = childNode;
childNode->prev = childNode;
childNode->next = childNode;
} else {
parentLastChild = parentFirstChild->prev;
childNode->prev = parentLastChild;
childNode->next = parentFirstChild;
parentFirstChild->prev = childNode;
parentLastChild->next = childNode;
}
}
return childNode;
}
/**
* Remove a node from the scene graph. It changes the links with its
* siblings and with its parent, it doesn't deallocate the memory
* since geo nodes are allocated in a pointer-bumping pool that
* gets thrown out when changing areas.
*/
struct GraphNode *geo_remove_child(struct GraphNode *graphNode) {
struct GraphNode *parent;
struct GraphNode **firstChild;
parent = graphNode->parent;
firstChild = &parent->children;
// Remove link with siblings
graphNode->prev->next = graphNode->next;
graphNode->next->prev = graphNode->prev;
// If this node was the first child, a new first child must be chosen
if (*firstChild == graphNode) {
// The list is circular, so this checks whether it was the only child
if (graphNode->next == graphNode) {
*firstChild = NULL; // Parent has no children anymore
} else {
*firstChild = graphNode->next; // Choose a new first child
}
}
return parent;
}
/**
* Reorders the given node so it's the first child of its parent.
* This is called on the Mario object when he is spawned. That's why Mario's
* object is always drawn before any other objects. (Note that the geo order
* is independent from processing group order, where Mario is not first.)
*/
struct GraphNode *geo_make_first_child(struct GraphNode *newFirstChild) {
struct GraphNode *lastSibling;
struct GraphNode *parent;
struct GraphNode **firstChild;
parent = newFirstChild->parent;
firstChild = &parent->children;
if (*firstChild != newFirstChild) {
if ((*firstChild)->prev != newFirstChild) {
newFirstChild->prev->next = newFirstChild->next;
newFirstChild->next->prev = newFirstChild->prev;
lastSibling = (*firstChild)->prev;
newFirstChild->prev = lastSibling;
newFirstChild->next = *firstChild;
(*firstChild)->prev = newFirstChild;
lastSibling->next = newFirstChild;
}
*firstChild = newFirstChild;
}
return parent;
}
/**
* Helper function for geo_call_global_function_nodes that recursively
* traverses the scene graph and calls the functions of global nodes.
*/
void geo_call_global_function_nodes_helper(struct GraphNode *graphNode, s32 callContext) {
struct GraphNode **globalPtr;
struct GraphNode *curNode;
struct FnGraphNode *asFnNode;
curNode = graphNode;
do {
asFnNode = (struct FnGraphNode *) curNode;
if (curNode->type & GRAPH_NODE_TYPE_FUNCTIONAL) {
if (asFnNode->func != NULL) {
asFnNode->func(callContext, curNode, NULL);
}
}
if (curNode->children != NULL) {
switch (curNode->type) {
case GRAPH_NODE_TYPE_MASTER_LIST:
globalPtr = (struct GraphNode **) &gCurGraphNodeMasterList;
break;
case GRAPH_NODE_TYPE_PERSPECTIVE:
globalPtr = (struct GraphNode **) &gCurGraphNodeCamFrustum;
break;
case GRAPH_NODE_TYPE_CAMERA:
globalPtr = (struct GraphNode **) &gCurGraphNodeCamera;
break;
case GRAPH_NODE_TYPE_OBJECT:
globalPtr = (struct GraphNode **) &gCurGraphNodeObject;
break;
default:
globalPtr = NULL;
break;
}
if (globalPtr != NULL) {
*globalPtr = curNode;
}
geo_call_global_function_nodes_helper(curNode->children, callContext);
if (globalPtr != NULL) {
*globalPtr = NULL;
}
}
} while ((curNode = curNode->next) != graphNode);
}
/**
* Call the update functions of geo nodes that are stored in global variables.
* These variables include gCurGraphNodeMasterList, gCurGraphNodeCamFrustum,
* gCurGraphNodeCamera and gCurGraphNodeObject.
* callContext is one of the GEO_CONTEXT_ defines.
* The graphNode argument should be of type GraphNodeRoot.
*/
void geo_call_global_function_nodes(struct GraphNode *graphNode, s32 callContext) {
if (graphNode->flags & GRAPH_RENDER_ACTIVE) {
gCurGraphNodeRoot = (struct GraphNodeRoot *) graphNode;
if (graphNode->children != NULL) {
geo_call_global_function_nodes_helper(graphNode->children, callContext);
}
gCurGraphNodeRoot = 0;
}
}
/**
* When objects are cleared, this is called on all object nodes (loaded or unloaded).
*/
void geo_reset_object_node(struct GraphNodeObject *graphNode) {
init_graph_node_object(NULL, graphNode, 0, gVec3fZero, gVec3sZero, gVec3fOne);
geo_add_child(&gObjParentGraphNode, &graphNode->node);
graphNode->node.flags &= ~GRAPH_RENDER_ACTIVE;
}
/**
* Initialize an object node using the given parameters
*/
void geo_obj_init(struct GraphNodeObject *graphNode, void *sharedChild, Vec3f pos, Vec3s angle) {
vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f);
vec3f_copy(graphNode->pos, pos);
vec3s_copy(graphNode->angle, angle);
graphNode->sharedChild = sharedChild;
graphNode->unk4C = 0;
graphNode->throwMatrix = NULL;
graphNode->animInfo.curAnim = NULL;
graphNode->node.flags |= GRAPH_RENDER_ACTIVE;
graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE;
graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD;
}
/**
* Initialize and object node using the given SpawnInfo struct
*/
void geo_obj_init_spawninfo(struct GraphNodeObject *graphNode, struct SpawnInfo *spawn) {
vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f);
vec3s_copy(graphNode->angle, spawn->startAngle);
graphNode->pos[0] = (f32) spawn->startPos[0];
graphNode->pos[1] = (f32) spawn->startPos[1];
graphNode->pos[2] = (f32) spawn->startPos[2];
graphNode->areaIndex = spawn->areaIndex;
graphNode->activeAreaIndex = spawn->activeAreaIndex;
graphNode->sharedChild = spawn->unk18;
graphNode->unk4C = spawn;
graphNode->throwMatrix = NULL;
graphNode->animInfo.curAnim = 0;
graphNode->node.flags |= GRAPH_RENDER_ACTIVE;
graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE;
graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION;
graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD;
}
/**
* Initialize the animation of an object node
*/
void geo_obj_init_animation(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr) {
struct Animation **animSegmented = segmented_to_virtual(animPtrAddr);
struct Animation *anim = segmented_to_virtual(*animSegmented);
if (graphNode->animInfo.curAnim != anim) {
graphNode->animInfo.curAnim = anim;
graphNode->animInfo.animFrame = anim->startFrame + ((anim->flags & ANIM_FLAG_FORWARD) ? 1 : -1);
graphNode->animInfo.animAccel = 0;
graphNode->animInfo.animYTrans = 0;
}
}
/**
* Initialize the animation of an object node
*/
void geo_obj_init_animation_accel(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr, u32 animAccel) {
struct Animation **animSegmented = segmented_to_virtual(animPtrAddr);
struct Animation *anim = segmented_to_virtual(*animSegmented);
if (graphNode->animInfo.curAnim != anim) {
graphNode->animInfo.curAnim = anim;
graphNode->animInfo.animYTrans = 0;
graphNode->animInfo.animFrameAccelAssist =
(anim->startFrame << 16) + ((anim->flags & ANIM_FLAG_FORWARD) ? animAccel : -animAccel);
graphNode->animInfo.animFrame = graphNode->animInfo.animFrameAccelAssist >> 16;
}
graphNode->animInfo.animAccel = animAccel;
}
/**
* Retrieves an index into animation data based on the attribute pointer
* An attribute is an x-, y- or z-component of the translation / rotation for a part
* Each attribute is a pair of s16's, where the first s16 represents the maximum frame
* and the second s16 the actual index. This index can be used to index in the array
* with actual animation values.
*/
s32 retrieve_animation_index(s32 frame, u16 **attributes) {
s32 result;
if (frame < (*attributes)[0]) {
result = (*attributes)[1] + frame;
} else {
result = (*attributes)[1] + (*attributes)[0] - 1;
}
*attributes += 2;
return result;
}
/**
* Update the animation frame of an object. The animation flags determine
* whether it plays forwards or backwards, and whether it stops or loops at
* the end etc.
*/
s16 geo_update_animation_frame(struct AnimInfo *obj, s32 *accelAssist) {
s32 result;
struct Animation *anim;
anim = obj->curAnim;
if (obj->animTimer == gAreaUpdateCounter || anim->flags & ANIM_FLAG_2) {
if (accelAssist != NULL) {
accelAssist[0] = obj->animFrameAccelAssist;
}
return obj->animFrame;
}
if (anim->flags & ANIM_FLAG_FORWARD) {
if (obj->animAccel) {
result = obj->animFrameAccelAssist - obj->animAccel;
} else {
result = (obj->animFrame - 1) << 16;
}
if (GET_HIGH_S16_OF_32(result) < anim->loopStart) {
if (anim->flags & ANIM_FLAG_NOLOOP) {
SET_HIGH_S16_OF_32(result, anim->loopStart);
} else {
SET_HIGH_S16_OF_32(result, anim->loopEnd - 1);
}
}
} else {
if (obj->animAccel != 0) {
result = obj->animFrameAccelAssist + obj->animAccel;
} else {
result = (obj->animFrame + 1) << 16;
}
if (GET_HIGH_S16_OF_32(result) >= anim->loopEnd) {
if (anim->flags & ANIM_FLAG_NOLOOP) {
SET_HIGH_S16_OF_32(result, anim->loopEnd - 1);
} else {
SET_HIGH_S16_OF_32(result, anim->loopStart);
}
}
}
if (accelAssist != 0) {
accelAssist[0] = result;
}
return GET_HIGH_S16_OF_32(result);
}
/**
* Unused function to retrieve an object's current animation translation
* Assumes that it has x, y and z data in animations, which isn't always the
* case since some animation types only have vertical or lateral translation.
* This might have been used for positioning the shadow under an object, which
* currently happens in-line in geo_process_shadow where it also accounts for
* animations without lateral translation.
*/
void geo_retreive_animation_translation(struct GraphNodeObject *obj, Vec3f position) {
struct Animation *animation = obj->animInfo.curAnim;
u16 *attribute;
s16 *values;
s16 frame;
if (animation != NULL) {
attribute = segmented_to_virtual((void *) animation->index);
values = segmented_to_virtual((void *) animation->values);
frame = obj->animInfo.animFrame;
if (frame < 0) {
frame = 0;
}
if (1) // ? necessary to match
{
position[0] = (f32) values[retrieve_animation_index(frame, &attribute)];
position[1] = (f32) values[retrieve_animation_index(frame, &attribute)];
position[2] = (f32) values[retrieve_animation_index(frame, &attribute)];
}
} else {
vec3f_set(position, 0, 0, 0);
}
}
/**
* Unused function to find the root of the geo node tree, which should be a
* GraphNodeRoot. If it is not for some reason, null is returned.
*/
struct GraphNodeRoot *geo_find_root(struct GraphNode *graphNode) {
struct GraphNodeRoot *resGraphNode = NULL;
while (graphNode->parent != NULL) {
graphNode = graphNode->parent;
}
if (graphNode->type == GRAPH_NODE_TYPE_ROOT) {
resGraphNode = (struct GraphNodeRoot *) graphNode;
}
return resGraphNode;
}
+434
View File
@@ -0,0 +1,434 @@
#ifndef GRAPH_NODE_H
#define GRAPH_NODE_H
#include "../include/PR/ultratypes.h"
#include "../include/PR/gbi.h"
#include "../include/types.h"
#include "../../memory.h"
#define GRAPH_RENDER_ACTIVE (1 << 0)
#define GRAPH_RENDER_CHILDREN_FIRST (1 << 1)
#define GRAPH_RENDER_BILLBOARD (1 << 2)
#define GRAPH_RENDER_Z_BUFFER (1 << 3)
#define GRAPH_RENDER_INVISIBLE (1 << 4)
#define GRAPH_RENDER_HAS_ANIMATION (1 << 5)
// Whether the node type has a function pointer of type GraphNodeFunc
#define GRAPH_NODE_TYPE_FUNCTIONAL 0x100
// Type used for Bowser and an unused geo function in obj_behaviors.c
#define GRAPH_NODE_TYPE_400 0x400
// The discriminant for different types of geo nodes
#define GRAPH_NODE_TYPE_ROOT 0x001
#define GRAPH_NODE_TYPE_ORTHO_PROJECTION 0x002
#define GRAPH_NODE_TYPE_PERSPECTIVE (0x003 | GRAPH_NODE_TYPE_FUNCTIONAL)
#define GRAPH_NODE_TYPE_MASTER_LIST 0x004
#define GRAPH_NODE_TYPE_START 0x00A
#define GRAPH_NODE_TYPE_LEVEL_OF_DETAIL 0x00B
#define GRAPH_NODE_TYPE_SWITCH_CASE (0x00C | GRAPH_NODE_TYPE_FUNCTIONAL)
#define GRAPH_NODE_TYPE_CAMERA (0x014 | GRAPH_NODE_TYPE_FUNCTIONAL)
#define GRAPH_NODE_TYPE_TRANSLATION_ROTATION 0x015
#define GRAPH_NODE_TYPE_TRANSLATION 0x016
#define GRAPH_NODE_TYPE_ROTATION 0x017
#define GRAPH_NODE_TYPE_OBJECT 0x018
#define GRAPH_NODE_TYPE_ANIMATED_PART 0x019
#define GRAPH_NODE_TYPE_BILLBOARD 0x01A
#define GRAPH_NODE_TYPE_DISPLAY_LIST 0x01B
#define GRAPH_NODE_TYPE_SCALE 0x01C
#define GRAPH_NODE_TYPE_SHADOW 0x028
#define GRAPH_NODE_TYPE_OBJECT_PARENT 0x029
#define GRAPH_NODE_TYPE_GENERATED_LIST (0x02A | GRAPH_NODE_TYPE_FUNCTIONAL)
#define GRAPH_NODE_TYPE_BACKGROUND (0x02C | GRAPH_NODE_TYPE_FUNCTIONAL)
#define GRAPH_NODE_TYPE_HELD_OBJ (0x02E | GRAPH_NODE_TYPE_FUNCTIONAL)
#define GRAPH_NODE_TYPE_CULLING_RADIUS 0x02F
// The number of master lists. A master list determines the order and render
// mode with which display lists are drawn.
#define GFX_NUM_MASTER_LISTS 8
// Passed as first argument to a GraphNodeFunc to give information about in
// which context it was called and what it is expected to do.
#define GEO_CONTEXT_CREATE 0 // called when node is created from a geo command
#define GEO_CONTEXT_RENDER 1 // called from rendering_graph_node.c
#define GEO_CONTEXT_AREA_UNLOAD 2 // called when unloading an area
#define GEO_CONTEXT_AREA_LOAD 3 // called when loading an area
#define GEO_CONTEXT_AREA_INIT 4 // called when initializing the 8 areas
#define GEO_CONTEXT_HELD_OBJ 5 // called when processing a GraphNodeHeldObj
// The signature for a function stored in a geo node
// The context argument depends on the callContext:
// - for GEO_CONTEXT_CREATE it is the AllocOnlyPool from which the node was allocated
// - for GEO_CONTEXT_RENDER or GEO_CONTEXT_HELD_OBJ it is the top of the float matrix stack with type Mat4
// - for GEO_CONTEXT_AREA_* it is the root geo node
typedef Gfx *(*GraphNodeFunc)(s32 callContext, struct GraphNode *node, void *context);
/** An extension of a graph node that includes a function pointer.
* Many graph node types have an update function that gets called
* when they are processed.
*/
struct FnGraphNode
{
/*0x00*/ struct GraphNode node;
/*0x14*/ GraphNodeFunc func;
};
/** The very root of the geo tree. Specifies the viewport.
*/
struct GraphNodeRoot
{
/*0x00*/ struct GraphNode node;
/*0x14*/ u8 areaIndex;
/*0x15*/ s8 unk15; // ?
/*0x16*/ s16 x;
/*0x18*/ s16 y;
/*0x1A*/ s16 width; // half width, 160
/*0x1C*/ s16 height; // half height
/*0x1E*/ s16 numViews; // number of entries in mystery array
/*0x20*/ struct GraphNode **views;
};
/** A node that sets up an orthographic projection based on the global
* root node. Used to draw the skybox image.
*/
struct GraphNodeOrthoProjection
{
/*0x00*/ struct GraphNode node;
/*0x14*/ f32 scale;
};
/** A node that sets up a perspective projection. Used for drawing the
* game world. It does not set up the camera position, that is done by
* the child of this node, which has type GraphNodeCamera.
*/
struct GraphNodePerspective
{
/*0x00*/ struct FnGraphNode fnNode;
/*0x18*/ s32 unused;
/*0x1C*/ f32 fov; // horizontal field of view in degrees
/*0x20*/ s16 near; // near clipping plane
/*0x22*/ s16 far; // far clipping plane
};
/** An entry in the master list. It is a linked list of display lists
* carrying a transformation matrix.
*/
struct DisplayListNode
{
Mtx *transform;
void *displayList;
struct DisplayListNode *next;
};
/** GraphNode that manages the 8 top-level display lists that will be drawn
* Each list has its own render mode, so for example water is drawn in a
* different master list than opaque objects.
* It also sets the z-buffer on before rendering and off after.
*/
struct GraphNodeMasterList
{
/*0x00*/ struct GraphNode node;
/*0x14*/ struct DisplayListNode *listHeads[GFX_NUM_MASTER_LISTS];
/*0x34*/ struct DisplayListNode *listTails[GFX_NUM_MASTER_LISTS];
};
/** Simply used as a parent to group multiple children.
* Does not have any additional functionality.
*/
struct GraphNodeStart
{
/*0x00*/ struct GraphNode node;
};
/** GraphNode that only renders its children if the current transformation matrix
* has a z-translation (in camera space) greater than minDistance and less than
* maxDistance.
* Usage examples: Mario has three level's of detail: Normal, low-poly arms only, and fully low-poly
* The tower in Whomp's fortress has two levels of detail.
*/
struct GraphNodeLevelOfDetail
{
/*0x00*/ struct GraphNode node;
/*0x14*/ s16 minDistance;
/*0x16*/ s16 maxDistance;
};
/** GraphNode that renders exactly one of its children.
* Which one is rendered is determined by the field 'selectedCase'
* which is set in the node's function.
* Usage examples: room visibility, coin animation, blinking, Mario's power-up / hand pose / cap
*/
struct GraphNodeSwitchCase
{
/*0x00*/ struct FnGraphNode fnNode;
/*0x18*/ s32 unused;
/*0x1C*/ s16 numCases;
/*0x1E*/ s16 selectedCase;
};
/**
* GraphNode that specifies the location and aim of the camera.
* When the roll is 0, the up vector is (0, 1, 0).
*/
struct GraphNodeCamera
{
/*0x00*/ struct FnGraphNode fnNode;
/*0x18*/ union {
// When the node is created, a mode is assigned to the node.
// Later in geo_camera_main a Camera is allocated,
// the mode is passed to the struct, and the field is overridden
// by a pointer to the struct. Gotta save those 4 bytes.
s32 mode;
struct Camera *camera;
} config;
/*0x1C*/ Vec3f pos;
/*0x28*/ Vec3f focus;
/*0x34*/ Mat4 *matrixPtr; // pointer to look-at matrix of this camera as a Mat4
/*0x38*/ s16 roll; // roll in look at matrix. Doesn't account for light direction unlike rollScreen.
/*0x3A*/ s16 rollScreen; // rolls screen while keeping the light direction consistent
};
/** GraphNode that translates and rotates its children.
* Usage example: wing cap wings.
* There is a dprint function that sets the translation and rotation values
* based on the ENEMYINFO array.
* The display list can be null, in which case it won't draw anything itself.
*/
struct GraphNodeTranslationRotation
{
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
/*0x18*/ Vec3s translation;
/*0x1E*/ Vec3s rotation;
};
/** GraphNode that translates itself and its children.
* Usage example: SUPER MARIO logo letters in debug level select.
* The display list can be null, in which case it won't draw anything itself.
*/
struct GraphNodeTranslation
{
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
/*0x18*/ Vec3s translation;
u8 pad1E[2];
};
/** GraphNode that rotates itself and its children.
* Usage example: Mario torso / head rotation. Its parameters are dynamically
* set by a parent script node in that case.
* The display list can be null, in which case it won't draw anything itself.
*/
struct GraphNodeRotation
{
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
/*0x18*/ Vec3s rotation;
u8 pad1E[2];
};
/** GraphNode part that transforms itself and its children based on animation
* data. This animation data is not stored in the node itself but in global
* variables that are set when object nodes are processed if the object has
* animation.
* Used for Mario, enemies and anything else with animation data.
* The display list can be null, in which case it won't draw anything itself.
*/
struct GraphNodeAnimatedPart
{
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
/*0x18*/ Vec3s translation;
};
/** A GraphNode that draws a display list rotated in a way to always face the
* camera. Note that if the entire object is a billboard (like a coin or 1-up)
* then it simply sets the billboard flag for the entire object, this node is
* used for billboard parts (like a chuckya or goomba body).
*/
struct GraphNodeBillboard
{
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
/*0x18*/ Vec3s translation;
};
/** A GraphNode that simply draws a display list without doing any
* transformation beforehand. It does inherit the parent's transformation.
*/
struct GraphNodeDisplayList
{
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
};
/** GraphNode part that scales itself and its children.
* Usage example: Mario's fist or shoe, which grows when attacking. This can't
* be done with an animated part sine animation data doesn't support scaling.
* Note that many scaling animations (like a goomba getting stomped) happen on
* the entire object. This node is only used when a single part needs to be scaled.
* There is also a level command that scales the entire level, used for THI.
* The display list can be null, in which case it won't draw anything itself.
*/
struct GraphNodeScale
{
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
/*0x18*/ f32 scale;
};
/** GraphNode that draws a shadow under an object.
* Every object starts with a shadow node.
* The shadow type determines the shape (round or rectangular), vertices (4 or 9)
* and other features.
*/
struct GraphNodeShadow
{
/*0x00*/ struct GraphNode node;
/*0x14*/ s16 shadowScale; // diameter (when a circle) or side (when a square) of shadow
/*0x16*/ u8 shadowSolidity; // opacity of shadow, 255 = opaque
/*0x17*/ u8 shadowType; // see ShadowType enum in shadow.h
};
/** GraphNode that contains as its sharedChild a group node containing all
* object nodes.
*/
struct GraphNodeObjectParent
{
/*0x00*/ struct GraphNode node;
/*0x14*/ struct GraphNode *sharedChild;
};
/** GraphNode that draws display lists not directly in memory but generated by
* a function.
* Used for wobbling paintings, water, environment effects.
* It might not draw anything, it could also just update something.
* For example: there is a node that stops water flow when the game is paused.
* The parameter field gives extra context info. For shifting sand or paintings,
* it can determine which texture to use.
*/
struct GraphNodeGenerated
{
/*0x00*/ struct FnGraphNode fnNode;
/*0x18*/ u32 parameter; // extra context for the function
};
/** GraphNode that draws a background image or a rectangle of a color.
* Drawn in an orthographic projection, used for skyboxes.
*/
struct GraphNodeBackground
{
/*0x00*/ struct FnGraphNode fnNode;
/*0x18*/ s32 unused;
/*0x1C*/ s32 background; // background ID, or rgba5551 color if fnNode.func is null
};
/** Renders the object that Mario is holding.
*/
struct GraphNodeHeldObject
{
/*0x00*/ struct FnGraphNode fnNode;
/*0x18*/ s32 playerIndex;
/*0x1C*/ struct Object *objNode;
/*0x20*/ Vec3s translation;
};
/** A node that allows an object to specify a different culling radius than the
* default one of 300. For this to work, it needs to be a direct child of the
* object node. Used for very large objects, such as shock wave rings that Bowser
* creates, tornadoes, the big eel.
*/
struct GraphNodeCullingRadius
{
/*0x00*/ struct GraphNode node;
/*0x14*/ s16 cullingRadius; // specifies the 'sphere radius' for purposes of frustum culling
u8 pad1E[2];
};
extern struct GraphNodeMasterList *gCurGraphNodeMasterList;
extern struct GraphNodePerspective *gCurGraphNodeCamFrustum;
extern struct GraphNodeCamera *gCurGraphNodeCamera;
extern struct GraphNodeHeldObject *gCurGraphNodeHeldObject;
extern u16 gAreaUpdateCounter;
extern struct GraphNode *gCurRootGraphNode;
extern struct GraphNode *gCurGraphNodeList[];
extern s16 gCurGraphNodeIndex;
//extern Vec3f gVec3fZero;
//extern Vec3s gVec3sZero;
//extern Vec3f gVec3fOne;
//extern Vec3s gVec3sOne;
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, s16 near, s16 far, GraphNodeFunc nodeFunc, s32 unused);
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);
struct GraphNodeSwitchCase *init_graph_node_switch_case(struct AllocOnlyPool *pool, struct GraphNodeSwitchCase *graphNode,
s16 numCases, s16 selectedCase, GraphNodeFunc nodeFunc, s32 unused);
struct GraphNodeCamera *init_graph_node_camera(struct AllocOnlyPool *pool, struct GraphNodeCamera *graphNode,
f32 *pos, f32 *focus, GraphNodeFunc func, s32 mode);
struct GraphNodeTranslationRotation *init_graph_node_translation_rotation(struct AllocOnlyPool *pool, struct GraphNodeTranslationRotation *graphNode,
s32 drawingLayer, void *displayList, Vec3s translation, Vec3s rotation);
struct GraphNodeTranslation *init_graph_node_translation(struct AllocOnlyPool *pool, struct GraphNodeTranslation *graphNode,
s32 drawingLayer, void *displayList, Vec3s translation);
struct GraphNodeRotation *init_graph_node_rotation(struct AllocOnlyPool *pool, struct GraphNodeRotation *graphNode,
s32 drawingLayer, void *displayList, Vec3s rotation);
struct GraphNodeScale *init_graph_node_scale(struct AllocOnlyPool *pool, struct GraphNodeScale *graphNode,
s32 drawingLayer, void *displayList, f32 scale);
struct GraphNodeObject *init_graph_node_object(struct AllocOnlyPool *pool, struct GraphNodeObject *graphNode,
struct GraphNode *sharedChild, Vec3f pos, Vec3s angle, Vec3f scale);
struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct AllocOnlyPool *pool, struct GraphNodeCullingRadius *graphNode, s16 radius);
struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool *pool, struct GraphNodeAnimatedPart *graphNode,
s32 drawingLayer, void *displayList, Vec3s translation);
struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool, struct GraphNodeBillboard *graphNode,
s32 drawingLayer, void *displayList, Vec3s translation);
struct GraphNodeDisplayList *init_graph_node_display_list(struct AllocOnlyPool *pool, struct GraphNodeDisplayList *graphNode,
s32 drawingLayer, void *displayList);
struct GraphNodeShadow *init_graph_node_shadow(struct AllocOnlyPool *pool, struct GraphNodeShadow *graphNode,
s16 shadowScale, u8 shadowSolidity, u8 shadowType);
struct GraphNodeObjectParent *init_graph_node_object_parent(struct AllocOnlyPool *pool, struct GraphNodeObjectParent *sp1c,
struct GraphNode *sharedChild);
struct GraphNodeGenerated *init_graph_node_generated(struct AllocOnlyPool *pool, struct GraphNodeGenerated *sp1c,
GraphNodeFunc gfxFunc, s32 parameter);
struct GraphNodeBackground *init_graph_node_background(struct AllocOnlyPool *pool, struct GraphNodeBackground *sp1c,
u16 background, GraphNodeFunc backgroundFunc, s32 zero);
struct GraphNodeHeldObject *init_graph_node_held_object(struct AllocOnlyPool *pool, struct GraphNodeHeldObject *sp1c,
struct Object *objNode, Vec3s translation,
GraphNodeFunc nodeFunc, s32 playerIndex);
struct GraphNode *geo_add_child(struct GraphNode *parent, struct GraphNode *childNode);
struct GraphNode *geo_remove_child(struct GraphNode *graphNode);
struct GraphNode *geo_make_first_child(struct GraphNode *newFirstChild);
void geo_call_global_function_nodes_helper(struct GraphNode *graphNode, s32 callContext);
void geo_call_global_function_nodes(struct GraphNode *graphNode, s32 callContext);
void geo_reset_object_node(struct GraphNodeObject *graphNode);
void geo_obj_init(struct GraphNodeObject *graphNode, void *sharedChild, Vec3f pos, Vec3s angle);
void geo_obj_init_spawninfo(struct GraphNodeObject *graphNode, struct SpawnInfo *spawn);
void geo_obj_init_animation(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr);
void geo_obj_init_animation_accel(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr, u32 animAccel);
s32 retrieve_animation_index(s32 frame, u16 **attributes);
s16 geo_update_animation_frame(struct AnimInfo *obj, s32 *accelAssist);
void geo_retreive_animation_translation(struct GraphNodeObject *obj, Vec3f position);
struct GraphNodeRoot *geo_find_root(struct GraphNode *graphNode);
// graph_node_manager
s16 *read_vec3s_to_vec3f(Vec3f, s16 *src);
s16 *read_vec3s(Vec3s dst, s16 *src);
s16 *read_vec3s_angle(Vec3s dst, s16 *src);
void register_scene_graph_node(struct GraphNode *graphNode);
#endif // GRAPH_NODE_H
+81
View File
@@ -0,0 +1,81 @@
#include "../include/PR/ultratypes.h"
#include "../include/types.h"
#include "geo_layout.h"
#include "graph_node.h"
#if IS_64_BIT
static s16 next_s16_in_geo_script(s16 **src) {
s16 ret;
if (((uintptr_t)(*src) & 7) == 4) {
*src += 2; // skip 32 bits
}
ret = *(*src)++;
if (((uintptr_t)(*src) & 7) == 4) {
*src += 2; // skip 32 bits
}
return ret;
}
#else
#define next_s16_in_geo_script(src) (*(*src)++)
#endif
/**
* Takes a pointer to three shorts (supplied by a geo layout script) and
* copies it to the destination float vector.
*/
s16 *read_vec3s_to_vec3f(Vec3f dst, s16 *src) {
dst[0] = next_s16_in_geo_script(&src);
dst[1] = next_s16_in_geo_script(&src);
dst[2] = next_s16_in_geo_script(&src);
return src;
}
/**
* Takes a pointer to three shorts (supplied by a geo layout script) and
* copies it to the destination vector. It's essentially a memcpy but consistent
* with the other two 'geo-script vector to internal vector' functions.
*/
s16 *read_vec3s(Vec3s dst, s16 *src) {
dst[0] = next_s16_in_geo_script(&src);
dst[1] = next_s16_in_geo_script(&src);
dst[2] = next_s16_in_geo_script(&src);
return src;
}
/**
* Takes a pointer to three angles in degrees (supplied by a geo layout script)
* and converts it to a vector of three in-game angle units in [-32768, 32767]
* range.
*/
s16 *read_vec3s_angle(Vec3s dst, s16 *src) {
dst[0] = (next_s16_in_geo_script(&src) << 15) / 180;
dst[1] = (next_s16_in_geo_script(&src) << 15) / 180;
dst[2] = (next_s16_in_geo_script(&src) << 15) / 180;
return src;
}
/**
* Add the given graph node as a child to the current top of the gfx stack:
* 'gCurGraphNodeList'. This is called from geo_layout commands to add nodes
* to the scene graph.
*/
void register_scene_graph_node(struct GraphNode *graphNode) {
if (graphNode != NULL) {
gCurGraphNodeList[gCurGraphNodeIndex] = graphNode;
if (gCurGraphNodeIndex == 0) {
if (gCurRootGraphNode == NULL) {
gCurRootGraphNode = graphNode;
}
} else {
if (gCurGraphNodeList[gCurGraphNodeIndex - 1]->type == GRAPH_NODE_TYPE_OBJECT_PARENT) {
((struct GraphNodeObjectParent *) gCurGraphNodeList[gCurGraphNodeIndex - 1])
->sharedChild = graphNode;
} else {
geo_add_child(gCurGraphNodeList[gCurGraphNodeIndex - 1], graphNode);
}
}
}
}
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
#ifndef MATH_UTIL_H
#define MATH_UTIL_H
#include "../include/PR/ultratypes.h"
#include "../include/PR/gbi.h"
#include "../include/types.h"
/*
* The sine and cosine tables overlap, but "#define gCosineTable (gSineTable +
* 0x400)" doesn't give expected codegen; gSineTable and gCosineTable need to
* be different symbols for code to match. Most likely the tables were placed
* adjacent to each other, and gSineTable cut short, such that reads overflow
* into gCosineTable.
*
* These kinds of out of bounds reads are undefined behavior, and break on
* e.g. GCC (which doesn't place the tables next to each other, and probably
* exploits array sizes for range analysis-based optimizations as well).
* Thus, for non-IDO compilers we use the standard-compliant version.
*/
extern f32 gSineTable[];
#ifdef AVOID_UB
#define gCosineTable (gSineTable + 0x400)
#else
extern f32 gCosineTable[];
#endif
#define sins(x) gSineTable[(u16) (x) >> 4]
#define coss(x) gCosineTable[(u16) (x) >> 4]
#define min(a, b) ((a) <= (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define sqr(x) ((x) * (x))
void *vec3f_copy(Vec3f dest, Vec3f src);
void *vec3f_set(Vec3f dest, f32 x, f32 y, f32 z);
void *vec3f_add(Vec3f dest, Vec3f a);
void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b);
void *vec3s_copy(Vec3s dest, Vec3s src);
void *vec3s_set(Vec3s dest, s16 x, s16 y, s16 z);
void *vec3s_add(Vec3s dest, Vec3s a);
void *vec3s_sum(Vec3s dest, Vec3s a, Vec3s b);
void *vec3s_sub(Vec3s dest, Vec3s a);
void *vec3s_to_vec3f(Vec3f dest, Vec3s a);
void *vec3f_to_vec3s(Vec3s dest, Vec3f a);
void *find_vector_perpendicular_to_plane(Vec3f dest, Vec3f a, Vec3f b, Vec3f c);
void *vec3f_cross(Vec3f dest, Vec3f a, Vec3f b);
void *vec3f_normalize(Vec3f dest);
void mtxf_copy(Mat4 dest, Mat4 src);
void mtxf_identity(Mat4 mtx);
void mtxf_translate(Mat4 dest, Vec3f b);
void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll);
void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f translate, Vec3s rotate);
void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f b, Vec3s c);
void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle);
void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw);
void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius);
void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b);
void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, Vec3f s);
void mtxf_mul_vec3s(Mat4 mtx, Vec3s b);
void mtxf_mul_vec3f(Mat4 mtx, Vec3f b);
void mtxf_to_mtx(Mtx *dest, Mat4 src);
void mtxf_rotate_xy(Mtx *mtx, s16 angle);
void get_pos_from_transform_mtx(Vec3f dest, Mat4 objMtx, Mat4 camMtx);
void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 *yaw);
void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s16 pitch, s16 yaw);
s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec);
f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec);
s16 atan2s(f32 y, f32 x);
f32 atan2f(f32 a, f32 b);
void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c);
void anim_spline_init(Vec4s *keyFrames);
s32 anim_spline_poll(Vec3f result);
// From object_helpers.c
void linear_mtxf_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v);
void linear_mtxf_transpose_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v);
#endif // MATH_UTIL_H
+415
View File
@@ -0,0 +1,415 @@
// CUSTOM/PATCH
#include "../shim.h"
#include "surface_collision.h"
#include "../include/surface_terrains.h"
#include "../../load_surfaces.h"
/**
* Iterate through the list of ceilings and find the first ceiling over a given point.
*/
static struct Surface *find_ceil_from_list( s32 x, s32 y, s32 z, f32 *pheight) {
register struct Surface *surf;
register s32 x1, z1, x2, z2, x3, z3;
struct Surface *ceil = NULL;
ceil = NULL;
// Stay in this loop until out of ceilings.
int count = loaded_surface_get_count();
for( int i = 0; i < count; ++i ) {
surf = loaded_surface_get_at_index(i);
// Do the check normally done in add_surface_to_cell
if( surf->normal.y >= -0.01f ) continue;
x1 = surf->vertex1[0];
z1 = surf->vertex1[2];
z2 = surf->vertex2[2];
x2 = surf->vertex2[0];
// Checking if point is in bounds of the triangle laterally.
if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) > 0) {
continue;
}
// Slight optimization by checking these later.
x3 = surf->vertex3[0];
z3 = surf->vertex3[2];
if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) > 0) {
continue;
}
if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) > 0) {
continue;
}
// // Determine if checking for the camera or not.
// if (gCheckingSurfaceCollisionsForCamera != 0) {
// if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) {
// continue;
// }
// }
// // Ignore camera only surfaces.
// else if (surf->type == SURFACE_CAMERA_BOUNDARY) {
// continue;
// }
{
f32 nx = surf->normal.x;
f32 ny = surf->normal.y;
f32 nz = surf->normal.z;
f32 oo = surf->originOffset;
f32 height;
// If a wall, ignore it. Likely a remnant, should never occur.
if (ny == 0.0f) {
continue;
}
// Find the ceil height at the specific point.
height = -(x * nx + nz * z + oo) / ny;
// Checks for ceiling interaction with a 78 unit buffer.
//! (Exposed Ceilings) Because any point above a ceiling counts
// as interacting with a ceiling, ceilings far below can cause
// "invisible walls" that are really just exposed ceilings.
if (y - (height - -78.0f) > 0.0f) {
continue;
}
if( height < *pheight )
{
*pheight = height;
ceil = surf;
}
}
}
//! (Surface Cucking) Since only the first ceil is returned and not the lowest,
// lower ceilings can be "cucked" by higher ceilings.
return ceil;
}
/**
* Iterate through the list of floors and find the first floor under a given point.
*/
static struct Surface *find_floor_from_list( s32 x, s32 y, s32 z, f32 *pheight) {
register struct Surface *surf;
register s32 x1, z1, x2, z2, x3, z3;
f32 nx, ny, nz;
f32 oo;
f32 height;
struct Surface *floor = NULL;
// Iterate through the list of floors until there are no more floors.
int count = loaded_surface_get_count();
for( int i = 0; i < count; ++i ) {
surf = loaded_surface_get_at_index(i);
// Do the check normally done in add_surface_to_cell
if( surf->normal.y <= 0.01f ) continue;
x1 = surf->vertex1[0];
z1 = surf->vertex1[2];
x2 = surf->vertex2[0];
z2 = surf->vertex2[2];
// Check that the point is within the triangle bounds.
if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) {
continue;
}
// To slightly save on computation time, set this later.
x3 = surf->vertex3[0];
z3 = surf->vertex3[2];
if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) {
continue;
}
if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) < 0) {
continue;
}
// // Determine if we are checking for the camera or not.
// if (gCheckingSurfaceCollisionsForCamera != 0) {
// if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) {
// continue;
// }
// }
// // If we are not checking for the camera, ignore camera only floors.
// else if (surf->type == SURFACE_CAMERA_BOUNDARY) {
// continue;
// }
nx = surf->normal.x;
ny = surf->normal.y;
nz = surf->normal.z;
oo = surf->originOffset;
// If a wall, ignore it. Likely a remnant, should never occur.
if (ny == 0.0f) {
continue;
}
// Find the height of the floor at a given location.
height = -(x * nx + nz * z + oo) / ny;
// Checks for floor interaction with a 78 unit buffer.
if (y - (height + -78.0f) < 0.0f) {
continue;
}
if( height > *pheight )
{
*pheight = height;
floor = surf;
}
}
//! (Surface Cucking) Since only the first floor is returned and not the highest,
// higher floors can be "cucked" by lower floors.
return floor;
}
static s32 find_wall_collisions_from_list( struct WallCollisionData *data) {
register struct Surface *surf;
register f32 offset;
register f32 radius = data->radius;
register f32 x = data->x;
register f32 y = data->y + data->offsetY;
register f32 z = data->z;
register f32 px, pz;
register f32 w1, w2, w3;
register f32 y1, y2, y3;
s32 numCols = 0;
// Max collision radius = 200
if (radius > 200.0f) {
radius = 200.0f;
}
// Stay in this loop until out of walls.
int count = loaded_surface_get_count();
for( int i = 0; i < count; ++i ) {
surf = loaded_surface_get_at_index(i);
// Do the check normally done in add_surface_to_cell
if( surf->normal.y < -0.01f || surf->normal.y > 0.01f ) continue;
if( surf->normal.x < -0.707f || surf->normal.x > 0.707f ) {
surf->flags |= SURFACE_FLAG_X_PROJECTION;
}
// Exclude a large number of walls immediately to optimize.
if (y < surf->lowerY || y > surf->upperY) {
continue;
}
offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset;
if (offset < -radius || offset > radius) {
continue;
}
px = x;
pz = z;
//! (Quantum Tunneling) Due to issues with the vertices walls choose and
// the fact they are floating point, certain floating point positions
// along the seam of two walls may collide with neither wall or both walls.
if (surf->flags & SURFACE_FLAG_X_PROJECTION) {
w1 = -surf->vertex1[2]; w2 = -surf->vertex2[2]; w3 = -surf->vertex3[2];
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
if (surf->normal.x > 0.0f) {
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) {
continue;
}
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) {
continue;
}
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) {
continue;
}
} else {
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) {
continue;
}
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) {
continue;
}
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) {
continue;
}
}
} else {
w1 = surf->vertex1[0]; w2 = surf->vertex2[0]; w3 = surf->vertex3[0];
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
if (surf->normal.z > 0.0f) {
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) {
continue;
}
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) {
continue;
}
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) {
continue;
}
} else {
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) {
continue;
}
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) {
continue;
}
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) {
continue;
}
}
}
// Determine if checking for the camera or not.
// if (gCheckingSurfaceCollisionsForCamera) {
// if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) {
// continue;
// }
// } else {
// // Ignore camera only surfaces.
// if (surf->type == SURFACE_CAMERA_BOUNDARY) {
// continue;
// }
// // If an object can pass through a vanish cap wall, pass through.
// if (surf->type == SURFACE_VANISH_CAP_WALLS) {
// // If an object can pass through a vanish cap wall, pass through.
// if (gCurrentObject != NULL
// && (gCurrentObject->activeFlags & ACTIVE_FLAG_MOVE_THROUGH_GRATE)) {
// continue;
// }
// // If Mario has a vanish cap, pass through the vanish cap wall.
// if (gCurrentObject != NULL && gCurrentObject == gMarioObject
// && (gMarioState->flags & MARIO_VANISH_CAP)) {
// continue;
// }
// }
// }
//! (Wall Overlaps) Because this doesn't update the x and z local variables,
// multiple walls can push mario more than is required.
data->x += surf->normal.x * (radius - offset);
data->z += surf->normal.z * (radius - offset);
//! (Unreferenced Walls) Since this only returns the first four walls,
// this can lead to wall interaction being missed. Typically unreferenced walls
// come from only using one wall, however.
if (data->numWalls < 4) {
data->walls[data->numWalls++] = surf;
}
numCols++;
}
return numCols;
}
s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius)
{
struct WallCollisionData collision;
s32 numCollisions = 0;
collision.offsetY = offsetY;
collision.radius = radius;
collision.x = *xPtr;
collision.y = *yPtr;
collision.z = *zPtr;
collision.numWalls = 0;
numCollisions = find_wall_collisions(&collision);
*xPtr = collision.x;
*yPtr = collision.y;
*zPtr = collision.z;
return numCollisions;
}
s32 find_wall_collisions(struct WallCollisionData *colData)
{
s32 numCollisions = 0;
s16 x = colData->x;
s16 z = colData->z;
colData->numWalls = 0;
if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) {
return numCollisions;
}
if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) {
return numCollisions;
}
numCollisions += find_wall_collisions_from_list(colData);
return numCollisions;
}
f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil)
{
f32 height = CELL_HEIGHT_LIMIT;
*pceil = find_ceil_from_list( posX, posY, posZ, &height );
return height;
}
struct FloorGeometry sFloorGeo;
f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometry **floorGeo)
{
struct Surface *floor;
f32 floorHeight = find_floor(xPos, yPos, zPos, &floor);
*floorGeo = NULL;
if (floor != NULL) {
sFloorGeo.normalX = floor->normal.x;
sFloorGeo.normalY = floor->normal.y;
sFloorGeo.normalZ = floor->normal.z;
sFloorGeo.originOffset = floor->originOffset;
*floorGeo = &sFloorGeo;
}
return floorHeight;
}
f32 find_floor_height(f32 x, f32 y, f32 z)
{
f32 height = FLOOR_LOWER_LIMIT;
find_floor_from_list( x, y, z, &height );
return height;
}
f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor)
{
f32 height = FLOOR_LOWER_LIMIT;
*pfloor = find_floor_from_list( xPos, yPos, zPos, &height );
return height;
}
f32 find_water_level(f32 x, f32 z)
{
return -10000.0f;
}
f32 find_poison_gas_level(f32 x, f32 z)
{
return -10000.0f;
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef SURFACE_COLLISION_H
#define SURFACE_COLLISION_H
#include "../include/PR/ultratypes.h"
#include "../include/types.h"
#define LEVEL_BOUNDARY_MAX 0x2000
#define CELL_SIZE 0x400
#define CELL_HEIGHT_LIMIT 20000.f
#define FLOOR_LOWER_LIMIT -11000.f
struct WallCollisionData
{
/*0x00*/ f32 x, y, z;
/*0x0C*/ f32 offsetY;
/*0x10*/ f32 radius;
/*0x14*/ s16 unk14;
/*0x16*/ s16 numWalls;
/*0x18*/ struct Surface *walls[4];
};
struct FloorGeometry
{
f32 unused[4]; // possibly position data?
f32 normalX;
f32 normalY;
f32 normalZ;
f32 originOffset;
};
s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius);
s32 find_wall_collisions(struct WallCollisionData *colData);
f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil);
f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometry **floorGeo);
f32 find_floor_height(f32 x, f32 y, f32 z);
f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor);
f32 find_water_level(f32 x, f32 z);
f32 find_poison_gas_level(f32 x, f32 z);
#endif // SURFACE_COLLISION_H