mirror of
https://github.com/n64decomp/sm64
synced 2026-08-05 01:09:36 -04:00
init2
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user