summaryrefslogtreecommitdiff
path: root/src/code
diff options
context:
space:
mode:
authorengineer124 <engineer124engineer124@gmail.com>2022-06-20 21:14:44 +1000
committerengineer124 <engineer124engineer124@gmail.com>2022-06-20 21:14:44 +1000
commite4904450de37de36c1fa3d203d02bf8dfaed152a (patch)
tree8a0c65f248cbe1676c46f926a7463631874f8863 /src/code
parent8df8271d6c84a235d91758d4a25bebd95ccb3c21 (diff)
parent37d3934e2c6866185b3ba76bad40224336a7a1f9 (diff)
Merge branch 'master' into parameter
Diffstat (limited to 'src/code')
-rw-r--r--src/code/code_800E8EA0.c153
-rw-r--r--src/code/flg_set.c2
-rw-r--r--src/code/game.c6
-rw-r--r--src/code/sys_math_atan.c48
-rw-r--r--src/code/sys_matrix.c2
-rw-r--r--src/code/z_DLF.c1
-rw-r--r--src/code/z_actor.c20
-rw-r--r--src/code/z_demo.c2
-rw-r--r--src/code/z_effect_soft_sprite.c1
-rw-r--r--src/code/z_fcurve_data.c85
-rw-r--r--src/code/z_fcurve_data_skelanime.c247
-rw-r--r--src/code/z_kaleido_manager.c1
-rw-r--r--src/code/z_kaleido_setup.c2
-rw-r--r--src/code/z_message.c6
-rw-r--r--src/code/z_overlay.c1
-rw-r--r--src/code/z_parameter.c5
-rw-r--r--src/code/z_play.c6
-rw-r--r--src/code/z_rcp.c8
-rw-r--r--src/code/z_skelanime.c4
-rw-r--r--src/code/z_sub_s.c517
-rw-r--r--src/code/z_vismono.c177
21 files changed, 1036 insertions, 258 deletions
diff --git a/src/code/code_800E8EA0.c b/src/code/code_800E8EA0.c
index d95f53269..6fb787451 100644
--- a/src/code/code_800E8EA0.c
+++ b/src/code/code_800E8EA0.c
@@ -1,107 +1,148 @@
#include "global.h"
-void func_800E8EA0(GlobalContext* globalCtx, Actor* actor, u16 textId) {
+void Actor_ContinueText(GlobalContext* globalCtx, Actor* actor, u16 textId) {
func_80151938(globalCtx, textId);
actor->textId = textId;
}
-s32 nop_800E8ED0(UNK_TYPE4 param_1) {
- return 0;
+/**
+ * EventCheckInf does not exist, so this always returns false
+ */
+s32 Flags_GetEventChkInf(s32 flag) {
+ return false;
}
-void nop_800E8EE0(UNK_TYPE4 param_1) {
+/**
+ * EventCheckInf does not exist, so this does nothing
+ */
+void Flags_SetEventChkInf(s32 flag) {
}
-s32 nop_800E8EEC(UNK_TYPE4 param_1) {
- return 0;
+/**
+ * InfTable does not exist, so this always returns false
+ */
+s32 Flags_GetInfTable(s32 flag) {
+ return false;
}
-void nop_800E8EFC(UNK_TYPE4 param_1) {
+/**
+ * InfTable does not exist, so this does nothing
+ */
+void Flags_SetInfTable(s32 flag) {
}
-s32 func_800E8F08(Vec3s* param_1, Vec3s* param_2) {
- Math_SmoothStepToS(&param_1->y, 0, 6, 6200, 100);
- Math_SmoothStepToS(&param_1->x, 0, 6, 6200, 100);
- Math_SmoothStepToS(&param_2->y, 0, 6, 6200, 100);
- Math_SmoothStepToS(&param_2->x, 0, 6, 6200, 100);
- return 1;
+s32 Actor_TrackNone(Vec3s* headRot, Vec3s* torsoRot) {
+ Math_SmoothStepToS(&headRot->y, 0, 6, 0x1838, 0x64);
+ Math_SmoothStepToS(&headRot->x, 0, 6, 0x1838, 0x64);
+ Math_SmoothStepToS(&torsoRot->y, 0, 6, 0x1838, 0x64);
+ Math_SmoothStepToS(&torsoRot->x, 0, 6, 0x1838, 0x64);
+ return true;
}
-s32 func_800E8FA4(Actor* actor, Vec3f* param_2, Vec3s* param_3, Vec3s* param_4) {
+s32 Actor_TrackPoint(Actor* actor, Vec3f* target, Vec3s* headRot, Vec3s* torsoRot) {
s16 targetPitch;
s16 targetYaw;
- s16 yawDiffFromTarget;
+ s16 yawDiff;
- targetPitch = Math_Vec3f_Pitch(&actor->focus.pos, param_2);
- targetYaw = Math_Vec3f_Yaw(&actor->focus.pos, param_2) - actor->world.rot.y;
+ targetPitch = Math_Vec3f_Pitch(&actor->focus.pos, target);
+ targetYaw = Math_Vec3f_Yaw(&actor->focus.pos, target) - actor->world.rot.y;
- Math_SmoothStepToS(&param_3->x, targetPitch, 6, 2000, 1);
- param_3->x = CLAMP(param_3->x, -6000, 6000);
+ Math_SmoothStepToS(&headRot->x, targetPitch, 6, 0x7D0, 1);
+ headRot->x = CLAMP(headRot->x, -0x1770, 0x1770);
- yawDiffFromTarget = Math_SmoothStepToS(&param_3->y, targetYaw, 6, 2000, 1);
- param_3->y = CLAMP(param_3->y, -8000, 8000);
+ yawDiff = Math_SmoothStepToS(&headRot->y, targetYaw, 6, 0x7D0, 1);
+ headRot->y = CLAMP(headRot->y, -0x1F40, 0x1F40);
- if (yawDiffFromTarget != 0) {
- if (ABS_ALT(param_3->y) < 8000) {
- return 0;
- }
+ if ((yawDiff != 0) && (ABS_ALT(headRot->y) < 0x1F40)) {
+ return false;
}
- Math_SmoothStepToS(&param_4->y, targetYaw - param_3->y, 4, 2000, 1);
- param_4->y = CLAMP(param_4->y, -12000, 12000);
+ Math_SmoothStepToS(&torsoRot->y, targetYaw - headRot->y, 4, 0x7D0, 1);
+ torsoRot->y = CLAMP(torsoRot->y, -0x2EE0, 0x2EE0);
- return 1;
+ return true;
}
-s32 func_800E9138(GlobalContext* globalCtx, Actor* actor, Vec3s* param_3, Vec3s* param_4, f32 param_5) {
+/**
+ * Same as Actor_TrackPlayer, except use the actor's world position as the focus point, with the height
+ * specified.
+ *
+ * @param play
+ * @param actor
+ * @param headRot the computed actor's head's rotation step
+ * @param torsoRot the computed actor's torso's rotation step
+ * @param focusHeight the height of the focus point relative to their world position
+ *
+ * @return true if rotated towards player, false if rotations were stepped back to zero.
+ *
+ * @note same note as Actor_TrackPlayer
+ */
+s32 Actor_TrackPlayerSetFocusHeight(GlobalContext* globalCtx, Actor* actor, Vec3s* headRot, Vec3s* torsoRot,
+ f32 focusHeight) {
Player* player = GET_PLAYER(globalCtx);
- s16 sVar3;
- Vec3f local_14;
+ s16 yaw;
+ Vec3f target;
actor->focus.pos = actor->world.pos;
- actor->focus.pos.y += param_5;
+ actor->focus.pos.y += focusHeight;
- if (((globalCtx->csCtx).state == 0) && (D_801D0D50 == 0)) {
- sVar3 = ABS_ALT(BINANG_SUB(actor->yawTowardsPlayer, actor->shape.rot.y));
- if (sVar3 >= 0x4300) {
- func_800E8F08(param_3, param_4);
- return 0;
+ if (!((globalCtx->csCtx.state != 0) || gDbgCamEnabled)) {
+ yaw = ABS_ALT(BINANG_SUB(actor->yawTowardsPlayer, actor->shape.rot.y));
+ if (yaw >= 0x4300) {
+ Actor_TrackNone(headRot, torsoRot);
+ return false;
}
}
- if (((globalCtx->csCtx).state != 0) || (D_801D0D50 != 0)) {
- local_14 = globalCtx->view.eye;
+ if ((globalCtx->csCtx.state != 0) || gDbgCamEnabled) {
+ target = globalCtx->view.eye;
} else {
- local_14 = player->actor.focus.pos;
+ target = player->actor.focus.pos;
}
- func_800E8FA4(actor, &local_14, param_3, param_4);
+ Actor_TrackPoint(actor, &target, headRot, torsoRot);
- return 1;
+ return true;
}
-s32 func_800E9250(GlobalContext* globalCtx, Actor* actor, Vec3s* param_3, Vec3s* param_4, Vec3f param_5) {
+/**
+ * Computes the necessary HeadRot and TorsoRot steps to be added to the normal rotation to smoothly turn an actors's
+ * head and torso towards the player if within a certain yaw, else smoothly returns the rotations back to zero.
+ * Also sets the focus position with the specified point.
+ *
+ * @param play
+ * @param actor
+ * @param headRot the computed actor's head's rotation step
+ * @param torsoRot the computed actor's torso's rotation step
+ * @param focusPos the point to set as the actor's focus position
+ *
+ * @return true if rotated towards player, false if rotations were stepped back to zero.
+ *
+ * @note if in a cutscene or debug camera is enabled, the computed rotation will instead turn towards the view eye no
+ * matter the yaw.
+ */
+s32 Actor_TrackPlayer(GlobalContext* globalCtx, Actor* actor, Vec3s* headRot, Vec3s* torsoRot, Vec3f focusPos) {
Player* player = GET_PLAYER(globalCtx);
- s16 sVar3;
- Vec3f local_14;
+ s16 yaw;
+ Vec3f target;
- actor->focus.pos = param_5;
+ actor->focus.pos = focusPos;
- if (((globalCtx->csCtx).state == 0) && (D_801D0D50 == 0)) {
- sVar3 = ABS_ALT(BINANG_SUB(actor->yawTowardsPlayer, actor->shape.rot.y));
- if (sVar3 >= 0x4300) {
- func_800E8F08(param_3, param_4);
- return 0;
+ if (!((globalCtx->csCtx.state != 0) || gDbgCamEnabled)) {
+ yaw = ABS_ALT(BINANG_SUB(actor->yawTowardsPlayer, actor->shape.rot.y));
+ if (yaw >= 0x4300) {
+ Actor_TrackNone(headRot, torsoRot);
+ return false;
}
}
- if (((globalCtx->csCtx).state != 0) || (D_801D0D50 != 0)) {
- local_14 = globalCtx->view.eye;
+ if ((globalCtx->csCtx.state != 0) || gDbgCamEnabled) {
+ target = globalCtx->view.eye;
} else {
- local_14 = player->actor.focus.pos;
+ target = player->actor.focus.pos;
}
- func_800E8FA4(actor, &local_14, param_3, param_4);
+ Actor_TrackPoint(actor, &target, headRot, torsoRot);
- return 1;
+ return true;
}
diff --git a/src/code/flg_set.c b/src/code/flg_set.c
index 53be7b68d..715d3756f 100644
--- a/src/code/flg_set.c
+++ b/src/code/flg_set.c
@@ -135,7 +135,7 @@ static s32 sTimer = 0;
void FlagSet_Update(GameState* gameState) {
GlobalContext* globalCtx = (GlobalContext*)gameState;
- Input* input = CONTROLLER1(globalCtx);
+ Input* input = CONTROLLER1(&globalCtx->state);
/* Intra-byte navigation */
diff --git a/src/code/game.c b/src/code/game.c
index 2713be299..6f796bc12 100644
--- a/src/code/game.c
+++ b/src/code/game.c
@@ -52,7 +52,7 @@ void GameState_SetFBFilter(Gfx** gfx, u32 arg1) {
sMonoColors.envColor.g = R_FB_FILTER_ENV_COLOR(1);
sMonoColors.envColor.b = R_FB_FILTER_ENV_COLOR(2);
sMonoColors.envColor.a = R_FB_FILTER_A;
- VisMono_Draw(&sMonoColors, &dlist, arg1);
+ VisMono_Draw(&sMonoColors, &dlist);
}
}
}
@@ -213,7 +213,7 @@ void GameState_Init(GameState* gameState, GameStateFunc init, GraphicsContext* g
func_80140CE0(&D_801F8010);
func_801420C0(&D_801F8020);
- func_801418B0(&sMonoColors);
+ VisMono_Init(&sMonoColors);
func_80140898(&D_801F8048);
func_801773A0(&D_801F7FF0);
func_8013ED9C();
@@ -235,7 +235,7 @@ void GameState_Destroy(GameState* gameState) {
func_801773C4(&D_801F7FF0);
func_80140D04(&D_801F8010);
func_801420F4(&D_801F8020);
- func_80141900(&sMonoColors);
+ VisMono_Destroy(&sMonoColors);
func_80140900(&D_801F8048);
THA_Dt(&gameState->heap);
GameAlloc_Cleanup(&gameState->alloc);
diff --git a/src/code/sys_math_atan.c b/src/code/sys_math_atan.c
index 09f355d64..d68e1e293 100644
--- a/src/code/sys_math_atan.c
+++ b/src/code/sys_math_atan.c
@@ -77,57 +77,57 @@ u16 sATan2Tbl[] = {
0x1FF6, 0x1FFB, 0x2000,
};
-u16 Math_GetAtan2Tbl(f32 opposite, f32 adjacent) {
- return sATan2Tbl[(s32)((opposite / adjacent) * 0x400)];
+u16 Math_GetAtan2Tbl(f32 y, f32 x) {
+ return sATan2Tbl[(s32)((y / x) * 0x400)];
}
-s16 Math_Atan2S(f32 opposite, f32 adjacent) {
+s16 Math_Atan2S(f32 y, f32 x) {
s32 angle;
- if (opposite == 0.0f) {
- if (adjacent >= 0.0f) {
+ if (y == 0.0f) {
+ if (x >= 0.0f) {
angle = 0;
} else {
angle = 0x8000;
}
- } else if (adjacent == 0.0f) {
- if (opposite >= 0.0f) {
+ } else if (x == 0.0f) {
+ if (y >= 0.0f) {
angle = 0x4000;
} else {
angle = 0xC000;
}
- } else if (opposite >= 0.0f) {
- if (adjacent >= 0.0f) {
- if (opposite <= adjacent) {
- angle = Math_GetAtan2Tbl(opposite, adjacent);
+ } else if (y >= 0.0f) {
+ if (x >= 0.0f) {
+ if (y <= x) {
+ angle = Math_GetAtan2Tbl(y, x);
} else {
- angle = 0x4000 - Math_GetAtan2Tbl(adjacent, opposite);
+ angle = 0x4000 - Math_GetAtan2Tbl(x, y);
}
} else {
- if (-adjacent < opposite) {
- angle = Math_GetAtan2Tbl(-adjacent, opposite) + 0x4000;
+ if (-x < y) {
+ angle = Math_GetAtan2Tbl(-x, y) + 0x4000;
} else {
- angle = 0x8000 - Math_GetAtan2Tbl(opposite, -adjacent);
+ angle = 0x8000 - Math_GetAtan2Tbl(y, -x);
}
}
- } else if (adjacent < 0.0f) {
- if (-opposite <= -adjacent) {
- angle = Math_GetAtan2Tbl(-opposite, -adjacent) + 0x8000;
+ } else if (x < 0.0f) {
+ if (-y <= -x) {
+ angle = Math_GetAtan2Tbl(-y, -x) + 0x8000;
} else {
- angle = 0xC000 - Math_GetAtan2Tbl(-adjacent, -opposite);
+ angle = 0xC000 - Math_GetAtan2Tbl(-x, -y);
}
} else {
- if (adjacent < -opposite) {
- angle = Math_GetAtan2Tbl(adjacent, -opposite) + 0xC000;
+ if (x < -y) {
+ angle = Math_GetAtan2Tbl(x, -y) + 0xC000;
} else {
- angle = -Math_GetAtan2Tbl(-opposite, adjacent);
+ angle = -Math_GetAtan2Tbl(-y, x);
}
}
return angle;
}
-f32 Math_Atan2F(f32 opposite, f32 adjacent) {
- return Math_Atan2S(opposite, adjacent) * (M_PI / 0x8000);
+f32 Math_Atan2F(f32 y, f32 x) {
+ return Math_Atan2S(y, x) * (M_PI / 0x8000);
}
s16 Math_FAtan2F(f32 adjacent, f32 opposite) {
diff --git a/src/code/sys_matrix.c b/src/code/sys_matrix.c
index f2ed19c38..4e517c810 100644
--- a/src/code/sys_matrix.c
+++ b/src/code/sys_matrix.c
@@ -38,7 +38,7 @@
* Throughout this file, `mode` indicates whether to multiply the matrix on top of the stack by the new construction
* (APPLY), or to just overwrite it (NEW).
*/
-
+#include "prevent_bss_reordering.h"
#include "global.h"
/* data */
diff --git a/src/code/z_DLF.c b/src/code/z_DLF.c
index 4c57aca01..862463b76 100644
--- a/src/code/z_DLF.c
+++ b/src/code/z_DLF.c
@@ -1,5 +1,6 @@
#include "global.h"
#include "system_malloc.h"
+#include "z64load.h"
#pragma GLOBAL_ASM("asm/non_matchings/code/z_DLF/Overlay_LoadGameState.s")
diff --git a/src/code/z_actor.c b/src/code/z_actor.c
index d933843c5..a937dc865 100644
--- a/src/code/z_actor.c
+++ b/src/code/z_actor.c
@@ -4,6 +4,7 @@
*/
#include "global.h"
+#include "z64load.h"
#include "overlays/actors/ovl_En_Horse/z_en_horse.h"
#include "overlays/actors/ovl_En_Part/z_en_part.h"
#include "overlays/actors/ovl_En_Box/z_en_box.h"
@@ -2003,11 +2004,11 @@ s32 Actor_PickUp(Actor* actor, GlobalContext* globalCtx, s32 getItemId, f32 xzRa
if (!(player->stateFlags1 & 0x3C7080) && Player_GetExplosiveHeld(player) < 0) {
if ((actor->xzDistToPlayer <= xzRange) && (fabsf(actor->playerHeightRel) <= fabsf(yRange))) {
- if (((getItemId == GI_MASK_CIRCUS_LEADER) || (getItemId == GI_PENDANT_OF_MEMORIES) ||
- (getItemId == GI_DEED_LAND) ||
- (((player->heldActor != NULL) || (actor == player->targetActor)) &&
+ if ((getItemId == GI_MASK_CIRCUS_LEADER || getItemId == GI_PENDANT_OF_MEMORIES ||
+ getItemId == GI_DEED_LAND ||
+ ((player->heldActor != NULL || actor == player->targetActor) &&
(getItemId > GI_NONE && getItemId < GI_MAX))) ||
- (!(player->stateFlags1 & 0x20000800))) {
+ !(player->stateFlags1 & 0x20000800)) {
s16 yawDiff = actor->yawTowardsPlayer - player->actor.shape.rot.y;
s32 absYawDiff = ABS_ALT(yawDiff);
@@ -4281,7 +4282,7 @@ s16 func_800BDB6C(Actor* actor, GlobalContext* globalCtx, s16 arg2, f32 arg3) {
Player* player = GET_PLAYER(globalCtx);
f32 phi_f2;
- if ((globalCtx->csCtx.state != 0) || (D_801D0D50 != 0)) {
+ if ((globalCtx->csCtx.state != 0) || gDbgCamEnabled) {
phi_f2 = Math_Vec3f_DistXYZ(&actor->world.pos, &globalCtx->view.eye) * 0.25f;
} else {
phi_f2 = Math_Vec3f_DistXYZ(&actor->world.pos, &player->actor.world.pos);
@@ -4483,9 +4484,10 @@ void func_800BE5CC(Actor* actor, ColliderJntSph* collider, s32 colliderIndex) {
}
}
-s32 func_800BE63C(struct EnBox* chest) {
- if ((chest->unk_1F1 == 5) || (chest->unk_1F1 == 6) || (chest->unk_1F1 == 7) || (chest->unk_1F1 == 8) ||
- (chest->unk_1F1 == 0xC)) {
+s32 Actor_IsSmallChest(struct EnBox* chest) {
+ if (chest->type == ENBOX_TYPE_SMALL || chest->type == ENBOX_TYPE_SMALL_INVISIBLE ||
+ chest->type == ENBOX_TYPE_SMALL_ROOM_CLEAR || chest->type == ENBOX_TYPE_SMALL_SWITCH_FLAG_FALL ||
+ chest->type == ENBOX_TYPE_SMALL_SWITCH_FLAG) {
return true;
}
return false;
@@ -4781,7 +4783,7 @@ void Actor_SpawnIceEffects(GlobalContext* globalCtx, Actor* actor, Vec3f limbPos
yaw = Actor_YawToPoint(actor, limbPos);
for (j = 0; j < effectsPerLimb; j++) {
- randomYaw = (Rand_Next() >> 0x13) + yaw;
+ randomYaw = ((s32)Rand_Next() >> 0x13) + yaw;
velocity.z = Rand_ZeroFloat(5.0f);
diff --git a/src/code/z_demo.c b/src/code/z_demo.c
index d4c9ad97a..32f193a41 100644
--- a/src/code/z_demo.c
+++ b/src/code/z_demo.c
@@ -917,7 +917,7 @@ s32 Cutscene_Command_Camera(GlobalContext* globalCtx, u8* cmd) {
bcopy(cmd, &sp1C, sizeof(s32));
cmd += sizeof(s32);
- if (func_8016A168() == 0) {
+ if (!Play_IsDebugCamEnabled()) {
func_80161998(cmd, &sCutsceneCameraInfo);
}
return sp1C + sizeof(s32);
diff --git a/src/code/z_effect_soft_sprite.c b/src/code/z_effect_soft_sprite.c
index fd1d0212c..56eab9e8a 100644
--- a/src/code/z_effect_soft_sprite.c
+++ b/src/code/z_effect_soft_sprite.c
@@ -1,4 +1,5 @@
#include "global.h"
+#include "z64load.h"
EffectSsInfo sEffectSsInfo = { NULL, 0, 0 };
diff --git a/src/code/z_fcurve_data.c b/src/code/z_fcurve_data.c
index a0bf887e1..5c0aa066a 100644
--- a/src/code/z_fcurve_data.c
+++ b/src/code/z_fcurve_data.c
@@ -1,5 +1,86 @@
+/**
+ * @file z_fcurve_data.c
+ * @brief Interpolation functions for use with Curve SkelAnime
+ */
#include "global.h"
+#include "z64curve.h"
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_fcurve_data/func_800F23E0.s")
+#define FCURVE_INTERP_CUBIC 0 // Interpolate using a Hermite cubic spline
+#define FCURVE_INTERP_NONE 1 // Return the value at the left endpoint instead of interpolating
+#define FCURVE_INTERP_LINEAR 2 // Interpolate linearly
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_fcurve_data/func_800F2478.s")
+/**
+ * Hermite cubic spline interpolation between two endpoints, a,b. More information available at
+ * https://en.wikipedia.org/wiki/Cubic_Hermite_spline
+ *
+ * @param t interpolation parameter rescaled to lie in [0,1], (x-a)/(b-a)
+ * @param interval distance (b-a) between the endpoints
+ * @param y0 p(a)
+ * @param y1 p(b)
+ * @param m0 p'(a)
+ * @param m1 p'(b)
+ * @return f32 p(t), value of the cubic interpolating polynomial
+ */
+f32 Curve_CubicHermiteSpline(f32 t, f32 interval, f32 y0, f32 y1, f32 m0, f32 m1) {
+ f32 t2 = t * t;
+ f32 t3 = t2 * t;
+ f32 t3x2 = t3 * 2.0f;
+ f32 t2x3 = t2 * 3.0f;
+
+ // Hermite basis cubics h_{ij} satisfy h_{ij}^{(j)}(i) = 1, the other three values being 0
+ f32 h00 = t3x2 - t2x3 + 1.0f; // h_{00}(t) = 2t^3 - 3t^2 + 1
+ f32 h01 = t2x3 - t3x2; // h_{01}(t) = 3t^2 - 2t^3
+ f32 h10 = t3 - t2 * 2.0f + t; // h_{10}(t) = t^3 - 2t^2 + t
+ f32 h11 = t3 - t2; // h_{11}(t) = t^3 - t^2
+
+ f32 ret = h00 * y0;
+
+ ret += h01 * y1;
+ ret += h10 * m0 * interval;
+ ret += h11 * m1 * interval;
+
+ return ret;
+}
+
+/**
+ * Interpolates based on an array of CurveInterpKnot.
+ *
+ * @param x point at which to interpolate.
+ * @param knots Beginning of CurveInterpKnot array to use.
+ * @param knotCount number of knots to read from the array.
+ * @return f32 interpolated value
+ */
+f32 Curve_Interpolate(f32 x, CurveInterpKnot* knots, s32 knotCount) {
+ // If outside the entire interpolation interval, return the value at the near endpoint.
+ if (x <= knots[0].abscissa) {
+ return knots[0].ordinate;
+ } else if (x >= knots[knotCount - 1].abscissa) {
+ return knots[knotCount - 1].ordinate;
+ } else {
+ s32 cur;
+
+ for (cur = 0;; cur++) {
+ s32 next = cur + 1;
+
+ // Find the subinterval in which x lies
+ if (x < knots[next].abscissa) {
+ if (knots[cur].flags & FCURVE_INTERP_NONE) {
+ // No interpolation
+ return knots[cur].ordinate;
+ } else if (knots[cur].flags & FCURVE_INTERP_LINEAR) {
+ // Linear interpolation
+ return knots[cur].ordinate +
+ ((x - (f32)knots[cur].abscissa) / ((f32)knots[next].abscissa - (f32)knots[cur].abscissa)) *
+ (knots[next].ordinate - knots[cur].ordinate);
+ } else {
+ // Cubic interpolation
+ f32 diff = (f32)knots[next].abscissa - (f32)knots[cur].abscissa;
+ f32 t = (x - (f32)knots[cur].abscissa) / ((f32)knots[next].abscissa - (f32)knots[cur].abscissa);
+
+ return Curve_CubicHermiteSpline(t, diff * (1.0f / 30.0f), knots[cur].ordinate, knots[next].ordinate,
+ knots[cur].rightGradient, knots[next].leftGradient);
+ }
+ }
+ }
+ }
+}
diff --git a/src/code/z_fcurve_data_skelanime.c b/src/code/z_fcurve_data_skelanime.c
index c8eb927fa..b40a1c351 100644
--- a/src/code/z_fcurve_data_skelanime.c
+++ b/src/code/z_fcurve_data_skelanime.c
@@ -1,130 +1,187 @@
+/**
+ * @file z_fcurve_data_skelanime.c
+ * @brief Curve skeleton animation system
+ *
+ * A curve skeleton has a fixed number of limbs, each of which has 9 properties that may be changed by the animation:
+ * - 3 scales,
+ * - 3 rotations,
+ * - 3 positions
+ * (note the position is stored in the animations instead of being stored in the limbs like SkelAnime would). Otherwise
+ * the structure is similar to an ordinary SkelAnime-compatible skeleton.
+ *
+ * The animations are significantly more complex than SkelAnime. A curve animation consists of 4 parts:
+ * - a header (CurveAnimationHeader)
+ * - a list of counts, one for each of the 9 properties of each limb (u8)
+ * - a list of interpolation data (CurveInterpKnot). The length is the sum of the counts.
+ * - a list of constant data (s16[9]). The length is the number of 0s in counts.
+ *
+ * If the interpolation count for a property is 0, the value of the property is copied from the next number in the
+ * constant data; there are no gaps for nonzero interpolation count.
+ * If the interpolation count N for a property is larger than 0, the next N elements of the interpolation data array
+ * are used to interpolate the value of the property, using Curve_Interpolate.
+ *
+ * Curve limbs may use LOD:
+ * - lower detail draws only the first displaylist
+ * - higher detail draws both.
+ */
+
#include "global.h"
+#include "z64curve.h"
-void SkelCurve_Clear(SkelAnimeCurve* skelCurve) {
+void SkelCurve_Clear(SkelCurve* skelCurve) {
skelCurve->limbCount = 0;
- skelCurve->limbList = NULL;
- skelCurve->transUpdIdx = NULL;
- skelCurve->animCurFrame = 0.0f;
- skelCurve->animSpeed = 0.0f;
- skelCurve->animFinalFrame = 0.0f;
- skelCurve->unk0C = 0.0f;
- skelCurve->transforms = NULL;
+ skelCurve->skeleton = NULL;
+ skelCurve->animation = NULL;
+ skelCurve->curFrame = 0.0f;
+ skelCurve->playSpeed = 0.0f;
+ skelCurve->endFrame = 0.0f;
+ skelCurve->unk_0C = 0.0f;
+ skelCurve->jointTable = NULL;
}
-s32 SkelCurve_Init(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, SkelCurveLimbList* limbListSeg,
- TransformUpdateIndex* transUpdIdx) {
+/**
+ * Initialises the SkelCurve struct and mallocs the joint table.
+ *
+ * @return bool always true
+ */
+s32 SkelCurve_Init(GlobalContext* globalCtx, SkelCurve* skelCurve, CurveSkeletonHeader* skeletonHeaderSeg,
+ CurveAnimationHeader* animation) {
SkelCurveLimb** limbs;
- SkelCurveLimbList* limbList = Lib_SegmentedToVirtual(limbListSeg);
+ CurveSkeletonHeader* skeletonHeader = Lib_SegmentedToVirtual(skeletonHeaderSeg);
- skelCurve->limbCount = limbList->limbCount;
- skelCurve->limbList = Lib_SegmentedToVirtual(limbList->limbs);
+ skelCurve->limbCount = skeletonHeader->limbCount;
+ skelCurve->skeleton = Lib_SegmentedToVirtual(skeletonHeader->limbs);
- skelCurve->transforms = ZeldaArena_Malloc(sizeof(*skelCurve->transforms) * skelCurve->limbCount);
+ skelCurve->jointTable = ZeldaArena_Malloc(sizeof(*skelCurve->jointTable) * skelCurve->limbCount);
- do {
- skelCurve->animCurFrame = 0.0f;
- } while (0);
- return 1;
+ skelCurve->curFrame = 0.0f;
+ return true;
}
-void SkelCurve_Destroy(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve) {
- if (skelCurve->transforms != NULL) {
- ZeldaArena_Free(skelCurve->transforms);
+/**
+ * Frees the joint table.
+ */
+void SkelCurve_Destroy(GlobalContext* globalCtx, SkelCurve* skelCurve) {
+ if (skelCurve->jointTable != NULL) {
+ ZeldaArena_Free(skelCurve->jointTable);
}
}
-void SkelCurve_SetAnim(SkelAnimeCurve* skelCurve, TransformUpdateIndex* transUpdIdx, f32 arg2, f32 animFinalFrame,
- f32 animCurFrame, f32 animSpeed) {
- skelCurve->unk0C = arg2 - skelCurve->animSpeed;
- skelCurve->animFinalFrame = animFinalFrame;
- skelCurve->animCurFrame = animCurFrame;
- skelCurve->animSpeed = animSpeed;
- skelCurve->transUpdIdx = transUpdIdx;
+void SkelCurve_SetAnim(SkelCurve* skelCurve, CurveAnimationHeader* animation, f32 arg2, f32 endFrame, f32 curFrame,
+ f32 playSpeed) {
+ skelCurve->unk_0C = arg2 - skelCurve->playSpeed;
+ skelCurve->endFrame = endFrame;
+ skelCurve->curFrame = curFrame;
+ skelCurve->playSpeed = playSpeed;
+ skelCurve->animation = animation;
}
-#ifdef NON_MATCHING
-/* Should be functionally equivalent, also migrating rodata makes it a lot cleaner */
-s32 SkelCurve_Update(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve) {
- s16* transforms;
- u8* transformRefIdx;
- TransformUpdateIndex* transformIndex;
- u16* transformCopyValues;
- s32 i;
- s32 ret = 0;
- s32 k;
- TransformData* transData;
- f32 transformValue;
- s32 j;
-
- transformIndex = Lib_SegmentedToVirtual(skelCurve->transUpdIdx);
- transformRefIdx = Lib_SegmentedToVirtual(transformIndex->refIndex);
- transData = Lib_SegmentedToVirtual(transformIndex->transformData);
- transformCopyValues = Lib_SegmentedToVirtual(transformIndex->copyValues);
- transforms = (s16*)skelCurve->transforms;
-
- skelCurve->animCurFrame += skelCurve->animSpeed * (globalCtx->state.framerateDivisor * 0.5f);
-
- if ((skelCurve->animSpeed >= 0.0f && skelCurve->animCurFrame > skelCurve->animFinalFrame) ||
- (skelCurve->animSpeed < 0.0f && skelCurve->animCurFrame < skelCurve->animFinalFrame)) {
- skelCurve->animCurFrame = skelCurve->animFinalFrame;
- ret = 1;
+typedef enum {
+ /* 0 */ SKELCURVE_VEC_TYPE_SCALE,
+ /* 1 */ SKELCURVE_VEC_TYPE_ROTATION,
+ /* 2 */ SKELCURVE_VEC_TYPE_POSIITON,
+ /* 3 */ SKELCURVE_VEC_TYPE_MAX
+} SkelCurveVecType;
+
+#define SKELCURVE_SCALE_SCALE 1024.0f
+#define SKELCURVE_SCALE_POSITION 100
+
+/**
+ * The only animation updating function.
+ *
+ * @return bool true when the animation has finished.
+ */
+s32 SkelCurve_Update(GlobalContext* globalCtx, SkelCurve* skelCurve) {
+ s16* jointData;
+ u8* knotCounts;
+ CurveAnimationHeader* animation;
+ u16* constantData;
+ s32 curLimb;
+ s32 ret = false;
+ s32 coord;
+ CurveInterpKnot* startKnot;
+ s32 vecType;
+
+ animation = Lib_SegmentedToVirtual(skelCurve->animation);
+ knotCounts = Lib_SegmentedToVirtual(animation->knotCounts);
+ startKnot = Lib_SegmentedToVirtual(animation->interpolationData);
+ constantData = Lib_SegmentedToVirtual(animation->constantData);
+ jointData = *skelCurve->jointTable;
+
+ skelCurve->curFrame += skelCurve->playSpeed * ((s32)globalCtx->state.framerateDivisor * 0.5f);
+
+ if (((skelCurve->playSpeed >= 0.0f) && (skelCurve->curFrame > skelCurve->endFrame)) ||
+ ((skelCurve->playSpeed < 0.0f) && (skelCurve->curFrame < skelCurve->endFrame))) {
+ skelCurve->curFrame = skelCurve->endFrame;
+ ret = true;
}
- for (i = 0; i < skelCurve->limbCount; i++) {
- for (j = 0; j < 3; j++) {
- for (k = 0; k < 3; k++, transformRefIdx++, transforms++) {
- if (*transformRefIdx == 0) {
- transformValue = *transformCopyValues;
- *transforms = transformValue;
- transformCopyValues++;
+ for (curLimb = 0; curLimb < skelCurve->limbCount; curLimb++) {
+
+ // scale/rotation/position
+ for (vecType = SKELCURVE_VEC_TYPE_SCALE; vecType < SKELCURVE_VEC_TYPE_MAX; vecType++) {
+
+ // x/y/z
+ for (coord = 0; coord < 3; coord++) {
+ f32 transformValue;
+
+ if (*knotCounts == 0) {
+ transformValue = *constantData;
+ *jointData = transformValue;
+ constantData++;
} else {
- transformValue = func_800F2478(skelCurve->animCurFrame, transData, *transformRefIdx);
- transData += *transformRefIdx;
- if (j == 0) {
- *transforms = transformValue * 1024.0f;
- } else if (j == 1) {
- *transforms = transformValue * (32768.0f / 180.0f);
- } else {
- *transforms = transformValue * 100.0f;
+ transformValue = Curve_Interpolate(skelCurve->curFrame, startKnot, *knotCounts);
+ startKnot += *knotCounts;
+ if (vecType == SKELCURVE_VEC_TYPE_SCALE) {
+ // Rescaling allows for more refined scaling using an s16
+ *jointData = transformValue * SKELCURVE_SCALE_SCALE;
+ } else if (vecType == SKELCURVE_VEC_TYPE_ROTATION) {
+ // Convert value from degrees to a binary angle
+ *jointData = DEG_TO_BINANG(transformValue);
+ } else { // SKELCURVE_VEC_TYPE_POSIITON
+ // Model to world scale conversion
+ *jointData = transformValue * SKELCURVE_SCALE_POSITION;
}
}
+ knotCounts++;
+ jointData++;
}
}
}
return ret;
}
-#else
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_fcurve_data_skelanime/SkelCurve_Update.s")
-#endif
-void SkelCurve_DrawLimb(GlobalContext* globalCtx, s32 limbIndex, SkelAnimeCurve* skelCurve,
+/**
+ * Recursively draws limbs with appropriate properties.
+ */
+void SkelCurve_DrawLimb(GlobalContext* globalCtx, s32 limbIndex, SkelCurve* skelCurve,
OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, Actor* thisx) {
- SkelCurveLimb* limb = Lib_SegmentedToVirtual(skelCurve->limbList[limbIndex]);
+ SkelCurveLimb* limb = Lib_SegmentedToVirtual(skelCurve->skeleton[limbIndex]);
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
- if (overrideLimbDraw == NULL ||
- (overrideLimbDraw != NULL && overrideLimbDraw(globalCtx, skelCurve, limbIndex, thisx))) {
+ if ((overrideLimbDraw == NULL) ||
+ ((overrideLimbDraw != NULL) && overrideLimbDraw(globalCtx, skelCurve, limbIndex, thisx))) {
Vec3f scale;
Vec3s rot;
Vec3f pos;
Gfx* dList;
- Vec3s* transform = (Vec3s*)&skelCurve->transforms[limbIndex];
-
- scale.x = transform->x / 1024.0f;
- scale.y = transform->y / 1024.0f;
- scale.z = transform->z / 1024.0f;
- transform++;
- rot.x = transform->x;
- rot.y = transform->y;
- rot.z = transform->z;
- transform++;
- pos.x = transform->x;
- pos.y = transform->y;
- pos.z = transform->z;
+ s16* jointData = skelCurve->jointTable[limbIndex];
+
+ scale.x = jointData[0] / SKELCURVE_SCALE_SCALE;
+ scale.y = jointData[1] / SKELCURVE_SCALE_SCALE;
+ scale.z = jointData[2] / SKELCURVE_SCALE_SCALE;
+ jointData += 3;
+ rot.x = jointData[0];
+ rot.y = jointData[1];
+ rot.z = jointData[2];
+ jointData += 3;
+ pos.x = jointData[0];
+ pos.y = jointData[1];
+ pos.z = jointData[2];
Matrix_TranslateRotateZYX(&pos, &rot);
Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY);
@@ -160,22 +217,22 @@ void SkelCurve_DrawLimb(GlobalContext* globalCtx, s32 limbIndex, SkelAnimeCurve*
postLimbDraw(globalCtx, skelCurve, limbIndex, thisx);
}
- if (limb->firstChildIdx != LIMB_DONE) {
- SkelCurve_DrawLimb(globalCtx, limb->firstChildIdx, skelCurve, overrideLimbDraw, postLimbDraw, lod, thisx);
+ if (limb->child != LIMB_DONE) {
+ SkelCurve_DrawLimb(globalCtx, limb->child, skelCurve, overrideLimbDraw, postLimbDraw, lod, thisx);
}
Matrix_Pop();
- if (limb->nextLimbIdx != LIMB_DONE) {
- SkelCurve_DrawLimb(globalCtx, limb->nextLimbIdx, skelCurve, overrideLimbDraw, postLimbDraw, lod, thisx);
+ if (limb->sibling != LIMB_DONE) {
+ SkelCurve_DrawLimb(globalCtx, limb->sibling, skelCurve, overrideLimbDraw, postLimbDraw, lod, thisx);
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
-void SkelCurve_Draw(Actor* actor, GlobalContext* globalCtx, SkelAnimeCurve* skelCurve,
+void SkelCurve_Draw(Actor* actor, GlobalContext* globalCtx, SkelCurve* skelCurve,
OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, Actor* thisx) {
- if (skelCurve->transforms != NULL) {
+ if (skelCurve->jointTable != NULL) {
SkelCurve_DrawLimb(globalCtx, 0, skelCurve, overrideLimbDraw, postLimbDraw, lod, thisx);
}
}
diff --git a/src/code/z_kaleido_manager.c b/src/code/z_kaleido_manager.c
index 5e89f49e9..73656d65d 100644
--- a/src/code/z_kaleido_manager.c
+++ b/src/code/z_kaleido_manager.c
@@ -1,4 +1,5 @@
#include "global.h"
+#include "z64load.h"
#define KALEIDO_OVERLAY(name) \
{ \
diff --git a/src/code/z_kaleido_setup.c b/src/code/z_kaleido_setup.c
index 3c9db3638..cf533bd88 100644
--- a/src/code/z_kaleido_setup.c
+++ b/src/code/z_kaleido_setup.c
@@ -60,7 +60,7 @@ void func_800F4A10(GlobalContext* globalCtx) {
}
void KaleidoSetup_Update(GlobalContext* globalCtx) {
- Input* input = CONTROLLER1(globalCtx);
+ Input* input = CONTROLLER1(&globalCtx->state);
MessageContext* msgCtx = &globalCtx->msgCtx;
Player* player = GET_PLAYER(globalCtx);
PauseContext* pauseCtx = &globalCtx->pauseCtx;
diff --git a/src/code/z_message.c b/src/code/z_message.c
index 96ce89379..104a1bfa9 100644
--- a/src/code/z_message.c
+++ b/src/code/z_message.c
@@ -67,7 +67,7 @@ void func_80147564(GlobalContext* globalCtx) {
s32 Message_ShouldAdvance(GlobalContext* globalCtx) {
MessageContext* msgCtx = &globalCtx->msgCtx;
- Input* controller = CONTROLLER1(globalCtx);
+ Input* controller = CONTROLLER1(&globalCtx->state);
if ((msgCtx->unk12020 == 0x10) || (msgCtx->unk12020 == 0x11)) {
if (CHECK_BTN_ALL(controller->press.button, BTN_A)) {
@@ -86,7 +86,7 @@ s32 Message_ShouldAdvance(GlobalContext* globalCtx) {
s32 Message_ShouldAdvanceSilent(GlobalContext* globalCtx) {
MessageContext* msgCtx = &globalCtx->msgCtx;
- Input* controller = CONTROLLER1(globalCtx);
+ Input* controller = CONTROLLER1(&globalCtx->state);
if (msgCtx->unk12020 == 0x10 || msgCtx->unk12020 == 0x11) {
return CHECK_BTN_ALL(controller->press.button, BTN_A);
@@ -117,7 +117,7 @@ void func_801477B4(GlobalContext* globalCtx) {
void func_80148B98(GlobalContext* globalCtx, u8 arg1) {
static s16 held = 0;
MessageContext* msgCtx = &globalCtx->msgCtx;
- Input* curInput = CONTROLLER1(globalCtx);
+ Input* curInput = CONTROLLER1(&globalCtx->state);
if ((curInput->rel.stick_y > 29) && held == 0) {
held = 1;
diff --git a/src/code/z_overlay.c b/src/code/z_overlay.c
index f635331c9..a358cefe6 100644
--- a/src/code/z_overlay.c
+++ b/src/code/z_overlay.c
@@ -1,4 +1,5 @@
#include "global.h"
+#include "z64load.h"
#pragma GLOBAL_ASM("asm/non_matchings/code/z_overlay/func_801651B0.s")
diff --git a/src/code/z_parameter.c b/src/code/z_parameter.c
index 2dc2b15c6..d673ed714 100644
--- a/src/code/z_parameter.c
+++ b/src/code/z_parameter.c
@@ -2069,13 +2069,14 @@ void func_80111CB4(GlobalContext* globalCtx) {
interfaceCtx->unk_222 = interfaceCtx->unk_224;
sp28 = true;
D_801BF884 = 0;
- } else if (CHECK_BTN_ALL(CONTROLLER1(globalCtx)->press.button, BTN_B)) {
+ } else if (CHECK_BTN_ALL(CONTROLLER1(&globalCtx->state)->press.button, BTN_B)) {
globalCtx->actorCtx.unk5 &= ~4;
interfaceCtx->unk_224 = 0;
interfaceCtx->unk_222 = interfaceCtx->unk_224;
sp28 = true;
D_801BF884 = 0;
- } else if (CHECK_BTN_ALL(CONTROLLER1(globalCtx)->press.button, BTN_A) || (func_801A5100() == 1)) {
+ } else if (CHECK_BTN_ALL(CONTROLLER1(&globalCtx->state)->press.button, BTN_A) ||
+ (func_801A5100() == 1)) {
if (!(gSaveContext.eventInf[4] & 2) ||
((gSaveContext.eventInf[4] & 2) && (ActorCutscene_GetCurrentIndex() == -1))) {
play_sound(NA_SE_SY_CAMERA_SHUTTER);
diff --git a/src/code/z_play.c b/src/code/z_play.c
index 6227eb3bd..b398849b6 100644
--- a/src/code/z_play.c
+++ b/src/code/z_play.c
@@ -309,10 +309,8 @@ s32 Play_IsUnderwater(GlobalContext* globalCtx, Vec3f* pos) {
}
}
-// z_demo and EnTest4
-// This data appears to be a boolean. It is only set by Play_Init.
-s32 func_8016A168(void) {
- return D_801D0D50;
+s32 Play_IsDebugCamEnabled(void) {
+ return gDbgCamEnabled;
}
// A mapping from playerActorCsIds to sGlobalCamDataSettings indices.
diff --git a/src/code/z_rcp.c b/src/code/z_rcp.c
index f4ffd43c1..1d13be316 100644
--- a/src/code/z_rcp.c
+++ b/src/code/z_rcp.c
@@ -1239,11 +1239,11 @@ void func_8012CF0C(GraphicsContext* gfxCtx, s32 clearFb, s32 clearZb, u8 r, u8 g
gSPDisplayList(&masterGfx[0], D_0E000000.syncSegments);
gSPDisplayList(&masterGfx[1], sFillSetupDL);
- gDPSetColorImage(&masterGfx[2], G_IM_FMT_RGBA, G_IM_SIZ_16b, D_801FBBCC, &D_0F000000);
+ gDPSetColorImage(&masterGfx[2], G_IM_FMT_RGBA, G_IM_SIZ_16b, D_801FBBCC, D_0F000000);
if (zbuffer != NULL) {
gDPSetDepthImage(&masterGfx[3], zbuffer);
} else {
- gDPSetDepthImage(&masterGfx[3], &D_0F000000);
+ gDPSetDepthImage(&masterGfx[3], D_0F000000);
}
gSPEndDisplayList(&masterGfx[4]);
@@ -1267,7 +1267,7 @@ void func_8012CF0C(GraphicsContext* gfxCtx, s32 clearFb, s32 clearZb, u8 r, u8 g
gDPSetRenderMode(&masterGfx[3], G_RM_NOOP, G_RM_NOOP2);
gDPSetFillColor(&masterGfx[4], (GPACK_RGBA5551(255, 255, 240, 0) << 16) | GPACK_RGBA5551(255, 255, 240, 0));
gSPDisplayList(&masterGfx[5], D_0E000000.clearFillRect);
- gDPSetColorImage(&masterGfx[6], G_IM_FMT_RGBA, G_IM_SIZ_16b, D_801FBBCC, &D_0F000000);
+ gDPSetColorImage(&masterGfx[6], G_IM_FMT_RGBA, G_IM_SIZ_16b, D_801FBBCC, D_0F000000);
gSPEndDisplayList(&masterGfx[7]);
}
@@ -1275,7 +1275,7 @@ void func_8012CF0C(GraphicsContext* gfxCtx, s32 clearFb, s32 clearZb, u8 r, u8 g
masterGfx = gGfxMasterDL->clearFrameBuffer;
- gDPSetColorImage(&masterGfx[0], G_IM_FMT_RGBA, G_IM_SIZ_16b, D_801FBBCC, &D_0F000000);
+ gDPSetColorImage(&masterGfx[0], G_IM_FMT_RGBA, G_IM_SIZ_16b, D_801FBBCC, D_0F000000);
gDPSetCycleType(&masterGfx[1], G_CYC_FILL);
gDPSetRenderMode(&masterGfx[2], G_RM_NOOP, G_RM_NOOP2);
gDPSetFillColor(&masterGfx[3], (GPACK_RGBA5551(r, g, b, 1) << 16) | GPACK_RGBA5551(r, g, b, 1));
diff --git a/src/code/z_skelanime.c b/src/code/z_skelanime.c
index 0a3334bc8..4c77b3669 100644
--- a/src/code/z_skelanime.c
+++ b/src/code/z_skelanime.c
@@ -627,9 +627,7 @@ void SkelAnime_GetFrameData(AnimationHeader* animation, s32 frame, s32 limbCount
for (i = 0; i < limbCount; i++) {
// Debug prints here, this is needed to prevent loop unrolling
- if (0) {
- if (0) {};
- }
+ if ((frameTable == NULL) || (jointIndices == NULL) || (dynamicData == NULL)) {}
frameTable->x = jointIndices->x >= staticIndexMax ? dynamicData[jointIndices->x] : frameData[jointIndices->x];
frameTable->y = jointIndices->y >= staticIndexMax ? dynamicData[jointIndices->y] : frameData[jointIndices->y];
frameTable->z = jointIndices->z >= staticIndexMax ? dynamicData[jointIndices->z] : frameData[jointIndices->z];
diff --git a/src/code/z_sub_s.c b/src/code/z_sub_s.c
index 3e5443bc6..4968185d0 100644
--- a/src/code/z_sub_s.c
+++ b/src/code/z_sub_s.c
@@ -178,8 +178,7 @@ s32 SubS_InCsMode(GlobalContext* globalCtx) {
* @param[in] stepRot boolean, step towards newRot instead of setting directly
* @param[in] overrideRot boolean, override newRot with the specified input.
*
- * Note:
- * If overrideRot is true, the rotation will automatically step instead of setting directly
+ * @note if overrideRot is true, the rotation will automatically step instead of setting directly
*/
s32 SubS_UpdateLimb(s16 newRotZ, s16 newRotY, Vec3f* pos, Vec3s* rot, s32 stepRot, s32 overrideRot) {
Vec3f newPos;
@@ -214,17 +213,273 @@ void SubS_UpdateFlags(u16* flags, u16 setBits, u16 unsetBits) {
*flags = (*flags & ~unsetBits) | setBits;
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013AF00.s")
+/**
+ * Fills the knot array to be used with time paths
+ *
+ * The default knot array just pads with `order` duplicate knots of the first knot at the front and of the last knot
+ * at the end.
+ *
+ * @param[out] knots an array of values that are used to compute the progress and the individual weights
+ * @param[in] order the order of the interpolation i.e. the number of points in the interpolation
+ * @param[in] numPoints the number of points to fill, generally the path count + order
+ *
+ * @note Same note as SubS_TimePathing_Update()
+ */
+void SubS_TimePathing_FillKnots(f32 knots[], s32 order, s32 numPoints) {
+ s32 i;
+ f32 val = 0.0f;
+
+ for (i = 0; i < numPoints; i++) {
+ if ((i >= order) && (i < (numPoints - order + 1))) {
+ val += 1.0f;
+ }
+ knots[i] = val;
+ }
+}
+
+typedef enum {
+ /* 0 */ SUBS_TIME_PATHING_PROGRESS_STATUS_ERROR,
+ /* 1 */ SUBS_TIME_PATHING_PROGRESS_STATUS_STILL_ON_PATH,
+ /* 2 */ SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END
+} SUBS_TIME_PATHING_PROGRESS_STATUS;
+
+/**
+ * Computes the progress to be used with time paths
+ *
+ * @param[out] progress the progress along the path, used to compute the weights
+ * @param[in] elapsedTime how much time has passed
+ * @param[in] waypointTime how much time per each waypoint
+ * @param[in] totalTime how much time the path should take to travel
+ * @param[in] pathCount the path count
+ * @param[in] order the order of the interpolation i.e. the number of points in the interpolation
+ * @param[in] knots see SubS_TimePathing_FillKnots()
+ *
+ * @return see SUBS_TIME_PATHING_PROGRESS_STATUS
+ */
+s32 SubS_TimePathing_ComputeProgress(f32* progress, s32 elapsedTime, s32 waypointTime, s32 totalTime, s32 pathCount,
+ s32 order, f32 knots[]) {
+ s32 i;
+ s32 j;
+ s32 k;
+ f32 waypointTimeInv; // The fraction of a waypoint a single unit of time contains
+
+ *progress = 0.0f;
+ if ((waypointTime <= 0) || (elapsedTime < 0)) {
+ return SUBS_TIME_PATHING_PROGRESS_STATUS_ERROR;
+ }
+
+ // When using the knots from SubS_TimePathing_FillKnots() these nested loops seem to simplify to
+ // *progress = (f32)elapsedTime / (f32)waypointTime;
+ waypointTimeInv = 1.0f / waypointTime;
+ k = 0;
+ for (i = order - 1; i < pathCount; i++) {
+ for (j = 0; j < waypointTime; j++) {
+ if (k == elapsedTime) {
+ break;
+ }
+ *progress += (knots[i + 1] - knots[i]) * waypointTimeInv;
+ k++;
+ }
+ }
+
+ return (elapsedTime == totalTime) ? SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END
+ : SUBS_TIME_PATHING_PROGRESS_STATUS_STILL_ON_PATH;
+}
+
+/**
+ * Computes the interpolation weights to be used with time paths
+ *
+ * Seems to use some kind of B-Spline interpolation algorithm
+ *
+ * @param[in] order the order of the interpolation i.e. the number of points in the interpolation, max is 10
+ * @param[in] progress see SubS_TimePathing_ComputeProgress()
+ * @param[in] waypoint the current waypoint
+ * @param[in] knots see SubS_TimePathing_FillKnots()
+ * @param[out] weights how much to weight each point considered
+ */
+void SubS_TimePathing_ComputeWeights(s32 order, f32 progress, s32 waypoint, f32 knots[], f32 weights[]) {
+ f32 weightsTemp[10][11];
+ s32 i;
+ s32 j;
+ s32 k;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B010.s")
+ for (i = 0; i < order; i++) {
+ for (j = 0; j < order + 1; j++) {
+ weightsTemp[i][j] = 0.0f;
+ }
+ }
+
+ weightsTemp[0][order - 1] = 1.0f;
+
+ for (i = 1; i < order; i++) {
+ for (j = waypoint - i, k = (order - 1) - i; j <= waypoint; j++, k++) {
+ if (knots[j + i] != knots[j]) {
+ weightsTemp[i][k] = ((progress - knots[j]) / (knots[j + i] - knots[j])) * weightsTemp[i - 1][k];
+ } else {
+ weightsTemp[i][k] = 0.0f;
+ }
+
+ if (knots[j + i + 1] != knots[j + 1]) {
+ weightsTemp[i][k] +=
+ ((knots[j + i + 1] - progress) / (knots[j + i + 1] - knots[j + 1])) * weightsTemp[i - 1][k + 1];
+ }
+ }
+ }
+ for (j = 0; j < order; j++) {
+ weights[j] = weightsTemp[order - 1][j];
+ }
+}
+
+/**
+ * Computes the X and Z component of the position to move to in time based paths
+ *
+ * @param[out] x computed x position
+ * @param[out] z computed z position
+ * @param[in] progress see SubS_TimePathing_ComputeProgress()
+ * @param[in] order the order of the interpolation i.e. the number of points in the interpolation, max is 10
+ * @param[in] waypoint the current waypoint
+ * @param[in] points the path's points
+ * @param[in] knots see SubS_TimePathing_FillKnots()
+ */
+void SubS_TimePathing_ComputeTargetPosXZ(f32* x, f32* z, f32 progress, s32 order, s32 waypoint, Vec3s points[],
+ f32 knots[]) {
+ f32 xPos;
+ f32 zPos;
+ f32 weights[11];
+ f32 weightedX;
+ f32 weightedZ;
+ f32 weightedTotal;
+ s32 i;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B0C8.s")
+ SubS_TimePathing_ComputeWeights(order, progress, waypoint, knots, weights);
+ weightedTotal = 0.0f;
+ weightedZ = 0.0f;
+ weightedX = 0.0f;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B350.s")
+ for (i = 0; i < order; i++) {
+ xPos = points[waypoint - order + i + 1].x;
+ zPos = points[waypoint - order + i + 1].z;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B6B0.s")
+ weightedX += weights[i] * xPos;
+ weightedZ += weights[i] * zPos;
+ weightedTotal += weights[i];
+ }
+ *x = weightedX / weightedTotal;
+ *z = weightedZ / weightedTotal;
+}
+
+/**
+ * Updates a time based path that an actor follows by:
+ * - Computing the X and Z components of the next point to move to
+ * - Updating the waypoint
+ * - Updating the time
+ *
+ * @param[in] path
+ * @param[out] progress see SubS_TimePathing_ComputeProgress()
+ * @param[in,out] elapsedTime how much time has passed
+ * @param[in] waypointTime how much time per each waypoint
+ * @param[in] totalTime how much time the path should take to travel
+ * @param[in,out] waypoint the current waypoint, this and the previous two points will be used to compute the targetPos
+ * @param[in] knots see SubS_TimePathing_FillKnots()
+ * @param[out] targetPos the computed position to move to
+ * @param[in] timeSpeed how fast time moves
+ *
+ * @return s32 returns true when the end has been reached.
+ *
+ * @note This system/function makes a couple of assumptions about the order used:
+ * 1. the order is assumed to be 3, see SUBS_TIME_PATHING_ORDER
+ * 2. even if SUBS_TIME_PATHING_ORDER is updated, the order can only be a max of 10
+ */
+s32 SubS_TimePathing_Update(Path* path, f32* progress, s32* elapsedTime, s32 waypointTime, s32 totalTime, s32* waypoint,
+ f32 knots[], Vec3f* targetPos, s32 timeSpeed) {
+ Vec3s* points = Lib_SegmentedToVirtual(path->points);
+ s32 state;
+ f32 endX;
+ f32 endZ;
+ s32 reachedEnd = false;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B878.s")
+ if (*waypoint >= path->count) {
+ state = SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END;
+ } else {
+ state = SubS_TimePathing_ComputeProgress(progress, *elapsedTime, waypointTime, totalTime, path->count,
+ SUBS_TIME_PATHING_ORDER, knots);
+ }
+
+ switch (state) {
+ case SUBS_TIME_PATHING_PROGRESS_STATUS_STILL_ON_PATH:
+ reachedEnd = false;
+ SubS_TimePathing_ComputeTargetPosXZ(&targetPos->x, &targetPos->z, *progress, SUBS_TIME_PATHING_ORDER,
+ *waypoint, points, knots);
+ break;
+ case SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END:
+ endX = points[path->count - 1].x;
+ endZ = points[path->count - 1].z;
+ targetPos->x = endX * 1;
+ targetPos->z = endZ * 1;
+ reachedEnd = true;
+ break;
+ }
+
+ *elapsedTime += timeSpeed;
+ if (*elapsedTime >= totalTime) {
+ *elapsedTime = totalTime;
+ } else if (*elapsedTime < 0) {
+ *elapsedTime = 0;
+ }
+ *waypoint = (*elapsedTime / waypointTime) + (SUBS_TIME_PATHING_ORDER - 1);
+
+ return reachedEnd;
+}
+
+/**
+ * Computes the initial Y component of a time based path
+ *
+ * @param[in] globalCtx
+ * @param[in] path
+ * @param[in] waypoint the current waypoint, this and the previous two points will be used to compute the target pos
+ * @param[out] targetPos the computed position to move to, only the Y component has meaning
+ *
+ * @note Same note as SubS_TimePathing_Update()
+ */
+void SubS_TimePathing_ComputeInitialY(GlobalContext* globalCtx, Path* path, s32 waypoint, Vec3f* targetPos) {
+ Vec3s* points = Lib_SegmentedToVirtual(path->points);
+ Vec3f posA;
+ Vec3f posB;
+ Vec3f posResult;
+ s32 i = waypoint - (SUBS_TIME_PATHING_ORDER - 1);
+ s16 max;
+ s16 min;
+ s32 isSetup;
+ CollisionPoly* outPoly = NULL;
+ s32 bgId = 0;
+
+ max = 0;
+ min = 0;
+ isSetup = false;
+ for (; i <= waypoint; i++) {
+ if (isSetup) {
+ if (max < points[i].y) {
+ max = points[i].y;
+ }
+ if (points[i].y < min) {
+ min = points[i].y;
+ }
+ } else {
+ max = min = points[i].y;
+ }
+ isSetup = true;
+ }
+ max += 30;
+ min -= 30;
+ posA = *targetPos;
+ posB = *targetPos;
+ posA.y = max;
+ posB.y = min;
+ if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &posA, &posB, &posResult, &outPoly, true, true, true, true,
+ &bgId)) {
+ targetPos->y = posResult.y;
+ }
+}
Path* SubS_GetAdditionalPath(GlobalContext* globalCtx, u8 pathIndex, s32 max) {
Path* path;
@@ -362,9 +617,189 @@ Path* SubS_GetDayDependentPath(GlobalContext* globalCtx, u8 pathIndex, u8 max, s
return path;
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013C068.s")
+/**
+ * Computes the point to move toward using a weight based algorithm that considers 4 points along the path
+ *
+ * @param path
+ * @param waypoint the current waypoint, this and the previous three points will be used to compute the point
+ * @param point the point computed
+ * @param progress the main weight value used to compute the weights for the points considered
+ * @param direction the direciton along the path to move, 1 for forwards, anything else for backwards
+ *
+ * @note only computes X and Z components of the point
+ */
+s32 SubS_WeightPathing_ComputePoint(Path* path, s32 waypoint, Vec3f* point, f32 progress, s32 direction) {
+ s32 i;
+ f32 weight0;
+ f32 weight1;
+ f32 weight2;
+ f32 weight3;
+ s32 lastPoint;
+ s32 secondLastPoint;
+ s32 secondPoint;
+ s32 firstPoint;
+ f32 xPoints[4];
+ f32 zPoints[4];
+ f32 oneMinusProgress;
+ f32 squared;
+ f32 cubed;
+ Vec3s* points;
+ s32 count = path->count;
+ s32 pointIndex;
+ s32 tmp;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013C624.s")
+ if (path == NULL) {
+ return false;
+ }
+ if (direction == 1) {
+ if (waypoint <= 2) {
+ pointIndex = 2;
+ } else {
+ pointIndex = (waypoint == 3) ? 3 : waypoint;
+ }
+ for (i = 0; i < 4; i++, pointIndex--) {
+ if (pointIndex <= 0) {
+ pointIndex = 0;
+ }
+ points = Lib_SegmentedToVirtual(path->points);
+ points = &points[pointIndex];
+ xPoints[i] = points->x;
+ zPoints[i] = points->z;
+ }
+ lastPoint = count - 1;
+ secondLastPoint = count - 2;
+ secondPoint = 3;
+ firstPoint = 2;
+ } else {
+ if (waypoint >= count - 3) {
+ pointIndex = count - 3;
+ } else {
+ tmp = waypoint + 4;
+ pointIndex = (count == tmp) ? count - 4 : waypoint;
+ }
+ for (i = 0; i < 4; i++, pointIndex++) {
+ if (pointIndex >= path->count) {
+ pointIndex = path->count - 1;
+ }
+ points = Lib_SegmentedToVirtual(path->points);
+ points = &points[pointIndex];
+ xPoints[i] = points->x;
+ zPoints[i] = points->z;
+ }
+ lastPoint = 0;
+ secondLastPoint = 1;
+ secondPoint = count - 4;
+ firstPoint = count - 3;
+ }
+ if (waypoint == lastPoint) {
+ oneMinusProgress = 1.0f - progress;
+ squared = progress * progress;
+ cubed = progress * squared;
+ weight0 = oneMinusProgress * oneMinusProgress * oneMinusProgress;
+ weight1 = (1.75f * cubed) - (4.5f * squared) + (3.0f * progress);
+ weight2 = ((-11.0f / 12.0f) * cubed) + (1.5f * squared);
+ weight3 = (1.0f / 6.0f) * cubed;
+ } else if (waypoint == secondLastPoint) {
+ oneMinusProgress = 1.0f - progress;
+ squared = progress * progress;
+ cubed = progress * squared;
+ weight0 = oneMinusProgress * oneMinusProgress * oneMinusProgress * ((void)0, 0.25f); //! FAKE:
+ weight1 = ((7.0f / 12.0f) * cubed) - (1.25f * squared) + (0.25f * progress) + (7.0f / 12.0f);
+ weight2 = (-0.5f * cubed) + (0.5f * squared) + (progress * 0.5f) + (1.0f / 6.0f);
+ weight3 = cubed * (1.0f / 6.0f);
+ } else if (waypoint == secondPoint) {
+ oneMinusProgress = 1.0f - progress;
+ squared = oneMinusProgress * oneMinusProgress;
+ cubed = oneMinusProgress * squared;
+ weight0 = (1.0f / 6.0f) * cubed;
+ weight1 = (-0.5f * cubed) + (0.5f * squared) + (0.5f * oneMinusProgress) + (1.0f / 6.0f);
+ weight2 = ((7.0f / 12.0f) * cubed) - (1.25f * squared) + (0.25f * oneMinusProgress) + (7.0f / 12.0f);
+ weight3 = progress * progress * progress * 0.25f;
+ } else if (((direction == 1) && (firstPoint >= waypoint)) || ((direction != 1) && (waypoint >= firstPoint))) {
+ oneMinusProgress = 1.0f - progress;
+ squared = oneMinusProgress * oneMinusProgress;
+ cubed = oneMinusProgress * squared;
+ weight0 = (1.0f / 6.0f) * cubed;
+ weight1 = ((-11.0f / 12.0f) * cubed) + (1.5f * squared);
+ weight2 = (1.75f * cubed) - (4.5f * squared) + (3.0f * oneMinusProgress);
+ weight3 = progress * progress * progress;
+ } else {
+ oneMinusProgress = 1.0f - progress;
+ squared = progress * progress;
+ cubed = squared * progress;
+ weight0 = oneMinusProgress * oneMinusProgress;
+ weight0 = oneMinusProgress * weight0 / 6.0f;
+ weight1 = (cubed * 0.5f) - squared + (2.0f / 3.0f);
+ weight2 = (cubed / -2.0f) + (squared * 0.5f) + (progress * 0.5f) + (1.0f / 6.0f);
+ weight3 = cubed / 6.0f;
+ }
+ point->x = (weight0 * xPoints[0]) + (weight1 * xPoints[1]) + (weight2 * xPoints[2]) + (weight3 * xPoints[3]);
+ point->z = (weight0 * zPoints[0]) + (weight1 * zPoints[1]) + (weight2 * zPoints[2]) + (weight3 * zPoints[3]);
+
+ return true;
+}
+
+// WeightPathing System is completely unused
+/**
+ * Moves an actor based on a weight based algorithm that takes into account 4 points along the path
+ *
+ * @param actor
+ * @param path
+ * @param waypoint the current waypoint, this and the previous three points will be used to move forward
+ * @param progress the progress towards a given waypoint, used to compute the weights
+ * @param direction the direction along the path to move, 1 for forwards, anything else for backwards
+ * @param returnStart boolean, true if the actor should wrap back to start when reaching the end
+ *
+ * @return s32 true if actor reached the end of the path in this iteration, false otherwise
+ */
+s32 SubS_WeightPathing_Move(Actor* actor, Path* path, s32* waypoint, f32* progress, s32 direction, s32 returnStart) {
+ Vec3f worldPos = actor->world.pos;
+ Vec3f velocity = actor->velocity;
+ Vec3f point;
+ f32 dist;
+
+ if (((direction != 1) && (*waypoint >= (path->count - 2))) || ((direction == 1) && (*waypoint < 2))) {
+ return false;
+ }
+ while (true) {
+ if (!SubS_WeightPathing_ComputePoint(path, *waypoint, &point, *progress, direction) ||
+ ((s32)(actor->speedXZ * 10000.0f) == 0)) {
+ return false;
+ }
+ dist = Math_Vec3f_DistXZ(&actor->world.pos, &point);
+ actor->world.rot.y = Math_Vec3f_Yaw(&actor->world.pos, &point);
+ Actor_MoveWithGravity(actor);
+ if (Math_Vec3f_DistXZ(&actor->world.pos, &point) < dist) {
+ break;
+ }
+ *progress += 0.1f;
+ if (*progress >= 1.1f) {
+ if (direction != 1) {
+ (*waypoint)++;
+ if (*waypoint >= (path->count - 2)) {
+ if (returnStart) {
+ *waypoint = 0;
+ } else {
+ return true;
+ }
+ }
+ } else {
+ (*waypoint)--;
+ if (*waypoint < 2) {
+ if (returnStart) {
+ *waypoint = path->count - 2;
+ } else {
+ return true;
+ }
+ }
+ }
+ *progress = 0.0f;
+ }
+ actor->world.pos = worldPos;
+ actor->velocity = velocity;
+ }
+ return false;
+}
s32 SubS_CopyPointFromPathCheckBounds(Path* path, s32 pointIndex, Vec3f* dst) {
Vec3s* point;
@@ -557,10 +992,10 @@ void SubS_DrawShadowTex(Actor* actor, GameState* gameState, u8* tex) {
Matrix_Translate(actor->world.pos.x, 0.0f, actor->world.pos.z, MTXMODE_NEW);
Matrix_Scale(0.6f, 1.0f, 0.6f, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
- gSPDisplayList(POLY_OPA_DISP++, gShadowDL);
+ gSPDisplayList(POLY_OPA_DISP++, gShadowMaterialDL);
gDPLoadTextureBlock(POLY_OPA_DISP++, tex, G_IM_FMT_I, G_IM_SIZ_8b, SUBS_SHADOW_TEX_WIDTH, SUBS_SHADOW_TEX_HEIGHT, 0,
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD);
- gSPDisplayList(POLY_OPA_DISP++, gShadowVtxDL);
+ gSPDisplayList(POLY_OPA_DISP++, gShadowModelDL);
CLOSE_DISPS(gfxCtx);
}
@@ -575,7 +1010,7 @@ void SubS_DrawShadowTex(Actor* actor, GameState* gameState, u8* tex) {
* @param[in] stepMin the minimun step in degrees
* @param[in] stepMax the maximum step in degrees
*/
-s16 SubS_ComputeTurnToPointRot(s16* rot, s16 rotMax, s16 target, f32 slowness, f32 stepMin, f32 stepMax) {
+s16 SubS_ComputeTrackPointRot(s16* rot, s16 rotMax, s16 target, f32 slowness, f32 stepMin, f32 stepMax) {
s16 prevRot = *rot;
f32 step;
f32 prevRotStep;
@@ -611,44 +1046,42 @@ s16 SubS_ComputeTurnToPointRot(s16* rot, s16 rotMax, s16 target, f32 slowness, f
/**
* Computes the necessary HeadRot and TorsoRot to smoothly turn an actors's head and torso to a point
*
- * @param[in] point the point to turn to
+ * @param[in] target the point to turn to
* @param[in] focusPos the actor's focus postion
* @param[in] shapeRot the actor's shape rotation
- * @param[in,out] turnTarget the intermediate target step that headRot and torsoRot step towards
+ * @param[in,out] trackTarget the intermediate target step that headRot and torsoRot step towards
* @param[in,out] headRot the computed head rotation
* @param[in,out] torsoRot the computed torso rotation
- * @param[in] options various options to adjust how the actor turns, see `SubS_ComputeTurnToPointRot and
- * TurnOptions/TurnOptionsSet`
- *
+ * @param[in] options various options to adjust how the actor turns, see SubS_ComputeTrackPointRot()
*/
-s32 SubS_TurnToPoint(Vec3f* point, Vec3f* focusPos, Vec3s* shapeRot, Vec3s* turnTarget, Vec3s* headRot, Vec3s* torsoRot,
- TurnOptionsSet* options) {
+s32 SubS_TrackPoint(Vec3f* target, Vec3f* focusPos, Vec3s* shapeRot, Vec3s* trackTarget, Vec3s* headRot,
+ Vec3s* torsoRot, TrackOptionsSet* options) {
s16 pitch;
s16 yaw;
s16 pad;
s16 targetY;
- f32 diffX = point->x - focusPos->x;
+ f32 diffX = target->x - focusPos->x;
s16 targetX;
- f32 diffZ = point->z - focusPos->z;
+ f32 diffZ = target->z - focusPos->z;
yaw = Math_FAtan2F(diffZ, diffX);
- pitch = Math_FAtan2F(sqrtf(SQ(diffX) + SQ(diffZ)), point->y - focusPos->y);
- Math_SmoothStepToS(&turnTarget->x, pitch, 4, 0x2710, 0);
- Math_SmoothStepToS(&turnTarget->y, yaw, 4, 0x2710, 0);
+ pitch = Math_FAtan2F(sqrtf(SQ(diffX) + SQ(diffZ)), target->y - focusPos->y);
+ Math_SmoothStepToS(&trackTarget->x, pitch, 4, 0x2710, 0);
+ Math_SmoothStepToS(&trackTarget->y, yaw, 4, 0x2710, 0);
targetX =
- SubS_ComputeTurnToPointRot(&headRot->x, options->headRotX.rotMax, turnTarget->x, options->headRotX.slowness,
- options->headRotX.rotStepMin, options->headRotX.rotStepMax);
+ SubS_ComputeTrackPointRot(&headRot->x, options->headRotX.rotMax, trackTarget->x, options->headRotX.slowness,
+ options->headRotX.rotStepMin, options->headRotX.rotStepMax);
//! @bug: torsoRotX uses headRotX slowness
- SubS_ComputeTurnToPointRot(&torsoRot->x, options->torsoRotX.rotMax, targetX, options->headRotX.slowness,
- options->torsoRotX.rotStepMin, options->torsoRotX.rotStepMax);
+ SubS_ComputeTrackPointRot(&torsoRot->x, options->torsoRotX.rotMax, targetX, options->headRotX.slowness,
+ options->torsoRotX.rotStepMin, options->torsoRotX.rotStepMax);
- targetY = turnTarget->y - shapeRot->y;
- SubS_ComputeTurnToPointRot(&headRot->y, options->headRotY.rotMax, targetY - torsoRot->y, options->headRotY.slowness,
- options->headRotY.rotStepMin, options->headRotY.rotStepMax);
- SubS_ComputeTurnToPointRot(&torsoRot->y, options->torsoRotY.rotMax, targetY - headRot->y,
- options->torsoRotY.slowness, options->torsoRotY.rotStepMin,
- options->torsoRotY.rotStepMax);
+ targetY = trackTarget->y - shapeRot->y;
+ SubS_ComputeTrackPointRot(&headRot->y, options->headRotY.rotMax, targetY - torsoRot->y, options->headRotY.slowness,
+ options->headRotY.rotStepMin, options->headRotY.rotStepMax);
+ SubS_ComputeTrackPointRot(&torsoRot->y, options->torsoRotY.rotMax, targetY - headRot->y,
+ options->torsoRotY.slowness, options->torsoRotY.rotStepMin,
+ options->torsoRotY.rotStepMax);
return true;
}
@@ -781,8 +1214,8 @@ s32 SubS_CopyPointFromPathList(Path* paths, s32 pathIndex, s32 pointIndex, Vec3f
return false;
}
-u8 SubS_GetPathCount(Path* paths, s32 index) {
- Path* path = &paths[index];
+u8 SubS_GetPathCountFromPathList(Path* paths, s32 pathIndex) {
+ Path* path = &paths[pathIndex];
return path->count;
}
@@ -1004,9 +1437,7 @@ s32 SubS_FillCutscenesList(Actor* actor, s16 cutscenes[], s16 numCutscenes) {
* @param[in] rot the angles to rotate with, uses just the x and y components
* @param[out] plane the computed plane
*
- * Notes:
- * The unit input vector is expected to already be normalized (only uses are with the z unit vector)
- *
+ * @note the unit input vector is expected to already be normalized (only uses are with the z unit vector)
*/
void SubS_ConstructPlane(Vec3f* point, Vec3f* unitVec, Vec3s* rot, Plane* plane) {
f32 sin;
@@ -1129,9 +1560,9 @@ s32 func_8013E8F8(Actor* actor, GlobalContext* globalCtx, f32 xzRange, f32 yRang
* @param[in] torsoZRotStepMax the max torso's Z rotation step
* @param[in] torsoXRotStepMax the max torso's X rotation step
*/
-s32 SubS_TurnToPointStep(Vec3f* worldPos, Vec3f* focusPos, s16 shapeYRot, Vec3f* yawTarget, Vec3f* pitchTarget,
- s16* headZRotStep, s16* headXRotStep, s16* torsoZRotStep, s16* torsoXRotStep,
- u16 headZRotStepMax, u16 headXRotStepMax, u16 torsoZRotStepMax, u16 torsoXRotStepMax) {
+s32 SubS_TrackPointStep(Vec3f* worldPos, Vec3f* focusPos, s16 shapeYRot, Vec3f* yawTarget, Vec3f* pitchTarget,
+ s16* headZRotStep, s16* headXRotStep, s16* torsoZRotStep, s16* torsoXRotStep,
+ u16 headZRotStepMax, u16 headXRotStepMax, u16 torsoZRotStepMax, u16 torsoXRotStepMax) {
s16 yaw = Math_Vec3f_Yaw(worldPos, yawTarget) - shapeYRot;
s16 pad;
s16 pad2;
diff --git a/src/code/z_vismono.c b/src/code/z_vismono.c
index 6c0d1ef22..b28ed2aa6 100644
--- a/src/code/z_vismono.c
+++ b/src/code/z_vismono.c
@@ -1,14 +1,179 @@
+/*
+ * File: z_vismono.c
+ * Description: Color frame buffer effect to desaturate the colors.
+ */
+
#include "global.h"
#include "system_malloc.h"
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vismono/func_801418B0.s")
+// Height of the fragments the color frame buffer (CFB) is split into.
+// It is the maximum amount of lines such that all rgba16 SCREEN_WIDTH-long lines fit into
+// the half of tmem (0x800 bytes) dedicated to color-indexed data.
+#define VISMONO_CFBFRAG_HEIGHT (0x800 / (SCREEN_WIDTH * G_IM_SIZ_16b_BYTES))
+
+// Maximum size of the dlist written by `VisMono_DesaturateDList`.
+// `VisMono_DesaturateDList` consistently uses `VISMONO_DLSIZE - 2` double words, so this can be 2 less.
+#define VISMONO_DLSIZE (3 + SCREEN_HEIGHT / VISMONO_CFBFRAG_HEIGHT * (7 + 2 + 2 + 3) + 2 + 2)
+
+// How much each color component contributes to the desaturated result.
+// These coefficients are close to what the YUV color space defines Y (luminance) as:
+// https://en.wikipedia.org/wiki/YUV#Conversion_to/from_RGB
+#define VISMONO_FAC_RED 2
+#define VISMONO_FAC_GREEN 4
+#define VISMONO_FAC_BLUE 1
+#define VISMONO_FAC_NORM (0x1F * VISMONO_FAC_RED + 0x1F * VISMONO_FAC_GREEN + 0x1F * VISMONO_FAC_BLUE)
+
+void VisMono_Init(VisMono* this) {
+ bzero(this, sizeof(VisMono));
+ this->unk_00 = 0;
+ this->setScissor = false;
+ this->primColor.r = 255;
+ this->primColor.g = 255;
+ this->primColor.b = 255;
+ this->primColor.a = 255;
+ this->envColor.r = 0;
+ this->envColor.g = 0;
+ this->envColor.b = 0;
+ this->envColor.a = 0;
+}
+
+void VisMono_Destroy(VisMono* this) {
+ SystemArena_Free(this->dList);
+}
+
+void VisMono_DesaturateTLUT(u16* tlut) {
+ s32 i;
+
+ for (i = 0; i < 256; i++) {
+ // `tlut[i]` is a IA16 color
+ // `i` corresponds to either byte of a RGBA16 color RRRR_RGGG GGBB_BBBA from the color frame buffer
+
+ // The high byte I (intensity) corresponds to `i` being interpreted as the high byte RRRR_RGGG
+ // I = (RRRRR * FAC_RED + GGG00 * FAC_GREEN) * (255 / FAC_NORM)
+
+ // The low byte A (alpha) corresponds to `i` being interpreted as the low byte GGBB_BBBA
+ // A = (000GG * FAC_GREEN + BBBBB * FAC_BLUE) * (255 / FAC_NORM)
+
+ // Note: I + A = (RRRRR * FAC_RED + GGGGG * FAC_GREEN + BBBBB * FAC_BLUE) * (255 / FAC_NORM)
+
+ tlut[i] = GPACK_IA16(
+ (((i >> 3) & 0x1F) * VISMONO_FAC_RED + ((i << 2) & 0x1F) * VISMONO_FAC_GREEN) * 255 / VISMONO_FAC_NORM,
+ (((i >> 6) & 0x1F) * VISMONO_FAC_GREEN + ((i >> 1) & 0x1F) * VISMONO_FAC_BLUE) * 255 / VISMONO_FAC_NORM);
+ }
+}
+
+Gfx* VisMono_DesaturateDList(Gfx* gfx) {
+ s32 y;
+ s32 height = VISMONO_CFBFRAG_HEIGHT;
+ u16* cfbFrag = D_0F000000;
+
+ gDPPipeSync(gfx++);
+ // `G_TT_IA16`: use color-indexed images, and IA16 palettes
+ gDPSetOtherMode(gfx++,
+ G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_IA16 | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_1PRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) | G_RM_CLD_SURF2);
+ // First color cycle sums texel 1 alpha and texel 0 color
+ // By using IA16 palettes, this means summing A (from the IA16 color texel 1 maps to)
+ // with I (from the IA16 color texel 0 maps to)
+ gDPSetCombineLERP(gfx++, 1, 0, TEXEL1_ALPHA, TEXEL0, 0, 0, 0, 1, PRIMITIVE, ENVIRONMENT, COMBINED, ENVIRONMENT, 0,
+ 0, 0, PRIMITIVE);
+
+ for (y = 0; y <= SCREEN_HEIGHT - height; y += height) {
+ // Load a few lines of the color frame buffer
+ gDPLoadTextureBlock(gfx++, cfbFrag, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2, height, 0,
+ G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK,
+ G_TX_NOLOD, G_TX_NOLOD);
+
+ // Set texel 0 to be a CI8 image with width `SCREEN_WIDTH * 2` and height `VISMONO_CFBFRAG_HEIGHT`
+ // Its position in texture image space is shifted along +S by 2
+ gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2 * G_IM_SIZ_8b_LINE_BYTES / 8, 0x0, G_TX_RENDERTILE,
+ 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0);
+ gDPSetTileSize(gfx++, G_TX_RENDERTILE, 2 << 2, 0, (SCREEN_WIDTH * 2 + 1) << 2,
+ (VISMONO_CFBFRAG_HEIGHT - 1) << 2);
+
+ // Set texel 1 to be a CI8 image with width `SCREEN_WIDTH * 2` and height `VISMONO_CFBFRAG_HEIGHT`
+ // Its position in texture image space is shifted along +S by 1
+ gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2 * G_IM_SIZ_8b_LINE_BYTES / 8, 0x0, 1, 1,
+ G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0);
+ gDPSetTileSize(gfx++, 1, 1 << 2, 0, (SCREEN_WIDTH * 2) << 2, (VISMONO_CFBFRAG_HEIGHT - 1) << 2);
+
+ // Draw a `SCREEN_WIDTH` wide, `height` high rectangle.
+ // Texture coordinate T (vertical) starts at 0 and changes by one each line (dtdy = 1)
+ // Texture coordinate S (horizontal) starts at 2 and changes by two each column (dsdx = 2)
+
+ // Because texel 0 is shifted by 2 and texel 1 only by 1 along +S,
+ // a pixel at S coordinates s = 2+2*n will look at the 2*n-th byte of texel 0 and the 2*n+1-th byte of texel 1.
+ // (in "s = 2+2*n" the first "2" is the starting S coordinate and the second "2" is the dsdx value)
+
+ // The 2*n-th byte of texel 0 is the high byte of the n-th RGBA16 color of the color frame buffer.
+ // The 2*n+1-th byte of texel 1 is the low byte of the n-th RGBA16 color of the color frame buffer.
+
+ // With the TLUT computed by `VisMono_DesaturateTLUT`:
+ // The 2*n-th byte of texel 0 maps to a IA16 color where the high byte I (intensity) corresponds to
+ // the high byte of the n-th RGBA16 color of the color frame buffer.
+ // The 2*n+1-th byte of texel 1 maps to a IA16 color where the low byte A (alpha) corresponds to
+ // the low byte of the n-th RGBA16 color of the color frame buffer.
+
+ // Since the combiner is in part set up to sum texel 0 color (I, intensity) with texel 1 alpha (A, alpha),
+ // the resulting color in the drawn rectangle is a desaturated color as defined by the `VISMONO_FAC_*` values.
+
+ gSPTextureRectangle(gfx++, 0, y << 2, SCREEN_WIDTH << 2, (y + height) << 2, G_TX_RENDERTILE, 2 << 5, 0, 2 << 10,
+ 1 << 10);
+ cfbFrag += SCREEN_WIDTH * height;
+ }
+
+ gDPPipeSync(gfx++);
+ gSPEndDisplayList(gfx++);
+ return gfx;
+}
+
+void VisMono_Draw(VisMono* this, Gfx** gfxp) {
+ Gfx* gfx = *gfxp;
+ u16* tlut;
+ Gfx* dList;
+ Gfx* dListEnd;
+
+ if (this->tlut) {
+ tlut = this->tlut;
+ } else {
+ tlut = Graph_DlistAlloc(&gfx, 256 * G_IM_SIZ_16b_BYTES);
+ VisMono_DesaturateTLUT(tlut);
+ }
+
+ if (this->dList) {
+ dList = this->dList;
+ } else {
+ dList = Graph_DlistAlloc(&gfx, VISMONO_DLSIZE * sizeof(Gfx));
+ dListEnd = VisMono_DesaturateDList(dList);
+ }
+
+ gDPPipeSync(gfx++);
+
+ if (this->setScissor == true) {
+ gSPDisplayList(gfx++, D_0E000000.setScissor);
+ }
+
+ gDPSetColor(gfx++, G_SETPRIMCOLOR, this->primColor.rgba);
+ gDPSetColor(gfx++, G_SETENVCOLOR, this->envColor.rgba);
+
+ gDPLoadTLUT_pal256(gfx++, tlut);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vismono/func_80141900.s")
+ gSPDisplayList(gfx++, dList);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vismono/func_80141924.s")
+ gDPPipeSync(gfx++);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vismono/func_80141C34.s")
+ *gfxp = gfx;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vismono/VisMono_Draw.s")
+void VisMono_DrawOld(VisMono* this) {
+ if (this->tlut == NULL) {
+ this->tlut = SystemArena_Malloc(256 * G_IM_SIZ_16b_BYTES);
+ VisMono_DesaturateTLUT(this->tlut);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vismono/func_8014204C.s")
+ if (this->dList == NULL) {
+ this->dList = SystemArena_Malloc(VISMONO_DLSIZE * sizeof(Gfx));
+ VisMono_DesaturateDList(this->dList);
+ }
+}