Files

244 lines
8.3 KiB
C++

#include "gamelib/animstack/animator_base.h"
#include "idlib/lib_print.h"
#include <cmath>
// 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<int, gameTimeUnique_t> ConvertBlendTime(
const idGameTimeManager* gameTimeManager, const int milliseconds) {
return idTypesafeNumber<int, gameTimeUnique_t>(
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<std::uint8_t>(parameters.blendOp);
branch->originBlend =
static_cast<std::uint8_t>(parameters.originBlend);
branch->currentAlpha = parameters.alpha;
branch->targetAlpha = parameters.alpha;
branch->alphaRate = 0.0f;
branch->filterGroup =
static_cast<std::uint8_t>(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<int, gameTimeUnique_t> 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<float>(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<md6WeightGroup_t>(
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<int, gameTimeUnique_t> currentTime) {
InternalPause(stack, currentTime);
}
void idAnimator_Base::Unpause(const idAnimStack* const stack,
const idTypesafeNumber<int, gameTimeUnique_t> 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<int, gameTimeUnique_t> 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<float>(GameLib_GetGameTicksPerSecond()) /
static_cast<float>(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<int, gameTimeUnique_t> 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<float>(GameLib_GetGameTicksPerSecond()) /
static_cast<float>(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;
}