244 lines
8.3 KiB
C++
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;
|
|
}
|