summaryrefslogtreecommitdiff
path: root/src/code/c_keyframe.c
diff options
context:
space:
mode:
authoremilybrooks <emilybrooksemilybrooks@gmail.com>2023-08-05 18:51:56 -0700
committerGitHub <noreply@github.com>2023-08-05 21:51:56 -0400
commit44f0a79323a08c107df4217c76e0563f55872a81 (patch)
treed933700160eb7e5a76118203dc6bd99569d481e0 /src/code/c_keyframe.c
parent94b300b0d0e228dc38dcb08ca160334fa2ba128f (diff)
c_keyframe OK (#25)
* matched a few functions * found some function names from the dnm+ map * floating point hell and struct hell * investigating skeletoninfo * structs * g3 * matching progress * cKF_SkeletonInfo_R_AnimationMove_base * lots of matched functions * matched 2 functions * keycalc * rodata problem fixed * cKF_SkeletonInfo_R_morphJoint matched * cKF_Si3_draw_R_SV fake match * cKF_SkeletonInfo_R_combine_translation matched * cKF_SkeletonInfo_R_combine_rotation matched * func_80053384_jp matched * cKF_SkeletonInfo_R_AnimationMove_ct_base matched * started documenting * naming some stuff * wrong struct size * moved stuff to header files * documenting * more documenting * comments for FrameControl done * cKF_Si3_draw_SV_R_child fake match * cleaning up draw functions * imported bss * matched cKF_SkeletonInfo_R_combine_play * matched cKF_SkeletonInfo_R_T_combine_play * matched cKF_SkeletonInfo_R_play * everything matching * cleaning up cKF_SkeletonInfo_R_play * started on morph stuff * cleaning up draw functions * cleaning up structs * trying to make pr ready * changed wording * review feedback * forgot some animation pointer temps * reverted cKF_SkeletonInfo_R_init_reverse_setspeedandmorphandmode * review2 * fixed draw function fake matches
Diffstat (limited to 'src/code/c_keyframe.c')
-rw-r--r--src/code/c_keyframe.c1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/src/code/c_keyframe.c b/src/code/c_keyframe.c
new file mode 100644
index 0000000..31a698f
--- /dev/null
+++ b/src/code/c_keyframe.c
@@ -0,0 +1,1227 @@
+/**
+ * @file c_keyframe.c
+ *
+ * Skeletal animation with keyframes.
+ *
+ * This system was used in Majora's Mask, however Doubutsu no Mori uses the "R" variants of structs, which differ
+ * slightly.
+ *
+ * Animation is broken down into multiple parts that work together.
+ *
+ * FrameControl: A struct responsible for keeping track of the time component of animations. Animations use frames
+ * (one thirtieth of a second) as a unit of time.
+ *
+ * Joint: A display list that represents a small section of the overall model. Joints have a hierarchal structure, where
+ * joints can specify child joints, and each joint inherits it's parent's transformation. Joints are represented in two
+ * places. A JointElemR struct stores the joint's display list. There's also a jointTable gets passed in from an actor.
+ * This is a Vec3s array, with the first vector storing the root translation, the second vector is the root rotation,
+ * and each vector after that is a joint's rotation.
+ *
+ * Morph: Normally switching from the previous animation to a new animation produces jarring motion as the joints jump
+ * to the first frame of the new animation in a single frame. Morph allows the joints to interpolate between their old
+ * position and the current animation's position over multiple frames (specified by morphCounter) to avoid this. If an
+ * animation is using morph an array separate from jointTable, morphTable stores the the animation's joint rotations for
+ * the current frame.
+ *
+ * BaseSkeletonR: A collection of JointElemR structs that make up the skeleton that will be animated.
+ *
+ * BaseAnimationR: Stores keyframes used to animate the skeleton. Skeletons are animated by rotating joints. They can't
+ * be translated or scaled, and the distance between two joints will always be the same. The only joint that has
+ * translation data is the root joint.
+ *
+ * SkeletonInfoR: The large struct containing a FrameControl, BaseSkeletonR, BaseAnimationR, jointTable, morphTable, and
+ * more.
+ */
+
+#include "global.h"
+#include "c_keyframe.h"
+#include "z64math.h"
+#include "m_lib.h"
+#include "gfx.h"
+#include "m_play.h"
+#include "fp.h"
+#include "sys_matrix.h"
+#include "sys_math3d.h"
+
+UNK_PTR* B_801458A0_jp[4];
+
+void cKF_FrameControl_zeroClear(FrameControl* frameControl) {
+ bzero(frameControl, sizeof(FrameControl));
+ frameControl->mode = ANIMATION_STOP;
+ frameControl->duration = 1.0f;
+ frameControl->currentFrame = 1.0f;
+ frameControl->speed = 1.0f;
+ frameControl->end = 1.0f;
+ frameControl->start = 1.0f;
+}
+
+void cKF_FrameControl_ct(FrameControl* frameControl) {
+ cKF_FrameControl_zeroClear(frameControl);
+}
+
+void cKF_FrameControl_setFrame(FrameControl* frameControl, f32 start, f32 end, f32 duration, f32 currentFrame,
+ f32 speed, AnimationMode mode) {
+ frameControl->start = start;
+ frameControl->end = (end < 1.0f) ? duration : end;
+ frameControl->duration = duration;
+ frameControl->speed = speed;
+ frameControl->currentFrame = currentFrame;
+ frameControl->mode = mode;
+}
+
+/**
+ * Check if the next frame will pass the specified frame number.
+ *
+ * @param[in] frameControl The FrameControl struct to check.
+ * @param[in] compareFrame The frame number to compare against.
+ * @param[out] remainder The amount of frames past compareFrame.
+ * @return Boolean. True if the next frame passes compareFrame.
+ */
+s32 cKF_FrameControl_passCheck(FrameControl* frameControl, f32 compareFrame, f32* remainder) {
+ f32 speed;
+
+ *remainder = 0.0f;
+
+ if (compareFrame == frameControl->currentFrame) {
+ return false;
+ }
+
+ speed = (frameControl->start < frameControl->end) ? frameControl->speed : -frameControl->speed;
+
+ if ((speed >= 0.0f && frameControl->currentFrame < compareFrame &&
+ frameControl->currentFrame + speed >= compareFrame) ||
+ (speed < 0.0f && compareFrame < frameControl->currentFrame &&
+ frameControl->currentFrame + speed <= compareFrame)) {
+ *remainder = frameControl->currentFrame + speed - compareFrame;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Check if the current frame is past the specified frame number.
+ *
+ * @return Boolean. True if the current frame is past compareFrame.
+ */
+s32 cKF_FrameControl_passCheck_now(FrameControl* frameControl, f32 compareFrame) {
+ s32 ret = false;
+
+ if (compareFrame != frameControl->currentFrame) {
+ f32 speed = (frameControl->start < frameControl->end) ? frameControl->speed : -frameControl->speed;
+
+ if ((speed >= 0.0f && compareFrame <= frameControl->currentFrame &&
+ frameControl->currentFrame - speed < compareFrame) ||
+ (speed < 0.0f && frameControl->currentFrame <= compareFrame &&
+ frameControl->currentFrame - speed > compareFrame)) {
+ ret = true;
+ }
+ } else {
+ ret = true;
+ }
+
+ return ret;
+}
+
+/**
+ * Check if an animation that plays once has completed.
+ *
+ * @return 0 if the animation is still playing. 1 if the animation mode is ANIMATION_STOP and has completed.
+ */
+s32 cKF_FrameControl_stop_proc(FrameControl* frameControl) {
+ f32 remainder;
+
+ if (frameControl->currentFrame == frameControl->end) {
+ return 1;
+ }
+ if (cKF_FrameControl_passCheck(frameControl, frameControl->end, &remainder)) {
+ frameControl->currentFrame = frameControl->end;
+ return 1;
+ }
+ if (cKF_FrameControl_passCheck(frameControl, frameControl->start, &remainder)) {
+ frameControl->currentFrame = frameControl->end;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Check if an animation that repeats has completed one loop.
+ *
+ * @return 0 if the animation is still playing. 2 if the animation mode is ANIMATION_REPEAT and a loop has completed.
+ */
+s32 cKF_FrameControl_repeat_proc(FrameControl* frameControl) {
+ f32 remainder;
+
+ if (cKF_FrameControl_passCheck(frameControl, frameControl->end, &remainder)) {
+ frameControl->currentFrame = (f32)(frameControl->start + remainder);
+ return 2;
+ }
+ if (cKF_FrameControl_passCheck(frameControl, frameControl->start, &remainder)) {
+ frameControl->currentFrame = frameControl->end + remainder;
+ return 2;
+ }
+
+ return 0;
+}
+
+/**
+ * Advance a FrameControl struct by 1 frame.
+ *
+ * @return 0 if the animation is still playing. 1 if the animation mode is ANIMATION_STOP and has completed. 2 if the
+ * animation mode is ANIMATION_REPEAT and a loop has completed.
+ */
+s32 cKF_FrameControl_play(FrameControl* frameControl) {
+ f32 speed;
+ s32 ret;
+
+ if (frameControl->mode == ANIMATION_STOP) {
+ ret = cKF_FrameControl_stop_proc(frameControl);
+ } else {
+ ret = cKF_FrameControl_repeat_proc(frameControl);
+ }
+
+ // if the animation is still playing
+ if (ret == 0) {
+ speed = (frameControl->start < frameControl->end) ? frameControl->speed : -frameControl->speed;
+ frameControl->currentFrame += speed;
+ }
+
+ // if the current frame is past the end, wrap the frame counter back to the start of the animation
+ if (frameControl->currentFrame < 1.0f) {
+ frameControl->currentFrame = (frameControl->currentFrame - 1.0f) + frameControl->duration;
+ } else if (frameControl->duration < frameControl->currentFrame) {
+ frameControl->currentFrame = (frameControl->currentFrame - frameControl->duration) + 1.0f;
+ }
+
+ return ret;
+}
+
+/**
+ * Interpolate between two values using a cubic Hermite curve.
+ *
+ * A Hermite curve is a parametric function p(t) where t is restricted to the domain [0, 1].
+ * It's defined with a start point and an end point. The shape of the curve is controlled by the tangents of the end
+ * points.
+ * If keyframes aren't placed at fixed intervals in time, the animation will abruptly change speed and direction when
+ * passing through keyframes. To avoid this, the tangents are multiplied by the length of time between the two
+ * keyframes.
+ *
+ * @param t Represents the desired position between keyframe 1 and keyframe 2 to interpolate.
+ * @param duration The amount of time between keyframe 1 and keyframe 2, in seconds.
+ * @param p0 The start point.
+ * @param p1 The end point.
+ * @param v0 The velocity at p0.
+ * @param v1 The velocity at p1.
+ * @return Interpolated value.
+ */
+f32 cKF_HermitCalc(f32 t, f32 duration, f32 p0, f32 p1, f32 v0, f32 v1) {
+ // Hermite basis functions
+ f32 h3 = 3.0f * SQ(t) - 2.0f * CB(t);
+ f32 h2 = -SQ(t) + CB(t);
+ f32 h1 = CB(t) - 2.0f * SQ(t) + t;
+ f32 h0 = 1.0f - h3;
+
+ return h0 * p0 + h3 * p1 + (h1 * v0 + h2 * v1) * duration;
+}
+
+/**
+ * Given a sequence of keyframes, return the value from currentFrame of the animation.
+ *
+ * Animations are defined by a small sequence of keyframes, which record a value at a specific frame of the
+ * animation. Any value between two keyframes is calculated by interpolating between them.
+ * Keyframes are 1 dimensional. In the context of a joint the x, y, and z rotation each use a separate
+ * sequence of keyframes.
+ *
+ * @param startIndex Which keyframe in dataSource to start with.
+ * @param sequenceLength How many keyframes are in the sequence.
+ * @param dataSource Array where all the keyframes are stored.
+ * @return The value that corresponds to currentFrame.
+ */
+s16 cKF_KeyCalc(s16 startIndex, s16 sequenceLength, Keyframe* dataSource, f32 currentFrame) {
+ Keyframe* ds = &dataSource[startIndex];
+ f32 frameDelta;
+ s32 kf2;
+ s32 kf1;
+
+ // If currentFrame is before the first keyframe of the sequence.
+ if (currentFrame <= ds[0].frame) {
+ return ds[0].value;
+ }
+ // If currentFrame is after the last frame of the sequence.
+ if (ds[sequenceLength - 1].frame <= currentFrame) {
+ return ds[sequenceLength - 1].value;
+ }
+ // Iterate over each keyframe to find which keyframes are before and after currentFrame.
+ for (kf2 = 1, kf1 = 0; true; kf1++, kf2++) {
+ if (currentFrame < ds[kf2].frame) {
+ frameDelta = ds[kf2].frame - ds[kf1].frame;
+ if (!IS_ZERO(frameDelta)) {
+ return nearbyint(cKF_HermitCalc((currentFrame - ds[kf1].frame) / frameDelta, frameDelta * (1.0f / 30),
+ ds[kf1].value, ds[kf2].value, ds[kf1].velocity, ds[kf2].velocity));
+ } else {
+ return ds[kf1].value;
+ }
+ }
+ }
+}
+
+void cKF_SkeletonInfo_subRotInterpolation(f32 t, s16* out, s16 jointRotation, s16 morphRotation) {
+ u16 urot1 = jointRotation;
+ s32 pad;
+ u16 urot2 = morphRotation;
+ f32 f1 = jointRotation;
+ f32 signedDiff = morphRotation - f1;
+ f32 f2 = urot1;
+ f32 unsignedDiff = urot2 - f2;
+
+ if (fabsf(signedDiff) < fabsf(unsignedDiff)) {
+ *out = f1 + signedDiff * t;
+ } else {
+ *out = f2 + unsignedDiff * t;
+ }
+}
+
+/**
+ * When playing a morph animation, linearly interpolate the root joint between it's current position and it's position
+ * in the current frame of the animation.
+ *
+ * Linear interpolation uses a parametric function p(t) where t is restricted to the domain [0, 1].
+ *
+ * @param t Represents the desired position between p0 and p1 to interpolate.
+ */
+void cKF_SkeletonInfo_morphST(s16* joint, s16* morph, f32 t) {
+ s32 i;
+
+ for (i = 0; i < 3; i++) {
+ if (*joint != *morph) {
+ f32 p0 = *joint;
+ f32 p1 = *morph;
+
+ *joint = p0 + (p1 - p0) * t;
+ }
+ joint++;
+ morph++;
+ }
+}
+
+void cKF_SkeletonInfo_R_zeroClear(SkeletonInfoR* skeletonInfo) {
+ bzero(skeletonInfo, sizeof(SkeletonInfoR));
+}
+
+void cKF_SkeletonInfo_R_ct(SkeletonInfoR* skeletonInfo, BaseSkeletonR* skeleton, BaseAnimationR* animation,
+ Vec3s* jointTable, Vec3s* morphTable) {
+ cKF_SkeletonInfo_R_zeroClear(skeletonInfo);
+ cKF_FrameControl_ct(&skeletonInfo->frameControl);
+ skeletonInfo->skeleton = Lib_SegmentedToVirtual(skeleton);
+ skeletonInfo->animation = Lib_SegmentedToVirtual(animation);
+ skeletonInfo->jointTable = jointTable;
+ skeletonInfo->morphTable = morphTable;
+}
+
+void cKF_SkeletonInfo_R_dt(SkeletonInfoR* skeletonInfo) {
+}
+
+void cKF_SkeletonInfo_R_init_standard_stop(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, 1.0f, 1.0,
+ 0.0f, ANIMATION_STOP, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_standard_stop_speedset(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable, f32 speed) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, 1.0f, speed,
+ 0.0f, ANIMATION_STOP, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_standard_stop_morph(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable, f32 morphCounter) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, 1.0f, 1.0,
+ morphCounter, ANIMATION_STOP, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_standard_repeat(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, 1.0f, 1.0,
+ 0.0f, ANIMATION_REPEAT, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_standard_repeat_speedset(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable, f32 speed) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, 1.0f, speed,
+ 0.0f, ANIMATION_REPEAT, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_standard_repeat_morph(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable, f32 morphCounter) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, 1.0f, 1.0,
+ morphCounter, ANIMATION_REPEAT, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init(SkeletonInfoR* skeletonInfo, BaseSkeletonR* skeleton, BaseAnimationR* animation,
+ f32 startFrame, f32 endFrame, f32 currentFrame, f32 speed, f32 morphCounter,
+ AnimationMode mode, Vec3s* diffRotTable) {
+ skeletonInfo->morphCounter = morphCounter;
+ skeletonInfo->skeleton = Lib_SegmentedToVirtual(skeleton);
+ skeletonInfo->animation = Lib_SegmentedToVirtual(animation);
+ cKF_FrameControl_setFrame(&skeletonInfo->frameControl, startFrame, endFrame, skeletonInfo->animation->duration,
+ currentFrame, speed, mode);
+ skeletonInfo->diffRotTable = diffRotTable;
+}
+
+void cKF_SkeletonInfo_R_setAnim(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ skeletonInfo->animation = animationPtr;
+ skeletonInfo->frameControl.duration = animationPtr->duration;
+}
+
+void cKF_SkeletonInfo_R_morphJoint(SkeletonInfoR* skeletonInfo) {
+ // the current position of the joints
+ Vec3s* joint = skeletonInfo->jointTable;
+ // the animation's current position
+ Vec3s* morph = skeletonInfo->morphTable;
+ // Represents the position between joint 1 and joint 2 to interpolate.
+ f32 parameter = !IS_ZERO(skeletonInfo->morphCounter) ? 1.0f / fabsf(skeletonInfo->morphCounter) : 0.0f;
+ Vec3s jointTemp;
+ Vec3s morphTemp;
+ Vec3s temp_vec;
+ s32 i;
+ f32 new_var;
+ f32 new_var2;
+
+ // the first Vec3s is the root joint's translation
+ cKF_SkeletonInfo_morphST((s16*)joint, (s16*)morph, parameter);
+
+ joint++;
+ morph++;
+
+ for (i = 0; i < skeletonInfo->skeleton->numberOfJoints; i++) {
+ jointTemp.x = joint->x;
+ jointTemp.y = joint->y;
+ jointTemp.z = joint->z;
+
+ morphTemp.x = morph->x;
+ morphTemp.y = morph->y;
+ morphTemp.z = morph->z;
+
+ if ((jointTemp.x != morphTemp.x) || (jointTemp.y != morphTemp.y) || (jointTemp.z != morphTemp.z)) {
+ temp_vec.x = 0x7FFF + jointTemp.x;
+ temp_vec.y = 0x7FFF - jointTemp.y;
+ temp_vec.z = 0x7FFF + jointTemp.z;
+
+ new_var = (fabsf(((f32)morphTemp.x) - jointTemp.x) + fabsf(((f32)morphTemp.y) - jointTemp.y)) +
+ fabsf(((f32)morphTemp.z) - jointTemp.z);
+ new_var2 = (fabsf(((f32)morphTemp.x) - temp_vec.x) + fabsf(((f32)morphTemp.y) - temp_vec.y)) +
+ fabsf(((f32)morphTemp.z) - temp_vec.z);
+
+ if (new_var < new_var2) {
+ cKF_SkeletonInfo_subRotInterpolation(parameter, &joint->x, jointTemp.x, morphTemp.x);
+ cKF_SkeletonInfo_subRotInterpolation(parameter, &joint->y, jointTemp.y, morphTemp.y);
+ cKF_SkeletonInfo_subRotInterpolation(parameter, &joint->z, jointTemp.z, morphTemp.z);
+ } else {
+ cKF_SkeletonInfo_subRotInterpolation(parameter, &joint->x, temp_vec.x, morphTemp.x);
+ cKF_SkeletonInfo_subRotInterpolation(parameter, &joint->y, temp_vec.y, morphTemp.y);
+ cKF_SkeletonInfo_subRotInterpolation(parameter, &joint->z, temp_vec.z, morphTemp.z);
+ }
+ }
+ morph++;
+ joint++;
+ }
+}
+
+/**
+ * Advance the animation one frame and transform the joints accordingly.
+ *
+ * Joints can be animated in two ways, with keyframes or constant values. Which one a value uses is stored in
+ * constKeyCheckBitTable. In this table, there is a byte for every joint, which is a bitfield. 1 is keyframe, 0 is
+ * constant value. The 00111000 bits correspond to that joint's x, y, or z translation. the 00000111 bits correspond to
+ * that joint's x, y, or z rotation. Note that only the root joint can be translated.
+ *
+ * @return 0 if the animation is still playing. 1 if the animation has completed. 2 if the animation repeated.
+ */
+s32 cKF_SkeletonInfo_R_play(SkeletonInfoR* skeletonInfo) {
+ s32 jointIndex;
+ s32 componentIndex;
+ u8* ckcbTable;
+ s32 keyframeNumberIndex = 0;
+ s32 constValueIndex = 0;
+ s32 keyframeStartIndex = 0;
+ s16* jointComponent =
+ (!IS_ZERO(skeletonInfo->morphCounter)) ? (s16*)skeletonInfo->morphTable : (s16*)skeletonInfo->jointTable;
+ s16* constValueTable;
+ Keyframe* dataSource;
+ s16* keyframeNumberTable;
+ u32 ckcbIndex;
+ Vec3s* joint;
+
+ constValueTable = Lib_SegmentedToVirtual(skeletonInfo->animation->constValueTable);
+ keyframeNumberTable = Lib_SegmentedToVirtual(skeletonInfo->animation->keyframeNumber);
+ dataSource = Lib_SegmentedToVirtual(skeletonInfo->animation->dataSource);
+ ckcbTable = Lib_SegmentedToVirtual(skeletonInfo->animation->constKeyCheckBitTable);
+
+ // Translate the root joint.
+ for (ckcbIndex = 0x20, componentIndex = 0; componentIndex < 3; componentIndex++) {
+
+ if (*ckcbTable & ckcbIndex) {
+ *jointComponent = cKF_KeyCalc(keyframeStartIndex, keyframeNumberTable[keyframeNumberIndex], dataSource,
+ skeletonInfo->frameControl.currentFrame);
+ keyframeStartIndex += keyframeNumberTable[keyframeNumberIndex];
+ keyframeNumberIndex++;
+
+ } else {
+ *jointComponent = constValueTable[constValueIndex];
+ constValueIndex++;
+ }
+
+ ckcbIndex >>= 1;
+ jointComponent++;
+ }
+
+ // Rotate every joint.
+ for (jointIndex = 0; jointIndex < skeletonInfo->skeleton->numberOfJoints; jointIndex++) {
+ ckcbIndex = 4;
+ for (componentIndex = 0; componentIndex < 3; componentIndex++) {
+
+ if (ckcbTable[jointIndex] & ckcbIndex) {
+ *jointComponent = cKF_KeyCalc(keyframeStartIndex, keyframeNumberTable[keyframeNumberIndex], dataSource,
+ skeletonInfo->frameControl.currentFrame);
+ keyframeStartIndex += keyframeNumberTable[keyframeNumberIndex];
+ keyframeNumberIndex++;
+
+ } else {
+ *jointComponent = constValueTable[constValueIndex];
+ constValueIndex++;
+ }
+
+ // Animations store angles in degrees * 10. These are converted to binary angles.
+ *jointComponent = DEG_TO_BINANG(FMOD(*jointComponent * 0.1f, 360.0f));
+
+ ckcbIndex >>= 1;
+ jointComponent++;
+ }
+ }
+
+ if (skeletonInfo->diffRotTable) {
+ joint = (!IS_ZERO(skeletonInfo->morphCounter)) ? skeletonInfo->morphTable : skeletonInfo->jointTable;
+ joint++;
+ for (jointIndex = 0; jointIndex < skeletonInfo->skeleton->numberOfJoints; jointIndex++) {
+ joint->x = joint->x + skeletonInfo->diffRotTable[jointIndex].x;
+ joint->y = joint->y + skeletonInfo->diffRotTable[jointIndex].y;
+ joint->z = joint->z + skeletonInfo->diffRotTable[jointIndex].z;
+ joint++;
+ }
+ }
+
+ // If the animation isn't using morph.
+ if (IS_ZERO(skeletonInfo->morphCounter)) {
+ return cKF_FrameControl_play(&skeletonInfo->frameControl);
+
+ } else if (skeletonInfo->morphCounter > 0.0f) {
+ // A positive morphCounter will wait to play the animation until the morph has finished.
+ cKF_SkeletonInfo_R_morphJoint(skeletonInfo);
+
+ skeletonInfo->morphCounter -= 1.0f;
+ if (skeletonInfo->morphCounter <= 0.0f) {
+ skeletonInfo->morphCounter = 0.0f;
+ }
+ return 0;
+
+ } else {
+ // A negative morphCounter will play the animation and morph at the same time.
+ cKF_SkeletonInfo_R_morphJoint(skeletonInfo);
+
+ skeletonInfo->morphCounter += 1.0f;
+ if (skeletonInfo->morphCounter >= 0.0f) {
+ skeletonInfo->morphCounter = 0.0f;
+ }
+ return cKF_FrameControl_play(&skeletonInfo->frameControl);
+ }
+}
+
+/**
+ * Draw a specified joint in a SkeletonInfo struct.
+ *
+ * This is a recursive function, that will call itself again for each child this joint has.
+ *
+ * Joints inherit the transformation of their parent joint. This is accomplished with a matrix stack, where each joint
+ * pushes it's own transformation matrix onto the stack, and pops the matrix when there are no more children.
+ *
+ * Actors can override the drawing process with beforeCallback and afterCallback functions. One common example is to set
+ * a specific joint's display list to null in order to make it invisible.
+ *
+ * @param jointIndex The index of the jointElem to draw.
+ */
+void cKF_Si3_draw_SV_R_child(PlayState* play, SkeletonInfoR* skeletonInfo, s32* jointIndex, DrawCallback beforeCallback,
+ DrawCallback afterCallback, void* arg, Mtx** mtx) {
+ JointElemR* jointElem = *jointIndex + (JointElemR*)Lib_SegmentedToVirtual(skeletonInfo->skeleton->jointElemTable);
+ s32 i;
+ Gfx* newDlist;
+ Gfx* shape;
+ u8 displayBufferFlag;
+ Vec3s rotation;
+ Vec3s* joint = &skeletonInfo->jointTable[*jointIndex];
+ Vec3f translation;
+
+ // if this is not the root joint.
+ if (*jointIndex != 0) {
+ translation.x = jointElem->translation.x;
+ translation.y = jointElem->translation.y;
+ translation.z = jointElem->translation.z;
+
+ } else {
+ s32 transformationFlag = skeletonInfo->animationMove.transformationFlag;
+ Vec3f* baseTranslation = &skeletonInfo->animationMove.baseShapeTranslation;
+
+ // Translate the root joint.
+ // The AnimationMove struct can specify an override translation.
+ if (transformationFlag & 1) {
+ translation.x = baseTranslation->x;
+ translation.z = baseTranslation->z;
+ } else {
+ translation.x = joint->x;
+ translation.z = joint->z;
+ }
+
+ if (transformationFlag & 2) {
+ translation.y = baseTranslation->y;
+ } else {
+ translation.y = joint->y;
+ }
+ }
+
+ // jointIndex increases by 1 each recursive call, but because the root joint uses the first 2 Vec3s in jointTable,
+ // the index of the current joint is actually jointIndex + 1.
+ joint++;
+ rotation = *joint;
+
+ if (*jointIndex == 0) {
+ s32 transformationFlag = skeletonInfo->animationMove.transformationFlag;
+
+ if (transformationFlag & 4) {
+ rotation.x = skeletonInfo->animationMove.baseShapeRotation.x;
+ rotation.y = skeletonInfo->animationMove.updatedBaseShapeRotation.y;
+ rotation.z = skeletonInfo->animationMove.updatedBaseShapeRotation.z;
+ }
+ }
+
+ OPEN_DISPS(play->state.gfxCtx);
+ Matrix_push();
+ newDlist = shape = jointElem->shape;
+ displayBufferFlag = jointElem->displayBufferFlag;
+
+ if ((beforeCallback == NULL) ||
+ (beforeCallback != NULL && beforeCallback(play, skeletonInfo, *jointIndex, &newDlist, &displayBufferFlag, arg,
+ &rotation, &translation) != NULL)) {
+ Matrix_softcv3_mult(&translation, &rotation);
+
+ if (newDlist != NULL) {
+ _Matrix_to_Mtx(*mtx);
+
+ if (displayBufferFlag & 1) {
+ gSPMatrix(POLY_XLU_DISP++, *mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, newDlist);
+ } else {
+ gSPMatrix(POLY_OPA_DISP++, *mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_OPA_DISP++, newDlist);
+ }
+
+ (*mtx)++;
+
+ } else if (shape != NULL) {
+ _Matrix_to_Mtx(*mtx);
+ gSPMatrix(POLY_OPA_DISP++, *mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ (*mtx)++;
+ }
+ }
+
+ if (afterCallback != NULL) {
+ afterCallback(play, skeletonInfo, *jointIndex, &newDlist, &displayBufferFlag, arg, &rotation, &translation);
+ }
+
+ (*jointIndex)++;
+
+ for (i = 0; i < jointElem->numberOfChildren; i++) {
+ cKF_Si3_draw_SV_R_child(play, skeletonInfo, jointIndex, beforeCallback, afterCallback, arg, mtx);
+ }
+
+ Matrix_pull();
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
+/**
+ * Draw all the joints in in a SkeletonInfo struct.
+ *
+ * This function calls cKF_Si3_draw_SV_R_child() to recursively draw each joint.
+ */
+void cKF_Si3_draw_R_SV(PlayState* play, SkeletonInfoR* skeletonInfo, Mtx* mtx, DrawCallback beforeCallback,
+ DrawCallback afterCallback, void* arg) {
+ s32 jointIndex;
+
+ if (mtx != NULL) {
+ OPEN_DISPS(play->state.gfxCtx);
+ gSPSegment(POLY_OPA_DISP++, 0x0D, mtx);
+ gSPSegment(POLY_XLU_DISP++, 0x0D, mtx);
+ jointIndex = 0;
+ cKF_Si3_draw_SV_R_child(play, skeletonInfo, &jointIndex, beforeCallback, afterCallback, arg, &mtx);
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+}
+
+void cKF_SkeletonInfo_R_init_standard_repeat_speedsetandmorph(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable, f32 speed, f32 morphCounter) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, 1.0f, speed,
+ morphCounter, ANIMATION_REPEAT, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_standard_repeat_setframeandspeedandmorph(SkeletonInfoR* skeletonInfo,
+ BaseAnimationR* animation, Vec3s* diffRotTable,
+ f32 currentFrame, f32 speed, f32 morphCounter) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, currentFrame,
+ speed, morphCounter, ANIMATION_REPEAT, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_standard_setframeandspeedandmorphandmode(SkeletonInfoR* skeletonInfo,
+ BaseAnimationR* animation, Vec3s* diffRotTable,
+ f32 currentFrame, f32 speed, f32 morphCounter,
+ AnimationMode mode) {
+ BaseAnimationR* animationPtr = Lib_SegmentedToVirtual(animation);
+
+ cKF_SkeletonInfo_R_init(skeletonInfo, skeletonInfo->skeleton, animation, 1.0f, animationPtr->duration, currentFrame,
+ speed, morphCounter, mode, diffRotTable);
+}
+
+void cKF_SkeletonInfo_R_init_reverse_setspeedandmorphandmode(SkeletonInfoR* skeletonInfo, BaseAnimationR* animation,
+ Vec3s* diffRotTable, f32 currentFrame, f32 speed,
+ AnimationMode mode) {
+ cKF_SkeletonInfo_R_init(
+ skeletonInfo, skeletonInfo->skeleton, animation, ((BaseAnimationR*)Lib_SegmentedToVirtual(animation))->duration,
+ 1.0f, ((BaseAnimationR*)Lib_SegmentedToVirtual(animation))->duration, currentFrame, speed, mode, diffRotTable);
+}
+
+void func_80053384_jp(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4, s32 arg5, UNK_PTR* arg6, UNK_PTR* arg7,
+ UNK_PTR* arg8) {
+ if (arg3 != 0) {
+ B_801458A0_jp[arg0] = OS_PHYSICAL_TO_K0(arg6);
+ }
+ if (arg4 != 0) {
+ B_801458A0_jp[arg1] = OS_PHYSICAL_TO_K0(arg7);
+ }
+ if (arg5 != 0) {
+ B_801458A0_jp[arg2] = OS_PHYSICAL_TO_K0(arg8);
+ }
+}
+
+void cKF_SkeletonInfo_R_combine_work_set(SkeletonInfoRCombineWork* combineWork, SkeletonInfoR* skeletonInfo) {
+ combineWork->skeletonInfo = skeletonInfo;
+ combineWork->constValueTable = Lib_SegmentedToVirtual(skeletonInfo->animation->constValueTable);
+ combineWork->keyframeNumber = Lib_SegmentedToVirtual(skeletonInfo->animation->keyframeNumber);
+ combineWork->dataSource = Lib_SegmentedToVirtual(skeletonInfo->animation->dataSource);
+ combineWork->constKeyCheckBitTable = Lib_SegmentedToVirtual(skeletonInfo->animation->constKeyCheckBitTable);
+ combineWork->keyframeNumberIndex = 0;
+ combineWork->ckcbIndex = 0;
+ combineWork->keyframeStartIndex = 0;
+}
+
+void cKF_SkeletonInfo_R_combine_translation(s16** joint, u32* flag, SkeletonInfoRCombineWork* combineWork, s8* arg3) {
+ SkeletonInfoRCombineWork* temp_s1;
+ SkeletonInfoRCombineWork* temp_s2;
+ s32 i;
+
+ temp_s1 = &combineWork[1];
+ temp_s2 = &combineWork[2];
+ for (i = 0; i < 3; i++) {
+ switch (*arg3) {
+ case 0:
+ if (*combineWork[0].constKeyCheckBitTable & *flag) {
+ **joint =
+ cKF_KeyCalc(combineWork[0].keyframeStartIndex,
+ combineWork[0].keyframeNumber[combineWork->keyframeNumberIndex],
+ combineWork[0].dataSource, combineWork[0].skeletonInfo->frameControl.currentFrame);
+ } else {
+ **joint = combineWork->constValueTable[combineWork->ckcbIndex];
+ }
+ break;
+
+ case 1:
+ if (*temp_s1->constKeyCheckBitTable & *flag) {
+ **joint =
+ cKF_KeyCalc(temp_s1->keyframeStartIndex, temp_s1->keyframeNumber[temp_s1->keyframeNumberIndex],
+ temp_s1->dataSource, temp_s1->skeletonInfo->frameControl.currentFrame);
+ } else {
+ **joint = temp_s1->constValueTable[temp_s1->ckcbIndex];
+ }
+ break;
+
+ case 2:
+ if (*temp_s2->constKeyCheckBitTable & *flag) {
+ **joint =
+ cKF_KeyCalc(temp_s2->keyframeStartIndex, temp_s2->keyframeNumber[temp_s2->keyframeNumberIndex],
+ temp_s2->dataSource, temp_s2->skeletonInfo->frameControl.currentFrame);
+ } else {
+ **joint = temp_s2->constValueTable[temp_s2->ckcbIndex];
+ }
+ break;
+ }
+ if (*combineWork[0].constKeyCheckBitTable & *flag) {
+ combineWork->keyframeStartIndex += combineWork[0].keyframeNumber[combineWork->keyframeNumberIndex++];
+ } else {
+ combineWork->ckcbIndex += 1;
+ }
+ if (*temp_s1->constKeyCheckBitTable & *flag) {
+ temp_s1->keyframeStartIndex += temp_s1->keyframeNumber[temp_s1->keyframeNumberIndex++];
+ } else {
+ temp_s1->ckcbIndex++;
+ }
+ if (*temp_s2->constKeyCheckBitTable & *flag) {
+ temp_s2->keyframeStartIndex += temp_s2->keyframeNumber[temp_s2->keyframeNumberIndex++];
+ } else {
+ temp_s2->ckcbIndex += 1;
+ }
+ *flag >>= 1;
+ (*joint)++;
+ }
+}
+
+void cKF_SkeletonInfo_R_combine_rotation(s16** joint, u32* flag, SkeletonInfoRCombineWork* combineWork, s8* arg3) {
+ SkeletonInfoRCombineWork* sp64;
+ SkeletonInfoRCombineWork* temp_s0;
+ SkeletonInfoRCombineWork* temp_s1;
+ s32 i;
+ s32 j;
+
+ sp64 = combineWork;
+ temp_s0 = &combineWork[1];
+ temp_s1 = &combineWork[2];
+ for (i = 0; i < sp64->skeletonInfo->skeleton->numberOfJoints; i++) {
+ *flag = 4;
+ for (j = 0; j < 3; j++) {
+ switch (arg3[i + 1]) {
+ case 0:
+ if (combineWork->constKeyCheckBitTable[i] & *flag) {
+ **joint =
+ cKF_KeyCalc(combineWork[0].keyframeStartIndex,
+ combineWork->keyframeNumber[combineWork->keyframeNumberIndex],
+ combineWork->dataSource, combineWork->skeletonInfo->frameControl.currentFrame);
+ } else {
+ **joint = combineWork->constValueTable[combineWork->ckcbIndex];
+ }
+ break;
+
+ case 1:
+ if (temp_s0->constKeyCheckBitTable[i] & *flag) {
+ **joint = cKF_KeyCalc(temp_s0->keyframeStartIndex,
+ temp_s0->keyframeNumber[temp_s0->keyframeNumberIndex],
+ temp_s0->dataSource, temp_s0->skeletonInfo->frameControl.currentFrame);
+ } else {
+ **joint = temp_s0->constValueTable[temp_s0->ckcbIndex];
+ }
+ break;
+
+ case 2:
+ if (temp_s1->constKeyCheckBitTable[i] & *flag) {
+ **joint = cKF_KeyCalc(temp_s1->keyframeStartIndex,
+ temp_s1->keyframeNumber[temp_s1->keyframeNumberIndex],
+ temp_s1->dataSource, temp_s1->skeletonInfo->frameControl.currentFrame);
+ } else {
+ **joint = temp_s1->constValueTable[temp_s1->ckcbIndex];
+ }
+ break;
+ }
+ if (combineWork->constKeyCheckBitTable[i] & *flag) {
+ combineWork->keyframeStartIndex += combineWork->keyframeNumber[combineWork->keyframeNumberIndex++];
+ } else {
+ combineWork->ckcbIndex++;
+ }
+ if (temp_s0->constKeyCheckBitTable[i] & *flag) {
+ temp_s0->keyframeStartIndex += temp_s0->keyframeNumber[temp_s0->keyframeNumberIndex++];
+ } else {
+ temp_s0->ckcbIndex++;
+ }
+ if (temp_s1->constKeyCheckBitTable[i] & *flag) {
+ temp_s1->keyframeStartIndex += temp_s1->keyframeNumber[temp_s1->keyframeNumberIndex++];
+ } else {
+ temp_s1->ckcbIndex++;
+ }
+ **joint = DEG_TO_BINANG(FMOD(**joint * 0.1f, 360.0f));
+ *flag >>= 1;
+ *joint += 1;
+ }
+ }
+}
+
+s32 cKF_SkeletonInfo_R_combine_play(SkeletonInfoR* skeletonInfo1, SkeletonInfoR* skeletonInfo2, s32 arg2, s32 arg3,
+ s32 arg4, s32 arg5, s8* flag) {
+ Vec3s* var_v0;
+ u32 spB0;
+ s16* joint;
+ SkeletonInfoRCombineWork combine1;
+ SkeletonInfoRCombineWork combine2;
+ SkeletonInfoRCombineWork combine3;
+ s32 var_s0;
+ UNK_PTR* sp44 = NULL;
+ UNK_PTR* sp40 = NULL;
+ s32 var_v1;
+
+ if ((skeletonInfo1 == NULL) || (skeletonInfo2 == NULL) || (arg2 < 0) || (arg2 >= 0x10) || (arg3 < 0) ||
+ (arg3 >= 0x10) || (flag == NULL)) {
+ return 0;
+ }
+
+ joint = (!IS_ZERO(skeletonInfo1->morphCounter)) ? (s16*)skeletonInfo1->morphTable : (s16*)skeletonInfo1->jointTable;
+
+ if (arg4 != 0) {
+ sp44 = OS_PHYSICAL_TO_K0(B_801458A0_jp[arg2]);
+ B_801458A0_jp[arg2] = OS_PHYSICAL_TO_K0(arg4);
+ cKF_SkeletonInfo_R_combine_work_set(&combine3, skeletonInfo1);
+ }
+
+ if (arg5 != 0) {
+ sp40 = OS_PHYSICAL_TO_K0(B_801458A0_jp[arg3]);
+ B_801458A0_jp[arg3] = OS_PHYSICAL_TO_K0(arg5);
+ cKF_SkeletonInfo_R_combine_work_set(&combine2, skeletonInfo2);
+ cKF_SkeletonInfo_R_combine_work_set(&combine1, skeletonInfo2);
+ }
+
+ spB0 = 0x20;
+ cKF_SkeletonInfo_R_combine_translation(&joint, &spB0, &combine3, flag);
+ cKF_SkeletonInfo_R_combine_rotation(&joint, &spB0, &combine3, flag);
+
+ if (skeletonInfo1->diffRotTable != NULL) {
+ var_v0 = (!IS_ZERO(skeletonInfo1->morphCounter)) ? skeletonInfo1->morphTable : skeletonInfo1->jointTable;
+ var_v0++;
+
+ for (var_v1 = 0; var_v1 < skeletonInfo1->skeleton->numberOfJoints; var_v1++) {
+ var_v0->x = var_v0->x + skeletonInfo1->diffRotTable[var_v1].x;
+ var_v0->y = var_v0->y + skeletonInfo1->diffRotTable[var_v1].y;
+ var_v0->z = var_v0->z + skeletonInfo1->diffRotTable[var_v1].z;
+ var_v0++;
+ }
+ }
+ if (IS_ZERO(skeletonInfo1->morphCounter)) {
+ cKF_FrameControl_play(&skeletonInfo2->frameControl);
+ var_s0 = cKF_FrameControl_play(&skeletonInfo1->frameControl);
+ func_80053384_jp(arg2, arg3, 0, arg4, arg5, 0, sp44, sp40, 0);
+ } else {
+ if (skeletonInfo1->morphCounter > 0.0f) {
+ cKF_SkeletonInfo_R_morphJoint(skeletonInfo1);
+ skeletonInfo1->morphCounter -= 1.0f;
+ if (skeletonInfo1->morphCounter <= 0.0f) {
+ skeletonInfo1->morphCounter = 0.0f;
+ }
+ func_80053384_jp(arg2, arg3, 0, arg4, arg5, 0, sp44, sp40, 0);
+ return 0;
+ }
+
+ cKF_SkeletonInfo_R_morphJoint(skeletonInfo1);
+
+ skeletonInfo1->morphCounter += 1.0f;
+ if (skeletonInfo1->morphCounter >= 0.0f) {
+ skeletonInfo1->morphCounter = 0.0f;
+ }
+
+ cKF_FrameControl_play(&skeletonInfo2->frameControl);
+ var_s0 = cKF_FrameControl_play(&skeletonInfo1->frameControl);
+ func_80053384_jp(arg2, arg3, 0, arg4, arg5, 0, sp44, sp40, 0);
+ }
+ return var_s0;
+}
+
+void cKF_SkeletonInfo_R_T_combine_play(s32* arg0, s32* arg1, s32* arg2, SkeletonInfoR* skeletonInfo1,
+ SkeletonInfoR* skeletonInfo2, SkeletonInfoR* skeletonInfo3, s32 arg6, s32 arg7,
+ s32 arg8, s32 arg9, s32 argA, s32 argB, s8* flag) {
+ s32 i;
+ u32 spB0;
+ s16* spAC;
+ SkeletonInfoRCombineWork sp8C;
+ SkeletonInfoRCombineWork sp6C;
+ SkeletonInfoRCombineWork sp4C;
+ UNK_PTR* sp48 = NULL;
+ UNK_PTR* sp44 = NULL;
+ UNK_PTR* sp40 = NULL;
+ Vec3s* var_v0;
+
+ if ((skeletonInfo1 == NULL) || (skeletonInfo2 == NULL) || (skeletonInfo3 == NULL) || (arg6 < 0) || (arg6 >= 0x10) ||
+ (arg7 < 0) || (arg7 >= 0x10) || (arg8 < 0) || (arg8 >= 0x10) || (flag == NULL)) {
+ return;
+ }
+
+ spAC = (!IS_ZERO(skeletonInfo1->morphCounter)) ? (s16*)skeletonInfo1->morphTable : (s16*)skeletonInfo1->jointTable;
+
+ if (arg9 != 0) {
+ sp48 = OS_PHYSICAL_TO_K0(B_801458A0_jp[arg6]);
+ B_801458A0_jp[arg6] = OS_PHYSICAL_TO_K0(arg9);
+ cKF_SkeletonInfo_R_combine_work_set(&sp4C, skeletonInfo1);
+ }
+ if (argA != 0) {
+ sp44 = OS_PHYSICAL_TO_K0(B_801458A0_jp[arg7]);
+ B_801458A0_jp[arg7] = OS_PHYSICAL_TO_K0(argA);
+ cKF_SkeletonInfo_R_combine_work_set(&sp6C, skeletonInfo2);
+ }
+ if (argB != 0) {
+ sp40 = OS_PHYSICAL_TO_K0(B_801458A0_jp[arg8]);
+ B_801458A0_jp[arg8] = OS_PHYSICAL_TO_K0(argB);
+ cKF_SkeletonInfo_R_combine_work_set(&sp8C, skeletonInfo3);
+ }
+
+ spB0 = 0x20;
+ cKF_SkeletonInfo_R_combine_translation(&spAC, &spB0, &sp4C, flag);
+ cKF_SkeletonInfo_R_combine_rotation(&spAC, &spB0, &sp4C, flag);
+
+ if (skeletonInfo1->diffRotTable != NULL) {
+ var_v0 = (!IS_ZERO(skeletonInfo1->morphCounter)) ? skeletonInfo1->morphTable : skeletonInfo1->jointTable;
+ var_v0++;
+
+ for (i = 0; i < skeletonInfo1->skeleton->numberOfJoints; i++) {
+ var_v0->x = var_v0->x + skeletonInfo1->diffRotTable[i].x;
+ var_v0->y = var_v0->y + skeletonInfo1->diffRotTable[i].y;
+ var_v0->z = var_v0->z + skeletonInfo1->diffRotTable[i].z;
+ var_v0++;
+ }
+ }
+
+ if (IS_ZERO(skeletonInfo1->morphCounter)) {
+ *arg0 = cKF_FrameControl_play(&skeletonInfo1->frameControl);
+ *arg1 = cKF_FrameControl_play(&skeletonInfo2->frameControl);
+ *arg2 = cKF_FrameControl_play(&skeletonInfo3->frameControl);
+ } else if (skeletonInfo1->morphCounter > 0.0f) {
+ cKF_SkeletonInfo_R_morphJoint(skeletonInfo1);
+ skeletonInfo1->morphCounter -= 1.0f;
+ if (skeletonInfo1->morphCounter <= 0.0f) {
+ skeletonInfo1->morphCounter = 0.0f;
+ }
+ *arg0 = 0;
+ *arg1 = 0;
+ *arg2 = 0;
+ } else {
+ cKF_SkeletonInfo_R_morphJoint(skeletonInfo1);
+ skeletonInfo1->morphCounter += 1.0f;
+ if (skeletonInfo1->morphCounter >= 0.0f) {
+ skeletonInfo1->morphCounter = 0.0f;
+ }
+ *arg0 = cKF_FrameControl_play(&skeletonInfo1->frameControl);
+ *arg1 = cKF_FrameControl_play(&skeletonInfo2->frameControl);
+ *arg2 = cKF_FrameControl_play(&skeletonInfo3->frameControl);
+ }
+ func_80053384_jp(arg6, arg7, arg8, arg9, argA, argB, sp48, sp44, sp40);
+}
+
+void cKF_SkeletonInfo_R_Animation_Set_base_shape_trs(SkeletonInfoR* skeletonInfo, f32 translationX, f32 translationY,
+ f32 translationZ, s16 rotX, s16 rotY, s16 rotZ) {
+ skeletonInfo->animationMove.baseShapeTranslation.x = translationX;
+ skeletonInfo->animationMove.baseShapeTranslation.y = translationY;
+ skeletonInfo->animationMove.baseShapeTranslation.z = translationZ;
+ skeletonInfo->animationMove.updatedBaseShapeRotation.x = skeletonInfo->animationMove.baseShapeRotation.x = rotX;
+ skeletonInfo->animationMove.updatedBaseShapeRotation.y = skeletonInfo->animationMove.baseShapeRotation.y = rotY;
+ skeletonInfo->animationMove.updatedBaseShapeRotation.z = skeletonInfo->animationMove.baseShapeRotation.z = rotZ;
+}
+
+void cKF_SkeletonInfo_R_AnimationMove_ct_base(Vec3f* arg0, Vec3f* arg1, s16 arg2, s16 arg3, f32 arg4,
+ SkeletonInfoR* skeletonInfo, s32 transformationFlag) {
+ s32 var_v0;
+
+ skeletonInfo->animationMove.transformationFlag = transformationFlag;
+ skeletonInfo->animationMove.counter = ABS(arg4);
+ skeletonInfo->animationMove.baseWorldPosition = ZeroVec;
+ skeletonInfo->animationMove.shapeWorldPositionCorrection = ZeroVec;
+
+ if (arg0 != NULL) {
+ if (arg1 == NULL) {
+ arg1 = arg0;
+ }
+ if (transformationFlag & 1) {
+ skeletonInfo->animationMove.baseWorldPosition.x = arg1->x;
+ skeletonInfo->animationMove.baseWorldPosition.z = arg1->z;
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.x = arg0->x - arg1->x;
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.z = arg0->z - arg1->z;
+ }
+ if (transformationFlag & 2) {
+ skeletonInfo->animationMove.baseWorldPosition.y = arg1->y;
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.y = (f32)(arg0->y - arg1->y);
+ }
+ }
+ skeletonInfo->animationMove.baseAngleY = arg3;
+ skeletonInfo->animationMove.shapeAngleCorrection = 0;
+ if (transformationFlag & 4) {
+ var_v0 = arg2 - arg3;
+ if (var_v0 > 0x8000) {
+ var_v0 = -(0x10000 - var_v0);
+ } else if (var_v0 < -0x8000) {
+ var_v0 = var_v0 + 0x10000;
+ }
+ skeletonInfo->animationMove.shapeAngleCorrection = var_v0;
+ }
+}
+
+void cKF_SkeletonInfo_R_AnimationMove_dt(SkeletonInfoR* skeletonInfo) {
+ s32 transformationFlag = skeletonInfo->animationMove.transformationFlag;
+ Vec3s* rootJointTranslation = &skeletonInfo->jointTable[0];
+ Vec3f* baseShapeTranslation = &skeletonInfo->animationMove.baseShapeTranslation;
+
+ if (transformationFlag & 1) {
+ rootJointTranslation->x = baseShapeTranslation->x;
+ rootJointTranslation->z = baseShapeTranslation->z;
+ }
+ if (transformationFlag & 2) {
+ rootJointTranslation->y = baseShapeTranslation->y;
+ }
+ if (transformationFlag & 4) {
+ Vec3s* rootJointRotation = &skeletonInfo->jointTable[1];
+
+ rootJointRotation->x = skeletonInfo->animationMove.baseShapeRotation.x;
+ rootJointRotation->y = skeletonInfo->animationMove.baseShapeRotation.y;
+ rootJointRotation->z = skeletonInfo->animationMove.baseShapeRotation.z;
+ }
+ skeletonInfo->animationMove.transformationFlag = 0;
+}
+
+void cKF_SkeletonInfo_R_AnimationMove_base(Vec3f* arg0, Vec3s* arg1, Vec3f* arg2, s16 arg3,
+ SkeletonInfoR* skeletonInfo) {
+ u32 transformationFlag = skeletonInfo->animationMove.transformationFlag;
+ f32 counter = skeletonInfo->animationMove.counter;
+ f32 count;
+ f32 var_ft4;
+
+ count = counter + 1.0f;
+ if (count > 1.0f) {
+ var_ft4 = 1.0f / count;
+ } else {
+ var_ft4 = 0.0f;
+ }
+
+ if (transformationFlag & 4) {
+ f32 temp6 = skeletonInfo->animationMove.shapeAngleCorrection;
+
+ if (count > 1.0f) {
+ temp6 *= var_ft4;
+ skeletonInfo->animationMove.shapeAngleCorrection -= (s16)temp6;
+ } else {
+ skeletonInfo->animationMove.shapeAngleCorrection = 0;
+ }
+ }
+ if (count > 1.0f) {
+ if (transformationFlag & 1) {
+ f32 posXTemp;
+ f32 posZTemp;
+
+ posXTemp = skeletonInfo->animationMove.shapeWorldPositionCorrection.x;
+ posXTemp *= var_ft4;
+
+ posZTemp = skeletonInfo->animationMove.shapeWorldPositionCorrection.z;
+ posZTemp *= var_ft4;
+
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.x -= posXTemp;
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.z -= posZTemp;
+ }
+ if (transformationFlag & 2) {
+ f32 posYTemp;
+
+ posYTemp = skeletonInfo->animationMove.shapeWorldPositionCorrection.y;
+ posYTemp *= var_ft4;
+
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.y -= posYTemp;
+ }
+ } else {
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.x = 0.0f;
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.y = 0.0f;
+ skeletonInfo->animationMove.shapeWorldPositionCorrection.z = 0.0f;
+ }
+
+ if ((arg1 != NULL) && (transformationFlag & 4)) {
+ s32 sp8C = skeletonInfo->animationMove.baseAngleY;
+ s32 sp88 = skeletonInfo->animationMove.shapeAngleCorrection;
+ Vec3s* sp28 = &skeletonInfo->animationMove.updatedBaseShapeRotation;
+ s32 sp80 = skeletonInfo->animationMove.baseShapeRotation.x;
+ s32 temp;
+
+ Matrix_push();
+ Matrix_rotateXYZ(skeletonInfo->jointTable[1].x, skeletonInfo->jointTable[1].y, skeletonInfo->jointTable[1].z,
+ 0);
+ Matrix_to_rotate2_new(get_Matrix_now(), sp28, 0);
+ Matrix_pull();
+ temp = sp28->x - sp80;
+ arg1->x = (sp8C + sp88) + temp;
+ }
+
+ if (arg0 != NULL) {
+ Vec3s* sp78 = skeletonInfo->jointTable;
+ s16 var_a0 = 0;
+
+ if (arg1 != NULL) {
+ var_a0 = arg1->x - arg3;
+ }
+
+ if (transformationFlag & 1) {
+ f32 baseTranslationXTemp = skeletonInfo->animationMove.baseShapeTranslation.x;
+ f32 baseTranslationZTemp = skeletonInfo->animationMove.baseShapeTranslation.z;
+ f32 sin1 = sin_s(var_a0);
+ f32 cos1 = cos_s(var_a0);
+ s32 pad[2];
+ f32 move_x = arg2->x * (sp78->x - ((baseTranslationXTemp * cos1) + (baseTranslationZTemp * sin1)));
+ f32 move_z = arg2->z * (sp78->z - ((-baseTranslationXTemp * sin1) + (baseTranslationZTemp * cos1)));
+ f32 sin2 = sin_s(arg3);
+ f32 cos2 = cos_s(arg3);
+ f32 correctBaseWorldXTemp = skeletonInfo->animationMove.shapeWorldPositionCorrection.x;
+ f32 correctBaseWorldZTemp = skeletonInfo->animationMove.shapeWorldPositionCorrection.z;
+ s32 pad2[2];
+
+ arg0->x = (skeletonInfo->animationMove.baseWorldPosition.x + correctBaseWorldXTemp) +
+ ((move_x * cos2) + (move_z * sin2));
+ arg0->z = (skeletonInfo->animationMove.baseWorldPosition.z + correctBaseWorldZTemp) +
+ ((-move_x * sin2) + (move_z * cos2));
+ }
+
+ if (transformationFlag & 2) {
+ f32 yTemp = skeletonInfo->animationMove.baseShapeTranslation.y;
+ f32 temp3 = (arg2->y * (sp78->y - yTemp));
+ f32 new_var = skeletonInfo->animationMove.shapeWorldPositionCorrection.y;
+
+ arg0->y = (skeletonInfo->animationMove.baseWorldPosition.y + new_var) + temp3;
+ }
+ }
+ counter -= 1.0f;
+
+ if (counter < 0.0f) {
+ counter = 0.0f;
+ }
+
+ skeletonInfo->animationMove.counter = counter;
+}
+
+void cKF_SkeletonInfo_R_AnimationMove_CulcTransToWorld(Vec3f* arg0, Vec3f* arg1, f32 arg2, f32 arg3, f32 arg4, s16 arg5,
+ Vec3f* arg6, SkeletonInfoR* skeleton, s32 arg8) {
+ Vec3s* temp_v0 = &skeleton->jointTable[0];
+ f32 sp20;
+ f32 sp1C;
+ f32 sp18;
+ f32 temp_fv0;
+
+ if (arg8 & 1) {
+ sp20 = temp_v0->x - arg2;
+ sp1C = temp_v0->z - arg4;
+ sp18 = sin_s(arg5);
+ temp_fv0 = cos_s(arg5);
+ arg0->x = (arg1->x + (arg6->x * ((sp20 * temp_fv0) + (sp1C * sp18))));
+ arg0->z = (arg1->z + (arg6->z * ((-sp20 * sp18) + (sp1C * temp_fv0))));
+ }
+ if (arg8 & 2) {
+ arg0->y = arg1->y + (arg6->y * (temp_v0->y - arg3));
+ }
+}