diff options
| author | Sirius902 <10891979+Sirius902@users.noreply.github.com> | 2025-05-18 15:17:14 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-18 17:17:14 -0500 |
| commit | 4e228e0a69da03589204b54c7b02d5813619f5ec (patch) | |
| tree | 9b3b047583740048bd7c92c97113e34082278414 | |
| parent | 3142df4517f9cc21641399b3e9e2b2bedc819cfe (diff) | |
[Enhancement] Add N64 Weird Frames restoration (#1077)
* [Enhancement] Add N64 Weird Frames restoration
* Tweaks to N64WeirdFrames
---------
Co-authored-by: Garrett <garrettjcox@gmail.com>
5 files changed, 209 insertions, 1 deletions
diff --git a/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/N64WeirdFrames.cpp b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/N64WeirdFrames.cpp new file mode 100644 index 000000000..fb7ffa221 --- /dev/null +++ b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/N64WeirdFrames.cpp @@ -0,0 +1,96 @@ +#include "public/bridge/consolevariablebridge.h" +#include "2s2h/GameInteractor/GameInteractor.h" +#include "2s2h/ShipInit.hpp" + +#include <vector> +#include <spdlog/spdlog.h> + +#include "WeirdAnimation.h" + +extern "C" { +#include "functions.h" +#include "z64player.h" + +#include "objects/gameplay_keep/gameplay_keep.h" + +int ResourceMgr_OTRSigCheck(char* imgData); +AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path); +} + +#define CVAR_NAME "gEnhancements.Restorations.N64WeirdFrames" +// This CVAR is defaulted to on, because it is consistent with hardware, but able to be disabled if desired. +#define CVAR CVarGetInteger(CVAR_NAME, 1) + +// A list of weird animations to contruct. These can either be an index out of bounds before +// the start of the animation or past the end of it. In either case you add a list of animations' +// data that are neighboring before or after the target animation. If more weird frame data is +// required then add more of the neighboring animations in ROM. +static std::vector<WeirdAnimation> weirdAnimations{ + // For weirdshots. + { gPlayerAnim_link_bow_side_walk, + PLAYER_LIMB_MAX, + IndexDirection::FORWARD, + { + "__OTR__misc/link_animetion/gPlayerAnim_alink_ozigi_Data", + "__OTR__misc/link_animetion/gPlayerAnim_pz_attackBendR_Data", + "__OTR__misc/link_animetion/gPlayerAnim_pz_attackCendR_Data", + "__OTR__misc/link_animetion/gPlayerAnim_pz_attackAendR_Data", + "__OTR__misc/link_animetion/gPlayerAnim_pz_cutterattack_Data", + "__OTR__misc/link_animetion/gPlayerAnim_kf_hanare_loop_Data", + "__OTR__misc/link_animetion/gPlayerAnimData_2C6350", + } }, +}; + +void RegisterN64WeirdFrames() { + COND_VB_SHOULD(VB_LOAD_PLAYER_ANIMATION_FRAME, CVAR, { + const auto entry = va_arg(args, AnimationEntry*); + if (entry == nullptr) { + return; + } + + auto animation = va_arg(args, LinkAnimationHeader*); + auto frame = va_arg(args, s32); + const auto limbCount = va_arg(args, s32); + const auto frameTable = va_arg(args, Vec3s*); + + std::optional<const char*> animationName; + + if (ResourceMgr_OTRSigCheck(reinterpret_cast<char*>(animation)) != 0) { + animationName = reinterpret_cast<const char*>(animation); + animation = reinterpret_cast<LinkAnimationHeader*>(ResourceMgr_LoadAnimByName(*animationName)); + } + + const auto playerAnimHeader = + static_cast<LinkAnimationHeader*>(Lib_SegmentedToVirtual(static_cast<void*>(animation))); + + if (frame < 0 || frame >= playerAnimHeader->common.frameCount) { + const auto direction = frame < 0 ? IndexDirection::BACKWARD : IndexDirection::FORWARD; + + if (animationName.has_value()) { + for (auto& weirdAnimation : weirdAnimations) { + if (weirdAnimation.GetDirection() == direction && + weirdAnimation.GetTargetAnimation() == *animationName) { + if (const auto frameData = weirdAnimation.GetFrame(frame, playerAnimHeader->common.frameCount); + frameData != nullptr) { + *should = false; + + SPDLOG_DEBUG("Weird animation for \"{}\": frame {}", weirdAnimation.GetTargetAnimation(), + frame); + std::memcpy(frameTable, frameData, sizeof(Vec3s) * limbCount + sizeof(s16)); + } else { + SPDLOG_WARN("Weird Frame {} not included in weird animation for \"{}\"", frame, + weirdAnimation.GetTargetAnimation()); + } + + return; + } + } + } + + SPDLOG_WARN("Weird Animation not present for \"{}\" but frame {} is out of bounds", + animationName.has_value() ? *animationName : "<null>", frame); + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterN64WeirdFrames, { CVAR_NAME }); diff --git a/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.cpp b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.cpp new file mode 100644 index 000000000..7f55e0663 --- /dev/null +++ b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.cpp @@ -0,0 +1,65 @@ +#include "WeirdAnimation.h" + +#include "ResourceManager.h" +#include "Context.h" + +#include <cassert> +#include <cstring> +#include <spdlog/spdlog.h> + +extern "C" { +#include "z64math.h" +} + +const void* WeirdAnimation::GetFrame(s32 frame, s32 targetAnimationFrameCount) { + if (!animationData.has_value()) { + Build(); + } + + if (direction == IndexDirection::BACKWARD) { + const auto frameData = std::end(*animationData) + (((sizeof(Vec3s) * limbCount + sizeof(s16)) * frame)); + + return frameData >= std::begin(*animationData) ? &*frameData : nullptr; + } else if (direction == IndexDirection::FORWARD) { + const auto frameData = std::begin(*animationData) + + (((sizeof(Vec3s) * limbCount + sizeof(s16)) * (frame - targetAnimationFrameCount))); + + return frameData < std::end(*animationData) ? &*frameData : nullptr; + } else { + SPDLOG_ERROR("Expected valid IndexDirection, got: {}", static_cast<int>(direction)); + assert(false); + return nullptr; + } +} + +void WeirdAnimation::Build() { + auto& animation = animationData.emplace(); + + for (const auto& neighborName : neighborAnimations) { + const auto neighbor = Ship::Context::GetInstance()->GetResourceManager()->LoadResource(neighborName); + + const auto prevSize = animation.size(); + animation.resize(prevSize + neighbor->GetPointerSize()); + std::memcpy(animation.data() + prevSize, neighbor->GetRawPointer(), neighbor->GetPointerSize()); + + // Animation data in ROM is padded to 0x10 bytes. Align the buffer the same way. + animation.resize((animation.size() + 0xF) & ~0xF); + } + + const auto frameCount = animation.size() / (sizeof(Vec3s) * limbCount + sizeof(s16)); + const auto directionStr = [this] { + switch (direction) { + case IndexDirection::BACKWARD: + return "backward"; + case IndexDirection::FORWARD: + return "forward"; + default: + SPDLOG_ERROR("Expected valid IndexDirection, got: {}", static_cast<int>(direction)); + assert(false); + return "???"; + } + }(); + + SPDLOG_DEBUG("Weird animation built for \"{}\": frameCount = {}, direction = {}", targetAnimation, frameCount, + directionStr); +} diff --git a/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.h b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.h new file mode 100644 index 000000000..e8c353720 --- /dev/null +++ b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.h @@ -0,0 +1,43 @@ +#pragma once + +#include <libultraship/libultra/types.h> + +#include <cstddef> +#include <optional> +#include <string> +#include <utility> +#include <vector> + +enum class IndexDirection { + BACKWARD, + FORWARD, +}; + +class WeirdAnimation { + public: + WeirdAnimation(std::string targetAnimation, s32 limbCount, IndexDirection direction, + std::vector<std::string> neighborAnimations) + : targetAnimation(std::move(targetAnimation)), limbCount(limbCount), direction(direction), + neighborAnimations(std::move(neighborAnimations)) { + } + + const std::string& GetTargetAnimation() { + return targetAnimation; + } + + IndexDirection GetDirection() { + return direction; + } + + const void* GetFrame(s32 frame, s32 targetAnimationFrameCount); + + private: + std::string targetAnimation; + s32 limbCount; + IndexDirection direction; + std::vector<std::string> neighborAnimations; + + std::optional<std::vector<std::byte>> animationData; + + void Build(); +}; diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index bcc84f4bc..9828f7aeb 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -239,6 +239,7 @@ typedef enum { VB_START_JUMPSLASH, VB_SETUP_TRANSITION, VB_BE_NEAR_DOOR, + VB_LOAD_PLAYER_ANIMATION_FRAME, } GIVanillaBehavior; typedef enum { diff --git a/mm/src/code/z_skelanime.c b/mm/src/code/z_skelanime.c index 924d368f9..33a6365bc 100644 --- a/mm/src/code/z_skelanime.c +++ b/mm/src/code/z_skelanime.c @@ -3,6 +3,8 @@ #include <string.h> #include <stdio.h> +#include "2s2h/GameInteractor/GameInteractor.h" + #define ANIM_INTERP 1 s32 PlayerAnimation_Loop(PlayState* play, SkelAnime* skelAnime); @@ -1017,7 +1019,8 @@ void AnimationContext_SetLoadFrame(PlayState* play, PlayerAnimationHeader* anima Vec3s* frameTable) { AnimationEntry* entry = AnimationContext_AddEntry(&play->animationCtx, ANIMATION_LINKANIMETION); - if (entry != NULL) { + if (GameInteractor_Should(VB_LOAD_PLAYER_ANIMATION_FRAME, entry != NULL, entry, animation, frame, limbCount, + frameTable)) { if (ResourceMgr_OTRSigCheck(animation) != 0) animation = ResourceMgr_LoadAnimByName(animation); |
