#include "gamelib/animstack/animator_base.h" #include "idlib/lib_print.h" #include // idAnimStack and idGameTimeManager are not yet safe to materialize from the // generated declarations. These named boundaries preserve every recovered // animator-base call while their authoritative translation units are ported. int GameLib_GetAnimatorIndex(const idAnimStack* stack, const idAnimator_Base* animator); void GameLib_AddAnimator(idAnimStack* stack, idAnimator_Base* animator); void GameLib_RemoveAnimator(idAnimStack* stack, idAnimator_Base* animator); void GameLib_SetAnimatorFlag(idAnimStack* stack, idAnimator_Base* animator, int flag); void GameLib_ClearAnimatorFlag(idAnimStack* stack, idAnimator_Base* animator, int flag); bool GameLib_IsAnimatorFlagSet(const idAnimStack* stack, const idAnimator_Base* animator, int flag); int GameLib_ConvertRealMillisecondsToGameTime( const idGameTimeManager* gameTimeManager, int milliseconds); int GameLib_GetGameTicksPerSecond(); bool GameLib_IsMD6NodeValid(const idMD6Node* node); namespace { enum animatorFlag_t { ANIMATORFLAG_INITIALIZED = 0x01, ANIMATORFLAG_ENABLED = 0x02, ANIMATORFLAG_SERIALIZE = 0x08 }; bool IsBlendFinished(const idMD6Branch& branch) { return branch.alphaRate == 0.0f || branch.currentAlpha == branch.targetAlpha; } bool IsBlendingOut(const idMD6Branch& branch) { return branch.currentAlpha > 0.0f && branch.targetAlpha == 0.0f && branch.alphaRate != 0.0f; } bool IsBlendingIn(const idMD6Branch& branch) { return branch.currentAlpha < 1.0f && branch.targetAlpha >= 1.0f && branch.alphaRate != 0.0f; } idTypesafeNumber ConvertBlendTime( const idGameTimeManager* gameTimeManager, const int milliseconds) { return idTypesafeNumber( GameLib_ConvertRealMillisecondsToGameTime( gameTimeManager, milliseconds)); } } // namespace idAnimator_Base::idAnimator_Base() : gametimeManager{nullptr} , weightGroup(MD6_WEIGHTGROUP_ALL) , filterGroup(MD6_WEIGHTGROUP_ALL) , serializeProps{{nullptr, nullptr}, nullptr, nullptr, false} , initialized(false) { } idAnimator_Base::~idAnimator_Base() = default; bool idAnimator_Base::InternalPostInit( const idAnimatorParms_Base& parameters) { if (parameters.animStack == nullptr) { return false; } GameLib_SetAnimatorFlag(parameters.animStack, this, ANIMATORFLAG_SERIALIZE); return true; } void idAnimator_Base::Shutdown(idAnimStack* const stack) { InternalShutdown(stack); GameLib_ClearAnimatorFlag(stack, this, ANIMATORFLAG_INITIALIZED); GameLib_RemoveAnimator(stack, this); } bool idAnimator_Base::IsInitialized(idAnimStack* const stack) const { return stack != nullptr && GameLib_IsAnimatorFlagSet( stack, this, ANIMATORFLAG_INITIALIZED); } bool idAnimator_Base::IsEnabled(idAnimStack* const stack) const { return stack != nullptr && GameLib_IsAnimatorFlagSet( stack, this, ANIMATORFLAG_ENABLED); } void idAnimator_Base::SetEnabled(idAnimStack* const stack, const bool enabled) { if (enabled) { GameLib_SetAnimatorFlag(stack, this, ANIMATORFLAG_ENABLED); } else { GameLib_ClearAnimatorFlag(stack, this, ANIMATORFLAG_ENABLED); } } bool idAnimator_Base::Init(idGameTimeManager* const gameTimeManager, const idAnimatorParms_Base& parameters) { if (GameLib_GetAnimatorIndex(parameters.animStack, this) >= 0 && initialized) { idLibPrint::Error("Attempted to add idAnimator %s twice.", parameters.name.c_str()); } gametimeManager.gameTimeManager = gameTimeManager; weightGroup = parameters.weightGroup; filterGroup = parameters.filterGroup; if (!InternalInit(parameters)) { return false; } idMD6Branch* const branch = InternalGetMergeBranch(); if (branch != nullptr) { branch->op = static_cast(parameters.blendOp); branch->originBlend = static_cast(parameters.originBlend); branch->currentAlpha = parameters.alpha; branch->targetAlpha = parameters.alpha; branch->alphaRate = 0.0f; branch->filterGroup = static_cast(parameters.filterGroup); } GameLib_AddAnimator(parameters.animStack, this); GameLib_SetAnimatorFlag(parameters.animStack, this, ANIMATORFLAG_INITIALIZED); GameLib_SetAnimatorFlag(parameters.animStack, this, ANIMATORFLAG_ENABLED); initialized = true; return InternalPostInit(parameters); } void idAnimator_Base::Blend(const idAnimStack* const stack, const int currentTime, const float targetAlpha, const int blendDurationMS, const bool reset) { idMD6Branch* const branch = InternalGetMergeBranch(); branch->targetAlpha = targetAlpha; const idTypesafeNumber blendTime = ConvertBlendTime(gametimeManager.gameTimeManager, blendDurationMS); const bool alreadyBlending = targetAlpha <= branch->currentAlpha ? IsBlendingOut(*branch) : IsBlendingIn(*branch); if (blendTime.value <= 0) { branch->currentAlpha = targetAlpha; branch->alphaRate = 0.0f; } else if (reset || !alreadyBlending) { branch->alphaRate = std::fabs(targetAlpha - branch->currentAlpha) * (1000.0f / static_cast(blendTime.value)); } InternalBlend(stack, currentTime, targetAlpha, blendTime); } bool idAnimator_Base::IsContributing() const { const idMD6Branch* const branch = InternalGetMergeBranch(); return branch != nullptr && branch->right != nullptr && GameLib_IsMD6NodeValid(branch->right) && (branch->currentAlpha > 0.0f || !IsBlendFinished(*branch)) && InternalIsContributing(); } md6WeightGroup_t idAnimator_Base::GetFilterGroup() const { return static_cast( InternalGetMergeBranch()->filterGroup); } float idAnimator_Base::GetAlpha() const { return InternalGetMergeBranch()->currentAlpha; } void idAnimator_Base::SetAlpha(const float alpha) { InternalGetMergeBranch()->currentAlpha = alpha; } void idAnimator_Base::Pause(const idAnimStack* const stack, const idTypesafeNumber currentTime) { InternalPause(stack, currentTime); } void idAnimator_Base::Unpause(const idAnimStack* const stack, const idTypesafeNumber currentTime) { InternalUnpause(stack, currentTime); } void idAnimator_Base::Start(const idAnimStack* const stack, const int currentTime, const int blendDurationMS, const bool reset) { idMD6Branch* const branch = InternalGetMergeBranch(); const idTypesafeNumber blendTime = ConvertBlendTime(gametimeManager.gameTimeManager, blendDurationMS); if (blendTime.value <= 0) { branch->currentAlpha = 1.0f; branch->targetAlpha = 1.0f; branch->alphaRate = 0.0f; } else if (reset || !IsBlendingIn(*branch)) { branch->targetAlpha = 1.0f; branch->alphaRate = (1.0f - branch->currentAlpha) * (static_cast(GameLib_GetGameTicksPerSecond()) / static_cast(blendTime.value)); } InternalStart(stack, currentTime, blendTime); } void idAnimator_Base::End(const idAnimStack* const stack, const int currentTime, const int blendDurationMS, const bool reset) { if (!initialized) { return; } idMD6Branch* const branch = InternalGetMergeBranch(); branch->targetAlpha = 0.0f; const idTypesafeNumber blendTime = ConvertBlendTime(gametimeManager.gameTimeManager, blendDurationMS); if (blendTime.value <= 0) { branch->alphaRate = 0.0f; branch->currentAlpha = 0.0f; } else if (reset || !IsBlendingOut(*branch)) { branch->alphaRate = branch->currentAlpha * (static_cast(GameLib_GetGameTicksPerSecond()) / static_cast(blendTime.value)); } InternalEnd(stack, currentTime, blendTime); } void idAnimator_Base::PreSerializeInit(idAnimStack* const stack, idClip* const clip, idGameTimeManager* const gameTimeManager) { serializeProps.animStack = stack; serializeProps.clip = clip; gametimeManager.gameTimeManager = gameTimeManager; serializeProps.createdThroughSerialization = true; }