summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Estelami <NEstelami@users.noreply.github.com>2025-03-31 13:20:04 -0400
committerGitHub <noreply@github.com>2025-03-31 17:20:04 +0000
commit12f1f51e30d4fd37a070b280bee6aa092df2b79b (patch)
treed677b4ff46e5014457a56cc5daa1885767c3e13a
parent9d832ac20e785f2ce53d1584636be043febf2276 (diff)
Interpolate Horse Animation (#1061)
m---------libultraship0
-rw-r--r--mm/2s2h/BenPort.cpp8
-rw-r--r--mm/2s2h/BenPort.h1
-rw-r--r--mm/2s2h/resource/importer/AudioSampleFactory.cpp7
-rw-r--r--mm/include/gfx.h1
-rw-r--r--mm/include/z64animation.h6
-rw-r--r--mm/include/z64skin.h11
-rw-r--r--mm/src/code/stubs.c21
-rw-r--r--mm/src/code/z_skelanime.c127
-rw-r--r--mm/src/code/z_skin.c108
-rw-r--r--mm/src/code/z_skin_awb.c25
-rw-r--r--mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c15
12 files changed, 245 insertions, 85 deletions
diff --git a/libultraship b/libultraship
-Subproject 62f9df4f74bf1fc2b3daf3cfb1aa7ecd2093e89
+Subproject 10424914206dda4d1c81fada08948810fad41b7
diff --git a/mm/2s2h/BenPort.cpp b/mm/2s2h/BenPort.cpp
index 299ac5d93..fec22907c 100644
--- a/mm/2s2h/BenPort.cpp
+++ b/mm/2s2h/BenPort.cpp
@@ -321,6 +321,11 @@ extern "C" uint32_t Ship_GetInterpolationFPS() {
return OTRGlobals::Instance->GetInterpolationFPS();
}
+// Numer of interpolated frames
+extern "C" uint32_t Ship_GetInterpolationFrameCount() {
+ return ceil((float)Ship_GetInterpolationFPS() / 20.0f);
+}
+
struct ExtensionEntry {
std::string path;
std::string ext;
@@ -924,8 +929,11 @@ void RunCommands(Gfx* Commands, const std::vector<std::unordered_map<Mtx*, MtxF>
// Process window events for resize, mouse, keyboard events
wnd->HandleEvents();
+ gInterpolationIndex = 0;
+
for (const auto& m : mtx_replacements) {
wnd->DrawAndRunGraphicsCommands(Commands, m);
+ gInterpolationIndex++;
}
}
diff --git a/mm/2s2h/BenPort.h b/mm/2s2h/BenPort.h
index 8a7fef0bd..5f8a82e1e 100644
--- a/mm/2s2h/BenPort.h
+++ b/mm/2s2h/BenPort.h
@@ -137,6 +137,7 @@ void Controller_UnblockGameInput();
void Overlay_DisplayText(float duration, const char* text);
void Overlay_DisplayText_Seconds(int seconds, const char* text);
uint32_t Ship_GetInterpolationFPS();
+uint32_t Ship_GetInterpolationFrameCount();
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void Gfx_UnregisterBlendedTexture(const char* name);
diff --git a/mm/2s2h/resource/importer/AudioSampleFactory.cpp b/mm/2s2h/resource/importer/AudioSampleFactory.cpp
index f3c5db017..a9f9574d8 100644
--- a/mm/2s2h/resource/importer/AudioSampleFactory.cpp
+++ b/mm/2s2h/resource/importer/AudioSampleFactory.cpp
@@ -135,7 +135,8 @@ static void FlacDecoderWorker(std::shared_ptr<SOH::AudioSample> audioSample, std
drflac_close(flac);
}
-static void OggDecoderWorker(std::shared_ptr<SOH::AudioSample> audioSample, std::shared_ptr<Ship::File> sampleFile) {
+static void OggDecoderWorker(std::shared_ptr<SOH::AudioSample> audioSample, std::shared_ptr<Ship::File> sampleFile,
+ std::shared_ptr<Ship::ResourceInitData> initData) {
OggVorbis_File vf;
char dataBuff[4096];
long read = 0;
@@ -178,7 +179,7 @@ static void OggDecoderWorker(std::shared_ptr<SOH::AudioSample> audioSample, std:
}
case OggType::None: {
char buff[2048];
- snprintf(buff, 2048, "Ogg file %s is not Vorbis or OPUS", sampleFile->InitData->Path.c_str());
+ snprintf(buff, 2048, "Ogg file %s is not Vorbis or OPUS", initData->Path.c_str());
throw std::runtime_error(buff);
break;
}
@@ -310,7 +311,7 @@ ResourceFactoryXMLAudioSampleV0::ReadResource(std::shared_ptr<Ship::File> file,
fileDecoderThread.detach();
return audioSample;
} else if (strcmp(customFormatStr, "ogg") == 0) {
- std::thread fileDecoderThread = std::thread(OggDecoderWorker, audioSample, sampleFile);
+ std::thread fileDecoderThread = std::thread(OggDecoderWorker, audioSample, sampleFile, initData);
fileDecoderThread.detach();
return audioSample;
} else if (strcmp(customFormatStr, "flac") == 0) {
diff --git a/mm/include/gfx.h b/mm/include/gfx.h
index ab031b075..4e5e6d210 100644
--- a/mm/include/gfx.h
+++ b/mm/include/gfx.h
@@ -243,6 +243,7 @@ void func_8012CF0C(GraphicsContext* gfxCtx, s32 clearFb, s32 clearZb, u8 r, u8 g
void func_8012D374(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b);
void func_8012D40C(f32* param_1, f32* param_2, s16* param_3);
void gSPSegment(void* value, int segNum, uintptr_t target);
+void gSPSegmentInterp(void* value, int segNum, uintptr_t target);
void gSPSegmentLoadRes(void* value, int segNum, uintptr_t target);
// void gDPSetTextureImage(Gfx* pkt, u32 format, u32 size, u32 width, uintptr_t i);
// void gDPSetTextureImageFB(Gfx* pkt, u32 format, u32 size, u32 width, int fb);
diff --git a/mm/include/z64animation.h b/mm/include/z64animation.h
index 973d5f0b1..36aa411e9 100644
--- a/mm/include/z64animation.h
+++ b/mm/include/z64animation.h
@@ -176,7 +176,8 @@ typedef struct SkelAnime {
/* 0x14 */ f32 animLength; // Total number of frames in the current animation's file.
/* 0x18 */ f32 curFrame; // Current frame in the animation
/* 0x1C */ f32 playSpeed; // Multiplied by R_UPDATE_RATE / 3 to get the animation's frame rate.
- /* 0x20 */ Vec3s* jointTable; // Current translation of model and rotations of all limbs
+ /* 0x20 */ Vec3s* jointTable; // Current translation of model and rotations of all limbs
+ /* 0x20 */ Vec3s* extraJointTable; // For interpolation on skinned skeletons
/* 0x24 */ Vec3s* morphTable; // Table of values used to morph between animations
/* 0x28 */ f32 morphWeight; // Weight of the current animation morph as a fraction in [0,1]
/* 0x2C */ f32 morphRate; // Reciprocal of the number of frames in the morph
@@ -189,6 +190,9 @@ typedef struct SkelAnime {
/* 0x36 */ s16 prevRot; // Previous rotation in worldspace.
/* 0x38 */ Vec3s prevTransl; // Previous modelspace translation.
/* 0x3E */ Vec3s baseTransl; // Base modelspace translation.
+
+ bool isSkinned;
+
} SkelAnime; // size = 0x44
typedef s32 (*OverrideLimbDrawOpa)(struct PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
diff --git a/mm/include/z64skin.h b/mm/include/z64skin.h
index a9e2ebc01..fba9fcf23 100644
--- a/mm/include/z64skin.h
+++ b/mm/include/z64skin.h
@@ -7,6 +7,13 @@ struct GraphicsContext;
struct GameState;
struct PlayState;
+// At 30fps, you have 2 interpolation frames
+// At 60fps, you have 3.
+// At 120fps, you have 6.
+// Do monitors go beyond 144hz?
+// We support up to 360fps, so let's go with that.
+#define MAX_INTERP_FRAMES 18
+
/**
* Holds a compact version of a vertex used in the Skin system (doesn't has the x, y, z positions or the flag member)
* It is used to initialise the Vtx used by an animated limb
@@ -67,7 +74,7 @@ typedef struct {
typedef struct {
/* 0x0 */ u8 index; // alternates every draw cycle
- /* 0x4 */ Vtx* buf[2]; // number of vertices in buffer determined by `totalVtxCount`
+ /* 0x4 */ Vtx* buf[2 * MAX_INTERP_FRAMES]; // number of vertices in buffer determined by `totalVtxCount`
} SkinLimbVtx; // size = 0xC
typedef struct {
@@ -101,6 +108,6 @@ void Skin_InitAnimatedLimb(struct GameState* gameState, Skin* skin, s32 limbInde
void Skin_Init(struct GameState* gameState, Skin* skin, SkeletonHeader* skeletonHeader, AnimationHeader* animationHeader);
void Skin_Free(struct GameState* gameState, Skin* skin);
s32 func_801387D4(Skin* skin, SkinLimb** skeleton, MtxF* limbMatrices, u8 parentIndex, u8 limbIndex);
-s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* limbMatrices, Actor* actor, s32 setTranslation);
+s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* limbMatrices, Actor* actor, s32 setTranslation, Vec3s* jointRot);
#endif
diff --git a/mm/src/code/stubs.c b/mm/src/code/stubs.c
index da40b1a1a..196ff3eec 100644
--- a/mm/src/code/stubs.c
+++ b/mm/src/code/stubs.c
@@ -167,6 +167,27 @@ void gSPSegment(void* value, int segNum, uintptr_t target) {
__gSPSegment(value, segNum, target);
}
+void gSPSegmentInterp(void* value, int segNum, uintptr_t target) {
+ char* imgData = (char*)target;
+
+ int res = ResourceMgr_OTRSigCheck(imgData);
+
+ // OTRTODO: Disabled for now to fix an issue with HD Textures.
+ // With HD textures, we need to pass the path to F3D, not the raw texture data.
+ // Otherwise the needed metadata is not available for proper rendering...
+ // This should *not* cause any crashes, but some testing may be needed...
+ // UPDATE: To maintain compatability it will still do the old behavior if the resource is a display list.
+ // That should not affect HD textures.
+ if (res) {
+ uintptr_t desiredTarget = (uintptr_t)ResourceMgr_LoadIfDListByName(imgData);
+
+ if (desiredTarget != NULL)
+ target = desiredTarget;
+ }
+
+ __gSPSegmentInterp(value, segNum, target);
+}
+
void gSPSegmentLoadRes(void* value, int segNum, uintptr_t target) {
char* imgData = (char*)target;
diff --git a/mm/src/code/z_skelanime.c b/mm/src/code/z_skelanime.c
index 924d368f9..676718d7b 100644
--- a/mm/src/code/z_skelanime.c
+++ b/mm/src/code/z_skelanime.c
@@ -620,30 +620,93 @@ void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* joi
CLOSE_DISPS(play->state.gfxCtx);
}
+// TODO: Put this in a dedicated math file
+// Actually there might already be something that accomplishes this...
+static s16 LerpS16(float a, float b, float t) {
+ return (s16)(a + (b - a) * t);
+}
+
/**
* Copies frame data from the frame data table, indexed by the joint index table.
* Indices below staticIndexMax are copied from that entry in the static frame data table.
* Indices above staticIndexMax are offsets to a frame data array indexed by the frame.
*/
-void SkelAnime_GetFrameData(AnimationHeader* animation, s32 frame, s32 limbCount, Vec3s* frameTable) {
+void SkelAnime_GetFrameData(AnimationHeader* animation, float frame, float animFrameCount, float animSpeed,
+ s32 limbCount, Vec3s* frameTable, Vec3s* interpFrameTable, bool isSkinnedSkeleton) {
if (ResourceMgr_OTRSigCheck(animation))
animation = ResourceMgr_LoadAnimByName(animation);
- AnimationHeader* animHeader = Lib_SegmentedToVirtual(animation);
- JointIndex* jointIndices = Lib_SegmentedToVirtual(animHeader->jointIndices);
- s16* frameData = Lib_SegmentedToVirtual(animHeader->frameData);
- s16* dynamicData = &frameData[frame];
- s32 i;
- u16 staticIndexMax = animHeader->staticIndexMax;
+ if (animSpeed != 0) {
+ // TODO: This feels wrong, but it prevents graphical issues
+ // Need to give this some proper thought later
+ if (animSpeed > 0)
+ animSpeed = 1;
+ else
+ animSpeed = -1;
+ }
+
+ float fpsDiv = Ship_GetInterpolationFPS() / 20.0f;
+
+ Vec3s* originalFrameTable = frameTable;
+ int interpolatedFrameCount = isSkinnedSkeleton ? Ship_GetInterpolationFrameCount() : 1;
+
+ for (int j = 0; j < Ship_GetInterpolationFrameCount(); j++) {
+ if (j > 0)
+ frameTable = &interpFrameTable[limbCount * (j - 1)];
+
+ float framePerc = frame - (s32)frame;
+ s32 frameBase = (s32)frame;
+ s32 frameBaseNext = (s32)(frame + animSpeed);
+
+ if (frameBaseNext < frameBase) {
+ int tmp = frameBase;
+ frameBase = frameBaseNext;
+ frameBaseNext = tmp;
+ }
+
+ if (animFrameCount != 0) {
+ frameBase = fmodf(frameBase, animFrameCount);
+ frameBaseNext = fmodf(frameBaseNext, animFrameCount);
+ }
+
+ if (frameBase < 0)
+ frameBase = animFrameCount - 1;
+
+ if (frameBaseNext < 0)
+ frameBaseNext = animFrameCount - 1;
- for (i = 0; i < limbCount; i++) {
- // Debug prints here, this is needed to prevent loop unrolling
- if ((frameTable == NULL) || (jointIndices == NULL) || (dynamicData == NULL)) {}
- frameTable->x = jointIndices->x >= staticIndexMax ? dynamicData[jointIndices->x] : frameData[jointIndices->x];
- frameTable->y = jointIndices->y >= staticIndexMax ? dynamicData[jointIndices->y] : frameData[jointIndices->y];
- frameTable->z = jointIndices->z >= staticIndexMax ? dynamicData[jointIndices->z] : frameData[jointIndices->z];
- jointIndices++;
- frameTable++;
+ AnimationHeader* animHeader = Lib_SegmentedToVirtual(animation);
+ JointIndex* jointIndices = Lib_SegmentedToVirtual(animHeader->jointIndices);
+ s16* frameData = Lib_SegmentedToVirtual(animHeader->frameData);
+ s16* dynamicData = &frameData[frameBase];
+ s16* dynamicDataNext = &frameData[frameBaseNext];
+ s32 i;
+ u16 staticIndexMax = animHeader->staticIndexMax;
+
+ for (i = 0; i < limbCount; i++) {
+ s16 dynamicDataX = 0;
+ s16 dynamicDataY = 0;
+ s16 dynamicDataZ = 0;
+
+ if (isSkinnedSkeleton) {
+ dynamicDataX = LerpS16(dynamicData[jointIndices->x], dynamicDataNext[jointIndices->x], framePerc);
+ dynamicDataY = LerpS16(dynamicData[jointIndices->y], dynamicDataNext[jointIndices->y], framePerc);
+ dynamicDataZ = LerpS16(dynamicData[jointIndices->z], dynamicDataNext[jointIndices->z], framePerc);
+ } else {
+ dynamicDataX = dynamicData[jointIndices->x];
+ dynamicDataY = dynamicData[jointIndices->y];
+ dynamicDataZ = dynamicData[jointIndices->z];
+ }
+
+ frameTable->x = jointIndices->x >= staticIndexMax ? dynamicDataX : frameData[jointIndices->x];
+ frameTable->y = jointIndices->y >= staticIndexMax ? dynamicDataY : frameData[jointIndices->y];
+ frameTable->z = jointIndices->z >= staticIndexMax ? dynamicDataZ : frameData[jointIndices->z];
+
+ jointIndices++;
+ frameTable++;
+ }
+
+ frame += animSpeed / fpsDiv;
}
}
@@ -1265,6 +1328,8 @@ void SkelAnime_InitPlayer(PlayState* play, SkelAnime* skelAnime, FlexSkeletonHea
skelAnime->morphTable = (void*)ALIGN16((uintptr_t)morphTableBuffer);
}
+ skelAnime->extraJointTable = ZeldaArena_Malloc(allocSize * MAX_INTERP_FRAMES);
+
PlayerAnimation_Change(play, skelAnime, animation, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f);
}
@@ -1587,6 +1652,9 @@ void SkelAnime_Init(PlayState* play, SkelAnime* skelAnime, SkeletonHeader* skele
skelAnime->morphTable = morphTable;
}
+ skelAnime->extraJointTable =
+ ZeldaArena_Malloc(sizeof(*skelAnime->jointTable) * skelAnime->limbCount * MAX_INTERP_FRAMES);
+
if (animation != NULL) {
Animation_PlayLoop(skelAnime, animation);
}
@@ -1617,6 +1685,9 @@ void SkelAnime_InitFlex(PlayState* play, SkelAnime* skelAnime, FlexSkeletonHeade
skelAnime->morphTable = morphTable;
}
+ skelAnime->extraJointTable =
+ ZeldaArena_Malloc(sizeof(*skelAnime->jointTable) * skelAnime->limbCount * MAX_INTERP_FRAMES);
+
if (animation != NULL) {
Animation_PlayLoop(skelAnime, animation);
}
@@ -1639,6 +1710,11 @@ void SkelAnime_InitSkin(GameState* gameState, SkelAnime* skelAnime, SkeletonHead
skelAnime->jointTable = ZeldaArena_Malloc(sizeof(*skelAnime->jointTable) * skelAnime->limbCount);
skelAnime->morphTable = ZeldaArena_Malloc(sizeof(*skelAnime->morphTable) * skelAnime->limbCount);
+ skelAnime->extraJointTable =
+ ZeldaArena_Malloc(sizeof(*skelAnime->jointTable) * skelAnime->limbCount * MAX_INTERP_FRAMES);
+
+ skelAnime->isSkinned = true;
+
// Debug prints here, required to match.
if (1) {}
@@ -1723,9 +1799,12 @@ s32 SkelAnime_MorphTaper(SkelAnime* skelAnime) {
* Gets frame data for the current frame as modified by morphTable and advances the morph
*/
void SkelAnime_AnimateFrame(SkelAnime* skelAnime) {
- Vec3s nextjointTable[100];
+ Vec3s nextjointTable[100 * MAX_INTERP_FRAMES];
+
+ SkelAnime_GetFrameData(skelAnime->animation, skelAnime->curFrame, skelAnime->animLength, skelAnime->playSpeed,
+ skelAnime->limbCount, skelAnime->jointTable, skelAnime->extraJointTable,
+ skelAnime->isSkinned);
- SkelAnime_GetFrameData(skelAnime->animation, skelAnime->curFrame, skelAnime->limbCount, skelAnime->jointTable);
if (skelAnime->mode & ANIM_INTERP) {
s32 frame = skelAnime->curFrame;
f32 partialFrame = skelAnime->curFrame - frame;
@@ -1734,7 +1813,8 @@ void SkelAnime_AnimateFrame(SkelAnime* skelAnime) {
if (frame >= (s32)skelAnime->animLength) {
frame = 0;
}
- SkelAnime_GetFrameData(skelAnime->animation, frame, skelAnime->limbCount, nextjointTable);
+ SkelAnime_GetFrameData(skelAnime->animation, frame, skelAnime->animLength, skelAnime->playSpeed,
+ skelAnime->limbCount, nextjointTable, skelAnime->extraJointTable, skelAnime->isSkinned);
SkelAnime_InterpFrameTable(skelAnime->limbCount, skelAnime->jointTable, skelAnime->jointTable, nextjointTable,
partialFrame);
}
@@ -1791,7 +1871,9 @@ s32 SkelAnime_Once(SkelAnime* skelAnime) {
f32 updateRate = gFramerateDivisorThird;
if (skelAnime->curFrame == skelAnime->endFrame) {
- SkelAnime_GetFrameData(skelAnime->animation, skelAnime->curFrame, skelAnime->limbCount, skelAnime->jointTable);
+ SkelAnime_GetFrameData(skelAnime->animation, skelAnime->curFrame, skelAnime->animLength, skelAnime->playSpeed,
+ skelAnime->limbCount, skelAnime->jointTable, skelAnime->extraJointTable,
+ skelAnime->isSkinned);
SkelAnime_AnimateFrame(skelAnime);
return true;
}
@@ -1842,13 +1924,16 @@ void Animation_ChangeImpl(SkelAnime* skelAnime, AnimationHeader* animation, f32
} else {
skelAnime->update.normal = SkelAnime_Morph;
}
- SkelAnime_GetFrameData(animation, startFrame, skelAnime->limbCount, skelAnime->morphTable);
+ SkelAnime_GetFrameData(animation, startFrame, skelAnime->animLength, skelAnime->playSpeed,
+ skelAnime->limbCount, skelAnime->morphTable, skelAnime->extraJointTable,
+ skelAnime->isSkinned);
}
skelAnime->morphWeight = 1.0f;
skelAnime->morphRate = 1.0f / morphFrames;
} else {
SkelAnime_SetUpdate(skelAnime);
- SkelAnime_GetFrameData(animation, startFrame, skelAnime->limbCount, skelAnime->jointTable);
+ SkelAnime_GetFrameData(animation, startFrame, skelAnime->animLength, skelAnime->playSpeed, skelAnime->limbCount,
+ skelAnime->jointTable, skelAnime->extraJointTable, skelAnime->isSkinned);
skelAnime->morphWeight = 0.0f;
}
diff --git a/mm/src/code/z_skin.c b/mm/src/code/z_skin.c
index 5569c886b..77b5f0515 100644
--- a/mm/src/code/z_skin.c
+++ b/mm/src/code/z_skin.c
@@ -1,9 +1,10 @@
#include "prevent_bss_reordering.h"
#include "global.h"
#include "z64skin.h"
+#include "BenPort.h"
// 60 is an arbitrary number which specifies the max amount of limbs per skeleton this system supports
-MtxF gSkinLimbMatrices[60];
+MtxF gSkinLimbMatrices[60 * MAX_INTERP_FRAMES];
static s32 sBssPad;
@@ -75,56 +76,67 @@ void Skin_ApplyLimbModifications(GraphicsContext* gfxCtx, Skin* skin, s32 limbIn
vtxEntry = &skin->vtxTable[limbIndex];
- vtxBuf = vtxEntry->buf[vtxEntry->index];
modifCount = data->limbModifCount;
- for (modif = modifications; modif < &modifications[modifCount]; modif++) {
- Vec3f spAC;
- Vec3f spA0;
-
- skinVertices = (SkinVertex*)Lib_SegmentedToVirtual(modif->skinVertices);
- limbTransformations = (SkinTransformation*)Lib_SegmentedToVirtual(modif->limbTransformations);
- transformCount = modif->transformCount;
-
- if (transformCount == 1) {
- spAC.x = limbTransformations[0].x;
- spAC.y = limbTransformations[0].y;
- spAC.z = limbTransformations[0].z;
-
- SkinMatrix_Vec3fMtxFMultXYZ(&gSkinLimbMatrices[limbTransformations[0].limbIndex], &spAC, &spDC);
- } else if (arg3) {
- transformationEntry = &limbTransformations[modif->unk_04];
-
- spA0.x = transformationEntry->x;
- spA0.y = transformationEntry->y;
- spA0.z = transformationEntry->z;
- SkinMatrix_Vec3fMtxFMultXYZ(&gSkinLimbMatrices[transformationEntry->limbIndex], &spA0, &spDC);
- } else {
- spDC.x = 0.0f;
- spDC.y = 0.0f;
- spDC.z = 0.0f;
-
- for (transformationEntry = limbTransformations; transformationEntry < &limbTransformations[transformCount];
- transformationEntry++) {
- scale = transformationEntry->scale * 0.01f;
-
- sp88.x = transformationEntry->x;
- sp88.y = transformationEntry->y;
- sp88.z = transformationEntry->z;
-
- SkinMatrix_Vec3fMtxFMultXYZ(&gSkinLimbMatrices[transformationEntry->limbIndex], &sp88, &spD0);
-
- spDC.x += spD0.x * scale;
- spDC.y += spD0.y * scale;
- spDC.z += spD0.z * scale;
+ int limbCount = skin->limbCount + 1;
+
+ for (int interpIdx = 0; interpIdx < Ship_GetInterpolationFrameCount(); interpIdx++) {
+ vtxBuf = vtxEntry->buf[vtxEntry->index + (interpIdx * 2)];
+
+ for (modif = modifications; modif < &modifications[modifCount]; modif++) {
+ Vec3f spAC;
+ Vec3f spA0;
+
+ skinVertices = (SkinVertex*)Lib_SegmentedToVirtual(modif->skinVertices);
+ limbTransformations = (SkinTransformation*)Lib_SegmentedToVirtual(modif->limbTransformations);
+ transformCount = modif->transformCount;
+
+ if (transformCount == 1) {
+ spAC.x = limbTransformations[0].x;
+ spAC.y = limbTransformations[0].y;
+ spAC.z = limbTransformations[0].z;
+
+ SkinMatrix_Vec3fMtxFMultXYZ(
+ &gSkinLimbMatrices[(interpIdx * limbCount) + limbTransformations[0].limbIndex], &spAC, &spDC);
+ } else if (arg3) {
+ transformationEntry = &limbTransformations[modif->unk_04];
+
+ spA0.x = transformationEntry->x;
+ spA0.y = transformationEntry->y;
+ spA0.z = transformationEntry->z;
+ SkinMatrix_Vec3fMtxFMultXYZ(
+ &gSkinLimbMatrices[(interpIdx * limbCount) + transformationEntry->limbIndex], &spA0, &spDC);
+ } else {
+ spDC.x = 0.0f;
+ spDC.y = 0.0f;
+ spDC.z = 0.0f;
+
+ for (transformationEntry = limbTransformations;
+ transformationEntry < &limbTransformations[transformCount]; transformationEntry++) {
+ scale = transformationEntry->scale * 0.01f;
+
+ sp88.x = transformationEntry->x;
+ sp88.y = transformationEntry->y;
+ sp88.z = transformationEntry->z;
+
+ SkinMatrix_Vec3fMtxFMultXYZ(
+ &gSkinLimbMatrices[(interpIdx * limbCount) + transformationEntry->limbIndex], &sp88, &spD0);
+
+ spDC.x += spD0.x * scale;
+ spDC.y += spD0.y * scale;
+ spDC.z += spD0.z * scale;
+ }
}
+
+ Skin_UpdateVertices(
+ &gSkinLimbMatrices[(interpIdx * limbCount) + limbTransformations[modif->unk_04].limbIndex],
+ skinVertices, modif, vtxBuf, &spDC);
}
- Skin_UpdateVertices(&gSkinLimbMatrices[limbTransformations[modif->unk_04].limbIndex], skinVertices, modif,
- vtxBuf, &spDC);
+ gSPSegmentInterp(POLY_OPA_DISP++, 0x08 + (interpIdx * 0x10), vtxBuf);
}
- gSPSegment(POLY_OPA_DISP++, 0x08, vtxEntry->buf[vtxEntry->index]);
+ // gSPSegment(POLY_OPA_DISP++, 0x08, vtxEntry->buf[vtxEntry->index]);
vtxEntry->index = (vtxEntry->index == 0);
@@ -192,7 +204,13 @@ void Skin_DrawImpl(Actor* actor, PlayState* play, Skin* skin, SkinPostDraw postD
OPEN_DISPS(gfxCtx);
if (!(drawFlags & SKIN_DRAW_FLAG_CUSTOM_TRANSFORMS)) {
- Skin_ApplyAnimTransformations(skin, gSkinLimbMatrices, actor, setTranslation);
+ int limbCount = skin->limbCount + 1;
+
+ Skin_ApplyAnimTransformations(skin, gSkinLimbMatrices, actor, setTranslation, skin->skelAnime.jointTable);
+
+ for (int i = 0; i < Ship_GetInterpolationFrameCount(); i++)
+ Skin_ApplyAnimTransformations(skin, gSkinLimbMatrices + (limbCount * (i + 1)), actor, setTranslation,
+ &skin->skelAnime.extraJointTable[limbCount * i]);
}
skeleton = Lib_SegmentedToVirtual(skin->skeletonHeader->segment);
diff --git a/mm/src/code/z_skin_awb.c b/mm/src/code/z_skin_awb.c
index ab2f4dc3a..3a7a5b0fd 100644
--- a/mm/src/code/z_skin_awb.c
+++ b/mm/src/code/z_skin_awb.c
@@ -71,8 +71,8 @@ void Skin_Init(GameState* gameState, Skin* skin, SkeletonHeader* skeletonHeader,
(((SkinLimb*)Lib_SegmentedToVirtual(skeleton[i]))->segment == NULL)) {
vtxEntry->index = 0;
- vtxEntry->buf[0] = NULL;
- vtxEntry->buf[1] = NULL;
+ for (int j = 0; j < ARRAY_COUNT(vtxEntry->buf); j++)
+ vtxEntry->buf[j] = NULL;
} else {
SkinAnimatedLimbData* animatedLimbData =
Lib_SegmentedToVirtual((((SkinLimb*)Lib_SegmentedToVirtual(skeleton[i]))->segment));
@@ -80,8 +80,9 @@ void Skin_Init(GameState* gameState, Skin* skin, SkeletonHeader* skeletonHeader,
{ s32 tmp; }
vtxEntry->index = 0;
- vtxEntry->buf[0] = ZeldaArena_Malloc(animatedLimbData->totalVtxCount * sizeof(Vtx));
- vtxEntry->buf[1] = ZeldaArena_Malloc(animatedLimbData->totalVtxCount * sizeof(Vtx));
+
+ for (int j = 0; j < ARRAY_COUNT(skin->vtxTable->buf); j++)
+ vtxEntry->buf[j] = ZeldaArena_Malloc(animatedLimbData->totalVtxCount * sizeof(Vtx));
Skin_InitAnimatedLimb(gameState, skin, i);
}
@@ -98,13 +99,11 @@ void Skin_Free(GameState* gameState, Skin* skin) {
s32 i;
for (i = 0; i < skin->limbCount; i++) {
- if (skin->vtxTable[i].buf[0] != NULL) {
- ZeldaArena_Free(skin->vtxTable[i].buf[0]);
- skin->vtxTable[i].buf[0] = NULL;
- }
- if (skin->vtxTable[i].buf[1] != NULL) {
- ZeldaArena_Free(skin->vtxTable[i].buf[1]);
- skin->vtxTable[i].buf[1] = NULL;
+ for (int j = 0; j < ARRAY_COUNT(skin->vtxTable->buf); j++) {
+ if (skin->vtxTable[i].buf[j] != NULL) {
+ ZeldaArena_Free(skin->vtxTable[i].buf[j]);
+ skin->vtxTable[i].buf[j] = NULL;
+ }
}
}
@@ -152,7 +151,7 @@ s32 func_801387D4(Skin* skin, SkinLimb** skeleton, MtxF* limbMatrices, u8 parent
/**
* Recursively applies matrix tranformations to each limb
*/
-s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* limbMatrices, Actor* actor, s32 setTranslation) {
+s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* limbMatrices, Actor* actor, s32 setTranslation, Vec3s* jointRot) {
s32 i;
s32 pad;
f32 yRot;
@@ -163,7 +162,6 @@ s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* limbMatrices, Actor* actor,
f32 xTransl;
f32 zTransl;
SkinLimb** skeleton = Lib_SegmentedToVirtual(skin->skeletonHeader->segment);
- Vec3s* jointRot = skin->skelAnime.jointTable;
jointRot++;
xRot = jointRot->x;
@@ -194,6 +192,7 @@ s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* limbMatrices, Actor* actor,
yRot = jointRot->y;
zRot = jointRot->z;
jointRot++;
+
SkinMatrix_SetRotateRPYTranslate(&limbMatrices[i], xRot, yRot, zRot, xTransl, yTransl, zTransl);
}
diff --git a/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c b/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c
index 9d38823ac..6de83c7f4 100644
--- a/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c
+++ b/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c
@@ -13,6 +13,7 @@
#include "overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.h"
#include "objects/object_horse_link_child/object_horse_link_child.h"
#include "2s2h/GameInteractor/GameInteractor.h"
+#include "BenPort.h"
#define FLAGS (ACTOR_FLAG_10)
@@ -1962,6 +1963,7 @@ void EnHorse_PlayIdleAnimation(EnHorse* this, s32 animIndex, f32 morphFrames, f3
this->stateFlags &= ~ENHORSE_LAND2_SOUND;
}
}
+
Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animIndex], 1.0f, startFrames,
Animation_GetLastFrame(sAnimationHeaders[this->type][this->animIndex]), ANIMMODE_ONCE,
morphFrames);
@@ -4202,6 +4204,10 @@ void EnHorse_Update(Actor* thisx, PlayState* play2) {
Vec3f dustVel = { 0.0f, 1.0f, 0.0f };
Player* player = GET_PLAYER(play);
+ for (int i = 0; i < Ship_GetInterpolationFrameCount(); i++)
+ memcpy(&this->skin.skelAnime.extraJointTable[i * this->skin.skelAnime.limbCount],
+ this->skin.skelAnime.jointTable, this->skin.skelAnime.limbCount * sizeof(Vec3s));
+
if (this->type == HORSE_TYPE_2) {
Actor_SetScale(&this->actor, 0.00648f);
} else if (this->type == HORSE_TYPE_DONKEY) {
@@ -4235,6 +4241,9 @@ void EnHorse_Update(Actor* thisx, PlayState* play2) {
this->unk_3EC = thisx->world.rot.y;
if ((this->animIndex == ENHORSE_ANIM_STOPPING) || (this->animIndex == ENHORSE_ANIM_REARING)) {
this->skin.skelAnime.jointTable[0].y += 0x154;
+
+ for (int i = 0; i < Ship_GetInterpolationFrameCount(); i++)
+ this->skin.skelAnime.extraJointTable[this->skin.skelAnime.limbCount * i].y += 0x154;
}
this->curFrame = this->skin.skelAnime.curFrame;
@@ -4718,6 +4727,12 @@ void EnHorse_Draw(Actor* thisx, PlayState* play) {
this->skin.skelAnime.jointTable->x = 0;
this->skin.skelAnime.jointTable->y = 0;
this->skin.skelAnime.jointTable->z = 0;
+
+ for (int i = 0; i < Ship_GetInterpolationFrameCount(); i++) {
+ this->skin.skelAnime.extraJointTable[this->skin.skelAnime.limbCount * i].x = 0;
+ this->skin.skelAnime.extraJointTable[this->skin.skelAnime.limbCount * i].y = 0;
+ this->skin.skelAnime.extraJointTable[this->skin.skelAnime.limbCount * i].z = 0;
+ }
}
SkelAnime_DrawFlexOpa(play, this->skin.skelAnime.skeleton, this->skin.skelAnime.jointTable,
this->skin.skelAnime.dListCount, func_80888D18, NULL, &this->actor);