This commit is contained in:
n64
2019-08-25 00:46:40 -04:00
commit 89e8690857
2846 changed files with 625030 additions and 0 deletions
+812
View File
@@ -0,0 +1,812 @@
#include <ultra64.h>
#include "sm64.h"
#include "behavior_script.h"
#include "game/memory.h"
#include "graph_node.h"
#include "surface_collision.h"
#include "game/object_helpers.h"
#include "game/object_helpers2.h"
#include "game/mario.h"
#include "game/display.h"
#include "game/obj_behaviors_2.h"
#include "behavior_data.h"
#include "game/object_list_processor.h"
static u16 gRandomSeed16;
// unused
static void func_80383B70(u32 segptr) {
gBehCommand = segmented_to_virtual((void *) segptr);
gCurrentObject->stackIndex = 0;
}
u16 RandomU16(void) {
u16 temp1, temp2;
if (gRandomSeed16 == 22026)
gRandomSeed16 = 0;
temp1 = (gRandomSeed16 & 0x00FF) << 8;
temp1 = temp1 ^ gRandomSeed16;
gRandomSeed16 = ((temp1 & 0x00FF) << 8) + ((temp1 & 0xFF00) >> 8);
temp1 = ((temp1 & 0x00FF) << 1) ^ gRandomSeed16;
temp2 = (temp1 >> 1) ^ 0xFF80;
if ((temp1 & 1) == 0) {
if (temp2 == 43605)
gRandomSeed16 = 0;
else
gRandomSeed16 = temp2 ^ 0x1FF4;
} else {
gRandomSeed16 = temp2 ^ 0x8180;
}
return gRandomSeed16;
}
f32 RandomFloat(void) {
f32 rnd = RandomU16();
return rnd / (double) 0x10000;
}
s32 RandomSign(void) {
if (RandomU16() >= 0x7FFF)
return 1;
else
return -1;
}
void func_80383D68(struct Object *object) {
object->header.gfx.pos[0] = object->oPosX;
object->header.gfx.pos[1] = object->oPosY + object->oGraphYOffset;
object->header.gfx.pos[2] = object->oPosZ;
object->header.gfx.angle[0] = object->oFaceAnglePitch & 0xFFFF;
object->header.gfx.angle[1] = object->oFaceAngleYaw & 0xFFFF;
object->header.gfx.angle[2] = object->oFaceAngleRoll & 0xFFFF;
}
static void cur_object_stack_push(u32 value) {
gCurrentObject->stack[gCurrentObject->stackIndex] = value;
gCurrentObject->stackIndex++;
}
static u32 cur_object_stack_pop(void) {
u32 value;
gCurrentObject->stackIndex--;
value = gCurrentObject->stack[gCurrentObject->stackIndex];
return value;
}
static void Unknown80383E44(void) // ?
{
for (;;)
;
}
static s32 beh_cmd_unhide(void) {
obj_hide();
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_graph_clear(void) {
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_billboard(void) {
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD;
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_graph_node(void) {
s32 index = (s16)(gBehCommand[0] & 0xFFFF);
gCurrentObject->header.gfx.sharedChild = gLoadedGraphNodes[index];
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_load_chill(void) {
u32 model = gBehCommand[1];
void *arg1 = (void *) gBehCommand[2];
struct Object *object = spawn_object_at_origin(gCurrentObject, 0, model, arg1);
copy_object_pos_and_angle(object, gCurrentObject);
gBehCommand += 3;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_spawn(void) {
u32 model = gBehCommand[1];
void *arg1 = (void *) gBehCommand[2];
struct Object *object = spawn_object_at_origin(gCurrentObject, 0, model, arg1);
copy_object_pos_and_angle(object, gCurrentObject);
gCurrentObject->prevObj = object;
gBehCommand += 3;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_load_chill_param(void) {
u32 behParam = (s16)(gBehCommand[0] & 0xFFFF);
u32 model = gBehCommand[1];
void *arg2 = (void *) gBehCommand[2];
struct Object *object = spawn_object_at_origin(gCurrentObject, 0, model, arg2);
copy_object_pos_and_angle(object, gCurrentObject);
object->oBehParams2ndByte = behParam;
gBehCommand += 3;
return BEH_CONTINUE;
}
static s32 beh_cmd_deactivate(void) {
gCurrentObject->activeFlags = 0;
return BEH_BREAK;
}
static s32 beh_cmd_break(void) {
return BEH_BREAK;
}
// unused
static s32 beh_cmd_break2(void) {
return BEH_BREAK;
}
static s32 beh_cmd_call(void) {
u32 *jumpAddress;
gBehCommand++;
cur_object_stack_push((u32)(gBehCommand + 1));
jumpAddress = (u32 *) segmented_to_virtual((void *) gBehCommand[0]);
gBehCommand = jumpAddress;
return BEH_CONTINUE;
}
static s32 beh_cmd_return(void) {
gBehCommand = (u32 *) cur_object_stack_pop();
return BEH_CONTINUE;
}
static s32 beh_cmd_delay(void) {
s16 arg0 = gBehCommand[0] & 0xFFFF;
if (gCurrentObject->unk1F4 < arg0 - 1) {
gCurrentObject->unk1F4++;
} else {
gCurrentObject->unk1F4 = 0;
gBehCommand++;
}
return BEH_BREAK;
}
static s32 beh_cmd_delay_var(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s32 arg0 = cur_object_get_int(objectOffset);
if (gCurrentObject->unk1F4 < (arg0 - 1)) {
gCurrentObject->unk1F4++;
} else {
gCurrentObject->unk1F4 = 0;
gBehCommand++;
}
return BEH_BREAK;
}
static s32 beh_cmd_goto(void) {
gBehCommand++;
gBehCommand = (u32 *) segmented_to_virtual((void *) gBehCommand[0]);
return BEH_CONTINUE;
}
// unused
static s32 Behavior26(void) {
s32 value = (u8)(gBehCommand[0] >> 16) & 0xFF;
cur_object_stack_push((u32)(gBehCommand + 1));
cur_object_stack_push(value);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_begin_repeat(void) {
s32 count = (s16)(gBehCommand[0] & 0xFFFF);
cur_object_stack_push((u32)(gBehCommand + 1));
cur_object_stack_push(count);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_end_repeat(void) {
u32 count = cur_object_stack_pop();
count--;
if (count != 0) {
gBehCommand = (u32 *) cur_object_stack_pop();
cur_object_stack_push((u32) gBehCommand);
cur_object_stack_push(count);
} else {
cur_object_stack_pop();
gBehCommand++;
}
return BEH_BREAK;
}
static s32 beh_cmd_end_repeat_nobreak(void) {
u32 count = cur_object_stack_pop();
count--;
if (count != 0) {
gBehCommand = (u32 *) cur_object_stack_pop();
cur_object_stack_push((u32) gBehCommand);
cur_object_stack_push(count);
} else {
cur_object_stack_pop();
gBehCommand++;
}
return BEH_CONTINUE;
}
static s32 beh_cmd_begin_loop(void) {
cur_object_stack_push((u32)(gBehCommand + 1));
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_end_loop(void) {
gBehCommand = (u32 *) cur_object_stack_pop();
cur_object_stack_push((u32) gBehCommand);
return BEH_BREAK;
}
typedef void (*BehaviorCallProc)(void);
static s32 beh_cmd_callnative(void) {
BehaviorCallProc behavior_proc = (BehaviorCallProc) gBehCommand[1];
behavior_proc();
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_float(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
f32 value = (s16)(gBehCommand[0] & 0xFFFF);
cur_object_set_float(objectOffset, value);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_int(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s16 value = gBehCommand[0] & 0xFFFF;
cur_object_set_int(objectOffset, value);
gBehCommand++;
return BEH_CONTINUE;
}
// unused
static s32 Behavior36(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
u32 value = (s16)(gBehCommand[1] & 0xFFFF);
cur_object_set_int(objectOffset, value);
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_float_rand(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
f32 min = (s16)(gBehCommand[0] & 0xFFFF);
f32 max = (s16)(gBehCommand[1] >> 16);
cur_object_set_float(objectOffset, (max * RandomFloat()) + min);
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_int_rand(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s32 min = (s16)(gBehCommand[0] & 0xFFFF);
s32 max = (s16)(gBehCommand[1] >> 16);
cur_object_set_int(objectOffset, (s32)(max * RandomFloat()) + min);
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_int_rand_rshift(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s32 min = (s16)(gBehCommand[0] & 0xFFFF);
s32 rshift = (s16)(gBehCommand[1] >> 16);
cur_object_set_int(objectOffset, (RandomU16() >> rshift) + min);
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_add_float_rand(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
f32 min = (s16)(gBehCommand[0] & 0xFFFF);
f32 max = (s16)(gBehCommand[1] >> 16);
cur_object_set_float(objectOffset,
(cur_object_get_float(objectOffset) + min) + (max * RandomFloat()));
gBehCommand += 2;
return BEH_CONTINUE;
}
// unused
static s32 beh_cmd_obj_add_int_rand_rshift(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s32 min = (s16)(gBehCommand[0] & 0xFFFF);
s32 rshift = (s16)(gBehCommand[1] >> 16);
s32 rnd = RandomU16();
cur_object_set_int(objectOffset, (cur_object_get_int(objectOffset) + min) + (rnd >> rshift));
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_add_float(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
f32 value = (s16)(gBehCommand[0] & 0xFFFF);
cur_object_add_float(objectOffset, value);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_add_int(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s16 value = gBehCommand[0] & 0xFFFF;
cur_object_add_int(objectOffset, value);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_or_int(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s32 value = (s16)(gBehCommand[0] & 0xFFFF);
value &= 0xFFFF;
cur_object_or_int(objectOffset, value);
gBehCommand++;
return BEH_CONTINUE;
}
// unused
static s32 beh_cmd_obj_bit_clear_int(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s32 value = (s16)(gBehCommand[0] & 0xFFFF);
value = (value & 0xFFFF) ^ 0xFFFF;
cur_object_and_int(objectOffset, value);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_int32(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
cur_object_set_int(objectOffset, gBehCommand[1]);
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_animate(void) {
s32 animIndex = (u8)((gBehCommand[0] >> 16) & 0xFF);
u32 *animations = gCurrentObject->oAnimations;
geo_obj_init_animation((struct GraphNodeObject *) gCurrentObject, &animations[animIndex]);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_drop_floor(void) {
f32 x = gCurrentObject->oPosX;
f32 y = gCurrentObject->oPosY;
f32 z = gCurrentObject->oPosZ;
f32 floor = find_floor_height(x, y + 200.0f, z);
gCurrentObject->oPosY = floor;
gCurrentObject->oMoveFlags |= OBJ_MOVE_ON_GROUND;
gBehCommand++;
return BEH_CONTINUE;
}
// unused
static s32 Behavior18(void) {
/* no operation */
UNUSED u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
gBehCommand++;
return BEH_CONTINUE;
}
// unused
static s32 Behavior1A(void) {
/* no operation */
UNUSED u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
gBehCommand++;
return BEH_CONTINUE;
}
// unused
static s32 Behavior19(void) {
/* no operation */
UNUSED u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_sum_float(void) {
u32 objectOffsetDst = (u8)((gBehCommand[0] >> 16) & 0xFF);
u32 objectOffsetSrc1 = (u8)((gBehCommand[0] >> 8) & 0xFF);
u32 objectOffsetSrc2 = (u8)((gBehCommand[0]) & 0xFF);
cur_object_set_float(objectOffsetDst, cur_object_get_float(objectOffsetSrc1)
+ cur_object_get_float(objectOffsetSrc2));
gBehCommand++;
return BEH_CONTINUE;
}
// unused
static s32 beh_cmd_obj_sum_int(void) {
u32 objectOffsetDst = (u8)((gBehCommand[0] >> 16) & 0xFF);
u32 objectOffsetSrc1 = (u8)((gBehCommand[0] >> 8) & 0xFF);
u32 objectOffsetSrc2 = (u8)((gBehCommand[0]) & 0xFF);
cur_object_set_int(objectOffsetDst,
cur_object_get_int(objectOffsetSrc1) + cur_object_get_int(objectOffsetSrc2));
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_set_hitbox(void) {
s16 colSphereX = gBehCommand[1] >> 16;
s16 colSphereY = gBehCommand[1] & 0xFFFF;
gCurrentObject->hitboxRadius = colSphereX;
gCurrentObject->hitboxHeight = colSphereY;
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_float2(void) {
s16 arg0 = gBehCommand[1] >> 16;
s16 arg1 = gBehCommand[1] & 0xFFFF;
gCurrentObject->hurtboxRadius = arg0;
gCurrentObject->hurtboxHeight = arg1;
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_collision_sphere(void) {
s16 colSphereX = gBehCommand[1] >> 16;
s16 colSphereY = gBehCommand[1] & 0xFFFF;
s16 unknown = gBehCommand[2] >> 16;
gCurrentObject->hitboxRadius = colSphereX;
gCurrentObject->hitboxHeight = colSphereY;
gCurrentObject->hitboxDownOffset = unknown;
gBehCommand += 3;
return BEH_CONTINUE;
}
// unused
static s32 Behavior24(void) {
/* no operation */
UNUSED s16 arg0 = (u8)((gBehCommand[0] >> 16) & 0xFF);
UNUSED s16 arg1 = gBehCommand[0] & 0xFFFF;
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_begin(void) {
if (obj_has_behavior(bhvHauntedChair))
bhv_init_room();
if (obj_has_behavior(bhvMadPiano))
bhv_init_room();
if (obj_has_behavior(bhvMessagePanel))
gCurrentObject->oCollisionDistance = 150.0f;
gBehCommand++;
return BEH_CONTINUE;
}
static void Unknown8038556C(s32 lastIndex) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
u32 table[16];
s32 i;
for (i = 0; i <= lastIndex / 2; i += 2) {
table[i] = (s16)(gBehCommand[i + 1] >> 16);
table[i + 1] = (s16)(gBehCommand[i + 1] & 0xFFFF);
}
cur_object_set_int(objectOffset, table[(s32)(lastIndex * RandomFloat())]);
}
static s32 beh_cmd_collision_data(void) {
u32 *collisionData = segmented_to_virtual((void *) gBehCommand[1]);
gCurrentObject->collisionData = collisionData;
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_pos(void) {
gCurrentObject->oHomeX = gCurrentObject->oPosX;
gCurrentObject->oHomeY = gCurrentObject->oPosY;
gCurrentObject->oHomeZ = gCurrentObject->oPosZ;
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_interact_type(void) {
gCurrentObject->oInteractType = gBehCommand[1];
gBehCommand += 2;
return BEH_CONTINUE;
}
// unused
static s32 Behavior31(void) {
gCurrentObject->oUnk190 = gBehCommand[1];
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_scale(void) {
UNUSED u8 sp1f = (gBehCommand[0] >> 16) & 0xFF;
s16 sp1c = gBehCommand[0] & 0xFFFF;
obj_scale((f32) sp1c / 100.0f);
gBehCommand++;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_set_gravity(void) {
UNUSED f32 sp04, sp00;
gCurrentObject->oWallHitboxRadius = (f32)(s16)(gBehCommand[1] >> 16);
gCurrentObject->oGravity = (f32)(s16)(gBehCommand[1] & 0xFFFF) / 100.0f;
gCurrentObject->oBounce = (f32)(s16)(gBehCommand[2] >> 16) / 100.0f;
gCurrentObject->oDragStrength = (f32)(s16)(gBehCommand[2] & 0xFFFF) / 100.0f;
gCurrentObject->oFriction = (f32)(s16)(gBehCommand[3] >> 16) / 100.0f;
gCurrentObject->oBuoyancy = (f32)(s16)(gBehCommand[3] & 0xFFFF) / 100.0f;
// unused parameters
sp04 = (f32)(s16)(gBehCommand[4] >> 16) / 100.0f;
sp00 = (f32)(s16)(gBehCommand[4] & 0xFFFF) / 100.0f;
gBehCommand += 5;
return BEH_CONTINUE;
}
static s32 beh_cmd_obj_bit_clear_int32(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s32 flags = gBehCommand[1];
flags = flags ^ 0xFFFFFFFF;
object_and_int(gCurrentObject->parentObj, objectOffset, flags);
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_spawn_addr(void) {
struct WaterSplashParams *arg0 = (struct WaterSplashParams *) gBehCommand[1];
spawn_water_splash(gCurrentObject, arg0);
gBehCommand += 2;
return BEH_CONTINUE;
}
static s32 beh_cmd_text_anim_rate(void) {
u8 objectOffset = (gBehCommand[0] >> 16) & 0xFF;
s16 arg1 = (gBehCommand[0] & 0xFFFF);
if ((gGlobalTimer % arg1) == 0)
cur_object_add_int(objectOffset, 1);
gBehCommand++;
return BEH_CONTINUE;
}
void stub_80385BF0(void) {
// (empty function)
}
typedef s32 (*BehCommandProc)(void);
static BehCommandProc BehaviorJumpTable[] = {
beh_cmd_begin,
beh_cmd_delay,
beh_cmd_call,
beh_cmd_return,
beh_cmd_goto,
beh_cmd_begin_repeat,
beh_cmd_end_repeat,
beh_cmd_end_repeat_nobreak,
beh_cmd_begin_loop,
beh_cmd_end_loop,
beh_cmd_break,
beh_cmd_break2,
beh_cmd_callnative,
beh_cmd_obj_add_float,
beh_cmd_obj_set_float,
beh_cmd_obj_add_int,
beh_cmd_obj_set_int,
beh_cmd_obj_or_int,
beh_cmd_obj_bit_clear_int,
beh_cmd_obj_set_int_rand_rshift,
beh_cmd_obj_set_float_rand,
beh_cmd_obj_set_int_rand,
beh_cmd_obj_add_float_rand,
beh_cmd_obj_add_int_rand_rshift,
Behavior18,
Behavior19,
Behavior1A,
beh_cmd_graph_node,
beh_cmd_obj_load_chill,
beh_cmd_deactivate,
beh_cmd_obj_drop_floor,
beh_cmd_obj_sum_float,
beh_cmd_obj_sum_int,
beh_cmd_billboard,
beh_cmd_unhide,
beh_cmd_set_hitbox,
Behavior24,
beh_cmd_delay_var,
Behavior26,
beh_cmd_obj_set_int32,
beh_cmd_obj_animate,
beh_cmd_obj_load_chill_param,
beh_cmd_collision_data,
beh_cmd_collision_sphere,
beh_cmd_obj_spawn,
beh_cmd_obj_set_pos,
beh_cmd_obj_set_float2,
beh_cmd_interact_type,
beh_cmd_obj_set_gravity,
Behavior31,
beh_cmd_scale,
beh_cmd_obj_bit_clear_int32,
beh_cmd_text_anim_rate,
beh_cmd_graph_clear,
Behavior36,
beh_cmd_spawn_addr,
};
void cur_object_exec_behavior(void) {
UNUSED u32 unused;
s16 flagsLo = gCurrentObject->oFlags;
f32 distanceFromMario;
BehCommandProc behCmdFunc;
s32 behProcResult;
if (flagsLo & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) {
gCurrentObject->oDistanceToMario = dist_between_objects(gCurrentObject, gMarioObject);
distanceFromMario = gCurrentObject->oDistanceToMario;
} else {
distanceFromMario = 0.0f;
}
if (flagsLo & OBJ_FLAG_COMPUTE_ANGLE_TO_MARIO)
gCurrentObject->oAngleToMario = angle_to_object(gCurrentObject, gMarioObject);
if (gCurrentObject->oAction != gCurrentObject->oPrevAction) {
(void) (gCurrentObject->oTimer = 0, gCurrentObject->oSubAction = 0,
gCurrentObject->oPrevAction = gCurrentObject->oAction);
}
gBehCommand = gCurrentObject->behScript;
do {
behCmdFunc = BehaviorJumpTable[*gBehCommand >> 24];
behProcResult = behCmdFunc();
} while (behProcResult == BEH_CONTINUE);
gCurrentObject->behScript = gBehCommand;
if (gCurrentObject->oTimer < 0x3FFFFFFF)
gCurrentObject->oTimer++;
if (gCurrentObject->oAction != gCurrentObject->oPrevAction) {
(void) (gCurrentObject->oTimer = 0, gCurrentObject->oSubAction = 0,
gCurrentObject->oPrevAction = gCurrentObject->oAction);
}
flagsLo = (s16) gCurrentObject->oFlags;
if (flagsLo & OBJ_FLAG_0010)
obj_set_facing_to_move_angles(gCurrentObject);
if (flagsLo & OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW)
gCurrentObject->oFaceAngleYaw = gCurrentObject->oMoveAngleYaw;
if (flagsLo & OBJ_FLAG_MOVE_XZ_USING_FVEL)
obj_move_xz_using_fvel_and_yaw();
if (flagsLo & OBJ_FLAG_MOVE_Y_WITH_TERMINAL_VEL)
obj_move_y_with_terminal_vel();
if (flagsLo & OBJ_FLAG_TRANSFORM_RELATIVE_TO_PARENT)
build_object_transform_relative_to_parent(gCurrentObject);
if (flagsLo & OBJ_FLAG_0800)
func_802A2270(gCurrentObject);
if (flagsLo & OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE)
func_80383D68(gCurrentObject);
if (gCurrentObject->oRoom != -1) {
obj_enable_rendering_if_mario_in_room();
} else if ((flagsLo & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) {
if (!(flagsLo & OBJ_FLAG_ACTIVE_FROM_AFAR)) {
if (distanceFromMario > gCurrentObject->oDrawingDistance) {
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY;
} else if (gCurrentObject->oHeldState == HELD_FREE) {
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY;
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef _BEHAVIOR_SCRIPT_H_
#define _BEHAVIOR_SCRIPT_H_
#define BEH_BREAK 1
#define BEH_CONTINUE 0
#define cur_object_get_int(offset) gCurrentObject->OBJECT_FIELD_S32(offset)
#define cur_object_get_float(offset) gCurrentObject->OBJECT_FIELD_F32(offset)
#define cur_object_add_float(offset, value) gCurrentObject->OBJECT_FIELD_F32(offset) += (f32)(value)
#define cur_object_set_float(offset, value) gCurrentObject->OBJECT_FIELD_F32(offset) = (f32)(value)
#define cur_object_add_int(offset, value) gCurrentObject->OBJECT_FIELD_S32(offset) += (s32)(value)
#define cur_object_set_int(offset, value) gCurrentObject->OBJECT_FIELD_S32(offset) = (s32)(value)
#define cur_object_or_int(offset, value) gCurrentObject->OBJECT_FIELD_S32(offset) |= (s32)(value)
#define cur_object_and_int(offset, value) gCurrentObject->OBJECT_FIELD_S32(offset) &= (s32)(value)
#define object_and_int(object, offset, value) object->OBJECT_FIELD_S32(offset) &= (s32)(value)
u16 RandomU16(void);
float RandomFloat(void);
s32 RandomSign(void);
void func_80383D68(struct Object *object);
void stub_80385BF0(void);
void cur_object_exec_behavior(void);
#endif /* _BEHAVIOR_SCRIPT_H_ */
+796
View File
@@ -0,0 +1,796 @@
#include <ultra64.h>
#include "sm64.h"
#include "geo_layout.h"
#include "math_util.h"
#include "game/memory.h"
#include "graph_node.h"
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 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 preset_player //Mario cam
* geo_open_node
* geo_render_obj
* geo_assign_as_view 1 // currently unused geo command
* geo_close_node
*
* geo_camera preset_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 preset_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
u32 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;
struct GraphNode gObjParentGraphNode;
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++] = (u32) &gGeoLayoutCommand[8];
gGeoLayoutStack[gGeoLayoutStackIndex++] = (gCurGraphNodeIndex << 16) + gGeoLayoutReturnIndex;
gGeoLayoutReturnIndex = gGeoLayoutStackIndex;
gGeoLayoutCommand = (u8 *) segmented_to_virtual((void *) cur_geo_cmd_s32(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++] = (u32) &gGeoLayoutCommand[8];
}
gGeoLayoutCommand = (u8 *) segmented_to_virtual((void *) cur_geo_cmd_s32(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;
}
// 0x05: Close node
void geo_layout_cmd_close_node(void) {
gCurGraphNodeIndex--;
gGeoLayoutCommand += 0x04;
}
/*
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;
}
/*
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;
}
/*
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 =
(struct GraphNode **) alloc_only_pool_alloc(gGraphNodePool, gGeoNumViews * sizeof(void *));
graphNode->views = gGeoViews;
graphNode->numViews = gGeoNumViews;
for (i = 0; i < gGeoNumViews; i++) {
gGeoViews[i] = NULL;
}
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x0C;
}
/*
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;
}
/*
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_s32(0x08);
gGeoLayoutCommand += 0x04;
}
graphNode = init_graph_node_perspective(gGraphNodePool, NULL, (f32) fov, near, far, frustumFunc, 0);
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08;
}
/*
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;
}
// 0x1F: No operation
void geo_layout_cmd_nop3(void) {
gGeoLayoutCommand += 0x10;
}
/*
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;
}
/*
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;
}
/*
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_s32(0x04), // case update function
0);
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08;
}
/*
0x0F: Create a camera scene graph node (GraphNodeCamera)
cmd+0x02: s16 camera type (changes from course to course)
cmd+0x04: s16 fromX
cmd+0x06: s16 fromY
cmd+0x08: s16 fromZ
cmd+0x0A: s16 toX
cmd+0x0C: s16 toY
cmd+0x0E: s16 toZ
cmd+0x10: GraphNodeFunc func
*/
void geo_layout_cmd_node_camera(void) {
struct GraphNodeCamera *graphNode;
s16 *cmdPos = (s16 *) &gGeoLayoutCommand[4];
Vec3f fromPos, toPos;
cmdPos = read_vec3s_to_vec3f(fromPos, cmdPos);
cmdPos = read_vec3s_to_vec3f(toPos, cmdPos);
graphNode = init_graph_node_camera(gGraphNodePool, NULL, fromPos, toPos,
(GraphNodeFunc) cur_geo_cmd_s32(0x10), cur_geo_cmd_s16(0x02));
register_scene_graph_node(&graphNode->fnNode.node);
gGeoViews[0] = &graphNode->fnNode.node;
gGeoLayoutCommand += 0x14;
}
/*
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;
break;
}
if (params & 0x80) {
displayList = *(void **) &cmdPos[0];
drawingLayer = params & 0x0F;
cmdPos += 2;
}
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;
}
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;
}
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 = (void *) cur_geo_cmd_s32(0x08);
drawingLayer = params & 0x0F;
gGeoLayoutCommand += 0x04;
}
graphNode = init_graph_node_scale(gGraphNodePool, NULL, drawingLayer, displayList, scale);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x08;
}
// 0x1E: No operation
void geo_layout_cmd_nop2(void) {
gGeoLayoutCommand += 0x08;
}
/*
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 = (void *) cur_geo_cmd_s32(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;
}
/*
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;
}
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 = (void *) cur_geo_cmd_s32(0x04);
graphNode = init_graph_node_display_list(gGraphNodePool, NULL, drawingLayer, displayList);
register_scene_graph_node(&graphNode->node);
gGeoLayoutCommand += 0x08;
}
/*
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;
}
// 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;
}
/*
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_s32(0x04), // asm function
cur_geo_cmd_s16(0x02)); // parameter
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08;
}
/*
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_s32(0x04), // asm function
0);
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x08;
}
// 0x1A: No operation
void geo_layout_cmd_nop(void) {
gGeoLayoutCommand += 0x08;
}
/*
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;
}
/*
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, 0, offset, (GraphNodeFunc) cur_geo_cmd_s32(0x08), cur_geo_cmd_u8(0x01));
register_scene_graph_node(&graphNode->fnNode.node);
gGeoLayoutCommand += 0x0C;
}
/*
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;
}
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 = (u8 *) segmented_to_virtual(segptr);
gGraphNodePool = pool;
gGeoLayoutStack[0] = 0;
gGeoLayoutStack[1] = 0;
while (gGeoLayoutCommand != NULL) {
GeoLayoutJumpTable[gGeoLayoutCommand[0x00]]();
}
return gCurRootGraphNode;
}
+77
View File
@@ -0,0 +1,77 @@
#ifndef _GEO_LAYOUT_H_
#define _GEO_LAYOUT_H_
#include "game/memory.h"
#define GEO_CMD_FLAGS_RESET 0
#define GEO_CMD_FLAGS_SET 1
#define GEO_CMD_FLAGS_CLEAR 2
#define cur_geo_cmd_u8(offset) \
(gGeoLayoutCommand[offset])
#define cur_geo_cmd_s16(offset) \
(*(s16 *) &gGeoLayoutCommand[offset])
#define cur_geo_cmd_s32(offset) \
(*(s32 *) &gGeoLayoutCommand[offset])
#define cur_geo_cmd_u32(offset) \
(*(u32 *) &gGeoLayoutCommand[offset])
extern struct AllocOnlyPool *gGraphNodePool;
extern struct GraphNode *gCurRootGraphNode;
extern UNUSED s32 D_8038BCA8;
extern struct GraphNode **gGeoViews;
extern u16 gGeoNumViews;
extern u32 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_ */
+856
View File
@@ -0,0 +1,856 @@
#include <ultra64.h>
#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"
// unused Mtx(s)
s16 identityMtx[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
s16 zeroMtx[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f };
Vec3s gVec3sZero = { 0, 0, 0 };
Vec3f gVec3fOne = { 1.0f, 1.0f, 1.0f };
UNUSED Vec3s gVec3sOne = { 1, 1, 1 };
/** 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 *fromPos,
f32 *toPos, GraphNodeFunc func, s32 preset) {
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->from, fromPos);
vec3f_copy(graphNode->to, toPos);
graphNode->fnNode.func = func;
graphNode->config.preset = preset;
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);
graphNode->sharedChild = sharedChild;
graphNode->throwMatrix = NULL;
graphNode->unk38.animID = 0;
graphNode->unk38.curAnim = NULL;
graphNode->unk38.animFrame = 0;
graphNode->unk38.animFrameAccelAssist = 0;
graphNode->unk38.animAccel = 0x10000;
graphNode->unk38.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,
s32 objNode, Vec3s translation,
GraphNodeFunc nodeFunc, s32 unused) {
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 = (struct GraphNodeObject *) objNode; // assumed type
graphNode->fnNode.func = nodeFunc;
graphNode->unused = unused;
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->unk38.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->unk18 = spawn->areaIndex;
graphNode->unk19 = spawn->activeAreaIndex;
graphNode->sharedChild = spawn->unk18;
graphNode->unk4C = spawn;
graphNode->throwMatrix = NULL;
graphNode->unk38.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, void *sp34) {
void **animSegmented = segmented_to_virtual(sp34);
struct Animation *anim = segmented_to_virtual(animSegmented[0]);
if (graphNode->unk38.curAnim != anim) {
graphNode->unk38.curAnim = anim;
graphNode->unk38.animFrame = (anim->unk04) + ((anim->flags & ANIM_FLAG_FORWARD) ? 1 : -1);
graphNode->unk38.animAccel = 0;
graphNode->unk38.animYTrans = 0;
}
}
/** Initialize the animation of an object node
*/
void geo_obj_init_animation_accel(struct GraphNodeObject *graphNode, void *sp34, u32 animAccel) {
void **animSegmented = segmented_to_virtual(sp34);
struct Animation *anim = segmented_to_virtual(animSegmented[0]);
if (graphNode->unk38.curAnim != anim) {
graphNode->unk38.curAnim = anim;
graphNode->unk38.animYTrans = 0;
graphNode->unk38.animFrameAccelAssist =
(anim->unk04 << 16) + ((anim->flags & ANIM_FLAG_FORWARD) ? animAccel : -animAccel);
graphNode->unk38.animFrame = graphNode->unk38.animFrameAccelAssist >> 16;
}
graphNode->unk38.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 GraphNodeObject_sub *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->unk06) {
if (anim->flags & ANIM_FLAG_NOLOOP) {
SET_HIGH_S16_OF_32(result, anim->unk06);
} else {
SET_HIGH_S16_OF_32(result, anim->unk08 - 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->unk08) {
if (anim->flags & ANIM_FLAG_NOLOOP) {
SET_HIGH_S16_OF_32(result, anim->unk08 - 1);
} else {
SET_HIGH_S16_OF_32(result, anim->unk06);
}
}
}
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->unk38.curAnim;
u16 *attribute;
s16 *values;
s16 frame;
if (animation != NULL) {
attribute = segmented_to_virtual(animation->index);
values = segmented_to_virtual(animation->values);
frame = obj->unk38.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;
}
+432
View File
@@ -0,0 +1,432 @@
#ifndef _GRAPH_NODE_H_
#define _GRAPH_NODE_H_
#include "types.h"
#include "game/memory.h"
struct AllocOnlyPool;
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 struct GraphNode gObjParentGraphNode;
extern Vec3f gVec3fZero;
extern Vec3s gVec3sZero;
extern Vec3f gVec3fOne;
extern Vec3s gVec3sOne;
#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
// 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 s32 (*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
{
void *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 preset is assigned to the node.
// Later in geo_camera_preset_and_pos a LevelCamera is allocated,
// the preset is passed to the struct, and the field is overridden
// by a pointer to the struct. Gotta save those 4 bytes.
s32 preset;
struct LevelCamera *levelCamera;
} config;
/*0x1C*/ Vec3f from;
/*0x28*/ Vec3f to;
/*0x34*/ void *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.
* Usef 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 orthgraphic 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 unused;
/*0x1C*/ struct GraphNodeObject *objNode; // assumed type
/*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 shockwave rings that Bowser
* creates, tornados, the big eel.
*/
struct GraphNodeCullingRadius
{
/*0x00*/ struct GraphNode node;
/*0x14*/ s16 cullingRadius; // specifies the 'sphere radius' for purposes of frustrum culling
u8 pad1E[2];
};
void init_scene_graph_node_links(struct GraphNode *, s32);
struct GraphNodeRoot *init_graph_node_root(struct AllocOnlyPool *, struct GraphNodeRoot *,
s16, s16 x, s16 y, s16 width, s16 height);
struct GraphNodeOrthoProjection *init_graph_node_ortho_projection(struct AllocOnlyPool *, struct GraphNodeOrthoProjection *, f32);
struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *pool, struct GraphNodePerspective *sp1c,
f32 sp20, s16 sp26, s16 sp2a, GraphNodeFunc sp2c, s32 sp30);
struct GraphNodeStart *init_graph_node_start(struct AllocOnlyPool *pool, struct GraphNodeStart *sp1c);
struct GraphNodeMasterList *init_graph_node_master_list(struct AllocOnlyPool *pool, struct GraphNodeMasterList *, s16 sp22);
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 sp26, GraphNodeFunc nodeFunc, s32 sp2c);
struct GraphNodeCamera *init_graph_node_camera(struct AllocOnlyPool *pool, struct GraphNodeCamera * sp1c,
f32 *sp20, f32 *sp24, GraphNodeFunc sp28, s32 sp2c);
struct GraphNodeTranslationRotation *init_graph_node_translation_rotation(struct AllocOnlyPool *pool,
struct GraphNodeTranslationRotation *graphNode, s32 drawingLayer, void *displayList, Vec3s sp28, Vec3s sp2c);
struct GraphNodeTranslation *init_graph_node_translation(struct AllocOnlyPool *pool, struct GraphNodeTranslation *graphNode,
s32 drawingLayer, void *displayList, Vec3s sp28);
struct GraphNodeRotation *init_graph_node_rotation(struct AllocOnlyPool *pool, struct GraphNodeRotation *graphNode,
s32 drawingLayer, void *displayList, Vec3s sp28);
struct GraphNodeScale *init_graph_node_scale(struct AllocOnlyPool *pool,
struct GraphNodeScale *graphNode, s32 drawingLayer, void *displayList, f32 sp28);
struct GraphNodeObject *init_graph_node_object(struct AllocOnlyPool *pool, struct GraphNodeObject *graphNode,
struct GraphNode *sp20, Vec3f pos, Vec3s angle, Vec3f scale);
struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct AllocOnlyPool *pool, struct GraphNodeCullingRadius *sp1c,
s16 sp22);
struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool *pool, struct GraphNodeAnimatedPart * graphNode,
s32 drawingLayer, void *displayList, Vec3s relativePos);
struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool,
struct GraphNodeBillboard *graphNode, s32 drawingLayer, void *displayList, Vec3s sp28);
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 *sp1c,
s16 sp22, u8 sp27, u8 sp2b);
struct GraphNodeObjectParent *init_graph_node_object_parent(struct AllocOnlyPool *pool, struct GraphNodeObjectParent *sp1c,
struct GraphNode *sp20);
struct GraphNodeGenerated *init_graph_node_generated(struct AllocOnlyPool *pool, struct GraphNodeGenerated *sp1c,
GraphNodeFunc sp20, s32 sp24);
struct GraphNodeBackground *init_graph_node_background(struct AllocOnlyPool *pool, struct GraphNodeBackground *sp1c,
u16 sp22, GraphNodeFunc sp24, s32 sp28);
struct GraphNodeHeldObject *init_graph_node_held_object(struct AllocOnlyPool *pool, struct GraphNodeHeldObject *sp1c,
s32 sp20, Vec3s sp24, GraphNodeFunc sp28, s32 sp2c);
struct GraphNode *geo_add_child(struct GraphNode *, struct GraphNode *);
struct GraphNode *geo_remove_child(struct GraphNode *);
struct GraphNode *geo_make_first_child(struct GraphNode *a0);
void geo_call_global_function_nodes_helper(struct GraphNode *, s32);
void geo_call_global_function_nodes(struct GraphNode *graphNode, s32 sp1c);
void geo_reset_object_node(struct GraphNodeObject *sp20);
void geo_obj_init(struct GraphNodeObject *sp18, void *sp1c, Vec3f sp20, Vec3s sp24);
void geo_obj_init_spawninfo(struct GraphNodeObject *sp18, struct SpawnInfo *sp1c);
void geo_obj_init_animation(struct GraphNodeObject *, void *);
void geo_obj_init_animation_accel(struct GraphNodeObject *sp30, void *sp34, u32 sp38);
s32 retrieve_animation_index(s32 a0, u16 **a1);
s16 geo_update_animation_frame(struct GraphNodeObject_sub *a0, s32* a1);
void geo_retreive_animation_translation(struct GraphNodeObject *sp28, Vec3f sp2c);
struct GraphNodeRoot *geo_find_root(struct GraphNode *graphNode);
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 *);
#endif /* _GRAPH_NODE_H_ */
+65
View File
@@ -0,0 +1,65 @@
#include <ultra64.h>
#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"
/** 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] = *src++;
dst[1] = *src++;
dst[2] = *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] = *src++;
dst[1] = *src++;
dst[2] = *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] = ((*src++) << 15) / 180;
dst[1] = ((*src++) << 15) / 180;
dst[2] = ((*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);
}
}
}
}
+792
View File
@@ -0,0 +1,792 @@
#include <ultra64.h>
#include "sm64.h"
#include "audio/external.h"
#include "game/display.h"
#include "game/game.h"
#include "geo_layout.h"
#include "graph_node.h"
#include "level_script.h"
#include "game/mario.h"
#include "math_util.h"
#include "game/memory.h"
#include "game/object_helpers.h"
#include "game/object_list_processor.h"
#include "game/area.h"
#include "game/save_file.h"
#include "game/sound_init.h"
#include "surface_collision.h"
#include "surface_load.h"
#include "goddard/renderer.h"
#include "game/profiler.h"
#define CMD_GET(type, offset) (*(type *) (offset + (u8 *) sCurrentCmd))
// These are equal
#define CMD_NEXT ((struct LevelCommand *) ((u8 *) sCurrentCmd + sCurrentCmd->size))
#define NEXT_CMD ((struct LevelCommand *) (sCurrentCmd->size + (u8 *) sCurrentCmd))
struct LevelCommand {
/*00*/ u8 type;
/*01*/ u8 size;
/*02*/ // variable sized argument data
};
enum ScriptStatus { SCRIPT_RUNNING = 1, SCRIPT_PAUSED = 0, SCRIPT_PAUSED2 = -1 };
static u32 sStack[32];
static struct AllocOnlyPool *sLevelPool = NULL;
static u16 sDelayFrames = 0;
static u16 sDelayFrames2 = 0;
static s16 sCurrAreaIndex = -1;
static u32 *sStackTop = sStack;
static u32 *sStackBase = NULL;
static s16 sScriptStatus;
static s32 sRegister;
static struct LevelCommand *sCurrentCmd;
static s32 eval_script_op(s8 op, s32 arg) {
s32 result = 0;
switch (op) {
case 0:
result = sRegister & arg;
break;
case 1:
result = !(sRegister & arg);
break;
case 2:
result = sRegister == arg;
break;
case 3:
result = sRegister != arg;
break;
case 4:
result = sRegister < arg;
break;
case 5:
result = sRegister <= arg;
break;
case 6:
result = sRegister > arg;
break;
case 7:
result = sRegister >= arg;
break;
}
return result;
}
static void level_cmd_load_and_execute(void) {
main_pool_push_state();
load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8), MEMORY_POOL_LEFT);
*sStackTop++ = (u32) NEXT_CMD;
*sStackTop++ = (u32) sStackBase;
sStackBase = sStackTop;
sCurrentCmd = segmented_to_virtual(CMD_GET(void *, 12));
}
static void level_cmd_exit_and_execute(void) {
void *targetAddr = CMD_GET(void *, 12);
main_pool_pop_state();
main_pool_push_state();
load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8), MEMORY_POOL_LEFT);
sStackTop = sStackBase;
sCurrentCmd = (struct LevelCommand *) segmented_to_virtual(targetAddr);
}
static void level_cmd_exit(void) {
main_pool_pop_state();
sStackTop = sStackBase;
sStackBase = (u32 *) *(--sStackTop);
sCurrentCmd = (struct LevelCommand *) *(--sStackTop);
}
static void level_cmd_sleep(void) {
sScriptStatus = SCRIPT_PAUSED;
if (sDelayFrames == 0) {
sDelayFrames = CMD_GET(s16, 2);
} else if (--sDelayFrames == 0) {
sCurrentCmd = CMD_NEXT;
sScriptStatus = SCRIPT_RUNNING;
}
}
static void level_cmd_sleep2(void) {
sScriptStatus = SCRIPT_PAUSED2;
if (sDelayFrames2 == 0) {
sDelayFrames2 = CMD_GET(s16, 2);
} else if (--sDelayFrames2 == 0) {
sCurrentCmd = CMD_NEXT;
sScriptStatus = SCRIPT_RUNNING;
}
}
static void level_cmd_jump(void) {
sCurrentCmd = (struct LevelCommand *) segmented_to_virtual(CMD_GET(void *, 4));
}
static void level_cmd_jump_and_link(void) {
*sStackTop++ = (u32) NEXT_CMD;
sCurrentCmd = (struct LevelCommand *) segmented_to_virtual(CMD_GET(void *, 4));
}
static void level_cmd_return(void) {
sCurrentCmd = (struct LevelCommand *) *(--sStackTop);
}
static void level_cmd_jump_and_link_push_arg(void) {
*sStackTop++ = (u32) NEXT_CMD;
*sStackTop++ = CMD_GET(s16, 2);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_jump_repeat(void) {
s32 val = *(sStackTop - 1);
if (val == 0) {
sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
} else if (--val != 0) {
*(sStackTop - 1) = val;
sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
} else {
sCurrentCmd = CMD_NEXT;
sStackTop -= 2;
}
}
static void level_cmd_loop_begin(void) {
*sStackTop++ = (u32) NEXT_CMD;
*sStackTop++ = 0;
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_loop_until(void) {
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
sCurrentCmd = CMD_NEXT;
sStackTop -= 2;
} else {
sCurrentCmd = (struct LevelCommand *) *(sStackTop - 2);
}
}
static void level_cmd_jump_if(void) {
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
sCurrentCmd = (struct LevelCommand *) segmented_to_virtual(CMD_GET(void *, 8));
} else {
sCurrentCmd = CMD_NEXT;
}
}
static void level_cmd_jump_and_link_if(void) {
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) != 0) {
*sStackTop++ = (u32) NEXT_CMD;
sCurrentCmd = (struct LevelCommand *) segmented_to_virtual(CMD_GET(void *, 8));
} else {
sCurrentCmd = CMD_NEXT;
}
}
static void level_cmd_skip_if(void) {
if (eval_script_op(CMD_GET(u8, 2), CMD_GET(s32, 4)) == 0) {
do {
sCurrentCmd = CMD_NEXT;
} while (sCurrentCmd->type == 0x0F || sCurrentCmd->type == 0x10);
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_skip(void) {
do {
sCurrentCmd = CMD_NEXT;
} while (sCurrentCmd->type == 0x10);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_skippable_nop(void) {
sCurrentCmd = CMD_NEXT;
}
// Converting data pointer to function pointer
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
static void level_cmd_call(void) {
s32 (*func)(s16, s32) = CMD_GET(void *, 4);
sRegister = func(CMD_GET(s16, 2), sRegister);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_call_loop(void) {
s32 (*func)(s16, s32) = CMD_GET(void *, 4);
sRegister = func(CMD_GET(s16, 2), sRegister);
if (sRegister == 0) {
sScriptStatus = SCRIPT_PAUSED;
} else {
sScriptStatus = SCRIPT_RUNNING;
sCurrentCmd = CMD_NEXT;
}
}
#pragma GCC diagnostic pop
static void level_cmd_set_register(void) {
sRegister = CMD_GET(s16, 2);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_push_pool_state(void) {
main_pool_push_state();
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_pop_pool_state(void) {
main_pool_pop_state();
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_load_to_fixed_address(void) {
load_to_fixed_pool_addr(CMD_GET(void *, 4), CMD_GET(void *, 8), CMD_GET(void *, 12));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_load_segment(void) {
load_segment(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8), MEMORY_POOL_LEFT);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_load_compressed_segment(void) {
load_segment_decompress(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_19(void) {
// TODO: Fix these hardcoded sizes
void *addr = main_pool_alloc(0xE1000, MEMORY_POOL_LEFT);
if (addr != NULL) {
gdm_init(addr, 0xE1000);
gd_add_to_heap(gZBuffer, 0x25800);
gd_add_to_heap(gFrameBuffer0, 0x70800);
gdm_setup();
gdm_maketestdl(CMD_GET(s16, 2));
} else {
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_1A(void) {
func_80278304(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_init_level(void) {
init_graph_node_start(NULL, (struct GraphNodeStart *) &gObjParentGraphNode);
clear_objects();
clear_areas();
main_pool_push_state();
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_clear_level(void) {
clear_objects();
func_8027A7C4();
clear_areas();
main_pool_pop_state();
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_alloc_level_pool(void) {
if (sLevelPool == NULL) {
sLevelPool = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool),
MEMORY_POOL_LEFT);
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_free_level_pool(void) {
s32 i;
alloc_only_pool_resize(sLevelPool, sLevelPool->usedSpace);
sLevelPool = NULL;
for (i = 0; i < 8; i++) {
if (gAreaData[i].terrainData != NULL) {
alloc_surface_pools();
break;
}
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_begin_area(void) {
u8 areaIndex = CMD_GET(u8, 2);
void *geoLayoutAddr = CMD_GET(void *, 4);
if (areaIndex < 8) {
struct GraphNodeRoot *screenArea =
(struct GraphNodeRoot *) process_geo_layout(sLevelPool, geoLayoutAddr);
struct GraphNodeCamera *node = (struct GraphNodeCamera *) screenArea->views[0];
sCurrAreaIndex = areaIndex;
screenArea->areaIndex = areaIndex;
gAreas[areaIndex].unk04 = (struct GraphNode *) screenArea;
if (node != NULL)
gAreas[areaIndex].camera = (struct LevelCamera *) node->config.levelCamera;
else
gAreas[areaIndex].camera = NULL;
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_end_area(void) {
sCurrAreaIndex = -1;
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_21(void) {
s16 val1 = CMD_GET(s16, 2) & 0x0FFF;
s16 val2 = CMD_GET(u16, 2) >> 12;
void *val3 = CMD_GET(void *, 4);
if (val1 < 256)
gLoadedGraphNodes[val1] =
(struct GraphNode *) init_graph_node_display_list(sLevelPool, 0, val2, val3);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_22(void) {
s16 arg0 = CMD_GET(s16, 2);
void *arg1 = CMD_GET(void *, 4);
if (arg0 < 256)
gLoadedGraphNodes[arg0] = process_geo_layout(sLevelPool, arg1);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_23(void) {
union {
s32 i;
f32 f;
} arg2;
s16 model = CMD_GET(s16, 2) & 0x0FFF;
s16 arg0H = CMD_GET(u16, 2) >> 12;
void *arg1 = CMD_GET(void *, 4);
arg2.i = CMD_GET(s32, 8); // store the raw word as a union s32. this allows is to reinterpret the
// contents as a f32 without the value being converted implicitly.
if (model < 256)
// GraphNodeScale has a GraphNode at the top. This
// is being stored to the array, so cast the pointer.
gLoadedGraphNodes[model] =
(struct GraphNode *) init_graph_node_scale(sLevelPool, 0, arg0H, arg1, arg2.f);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_init_mario(void) {
vec3s_set(gMarioSpawnInfo->startPos, 0, 0, 0);
vec3s_set(gMarioSpawnInfo->startAngle, 0, 0, 0);
gMarioSpawnInfo->activeAreaIndex = -1;
gMarioSpawnInfo->areaIndex = 0;
gMarioSpawnInfo->behaviorArg = CMD_GET(u32, 4);
gMarioSpawnInfo->behaviorScript = CMD_GET(void *, 8);
gMarioSpawnInfo->unk18 = gLoadedGraphNodes[CMD_GET(u8, 3)];
gMarioSpawnInfo->next = NULL;
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_place_object(void) {
u8 val7 = 1 << (gCurrActNum - 1);
u16 model;
struct SpawnInfo *spawnInfo;
if (sCurrAreaIndex != -1 && ((CMD_GET(u8, 2) & val7) || CMD_GET(u8, 2) == 0x1F)) {
model = CMD_GET(u8, 3);
spawnInfo = alloc_only_pool_alloc(sLevelPool, sizeof(struct SpawnInfo));
spawnInfo->startPos[0] = CMD_GET(s16, 4);
spawnInfo->startPos[1] = CMD_GET(s16, 6);
spawnInfo->startPos[2] = CMD_GET(s16, 8);
spawnInfo->startAngle[0] = CMD_GET(s16, 10) * 0x8000 / 180;
spawnInfo->startAngle[1] = CMD_GET(s16, 12) * 0x8000 / 180;
spawnInfo->startAngle[2] = CMD_GET(s16, 14) * 0x8000 / 180;
spawnInfo->areaIndex = sCurrAreaIndex;
spawnInfo->activeAreaIndex = sCurrAreaIndex;
spawnInfo->behaviorArg = CMD_GET(u32, 16);
spawnInfo->behaviorScript = CMD_GET(void *, 20);
spawnInfo->unk18 = gLoadedGraphNodes[model];
spawnInfo->next = gAreas[sCurrAreaIndex].objectSpawnInfos;
gAreas[sCurrAreaIndex].objectSpawnInfos = spawnInfo;
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_create_warp_node(void) {
if (sCurrAreaIndex != -1) {
struct ObjectWarpNode *warpNode =
alloc_only_pool_alloc(sLevelPool, sizeof(struct ObjectWarpNode));
warpNode->node.id = CMD_GET(u8, 2);
warpNode->node.destLevel = CMD_GET(u8, 3) + CMD_GET(u8, 6);
warpNode->node.destArea = CMD_GET(u8, 4);
warpNode->node.destNode = CMD_GET(u8, 5);
warpNode->object = NULL;
warpNode->next = gAreas[sCurrAreaIndex].warpNodes;
gAreas[sCurrAreaIndex].warpNodes = warpNode;
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_create_instant_warp(void) {
s32 i;
struct InstantWarp *warp;
if (sCurrAreaIndex != -1) {
if (gAreas[sCurrAreaIndex].instantWarps == NULL) {
gAreas[sCurrAreaIndex].instantWarps =
alloc_only_pool_alloc(sLevelPool, 4 * sizeof(struct InstantWarp));
for (i = INSTANT_WARP_INDEX_START; i < INSTANT_WARP_INDEX_STOP; i++)
gAreas[sCurrAreaIndex].instantWarps[i].id = 0;
}
warp = gAreas[sCurrAreaIndex].instantWarps + CMD_GET(u8, 2);
warp[0].id = 1;
warp[0].area = CMD_GET(u8, 3);
warp[0].displacement[0] = CMD_GET(s16, 4);
warp[0].displacement[1] = CMD_GET(s16, 6);
warp[0].displacement[2] = CMD_GET(s16, 8);
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_terrain_type(void) {
if (sCurrAreaIndex != -1)
gAreas[sCurrAreaIndex].terrainType |= CMD_GET(s16, 2);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_create_painting_warp_node(void) {
s32 i;
struct WarpNode *node;
if (sCurrAreaIndex != -1) {
if (gAreas[sCurrAreaIndex].paintingWarpNodes == NULL) {
gAreas[sCurrAreaIndex].paintingWarpNodes =
alloc_only_pool_alloc(sLevelPool, 45 * sizeof(struct WarpNode));
for (i = 0; i < 45; i++)
gAreas[sCurrAreaIndex].paintingWarpNodes[i].id = 0;
}
node = &gAreas[sCurrAreaIndex].paintingWarpNodes[CMD_GET(u8, 2)];
node->id = 1;
node->destLevel = CMD_GET(u8, 3) + CMD_GET(u8, 6);
node->destArea = CMD_GET(u8, 4);
node->destNode = CMD_GET(u8, 5);
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_3A(void) {
struct UnusedArea28 *val4;
if (sCurrAreaIndex != -1) {
if ((val4 = gAreas[sCurrAreaIndex].unused28) == NULL)
val4 = gAreas[sCurrAreaIndex].unused28 =
alloc_only_pool_alloc(sLevelPool, sizeof(struct UnusedArea28));
val4->unk00 = CMD_GET(s16, 2);
val4->unk02 = CMD_GET(s16, 4);
val4->unk04 = CMD_GET(s16, 6);
val4->unk06 = CMD_GET(s16, 8);
val4->unk08 = CMD_GET(s16, 10);
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_create_whirlpool(void) {
struct Whirlpool *whirlpool;
s32 index = CMD_GET(u8, 2);
s32 beatBowser2 =
(save_file_get_flags() & (SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) != 0;
if (CMD_GET(u8, 3) == 0 || (CMD_GET(u8, 3) == 1 && !beatBowser2)
|| (CMD_GET(u8, 3) == 2 && beatBowser2) || (CMD_GET(u8, 3) == 3 && gCurrActNum >= 2)) {
if (sCurrAreaIndex != -1 && index < 2) {
if ((whirlpool = gAreas[sCurrAreaIndex].whirlpools[index]) == NULL) {
whirlpool = alloc_only_pool_alloc(sLevelPool, sizeof(struct Whirlpool));
gAreas[sCurrAreaIndex].whirlpools[index] = whirlpool;
}
vec3s_set(whirlpool->pos, CMD_GET(s16, 4), CMD_GET(s16, 6), CMD_GET(s16, 8));
whirlpool->strength = CMD_GET(s16, 10);
}
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_blackout(void) {
osViBlack(CMD_GET(u8, 2));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_gamma(void) {
osViSetSpecialFeatures(CMD_GET(u8, 2) == 0 ? OS_VI_GAMMA_OFF : OS_VI_GAMMA_ON);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_terrain_data(void) {
if (sCurrAreaIndex != -1)
gAreas[sCurrAreaIndex].terrainData = segmented_to_virtual(CMD_GET(void *, 4));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_rooms(void) {
if (sCurrAreaIndex != -1)
gAreas[sCurrAreaIndex].surfaceRooms = segmented_to_virtual(CMD_GET(void *, 4));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_39(void) {
if (sCurrAreaIndex != -1)
gAreas[sCurrAreaIndex].macroObjects = segmented_to_virtual(CMD_GET(void *, 4));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_load_area(void) {
s16 areaIndex = CMD_GET(u8, 2);
UNUSED void *unused = (u8 *) sCurrentCmd + 4;
func_80320890();
load_area(areaIndex);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_2A(void) {
func_8027A998();
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_mario_start_pos(void) {
gMarioSpawnInfo->areaIndex = CMD_GET(u8, 2);
vec3s_copy(gMarioSpawnInfo->startPos, CMD_GET(Vec3s, 6));
vec3s_set(gMarioSpawnInfo->startAngle, 0, CMD_GET(s16, 4) * 0x8000 / 180, 0);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_2C(void) {
func_8027AA88();
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_2D(void) {
area_update_objects();
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_transition(void) {
if (gCurrentArea != NULL)
play_transition(CMD_GET(u8, 2), CMD_GET(u8, 3), CMD_GET(u8, 4), CMD_GET(u8, 5), CMD_GET(u8, 6));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_nop(void) {
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_30(void) {
if (sCurrAreaIndex != -1) {
if (CMD_GET(u8, 2) < 2)
gAreas[sCurrAreaIndex].dialog[CMD_GET(u8, 2)] = CMD_GET(u8, 3);
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_music(void) {
if (sCurrAreaIndex != -1) {
gAreas[sCurrAreaIndex].musicParam = CMD_GET(s16, 2);
gAreas[sCurrAreaIndex].musicParam2 = CMD_GET(s16, 4);
}
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_set_menu_music(void) {
set_background_music(0, CMD_GET(s16, 2), 0);
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_38(void) {
func_802491FC(CMD_GET(s16, 2));
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_get_or_set_var(void) {
if (CMD_GET(u8, 2) == 0) {
switch (CMD_GET(u8, 3)) {
case 0:
gCurrSaveFileNum = sRegister;
break;
case 1:
gCurrCourseNum = sRegister;
break;
case 2:
gCurrActNum = sRegister;
break;
case 3:
gCurrLevelNum = sRegister;
break;
case 4:
gCurrAreaIndex = sRegister;
break;
}
} else {
switch (CMD_GET(u8, 3)) {
case 0:
sRegister = gCurrSaveFileNum;
break;
case 1:
sRegister = gCurrCourseNum;
break;
case 2:
sRegister = gCurrActNum;
break;
case 3:
sRegister = gCurrLevelNum;
break;
case 4:
sRegister = gCurrAreaIndex;
break;
}
}
sCurrentCmd = CMD_NEXT;
}
static void (*LevelScriptJumpTable[])(void) = {
/*00*/ level_cmd_load_and_execute,
/*01*/ level_cmd_exit_and_execute,
/*02*/ level_cmd_exit,
/*03*/ level_cmd_sleep,
/*04*/ level_cmd_sleep2,
/*05*/ level_cmd_jump,
/*06*/ level_cmd_jump_and_link,
/*07*/ level_cmd_return,
/*08*/ level_cmd_jump_and_link_push_arg,
/*09*/ level_cmd_jump_repeat,
/*0A*/ level_cmd_loop_begin,
/*0B*/ level_cmd_loop_until,
/*0C*/ level_cmd_jump_if,
/*0D*/ level_cmd_jump_and_link_if,
/*0E*/ level_cmd_skip_if,
/*0F*/ level_cmd_skip,
/*10*/ level_cmd_skippable_nop,
/*11*/ level_cmd_call,
/*12*/ level_cmd_call_loop,
/*13*/ level_cmd_set_register,
/*14*/ level_cmd_push_pool_state,
/*15*/ level_cmd_pop_pool_state,
/*16*/ level_cmd_load_to_fixed_address,
/*17*/ level_cmd_load_segment,
/*18*/ level_cmd_load_compressed_segment,
/*19*/ level_cmd_19,
/*1A*/ level_cmd_1A,
/*1B*/ level_cmd_init_level,
/*1C*/ level_cmd_clear_level,
/*1D*/ level_cmd_alloc_level_pool,
/*1E*/ level_cmd_free_level_pool,
/*1F*/ level_cmd_begin_area,
/*20*/ level_cmd_end_area,
/*21*/ level_cmd_21,
/*22*/ level_cmd_22,
/*23*/ level_cmd_23,
/*24*/ level_cmd_place_object,
/*25*/ level_cmd_init_mario,
/*26*/ level_cmd_create_warp_node,
/*27*/ level_cmd_create_painting_warp_node,
/*28*/ level_cmd_create_instant_warp,
/*29*/ level_cmd_load_area,
/*2A*/ level_cmd_2A,
/*2B*/ level_cmd_set_mario_start_pos,
/*2C*/ level_cmd_2C,
/*2D*/ level_cmd_2D,
/*2E*/ level_cmd_set_terrain_data,
/*2F*/ level_cmd_set_rooms,
/*30*/ level_cmd_30,
/*31*/ level_cmd_set_terrain_type,
/*32*/ level_cmd_nop,
/*33*/ level_cmd_set_transition,
/*34*/ level_cmd_set_blackout,
/*35*/ level_cmd_set_gamma,
/*36*/ level_cmd_set_music,
/*37*/ level_cmd_set_menu_music,
/*38*/ level_cmd_38,
/*39*/ level_cmd_39,
/*3A*/ level_cmd_3A,
/*3B*/ level_cmd_create_whirlpool,
/*3C*/ level_cmd_get_or_set_var,
};
struct LevelCommand *level_script_execute(struct LevelCommand *cmd) {
sScriptStatus = SCRIPT_RUNNING;
sCurrentCmd = cmd;
while (sScriptStatus == SCRIPT_RUNNING)
LevelScriptJumpTable[sCurrentCmd->type]();
profiler_log_thread5_time(LEVEL_SCRIPT_EXECUTE);
init_render_image();
render_game();
end_master_display_list();
alloc_display_list(0);
return sCurrentCmd;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef _LEVEL_SCRIPT_H
#define _LEVEL_SCRIPT_H
extern u8 gFrameBuffer0[];
extern u8 gFrameBuffer1[];
extern u8 gFrameBuffer2[];
struct LevelCommand *level_script_execute(struct LevelCommand *cmd);
extern u8 level_script_entry[];
#endif /* _LEVEL_SCRIPT_H */
+830
View File
@@ -0,0 +1,830 @@
#include <ultra64.h>
#include "sm64.h"
#include "engine/graph_node.h"
#include "math_util.h"
#include "surface_collision.h"
extern s16 gArctanTable[];
// Variables for a spline curve animation (used for the flight path in the grand star cutscene)
Vec4s *gSplineKeyframe;
float gSplineKeyframeFraction;
int gSplineState;
// These functions have bogus return values.
// Disable the compiler warning.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-local-addr"
/// Copy vector 'src' to 'dest'
void *vec3f_copy(Vec3f dest, Vec3f src) {
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
return &dest; //! warning: function returns address of local variable
}
/// Set vector 'dest' to (x, y, z)
void *vec3f_set(Vec3f dest, f32 x, f32 y, f32 z) {
dest[0] = x;
dest[1] = y;
dest[2] = z;
return &dest; //! warning: function returns address of local variable
}
/// Add vector 'a' to 'dest'
void *vec3f_add(Vec3f dest, Vec3f a) {
dest[0] += a[0];
dest[1] += a[1];
dest[2] += a[2];
return &dest; //! warning: function returns address of local variable
}
/// Make 'dest' the sum of vectors a and b.
void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b) {
dest[0] = a[0] + b[0];
dest[1] = a[1] + b[1];
dest[2] = a[2] + b[2];
return &dest; //! warning: function returns address of local variable
}
/// Copy vector src to dest
void *vec3s_copy(Vec3s dest, Vec3s src) {
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
return &dest; //! warning: function returns address of local variable
}
/// Set vector 'dest' to (x, y, z)
void *vec3s_set(Vec3s dest, s16 x, s16 y, s16 z) {
dest[0] = x;
dest[1] = y;
dest[2] = z;
return &dest; //! warning: function returns address of local variable
}
/// Add vector a to 'dest'
void *vec3s_add(Vec3s dest, Vec3s a) {
dest[0] += a[0];
dest[1] += a[1];
dest[2] += a[2];
return &dest; //! warning: function returns address of local variable
}
/// Make 'dest' the sum of vectors a and b.
void *vec3s_sum(Vec3s dest, Vec3s a, Vec3s b) {
dest[0] = a[0] + b[0];
dest[1] = a[1] + b[1];
dest[2] = a[2] + b[2];
return &dest; //! warning: function returns address of local variable
}
/// Subtract vector a from 'dest'
void *vec3s_sub(Vec3s dest, Vec3s a) {
dest[0] -= a[0];
dest[1] -= a[1];
dest[2] -= a[2];
return &dest; //! warning: function returns address of local variable
}
/// Convert short vector a to float vector 'dest'
void *vec3s_to_vec3f(Vec3f dest, Vec3s a) {
dest[0] = a[0];
dest[1] = a[1];
dest[2] = a[2];
return &dest; //! warning: function returns address of local variable
}
/** Convert float vector a to a short vector 'dest' by rounding the components
* to the nearest integer.
*/
void *vec3f_to_vec3s(Vec3s dest, Vec3f a) {
// add/subtract 0.5 in order to round to the nearest s32 instead of truncating
dest[0] = a[0] + ((a[0] > 0) ? 0.5f : -0.5f);
dest[1] = a[1] + ((a[1] > 0) ? 0.5f : -0.5f);
dest[2] = a[2] + ((a[2] > 0) ? 0.5f : -0.5f);
return &dest; //! warning: function returns address of local variable
}
/** Set 'dest' the normal vector of a triangle with vertices a, b and c.
* It is similar to vec3f_cross, but it calculates the vectors (c-b) and (b-a)
* at the same time.
*/
void *find_vector_perpendicular_to_plane(Vec3f dest, Vec3f a, Vec3f b, Vec3f c) {
dest[0] = (b[1] - a[1]) * (c[2] - b[2]) - (c[1] - b[1]) * (b[2] - a[2]);
dest[1] = (b[2] - a[2]) * (c[0] - b[0]) - (c[2] - b[2]) * (b[0] - a[0]);
dest[2] = (b[0] - a[0]) * (c[1] - b[1]) - (c[0] - b[0]) * (b[1] - a[1]);
return &dest; //! warning: function returns address of local variable
}
/// Make vector 'dest' the cross product of vectors a and b.
void *vec3f_cross(Vec3f dest, Vec3f a, Vec3f b) {
dest[0] = a[1] * b[2] - b[1] * a[2];
dest[1] = a[2] * b[0] - b[2] * a[0];
dest[2] = a[0] * b[1] - b[0] * a[1];
return &dest; //! warning: function returns address of local variable
}
/// Scale vector 'dest' so it has length 1
void *vec3f_normalize(Vec3f dest) {
//! Possible division by zero
f32 invsqrt = 1.0f / sqrtf(dest[0] * dest[0] + dest[1] * dest[1] + dest[2] * dest[2]);
dest[0] *= invsqrt;
dest[1] *= invsqrt;
dest[2] *= invsqrt;
return &dest; //! warning: function returns address of local variable
}
#pragma GCC diagnostic pop
/// Copy matrix 'src' to 'dest'
void mtxf_copy(Mat4 dest, Mat4 src) {
register s32 i;
register u32 *d = (u32 *) dest;
register u32 *s = (u32 *) src;
for (i = 0; i < 16; i++)
*d++ = *s++;
}
/** Set mtx to the identity matrix
*/
void mtxf_identity(Mat4 mtx) {
register s32 i;
register f32 *dest;
// initialize everything except the first and last cells to 0
// (this need to be on one line to match on PAL)
for (dest = (f32 *) mtx + 1, i = 0; i < 14; dest++, i++)
*dest = 0;
// initialize the diagonal cells to 1
for (dest = (f32 *) mtx, i = 0; i < 4; dest += 5, i++)
*dest = 1;
}
/** Set dest to a translation matrix of vector b
*/
void mtxf_translate(Mat4 dest, Vec3f b) {
mtxf_identity(dest);
dest[3][0] = b[0];
dest[3][1] = b[1];
dest[3][2] = b[2];
}
/** Set mtx to a look-at matrix for the camera. The resulting transformation
* transforms the world as if there exists a camera at position 'from' pointed
* at the position 'to'. The up-vector is assumed to be (0, 1, 0), but the 'roll'
* angle allows a bank rotation of the camera.
*/
void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll) {
register f32 invLength;
f32 dx;
f32 dz;
f32 xColY;
f32 yColY;
f32 zColY;
f32 xColZ;
f32 yColZ;
f32 zColZ;
f32 xColX;
f32 yColX;
f32 zColX;
dx = to[0] - from[0];
dz = to[2] - from[2];
invLength = -1.0 / sqrtf(dx * dx + dz * dz);
dx *= invLength;
dz *= invLength;
yColY = coss(roll);
xColY = sins(roll) * dz;
zColY = -sins(roll) * dx;
xColZ = to[0] - from[0];
yColZ = to[1] - from[1];
zColZ = to[2] - from[2];
invLength = -1.0 / sqrtf(xColZ * xColZ + yColZ * yColZ + zColZ * zColZ);
xColZ *= invLength;
yColZ *= invLength;
zColZ *= invLength;
xColX = yColY * zColZ - zColY * yColZ;
yColX = zColY * xColZ - xColY * zColZ;
zColX = xColY * yColZ - yColY * xColZ;
invLength = 1.0 / sqrtf(xColX * xColX + yColX * yColX + zColX * zColX);
xColX *= invLength;
yColX *= invLength;
zColX *= invLength;
xColY = yColZ * zColX - zColZ * yColX;
yColY = zColZ * xColX - xColZ * zColX;
zColY = xColZ * yColX - yColZ * xColX;
invLength = 1.0 / sqrtf(xColY * xColY + yColY * yColY + zColY * zColY);
xColY *= invLength;
yColY *= invLength;
zColY *= invLength;
mtx[0][0] = xColX;
mtx[1][0] = yColX;
mtx[2][0] = zColX;
mtx[3][0] = -(from[0] * xColX + from[1] * yColX + from[2] * zColX);
mtx[0][1] = xColY;
mtx[1][1] = yColY;
mtx[2][1] = zColY;
mtx[3][1] = -(from[0] * xColY + from[1] * yColY + from[2] * zColY);
mtx[0][2] = xColZ;
mtx[1][2] = yColZ;
mtx[2][2] = zColZ;
mtx[3][2] = -(from[0] * xColZ + from[1] * yColZ + from[2] * zColZ);
mtx[0][3] = 0;
mtx[1][3] = 0;
mtx[2][3] = 0;
mtx[3][3] = 1;
}
/**
* Build a matrix that rotates around the z axis, then the x axis, then the y
* axis, and then translates.
*/
void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f translate, Vec3s rotate) {
register f32 sx = sins(rotate[0]);
register f32 cx = coss(rotate[0]);
register f32 sy = sins(rotate[1]);
register f32 cy = coss(rotate[1]);
register f32 sz = sins(rotate[2]);
register f32 cz = coss(rotate[2]);
dest[0][0] = cy * cz + sx * sy * sz;
dest[1][0] = -cy * sz + sx * sy * cz;
dest[2][0] = cx * sy;
dest[3][0] = translate[0];
dest[0][1] = cx * sz;
dest[1][1] = cx * cz;
dest[2][1] = -sx;
dest[3][1] = translate[1];
dest[0][2] = -sy * cz + sx * cy * sz;
dest[1][2] = sy * sz + sx * cy * cz;
dest[2][2] = cx * cy;
dest[3][2] = translate[2];
dest[0][3] = dest[1][3] = dest[2][3] = 0.0f;
dest[3][3] = 1.0f;
}
/**
* Build a matrix that rotates around the x axis, then the y axis, then the z
* axis, and then translates.
*/
void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f b, Vec3s c) {
register f32 sx = sins(c[0]);
register f32 cx = coss(c[0]);
register f32 sy = sins(c[1]);
register f32 cy = coss(c[1]);
register f32 sz = sins(c[2]);
register f32 cz = coss(c[2]);
dest[0][0] = cy * cz;
dest[0][1] = cy * sz;
dest[0][2] = -sy;
dest[0][3] = 0;
dest[1][0] = sx * sy * cz - cx * sz;
dest[1][1] = sx * sy * sz + cx * cz;
dest[1][2] = sx * cy;
dest[1][3] = 0;
dest[2][0] = cx * sy * cz + sx * sz;
dest[2][1] = cx * sy * sz - sx * cz;
dest[2][2] = cx * cy;
dest[2][3] = 0;
dest[3][0] = b[0];
dest[3][1] = b[1];
dest[3][2] = b[2];
dest[3][3] = 1;
}
/** Set 'dest' to a transformation matrix that turns an object to face the camera.
* 'mtx' is the look-at matrix from the camera
* 'position' is the position of the object in the world
* 'angle' rotates the object while still facing the camera.
*/
void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) {
dest[0][0] = coss(angle);
dest[0][1] = sins(angle);
dest[0][2] = 0;
dest[0][3] = 0;
dest[1][0] = -dest[0][1];
dest[1][1] = dest[0][0];
dest[1][2] = 0;
dest[1][3] = 0;
dest[2][0] = 0;
dest[2][1] = 0;
dest[2][2] = 1;
dest[2][3] = 0;
dest[3][0] =
mtx[0][0] * position[0] + mtx[1][0] * position[1] + mtx[2][0] * position[2] + mtx[3][0];
dest[3][1] =
mtx[0][1] * position[0] + mtx[1][1] * position[1] + mtx[2][1] * position[2] + mtx[3][1];
dest[3][2] =
mtx[0][2] * position[0] + mtx[1][2] * position[1] + mtx[2][2] * position[2] + mtx[3][2];
dest[3][3] = 1;
}
/** Set 'dest' to a transformation matrix that aligns an object with the terrain
* based on the normal. Used for enemies.
* 'upDir' is the terrain normal
* 'yaw' is the angle which it should face
* 'pos' is the object's position in the world
*/
void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw) {
Vec3f lateralDir;
Vec3f leftDir;
Vec3f forwardDir;
vec3f_set(lateralDir, sins(yaw), 0, coss(yaw));
vec3f_normalize(upDir);
vec3f_cross(leftDir, upDir, lateralDir);
vec3f_normalize(leftDir);
vec3f_cross(forwardDir, leftDir, upDir);
vec3f_normalize(forwardDir);
dest[0][0] = leftDir[0];
dest[0][1] = leftDir[1];
dest[0][2] = leftDir[2];
dest[3][0] = pos[0];
dest[1][0] = upDir[0];
dest[1][1] = upDir[1];
dest[1][2] = upDir[2];
dest[3][1] = pos[1];
dest[2][0] = forwardDir[0];
dest[2][1] = forwardDir[1];
dest[2][2] = forwardDir[2];
dest[3][2] = pos[2];
dest[0][3] = 0.0f;
dest[1][3] = 0.0f;
dest[2][3] = 0.0f;
dest[3][3] = 1.0f;
}
/** Set 'mtx' to a transformation matrix that aligns an object with the terrain
* based on 3 height samples in an equilateral triangle around the object.
* Used for Mario when crawling or sliding.
* 'yaw' is the angle which it should face
* 'pos' is the object's position in the world
* 'radius' is the distance from each triangle vertex to the center
*/
void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius) {
struct Surface *sp74;
Vec3f point0;
Vec3f point1;
Vec3f point2;
Vec3f forward;
Vec3f xColumn;
Vec3f yColumn;
Vec3f zColumn;
f32 avgY;
f32 minY = -radius * 3;
point0[0] = pos[0] + radius * sins(yaw + 0x2AAA);
point0[2] = pos[2] + radius * coss(yaw + 0x2AAA);
point1[0] = pos[0] + radius * sins(yaw + 0x8000);
point1[2] = pos[2] + radius * coss(yaw + 0x8000);
point2[0] = pos[0] + radius * sins(yaw + 0xD555);
point2[2] = pos[2] + radius * coss(yaw + 0xD555);
point0[1] = find_floor(point0[0], pos[1] + 150, point0[2], &sp74);
point1[1] = find_floor(point1[0], pos[1] + 150, point1[2], &sp74);
point2[1] = find_floor(point2[0], pos[1] + 150, point2[2], &sp74);
if (point0[1] - pos[1] < minY)
point0[1] = pos[1];
if (point1[1] - pos[1] < minY)
point1[1] = pos[1];
if (point2[1] - pos[1] < minY)
point2[1] = pos[1];
avgY = (point0[1] + point1[1] + point2[1]) / 3;
vec3f_set(forward, sins(yaw), 0, coss(yaw));
find_vector_perpendicular_to_plane(yColumn, point0, point1, point2);
vec3f_normalize(yColumn);
vec3f_cross(xColumn, yColumn, forward);
vec3f_normalize(xColumn);
vec3f_cross(zColumn, xColumn, yColumn);
vec3f_normalize(zColumn);
mtx[0][0] = xColumn[0];
mtx[0][1] = xColumn[1];
mtx[0][2] = xColumn[2];
mtx[3][0] = pos[0];
mtx[1][0] = yColumn[0];
mtx[1][1] = yColumn[1];
mtx[1][2] = yColumn[2];
mtx[3][1] = (avgY < pos[1]) ? pos[1] : avgY;
mtx[2][0] = zColumn[0];
mtx[2][1] = zColumn[1];
mtx[2][2] = zColumn[2];
mtx[3][2] = pos[2];
mtx[0][3] = 0;
mtx[1][3] = 0;
mtx[2][3] = 0;
mtx[3][3] = 1;
}
/** Sets matrix 'dest' to the matrix product b * a assuming they are both
* transformation matrices with a w-component of 1. Since the bottom row
* is assumed to equal [0, 0, 0, 1], it saves some multiplications and
* addition.
* The resulting matrix represents first applying transformation b and
* then a.
*/
void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b) {
Mat4 temp;
register f32 entry0;
register f32 entry1;
register f32 entry2;
// column 0
entry0 = a[0][0];
entry1 = a[0][1];
entry2 = a[0][2];
temp[0][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0];
temp[0][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1];
temp[0][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2];
// column 1
entry0 = a[1][0];
entry1 = a[1][1];
entry2 = a[1][2];
temp[1][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0];
temp[1][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1];
temp[1][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2];
// column 2
entry0 = a[2][0];
entry1 = a[2][1];
entry2 = a[2][2];
temp[2][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0];
temp[2][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1];
temp[2][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2];
// column 3
entry0 = a[3][0];
entry1 = a[3][1];
entry2 = a[3][2];
temp[3][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0] + b[3][0];
temp[3][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1] + b[3][1];
temp[3][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2] + b[3][2];
temp[0][3] = temp[1][3] = temp[2][3] = 0;
temp[3][3] = 1;
mtxf_copy(dest, temp);
}
/** Set matrix 'dest' to 'mtx' scaled by vector s
*/
void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, Vec3f s) {
register s32 i;
for (i = 0; i < 4; i++) {
dest[0][i] = mtx[0][i] * s[0];
dest[1][i] = mtx[1][i] * s[1];
dest[2][i] = mtx[2][i] * s[2];
dest[3][i] = mtx[3][i];
}
}
/** Multiply a vector with a transformation matrix, which applies the transformation
* to the point. Note that the bottom row is assumed to be [0, 0, 0, 1], which is
* true for transformation matrices if the translation has a w component of 1.
*/
void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) {
register f32 x = b[0];
register f32 y = b[1];
register f32 z = b[2];
b[0] = x * mtx[0][0] + y * mtx[1][0] + z * mtx[2][0] + mtx[3][0];
b[1] = x * mtx[0][1] + y * mtx[1][1] + z * mtx[2][1] + mtx[3][1];
b[2] = x * mtx[0][2] + y * mtx[1][2] + z * mtx[2][2] + mtx[3][2];
}
/** Convert float matrix 'src' to fixed point matrix 'dest'.
* The float matrix may not contain entries larger than 65536 or the console
* crashes. The fixed point matrix has entries with a 16-bit integer part, so
* the floating point numbers are multipled by 2^16 before being cast to a s32
* integer. If this doesn't fit, the N64 and iQue consoles will throw an
* exception. On Wii and Wii U Virtual Console the value will simply be clamped
* and no crashes occur.
*/
void mtxf_to_mtx(Mtx *dest, Mat4 src) {
s32 asFixedPoint;
register s32 i;
register s16 *a3 = (s16 *) dest; // all integer parts stored in first 16 bytes
register s16 *t0 = (s16 *) dest + 16; // all fraction parts stored in last 16 bytes
register f32 *t1 = (f32 *) src;
for (i = 0; i < 16; i++) {
asFixedPoint = *t1++ * (1 << 16); //! float-to-integer conversion responsible for PU crashes
*a3++ = GET_HIGH_S16_OF_32(asFixedPoint); // integer part
*t0++ = GET_LOW_S16_OF_32(asFixedPoint); // fraction part
}
}
/** Set 'mtx' to a transformation matrix that rotates around the z axis.
*/
void mtxf_rotate_xy(Mtx *mtx, s16 angle) {
Mat4 temp;
mtxf_identity(temp);
temp[0][0] = coss(angle);
temp[0][1] = sins(angle);
temp[1][0] = -temp[0][1];
temp[1][1] = temp[0][0];
mtxf_to_mtx(mtx, temp);
}
/** Extract a position given an object's transformation matrix and a camera matrix.
* This is used for determining the world position of the held object: since objMtx
* inherits the transformation from both the camera and Mario, it calculates this
* by taking the camera matrix and inverting its transformation by first rotating
* objMtx back from screen orientation to world orientation, and then subtracting
* the camera position.
*/
void get_pos_from_transform_mtx(Vec3f dest, Mat4 objMtx, Mat4 camMtx) {
f32 camX = camMtx[3][0] * camMtx[0][0] + camMtx[3][1] * camMtx[0][1] + camMtx[3][2] * camMtx[0][2];
f32 camY = camMtx[3][0] * camMtx[1][0] + camMtx[3][1] * camMtx[1][1] + camMtx[3][2] * camMtx[1][2];
f32 camZ = camMtx[3][0] * camMtx[2][0] + camMtx[3][1] * camMtx[2][1] + camMtx[3][2] * camMtx[2][2];
dest[0] =
objMtx[3][0] * camMtx[0][0] + objMtx[3][1] * camMtx[0][1] + objMtx[3][2] * camMtx[0][2] - camX;
dest[1] =
objMtx[3][0] * camMtx[1][0] + objMtx[3][1] * camMtx[1][1] + objMtx[3][2] * camMtx[1][2] - camY;
dest[2] =
objMtx[3][0] * camMtx[2][0] + objMtx[3][1] * camMtx[2][1] + objMtx[3][2] * camMtx[2][2] - camZ;
}
/** Take the vector starting at 'from' pointed at 'to' an retrieve the length
* of that vector, as well as the yaw and pitch angles.
*/
void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 *yaw) {
register f32 x = to[0] - from[0];
register f32 y = to[1] - from[1];
register f32 z = to[2] - from[2];
*dist = sqrtf(x * x + y * y + z * z);
*pitch = atan2s(sqrtf(x * x + z * z), y);
*yaw = atan2s(z, x);
}
/** Construct the 'to' point which is distance 'dist' away from the 'from' position,
* and has the angles pitch and yaw.
*/
void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s16 pitch, s16 yaw) {
to[0] = from[0] + dist * coss(pitch) * sins(yaw);
to[1] = from[1] + dist * sins(pitch);
to[2] = from[2] + dist * coss(pitch) * coss(yaw);
}
/** Return the value 'current' after it tries to approach target, going up at
* most 'inc' and going down at most 'dec'.
*/
s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec) {
//! If target is close to the max or min s32, then it's possible to overflow
// past it without stopping.
if (current < target) {
current += inc;
if (current > target)
current = target;
} else {
current -= dec;
if (current < target)
current = target;
}
return current;
}
/** Return the value 'current' after it tries to approach target, going up at
* most 'inc' and going down at most 'dec'.
*/
f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec) {
if (current < target) {
current += inc;
if (current > target)
current = target;
} else {
current -= dec;
if (current < target)
current = target;
}
return current;
}
/** Helper function for atan2s. Does a look up of the arctangent of y/x assuming
* the resulting angle is in range [0, 0x2000] (1/8 of a circle).
*/
static u16 atan2_lookup(f32 y, f32 x) {
u16 ret;
if (x == 0)
ret = gArctanTable[0];
else
ret = gArctanTable[(s32)(y / x * 1024 + 0.5f)];
return ret;
}
/** Compute the angle from (0, 0) to (x, y) as a s16. Given that terrain is in
* the xz-plane, this is commonly called with (z, x) to get a yaw angle.
*/
s16 atan2s(f32 y, f32 x) {
u16 ret;
if (x >= 0) {
if (y >= 0) {
if (y >= x)
ret = atan2_lookup(x, y);
else
ret = 0x4000 - atan2_lookup(y, x);
} else {
y = -y;
if (y < x)
ret = 0x4000 + atan2_lookup(y, x);
else
ret = 0x8000 - atan2_lookup(x, y);
}
} else {
x = -x;
if (y < 0) {
y = -y;
if (y >= x)
ret = 0x8000 + atan2_lookup(x, y);
else
ret = 0xC000 - atan2_lookup(y, x);
} else {
if (y < x)
ret = 0xC000 + atan2_lookup(y, x);
else
ret = -atan2_lookup(x, y);
}
}
return ret;
}
/** Compute the atan2 in radians by calling atan2s and converting the result.
*/
f32 atan2f(f32 y, f32 x) {
return (f32) atan2s(y, x) * M_PI / 0x8000;
}
#define CURVE_BEGIN_1 1
#define CURVE_BEGIN_2 2
#define CURVE_MIDDLE 3
#define CURVE_END_1 4
#define CURVE_END_2 5
/** Set 'result' to a 4-vector with weights corresponding to interpolation
* value t in [0, 1] and gSplineState. Given the current control point P, these
* weights are for P[0], P[1], P[2] and P[3] to obtain an interpolated point.
* The weights naturally sum to 1, and they are also always in range [0, 1] so
* the inteprolated point will never overshoot. The curve is guaranteed to go
* through the first and last point, but not through intermediate points.
*
* gSplineState ensures that the curve is clamped: the first two points
* and last two points have different weight formulas. These are the weights
* just before gSplineState transitions:
* 1: [1, 0, 0, 0]
* 1->2: [0, 3/12, 7/12, 2/12]
* 2->3: [0, 1/6, 4/6, 1/6]
* 3->3: [0, 1/6, 4/6, 1/6] (repeats)
* 3->4: [0, 1/6, 4/6, 1/6]
* 4->5: [0, 2/12, 7/12, 3/12]
* 5: [0, 0, 0, 1]
*
* I suspect that the weight formulas will give a 3rd degree B-spline with the
* common uniform clamped knot vector, e.g. for n points:
* [0, 0, 0, 0, 1, 2, ... n-1, n, n, n, n]
* TODO: verify the classification of the spline / figure out how polynomials were computed
*/
void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c) {
f32 tinv = 1 - t;
f32 tinv2 = tinv * tinv;
f32 tinv3 = tinv2 * tinv;
f32 t2 = t * t;
f32 t3 = t2 * t;
switch (gSplineState) {
case CURVE_BEGIN_1:
result[0] = tinv3;
result[1] = t3 * 1.75f - t2 * 4.5f + t * 3.0f;
result[2] = -t3 * (11 / 12.0f) + t2 * 1.5f;
result[3] = t3 * (1 / 6.0f);
break;
case CURVE_BEGIN_2:
result[0] = tinv3 * 0.25f;
result[1] = t3 * (7 / 12.0f) - t2 * 1.25f + t * 0.25f + (7 / 12.0f);
result[2] = -t3 * 0.5f + t2 * 0.5f + t * 0.5f + (1 / 6.0f);
result[3] = t3 * (1 / 6.0f);
break;
case CURVE_MIDDLE:
result[0] = tinv3 * (1 / 6.0f);
result[1] = t3 * 0.5f - t2 + (4 / 6.0f);
result[2] = -t3 * 0.5f + t2 * 0.5f + t * 0.5f + (1 / 6.0f);
result[3] = t3 * (1 / 6.0f);
break;
case CURVE_END_1:
result[0] = tinv3 * (1 / 6.0f);
result[1] = -tinv3 * 0.5f + tinv2 * 0.5f + tinv * 0.5f + (1 / 6.0f);
result[2] = tinv3 * (7 / 12.0f) - tinv2 * 1.25f + tinv * 0.25f + (7 / 12.0f);
result[3] = t3 * 0.25f;
break;
case CURVE_END_2:
result[0] = tinv3 * (1 / 6.0f);
result[1] = -tinv3 * (11 / 12.0f) + tinv2 * 1.5f;
result[2] = tinv3 * 1.75f - tinv2 * 4.5f + tinv * 3.0f;
result[3] = t3;
break;
}
}
/** Initialize a spline animation.
* 'keyframes' should be an array of (s, x, y, z) vectors
* s: the speed of the keyframe in 1000/frames, e.g. s=100 means the keyframe lasts 10 frames
* (x, y, z): point in 3D space on the curve
* The array should end with three entries with s=0 (infinite keyframe duration).
* That's because the spline has a 3rd degree polynomial, so it looks 3 points ahead.
*/
void anim_spline_init(Vec4s *keyFrames) {
gSplineKeyframe = keyFrames;
gSplineKeyframeFraction = 0;
gSplineState = 1;
}
/** Poll the next point from a spline animation.
* anim_spline_init should be called before polling for vectors.
* Returns TRUE when the last point is reached, FALSE otherwise.
*/
s32 anim_spline_poll(Vec3f result) {
Vec4f weights;
s32 i;
s32 hasEnded = FALSE;
vec3f_copy(result, gVec3fZero);
spline_get_weights(weights, gSplineKeyframeFraction, gSplineState);
for (i = 0; i < 4; i++) {
result[0] += weights[i] * gSplineKeyframe[i][1];
result[1] += weights[i] * gSplineKeyframe[i][2];
result[2] += weights[i] * gSplineKeyframe[i][3];
}
if ((gSplineKeyframeFraction += gSplineKeyframe[0][0] / 1000.0f) >= 1) {
gSplineKeyframe++;
gSplineKeyframeFraction--;
switch (gSplineState) {
case CURVE_END_2:
hasEnded = TRUE;
break;
case CURVE_MIDDLE:
if (gSplineKeyframe[2][0] == 0)
gSplineState = CURVE_END_1;
break;
default:
gSplineState++;
break;
}
}
return hasEnded;
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef _MATH_UTIL_H_
#define _MATH_UTIL_H_
extern f32 gSineTable[];
extern f32 gCosineTable[];
#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(f32 dest[4][4], f32 src[4][4]);
void mtxf_identity(f32 mtx[4][4]);
void mtxf_translate(f32 a[4][4], Vec3f b);
void mtxf_lookat(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d);
void mtxf_rotate_zxy_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c);
void mtxf_rotate_xyz_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c);
void mtxf_billboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d);
void mtxf_align_terrain_normal(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d);
void mtxf_align_terrain_triangle(f32 mtx[4][4], Vec3f b, s16 c, f32 d);
void mtxf_mul(f32 dest[4][4], f32 a[4][4], f32 b[4][4]);
void mtxf_scale_vec3f(f32 a[4][4], f32 b[4][4], Vec3f c);
void mtxf_mul_vec3s(f32 a[4][4], Vec3s b);
void mtxf_to_mtx(Mtx *a, f32 b[4][4]);
void mtxf_rotate_xy(Mtx *a, s16 b);
void get_pos_from_transform_mtx(Vec3f a, f32 b[4][4], f32 c[4][4]);
void vec3f_get_dist_and_angle(Vec3f a, Vec3f b, f32 *c, s16 *d, s16 *e);
void vec3f_set_dist_and_angle(Vec3f a, Vec3f b, f32 c, s16 d, s16 e);
s32 approach_s32(s32 a, s32 b, s32 c, s32 d);
f32 approach_f32(f32 a, f32 b, f32 c, f32 d);
s16 atan2s(f32 a, f32 b);
f32 atan2f(f32 a, f32 b);
void spline_get_weights(Vec4f a, f32 b, UNUSED s32 c);
void anim_spline_init(Vec4s *a);
s32 anim_spline_poll(Vec3f a);
#endif
+760
View File
@@ -0,0 +1,760 @@
#include <ultra64.h>
#include "sm64.h"
#include "game/level_update.h"
#include "game/debug.h"
#include "game/camera.h"
#include "game/mario.h"
#include "behavior_script.h"
#include "surface_collision.h"
#include "surface_load.h"
#include "game/object_list_processor.h"
#include "game/room.h"
/**************************************************
* WALLS *
**************************************************/
/**
* Iterate through the list of walls until all walls are checked and
* have given their wall push.
*/
static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode,
struct WallCollisionData *data) {
register f32 offset;
register f32 radius = data->radius;
register struct Surface *surf;
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.
while (surfaceNode != NULL) {
surf = surfaceNode->surface;
surfaceNode = surfaceNode->next;
// 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;
}
/**
* Formats the position and wall search for find_wall_collisions.
*/
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;
}
/**
* Find wall collisions and receive their push.
*/
s32 find_wall_collisions(struct WallCollisionData *colData) {
struct SurfaceNode *node;
s16 cellX, cellZ;
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;
// World (level) consists of a 16x16 grid. Find where the collision is on
// the grid (round toward -inf)
cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0x0F;
cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0x0F;
// Check for surfaces belonging to objects.
node = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next;
numCollisions += find_wall_collisions_from_list(node, colData);
// Check for surfaces that are a part of level geometry.
node = gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next;
numCollisions += find_wall_collisions_from_list(node, colData);
// Increment the debug tracker.
gNumCalls.wall += 1;
return numCollisions;
}
/**************************************************
* CEILINGS *
**************************************************/
/**
* Iterate through the list of ceilings and find the first ceiling over a given point.
*/
static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, 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.
while (surfaceNode != NULL) {
surf = surfaceNode->surface;
surfaceNode = surfaceNode->next;
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;
*pheight = height;
ceil = surf;
break;
}
}
//! (Surface Cucking) Since only the first ceil is returned and not the lowest,
// lower ceilings can be "cucked" by higher ceilings.
return ceil;
}
/**
* Find the lowest ceiling above a given position and return the height.
*/
f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil) {
s16 cellZ, cellX;
struct Surface *ceil, *dynamicCeil;
struct SurfaceNode *surfaceList;
f32 height = 20000.0f;
f32 dynamicHeight = 20000.0f;
s16 x, y, z;
//! (Parallel Universes) Because position is casted to an s16, reaching higher
// float locations can return ceilings despite them not existing there.
//(Dynamic ceilings will unload due to the range.)
x = (s16) posX;
y = (s16) posY;
z = (s16) posZ;
*pceil = NULL;
if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) {
return height;
}
if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) {
return height;
}
// Each level is split into cells to limit load, find the appropriate cell.
cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0xF;
cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0xF;
// Check for surfaces belonging to objects.
surfaceList = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next;
dynamicCeil = find_ceil_from_list(surfaceList, x, y, z, &dynamicHeight);
// Check for surfaces that are a part of level geometry.
surfaceList = gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next;
ceil = find_ceil_from_list(surfaceList, x, y, z, &height);
if (dynamicHeight < height) {
ceil = dynamicCeil;
height = dynamicHeight;
}
*pceil = ceil;
// Increment the debug tracker.
gNumCalls.ceil += 1;
return height;
}
/**************************************************
* FLOORS *
**************************************************/
/**
* Find the height of the highest floor below an object.
*/
static f32 unused_obj_find_floor_height(struct Object *obj) {
struct Surface *floor;
f32 floorHeight = find_floor(obj->oPosX, obj->oPosY, obj->oPosZ, &floor);
return floorHeight;
}
/**
* Basically a local variable that passes through floor geo info.
*/
struct FloorGeometry sFloorGeo;
static u8 unused8038BE50[0x40];
/**
* Return the floor height underneath (xPos, yPos, zPos) and populate `floorGeo`
* with data about the floor's normal vector and origin offset. Also update
* 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;
}
/**
* Iterate through the list of floors and find the first floor under a given point.
*/
static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, 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.
while (surfaceNode != NULL) {
surf = surfaceNode->surface;
surfaceNode = surfaceNode->next;
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;
*pheight = height;
floor = surf;
break;
}
//! (Surface Cucking) Since only the first floor is returned and not the highest,
// higher floors can be "cucked" by lower floors.
return floor;
}
/**
* Find the height of the highest floor below a point.
*/
f32 find_floor_height(f32 x, f32 y, f32 z) {
struct Surface *floor;
f32 floorHeight = find_floor(x, y, z, &floor);
return floorHeight;
}
/**
* Find the highest dynamic floor under a given position. Perhaps originally static and
* and dynamic floors were checked separately.
*/
static f32 unused_find_dynamic_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) {
struct SurfaceNode *surfaceList;
struct Surface *floor;
f32 floorHeight = -11000.0f;
// Would normally cause PUs, but dynamic floors unload at that range.
s16 x = (s16) xPos;
s16 y = (s16) yPos;
s16 z = (s16) zPos;
// Each level is split into cells to limit load, find the appropriate cell.
s16 cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0x0F;
s16 cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0x0F;
surfaceList = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next;
floor = find_floor_from_list(surfaceList, x, y, z, &floorHeight);
*pfloor = floor;
return floorHeight;
}
/**
* Find the highest floor under a given position and return the height.
*/
f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) {
s16 cellZ, cellX;
struct Surface *floor, *dynamicFloor;
struct SurfaceNode *surfaceList;
f32 height = -11000.0f;
f32 dynamicHeight = -11000.0f;
//! (Parallel Universes) Because position is casted to an s16, reaching higher
// float locations can return floors despite them not existing there.
//(Dynamic floors will unload due to the range.)
s16 x = (s16) xPos;
s16 y = (s16) yPos;
s16 z = (s16) zPos;
*pfloor = NULL;
if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) {
return height;
}
if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) {
return height;
}
// Each level is split into cells to limit load, find the appropriate cell.
cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0xF;
cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & 0xF;
// Check for surfaces belonging to objects.
surfaceList = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next;
dynamicFloor = find_floor_from_list(surfaceList, x, y, z, &dynamicHeight);
// Check for surfaces that are a part of level geometry.
surfaceList = gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next;
floor = find_floor_from_list(surfaceList, x, y, z, &height);
// To prevent the Merry-Go-Round room from loading when Mario passes above the hole that leads
// there, SURFACE_INTANGIBLE is used. This prevent the wrong room from loading, but can also allow
// Mario to pass through.
if (!gFindFloorIncludeSurfaceIntangible) {
//! (BBH Crash) Most NULL checking is done by checking the height of the floor returned
// instead of checking directly for a NULL floor. If this check returns a NULL floor
// (happens when there is no floor under the SURFACE_INTANGIBLE floor) but returns the height
// of the SURFACE_INTANGIBLE floor instead of the typical -11000 returned for a NULL floor.
if (floor != NULL && floor->type == SURFACE_INTANGIBLE) {
floor = find_floor_from_list(surfaceList, x, (s32)(height - 200.0f), z, &height);
}
} else {
// To prevent accidentally leaving the floor tangible, stop checking for it.
gFindFloorIncludeSurfaceIntangible = FALSE;
}
// If a floor was missed, increment the debug counter.
if (floor == NULL) {
gNumFindFloorMisses += 1;
}
if (dynamicHeight > height) {
floor = dynamicFloor;
height = dynamicHeight;
}
*pfloor = floor;
// Increment the debug tracker.
gNumCalls.floor += 1;
return height;
}
/**************************************************
* ENVIRONMENTAL BOXES *
**************************************************/
/**
* Finds the height of water at a given location.
*/
f32 find_water_level(f32 x, f32 z) {
s32 i;
s32 numRegions;
s16 val;
f32 loX, hiX, loZ, hiZ;
f32 waterLevel = -11000.0f;
s16 *p = gEnvironmentRegions;
if (p != NULL) {
numRegions = *p++;
for (i = 0; i < numRegions; i++) {
val = *p++;
loX = *p++;
loZ = *p++;
hiX = *p++;
hiZ = *p++;
// If the location is within a water box and it is a water box.
// Water is less than 50 val only, while above is gas and such.
if (loX < x && x < hiX && loZ < z && z < hiZ && val < 50) {
// Set the water height. Since this breaks, only return the first height.
waterLevel = *p;
break;
}
p++;
}
}
return waterLevel;
}
/**
* Finds the height of the poison gas (used only in HMC) at a given location.
*/
f32 find_poison_gas_level(f32 x, f32 z) {
s32 i;
s32 numRegions;
UNUSED s32 unused;
s16 val;
f32 loX, hiX, loZ, hiZ;
f32 gasLevel = -11000.0f;
s16 *p = gEnvironmentRegions;
if (p != NULL) {
numRegions = *p++;
for (i = 0; i < numRegions; i++) {
val = *p;
if (val >= 50) {
loX = *(p + 1);
loZ = *(p + 2);
hiX = *(p + 3);
hiZ = *(p + 4);
// If the location is within a gas's box and it is a gas box.
// Gas has a value of 50, 60, etc.
if (loX < x && x < hiX && loZ < z && z < hiZ && val % 10 == 0) {
// Set the gas height. Since this breaks, only return the first height.
gasLevel = *(p + 5);
break;
}
}
p += 6;
}
}
return gasLevel;
}
/**************************************************
* DEBUG *
**************************************************/
/**
* Finds the length of a surface list for debug purposes.
*/
static s32 surface_list_length(struct SurfaceNode *list) {
s32 count = 0;
while (list != NULL) {
list = list->next;
count++;
}
return count;
}
/**
* Print the area,number of walls, how many times they were called,
* and some allocation information.
*/
void debug_surface_list_info(f32 xPos, f32 zPos) {
struct SurfaceNode *list;
s32 numFloors = 0;
s32 numWalls = 0;
s32 numCeils = 0;
s32 cellX = (xPos + LEVEL_BOUNDARY_MAX) / CELL_SIZE;
s32 cellZ = (zPos + LEVEL_BOUNDARY_MAX) / CELL_SIZE;
list = gStaticSurfacePartition[cellZ & 0x0F][cellX & 0x0F][SPATIAL_PARTITION_FLOORS].next;
numFloors += surface_list_length(list);
list = gDynamicSurfacePartition[cellZ & 0x0F][cellX & 0x0F][SPATIAL_PARTITION_FLOORS].next;
numFloors += surface_list_length(list);
list = gStaticSurfacePartition[cellZ & 0x0F][cellX & 0x0F][SPATIAL_PARTITION_WALLS].next;
numWalls += surface_list_length(list);
list = gDynamicSurfacePartition[cellZ & 0x0F][cellX & 0x0F][SPATIAL_PARTITION_WALLS].next;
numWalls += surface_list_length(list);
list = gStaticSurfacePartition[cellZ & 0x0F][cellX & 0x0F][SPATIAL_PARTITION_CEILS].next;
numCeils += surface_list_length(list);
list = gDynamicSurfacePartition[cellZ & 0x0F][cellX & 0x0F][SPATIAL_PARTITION_CEILS].next;
numCeils += surface_list_length(list);
print_debug_top_down_mapinfo("area %x", cellZ * 16 + cellX);
// Names represent ground, walls, and roofs as found in SMS.
print_debug_top_down_mapinfo("dg %d", numFloors);
print_debug_top_down_mapinfo("dw %d", numWalls);
print_debug_top_down_mapinfo("dr %d", numCeils);
set_text_array_x_y(80, -3);
print_debug_top_down_mapinfo("%d", gNumCalls.floor);
print_debug_top_down_mapinfo("%d", gNumCalls.wall);
print_debug_top_down_mapinfo("%d", gNumCalls.ceil);
set_text_array_x_y(-80, 0);
// listal- List Allocated?, statbg- Static Background?, movebg- Moving Background?
print_debug_top_down_mapinfo("listal %d", gSurfaceNodesAllocated);
print_debug_top_down_mapinfo("statbg %d", gNumStaticSurfaces);
print_debug_top_down_mapinfo("movebg %d", gSurfacesAllocated - gNumStaticSurfaces);
gNumCalls.floor = 0;
gNumCalls.ceil = 0;
gNumCalls.wall = 0;
}
/**
* An unused function that finds and interacts with any type of surface.
* Perhaps an original implementation of surfaces before they were more specialized.
*/
static s32 unused_resolve_floor_or_ceil_collisions(s32 checkCeil, f32 *px, f32 *py, f32 *pz, f32 radius,
struct Surface **psurface, f32 *surfaceHeight) {
f32 nx, ny, nz, oo;
f32 x = *px, y = *py, z = *pz;
f32 offset, distance;
*psurface = NULL;
if (checkCeil) {
*surfaceHeight = find_ceil(x, y, z, psurface);
} else {
*surfaceHeight = find_floor(x, y, z, psurface);
}
if (*psurface == NULL) {
return -1;
}
nx = (*psurface)->normal.x;
ny = (*psurface)->normal.y;
nz = (*psurface)->normal.z;
oo = (*psurface)->originOffset;
offset = nx * x + ny * y + nz * z + oo;
distance = offset >= 0 ? offset : -offset;
// Interesting surface interaction that should be surf type independent.
if (distance < radius) {
*px += nx * (radius - offset);
*py += ny * (radius - offset);
*pz += nz * (radius - offset);
return 1;
}
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef _SURFACE_COLLISION_H
#define _SURFACE_COLLISION_H
#define LEVEL_BOUNDARY_MAX 0x2000
#define CELL_SIZE 0x400
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);
void debug_surface_list_info(f32 xPos, f32 zPos);
#endif /* _SURFACE_COLLISION_H */
+738
View File
@@ -0,0 +1,738 @@
#include <ultra64.h>
#include "prevent_bss_reordering.h"
#include "sm64.h"
#include "game/ingame_menu.h"
#include "graph_node.h"
#include "behavior_script.h"
#include "behavior_data.h"
#include "game/memory.h"
#include "game/object_helpers.h"
#include "game/macro_special_objects.h"
#include "surface_collision.h"
#include "game/mario.h"
#include "game/object_list_processor.h"
#include "game/room.h"
#include "surface_load.h"
s32 unused8038BE90;
/**
* Partitions for course and object surfaces. The arrays represent
* the 16x16 cells that each level is split into.
*/
SpatialPartitionCell gStaticSurfacePartition[16][16];
SpatialPartitionCell gDynamicSurfacePartition[16][16];
/**
* Pools of data to contain either surface nodes or surfaces.
*/
struct SurfaceNode *sSurfaceNodePool;
struct Surface *sSurfacePool;
/**
* The size of the surface pool (2300).
*/
s16 sSurfacePoolSize;
u8 unused8038EEA8[0x30];
/**
* Allocate the part of the surface node pool to contain a surface node.
*/
static struct SurfaceNode *alloc_surface_node(void) {
struct SurfaceNode *node = &sSurfaceNodePool[gSurfaceNodesAllocated];
gSurfaceNodesAllocated++;
node->next = NULL;
//! A bounds check! If there's more surface nodes than 7000 allowed,
// we, um...
// Perhaps originally just debug feedback?
if (gSurfaceNodesAllocated >= 7000) {
}
return node;
}
/**
* Allocate the part of the surface pool to contain a surface and
* initialize the surface.
*/
static struct Surface *alloc_surface(void) {
struct Surface *surface = &sSurfacePool[gSurfacesAllocated];
gSurfacesAllocated++;
//! A bounds check! If there's more surfaces than the 2300 allowed,
// we, um...
// Perhaps originally just debug feedback?
if (gSurfacesAllocated >= sSurfacePoolSize) {
}
surface->type = 0;
surface->force = 0;
surface->flags = 0;
surface->room = 0;
surface->object = NULL;
return surface;
}
/**
* Iterates through the entire partition, clearing the surfaces.
*/
static void clear_spatial_partition(SpatialPartitionCell *cells) {
register s32 i = 16 * 16;
while (i--) {
(*cells)[SPATIAL_PARTITION_FLOORS].next = NULL;
(*cells)[SPATIAL_PARTITION_CEILS].next = NULL;
(*cells)[SPATIAL_PARTITION_WALLS].next = NULL;
cells++;
}
}
/**
* Clears the static (level) surface partitions for new use.
*/
static void clear_static_surfaces(void) {
clear_spatial_partition(&gStaticSurfacePartition[0][0]);
}
/**
* Add a surface to the correct cell list of surfaces.
*/
static void add_surface_to_cell(s16 dynamic, s16 cellX, s16 cellZ, struct Surface *surface) {
struct SurfaceNode *newNode = alloc_surface_node();
struct SurfaceNode *list;
s16 surfacePriority;
s16 priority;
s16 sortDir;
s16 listIndex;
if (surface->normal.y > 0.01) {
listIndex = SPATIAL_PARTITION_FLOORS;
sortDir = 1; // highest to lowest, then insertion order
} else if (surface->normal.y < -0.01) {
listIndex = SPATIAL_PARTITION_CEILS;
sortDir = -1; // lowest to highest, then insertion order
} else {
listIndex = SPATIAL_PARTITION_WALLS;
sortDir = 0; // insertion order
if (surface->normal.x < -0.707 || surface->normal.x > 0.707) {
surface->flags |= SURFACE_FLAG_X_PROJECTION;
}
}
//! (Surface Cucking) Surfaces are sorted by the height of their first
// vertex. Since vertices aren't ordered by height, this causes many
// lower triangles to be sorted higher. This worsens surface cucking since
// many functions only use the first triangle in surface order that fits,
// missing higher surfaces.
// upperY would be a better sort method.
surfacePriority = surface->vertex1[1] * sortDir;
newNode->surface = surface;
if (dynamic) {
list = &gDynamicSurfacePartition[cellZ][cellX][listIndex];
} else {
list = &gStaticSurfacePartition[cellZ][cellX][listIndex];
}
// Loop until we find the appropriate place for the surface in the list.
while (list->next != NULL) {
priority = list->next->surface->vertex1[1] * sortDir;
if (surfacePriority > priority) {
break;
}
list = list->next;
}
newNode->next = list->next;
list->next = newNode;
}
/**
* Returns the lowest of three values.
*/
static s16 min_3(s16 a0, s16 a1, s16 a2) {
if (a1 < a0) {
a0 = a1;
}
if (a2 < a0) {
a0 = a2;
}
return a0;
}
/**
* Returns the highest of three values.
*/
static s16 max_3(s16 a0, s16 a1, s16 a2) {
if (a1 > a0) {
a0 = a1;
}
if (a2 > a0) {
a0 = a2;
}
return a0;
}
/**
* Every level is split into 16 * 16 cells of surfaces (to limit computing
* time). This function determines the lower cell for a given x/z position.
*/
static s16 lower_cell_index(s16 t) {
s16 index;
// Move from range [-0x2000, 0x2000) to [0, 0x4000)
t += 0x2000;
if (t < 0) {
t = 0;
}
// [0, 16)
index = t / 0x400;
// Include extra cell if close to boundary
//! Some wall checks are larger than the buffer, meaning wall checks can
// miss walls that are near a cell border.
if (t % 0x400 < 50) {
index -= 1;
}
if (index < 0) {
index = 0;
}
// Potentially > 15, but since the upper index is <= 15, not exploitable
return index;
}
/**
* Every level is split into 16 * 16 cells of surfaces (to limit computing
* time). This function determines the upper cell for a given x/z position.
*/
static s16 upper_cell_index(s16 t) {
s16 index;
// Move from range [-0x2000, 0x2000) to [0, 0x4000)
t += 0x2000;
if (t < 0) {
t = 0;
}
// [0, 16)
index = t / 0x400;
// Include extra cell if close to boundary
//! Some wall checks are larger than the buffer, meaning wall checks can
// miss walls that are near a cell border.
if (t % 0x400 > 0x400 - 50) {
index += 1;
}
if (index > 15) {
index = 15;
}
// Potentially < 0, but since lower index is >= 0, not exploitable
return index;
}
/**
* Every level is split into 16x16 cells, this takes a surface, finds
* the appropriate cells (with a buffer), and adds the surface to those
* cells.
*/
static void add_surface(struct Surface *surface, s32 dynamic) {
// minY/maxY maybe? s32 instead of s16, though.
UNUSED s32 unused1, unused2;
s16 minX, minZ, maxX, maxZ;
s16 minCellX, minCellZ, maxCellX, maxCellZ;
s16 cellZ, cellX;
// cellY maybe? s32 instead of s16, though.
UNUSED s32 unused3 = 0;
minX = min_3(surface->vertex1[0], surface->vertex2[0], surface->vertex3[0]);
minZ = min_3(surface->vertex1[2], surface->vertex2[2], surface->vertex3[2]);
maxX = max_3(surface->vertex1[0], surface->vertex2[0], surface->vertex3[0]);
maxZ = max_3(surface->vertex1[2], surface->vertex2[2], surface->vertex3[2]);
minCellX = lower_cell_index(minX);
maxCellX = upper_cell_index(maxX);
minCellZ = lower_cell_index(minZ);
maxCellZ = upper_cell_index(maxZ);
for (cellZ = minCellZ; cellZ <= maxCellZ; cellZ++) {
for (cellX = minCellX; cellX <= maxCellX; cellX++) {
add_surface_to_cell(dynamic, cellX, cellZ, surface);
}
}
}
static void unused_80382B6C(void) {
}
/**
* Initialize a surface from reading it's data and putting it into a surface
* stuct.
*/
static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) {
struct Surface *surface;
register s32 x1, y1, z1;
register s32 x2, y2, z2;
register s32 x3, y3, z3;
s32 maxY, minY;
f32 nx, ny, nz;
f32 mag;
s16 offset1, offset2, offset3;
offset1 = 3 * (*vertexIndices)[0];
offset2 = 3 * (*vertexIndices)[1];
offset3 = 3 * (*vertexIndices)[2];
x1 = *(vertexData + offset1 + 0);
y1 = *(vertexData + offset1 + 1);
z1 = *(vertexData + offset1 + 2);
x2 = *(vertexData + offset2 + 0);
y2 = *(vertexData + offset2 + 1);
z2 = *(vertexData + offset2 + 2);
x3 = *(vertexData + offset3 + 0);
y3 = *(vertexData + offset3 + 1);
z3 = *(vertexData + offset3 + 2);
// (v2 - v1) x (v3 - v2)
nx = (y2 - y1) * (z3 - z2) - (z2 - z1) * (y3 - y2);
ny = (z2 - z1) * (x3 - x2) - (x2 - x1) * (z3 - z2);
nz = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2);
mag = sqrtf(nx * nx + ny * ny + nz * nz);
// Could have used min_3 and max_3 for this...
minY = y1;
if (y2 < minY)
minY = y2;
if (y3 < minY)
minY = y3;
maxY = y1;
if (y2 > maxY)
maxY = y2;
if (y3 > maxY)
maxY = y3;
// Checking to make sure no DIV/0
if (mag < 0.0001) {
return NULL;
}
mag = (f32)(1.0 / mag);
nx *= mag;
ny *= mag;
nz *= mag;
surface = alloc_surface();
surface->vertex1[0] = x1;
surface->vertex2[0] = x2;
surface->vertex3[0] = x3;
surface->vertex1[1] = y1;
surface->vertex2[1] = y2;
surface->vertex3[1] = y3;
surface->vertex1[2] = z1;
surface->vertex2[2] = z2;
surface->vertex3[2] = z3;
surface->normal.x = nx;
surface->normal.y = ny;
surface->normal.z = nz;
surface->originOffset = -(nx * x1 + ny * y1 + nz * z1);
surface->lowerY = minY - 5;
surface->upperY = maxY + 5;
return surface;
}
/**
* Returns whether a surface has exertion/moves Mario
* based on the surface type.
*/
static s32 surface_has_force(s16 surfaceType) {
s32 hasForce = FALSE;
switch (surfaceType) {
case SURFACE_0004: // Unused
case SURFACE_FLOWING_WATER:
case SURFACE_DEEP_MOVING_QUICKSAND:
case SURFACE_SHALLOW_MOVING_QUICKSAND:
case SURFACE_MOVING_QUICKSAND:
case SURFACE_HORIZONTAL_WIND:
case SURFACE_INSTANT_MOVING_QUICKSAND:
hasForce = TRUE;
break;
default:
break;
}
return hasForce;
}
/**
* Returns whether a surface should have the
* SURFACE_FLAG_NO_CAM_COLLISION flag.
*/
static s32 surf_has_no_cam_collision(s16 surfaceType) {
s32 flags = 0;
switch (surfaceType) {
case SURFACE_NO_CAM_COLLISION:
case SURFACE_NO_CAM_COLLISION_77: // Unused
case SURFACE_NO_CAM_COL_VERY_SLIPPERY:
case SURFACE_SWITCH:
flags = SURFACE_FLAG_NO_CAM_COLLISION;
break;
default:
break;
}
return flags;
}
/**
* Load in the surfaces for a given surface type. This includes setting the flags,
* exertion, and room.
*/
static void load_static_surfaces(s16 **data, s16 *vertexData, s16 surfaceType, s8 **surfaceRooms) {
s32 i;
s32 numSurfaces;
struct Surface *surface;
s8 room = 0;
s16 hasForce = surface_has_force(surfaceType);
s16 flags = surf_has_no_cam_collision(surfaceType);
numSurfaces = *(*data);
*data += 1;
for (i = 0; i < numSurfaces; i++) {
if (*surfaceRooms != NULL) {
room = *(*surfaceRooms);
*surfaceRooms += 1;
}
surface = read_surface_data(vertexData, data);
if (surface != NULL) {
surface->room = room;
surface->type = surfaceType;
surface->flags = (s8) flags;
if (hasForce) {
surface->force = *(*data + 3);
} else {
surface->force = 0;
}
add_surface(surface, FALSE);
}
*data += 3;
if (hasForce) {
*data += 1;
}
}
}
/**
* Read the data for vertices for reference by triangles.
*/
static s16 *read_vertex_data(s16 **data) {
s32 numVertices;
UNUSED s16 unused1[3];
UNUSED s16 unused2[3];
s16 *vertexData;
numVertices = *(*data);
(*data)++;
vertexData = *data;
*data += 3 * numVertices;
return vertexData;
}
/**
* Loads in special environmental regions, such as water,
* poison gas, and JRB fog.
*/
static void load_environmental_regions(s16 **data) {
s32 numRegions;
s32 i;
gEnvironmentRegions = *data;
numRegions = *(*data)++;
if (numRegions > 20) {
}
for (i = 0; i < numRegions; i++) {
UNUSED s16 val, loX, loZ, hiX, hiZ;
s16 height;
val = *(*data)++;
loX = *(*data)++;
hiX = *(*data)++;
loZ = *(*data)++;
hiZ = *(*data)++;
height = *(*data)++;
gEnvironmentLevels[i] = height;
}
}
/**
* Allocate some of the main pool for surfaces (2300 surf) and for surface nodes (7000 nodes).
*/
void alloc_surface_pools(void) {
sSurfacePoolSize = 2300;
sSurfaceNodePool = main_pool_alloc(7000 * sizeof(struct SurfaceNode), MEMORY_POOL_LEFT);
sSurfacePool = main_pool_alloc(sSurfacePoolSize * sizeof(struct Surface), MEMORY_POOL_LEFT);
gCCMEnteredSlide = 0;
func_802DA4DC();
}
/**
* Process the level file, loading in vertices, surfaces, some objects, and environmental
* boxes (water, gas, JRB fog).
*/
void load_area_terrain(s16 index, s16 *data, s8 *surfaceRooms, s16 *macroObjects) {
s16 terrainLoadType;
s16 *vertexData;
UNUSED s32 unused;
// Initialize the data for this.
gEnvironmentRegions = NULL;
unused8038BE90 = 0;
gSurfaceNodesAllocated = 0;
gSurfacesAllocated = 0;
clear_static_surfaces();
// A while loop interating through each section of the level data. Sections of data
// are prefixed by a terrain "type." This type is reused for surfaces as the surface
// type.
while (TRUE) {
terrainLoadType = *data;
data++;
if (TERRAIN_LOAD_IS_SURFACE_TYPE_LOW(terrainLoadType)) {
load_static_surfaces(&data, vertexData, terrainLoadType, &surfaceRooms);
} else if (terrainLoadType == TERRAIN_LOAD_VERTICES) {
vertexData = read_vertex_data(&data);
} else if (terrainLoadType == TERRAIN_LOAD_OBJECTS) {
spawn_special_objects(index, &data);
} else if (terrainLoadType == TERRAIN_LOAD_ENVIRONMENT) {
load_environmental_regions(&data);
} else if (terrainLoadType == TERRAIN_LOAD_CONTINUE) {
continue;
} else if (terrainLoadType == TERRAIN_LOAD_END) {
break;
} else if (TERRAIN_LOAD_IS_SURFACE_TYPE_HIGH(terrainLoadType)) {
load_static_surfaces(&data, vertexData, terrainLoadType, &surfaceRooms);
continue;
}
}
if (macroObjects != NULL && *macroObjects != -1) {
// If the first macro object presetID is within the range [0, 29].
// Generally an early spawning method, every object is in BBH (the first level).
if (0 <= *macroObjects && *macroObjects < 30) {
spawn_macro_objects_hardcoded(index, macroObjects);
}
// A more general version that can spawn more objects.
else {
spawn_macro_objects(index, macroObjects);
}
}
gNumStaticSurfaceNodes = gSurfaceNodesAllocated;
gNumStaticSurfaces = gSurfacesAllocated;
}
/**
* If not in time stop, clear the surface partitions.
*/
void clear_dynamic_surfaces(void) {
if (!(gTimeStopState & TIME_STOP_ACTIVE)) {
gSurfacesAllocated = gNumStaticSurfaces;
gSurfaceNodesAllocated = gNumStaticSurfaceNodes;
clear_spatial_partition(&gDynamicSurfacePartition[0][0]);
}
}
static void unused_80383604(void) {
}
/**
* Applies an object's tranformation to the object's vertices.
*/
static void transform_object_vertices(s16 **data, s16 *vertexData) {
register s16 *vertices;
register s32 numVertices;
register f32 vx;
register f32 vy;
register f32 vz;
Mat4 *objectTransform;
Mat4 m;
UNUSED s16 unused;
objectTransform = &gCurrentObject->transform;
numVertices = *(*data);
(*data)++;
vertices = *data;
if (gCurrentObject->header.gfx.throwMatrix == NULL) {
gCurrentObject->header.gfx.throwMatrix = objectTransform;
build_object_transform_from_pos_and_angle(gCurrentObject, O_POS_INDEX, O_FACE_ANGLE_INDEX);
}
apply_object_scale_to_matrix(gCurrentObject, m, *objectTransform);
// Go through all vertices, rotating and translating them to transform the object.
while (numVertices--) {
vx = *(vertices++);
vy = *(vertices++);
vz = *(vertices++);
//! No bounds check on vertex data
*vertexData++ = (s16)(vx * m[0][0] + vy * m[1][0] + vz * m[2][0] + m[3][0]);
*vertexData++ = (s16)(vx * m[0][1] + vy * m[1][1] + vz * m[2][1] + m[3][1]);
*vertexData++ = (s16)(vx * m[0][2] + vy * m[1][2] + vz * m[2][2] + m[3][2]);
}
*data = vertices;
}
/**
* Load in the surfaces for the gCurrentObject. This includes setting the flags,
* exertion, and room.
*/
static void load_object_surfaces(s16 **data, s16 *vertexData) {
s32 surfaceType;
s32 i;
s32 numSurfaces;
s16 hasForce;
s16 flags;
s16 room;
surfaceType = *(*data);
(*data)++;
numSurfaces = *(*data);
(*data)++;
hasForce = surface_has_force(surfaceType);
flags = surf_has_no_cam_collision(surfaceType);
flags |= SURFACE_FLAG_DYNAMIC;
// The DDD warp is initially loaded at the origin and moved to the proper
// position in paintings.c and doesn't update its room, so set it here.
if (gCurrentObject->behavior == segmented_to_virtual(bhvDddWarp)) {
room = 5;
} else {
room = 0;
}
for (i = 0; i < numSurfaces; i++) {
struct Surface *surface = read_surface_data(vertexData, data);
if (surface != NULL) {
surface->object = gCurrentObject;
surface->type = surfaceType;
if (hasForce) {
surface->force = *(*data + 3);
} else {
surface->force = 0;
}
surface->flags |= flags;
surface->room = (s8) room;
add_surface(surface, TRUE);
}
if (hasForce) {
*data += 4;
} else {
*data += 3;
}
}
}
/**
* Transform an object's vertices, reload them, and render the object.
*/
void load_object_collision_model(void) {
UNUSED s32 unused;
s16 vertexData[600];
s16 *collisionData = gCurrentObject->collisionData;
f32 marioDist = gCurrentObject->oDistanceToMario;
f32 tangibleDist = gCurrentObject->oCollisionDistance;
// On an object's first frame, the distance is set to 19000.0f.
// If the distance hasn't been updated, update it now.
if (gCurrentObject->oDistanceToMario == 19000.0f) {
marioDist = dist_between_objects(gCurrentObject, gMarioObject);
}
// If the object collision is supposed to be loaded more than the
// drawing distance of 4000, extend the drawing range.
if (gCurrentObject->oCollisionDistance > 4000.0f) {
gCurrentObject->oDrawingDistance = gCurrentObject->oCollisionDistance;
}
// Update if no Time Stop, in range, and in the current room.
if (!(gTimeStopState & TIME_STOP_ACTIVE) && marioDist < tangibleDist
&& !(gCurrentObject->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)) {
collisionData++;
transform_object_vertices(&collisionData, vertexData);
// TERRAIN_LOAD_CONTINUE acts as an "end" to the terrain data.
while (*collisionData != TERRAIN_LOAD_CONTINUE) {
load_object_surfaces(&collisionData, vertexData);
}
}
if (marioDist < gCurrentObject->oDrawingDistance) {
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
} else {
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
}
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef _SURFACE_LOAD_H
#define _SURFACE_LOAD_H
#include "types.h"
struct SurfaceNode
{
struct SurfaceNode *next;
struct Surface *surface;
};
enum
{
SPATIAL_PARTITION_FLOORS,
SPATIAL_PARTITION_CEILS,
SPATIAL_PARTITION_WALLS
};
typedef struct SurfaceNode SpatialPartitionCell[3];
// Needed for bs bss reordering memes.
extern s32 unused8038BE90;
extern SpatialPartitionCell gStaticSurfacePartition[16][16];
extern SpatialPartitionCell gDynamicSurfacePartition[16][16];
extern struct SurfaceNode *sSurfaceNodePool;
extern struct Surface *sSurfacePool;
extern s16 sSurfacePoolSize;
void alloc_surface_pools(void);
void load_area_terrain(s16 index, s16 *data, s8 *surfaceRooms, s16 *macroObjects);
void clear_dynamic_surfaces(void);
void load_object_collision_model(void);
#endif