diff options
| author | Tharo <17233964+Thar0@users.noreply.github.com> | 2024-05-17 13:29:19 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-17 08:29:19 -0400 |
| commit | c609d3dcae48ec2bd261c800f6cf75911a7bd1b2 (patch) | |
| tree | e2af33178a712f1a008d9c61af4c44a5f5dfdab2 /src | |
| parent | 27aa3f748b838efd6e779655e35c4cd1c38b09e1 (diff) | |
c_keyframe.c decompiled and documented (#1630)
* 2 non-matching
* 1 function left
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
* Fully matching
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
* Documented, has bss reordering issues
* Fixes
* Apply suggestions
* Format
* Re-add cast oops
* Remove != 0 for override and transform limb draws, format
---------
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
Co-authored-by: Derek Hensley <hensley.derek58@gmail.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/code/c_keyframe.c | 1329 | ||||
| -rw-r--r-- | src/code/z_collision_check.c | 1 | ||||
| -rw-r--r-- | src/code/z_demo.c | 22 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Boss_03/z_boss_03.c | 1 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.c | 37 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.h | 2 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.c | 23 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.h | 6 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Eff_Change/z_eff_change.c | 24 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Eff_Change/z_eff_change.h | 6 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Fall2/z_en_fall2.c | 18 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Fall2/z_en_fall2.h | 6 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Test/z_en_test.c | 26 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Test/z_en_test.h | 6 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Test7/z_en_test7.c | 26 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Test7/z_en_test7.h | 6 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c | 2 |
17 files changed, 1405 insertions, 136 deletions
diff --git a/src/code/c_keyframe.c b/src/code/c_keyframe.c index 5914eec77..1f148b75c 100644 --- a/src/code/c_keyframe.c +++ b/src/code/c_keyframe.c @@ -1,85 +1,1330 @@ +/** + * @file c_keyframe.c + * + * This file implements a skeletal animation system supporting all of scale, rotation and translation on all joints. It + * uses keyframe data and interpolates intermediate values via cubic Hermite splines. + */ #include "global.h" +#include "libc64/fixed_point.h" -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801830A0.s") +#define FMOD(x, mod) ((x) - ((s32)((x) * (1.0f / (mod))) * (f32)(mod))) -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801830C8.s") +/** + * @note Original name: cKF_FrameControl_zeroClera + */ +void FrameCtrl_Reset(FrameControl* frameCtrl) { + frameCtrl->frameCount = 0.0f; + frameCtrl->curTime = 0.0f; + frameCtrl->speed = 0.0f; + frameCtrl->end = 0.0f; + frameCtrl->start = 0.0f; + frameCtrl->animMode = KEYFRAME_ANIM_ONCE; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801830E8.s") +/** + * @note Original name: cKF_FrameControl_ct + */ +void FrameCtrl_Init(FrameControl* frameCtrl) { + FrameCtrl_Reset(frameCtrl); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183148.s") +/** + * @note Original name: cKF_FrameControl_setFrame + */ +void FrameCtrl_SetProperties(FrameControl* frameCtrl, f32 startTime, f32 endTime, f32 frameCount, f32 t, f32 speed, + s32 animMode) { + frameCtrl->start = startTime; + frameCtrl->end = (endTime < 1.0f) ? frameCount : endTime; + frameCtrl->frameCount = frameCount; + frameCtrl->speed = speed; + frameCtrl->curTime = t; + frameCtrl->animMode = animMode; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183224.s") +/** + * @note Original name: cKF_FrameControl_passCheck + */ +s32 FrameCtrl_PassCheck(FrameControl* frameCtrl, f32 t, f32* remainingTime) { + f32 curTime; + f32 speed; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801832B0.s") + *remainingTime = 0.0f; + curTime = frameCtrl->curTime; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_8018332C.s") + if (t == curTime) { + return false; + } -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_8018340C.s") + speed = ((frameCtrl->start < frameCtrl->end) ? frameCtrl->speed : -frameCtrl->speed) * (30.0f / 20.0f); -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183430.s") + if (((speed >= 0.0f) && (curTime < t) && (t <= curTime + speed)) || + ((speed < 0.0f) && (t < curTime) && (curTime + speed <= t))) { -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_8018349C.s") + *remainingTime = curTime + speed - t; + return true; + } + return false; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801834A8.s") +/** + * Updates a FrameControl structure whose mode is KEYFRAME_ANIM_ONCE + * + * @note Original name: cKF_FrameControl_stop_proc + */ +s32 FrameCtrl_UpdateOnce(FrameControl* frameCtrl) { + f32 remainingTime; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183510.s") + if (frameCtrl->curTime == frameCtrl->end) { + // If the current time is at the end time, the animation is done. + return KEYFRAME_DONE_ONCE; + } + if (FrameCtrl_PassCheck(frameCtrl, frameCtrl->end, &remainingTime)) { + frameCtrl->curTime = frameCtrl->end; + return KEYFRAME_DONE_ONCE; + } + if (FrameCtrl_PassCheck(frameCtrl, frameCtrl->start, &remainingTime)) { + frameCtrl->curTime = frameCtrl->end; + return KEYFRAME_DONE_ONCE; + } + return KEYFRAME_NOT_DONE; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183580.s") +/** + * Updates a FrameControl structure whose mode is KEYFRAME_ANIM_LOOP + * + * @note Original name: cKF_FrameControl_repeat_proc + */ +s32 FrameCtrl_UpdateLoop(FrameControl* frameCtrl) { + f32 remainingTime; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801835EC.s") + if (FrameCtrl_PassCheck(frameCtrl, frameCtrl->end, &remainingTime)) { + frameCtrl->curTime = frameCtrl->start + remainingTime; + return KEYFRAME_DONE_LOOP; + } + if (FrameCtrl_PassCheck(frameCtrl, frameCtrl->start, &remainingTime)) { + frameCtrl->curTime = frameCtrl->end + remainingTime; + return KEYFRAME_DONE_LOOP; + } + return KEYFRAME_NOT_DONE; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183658.s") +/** + * Check if the animation has finished playing and update the animation frame number. + * + * @note Original name: cKF_FrameControl_play + */ +s32 FrameCtrl_Update(FrameControl* frameCtrl) { + s32 result; + f32 speed; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801836CC.s") + // Check if the animation is done, possibly updating curTime + if (frameCtrl->animMode == KEYFRAME_ANIM_ONCE) { + result = FrameCtrl_UpdateOnce(frameCtrl); + } else { + result = FrameCtrl_UpdateLoop(frameCtrl); + } -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_8018373C.s") + if (result == KEYFRAME_NOT_DONE) { + // Animation is not done, step curTime by (speed * (30.0f / 20.0f)), adjusting the sign if the animation is + // playing in reverse (end <= start) + speed = (frameCtrl->start < frameCtrl->end) ? frameCtrl->speed : -frameCtrl->speed; + frameCtrl->curTime = frameCtrl->curTime + speed * (30.0f / 20.0f); + } -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801837CC.s") + // Adjust time for looping + if (frameCtrl->curTime < 1.0f) { + // Wrap from the start to the end of the animation + frameCtrl->curTime = (frameCtrl->curTime - 1.0f) + frameCtrl->frameCount; + } else if (frameCtrl->frameCount < frameCtrl->curTime) { + // Wrap from the end to the start of the animation + frameCtrl->curTime = (frameCtrl->curTime - frameCtrl->frameCount) + 1.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183808.s") + return result; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183880.s") +/** + * @note Original name unknown + */ +void Keyframe_ResetFlex(KFSkelAnimeFlex* kfSkelAnime) { + kfSkelAnime->skeleton = NULL; + kfSkelAnime->animation = NULL; + kfSkelAnime->jointTable = NULL; + kfSkelAnime->transformCallbacks = NULL; + kfSkelAnime->morphTable = NULL; + kfSkelAnime->morphFrames = 0.0f; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183A3C.s") +/** + * Initializes a flex-type keyframe skeleton. The initial animation type is KEYFRAME_ANIM_ONCE. + * + * @param skeleton Skeleton to animate + * @param animation Initial animation to use + * @param jointTable Joint table to store limb transformations. Should have enough space to store a root translation + * plus a limb rotation for all limbs in the skeleton. + * @param morphTable Joint table to store morph interpolation values. Should have enough space to store a root + * translation plus a limb rotation for all limbs in the skeleton. + * @param transformCallbacks Array of limb transformation callbacks that will be called when drawing a particular limb. + * The limb data contains the index to select which callback to run. + * + * @note Original name unknown + */ +void Keyframe_InitFlex(KFSkelAnimeFlex* kfSkelAnime, KeyFrameFlexSkeleton* skeleton, KeyFrameAnimation* animation, + Vec3s* jointTable, Vec3s* morphTable, KeyframeTransformCallback* transformCallbacks) { + Keyframe_ResetFlex(kfSkelAnime); + FrameCtrl_Init(&kfSkelAnime->frameCtrl); + kfSkelAnime->skeleton = Lib_SegmentedToVirtual(skeleton); + kfSkelAnime->animation = Lib_SegmentedToVirtual(animation); + kfSkelAnime->jointTable = jointTable; + kfSkelAnime->morphTable = morphTable; + kfSkelAnime->transformCallbacks = transformCallbacks; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183B08.s") +/** + * Destroys a flex-type keyframe skeleton. + * + * @note Original name unknown + */ +void Keyframe_DestroyFlex(KFSkelAnimeFlex* kfSkelAnime) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183B68.s") +void Keyframe_FlexChangeAnim(KFSkelAnimeFlex* kfSkelAnime, KeyFrameFlexSkeleton* skeleton, KeyFrameAnimation* animation, + f32 startTime, f32 endTime, f32 t, f32 speed, f32 morphFrames, s32 animMode); -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80183DE0.s") +/** + * Immediately changes to an animation that plays once from start to end at the default speed. + * + * @param animation Animation data to switch to + * + * @note Original name unknown + */ +void Keyframe_FlexPlayOnce(KFSkelAnimeFlex* kfSkelAnime, KeyFrameAnimation* animation) { + Keyframe_FlexChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, 0.0f, + KEYFRAME_ANIM_ONCE); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_8018410C.s") +/** + * Immediately changes to an animation that plays once from start to end at the specified speed. + * + * @param animation Animation data to switch to + * @param speed Playback speed + * + * @note Original name unknown + */ +void Keyframe_FlexPlayOnceSetSpeed(KFSkelAnimeFlex* kfSkelAnime, KeyFrameAnimation* animation, f32 speed) { + Keyframe_FlexChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, speed, 0.0f, + KEYFRAME_ANIM_ONCE); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_8018450C.s") +/** + * Smoothly transitions to an animation that plays once from start to end at the default speed, specifying the number of + * frames for the transition. + * + * @param animation Animation data to switch to + * @param morphFrames Number of frames to take to transition from the previous pose to the new animation. Positive morph + * frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose + * immediately before the animation change. + * + * @note Original name unknown + */ +void Keyframe_FlexMorphToPlayOnce(KFSkelAnimeFlex* kfSkelAnime, KeyFrameAnimation* animation, f32 morphFrames) { + Keyframe_FlexChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, + morphFrames, KEYFRAME_ANIM_ONCE); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801845A4.s") +/** + * Immediately changes to an animation that loops over start to end at the default speed. + * + * @param animation Animation data to switch to + * + * @note Original name unknown + */ +void Keyframe_FlexPlayLoop(KFSkelAnimeFlex* kfSkelAnime, KeyFrameAnimation* animation) { + Keyframe_FlexChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, 0.0f, + KEYFRAME_ANIM_LOOP); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801845C8.s") +/** + * Immediately changes to an animation that loops over start to end at the specified speed. + * + * @param animation Animation data to switch to + * @param speed Playback speed + * + * @note Original name unknown + */ +void Keyframe_FlexPlayLoopSetSpeed(KFSkelAnimeFlex* kfSkelAnime, KeyFrameAnimation* animation, f32 speed) { + Keyframe_FlexChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, speed, 0.0f, + KEYFRAME_ANIM_LOOP); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_8018462C.s") +/** + * Smoothly transitions to an animation that loops over start to end at the default speed, specifying the number of + * frames for the transition. + * + * @param animation Animation data to switch to + * @param morphFrames Number of frames to take to transition from the previous pose to the new animation. Positive morph + * frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose + * immediately before the animation change. + * + * @note Original name unknown + */ +void Keyframe_FlexMorphToPlayLoop(KFSkelAnimeFlex* kfSkelAnime, KeyFrameAnimation* animation, f32 morphFrames) { + Keyframe_FlexChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, + morphFrames, KEYFRAME_ANIM_LOOP); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80184638.s") +/** + * General way to set a new animation for flex-type skeletons, allowing choice of playback speed, start/end loop points, + * start time, play mode, and number of transition frames. + * + * Time parameters are valid from 0 to the last frame of the animation. + * + * @param skeleton Skeleton that will be animated + * @param animation Animation data to switch to + * @param startTime Loop start time + * @param endTime Loop end time, 0 indicates to use the animation length + * @param t Playback start time + * @param speed Playback speed + * @param morphFrames Number of frames to take to transition from the previous pose to the new animation. Positive morph + * frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose + * immediately before the animation change. + * @param animMode Animation play mode, see KeyFrameAnimMode enum + * + * @see KeyFrameAnimMode + * + * @note Original name unknown + */ +void Keyframe_FlexChangeAnim(KFSkelAnimeFlex* kfSkelAnime, KeyFrameFlexSkeleton* skeleton, KeyFrameAnimation* animation, + f32 startTime, f32 endTime, f32 t, f32 speed, f32 morphFrames, s32 animMode) { + kfSkelAnime->morphFrames = morphFrames; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801846AC.s") + if (kfSkelAnime->skeleton != skeleton) { + kfSkelAnime->skeleton = Lib_SegmentedToVirtual(skeleton); + } + kfSkelAnime->animation = Lib_SegmentedToVirtual(animation); -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80184728.s") + FrameCtrl_SetProperties(&kfSkelAnime->frameCtrl, startTime, endTime, kfSkelAnime->animation->frameCount, t, speed, + animMode); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801847A0.s") +/** + * Switches to a new animation without changing any of the playback parameters. + * + * @param animation The animation to switch to + * + * @note Original name unknown + */ +void Keyframe_FlexChangeAnimQuick(KFSkelAnimeFlex* kfSkelAnime, KeyFrameAnimation* animation) { + kfSkelAnime->animation = Lib_SegmentedToVirtual(animation); + kfSkelAnime->frameCtrl.frameCount = kfSkelAnime->animation->frameCount; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80184818.s") +/** + * Compute a value on the cubic Hermite spline x(t) at a time `t` in the unit interval [0, 1] + * + * @param t Time parameter at which to sample the curve + * @param delta Scales the rates of change of the curve endpoints v0 and v1 + * @param x0 Value on the curve at t=0 + * @param x1 Value on the curve at t=1 + * @param v0 Rate of change of the curve at t=0 + * @param v1 Rate of change of the curve at t=1 + * + * @note Original name: cKF_HermitCalc + */ +f32 Keyframe_Interpolate(f32 t, f32 delta, f32 x0, f32 x1, f32 v0, f32 v1) { + f32 px1 = 3.0f * SQ(t) - 2.0f * CB(t); + f32 px0 = 1.0f - px1; + f32 pv0 = CB(t) - 2.0f * SQ(t) + t; + f32 pv1 = CB(t) - SQ(t); -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80184898.s") + return px0 * x0 + px1 * x1 + (pv0 * v0 + pv1 * v1) * delta; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80184914.s") +/** + * Computes an output value at time `t` based on interpolation of the provided keyframes. + * Interpolation between keyframes is performed via cubic Hermite splines. + * + * @param kfStart Index of the first keyframe to consider + * @param kfNum Number of keyframes following the first keyframe to consider + * @param keyFrames Array of all keyframes + * @param t Time at which to sample the interpolated curve + * + * @return The interpolated value + * + * @note Original name: cKF_KeyCalc + */ +s16 Keyframe_KeyCalc(s16 kfStart, s16 kfNum, KeyFrame* keyFrames, f32 t) { + KeyFrame* keyFramesOffset = &keyFrames[kfStart]; + f32 delta; + s16 kf1; + s16 kf2; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801849A0.s") + if (t <= keyFramesOffset->frame) { + return keyFramesOffset->value; + } + if (keyFramesOffset[kfNum - 1].frame <= t) { + return keyFramesOffset[kfNum - 1].value; + } -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801849DC.s") + kf1 = 0; + kf2 = 1; + while (true) { + // Search for the keyframes kf1 and kf2 such that kf1.frame <= t < kf2.frame + if (t < keyFramesOffset[kf2].frame) { + delta = keyFramesOffset[kf2].frame - keyFramesOffset[kf1].frame; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80184C48.s") + if (!IS_ZERO(delta)) { + // Between two keyframes, interpolate a value and round to nearest integer + return nearbyint(Keyframe_Interpolate((t - keyFramesOffset[kf1].frame) / delta, delta * (1.0f / 30), + keyFramesOffset[kf1].value, keyFramesOffset[kf2].value, + keyFramesOffset[kf1].velocity, keyFramesOffset[kf2].velocity)); + } else { + // Close enough to a keyframe, take the specified value with no interpolation + return keyFramesOffset[kf1].value; + } + } + kf1++; + kf2++; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801850A0.s") +/** + * Morph interpolator for rotation. + * + * Linearly interpolates between `rot1` and `rot2` with weight `t`, choosing either signed angles or unsigned angles + * based on whichever choice has the smaller distance between the two. + * + * @note Original name: cKF_SkeletonInfo_subRotInterpolation + */ +void Keyframe_MorphInterpolateRotation(f32 t, s16* out, s16 rot1, s16 rot2) { + u16 urot1 = rot1; + s32 pad; + u16 urot2 = rot2; + f32 rot1f = rot1; + f32 signedDiff = rot2 - rot1f; + f32 urot1f = urot1; + f32 unsignedDiff = urot2 - urot1f; -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_801853C8.s") + if (fabsf(signedDiff) < fabsf(unsignedDiff)) { + *out = rot1f + signedDiff * t; + } else { + *out = urot1f + unsignedDiff * t; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/code/c_keyframe/func_80185460.s") +/** + * Morph interpolator for translation and scale. + * + * Linearly interpolates between `jointData` and `morphData` with weight `t`, storing the result back into `jointData`. + * + * @note Original name: cKF_SkeletonInfo_morphST + */ +void Keyframe_MorphInterpolateLinear(s16* jointData, s16* morphData, f32 t) { + s32 i; + + for (i = 0; i < 3; i++) { + if (*jointData != *morphData) { + f32 f1 = *jointData; + f32 f2 = *morphData; + *jointData = f1 + (f2 - f1) * t; + } + jointData++; + morphData++; + } +} + +/** + * Apply morph interpolation for the provided skeleton. Morph interpolation seeks to provide interpolation between + * a previous animation and a new animation over a fixed period of time (morphFrames) + * + * @note Original name unknown + */ +void Keyframe_FlexMorphInterpolation(KFSkelAnimeFlex* kfSkelAnime) { + Vec3s* jointTable = kfSkelAnime->jointTable; + Vec3s* morphTable = kfSkelAnime->morphTable; + f32 t = 1.0f / fabsf(kfSkelAnime->morphFrames); + s32 limbIndex; + + for (limbIndex = 0; limbIndex < kfSkelAnime->skeleton->limbCount; limbIndex++) { + Vec3s frameRot; + Vec3s morphRot; + + // Interpolate scale + Keyframe_MorphInterpolateLinear((s16*)jointTable, (s16*)morphTable, t); + jointTable++; + morphTable++; + + // Read rotation + frameRot.x = jointTable->x; + frameRot.y = jointTable->y; + frameRot.z = jointTable->z; + + morphRot.x = morphTable->x; + morphRot.y = morphTable->y; + morphRot.z = morphTable->z; + + // Interpolate rotation + if (frameRot.x != morphRot.x || frameRot.y != morphRot.y || frameRot.z != morphRot.z) { + Vec3s frameRotInv; + f32 norm1; + f32 norm2; + + frameRotInv.x = 0x7FFF + frameRot.x; + frameRotInv.y = 0x7FFF - frameRot.y; + frameRotInv.z = 0x7FFF + frameRot.z; + + // Compute L1 norms + norm1 = fabsf((f32)morphRot.x - frameRot.x) + fabsf((f32)morphRot.y - frameRot.y) + + fabsf((f32)morphRot.z - frameRot.z); + norm2 = fabsf((f32)morphRot.x - frameRotInv.x) + fabsf((f32)morphRot.y - frameRotInv.y) + + fabsf((f32)morphRot.z - frameRotInv.z); + + if (norm1 < norm2) { + // frameRot is closer to morphRot than frameRotInv, interpolate between these two + Keyframe_MorphInterpolateRotation(t, &jointTable->x, frameRot.x, morphRot.x); + Keyframe_MorphInterpolateRotation(t, &jointTable->y, frameRot.y, morphRot.y); + Keyframe_MorphInterpolateRotation(t, &jointTable->z, frameRot.z, morphRot.z); + } else { + // frameRotInv is closer to morphRot than frameRot, interpolate between these two + Keyframe_MorphInterpolateRotation(t, &jointTable->x, frameRotInv.x, morphRot.x); + Keyframe_MorphInterpolateRotation(t, &jointTable->y, frameRotInv.y, morphRot.y); + Keyframe_MorphInterpolateRotation(t, &jointTable->z, frameRotInv.z, morphRot.z); + } + } + morphTable++; + jointTable++; + + // Interpolate translation + Keyframe_MorphInterpolateLinear((s16*)jointTable, (s16*)morphTable, t); + jointTable++; + morphTable++; + } +} + +/** + * Advances the current animation and updates all frame tables for flex-type keyframe skeletons. + * + * @return s32 + * KEYFRAME_NOT_DONE : If the animation is still playing + * KEYFRAME_DONE_ONCE : If the animation was set to play once and has finished playing + * KEYFRAME_DONE_LOOP : If the animation was set to play in a loop and has finished a loop + * + * @note Original name unknown + */ +s32 Keyframe_UpdateFlex(KFSkelAnimeFlex* kfSkelAnime) { + s32 limbIndex; + s32 pad[2]; + u16* bitFlags; + s16* outputValues; + s32 kfn = 0; + s32 fixedValueIndex = 0; + s32 kfStart = 0; + s16* fixedValues; + KeyFrame* keyFrames; + s16* kfNums; + u32 bit; + s32 i; + s32 j; + + // If there are morph frames to process, use the morph table + if (kfSkelAnime->morphFrames != 0.0f) { + outputValues = (s16*)kfSkelAnime->morphTable; + } else { + outputValues = (s16*)kfSkelAnime->jointTable; + } + + // Array of preset values to pull from + fixedValues = Lib_SegmentedToVirtual(kfSkelAnime->animation->fixedValues); + + // Array of number of keyframes belonging to each limb + kfNums = Lib_SegmentedToVirtual(kfSkelAnime->animation->kfNums); + + // Array of keyframes, ordered by frame number + keyFrames = Lib_SegmentedToVirtual(kfSkelAnime->animation->keyFrames); + + // The bitFlags array indicates whether a transformation on an axis should interpolate a value (if the bit is set) + // or pull from an array of constant values (if the bit is unset) if the transformation on an axis does not change + // during the animtion. For the flex-type keyframe skeletons the flags for each limb are contained in 16 bits. + // The bitFlags layout for the flex-type keyframe skeletons is the same for all limbs: + // [8] : Scale x + // [7] : Scale y + // [6] : Scale z + // [5] : Rotate x + // [4] : Rotate y + // [3] : Rotate z + // [2] : Translate x + // [1] : Translate y + // [0] : Translate z + bitFlags = Lib_SegmentedToVirtual(kfSkelAnime->animation->bitFlags.flex); + + // For each limb + for (limbIndex = 0; limbIndex < kfSkelAnime->skeleton->limbCount; limbIndex++) { + bit = 1 << (3 * 3 - 1); + + // 3 iter (scale, rotate, translate) + for (i = 0; i < 3; i++) { + // 3 iter (x, y, z) + for (j = 0; j < 3; j++) { + if (bitFlags[limbIndex] & bit) { + // If the bit is set, interpolate with keyframes + *outputValues = Keyframe_KeyCalc(kfStart, kfNums[kfn], keyFrames, kfSkelAnime->frameCtrl.curTime); + kfStart += kfNums[kfn]; + kfn++; + } else { + // If the bit is not set, pull from preset values + *outputValues = fixedValues[fixedValueIndex]; + fixedValueIndex++; + } + bit >>= 1; + + if (i == 1) { + // For rotations, translate angle value from tenths of a degree to binang + *outputValues = DEG_TO_BINANG(FMOD(*outputValues * 0.1f, 360)); + } + outputValues++; + } + } + } + + if (IS_ZERO(kfSkelAnime->morphFrames)) { + // No morph, just play the animation + return FrameCtrl_Update(&kfSkelAnime->frameCtrl); + } else if (kfSkelAnime->morphFrames > 0.0f) { + // Morph to first frame before playing the animation proper + Keyframe_FlexMorphInterpolation(kfSkelAnime); + kfSkelAnime->morphFrames -= 1.0f; + if (kfSkelAnime->morphFrames <= 0.0f) { + kfSkelAnime->morphFrames = 0.0f; + } + return KEYFRAME_NOT_DONE; + } else { + // Play the animation immediately, morphing as it plays + Keyframe_FlexMorphInterpolation(kfSkelAnime); + kfSkelAnime->morphFrames += 1.0f; + if (kfSkelAnime->morphFrames >= 0.0f) { + kfSkelAnime->morphFrames = 0.0f; + } + return FrameCtrl_Update(&kfSkelAnime->frameCtrl); + } +} + +/** + * Draws the limb specified by `limbIndex` of type `KeyFrameFlexLimb` belonging to a flex-type keyframe skeleton to the + * display buffer specified by the limb's drawFlags. + * + * @param limbIndex Pointer to the index of the limb to draw + * @param overrideKeyframeDraw Callback for before submitting the limb to be drawn. The matrix state will not include + * the transformation for the current limb. + * @param postKeyframeDraw Callback for after submitting the limb to be drawn. The matrix state will include + * the transformation for the current limb. + * @param arg An arbitrary argument to pass to the callbacks. + * @param mtxStack Matrix stack for limb transformations. Should have enough room for one matrix per limb. + * + * @note Original name unknown + */ +void Keyframe_DrawFlexLimb(PlayState* play, KFSkelAnimeFlex* kfSkelAnime, s32* limbIndex, + OverrideKeyframeDrawScaled overrideKeyframeDraw, PostKeyframeDrawScaled postKeyframeDraw, + void* arg, Mtx** mtxStack) { + KeyFrameFlexLimb* limb = Lib_SegmentedToVirtual(kfSkelAnime->skeleton->limbs); + s32 i; + Gfx* newDList; + Gfx* limbDList; + u8 drawFlags; + Vec3f scale; + Vec3s rot; + Vec3f pos; + Vec3s* jointData; + + OPEN_DISPS(play->state.gfxCtx); + + limb += *limbIndex; + jointData = &kfSkelAnime->jointTable[*limbIndex * 3]; + + scale.x = jointData->x * 0.01f; + scale.y = jointData->y * 0.01f; + scale.z = jointData->z * 0.01f; + + jointData++; + + rot.x = jointData->x; + rot.y = jointData->y; + rot.z = jointData->z; + + jointData++; + + pos.x = jointData->x; + pos.y = jointData->y; + pos.z = jointData->z; + + Matrix_Push(); + + newDList = limbDList = limb->dList; + drawFlags = limb->drawFlags; + + if (overrideKeyframeDraw == NULL || + (overrideKeyframeDraw != NULL && + overrideKeyframeDraw(play, kfSkelAnime, *limbIndex, &newDList, &drawFlags, arg, &scale, &rot, &pos))) { + if ((kfSkelAnime->transformCallbacks == NULL) || (limb->callbackIndex == KF_CALLBACK_INDEX_NONE) || + (kfSkelAnime->transformCallbacks[limb->callbackIndex] == NULL) || + kfSkelAnime->transformCallbacks[limb->callbackIndex](play, kfSkelAnime, *limbIndex, &newDList, &drawFlags, + arg)) { + + Matrix_TranslateRotateZYX(&pos, &rot); + + if (scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f) { + Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY); + } + + if (newDList != NULL) { + Matrix_ToMtx(*mtxStack); + + if (drawFlags & KEYFRAME_DRAW_XLU) { + gSPMatrix(POLY_XLU_DISP++, *mtxStack, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, newDList); + } else { + gSPMatrix(POLY_OPA_DISP++, *mtxStack, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, newDList); + } + + (*mtxStack)++; + } else if (limbDList != NULL) { + gSPMatrix(POLY_OPA_DISP++, *mtxStack, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + Matrix_ToMtx(*mtxStack); + + (*mtxStack)++; + } + } + } + + if (postKeyframeDraw != NULL) { + postKeyframeDraw(play, kfSkelAnime, *limbIndex, &newDList, &drawFlags, arg, &scale, &rot, &pos); + } + + (*limbIndex)++; + + for (i = 0; i < limb->numChildren; i++) { + Keyframe_DrawFlexLimb(play, kfSkelAnime, limbIndex, overrideKeyframeDraw, postKeyframeDraw, arg, mtxStack); + } + + Matrix_Pop(); + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * Draws a flex-type keyframe skeleton in its current pose. + * + * @param mtxStack Matrix stack for limb transformations. Should have enough room for one matrix per limb. + * @param overrideKeyframeDraw Callback for before submitting the limb to be drawn. The matrix state will not include + * the transformation for the current limb. + * @param postKeyframeDraw Callback for after submitting the limb to be drawn. The matrix state will include + * the transformation for the current limb. + * @param arg An arbitrary argument to pass to the callbacks. + * + * @note Original name unknown + */ +void Keyframe_DrawFlex(PlayState* play, KFSkelAnimeFlex* kfSkelAnime, Mtx* mtxStack, + OverrideKeyframeDrawScaled overrideKeyframeDraw, PostKeyframeDrawScaled postKeyframeDraw, + void* arg) { + s32 limbIndex; + + if (mtxStack == NULL) { + return; + } + + OPEN_DISPS(play->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x0D, mtxStack); + gSPSegment(POLY_XLU_DISP++, 0x0D, mtxStack); + + limbIndex = 0; + Keyframe_DrawFlexLimb(play, kfSkelAnime, &limbIndex, overrideKeyframeDraw, postKeyframeDraw, arg, &mtxStack); + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * @note Original name: cKF_SkeletonInfo_R_zeroClear + */ +void Keyframe_ResetStandard(KFSkelAnime* kfSkelAnime) { + kfSkelAnime->skeleton = NULL; + kfSkelAnime->animation = NULL; + kfSkelAnime->jointTable = NULL; + kfSkelAnime->morphTable = NULL; + kfSkelAnime->rotOffsetsTable = NULL; + kfSkelAnime->morphFrames = 0.0f; +} + +/** + * Initializes a standard-type keyframe skeleton. The initial animation type is KEYFRAME_ANIM_ONCE. + * + * @param skeleton Skeleton to animate + * @param animation Initial animation to use + * @param jointTable Joint table to store limb transformations. Should have enough space to store a root translation + * plus a limb rotation for all limbs in the skeleton. + * @param morphTable Joint table to store morph interpolation values. Should have enough space to store a root + * translation plus a limb rotation for all limbs in the skeleton. + * + * @note Original name: cKF_SkeletonInfo_R_ct + */ +void Keyframe_InitStandard(KFSkelAnime* kfSkelAnime, KeyFrameSkeleton* skeleton, KeyFrameAnimation* animation, + Vec3s* jointTable, Vec3s* morphTable) { + Keyframe_ResetStandard(kfSkelAnime); + FrameCtrl_Init(&kfSkelAnime->frameCtrl); + kfSkelAnime->skeleton = Lib_SegmentedToVirtual(skeleton); + kfSkelAnime->animation = Lib_SegmentedToVirtual(animation); + kfSkelAnime->jointTable = jointTable; + kfSkelAnime->morphTable = morphTable; +} + +/** + * Destroys a standard-type keyframe skeleton. + * + * @note Original name: cKF_SkeletonInfo_R_dt + */ +void Keyframe_DestroyStandard(KFSkelAnime* kfSkelAnime) { +} + +void Keyframe_StandardChangeAnim(KFSkelAnime* kfSkelAnime, KeyFrameSkeleton* skeleton, KeyFrameAnimation* animation, + f32 startTime, f32 endTime, f32 t, f32 speed, f32 morphFrames, s32 animMode, + Vec3s* rotOffsetsTable); + +/** + * Immediately changes to an animation that plays once from start to end at the default speed. + * + * @param animation Animation data to switch to + * @param rotOffsetsTable Table of length `skeleton->limbCount` containing rotations to add to every pose of the + * animation. + * + * @note Original name: cKF_SkeletonInfo_R_init_standard_stop + */ +void Keyframe_StandardPlayOnce(KFSkelAnime* kfSkelAnime, KeyFrameAnimation* animation, Vec3s* rotOffsetsTable) { + Keyframe_StandardChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, 0.0f, + KEYFRAME_ANIM_ONCE, rotOffsetsTable); +} + +/** + * Immediately changes to an animation that plays once from start to end at the specified speed. + * + * @param animation Animation data to switch to + * @param rotOffsetsTable Table of length `skeleton->limbCount` containing rotations to add to every pose of the + * animation. + * @param speed Playback speed + * + * @note Original name: cKF_SkeletonInfo_R_init_standard_stop_speedset + */ +void Keyframe_StandardPlayOnceSetSpeed(KFSkelAnime* kfSkelAnime, KeyFrameAnimation* animation, Vec3s* rotOffsetsTable, + f32 speed) { + Keyframe_StandardChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, speed, 0.0f, + KEYFRAME_ANIM_ONCE, rotOffsetsTable); +} + +/** + * Smoothly transitions to an animation that plays once from start to end at the default speed. + * + * @param animation Animation data to switch to + * @param rotOffsetsTable Table of length `skeleton->limbCount` containing rotations to add to every pose of the + * animation. + * @param morphFrames Number of frames to take to transition from the previous pose to the new animation. Positive morph + * frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose + * immediately before the animation change. + * + * @note Original name: cKF_SkeletonInfo_R_init_standard_stop_morph + */ +void Keyframe_StandardMorphToPlayOnce(KFSkelAnime* kfSkelAnime, KeyFrameAnimation* animation, Vec3s* rotOffsetsTable, + f32 morphFrames) { + Keyframe_StandardChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, + morphFrames, KEYFRAME_ANIM_ONCE, rotOffsetsTable); +} + +/** + * Immediately changes to an animation that loops at the default. + * + * @param animation Animation data to switch to + * @param rotOffsetsTable Table of length `skeleton->limbCount` containing rotations to add to every pose of the + * animation. + * + * @note Original name: cKF_SkeletonInfo_R_init_standard_repeat + */ +void Keyframe_StandardPlayLoop(KFSkelAnime* kfSkelAnime, KeyFrameAnimation* animation, Vec3s* rotOffsetsTable) { + Keyframe_StandardChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, 0.0f, + KEYFRAME_ANIM_LOOP, rotOffsetsTable); +} + +/** + * Immediately changes to an animation that loops over start to end at the specified speed. + * + * @param animation Animation data to switch to + * @param rotOffsetsTable Table of length `skeleton->limbCount` containing rotations to add to every pose of the + * animation. + * @param speed Playback speed + * + * @note Original name: cKF_SkeletonInfo_R_init_standard_repeat_speedset + */ +void Keyframe_StandardPlayLoopSetSpeed(KFSkelAnime* kfSkelAnime, KeyFrameAnimation* animation, Vec3s* rotOffsetsTable, + f32 speed) { + Keyframe_StandardChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, speed, 0.0f, + KEYFRAME_ANIM_LOOP, rotOffsetsTable); +} + +/** + * Smoothly transitions to an animation that loops over start to end at the default speed, specifying the number of + * frames for the transition. + * + * @param animation Animation data to switch to + * @param rotOffsetsTable Table of length `skeleton->limbCount` containing rotations to add to every pose of the + * animation. + * @param morphFrames Number of frames to take to transition from the previous pose to the new animation. Positive morph + * frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose + * immediately before the animation change. + * + * @note Original name: cKF_SkeletonInfo_R_init_standard_repeat_morph + */ +void Keyframe_StandardMorphToPlayLoop(KFSkelAnime* kfSkelAnime, KeyFrameAnimation* animation, Vec3s* rotOffsetsTable, + f32 morphFrames) { + Keyframe_StandardChangeAnim(kfSkelAnime, kfSkelAnime->skeleton, animation, 1.0f, + ((KeyFrameAnimation*)Lib_SegmentedToVirtual(animation))->frameCount, 1.0f, 1.0f, + morphFrames, KEYFRAME_ANIM_LOOP, rotOffsetsTable); +} + +/** + * General way to set a new animation for standard-type skeletons, allowing choice of playback speed, start/end loop + * points, start time, play mode, and number of transition frames. + * + * Time parameters are valid from 0 to the last frame of the animation. + * + * @param skeleton Skeleton that will be animated + * @param animation Animation data to switch to + * @param startTime Loop start time + * @param endTime Loop end time, 0 indicates to use the animation length + * @param t Playback start time + * @param speed Playback speed + * @param morphFrames Number of frames to take to transition from the previous pose to the new animation. Positive morph + * frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose + * immediately before the animation change. + * @param animMode Animation play mode, see KeyFrameAnimMode enum + * @param rotOffsetsTable Table of length `skeleton->limbCount` containing rotations to add to every pose of the + * animation. + * + * @see KeyFrameAnimMode + * + * @note Original name: cKF_SkeletonInfo_R_init + */ +void Keyframe_StandardChangeAnim(KFSkelAnime* kfSkelAnime, KeyFrameSkeleton* skeleton, KeyFrameAnimation* animation, + f32 startTime, f32 endTime, f32 t, f32 speed, f32 morphFrames, s32 animMode, + Vec3s* rotOffsetsTable) { + kfSkelAnime->morphFrames = morphFrames; + kfSkelAnime->skeleton = Lib_SegmentedToVirtual(skeleton); + kfSkelAnime->animation = Lib_SegmentedToVirtual(animation); + + FrameCtrl_SetProperties(&kfSkelAnime->frameCtrl, startTime, endTime, kfSkelAnime->animation->frameCount, t, speed, + animMode); + kfSkelAnime->rotOffsetsTable = rotOffsetsTable; +} + +/** + * Switches to a new animation without changing any of the playback parameters. + * + * @param animation The animation to switch to + * + * @note Original name: cKF_SkeletonInfo_R_setAnim + */ +void Keyframe_StandardChangeAnimQuick(KFSkelAnime* kfSkelAnime, KeyFrameAnimation* animation) { + kfSkelAnime->animation = Lib_SegmentedToVirtual(animation); + kfSkelAnime->frameCtrl.frameCount = kfSkelAnime->animation->frameCount; +} + +/** + * Apply morph interpolation for the provided skeleton. Morph interpolation seeks to provide interpolation between + * a previous animation and a new animation over a fixed period of time (morphFrames) + * + * @note Original name: cKF_SkeletonInfo_R_morphJoint + */ +void Keyframe_StandardMorphInterpolation(KFSkelAnime* kfSkelAnime) { + Vec3s* jointTable = kfSkelAnime->jointTable; + Vec3s* morphTable = kfSkelAnime->morphTable; + f32 t = 1.0f / fabsf(kfSkelAnime->morphFrames); + s32 limbIndex; + + // Interpolate root translation + Keyframe_MorphInterpolateLinear((s16*)jointTable, (s16*)morphTable, t); + jointTable++; + morphTable++; + + for (limbIndex = 0; limbIndex < kfSkelAnime->skeleton->limbCount; limbIndex++) { + Vec3s frameRot; + Vec3s morphRot; + + frameRot.x = jointTable->x; + frameRot.y = jointTable->y; + frameRot.z = jointTable->z; + + morphRot.x = morphTable->x; + morphRot.y = morphTable->y; + morphRot.z = morphTable->z; + + // Interpolate rotation + if (frameRot.x != morphRot.x || frameRot.y != morphRot.y || frameRot.z != morphRot.z) { + Vec3s frameRotInv; + f32 norm1; + f32 norm2; + + frameRotInv.x = 0x7FFF + frameRot.x; + frameRotInv.y = 0x7FFF - frameRot.y; + frameRotInv.z = 0x7FFF + frameRot.z; + + // Compute L1 norms + norm1 = fabsf((f32)morphRot.x - frameRot.x) + fabsf((f32)morphRot.y - frameRot.y) + + fabsf((f32)morphRot.z - frameRot.z); + norm2 = fabsf((f32)morphRot.x - frameRotInv.x) + fabsf((f32)morphRot.y - frameRotInv.y) + + fabsf((f32)morphRot.z - frameRotInv.z); + + if (norm1 < norm2) { + // frameRot is closer to morphRot than frameRotInv, interpolate between these two + Keyframe_MorphInterpolateRotation(t, &jointTable->x, frameRot.x, morphRot.x); + Keyframe_MorphInterpolateRotation(t, &jointTable->y, frameRot.y, morphRot.y); + Keyframe_MorphInterpolateRotation(t, &jointTable->z, frameRot.z, morphRot.z); + } else { + // frameRotInv is closer to morphRot than frameRot, interpolate between these two + Keyframe_MorphInterpolateRotation(t, &jointTable->x, frameRotInv.x, morphRot.x); + Keyframe_MorphInterpolateRotation(t, &jointTable->y, frameRotInv.y, morphRot.y); + Keyframe_MorphInterpolateRotation(t, &jointTable->z, frameRotInv.z, morphRot.z); + } + } + morphTable++; + jointTable++; + } +} + +/** + * Advances the current animation and updates all frame tables for standard-type keyframe skeletons. + * + * @return s32 + * KEYFRAME_NOT_DONE : If the animation is still playing + * KEYFRAME_DONE_ONCE : If the animation was set to play once and has finished playing + * KEYFRAME_DONE_LOOP : If the animation was set to play in a loop and has finished a loop + * + * @note Original name: cKF_SkeletonInfo_R_play + */ +s32 Keyframe_UpdateStandard(KFSkelAnime* kfSkelAnime) { + s32 limbIndex; + u32 bit; + u8* bitFlags; + s32 i; + s32 kfn = 0; + s32 fixedValueIndex = 0; + s32 kfStart = 0; + s16* fixedValues; + KeyFrame* keyFrames; + s16* kfNums; + s16* outputValues; + + // Choose which array to update, if currently morphing update the morph table else update the joint table + if (kfSkelAnime->morphFrames != 0.0f) { + outputValues = (s16*)kfSkelAnime->morphTable; + } else { + outputValues = (s16*)kfSkelAnime->jointTable; + } + + fixedValues = Lib_SegmentedToVirtual(kfSkelAnime->animation->fixedValues); + kfNums = Lib_SegmentedToVirtual(kfSkelAnime->animation->kfNums); + keyFrames = Lib_SegmentedToVirtual(kfSkelAnime->animation->keyFrames); + + // The bitFlags array indicates whether a transformation on an axis should interpolate a value (if the bit is set) + // or pull from an array of constant values (if the bit is unset) if the transformation on an axis does not change + // during the animtion. For the standard-type keyframe skeletons the flags for each limb are contained in 8 bits. + // The bitFlags layout for the standard-type keyframe skeletons is different for the root limb, which may have a + // translation: + // [5] = tx + // [4] = ty + // [3] = tz + // [2] = rx + // [1] = ry + // [0] = rz + // Otherwise, the layout only contains rotations: + // [2] = rx + // [1] = ry + // [0] = rz + bitFlags = Lib_SegmentedToVirtual(kfSkelAnime->animation->bitFlags.standard); + + // Interpolate translation for the root limb + + bit = 1 << (3 * 2 - 1); + + // 3 iter (x, y, z) + for (i = 0; i < 3; i++) { + if (bitFlags[0] & bit) { + *outputValues = Keyframe_KeyCalc(kfStart, kfNums[kfn], keyFrames, kfSkelAnime->frameCtrl.curTime); + kfStart += kfNums[kfn++]; + } else { + *outputValues = fixedValues[fixedValueIndex++]; + } + bit >>= 1; + outputValues++; + } + + // Update rotation for all limbs + + for (limbIndex = 0; limbIndex < kfSkelAnime->skeleton->limbCount; limbIndex++) { + bit = 1 << (3 - 1); + + // 3 iter (x, y, z) + for (i = 0; i < 3; i++) { + s32 pad; + + if (bitFlags[limbIndex] & bit) { + *outputValues = Keyframe_KeyCalc(kfStart, kfNums[kfn], keyFrames, kfSkelAnime->frameCtrl.curTime); + kfStart += kfNums[kfn++]; + } else { + *outputValues = fixedValues[fixedValueIndex++]; + } + bit >>= 1; + + // Translate angle value from tenths of a degree to binang + *outputValues = DEG_TO_BINANG(FMOD(*outputValues * 0.1f, 360)); + outputValues++; + } + } + + if (kfSkelAnime->rotOffsetsTable != NULL) { + Vec3s* table; + + if (kfSkelAnime->morphFrames != 0.0f) { + table = kfSkelAnime->morphTable; + } else { + table = kfSkelAnime->jointTable; + } + table++; // Skip root translation + + // Add all offsets to rotations + for (limbIndex = 0; limbIndex < kfSkelAnime->skeleton->limbCount; limbIndex++, table++) { + table->x = table->x + kfSkelAnime->rotOffsetsTable[limbIndex].x; + table->y = table->y + kfSkelAnime->rotOffsetsTable[limbIndex].y; + table->z = table->z + kfSkelAnime->rotOffsetsTable[limbIndex].z; + } + } + + if (IS_ZERO(kfSkelAnime->morphFrames)) { + // No morph, just play the animation + return FrameCtrl_Update(&kfSkelAnime->frameCtrl); + } else if (kfSkelAnime->morphFrames > 0.0f) { + // Morph to first frame before playing the animation proper + Keyframe_StandardMorphInterpolation(kfSkelAnime); + kfSkelAnime->morphFrames -= 1.0f; + if (kfSkelAnime->morphFrames <= 0.0f) { + kfSkelAnime->morphFrames = 0.0f; + } + return KEYFRAME_NOT_DONE; + } else { + // Play the animation immediately, morphing as it plays + Keyframe_StandardMorphInterpolation(kfSkelAnime); + kfSkelAnime->morphFrames += 1.0f; + if (kfSkelAnime->morphFrames >= 0.0f) { + kfSkelAnime->morphFrames = 0.0f; + } + return FrameCtrl_Update(&kfSkelAnime->frameCtrl); + } +} + +/** + * Draws the limb specified by `limbIndex` of type `KeyFrameStandardLimb` belonging to a standard-type keyframe skeleton + * to the display buffer specified by the limb's drawFlags. + * + * @param limbIndex Pointer to the index of the limb to draw + * @param overrideKeyframeDraw Callback for before submitting the limb to be drawn. The matrix state will not include + * the transformation for the current limb. + * @param postKeyframeDraw Callback for after submitting the limb to be drawn. The matrix state will include + * the transformation for the current limb. + * @param arg An arbitrary argument to pass to the callbacks. + * @param mtxStack Matrix stack for limb transformations. Should have enough room for one matrix per limb. + * + * @note Original name: cKF_Si3_draw_SV_R_child + */ +void Keyframe_DrawStandardLimb(PlayState* play, KFSkelAnime* kfSkelAnime, s32* limbIndex, + OverrideKeyframeDraw overrideKeyframeDraw, PostKeyframeDraw postKeyframeDraw, void* arg, + Mtx** mtxStack) { + KeyFrameStandardLimb* limb = + *limbIndex + (KeyFrameStandardLimb*)Lib_SegmentedToVirtual(kfSkelAnime->skeleton->limbs); + s32 i; + Gfx* newDList; + Gfx* limbDList; + u8 drawFlags; + Vec3s rot; + Vec3s* jointData = &kfSkelAnime->jointTable[*limbIndex]; + Vec3f pos; + + if (*limbIndex != 0) { + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + } else { + pos.x = jointData->x; + pos.y = jointData->y; + pos.z = jointData->z; + } + + jointData++; + + rot.x = jointData->x; + rot.y = jointData->y; + rot.z = jointData->z; + + OPEN_DISPS(play->state.gfxCtx); + + Matrix_Push(); + + newDList = limbDList = limb->dList; + drawFlags = limb->drawFlags; + + if (overrideKeyframeDraw == NULL || + (overrideKeyframeDraw != NULL && + overrideKeyframeDraw(play, kfSkelAnime, *limbIndex, &newDList, &drawFlags, arg, &rot, &pos))) { + + Matrix_TranslateRotateZYX(&pos, &rot); + + if (newDList != NULL) { + Matrix_ToMtx(*mtxStack); + + if (drawFlags & KEYFRAME_DRAW_XLU) { + gSPMatrix(POLY_XLU_DISP++, *mtxStack, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, newDList); + } else { + gSPMatrix(POLY_OPA_DISP++, *mtxStack, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, newDList); + } + (*mtxStack)++; + } else if (limbDList != NULL) { + Matrix_ToMtx(*mtxStack); + + gSPMatrix(POLY_OPA_DISP++, *mtxStack, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + (*mtxStack)++; + } + } + + if (postKeyframeDraw != NULL) { + postKeyframeDraw(play, kfSkelAnime, *limbIndex, &newDList, &drawFlags, arg, &rot, &pos); + } + + (*limbIndex)++; + + for (i = 0; i < limb->numChildren; i++) { + Keyframe_DrawStandardLimb(play, kfSkelAnime, limbIndex, overrideKeyframeDraw, postKeyframeDraw, arg, mtxStack); + } + + Matrix_Pop(); + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * Draws a standard-type keyframe skeleton in its current pose. + * + * @param mtxStack Matrix stack for limb transformations. Should have enough room for one matrix per limb. + * @param overrideKeyframeDraw Callback for before submitting the limb to be drawn. The matrix state will not include + * the transformation for the current limb. + * @param postKeyframeDraw Callback for after submitting the limb to be drawn. The matrix state will include + * the transformation for the current limb. + * @param arg An arbitrary argument to pass to the callbacks. + * + * @note Original name: cKF_Si3_draw_R_SV + */ +void Keyframe_DrawStandard(PlayState* play, KFSkelAnime* kfSkelAnime, Mtx* mtxStack, + OverrideKeyframeDraw overrideKeyframeDraw, PostKeyframeDraw postKeyframeDraw, void* arg) { + s32 limbIndex; + + if (mtxStack == NULL) { + return; + } + + OPEN_DISPS(play->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x0D, mtxStack); + gSPSegment(POLY_XLU_DISP++, 0x0D, mtxStack); + + limbIndex = 0; + Keyframe_DrawStandardLimb(play, kfSkelAnime, &limbIndex, overrideKeyframeDraw, postKeyframeDraw, arg, &mtxStack); + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * Extracts the x,y,z scales for the limb `targetLimbIndex` for the current pose from a flex-type keyframe skeleton. + * + * The output scale values are quantized, that is they have been multiplied by 100 and rounded to an integer. To get + * the scale values in coordinate units, multiply the result by 0.01. + * + * @param targetLimbIndex The limb index for which to extract the scale for + * @param scale Vec3s of the x,y,z scale for the chosen limb + * + * @note Original name unknown + */ +void Keyframe_FlexGetScale(KFSkelAnimeFlex* kfSkelAnime, s32 targetLimbIndex, Vec3s* scale) { + s16* kfNums; + s32 i; + s32 kfn = 0; + s32 fixedValueIndex = 0; + s32 kfStart = 0; + s32 j; + u16* bitFlags; + s16* scaleArray = (s16*)scale; + s16* fixedValues; + KeyFrame* keyFrames; + s32 limbIndex; + + fixedValues = Lib_SegmentedToVirtual(kfSkelAnime->animation->fixedValues); + kfNums = Lib_SegmentedToVirtual(kfSkelAnime->animation->kfNums); + keyFrames = Lib_SegmentedToVirtual(kfSkelAnime->animation->keyFrames); + bitFlags = Lib_SegmentedToVirtual(kfSkelAnime->animation->bitFlags.flex); + + for (limbIndex = 0; limbIndex < kfSkelAnime->skeleton->limbCount; limbIndex++) { + u32 bit = 1 << (3 * 3 - 1); + + // 3 iter (scale, rotation, translation) + for (i = 0; i < 3; i++) { + if ((limbIndex == targetLimbIndex) && (i == 0)) { + // Is the target limb and is scale data, compute and write out scale values for each axis + // 3 iter (x, y, z) + for (j = 0; j < 3; j++) { + if (bitFlags[limbIndex] & bit) { + *scaleArray = Keyframe_KeyCalc(kfStart, kfNums[kfn], keyFrames, kfSkelAnime->frameCtrl.curTime); + kfStart += kfNums[kfn]; + kfn++; + } else { + *scaleArray = fixedValues[fixedValueIndex]; + fixedValueIndex++; + } + bit >>= 1; + scaleArray++; + } + } else { + // Not the target limb or scale data, step over values + // 3 iter (x, y, z) + for (j = 0; j < 3; j++) { + if (bitFlags[limbIndex] & bit) { + kfStart += kfNums[kfn]; + kfn++; + } else { + fixedValueIndex++; + } + bit >>= 1; + } + } + } + } +} diff --git a/src/code/z_collision_check.c b/src/code/z_collision_check.c index 11c3865e1..5b25e29b3 100644 --- a/src/code/z_collision_check.c +++ b/src/code/z_collision_check.c @@ -1,3 +1,4 @@ +#include "prevent_bss_reordering.h" #include "z64collision_check.h" #include "z64actor.h" diff --git a/src/code/z_demo.c b/src/code/z_demo.c index 7527cc3a1..d7d0d1c33 100644 --- a/src/code/z_demo.c +++ b/src/code/z_demo.c @@ -14,6 +14,28 @@ static s16 sBssPad; u8 gDisablePlayerCsActionStartPos; s16 gDungeonBossWarpSceneId; +// clang-format off +// Partial structs taken from "prevent_bss_reordering.h" +struct Dummy200 { int x; }; +struct Dummy201 { int x; }; +struct Dummy202 { int x; }; +struct Dummy203 { int x; }; +struct Dummy204 { int x; }; +struct Dummy205 { int x; }; +struct Dummy206 { int x; }; +struct Dummy207 { int x; }; +struct Dummy208 { int x; }; +struct Dummy209 { int x; }; +struct Dummy210 { int x; }; +struct Dummy211 { int x; }; +struct Dummy212 { int x; }; +struct Dummy213 { int x; }; +struct Dummy214 { int x; }; +struct Dummy215 { int x; }; +struct Dummy216 { int x; }; +struct Dummy217 { int x; }; +// clang-format on + #include "z64quake.h" #include "z64rumble.h" #include "z64shrink_window.h" diff --git a/src/overlays/actors/ovl_Boss_03/z_boss_03.c b/src/overlays/actors/ovl_Boss_03/z_boss_03.c index fde9797bf..9dc32de4e 100644 --- a/src/overlays/actors/ovl_Boss_03/z_boss_03.c +++ b/src/overlays/actors/ovl_Boss_03/z_boss_03.c @@ -49,7 +49,6 @@ * - Seaweed */ -#include "prevent_bss_reordering.h" #include "z_boss_03.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_En_Water_Effect/z_en_water_effect.h" diff --git a/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.c b/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.c index b40707f64..91d6c2377 100644 --- a/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.c +++ b/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.c @@ -46,14 +46,14 @@ void DemoMoonend_Init(Actor* thisx, PlayState* play) { this->actionFunc = func_80C17B60; } else { Actor_SetScale(&this->actor, 0.095f); - func_80183430(&this->skeletonInfo, &object_moonend_Blob_00B5A0, &object_moonend_Blob_001214, this->jointTable, - this->morphTable, NULL); - func_801834A8(&this->skeletonInfo, &object_moonend_Blob_001214); + Keyframe_InitFlex(&this->kfSkelAnime, (KeyFrameFlexSkeleton*)&object_moonend_Blob_00B5A0, + (KeyFrameAnimation*)&object_moonend_Blob_001214, this->jointTable, this->morphTable, NULL); + Keyframe_FlexPlayOnce(&this->kfSkelAnime, (KeyFrameAnimation*)&object_moonend_Blob_001214); this->cueType = CS_CMD_ACTOR_CUE_560; this->actionFunc = func_80C17C48; this->actor.home.rot.z = 0; this->actor.draw = NULL; - this->skeletonInfo.frameCtrl.unk_C = 2.0f / 3.0f; + this->kfSkelAnime.frameCtrl.speed = 2.0f / 3.0f; } } @@ -61,7 +61,7 @@ void DemoMoonend_Destroy(Actor* thisx, PlayState* play) { DemoMoonend* this = THIS; if (DEMOMOONEND_GET_PARAM_F(thisx) != 1) { - func_8018349C(&this->skeletonInfo); + Keyframe_DestroyFlex(&this->kfSkelAnime); } } @@ -97,7 +97,7 @@ void func_80C17B60(DemoMoonend* this, PlayState* play) { } void func_80C17C48(DemoMoonend* this, PlayState* play) { - if (func_80183DE0(&this->skeletonInfo)) { + if (Keyframe_UpdateFlex(&this->kfSkelAnime)) { this->actor.home.rot.z = 0; } if (Cutscene_IsCueInChannel(play, this->cueType)) { @@ -110,14 +110,14 @@ void func_80C17C48(DemoMoonend* this, PlayState* play) { switch (this->cueId) { case 1: this->actor.draw = DemoMoonend_Draw; - func_801834A8(&this->skeletonInfo, &object_moonend_Blob_001214); - this->skeletonInfo.frameCtrl.unk_C = 0.0f; + Keyframe_FlexPlayOnce(&this->kfSkelAnime, (KeyFrameAnimation*)&object_moonend_Blob_001214); + this->kfSkelAnime.frameCtrl.speed = 0.0f; break; case 2: this->actor.draw = DemoMoonend_Draw; - func_801834A8(&this->skeletonInfo, &object_moonend_Blob_001214); - this->skeletonInfo.frameCtrl.unk_C = 2.0f / 3.0f; + Keyframe_FlexPlayOnce(&this->kfSkelAnime, (KeyFrameAnimation*)&object_moonend_Blob_001214); + this->kfSkelAnime.frameCtrl.speed = 2.0f / 3.0f; Actor_PlaySfx(&this->actor, NA_SE_EV_MOON_EXPLOSION); this->actor.home.rot.z = 1; break; @@ -166,8 +166,8 @@ void DemoMoonend_Update(Actor* thisx, PlayState* play) { this->actionFunc(this, play); } -s32 func_80C17E70(PlayState* play, SkeletonInfo* skeletonInfo, s32 limbIndex, Gfx** dList, u8* flags, Actor* thisx, - Vec3f* scale, Vec3s* rot, Vec3f* pos) { +s32 DemoMoonend_OverrideLimbDraw(PlayState* play, KFSkelAnimeFlex* kfSkelAnime, s32 limbIndex, Gfx** dList, u8* flags, + void* thisx, Vec3f* scale, Vec3s* rot, Vec3f* pos) { DemoMoonend* this = THIS; if (limbIndex == 2) { @@ -178,8 +178,8 @@ s32 func_80C17E70(PlayState* play, SkeletonInfo* skeletonInfo, s32 limbIndex, Gf return true; } -s32 func_80C17EE0(PlayState* play, SkeletonInfo* skeleton, s32 limbIndex, Gfx** dList, u8* flags, Actor* thisx, - Vec3f* scale, Vec3s* rot, Vec3f* pos) { +s32 DemoMoonend_PostLimbDraw(PlayState* play, KFSkelAnimeFlex* kfSkelAnime, s32 limbIndex, Gfx** dList, u8* flags, + void* thisx, Vec3f* scale, Vec3s* rot, Vec3f* pos) { DemoMoonend* this = THIS; if (limbIndex == 8) { @@ -191,16 +191,17 @@ s32 func_80C17EE0(PlayState* play, SkeletonInfo* skeleton, s32 limbIndex, Gfx** void DemoMoonend_Draw(Actor* thisx, PlayState* play) { DemoMoonend* this = THIS; - Mtx* mtx; + Mtx* mtxStack; AnimatedMat_Draw(play, (AnimatedMaterial*)Lib_SegmentedToVirtual(object_moonend_Matanimheader_00B540)); - mtx = GRAPH_ALLOC(play->state.gfxCtx, this->skeletonInfo.unk_18->unk_1 * sizeof(Mtx)); + mtxStack = GRAPH_ALLOC(play->state.gfxCtx, this->kfSkelAnime.skeleton->dListCount * sizeof(Mtx)); - if (mtx != NULL) { + if (mtxStack != NULL) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); Gfx_SetupDL25_Opa(play->state.gfxCtx); - func_8018450C(play, &this->skeletonInfo, mtx, func_80C17E70, func_80C17EE0, &this->actor); + Keyframe_DrawFlex(play, &this->kfSkelAnime, mtxStack, DemoMoonend_OverrideLimbDraw, DemoMoonend_PostLimbDraw, + &this->actor); } } diff --git a/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.h b/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.h index b56c60a9b..692b0b84a 100644 --- a/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.h +++ b/src/overlays/actors/ovl_Demo_Moonend/z_demo_moonend.h @@ -12,7 +12,7 @@ typedef void (*DemoMoonendActionFunc)(struct DemoMoonend*, PlayState*); typedef struct DemoMoonend { /* 0x000 */ Actor actor; - /* 0x144 */ SkeletonInfo skeletonInfo; + /* 0x144 */ KFSkelAnimeFlex kfSkelAnime; /* 0x174 */ Vec3s jointTable[30]; /* 0x228 */ Vec3s morphTable[30]; /* 0x2DC */ u16 cueType; diff --git a/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.c b/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.c index 6ffd93ace..3780c075c 100644 --- a/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.c +++ b/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.c @@ -73,9 +73,9 @@ void DemoSyoten_Init(Actor* thisx, PlayState* play) { switch (DEMOSYOTEN_GET_F(&this->actor)) { case DEMOSYOTEN_F_0: - func_80183430(&this->unk_144, &object_syoten_Blob_001328, &object_syoten_Blob_00023C, this->unk_174, - this->unk_2A6, NULL); - func_801835EC(&this->unk_144, &object_syoten_Blob_00023C); + Keyframe_InitFlex(&this->kfSkelAnime, (KeyFrameFlexSkeleton*)&object_syoten_Blob_001328, + (KeyFrameAnimation*)&object_syoten_Blob_00023C, this->jointTable, this->morphTable, NULL); + Keyframe_FlexPlayLoop(&this->kfSkelAnime, (KeyFrameAnimation*)&object_syoten_Blob_00023C); this->actor.draw = NULL; this->actionFunc = func_80C16A74; this->actor.child = @@ -131,7 +131,7 @@ void DemoSyoten_Destroy(Actor* thisx, PlayState* play) { DemoSyoten* this = THIS; if (DEMOSYOTEN_GET_F(&this->actor) == DEMOSYOTEN_F_0) { - func_8018349C(&this->unk_144); + Keyframe_DestroyFlex(&this->kfSkelAnime); } } @@ -221,7 +221,7 @@ void func_80C16A64(DemoSyoten* this, PlayState* play) { void func_80C16A74(DemoSyoten* this, PlayState* play) { u16 cueId; - func_80183DE0(&this->unk_144); + Keyframe_UpdateFlex(&this->kfSkelAnime); if (Cutscene_IsCueInChannel(play, this->cueType)) { if ((play->csCtx.curFrame >= 160) && (play->csCtx.curFrame < 322)) { Actor_PlaySfx_Flagged(&this->actor, NA_SE_EV_IKANA_SOUL_LV - SFX_FLAG); @@ -436,13 +436,14 @@ void DemoSyoten_Update(Actor* thisx, PlayState* play) { this->actionFunc(this, play); } -s32 func_80C170F8(PlayState* play, UNK_TYPE arg1, s32 arg2, UNK_TYPE arg3, UNK_TYPE arg4, Actor* thisx) { +s32 DemoSyoten_OverrideLimbDraw(PlayState* play, KFSkelAnimeFlex* kfSkelAnime, s32 limbIndex, Gfx** dList, u8* flags, + void* thisx, Vec3f* scale, Vec3s* rot, Vec3f* pos) { GraphicsContext* gfxCtx = play->state.gfxCtx; DemoSyoten* this = THIS; OPEN_DISPS(gfxCtx); - switch (arg2) { + switch (limbIndex) { case 2: gDPPipeSync(POLY_XLU_DISP++); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 240, 140, (s32)(this->unk_3D8 * 150.0f)); @@ -483,16 +484,16 @@ s32 func_80C170F8(PlayState* play, UNK_TYPE arg1, s32 arg2, UNK_TYPE arg3, UNK_T void func_80C173B4(Actor* thisx, PlayState* play) { s32 pad; DemoSyoten* this = THIS; - Mtx* mtx; + Mtx* mtxStack; AnimatedMat_DrawXlu(play, Lib_SegmentedToVirtual(&object_syoten_Matanimheader_001298)); - mtx = GRAPH_ALLOC(play->state.gfxCtx, this->unk_144.unk_18->unk_1 * sizeof(Mtx)); + mtxStack = GRAPH_ALLOC(play->state.gfxCtx, this->kfSkelAnime.skeleton->dListCount * sizeof(Mtx)); - if (mtx != NULL) { + if (mtxStack != NULL) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); Matrix_Mult(&play->billboardMtxF, MTXMODE_APPLY); - func_8018450C(play, &this->unk_144, mtx, (void*)func_80C170F8, 0, &this->actor); + Keyframe_DrawFlex(play, &this->kfSkelAnime, mtxStack, DemoSyoten_OverrideLimbDraw, NULL, &this->actor); } } diff --git a/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.h b/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.h index d5caa1920..4c0e3d303 100644 --- a/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.h +++ b/src/overlays/actors/ovl_Demo_Syoten/z_demo_syoten.h @@ -22,9 +22,9 @@ typedef enum { typedef struct DemoSyoten { /* 0x000 */ Actor actor; - /* 0x144 */ SkeletonInfo unk_144; - /* 0x174 */ Vec3s unk_174[51]; - /* 0x2A6 */ Vec3s unk_2A6[51]; + /* 0x144 */ KFSkelAnimeFlex kfSkelAnime; + /* 0x174 */ Vec3s jointTable[51]; + /* 0x2A6 */ Vec3s morphTable[51]; /* 0x3D8 */ f32 unk_3D8; /* 0x3DC */ Gfx* unk_3DC; /* 0x3E0 */ AnimatedMaterial* unk_3E0; diff --git a/src/overlays/actors/ovl_Eff_Change/z_eff_change.c b/src/overlays/actors/ovl_Eff_Change/z_eff_change.c index 9839d29d5..24a079632 100644 --- a/src/overlays/actors/ovl_Eff_Change/z_eff_change.c +++ b/src/overlays/actors/ovl_Eff_Change/z_eff_change.c @@ -50,19 +50,19 @@ void EffChange_Init(Actor* thisx, PlayState* play) { EffChange_SetColors(this, EFFCHANGE_GET_COLORS(thisx)); Actor_SetScale(&this->actor, 0.075f); this->primColors[3] = 0; - func_80183430(&this->skeletonInfo, gameplay_keep_Blob_02900C, gameplay_keep_Blob_0281DC, this->jointTable, - this->morphTable, NULL); - func_801834A8(&this->skeletonInfo, gameplay_keep_Blob_0281DC); + Keyframe_InitFlex(&this->kfSkelAnime, (KeyFrameFlexSkeleton*)&gameplay_keep_Blob_02900C, + (KeyFrameAnimation*)&gameplay_keep_Blob_0281DC, this->jointTable, this->morphTable, NULL); + Keyframe_FlexPlayOnce(&this->kfSkelAnime, (KeyFrameAnimation*)&gameplay_keep_Blob_0281DC); this->step = 0; this->actor.shape.rot.y = 0; - this->skeletonInfo.frameCtrl.unk_C = (2.0f / 3.0f); + this->kfSkelAnime.frameCtrl.speed = 2.0f / 3.0f; CutsceneManager_Queue(CS_ID_GLOBAL_ELEGY); } void EffChange_Destroy(Actor* thisx, PlayState* play) { EffChange* this = THIS; - func_8018349C(&this->skeletonInfo); + Keyframe_DestroyFlex(&this->kfSkelAnime); } void EffChange_SetColors(EffChange* this, s32 arg1) { @@ -78,7 +78,7 @@ void EffChange_SetColors(EffChange* this, s32 arg1) { void func_80A4C5CC(EffChange* this, PlayState* play) { f32 phi_fv0; - if (func_80183DE0(&this->skeletonInfo)) { + if (Keyframe_UpdateFlex(&this->kfSkelAnime)) { Actor_Kill(&this->actor); CutsceneManager_Stop(CS_ID_GLOBAL_ELEGY); Environment_AdjustLights(play, 0.0f, 850.0f, 0.2f, 0.0f); @@ -86,13 +86,13 @@ void func_80A4C5CC(EffChange* this, PlayState* play) { } this->step++; - if (this->skeletonInfo.frameCtrl.unk_10 < 20.0f) { + if (this->kfSkelAnime.frameCtrl.curTime < 20.0f) { if ((this->primColors[3]) < 242) { this->primColors[3] += 13; } else { this->primColors[3] = 255; } - } else if (this->skeletonInfo.frameCtrl.unk_10 > 70.0f) { + } else if (this->kfSkelAnime.frameCtrl.curTime > 70.0f) { if ((this->primColors[3]) >= 14) { this->primColors[3] -= 13; } else { @@ -126,13 +126,13 @@ void EffChange_Update(Actor* thisx, PlayState* play) { void EffChange_Draw(Actor* thisx, PlayState* play) { s32 pad; - Mtx* mtx; + Mtx* mtxStack; EffChange* this = THIS; AnimatedMat_DrawStepXlu(play, Lib_SegmentedToVirtual(&gameplay_keep_Matanimheader_028FEC), this->step); - mtx = GRAPH_ALLOC(play->state.gfxCtx, this->skeletonInfo.unk_18->unk_1 * sizeof(Mtx)); + mtxStack = GRAPH_ALLOC(play->state.gfxCtx, this->kfSkelAnime.skeleton->dListCount * sizeof(Mtx)); - if (mtx != NULL) { + if (mtxStack != NULL) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); Matrix_RotateYS((Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000), MTXMODE_APPLY); @@ -141,7 +141,7 @@ void EffChange_Draw(Actor* thisx, PlayState* play) { gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, this->primColors[0], this->primColors[1], this->primColors[2], this->primColors[3]); gDPSetEnvColor(POLY_XLU_DISP++, this->envColors[0], this->envColors[1], this->envColors[2], 255); - func_8018450C(play, &this->skeletonInfo, mtx, NULL, NULL, &this->actor); + Keyframe_DrawFlex(play, &this->kfSkelAnime, mtxStack, NULL, NULL, &this->actor); CLOSE_DISPS(play->state.gfxCtx); } diff --git a/src/overlays/actors/ovl_Eff_Change/z_eff_change.h b/src/overlays/actors/ovl_Eff_Change/z_eff_change.h index 194ae3ee2..c109519ae 100644 --- a/src/overlays/actors/ovl_Eff_Change/z_eff_change.h +++ b/src/overlays/actors/ovl_Eff_Change/z_eff_change.h @@ -12,9 +12,9 @@ typedef void (*EffChangeActionFunc)(struct EffChange*, PlayState*); typedef struct EffChange { /* 0x000 */ Actor actor; - /* 0x144 */ SkeletonInfo skeletonInfo; - /* 0x174 */ Vec3s jointTable[PINK_DEKU_FLOWER_LIMB_BACK_RIGHT_PETAL]; - /* 0x198 */ Vec3s morphTable[PINK_DEKU_FLOWER_LIMB_BACK_RIGHT_PETAL]; + /* 0x144 */ KFSkelAnimeFlex kfSkelAnime; + /* 0x174 */ Vec3s jointTable[6]; + /* 0x198 */ Vec3s morphTable[6]; /* 0x1BC */ s16 step; /* 0x1BE */ u8 primColors[4]; /* 0x1C2 */ u8 envColors[4]; diff --git a/src/overlays/actors/ovl_En_Fall2/z_en_fall2.c b/src/overlays/actors/ovl_En_Fall2/z_en_fall2.c index 68f9d4dab..b1a67d9e0 100644 --- a/src/overlays/actors/ovl_En_Fall2/z_en_fall2.c +++ b/src/overlays/actors/ovl_En_Fall2/z_en_fall2.c @@ -35,9 +35,9 @@ void EnFall2_Init(Actor* thisx, PlayState* play) { Actor_SetScale(&this->actor, 1.0f); this->actionFunc = EnFall2_DoNothing; - func_80183430(&this->skeletonInfo, object_fall2_Blob_008898, object_fall2_Blob_005EF4, this->unk174, this->unk228, - NULL); - func_801835EC(&this->skeletonInfo, object_fall2_Blob_005EF4); + Keyframe_InitFlex(&this->kfSkelAnime, (KeyFrameFlexSkeleton*)&object_fall2_Blob_008898, + (KeyFrameAnimation*)&object_fall2_Blob_005EF4, this->jointTable, this->morphTable, NULL); + Keyframe_FlexPlayLoop(&this->kfSkelAnime, (KeyFrameAnimation*)&object_fall2_Blob_005EF4); this->unk2DC = Lib_SegmentedToVirtual(object_fall2_Matanimheader_008840); Actor_SetScale(&this->actor, 0.02f); this->actionFunc = EnFall2_HandleCutscene; @@ -48,7 +48,7 @@ void EnFall2_Init(Actor* thisx, PlayState* play) { void EnFall2_Destroy(Actor* thisx, PlayState* play) { EnFall2* this = THIS; - func_8018349C(&this->skeletonInfo); + Keyframe_DestroyFlex(&this->kfSkelAnime); } static u8 sAlphaTableIndices[] = { @@ -125,7 +125,7 @@ void func_80C1B8F0(EnFall2* this) { } void EnFall2_HandleCutscene(EnFall2* this, PlayState* play) { - func_80183DE0(&this->skeletonInfo); + Keyframe_UpdateFlex(&this->kfSkelAnime); if (Cutscene_IsCueInChannel(play, this->cueType)) { Cutscene_ActorTranslateAndYaw(&this->actor, play, Cutscene_GetCueChannel(play, this->cueType)); if (this->cueId != play->csCtx.actorCues[Cutscene_GetCueChannel(play, this->cueType)]->id) { @@ -151,18 +151,18 @@ void EnFall2_Update(Actor* thisx, PlayState* play) { void EnFall2_Draw(Actor* thisx, PlayState* play) { s32 pad; EnFall2* this = THIS; - Mtx* mtx; + Mtx* mtxStack; if (!(this->alphaLevel <= 0.0f)) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); AnimatedMat_DrawXlu(play, Lib_SegmentedToVirtual(object_fall2_Matanimheader_008840)); - mtx = GRAPH_ALLOC(play->state.gfxCtx, this->skeletonInfo.unk_18->unk_1 * sizeof(Mtx)); + mtxStack = GRAPH_ALLOC(play->state.gfxCtx, this->kfSkelAnime.skeleton->dListCount * sizeof(Mtx)); - if (mtx != NULL) { + if (mtxStack != NULL) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); Matrix_RotateYS((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000), MTXMODE_APPLY); - func_8018450C(play, &this->skeletonInfo, mtx, NULL, NULL, &this->actor); + Keyframe_DrawFlex(play, &this->kfSkelAnime, mtxStack, NULL, NULL, &this->actor); } } } diff --git a/src/overlays/actors/ovl_En_Fall2/z_en_fall2.h b/src/overlays/actors/ovl_En_Fall2/z_en_fall2.h index 3555595f3..1906f81d3 100644 --- a/src/overlays/actors/ovl_En_Fall2/z_en_fall2.h +++ b/src/overlays/actors/ovl_En_Fall2/z_en_fall2.h @@ -10,9 +10,9 @@ typedef void (*EnFall2ActionFunc)(struct EnFall2*, PlayState*); typedef struct EnFall2 { /* 0x000 */ Actor actor; - /* 0x144 */ SkeletonInfo skeletonInfo; - /* 0x174 */ Vec3s unk174[30]; - /* 0x228 */ Vec3s unk228[30]; + /* 0x144 */ KFSkelAnimeFlex kfSkelAnime; + /* 0x174 */ Vec3s jointTable[30]; + /* 0x228 */ Vec3s morphTable[30]; /* 0x2DC */ AnimatedMaterial* unk2DC; /* 0x2E0 */ f32 alphaLevel; /* 0x2E4 */ s16 cueId; diff --git a/src/overlays/actors/ovl_En_Test/z_en_test.c b/src/overlays/actors/ovl_En_Test/z_en_test.c index 12aba1abb..6cff068b3 100644 --- a/src/overlays/actors/ovl_En_Test/z_en_test.c +++ b/src/overlays/actors/ovl_En_Test/z_en_test.c @@ -186,26 +186,26 @@ void EnTest_Init(Actor* thisx, PlayState* play2) { this->surfaceMaterial = SurfaceType_GetMaterial(&play->colCtx, thisx->floorPoly, bgId); } - func_80183430(&this->skeletonInfo, &gameplay_keep_Blob_06EB70, &gameplay_keep_Blob_06BB0C, this->unk_178, - this->unk_1C0, NULL); - func_801834A8(&this->skeletonInfo, &gameplay_keep_Blob_06BB0C); - this->skeletonInfo.frameCtrl.unk_10 = 9.0f; + Keyframe_InitFlex(&this->kfSkelAnime, (KeyFrameFlexSkeleton*)&gameplay_keep_Blob_06EB70, + (KeyFrameAnimation*)&gameplay_keep_Blob_06BB0C, this->jointTable, this->morphTable, NULL); + Keyframe_FlexPlayOnce(&this->kfSkelAnime, (KeyFrameAnimation*)&gameplay_keep_Blob_06BB0C); + this->kfSkelAnime.frameCtrl.curTime = 9.0f; func_80862B70(this->unk_20C); } void EnTest_Destroy(Actor* thisx, PlayState* play) { EnTest* this = THIS; - func_8018349C(&this->skeletonInfo); + Keyframe_DestroyFlex(&this->kfSkelAnime); } void EnTest_Update(Actor* thisx, PlayState* play) { EnTest* this = THIS; s32 i; - this->unk_208 = this->skeletonInfo.frameCtrl.unk_10; + this->unk_208 = this->kfSkelAnime.frameCtrl.curTime; - if (func_80183DE0(&this->skeletonInfo) && (this->actor.parent == NULL) && (this->actor.params != -1)) { + if (Keyframe_UpdateFlex(&this->kfSkelAnime) && (this->actor.parent == NULL) && (this->actor.params != -1)) { this->unk_209++; if (this->unk_209 > 20) { Actor_Kill(&this->actor); @@ -224,8 +224,8 @@ void EnTest_Update(Actor* thisx, PlayState* play) { func_80862EDC(this->unk_20C); } -s32 EnTest_OverrideKeyframeDraw(PlayState* play, SkeletonInfo* skeletonInfo, s32 limbIndex, Gfx** dList, u8* flags, - Actor* thisx, Vec3f* scale, Vec3s* rot, Vec3f* pos) { +s32 EnTest_OverrideLimbDraw(PlayState* play, KFSkelAnimeFlex* kfSkelAnime, s32 limbIndex, Gfx** dList, u8* flags, + void* thisx, Vec3f* scale, Vec3s* rot, Vec3f* pos) { EnTest* this = THIS; OPEN_DISPS(play->state.gfxCtx); @@ -250,7 +250,7 @@ s32 EnTest_OverrideKeyframeDraw(PlayState* play, SkeletonInfo* skeletonInfo, s32 void EnTest_Draw(Actor* thisx, PlayState* play) { EnTest* this = THIS; - Mtx* mtx; + Mtx* mtxStack; s32 sp2C = this->unk_208 - 1; if (sp2C >= 29) { @@ -264,11 +264,11 @@ void EnTest_Draw(Actor* thisx, PlayState* play) { AnimatedMat_DrawStep(play, Lib_SegmentedToVirtual(gameplay_keep_Matanimheader_06B6A0), sp2C); } - mtx = GRAPH_ALLOC(play->state.gfxCtx, this->skeletonInfo.unk_18->unk_1 * sizeof(Mtx)); + mtxStack = GRAPH_ALLOC(play->state.gfxCtx, this->kfSkelAnime.skeleton->dListCount * sizeof(Mtx)); - if (mtx != NULL) { + if (mtxStack != NULL) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); - func_8018450C(play, &this->skeletonInfo, mtx, EnTest_OverrideKeyframeDraw, NULL, thisx); + Keyframe_DrawFlex(play, &this->kfSkelAnime, mtxStack, EnTest_OverrideLimbDraw, NULL, thisx); func_80863048(play, this->unk_20C); } } diff --git a/src/overlays/actors/ovl_En_Test/z_en_test.h b/src/overlays/actors/ovl_En_Test/z_en_test.h index 68395a96a..de622d446 100644 --- a/src/overlays/actors/ovl_En_Test/z_en_test.h +++ b/src/overlays/actors/ovl_En_Test/z_en_test.h @@ -18,10 +18,10 @@ typedef struct { typedef struct EnTest { /* 0x000 */ Actor actor; - /* 0x144 */ SkeletonInfo skeletonInfo; + /* 0x144 */ KFSkelAnimeFlex kfSkelAnime; /* 0x174 */ s32 unk_174; - /* 0x178 */ Vec3s unk_178[12]; - /* 0x1C0 */ Vec3s unk_1C0[12]; + /* 0x178 */ Vec3s jointTable[12]; + /* 0x1C0 */ Vec3s morphTable[12]; /* 0x208 */ u8 unk_208; /* 0x209 */ u8 unk_209; /* 0x20A */ u8 surfaceMaterial; diff --git a/src/overlays/actors/ovl_En_Test7/z_en_test7.c b/src/overlays/actors/ovl_En_Test7/z_en_test7.c index 690e1a576..0853fe117 100644 --- a/src/overlays/actors/ovl_En_Test7/z_en_test7.c +++ b/src/overlays/actors/ovl_En_Test7/z_en_test7.c @@ -383,9 +383,9 @@ void EnTest7_Init(Actor* thisx, PlayState* play2) { this->playerScaleZ = player->actor.scale.z; // Keyframe animations - func_80183430(&this->skeletonInfo, &gameplay_keep_Blob_085640, &gameplay_keep_Blob_083534, this->unk_18FC, - this->unk_1BA8, NULL); - func_801834A8(&this->skeletonInfo, &gameplay_keep_Blob_083534); + Keyframe_InitFlex(&this->kfSkelAnime, (KeyFrameFlexSkeleton*)&gameplay_keep_Blob_085640, + (KeyFrameAnimation*)&gameplay_keep_Blob_083534, this->jointTable, this->morphTable, NULL); + Keyframe_FlexPlayOnce(&this->kfSkelAnime, (KeyFrameAnimation*)&gameplay_keep_Blob_083534); EnTest7_InitFeathers(this->feathers); EnTest7_InitWindCapsule(&this->windCapsule); @@ -482,20 +482,20 @@ void EnTest7_UpdateGrowingWindCapsule(EnTest7* this, PlayState* play) { void EnTest7_WarpCsPart2(EnTest7* this, PlayState* play) { Vec3f featherPos; - if (func_80183DE0(&this->skeletonInfo)) { + if (Keyframe_UpdateFlex(&this->kfSkelAnime)) { EnTest7_SetupAction(this, EnTest7_WarpCsPart3); } - if (this->skeletonInfo.frameCtrl.unk_10 > 60.0f) { + if (this->kfSkelAnime.frameCtrl.curTime > 60.0f) { EnTest7_UpdateGrowingWindCapsule(this, play); } - if ((this->skeletonInfo.frameCtrl.unk_10 > 20.0f) && !(this->flags & OWL_WARP_FLAGS_40)) { + if ((this->kfSkelAnime.frameCtrl.curTime > 20.0f) && !(this->flags & OWL_WARP_FLAGS_40)) { this->flags |= OWL_WARP_FLAGS_40; Audio_PlaySfx_AtPos(&this->actor.projectedPos, NA_SE_PL_WARP_WING_CLOSE); } - if (this->skeletonInfo.frameCtrl.unk_10 > 42.0f) { + if (this->kfSkelAnime.frameCtrl.curTime > 42.0f) { if (!(this->flags & OWL_WARP_FLAGS_80)) { this->flags |= OWL_WARP_FLAGS_80; Audio_PlaySfx_AtPos(&this->actor.projectedPos, NA_SE_PL_WARP_WING_ROLL); @@ -538,7 +538,7 @@ void EnTest7_WarpCsPart3(EnTest7* this, PlayState* play) { Math_Vec3f_Copy(&featherPos, &this->actor.world.pos); EnTest7_AddAndChooseFeather(this->feathers, &featherPos, true); - this->unk_18FC[1].y += 0x2EE0; + this->jointTable[1].y += 0x2EE0; } void EnTest7_WarpCsPart4(EnTest7* this, PlayState* play) { @@ -953,8 +953,8 @@ void EnTest7_Update(Actor* thisx, PlayState* play) { (this->flags & OWL_WARP_FLAGS_10) != 0); } -s32 func_80AF31D0(PlayState* play, SkeletonInfo* skeletonInfo, s32 limbIndex, Gfx** dList, u8* flags, Actor* thisx, - Vec3f* scale, Vec3s* rot, Vec3f* pos) { +s32 EnTest7_OverrideLimbDraw(PlayState* play, KFSkelAnimeFlex* kfSkelAnime, s32 limbIndex, Gfx** dList, u8* flags, + void* thisx, Vec3f* scale, Vec3s* rot, Vec3f* pos) { EnTest7* this = THIS; Vec3f featherPos; @@ -972,12 +972,12 @@ void EnTest7_Draw(Actor* thisx, PlayState* play) { // Draw wings if (this->flags & OWL_WARP_FLAGS_DRAW_WINGS) { - Mtx* mtx = GRAPH_ALLOC(play->state.gfxCtx, this->skeletonInfo.unk_18->unk_1 * sizeof(Mtx)); + Mtx* mtxStack = GRAPH_ALLOC(play->state.gfxCtx, this->kfSkelAnime.skeleton->dListCount * sizeof(Mtx)); - if (mtx == NULL) { + if (mtxStack == NULL) { return; } - func_8018450C(play, &this->skeletonInfo, mtx, func_80AF31D0, NULL, &this->actor); + Keyframe_DrawFlex(play, &this->kfSkelAnime, mtxStack, EnTest7_OverrideLimbDraw, NULL, &this->actor); } // Draw windCapsule encasing that surrounds player after wings diff --git a/src/overlays/actors/ovl_En_Test7/z_en_test7.h b/src/overlays/actors/ovl_En_Test7/z_en_test7.h index 87c26a356..6f7dd7436 100644 --- a/src/overlays/actors/ovl_En_Test7/z_en_test7.h +++ b/src/overlays/actors/ovl_En_Test7/z_en_test7.h @@ -53,9 +53,9 @@ typedef struct EnTest7 { /* 0x0144 */ s32 flags; /* 0x0148 */ OwlWarpWindCapsule windCapsule; /* 0x015C */ OwlWarpFeather feathers[OWL_WARP_NUM_FEATHERS]; - /* 0x18CC */ SkeletonInfo skeletonInfo; // wingsSkeletonInfo - /* 0x18FC */ Vec3s unk_18FC[114]; // wingsFrameData - /* 0x1BA8 */ Vec3s unk_1BA8[114]; + /* 0x18CC */ KFSkelAnimeFlex kfSkelAnime; + /* 0x18FC */ Vec3s jointTable[114]; + /* 0x1BA8 */ Vec3s morphTable[114]; /* 0x1E54 */ s32 timer; /* 0x1E58 */ EnTest7ActionFunc actionFunc; /* 0x1E5C */ EnTest7PlayerCamFunc playerCamFunc; diff --git a/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c b/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c index 3218c3050..4e593f30e 100644 --- a/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c +++ b/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c @@ -23,7 +23,7 @@ * Front * */ - +#include "prevent_bss_reordering.h" #include "z_obj_takaraya_wall.h" #include "objects/object_takaraya_objects/object_takaraya_objects.h" |
