diff options
| author | Nicholas Estelami <NEstelami@users.noreply.github.com> | 2025-03-31 13:20:04 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-31 17:20:04 +0000 |
| commit | 12f1f51e30d4fd37a070b280bee6aa092df2b79b (patch) | |
| tree | d677b4ff46e5014457a56cc5daa1885767c3e13a | |
| parent | 9d832ac20e785f2ce53d1584636be043febf2276 (diff) | |
Interpolate Horse Animation (#1061)
| m--------- | libultraship | 0 | ||||
| -rw-r--r-- | mm/2s2h/BenPort.cpp | 8 | ||||
| -rw-r--r-- | mm/2s2h/BenPort.h | 1 | ||||
| -rw-r--r-- | mm/2s2h/resource/importer/AudioSampleFactory.cpp | 7 | ||||
| -rw-r--r-- | mm/include/gfx.h | 1 | ||||
| -rw-r--r-- | mm/include/z64animation.h | 6 | ||||
| -rw-r--r-- | mm/include/z64skin.h | 11 | ||||
| -rw-r--r-- | mm/src/code/stubs.c | 21 | ||||
| -rw-r--r-- | mm/src/code/z_skelanime.c | 127 | ||||
| -rw-r--r-- | mm/src/code/z_skin.c | 108 | ||||
| -rw-r--r-- | mm/src/code/z_skin_awb.c | 25 | ||||
| -rw-r--r-- | mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c | 15 |
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); |
