From 0e51a51fb1d9a3d076039604ec29c9024ccf2719 Mon Sep 17 00:00:00 2001 From: EllipticEllipsis Date: Fri, 20 May 2022 07:27:54 +0100 Subject: [PATCH] Document SkelCurve system (#1192) * Rename and document SkelCurve * AVOID_UB in DemoTreLgt_OverrideLimbDraw * Rename code_8006C510 * Objdump flags in the makefile * Move CC_CHECK above compilation * Review 1 * Review 2 * Review 3 * Review Add doxygen comments to file head use angle macro, improve bug comment, make arguments of SkelCurve_Draw more consistent, Change this temp to pad in MagicWind --- Makefile | 14 +- include/functions.h | 10 - include/z64.h | 1 + include/z64animation.h | 48 ---- include/z64curve.h | 68 +++++ spec | 2 +- src/code/code_8006C510.c | 40 --- src/code/z_fcurve_data.c | 85 +++++++ src/code/z_fcurve_data_skelanime.c | 232 +++++++++++------- .../actors/ovl_Demo_Effect/z_demo_effect.c | 14 +- .../actors/ovl_Demo_Effect/z_demo_effect.h | 2 +- .../actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c | 27 +- .../actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h | 2 +- .../actors/ovl_Magic_Wind/z_magic_wind.c | 8 +- .../actors/ovl_Magic_Wind/z_magic_wind.h | 2 +- 15 files changed, 342 insertions(+), 213 deletions(-) create mode 100644 include/z64curve.h delete mode 100644 src/code/code_8006C510.c create mode 100644 src/code/z_fcurve_data.c diff --git a/Makefile b/Makefile index 0006b93332..d708f5d435 100644 --- a/Makefile +++ b/Makefile @@ -142,6 +142,8 @@ else CC_CHECK = @: endif +OBJDUMP_FLAGS := -d -r -z -Mreg-names=32 + #### Files #### # ROM image @@ -320,21 +322,21 @@ build/src/boot/z_std_dma.o: build/dmadata_table_spec.h build/src/dmadata/dmadata.o: build/dmadata_table_spec.h build/src/%.o: src/%.c - $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $< $(CC_CHECK) $< - @$(OBJDUMP) -d $@ > $(@:.o=.s) + $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $< + @$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s) build/src/libultra/libc/ll.o: src/libultra/libc/ll.c - $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $< $(CC_CHECK) $< + $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $< python3 tools/set_o32abi_bit.py $@ - @$(OBJDUMP) -d $@ > $(@:.o=.s) + @$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s) build/src/libultra/libc/llcvt.o: src/libultra/libc/llcvt.c - $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $< $(CC_CHECK) $< + $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $< python3 tools/set_o32abi_bit.py $@ - @$(OBJDUMP) -d $@ > $(@:.o=.s) + @$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s) build/src/overlays/%_reloc.o: build/$(SPEC) $(FADO) $$(tools/reloc_prereq $< $(notdir $*)) -n $(notdir $*) -o $(@:.o=.s) -M $(@:.o=.d) diff --git a/include/functions.h b/include/functions.h index 6801761c81..3fbbf7295e 100644 --- a/include/functions.h +++ b/include/functions.h @@ -840,16 +840,6 @@ void Flags_UnsetAllEnv(GlobalContext* globalCtx); void Flags_SetEnv(GlobalContext* globalCtx, s16 flag); void Flags_UnsetEnv(GlobalContext* globalCtx, s16 flag); s32 Flags_GetEnv(GlobalContext* globalCtx, s16 flag); -f32 func_8006C5A8(f32 target, TransformData* transData, s32 refIdx); -void SkelCurve_Clear(SkelAnimeCurve* skelCurve); -s32 SkelCurve_Init(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, SkelCurveLimbList* limbListSeg, - TransformUpdateIndex* transUpdIdx); -void SkelCurve_Destroy(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve); -void SkelCurve_SetAnim(SkelAnimeCurve* skelCurve, TransformUpdateIndex* transUpdIdx, f32 arg2, f32 animFinalFrame, - f32 animCurFrame, f32 animSpeed); -s32 SkelCurve_Update(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve); -void SkelCurve_Draw(Actor* actor, GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, - OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, void* data); s32 func_8006CFC0(s32 scene); void func_8006D074(GlobalContext* globalCtx); void func_8006D0AC(GlobalContext* globalCtx); diff --git a/include/z64.h b/include/z64.h index 01108f77e8..5cfefc5fdc 100644 --- a/include/z64.h +++ b/include/z64.h @@ -15,6 +15,7 @@ #include "z64environment.h" #include "z64cutscene.h" #include "z64collision_check.h" +#include "z64curve.h" #include "z64scene.h" #include "z64effect.h" #include "z64item.h" diff --git a/include/z64animation.h b/include/z64animation.h index 0a5b5e1417..faba103974 100755 --- a/include/z64animation.h +++ b/include/z64animation.h @@ -189,54 +189,6 @@ typedef struct AnimationContext { typedef void (*AnimationEntryCallback)(struct GlobalContext* globalCtx, AnimationEntryData* data); -// fcurve_skelanime structs -typedef struct { - /* 0x0000 */ u16 unk_00; // appears to be flags - /* 0x0002 */ s16 unk_02; - /* 0x0004 */ s16 unk_04; - /* 0x0006 */ s16 unk_06; - /* 0x0008 */ f32 unk_08; -} TransformData; // size = 0xC - -typedef struct { - /* 0x0000 */ u8* refIndex; - /* 0x0004 */ TransformData* transformData; - /* 0x0008 */ s16* copyValues; - /* 0x000C */ s16 unk_0C; - /* 0x000E */ s16 unk_0E; -} TransformUpdateIndex; // size = 0x10 - -typedef struct { - /* 0x0000 */ u8 firstChildIdx; - /* 0x0001 */ u8 nextLimbIdx; - /* 0x0004 */ Gfx* dList[2]; -} SkelCurveLimb; // size = 0xC - -typedef struct { - /* 0x0000 */ SkelCurveLimb** limbs; - /* 0x0004 */ u8 limbCount; -} SkelCurveLimbList; // size = 0x8 - -typedef struct { - /* 0x0000 */ Vec3s scale; - /* 0x0006 */ Vec3s rot; - /* 0x000C */ Vec3s pos; -} LimbTransform; // size = 0x12 - -typedef struct { - /* 0x0000 */ u8 limbCount; - /* 0x0004 */ SkelCurveLimb** limbList; - /* 0x0008 */ TransformUpdateIndex* transUpdIdx; - /* 0x000C */ f32 unk_0C; // seems to be unused - /* 0x0010 */ f32 animFinalFrame; - /* 0x0014 */ f32 animSpeed; - /* 0x0018 */ f32 animCurFrame; - /* 0x001C */ LimbTransform* transforms; -} SkelAnimeCurve; // size = 0x20 - -typedef s32 (*OverrideCurveLimbDraw)(struct GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void*); -typedef void (*PostCurveLimbDraw)(struct GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void*); - typedef s32 (*AnimUpdateFunc)(); typedef struct SkelAnime { diff --git a/include/z64curve.h b/include/z64curve.h new file mode 100644 index 0000000000..a25fdaca1a --- /dev/null +++ b/include/z64curve.h @@ -0,0 +1,68 @@ +#ifndef Z64_CURVE_H +#define Z64_CURVE_H + +#include "ultra64/types.h" +#include "z64math.h" + +struct GlobalContext; + +typedef struct { + /* 0x0 */ u16 flags; // Only the bottom two bits are used, although others are set in objects + /* 0x2 */ s16 abscissa; // knot input value + /* 0x4 */ s16 leftGradient; // left derivative at the point + /* 0x6 */ s16 rightGradient; // right derivative at the point + /* 0x8 */ f32 ordinate; // output value +} CurveInterpKnot; // size = 0xC + +typedef struct { + /* 0x0 */ u8* knotCounts; + /* 0x4 */ CurveInterpKnot* interpolationData; + /* 0x8 */ s16* constantData; + /* 0xC */ s16 unk_0C; // Set but not used, always 1 in objects + /* 0xE */ s16 frameCount; // Not used, inferred from use in objects +} CurveAnimationHeader; // size = 0x10 + +typedef struct { + /* 0x0 */ u8 child; + /* 0x1 */ u8 sibling; + /* 0x4 */ Gfx* dList[2]; +} SkelCurveLimb; // size = 0xC + +typedef struct { + /* 0x0 */ SkelCurveLimb** limbs; + /* 0x4 */ u8 limbCount; +} CurveSkeletonHeader; // size = 0x8 + +typedef struct { + /* 0x00 */ u8 limbCount; + /* 0x04 */ SkelCurveLimb** skeleton; + /* 0x08 */ CurveAnimationHeader* animation; + /* 0x0C */ f32 unk_0C; // set but not used + /* 0x10 */ f32 endFrame; + /* 0x14 */ f32 playSpeed; + /* 0x18 */ f32 curFrame; + /* 0x1C */ s16 (*jointTable)[9]; +} SkelCurve; // size = 0x20 + +typedef s32 (*OverrideCurveLimbDraw)(struct GlobalContext* globalCtx, SkelCurve* skelCuve, s32 limbIndex, void* thisx); +typedef void (*PostCurveLimbDraw)(struct GlobalContext* globalCtx, SkelCurve* skelCuve, s32 limbIndex, void* thisx); + + + +f32 Curve_Interpolate(f32 x, CurveInterpKnot* transData, s32 refIdx); + +void SkelCurve_Clear(SkelCurve* skelCurve); +s32 SkelCurve_Init(struct GlobalContext* globalCtx, SkelCurve* skelCurve, CurveSkeletonHeader* limbListSeg, CurveAnimationHeader* transUpdIdx); +void SkelCurve_Destroy(struct GlobalContext* globalCtx, SkelCurve* skelCurve); +void SkelCurve_SetAnim(SkelCurve* skelCurve, CurveAnimationHeader* transUpdIdx, f32 arg2, f32 endFrame, f32 curFrame, f32 playSpeed); +s32 SkelCurve_Update(struct GlobalContext* globalCtx, SkelCurve* skelCurve); +void SkelCurve_Draw(Actor* actor, struct GlobalContext* globalCtx, SkelCurve* skelCurve, OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, void* thisx); + + +// ZAPD compatibility typedefs +// TODO: Remove when ZAPD adds support for them +typedef CurveInterpKnot TransformData; +typedef CurveAnimationHeader TransformUpdateIndex; +typedef CurveSkeletonHeader SkelCurveLimbList; + +#endif diff --git a/spec b/spec index 008e6a97ef..4e8840cc65 100644 --- a/spec +++ b/spec @@ -322,7 +322,7 @@ beginseg include "build/src/code/z_elf_message.o" include "build/src/code/z_face_reaction.o" include "build/src/code/code_8006C3A0.o" - include "build/src/code/code_8006C510.o" + include "build/src/code/z_fcurve_data.o" include "build/src/code/z_fcurve_data_skelanime.o" include "build/src/code/z_game_dlftbls.o" include "build/src/code/z_horse.o" diff --git a/src/code/code_8006C510.c b/src/code/code_8006C510.c deleted file mode 100644 index 833009e814..0000000000 --- a/src/code/code_8006C510.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "global.h" - -f32 func_8006C510(f32 arg0, f32 arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5) { - char pad[0x1C]; - f32 sq = SQ(arg0); - f32 cube = sq * arg0; - - return (((cube + cube) - sq * 3.0f) + 1.0f) * arg2 + (sq * 3.0f - (cube + cube)) * arg3 + - ((cube - (sq + sq)) + arg0) * arg4 * arg1 + (cube - sq) * arg5 * arg1; -} - -f32 func_8006C5A8(f32 target, TransformData* transData, s32 refIdx) { - s32 i; - s32 j; - - if (target <= transData->unk_02) { - return transData->unk_08; - } - if (target >= transData[refIdx - 1].unk_02) { - return transData[refIdx - 1].unk_08; - } - - for (i = 0;; i++) { - j = i + 1; - if (transData[j].unk_02 > target) { - if (transData[i].unk_00 & 1) { - return transData[i].unk_08; - } else if (transData[i].unk_00 & 2) { - return transData[i].unk_08 + - ((target - (f32)transData[i].unk_02) / ((f32)transData[j].unk_02 - (f32)transData[i].unk_02)) * - (transData[j].unk_08 - transData[i].unk_08); - } else { - f32 diff = (f32)transData[j].unk_02 - (f32)transData[i].unk_02; - return func_8006C510((target - transData[i].unk_02) / ((f32)transData[j].unk_02 - transData[i].unk_02), - diff * (1.0f / 30.0f), transData[i].unk_08, transData[j].unk_08, - transData[i].unk_06, transData[j].unk_04); - } - } - } -} diff --git a/src/code/z_fcurve_data.c b/src/code/z_fcurve_data.c new file mode 100644 index 0000000000..a1c03a9278 --- /dev/null +++ b/src/code/z_fcurve_data.c @@ -0,0 +1,85 @@ +/** + * File: z_fcurve_data.c + * Description: Interpolation functions for use with Curve SkelAnime + */ +#include "global.h" +#include "z64curve.h" + +#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 + +/** + * 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 abcf4a9e37..23889413a9 100644 --- a/src/code/z_fcurve_data_skelanime.c +++ b/src/code/z_fcurve_data_skelanime.c @@ -1,90 +1,151 @@ +/** + * @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->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 = SEGMENTED_TO_VIRTUAL(limbListSeg); + CurveSkeletonHeader* skeletonHeader = SEGMENTED_TO_VIRTUAL(skeletonHeaderSeg); - skelCurve->limbCount = limbList->limbCount; - skelCurve->limbList = SEGMENTED_TO_VIRTUAL(limbList->limbs); + skelCurve->limbCount = skeletonHeader->limbCount; + skelCurve->skeleton = SEGMENTED_TO_VIRTUAL(skeletonHeader->limbs); - skelCurve->transforms = ZeldaArena_MallocDebug(sizeof(*skelCurve->transforms) * skelCurve->limbCount, + skelCurve->jointTable = ZeldaArena_MallocDebug(sizeof(*skelCurve->jointTable) * skelCurve->limbCount, "../z_fcurve_data_skelanime.c", 125); - ASSERT(skelCurve->transforms != NULL, "this->now_joint != NULL", "../z_fcurve_data_skelanime.c", 127); - skelCurve->animCurFrame = 0.0f; - return 1; + ASSERT(skelCurve->jointTable != NULL, "this->now_joint != NULL", "../z_fcurve_data_skelanime.c", 127); + skelCurve->curFrame = 0.0f; + return true; } -void SkelCurve_Destroy(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve) { - if (skelCurve->transforms != NULL) { - ZeldaArena_FreeDebug(skelCurve->transforms, "../z_fcurve_data_skelanime.c", 146); +/** + * Frees the joint table. + */ +void SkelCurve_Destroy(GlobalContext* globalCtx, SkelCurve* skelCurve) { + if (skelCurve->jointTable != NULL) { + ZeldaArena_FreeDebug(skelCurve->jointTable, "../z_fcurve_data_skelanime.c", 146); } } -void SkelCurve_SetAnim(SkelAnimeCurve* skelCurve, TransformUpdateIndex* transUpdIdx, f32 arg2, f32 animFinalFrame, - f32 animCurFrame, f32 animSpeed) { - skelCurve->unk_0C = 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; } -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; +typedef enum { + /* 0 */ SKELCURVE_VEC_TYPE_SCALE, + /* 1 */ SKELCURVE_VEC_TYPE_ROTATION, + /* 2 */ SKELCURVE_VEC_TYPE_POSIITON, + /* 3 */ SKELCURVE_VEC_TYPE_MAX +} SkelCurveVecType; - transformIndex = SEGMENTED_TO_VIRTUAL(skelCurve->transUpdIdx); - transformRefIdx = SEGMENTED_TO_VIRTUAL(transformIndex->refIndex); - transData = SEGMENTED_TO_VIRTUAL(transformIndex->transformData); - transformCopyValues = SEGMENTED_TO_VIRTUAL(transformIndex->copyValues); - transforms = (s16*)skelCurve->transforms; +#define SKELCURVE_SCALE_SCALE 1024.0f +#define SKELCURVE_SCALE_POSITION 100 - skelCurve->animCurFrame += skelCurve->animSpeed * R_UPDATE_RATE * 0.5f; +/** + * 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; - if ((skelCurve->animSpeed >= 0.0f && skelCurve->animCurFrame > skelCurve->animFinalFrame) || - (skelCurve->animSpeed < 0.0f && skelCurve->animCurFrame < skelCurve->animFinalFrame)) { - skelCurve->animCurFrame = skelCurve->animFinalFrame; - ret = 1; + animation = SEGMENTED_TO_VIRTUAL(skelCurve->animation); + knotCounts = SEGMENTED_TO_VIRTUAL(animation->knotCounts); + startKnot = SEGMENTED_TO_VIRTUAL(animation->interpolationData); + constantData = SEGMENTED_TO_VIRTUAL(animation->constantData); + jointData = *skelCurve->jointTable; + + skelCurve->curFrame += skelCurve->playSpeed * R_UPDATE_RATE * 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_8006C5A8(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++; } } } @@ -92,33 +153,36 @@ s32 SkelCurve_Update(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve) { return ret; } -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, void* data) { - SkelCurveLimb* limb = SEGMENTED_TO_VIRTUAL(skelCurve->limbList[limbIndex]); + SkelCurveLimb* limb = SEGMENTED_TO_VIRTUAL(skelCurve->skeleton[limbIndex]); OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fcurve_data_skelanime.c", 279); Matrix_Push(); - if (overrideLimbDraw == NULL || - (overrideLimbDraw != NULL && overrideLimbDraw(globalCtx, skelCurve, limbIndex, data))) { + if ((overrideLimbDraw == NULL) || + ((overrideLimbDraw != NULL) && overrideLimbDraw(globalCtx, skelCurve, limbIndex, data))) { Vec3f scale; Vec3s rot; Vec3f pos; Gfx* dList; - Vec3s* transform = (Vec3s*)&skelCurve->transforms[limbIndex]; + s16* jointData = skelCurve->jointTable[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; + 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); @@ -157,22 +221,22 @@ void SkelCurve_DrawLimb(GlobalContext* globalCtx, s32 limbIndex, SkelAnimeCurve* postLimbDraw(globalCtx, skelCurve, limbIndex, data); } - if (limb->firstChildIdx != LIMB_DONE) { - SkelCurve_DrawLimb(globalCtx, limb->firstChildIdx, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); + if (limb->child != LIMB_DONE) { + SkelCurve_DrawLimb(globalCtx, limb->child, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); } Matrix_Pop(); - if (limb->nextLimbIdx != LIMB_DONE) { - SkelCurve_DrawLimb(globalCtx, limb->nextLimbIdx, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); + if (limb->sibling != LIMB_DONE) { + SkelCurve_DrawLimb(globalCtx, limb->sibling, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); } CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fcurve_data_skelanime.c", 371); } -void SkelCurve_Draw(Actor* actor, GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, +void SkelCurve_Draw(Actor* actor, GlobalContext* globalCtx, SkelCurve* skelCurve, OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, void* data) { - if (skelCurve->transforms != NULL) { + if (skelCurve->jointTable != NULL) { SkelCurve_DrawLimb(globalCtx, 0, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); } } diff --git a/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.c b/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.c index 40ab669c1f..e921719ce6 100644 --- a/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.c +++ b/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.c @@ -2043,7 +2043,7 @@ void DemoEffect_DrawGetItem(Actor* thisx, GlobalContext* globalCtx) { /** * Callback for the SkelCurve system to draw the animated limbs. */ -s32 DemoEffect_DrawTimewarpLimbs(GlobalContext* globalCtx, SkelAnimeCurve* skelCuve, s32 limbIndex, void* thisx) { +s32 DemoEffect_OverrideLimbDrawTimeWarp(GlobalContext* globalCtx, SkelCurve* skelCurve, s32 limbIndex, void* thisx) { s32 pad; DemoEffect* this = (DemoEffect*)thisx; u32 frames = globalCtx->gameplayFrames; @@ -2058,13 +2058,13 @@ s32 DemoEffect_DrawTimewarpLimbs(GlobalContext* globalCtx, SkelAnimeCurve* skelC CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3172); if (limbIndex == 0) { - LimbTransform* transform = &skelCuve->transforms[0]; + s16* transform = skelCurve->jointTable[0]; - transform->scale.y = 1024; - transform->scale.z = transform->scale.x = 1024; + transform[2] = transform[0] = 1024; + transform[1] = 1024; } - return 1; + return true; } /** @@ -2079,9 +2079,11 @@ void DemoEffect_DrawTimeWarp(Actor* thisx, GlobalContext* globalCtx) { Flags_GetEnv(globalCtx, 1) || gSaveContext.sceneSetupIndex >= 4 || gSaveContext.entranceIndex == ENTR_TOKINOMA_4) { OPEN_DISPS(gfxCtx, "../z_demo_effect.c", 3201); + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 25); Matrix_Scale(2.0f, 2.0f, 2.0f, MTXMODE_APPLY); - SkelCurve_Draw(thisx, globalCtx, &this->skelCurve, DemoEffect_DrawTimewarpLimbs, NULL, 1, this); + SkelCurve_Draw(&this->actor, globalCtx, &this->skelCurve, DemoEffect_OverrideLimbDrawTimeWarp, NULL, 1, &this->actor); + CLOSE_DISPS(gfxCtx, "../z_demo_effect.c", 3216); } } diff --git a/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.h b/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.h index 5434b1d368..42f1cd1307 100644 --- a/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.h +++ b/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.h @@ -78,7 +78,7 @@ typedef struct { typedef struct DemoEffect { /* 0x0000 */ Actor actor; - /* 0x014C */ SkelAnimeCurve skelCurve; + /* 0x014C */ SkelCurve skelCurve; /* 0x016C */ u8 initObjectBankIndex; /* 0x0170 */ Gfx* jewelDisplayList; /* 0x0174 */ Gfx* jewelHolderDisplayList; diff --git a/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c b/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c index 1e5c74e86d..1695d594b6 100644 --- a/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c +++ b/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c @@ -38,7 +38,7 @@ const ActorInit Demo_Tre_Lgt_InitVars = { (ActorFunc)DemoTreLgt_Draw, }; -static TransformUpdateIndex* sTransformUpdIdx[] = { &gTreasureChestCurveAnim_4B60, &gTreasureChestCurveAnim_4F70 }; +static CurveAnimationHeader* sAnimations[] = { &gTreasureChestCurveAnim_4B60, &gTreasureChestCurveAnim_4F70 }; static DemoTreLgtActionFunc sActionFuncs[] = { func_8099375C, @@ -48,7 +48,7 @@ static DemoTreLgtActionFunc sActionFuncs[] = { void DemoTreLgt_Init(Actor* thisx, GlobalContext* globalCtx) { DemoTreLgt* this = (DemoTreLgt*)thisx; - if (!SkelCurve_Init(globalCtx, &this->skelCurve, &gTreasureChestCurveSkel, sTransformUpdIdx[0])) { + if (!SkelCurve_Init(globalCtx, &this->skelCurve, &gTreasureChestCurveSkel, sAnimations[0])) { // "Demo_Tre_Lgt_Actor_ct (); Construct failed" osSyncPrintf("Demo_Tre_Lgt_Actor_ct();コンストラクト失敗\n"); } @@ -74,25 +74,25 @@ void func_80993754(DemoTreLgt* this) { void func_8099375C(DemoTreLgt* this, GlobalContext* globalCtx) { EnBox* treasureChest = (EnBox*)this->actor.parent; - if (treasureChest != NULL && Animation_OnFrame(&treasureChest->skelanime, 10.0f)) { + if ((treasureChest != NULL) && Animation_OnFrame(&treasureChest->skelanime, 10.0f)) { func_809937B4(this, globalCtx, treasureChest->skelanime.curFrame); } } void func_809937B4(DemoTreLgt* this, GlobalContext* globalCtx, f32 currentFrame) { - SkelAnimeCurve* skelCurve = &this->skelCurve; + SkelCurve* skelCurve = &this->skelCurve; s32 pad[2]; this->action = DEMO_TRE_LGT_ACTION_ANIMATE; - SkelCurve_SetAnim(skelCurve, sTransformUpdIdx[gSaveContext.linkAge], 1.0f, + SkelCurve_SetAnim(skelCurve, sAnimations[gSaveContext.linkAge], 1.0f, sDemoTreLgtInfo[gSaveContext.linkAge].endFrame + sDemoTreLgtInfo[gSaveContext.linkAge].unk_08, currentFrame, 1.0f); SkelCurve_Update(globalCtx, skelCurve); } void func_80993848(DemoTreLgt* this, GlobalContext* globalCtx) { - f32 currentFrame = this->skelCurve.animCurFrame; + f32 currentFrame = this->skelCurve.curFrame; if (currentFrame < sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].endFrame) { this->unk_170 = 255; @@ -131,7 +131,7 @@ void DemoTreLgt_Update(Actor* thisx, GlobalContext* globalCtx) { sActionFuncs[this->action](this, globalCtx); } -s32 DemoTreLgt_OverrideLimbDraw(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void* thisx) { +s32 DemoTreLgt_OverrideLimbDraw(GlobalContext* globalCtx, SkelCurve* skelCurve, s32 limbIndex, void* thisx) { s32 pad; DemoTreLgt* this = (DemoTreLgt*)thisx; @@ -149,9 +149,14 @@ s32 DemoTreLgt_OverrideLimbDraw(GlobalContext* globalCtx, SkelAnimeCurve* skelCu CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_tre_lgt.c", 448); //! @bug missing return - // If the return value ends up being false (0), the limb won't draw (meaning no limb at all will draw). - // In MQ Debug, `Graph_CloseDisps` has the last instruction writing to v0 before this function ends. - // That instruction sets v0 to a non-NULL pointer, which is "true", so the limbs get drawn. + //! If the returned value (i.e. the contents of v0) ends up being false (0), the limb won't draw. Therefore what + //! matters is what was last written to v0 before the end of the function. + //! - In debug versions, the last instruction that does this is in `Graph_CloseDisps`. + //! - In retail versions, `gDPSetPrimColor` writes to it last. + //! In both cases, that instruction sets v0 to a non-NULL pointer, which is "true", so the limb happens to be drawn. +#ifdef AVOID_UB + return true; +#endif } void DemoTreLgt_Draw(Actor* thisx, GlobalContext* globalCtx) { @@ -166,7 +171,7 @@ void DemoTreLgt_Draw(Actor* thisx, GlobalContext* globalCtx) { func_80093D84(gfxCtx); gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 0); - SkelCurve_Draw(&this->actor, globalCtx, &this->skelCurve, DemoTreLgt_OverrideLimbDraw, NULL, 1, thisx); + SkelCurve_Draw(&this->actor, globalCtx, &this->skelCurve, DemoTreLgt_OverrideLimbDraw, NULL, 1, &this->actor); CLOSE_DISPS(gfxCtx, "../z_demo_tre_lgt.c", 476); } diff --git a/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h b/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h index d34056ab8f..77de6b720d 100644 --- a/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h +++ b/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h @@ -10,7 +10,7 @@ typedef void (*DemoTreLgtActionFunc)(struct DemoTreLgt*, GlobalContext*); typedef struct DemoTreLgt { /* 0x0000 */ Actor actor; - /* 0x014C */ SkelAnimeCurve skelCurve; + /* 0x014C */ SkelCurve skelCurve; /* 0x016C */ s32 action; /* 0x0170 */ u32 unk_170; // some sort of alpha /* 0x0174 */ u32 unk_174; // another sort of alpha diff --git a/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c b/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c index 6ac1ee4656..34dd4647b3 100644 --- a/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c +++ b/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c @@ -45,7 +45,7 @@ void MagicWind_Init(Actor* thisx, GlobalContext* globalCtx) { MagicWind* this = (MagicWind*)thisx; Player* player = GET_PLAYER(globalCtx); - if (SkelCurve_Init(globalCtx, &this->skelCurve, &sSkel, &sAnim) == 0) { + if (!SkelCurve_Init(globalCtx, &this->skelCurve, &sSkel, &sAnim)) { // "Magic_Wind_Actor_ct (): Construct failed" osSyncPrintf("Magic_Wind_Actor_ct():コンストラクト失敗\n"); } @@ -140,8 +140,8 @@ void MagicWind_Update(Actor* thisx, GlobalContext* globalCtx) { this->actionFunc(this, globalCtx); } -s32 MagicWind_OverrideLimbDraw(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void* thisx) { - MagicWind* this = (MagicWind*)thisx; +s32 MagicWind_OverrideLimbDraw(GlobalContext* globalCtx, SkelCurve* skelCurve, s32 limbIndex, void* thisx) { + s32 pad; OPEN_DISPS(globalCtx->state.gfxCtx, "../z_magic_wind.c", 615); @@ -173,7 +173,7 @@ void MagicWind_Draw(Actor* thisx, GlobalContext* globalCtx) { if (this->actionFunc != MagicWind_WaitForTimer) { POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 25); - SkelCurve_Draw(thisx, globalCtx, &this->skelCurve, MagicWind_OverrideLimbDraw, NULL, 1, NULL); + SkelCurve_Draw(&this->actor, globalCtx, &this->skelCurve, MagicWind_OverrideLimbDraw, NULL, 1, NULL); } CLOSE_DISPS(gfxCtx, "../z_magic_wind.c", 673); diff --git a/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.h b/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.h index 25b3e7d70a..26a302e7aa 100644 --- a/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.h +++ b/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.h @@ -10,7 +10,7 @@ typedef void (*MagicWindFunc)(struct MagicWind* this, GlobalContext* globalCtx); typedef struct MagicWind { /* 0x0000 */ Actor actor; - /* 0x014C */ SkelAnimeCurve skelCurve; + /* 0x014C */ SkelCurve skelCurve; /* 0x016C */ s16 timer; /* 0x0170 */ MagicWindFunc actionFunc; } MagicWind; // size = 0x0174