mirror of https://github.com/AxioDL/metaforce.git
1046 lines
36 KiB
C++
1046 lines
36 KiB
C++
#include "CAnimData.hpp"
|
|
#include "CCharacterInfo.hpp"
|
|
#include "CCharLayoutInfo.hpp"
|
|
#include "Graphics/CSkinnedModel.hpp"
|
|
#include "CCharacterFactory.hpp"
|
|
#include "CAnimationManager.hpp"
|
|
#include "CTransitionManager.hpp"
|
|
#include "CAdditiveAnimPlayback.hpp"
|
|
#include "CBoolPOINode.hpp"
|
|
#include "CInt32POINode.hpp"
|
|
#include "CParticlePOINode.hpp"
|
|
#include "CSoundPOINode.hpp"
|
|
#include "CParticleGenInfo.hpp"
|
|
#include "IAnimReader.hpp"
|
|
#include "CAnimTreeNode.hpp"
|
|
#include "CAnimPerSegmentData.hpp"
|
|
#include "CSegStatementSet.hpp"
|
|
#include "CStateManager.hpp"
|
|
#include "CAnimPlaybackParms.hpp"
|
|
#include "CAnimTreeBlend.hpp"
|
|
#include "CPrimitive.hpp"
|
|
#include "CAllFormatsAnimSource.hpp"
|
|
#include "GameGlobalObjects.hpp"
|
|
|
|
namespace urde
|
|
{
|
|
static logvisor::Module Log("CAnimData");
|
|
|
|
rstl::reserved_vector<CBoolPOINode, 8>CAnimData::g_BoolPOINodes;
|
|
rstl::reserved_vector<CInt32POINode, 16> CAnimData::g_Int32POINodes;
|
|
rstl::reserved_vector<CParticlePOINode, 20> CAnimData::g_ParticlePOINodes;
|
|
rstl::reserved_vector<CSoundPOINode, 20> CAnimData::g_SoundPOINodes;
|
|
rstl::reserved_vector<CInt32POINode, 16> CAnimData::g_TransientInt32POINodes;
|
|
|
|
void CAnimData::FreeCache()
|
|
{
|
|
}
|
|
|
|
void CAnimData::InitializeCache()
|
|
{
|
|
}
|
|
|
|
CAnimData::CAnimData(CAssetId id,
|
|
const CCharacterInfo& character,
|
|
int defaultAnim, int charIdx, bool loop,
|
|
const TLockedToken<CCharLayoutInfo>& layout,
|
|
const TToken<CSkinnedModel>& model,
|
|
const rstl::optional_object<TToken<CMorphableSkinnedModel>>& iceModel,
|
|
const std::weak_ptr<CAnimSysContext>& ctx,
|
|
const std::shared_ptr<CAnimationManager>& animMgr,
|
|
const std::shared_ptr<CTransitionManager>& transMgr,
|
|
const TLockedToken<CCharacterFactory>& charFactory,
|
|
int drawInstCount)
|
|
: x0_charFactory(charFactory),
|
|
xc_charInfo(character),
|
|
xcc_layoutData(layout),
|
|
xd8_modelData(model),
|
|
xfc_animCtx(ctx.lock()),
|
|
x100_animMgr(animMgr),
|
|
x1d8_selfId(id),
|
|
x1fc_transMgr(transMgr),
|
|
x204_charIdx(charIdx),
|
|
x208_defaultAnim(defaultAnim),
|
|
x224_pose(layout->GetSegIdList().GetList().size()),
|
|
x2fc_poseBuilder(layout),
|
|
m_drawInstCount(drawInstCount)
|
|
{
|
|
x220_25_loop = loop;
|
|
|
|
if (iceModel)
|
|
xe4_iceModelData = *iceModel;
|
|
|
|
g_BoolPOINodes.resize(8);
|
|
g_Int32POINodes.resize(16);
|
|
g_ParticlePOINodes.resize(20);
|
|
g_SoundPOINodes.resize(20);
|
|
g_TransientInt32POINodes.resize(16);
|
|
|
|
x108_aabb = xd8_modelData->GetModel()->GetAABB();
|
|
x120_particleDB.CacheParticleDesc(xc_charInfo.GetParticleResData());
|
|
|
|
CHierarchyPoseBuilder pb(xcc_layoutData);
|
|
pb.BuildNoScale(x224_pose);
|
|
x220_30_poseBuilt = true;
|
|
|
|
if (defaultAnim == -1)
|
|
{
|
|
defaultAnim = 0;
|
|
Log.report(logvisor::Warning, "Character %s has invalid initial animation, so defaulting to first.",
|
|
character.GetCharacterName().c_str());
|
|
}
|
|
|
|
std::shared_ptr<CAnimTreeNode> treeNode =
|
|
GetAnimationManager()->GetAnimationTree(character.GetAnimationIndex(defaultAnim),
|
|
CMetaAnimTreeBuildOrders::NoSpecialOrders());
|
|
if (treeNode != x1f8_animRoot)
|
|
x1f8_animRoot = treeNode;
|
|
}
|
|
|
|
CAssetId CAnimData::GetEventResourceIdForAnimResourceId(CAssetId id) const
|
|
{
|
|
return x0_charFactory->GetEventResourceIdForAnimResourceId(id);
|
|
}
|
|
|
|
void CAnimData::AddAdditiveSegData(const CSegIdList& list, CSegStatementSet& stSet)
|
|
{
|
|
for (std::pair<u32, CAdditiveAnimPlayback>& additive : x434_additiveAnims)
|
|
if (additive.second.GetTargetWeight() > 0.00001f)
|
|
additive.second.AddToSegStatementSet(list, *xcc_layoutData.GetObj(), stSet);
|
|
}
|
|
|
|
SAdvancementResults CAnimData::AdvanceAdditiveAnim(std::shared_ptr<CAnimTreeNode>& anim, const CCharAnimTime& time)
|
|
{
|
|
SAdvancementResults ret = anim->VAdvanceView(time);
|
|
std::pair<std::unique_ptr<IAnimReader>, bool> simplified = anim->Simplified();
|
|
if (simplified.second)
|
|
anim = std::static_pointer_cast<CAnimTreeNode>(std::shared_ptr<IAnimReader>(std::move(simplified.first)));
|
|
return ret;
|
|
}
|
|
|
|
SAdvancementDeltas CAnimData::AdvanceAdditiveAnims(float dt)
|
|
{
|
|
CCharAnimTime time(dt);
|
|
|
|
SAdvancementDeltas deltas = {};
|
|
|
|
for (std::pair<u32, CAdditiveAnimPlayback>& additive : x434_additiveAnims)
|
|
{
|
|
std::shared_ptr<CAnimTreeNode>& anim = additive.second.GetAnim();
|
|
if (additive.second.IsActive())
|
|
{
|
|
while (time.GreaterThanZero() && std::fabs(time.GetSeconds()) >= 0.00001f)
|
|
{
|
|
x210_passedIntCount += anim->GetInt32POIList(time, g_Int32POINodes.data(), 16, x210_passedIntCount, 0);
|
|
x20c_passedBoolCount += anim->GetBoolPOIList(time, g_BoolPOINodes.data(), 8, x20c_passedBoolCount, 0);
|
|
x214_passedParticleCount += anim->GetParticlePOIList(time, g_ParticlePOINodes.data(), 8, x214_passedParticleCount, 0);
|
|
x218_passedSoundCount += anim->GetSoundPOIList(time, g_SoundPOINodes.data(), 8, x218_passedSoundCount, 0);
|
|
|
|
SAdvancementResults results = AdvanceAdditiveAnim(anim, time);
|
|
deltas.x0_posDelta += results.x8_deltas.x0_posDelta;
|
|
deltas.xc_rotDelta *= results.x8_deltas.xc_rotDelta;
|
|
time = results.x0_remTime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CCharAnimTime remTime = anim->VGetTimeRemaining();
|
|
while (remTime.GreaterThanZero() && std::fabs(remTime.GetSeconds()) >= 0.00001f)
|
|
{
|
|
x210_passedIntCount += anim->GetInt32POIList(time, g_Int32POINodes.data(), 16, x210_passedIntCount, 0);
|
|
x20c_passedBoolCount += anim->GetBoolPOIList(time, g_BoolPOINodes.data(), 8, x20c_passedBoolCount, 0);
|
|
x214_passedParticleCount += anim->GetParticlePOIList(time, g_ParticlePOINodes.data(), 8, x214_passedParticleCount, 0);
|
|
x218_passedSoundCount += anim->GetSoundPOIList(time, g_SoundPOINodes.data(), 8, x218_passedSoundCount, 0);
|
|
|
|
SAdvancementResults results = AdvanceAdditiveAnim(anim, time);
|
|
deltas.x0_posDelta += results.x8_deltas.x0_posDelta;
|
|
deltas.xc_rotDelta *= results.x8_deltas.xc_rotDelta;
|
|
CCharAnimTime tmpTime = anim->VGetTimeRemaining();
|
|
if (tmpTime < results.x0_remTime)
|
|
remTime = tmpTime;
|
|
else
|
|
remTime = results.x0_remTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
return deltas;
|
|
}
|
|
|
|
SAdvancementDeltas CAnimData::UpdateAdditiveAnims(float dt)
|
|
{
|
|
for (auto it = x434_additiveAnims.begin() ; it != x434_additiveAnims.end() ;)
|
|
{
|
|
it->second.Update(dt);
|
|
CCharAnimTime timeRem = it->second.GetAnim()->VGetTimeRemaining();
|
|
if (timeRem.EpsilonZero() && it->second.Get20())
|
|
it->second.FadeOut();
|
|
if (it->second.GetPhase() == EAdditivePlaybackPhase::FadedOut)
|
|
{
|
|
it = x434_additiveAnims.erase(it);
|
|
continue;
|
|
}
|
|
++it;
|
|
}
|
|
|
|
return AdvanceAdditiveAnims(dt);
|
|
}
|
|
|
|
bool CAnimData::IsAdditiveAnimation(u32 idx) const
|
|
{
|
|
u32 animIdx = xc_charInfo.GetAnimationIndex(idx);
|
|
return x0_charFactory->HasAdditiveInfo(animIdx);
|
|
}
|
|
|
|
bool CAnimData::IsAdditiveAnimationAdded(u32 idx) const
|
|
{
|
|
auto search = std::find_if(x434_additiveAnims.cbegin(), x434_additiveAnims.cend(),
|
|
[&](const std::pair<u32, CAdditiveAnimPlayback>& pair) -> bool {
|
|
return pair.first == idx;
|
|
});
|
|
if (search == x434_additiveAnims.cend())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
const std::shared_ptr<CAnimTreeNode>& CAnimData::GetAdditiveAnimationTree(u32 idx) const
|
|
{
|
|
auto search = std::find_if(x434_additiveAnims.cbegin(), x434_additiveAnims.cend(),
|
|
[&](const std::pair<u32, CAdditiveAnimPlayback>& pair) -> bool {
|
|
return pair.first == idx;
|
|
});
|
|
return search->second.GetAnim();
|
|
}
|
|
|
|
bool CAnimData::IsAdditiveAnimationActive(u32 idx) const
|
|
{
|
|
auto search = std::find_if(x434_additiveAnims.cbegin(), x434_additiveAnims.cend(),
|
|
[&](const std::pair<u32, CAdditiveAnimPlayback>& pair) -> bool {
|
|
return pair.first == idx;
|
|
});
|
|
if (search == x434_additiveAnims.cend())
|
|
return false;
|
|
return search->second.IsActive();
|
|
}
|
|
|
|
void CAnimData::DelAdditiveAnimation(u32 idx)
|
|
{
|
|
u32 animIdx = xc_charInfo.GetAnimationIndex(idx);
|
|
for (std::pair<u32, CAdditiveAnimPlayback>& anim : x434_additiveAnims)
|
|
{
|
|
if (anim.first == animIdx &&
|
|
anim.second.GetPhase() != EAdditivePlaybackPhase::FadingOut &&
|
|
anim.second.GetPhase() != EAdditivePlaybackPhase::FadedOut)
|
|
{
|
|
anim.second.FadeOut();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CAnimData::AddAdditiveAnimation(u32 idx, float weight, bool active, bool b)
|
|
{
|
|
u32 animIdx = xc_charInfo.GetAnimationIndex(idx);
|
|
for (std::pair<u32, CAdditiveAnimPlayback>& anim : x434_additiveAnims)
|
|
{
|
|
if (anim.first == animIdx &&
|
|
anim.second.GetPhase() != EAdditivePlaybackPhase::FadingOut &&
|
|
anim.second.GetPhase() != EAdditivePlaybackPhase::FadedOut)
|
|
{
|
|
anim.second.SetActive(active);
|
|
anim.second.SetWeight(weight);
|
|
anim.second.Set20(!anim.second.IsActive() && b);
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<CAnimTreeNode> node =
|
|
GetAnimationManager()->GetAnimationTree(animIdx, CMetaAnimTreeBuildOrders::NoSpecialOrders());
|
|
|
|
const CAdditiveAnimationInfo& info = x0_charFactory->FindAdditiveInfo(animIdx);
|
|
x434_additiveAnims.emplace_back(std::make_pair(idx, CAdditiveAnimPlayback(node, weight, active, info, b)));
|
|
}
|
|
|
|
float CAnimData::GetAdditiveAnimationWeight(u32 idx) const
|
|
{
|
|
u32 animIdx = xc_charInfo.GetAnimationIndex(idx);
|
|
for (const std::pair<u32, CAdditiveAnimPlayback>& anim : x434_additiveAnims)
|
|
{
|
|
if (anim.first == animIdx)
|
|
return anim.second.GetTargetWeight();
|
|
}
|
|
return 0.f;
|
|
}
|
|
|
|
std::shared_ptr<CAnimationManager> CAnimData::GetAnimationManager()
|
|
{
|
|
return x100_animMgr;
|
|
}
|
|
|
|
void CAnimData::SetPhase(float ph)
|
|
{
|
|
x1f8_animRoot->VSetPhase(ph);
|
|
}
|
|
|
|
void CAnimData::Touch(const CSkinnedModel& model, int shadIdx) const
|
|
{
|
|
const_cast<CBooModel&>(*model.GetModelInst()).Touch(shadIdx);
|
|
}
|
|
|
|
SAdvancementDeltas CAnimData::GetAdvancementDeltas(const CCharAnimTime& a,
|
|
const CCharAnimTime& b) const
|
|
{
|
|
return x1f8_animRoot->VGetAdvancementResults(a, b).x8_deltas;
|
|
}
|
|
|
|
CCharAnimTime CAnimData::GetTimeOfUserEvent(EUserEventType type, const CCharAnimTime& time) const
|
|
{
|
|
u32 count = x1f8_animRoot->GetInt32POIList(time, g_TransientInt32POINodes.data(), 16, 0, 64);
|
|
for (int i=0 ; i<count ; ++i)
|
|
{
|
|
CInt32POINode& poi = g_TransientInt32POINodes[i];
|
|
if (poi.GetPoiType() == EPOIType::UserEvent &&
|
|
EUserEventType(poi.GetValue()) == type)
|
|
{
|
|
for (; i<count ; ++i)
|
|
g_TransientInt32POINodes[i] = CInt32POINode();
|
|
return poi.GetTime();
|
|
}
|
|
else
|
|
{
|
|
poi = CInt32POINode();
|
|
}
|
|
}
|
|
return CCharAnimTime::Infinity();
|
|
}
|
|
|
|
void CAnimData::MultiplyPlaybackRate(float mul)
|
|
{
|
|
x200_speedScale += mul;
|
|
}
|
|
|
|
void CAnimData::SetPlaybackRate(float set)
|
|
{
|
|
x200_speedScale = set;
|
|
}
|
|
|
|
void CAnimData::SetRandomPlaybackRate(CRandom16& r)
|
|
{
|
|
for (int i=0 ; i<x210_passedIntCount ; ++i)
|
|
{
|
|
CInt32POINode& poi = g_Int32POINodes[i];
|
|
if (poi.GetPoiType() == EPOIType::RandRate)
|
|
{
|
|
float tmp = (r.Next() % poi.GetValue()) / 100.f;
|
|
if ((r.Next() % 100) < 50)
|
|
x200_speedScale = 1.f + tmp;
|
|
else
|
|
x200_speedScale = 1.f - tmp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CAnimData::CalcPlaybackAlignmentParms(const CAnimPlaybackParms& parms,
|
|
const std::shared_ptr<CAnimTreeNode>& node)
|
|
{
|
|
zeus::CQuaternion orient;
|
|
x1e8_alignRot = zeus::CQuaternion::skNoRotation;
|
|
|
|
x220_27_ = false;
|
|
if (parms.GetDeltaOrient() && parms.GetObjectXform())
|
|
{
|
|
ResetPOILists();
|
|
x210_passedIntCount += node->GetInt32POIList(CCharAnimTime::Infinity(), g_Int32POINodes.data(),
|
|
16, x210_passedIntCount, 64);
|
|
for (int i=0 ; i<x210_passedIntCount ; ++i)
|
|
{
|
|
CInt32POINode& poi = g_Int32POINodes[i];
|
|
if (poi.GetPoiType() == EPOIType::UserEvent &&
|
|
EUserEventType(poi.GetValue()) == EUserEventType::AlignTargetRot)
|
|
{
|
|
SAdvancementResults res = node->VGetAdvancementResults(poi.GetTime(), 0.f);
|
|
orient = zeus::CQuaternion::slerp(zeus::CQuaternion::skNoRotation,
|
|
*parms.GetDeltaOrient() *
|
|
zeus::CQuaternion(parms.GetObjectXform()->buildMatrix3f().inverted()) *
|
|
res.x8_deltas.xc_rotDelta.inverse(),
|
|
1.f / (60.f * poi.GetTime().GetSeconds()));
|
|
x1e8_alignRot = orient;
|
|
x220_27_ = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!x220_27_)
|
|
{
|
|
bool didAlign = false;
|
|
bool didStart = false;
|
|
zeus::CVector3f posStart, posAlign;
|
|
CCharAnimTime timeStart, timeAlign;
|
|
if (parms.GetTargetPos() && parms.GetObjectXform())
|
|
{
|
|
ResetPOILists();
|
|
x210_passedIntCount += node->GetInt32POIList(CCharAnimTime::Infinity(), g_Int32POINodes.data(),
|
|
16, x210_passedIntCount, 64);
|
|
for (int i=0 ; i<x210_passedIntCount ; ++i)
|
|
{
|
|
CInt32POINode& poi = g_Int32POINodes[i];
|
|
if (poi.GetPoiType() == EPOIType::UserEvent)
|
|
{
|
|
if (EUserEventType(poi.GetValue()) == EUserEventType::AlignTargetPosStart)
|
|
{
|
|
didStart = true;
|
|
SAdvancementResults res = node->VGetAdvancementResults(poi.GetTime(), 0.f);
|
|
posStart = res.x8_deltas.x0_posDelta;
|
|
timeStart = poi.GetTime();
|
|
|
|
if (parms.GetIsUseLocator())
|
|
posStart += GetLocatorTransform(poi.GetLocatorName(), &poi.GetTime()).origin;
|
|
|
|
if (didAlign)
|
|
break;
|
|
}
|
|
else if (EUserEventType(poi.GetValue()) == EUserEventType::AlignTargetPos)
|
|
{
|
|
didAlign = true;
|
|
SAdvancementResults res = node->VGetAdvancementResults(poi.GetTime(), 0.f);
|
|
posAlign = res.x8_deltas.x0_posDelta;
|
|
timeAlign = poi.GetTime();
|
|
|
|
if (parms.GetIsUseLocator())
|
|
posAlign += GetLocatorTransform(poi.GetLocatorName(), &poi.GetTime()).origin;
|
|
|
|
if (didStart)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (didAlign && didStart)
|
|
{
|
|
zeus::CVector3f scaleStart = *parms.GetObjectScale() * posStart;
|
|
zeus::CVector3f scaleAlign = *parms.GetObjectScale() * posAlign;
|
|
x1dc_alignPos = (parms.GetObjectXform()->inverse() * *parms.GetTargetPos() - scaleStart -
|
|
(scaleAlign - scaleStart)) / *parms.GetObjectScale() *
|
|
(1.f / (timeAlign.GetSeconds() - timeStart.GetSeconds()));
|
|
x220_28_ = true;
|
|
x220_26_ = false;
|
|
}
|
|
else
|
|
{
|
|
x1dc_alignPos = zeus::CVector3f::skZero;
|
|
x220_28_ = false;
|
|
x220_26_ = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool didStart = false;
|
|
bool didAlign = false;
|
|
CCharAnimTime timeStart, timeAlign;
|
|
zeus::CVector3f startPos;
|
|
if (parms.GetTargetPos() && parms.GetObjectXform())
|
|
{
|
|
ResetPOILists();
|
|
x210_passedIntCount += node->GetInt32POIList(CCharAnimTime::Infinity(), g_Int32POINodes.data(),
|
|
16, x210_passedIntCount, 64);
|
|
for (int i=0 ; i<x210_passedIntCount ; ++i)
|
|
{
|
|
CInt32POINode& poi = g_Int32POINodes[i];
|
|
if (poi.GetPoiType() == EPOIType::UserEvent)
|
|
{
|
|
if (EUserEventType(poi.GetValue()) == EUserEventType::AlignTargetPosStart)
|
|
{
|
|
didStart = true;
|
|
timeStart = poi.GetTime();
|
|
if (didAlign)
|
|
break;
|
|
}
|
|
else if (EUserEventType(poi.GetValue()) == EUserEventType::AlignTargetPos)
|
|
{
|
|
didAlign = true;
|
|
timeAlign = poi.GetTime();
|
|
if (didStart)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (didAlign && didStart)
|
|
{
|
|
CCharAnimTime frameInterval(1.f / 60.f);
|
|
orient = zeus::CQuaternion::skNoRotation;
|
|
x1e8_alignRot = zeus::CQuaternion::skNoRotation;
|
|
x220_27_ = true;
|
|
CCharAnimTime time;
|
|
zeus::CVector3f pos;
|
|
zeus::CQuaternion quat;
|
|
bool foundStartPos = false;
|
|
while (time < timeAlign)
|
|
{
|
|
SAdvancementResults res = node->VGetAdvancementResults(frameInterval, time);
|
|
pos += quat.toTransform() * res.x8_deltas.x0_posDelta;
|
|
quat *= (res.x8_deltas.xc_rotDelta * orient);
|
|
if (!foundStartPos && time >= timeStart)
|
|
{
|
|
startPos = pos;
|
|
foundStartPos = true;
|
|
}
|
|
time += frameInterval;
|
|
}
|
|
zeus::CVector3f scaleStart = startPos * *parms.GetObjectScale();
|
|
zeus::CVector3f scaleAlign = pos * *parms.GetObjectScale();
|
|
x1dc_alignPos = (parms.GetObjectXform()->inverse() * *parms.GetTargetPos() - scaleStart -
|
|
(scaleAlign - scaleStart)) / *parms.GetObjectScale() *
|
|
(1.f / (timeAlign.GetSeconds() - timeStart.GetSeconds()));
|
|
x220_28_ = true;
|
|
x220_26_ = false;
|
|
}
|
|
else
|
|
{
|
|
x1dc_alignPos = zeus::CVector3f::skZero;
|
|
x220_28_ = false;
|
|
x220_26_ = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
x1dc_alignPos = zeus::CVector3f::skZero;
|
|
x220_28_ = false;
|
|
x220_26_ = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
zeus::CTransform CAnimData::GetLocatorTransform(CSegId id, const CCharAnimTime* time) const
|
|
{
|
|
if (id == 0xFF)
|
|
return {};
|
|
|
|
zeus::CTransform ret;
|
|
if (!x220_31_poseCached)
|
|
const_cast<CAnimData*>(this)->RecalcPoseBuilder(time);
|
|
|
|
if (!x220_30_poseBuilt)
|
|
x2fc_poseBuilder.BuildTransform(id, ret);
|
|
else
|
|
{
|
|
zeus::CMatrix3f rot = x224_pose.GetRotation(id);
|
|
zeus::CVector3f offset = x224_pose.GetOffset(id);
|
|
ret.setRotation(rot);
|
|
ret.origin = offset;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
zeus::CTransform CAnimData::GetLocatorTransform(const std::string& name, const CCharAnimTime* time) const
|
|
{
|
|
return GetLocatorTransform(xcc_layoutData->GetSegIdFromString(name), time);
|
|
}
|
|
|
|
bool CAnimData::IsAnimTimeRemaining(float rem, const std::string& name) const
|
|
{
|
|
if (!x1f8_animRoot)
|
|
return false;
|
|
return x1f8_animRoot->VGetTimeRemaining().GetSeconds() <= rem;
|
|
}
|
|
|
|
float CAnimData::GetAnimTimeRemaining(const std::string& name) const
|
|
{
|
|
float rem = x1f8_animRoot->VGetTimeRemaining().GetSeconds();
|
|
if (x200_speedScale)
|
|
return rem / x200_speedScale;
|
|
return rem;
|
|
}
|
|
|
|
float CAnimData::GetAnimationDuration(int animIn) const
|
|
{
|
|
std::shared_ptr<IMetaAnim> anim = x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(animIn));
|
|
std::set<CPrimitive> prims;
|
|
anim->GetUniquePrimitives(prims);
|
|
|
|
SObjectTag tag{FOURCC('ANIM'), 0};
|
|
float durAccum = 0.f;
|
|
for (const CPrimitive& prim : prims)
|
|
{
|
|
tag.id = prim.GetAnimResId();
|
|
TLockedToken<CAllFormatsAnimSource> animRes = xfc_animCtx->xc_store.GetObj(tag);
|
|
|
|
CCharAnimTime dur;
|
|
switch (animRes->GetFormat())
|
|
{
|
|
case EAnimFormat::Uncompressed:
|
|
default:
|
|
{
|
|
const CAnimSource& src = animRes->GetAsCAnimSource();
|
|
dur = src.GetDuration();
|
|
break;
|
|
}
|
|
case EAnimFormat::BitstreamCompressed:
|
|
case EAnimFormat::BitstreamCompressed24:
|
|
{
|
|
const CFBStreamedCompression& src = animRes->GetAsCFBStreamedCompression();
|
|
dur = src.GetAnimationDuration();
|
|
break;
|
|
}
|
|
}
|
|
|
|
durAccum += dur.GetSeconds();
|
|
}
|
|
|
|
if (anim->GetType() == EMetaAnimType::Random)
|
|
return durAccum / float(prims.size());
|
|
return durAccum;
|
|
}
|
|
|
|
std::shared_ptr<CAnimSysContext> CAnimData::GetAnimSysContext() const
|
|
{
|
|
return xfc_animCtx;
|
|
}
|
|
|
|
std::shared_ptr<CAnimationManager> CAnimData::GetAnimationManager() const
|
|
{
|
|
return x100_animMgr;
|
|
}
|
|
|
|
void CAnimData::RecalcPoseBuilder(const CCharAnimTime* time)
|
|
{
|
|
if (!x1f8_animRoot)
|
|
return;
|
|
|
|
const CSegIdList& segIdList = GetCharLayoutInfo().GetSegIdList();
|
|
CSegStatementSet segSet;
|
|
if (time)
|
|
x1f8_animRoot->VGetSegStatementSet(segIdList, segSet, *time);
|
|
else
|
|
x1f8_animRoot->VGetSegStatementSet(segIdList, segSet);
|
|
|
|
AddAdditiveSegData(segIdList, segSet);
|
|
for (const CSegId& id : segIdList.GetList())
|
|
{
|
|
if (id == 3)
|
|
continue;
|
|
CAnimPerSegmentData& segData = segSet[id];
|
|
if (segData.x1c_hasOffset)
|
|
x2fc_poseBuilder.Insert(id, segData.x0_rotation, segData.x10_offset);
|
|
else
|
|
x2fc_poseBuilder.Insert(id, segData.x0_rotation);
|
|
}
|
|
}
|
|
|
|
void CAnimData::RenderAuxiliary(const zeus::CFrustum& frustum) const
|
|
{
|
|
const_cast<CParticleDatabase&>(x120_particleDB).AddToRendererClipped(frustum);
|
|
}
|
|
|
|
void CAnimData::Render(CSkinnedModel& model, const CModelFlags& drawFlags,
|
|
const rstl::optional_object<CVertexMorphEffect>& morphEffect,
|
|
const float* morphMagnitudes)
|
|
{
|
|
SetupRender(model, drawFlags, morphEffect, morphMagnitudes);
|
|
DrawSkinnedModel(model, drawFlags);
|
|
}
|
|
|
|
void CAnimData::SetupRender(CSkinnedModel& model,
|
|
const CModelFlags& drawFlags,
|
|
const rstl::optional_object<CVertexMorphEffect>& morphEffect,
|
|
const float* morphMagnitudes)
|
|
{
|
|
if (!x220_30_poseBuilt)
|
|
{
|
|
x2fc_poseBuilder.BuildNoScale(x224_pose);
|
|
x220_30_poseBuilt = true;
|
|
}
|
|
PoseSkinnedModel(model, x224_pose, drawFlags, morphEffect, morphMagnitudes);
|
|
}
|
|
|
|
void CAnimData::DrawSkinnedModel(CSkinnedModel& model, const CModelFlags& flags)
|
|
{
|
|
model.Draw(flags);
|
|
}
|
|
|
|
void CAnimData::PreRender()
|
|
{
|
|
if (!x220_31_poseCached)
|
|
{
|
|
RecalcPoseBuilder(nullptr);
|
|
x220_31_poseCached = true;
|
|
x220_30_poseBuilt = false;
|
|
}
|
|
}
|
|
|
|
void CAnimData::BuildPose()
|
|
{
|
|
if (!x220_31_poseCached)
|
|
{
|
|
RecalcPoseBuilder(nullptr);
|
|
x220_31_poseCached = true;
|
|
x220_30_poseBuilt = false;
|
|
}
|
|
|
|
if (!x220_30_poseBuilt)
|
|
{
|
|
x2fc_poseBuilder.BuildNoScale(x224_pose);
|
|
x220_30_poseBuilt = true;
|
|
}
|
|
}
|
|
|
|
void CAnimData::PrimitiveSetToTokenVector(const std::set<CPrimitive>& primSet,
|
|
std::vector<CToken>& tokensOut, bool preLock)
|
|
{
|
|
tokensOut.reserve(primSet.size());
|
|
|
|
SObjectTag tag{FOURCC('ANIM'), 0};
|
|
for (const CPrimitive& prim : primSet)
|
|
{
|
|
tag.id = prim.GetAnimResId();
|
|
tokensOut.push_back(g_SimplePool->GetObj(tag));
|
|
if (preLock)
|
|
tokensOut.back().Lock();
|
|
}
|
|
}
|
|
|
|
void CAnimData::GetAnimationPrimitives(const CAnimPlaybackParms& parms, std::set<CPrimitive>& primsOut) const
|
|
{
|
|
std::shared_ptr<IMetaAnim> animA =
|
|
x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(parms.GetAnimationId()));
|
|
animA->GetUniquePrimitives(primsOut);
|
|
|
|
if (parms.GetSecondAnimationId() != -1)
|
|
{
|
|
std::shared_ptr<IMetaAnim> animB =
|
|
x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(parms.GetSecondAnimationId()));
|
|
animB->GetUniquePrimitives(primsOut);
|
|
}
|
|
}
|
|
|
|
void CAnimData::SetAnimation(const CAnimPlaybackParms& parms, bool noTrans)
|
|
{
|
|
if (parms.GetAnimationId() == x40c_playbackParms.GetAnimationId() ||
|
|
(parms.GetSecondAnimationId() == x40c_playbackParms.GetSecondAnimationId() &&
|
|
parms.GetBlendFactor() == x40c_playbackParms.GetBlendFactor() &&
|
|
parms.GetBlendFactor() != 1.f) ||
|
|
parms.GetSecondAnimationId() == -1)
|
|
{
|
|
if (x220_29_animationJustStarted)
|
|
return;
|
|
}
|
|
|
|
x40c_playbackParms.SetAnimationId(parms.GetAnimationId());
|
|
x40c_playbackParms.SetSecondAnimationId(parms.GetSecondAnimationId());
|
|
x40c_playbackParms.SetBlendFactor(parms.GetBlendFactor());
|
|
x200_speedScale = 1.f;
|
|
x208_defaultAnim = parms.GetAnimationId();
|
|
|
|
u32 animIdxA = xc_charInfo.GetAnimationIndex(parms.GetAnimationId());
|
|
|
|
ResetPOILists();
|
|
|
|
std::shared_ptr<CAnimTreeNode> blendNode;
|
|
if (parms.GetSecondAnimationId() != -1)
|
|
{
|
|
u32 animIdxB = xc_charInfo.GetAnimationIndex(parms.GetSecondAnimationId());
|
|
|
|
std::shared_ptr<CAnimTreeNode> treeA =
|
|
x100_animMgr->GetAnimationTree(animIdxA, CMetaAnimTreeBuildOrders::NoSpecialOrders());
|
|
std::shared_ptr<CAnimTreeNode> treeB =
|
|
x100_animMgr->GetAnimationTree(animIdxB, CMetaAnimTreeBuildOrders::NoSpecialOrders());
|
|
|
|
blendNode = std::make_shared<CAnimTreeBlend>(false, treeA, treeB, parms.GetBlendFactor(),
|
|
CAnimTreeBlend::CreatePrimitiveName(treeA, treeB,
|
|
parms.GetBlendFactor()));
|
|
}
|
|
else
|
|
{
|
|
blendNode = x100_animMgr->GetAnimationTree(animIdxA, CMetaAnimTreeBuildOrders::NoSpecialOrders());
|
|
}
|
|
|
|
if (!noTrans && x1f8_animRoot)
|
|
x1f8_animRoot = x1fc_transMgr->GetTransitionTree(x1f8_animRoot, blendNode);
|
|
else
|
|
x1f8_animRoot = blendNode;
|
|
|
|
x220_24_animating = parms.GetIsPlayAnimation();
|
|
CalcPlaybackAlignmentParms(parms, blendNode);
|
|
ResetPOILists();
|
|
x220_29_animationJustStarted = true;
|
|
}
|
|
|
|
SAdvancementDeltas CAnimData::DoAdvance(float dt, bool& suspendParticles, CRandom16& random, bool advTree)
|
|
{
|
|
suspendParticles = false;
|
|
|
|
zeus::CVector3f offsetPre, offsetPost;
|
|
zeus::CQuaternion quatPre, quatPost;
|
|
|
|
ResetPOILists();
|
|
float scaleDt = dt * x200_speedScale;
|
|
if (x2fc_poseBuilder.HasRoot())
|
|
{
|
|
SAdvancementDeltas deltas = UpdateAdditiveAnims(scaleDt);
|
|
offsetPre = deltas.x0_posDelta;
|
|
quatPre = deltas.xc_rotDelta;
|
|
}
|
|
|
|
if (!x220_24_animating)
|
|
{
|
|
suspendParticles = true;
|
|
return {};
|
|
}
|
|
|
|
if (x220_29_animationJustStarted)
|
|
{
|
|
x220_29_animationJustStarted = false;
|
|
suspendParticles = true;
|
|
}
|
|
|
|
if (advTree && x1f8_animRoot)
|
|
{
|
|
SetRandomPlaybackRate(random);
|
|
CCharAnimTime time(scaleDt);
|
|
if (x220_25_loop)
|
|
{
|
|
while (time.GreaterThanZero() && !time.EpsilonZero())
|
|
{
|
|
x210_passedIntCount += x1f8_animRoot->GetInt32POIList(time, g_Int32POINodes.data(), 16, x210_passedIntCount, 0);
|
|
x20c_passedBoolCount += x1f8_animRoot->GetBoolPOIList(time, g_BoolPOINodes.data(), 16, x20c_passedBoolCount, 0);
|
|
x214_passedParticleCount += x1f8_animRoot->GetParticlePOIList(time, g_ParticlePOINodes.data(), 16, x214_passedParticleCount, 0);
|
|
x218_passedSoundCount += x1f8_animRoot->GetSoundPOIList(time, g_SoundPOINodes.data(), 16, x218_passedSoundCount, 0);
|
|
AdvanceAnim(time, offsetPost, quatPost);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CCharAnimTime remTime = x1f8_animRoot->VGetTimeRemaining();
|
|
while (remTime.GreaterThanZero() && !remTime.EpsilonZero())
|
|
{
|
|
x210_passedIntCount += x1f8_animRoot->GetInt32POIList(time, g_Int32POINodes.data(), 16, x210_passedIntCount, 0);
|
|
x20c_passedBoolCount += x1f8_animRoot->GetBoolPOIList(time, g_BoolPOINodes.data(), 16, x20c_passedBoolCount, 0);
|
|
x214_passedParticleCount += x1f8_animRoot->GetParticlePOIList(time, g_ParticlePOINodes.data(), 16, x214_passedParticleCount, 0);
|
|
x218_passedSoundCount += x1f8_animRoot->GetSoundPOIList(time, g_SoundPOINodes.data(), 16, x218_passedSoundCount, 0);
|
|
AdvanceAnim(time, offsetPost, quatPost);
|
|
remTime = x1f8_animRoot->VGetTimeRemaining();
|
|
time = std::max(0.f, std::min(remTime.GetSeconds(), time.GetSeconds()));
|
|
if (remTime.EpsilonZero())
|
|
{
|
|
x220_24_animating = false;
|
|
x1dc_alignPos = zeus::CVector3f::skZero;
|
|
x220_28_ = false;
|
|
x220_26_ = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
x220_31_poseCached = false;
|
|
x220_30_poseBuilt = false;
|
|
}
|
|
|
|
return {offsetPost + offsetPre, quatPost * quatPre};
|
|
}
|
|
|
|
SAdvancementDeltas CAnimData::Advance(float dt, const zeus::CVector3f& scale,
|
|
CStateManager& stateMgr, TAreaId aid, bool advTree)
|
|
{
|
|
bool suspendParticles;
|
|
SAdvancementDeltas deltas = DoAdvance(dt, suspendParticles, *stateMgr.GetActiveRandom(), advTree);
|
|
if (suspendParticles)
|
|
x120_particleDB.SuspendAllActiveEffects(stateMgr);
|
|
|
|
for (CParticlePOINode& node : g_ParticlePOINodes)
|
|
{
|
|
if (node.GetCharacterIndex() == -1 || node.GetCharacterIndex() == x204_charIdx)
|
|
{
|
|
x120_particleDB.AddParticleEffect(node.GetString(), node.GetFlags(), node.GetParticleData(),
|
|
scale, stateMgr, aid, false, x21c_particleLightIdx);
|
|
}
|
|
}
|
|
|
|
return deltas;
|
|
}
|
|
|
|
SAdvancementDeltas CAnimData::AdvanceIgnoreParticles(float dt, CRandom16& random, bool advTree)
|
|
{
|
|
bool suspendParticles;
|
|
return DoAdvance(dt, suspendParticles, random, advTree);
|
|
}
|
|
|
|
void CAnimData::AdvanceAnim(CCharAnimTime& time, zeus::CVector3f& offset, zeus::CQuaternion& quat)
|
|
{
|
|
SAdvancementResults results;
|
|
std::pair<std::unique_ptr<IAnimReader>, bool> simplified = {};
|
|
|
|
if (x104_animDir == EAnimDir::Forward)
|
|
{
|
|
results = x1f8_animRoot->VAdvanceView(time);
|
|
simplified = x1f8_animRoot->Simplified();
|
|
}
|
|
|
|
if (simplified.second)
|
|
{
|
|
x1f8_animRoot = std::static_pointer_cast<CAnimTreeNode>(
|
|
std::shared_ptr<IAnimReader>(std::move(simplified.first)));
|
|
}
|
|
|
|
if ((x220_28_ || x220_27_) && x210_passedIntCount > 0)
|
|
{
|
|
for (CInt32POINode& node : g_Int32POINodes)
|
|
{
|
|
if (node.GetPoiType() == EPOIType::UserEvent)
|
|
{
|
|
switch (EUserEventType(node.GetValue()))
|
|
{
|
|
case EUserEventType::AlignTargetPosStart:
|
|
{
|
|
x220_26_ = true;
|
|
break;
|
|
}
|
|
case EUserEventType::AlignTargetPos:
|
|
{
|
|
x1dc_alignPos = zeus::CVector3f::skZero;
|
|
x220_28_ = false;
|
|
x220_26_ = false;
|
|
break;
|
|
}
|
|
case EUserEventType::AlignTargetRot:
|
|
{
|
|
x1e8_alignRot = zeus::CQuaternion::skNoRotation;
|
|
x220_27_ = false;
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
offset += results.x8_deltas.x0_posDelta;
|
|
if (x220_26_)
|
|
offset += x1dc_alignPos * time.GetSeconds();
|
|
|
|
zeus::CQuaternion rot = results.x8_deltas.xc_rotDelta * x1e8_alignRot;
|
|
quat = quat * rot;
|
|
x1dc_alignPos = rot.transform(x1dc_alignPos);
|
|
time = results.x0_remTime;
|
|
}
|
|
|
|
void CAnimData::SetXRayModel(const TLockedToken<CModel>& model, const TLockedToken<CSkinRules>& skinRules)
|
|
{
|
|
xf4_xrayModel = std::make_shared<CSkinnedModel>(model, skinRules, xd8_modelData->GetLayoutInfo(), 0, m_drawInstCount);
|
|
}
|
|
|
|
void CAnimData::SetInfraModel(const TLockedToken<CModel>& model, const TLockedToken<CSkinRules>& skinRules)
|
|
{
|
|
xf8_infraModel = std::make_shared<CSkinnedModel>(model, skinRules, xd8_modelData->GetLayoutInfo(), 0, m_drawInstCount);
|
|
}
|
|
|
|
void CAnimData::PoseSkinnedModel(CSkinnedModel& model, const CPoseAsTransforms& pose,
|
|
const CModelFlags& drawFlags,
|
|
const rstl::optional_object<CVertexMorphEffect>& morphEffect,
|
|
const float* morphMagnitudes)
|
|
{
|
|
model.Calculate(pose, drawFlags, morphEffect, morphMagnitudes);
|
|
}
|
|
|
|
void CAnimData::AdvanceParticles(const zeus::CTransform& xf, float dt,
|
|
const zeus::CVector3f& vec, CStateManager& stateMgr)
|
|
{
|
|
x120_particleDB.Update(dt, x224_pose, *xcc_layoutData, xf, vec, stateMgr);
|
|
}
|
|
|
|
float CAnimData::GetAverageVelocity(int animIn) const
|
|
{
|
|
std::shared_ptr<IMetaAnim> anim = x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(animIn));
|
|
std::set<CPrimitive> prims;
|
|
anim->GetUniquePrimitives(prims);
|
|
|
|
SObjectTag tag{FOURCC('ANIM'), 0};
|
|
float velAccum = 0.f;
|
|
float durAccum = 0.f;
|
|
for (const CPrimitive& prim : prims)
|
|
{
|
|
tag.id = prim.GetAnimResId();
|
|
TLockedToken<CAllFormatsAnimSource> animRes = xfc_animCtx->xc_store.GetObj(tag);
|
|
|
|
CCharAnimTime dur;
|
|
float avgVel;
|
|
switch (animRes->GetFormat())
|
|
{
|
|
case EAnimFormat::Uncompressed:
|
|
default:
|
|
{
|
|
const CAnimSource& src = animRes->GetAsCAnimSource();
|
|
dur = src.GetDuration();
|
|
avgVel = src.GetAverageVelocity();
|
|
break;
|
|
}
|
|
case EAnimFormat::BitstreamCompressed:
|
|
case EAnimFormat::BitstreamCompressed24:
|
|
{
|
|
const CFBStreamedCompression& src = animRes->GetAsCFBStreamedCompression();
|
|
dur = src.GetAnimationDuration();
|
|
avgVel = src.GetAverageVelocity();
|
|
break;
|
|
}
|
|
}
|
|
|
|
velAccum += dur.GetSeconds() * avgVel;
|
|
durAccum += dur.GetSeconds();
|
|
}
|
|
|
|
if (durAccum > 0.f)
|
|
return velAccum / durAccum;
|
|
return 0.f;
|
|
}
|
|
|
|
void CAnimData::ResetPOILists()
|
|
{
|
|
x20c_passedBoolCount = 0;
|
|
x210_passedIntCount = 0;
|
|
x214_passedParticleCount = 0;
|
|
x218_passedSoundCount = 0;
|
|
}
|
|
|
|
CSegId CAnimData::GetLocatorSegId(const std::string& name) const
|
|
{
|
|
return xcc_layoutData->GetSegIdFromString(name);
|
|
}
|
|
|
|
zeus::CAABox CAnimData::GetBoundingBox(const zeus::CTransform& xf) const
|
|
{
|
|
return GetBoundingBox().getTransformedAABox(xf);
|
|
}
|
|
|
|
zeus::CAABox CAnimData::GetBoundingBox() const
|
|
{
|
|
auto aabbList = xc_charInfo.GetAnimBBoxList();
|
|
if (aabbList.empty())
|
|
return x108_aabb;
|
|
|
|
CAnimTreeEffectiveContribution contrib = x1f8_animRoot->GetContributionOfHighestInfluence();
|
|
auto search = std::find_if(aabbList.cbegin(), aabbList.cend(),
|
|
[&](const std::pair<std::string, zeus::CAABox>& other) -> bool {
|
|
return contrib.x4_name == other.first;
|
|
});
|
|
if (search == aabbList.cend())
|
|
return x108_aabb;
|
|
|
|
return search->second;
|
|
}
|
|
|
|
void CAnimData::SubstituteModelData(const TCachedToken<CSkinnedModel>& model)
|
|
{
|
|
xd8_modelData = model;
|
|
x108_aabb = xd8_modelData->GetModel()->GetAABB();
|
|
}
|
|
|
|
void CAnimData::SetParticleCEXTValue(const std::string& name, int idx, float value)
|
|
{
|
|
auto search = std::find_if(xc_charInfo.x98_effects.begin(), xc_charInfo.x98_effects.end(),
|
|
[&name](const auto& v) { return v.first == name; });
|
|
if (search != xc_charInfo.x98_effects.end() && search->second.size())
|
|
x120_particleDB.SetCEXTValue(search->second.front().GetComponentName(), idx, value);
|
|
}
|
|
|
|
}
|