Implement CVertexMorphEffect & CSkinnedModelWithAvgNormals (prev. CMorphableSkinnedModel)

This commit is contained in:
Luke Street 2022-03-10 18:56:50 -05:00
parent ddc34e3a0c
commit d7dbec5be0
13 changed files with 101 additions and 72 deletions

View File

@ -41,7 +41,7 @@ void CAnimData::InitializeCache() {}
CAnimData::CAnimData(CAssetId id, const CCharacterInfo& character, int defaultAnim, int charIdx, bool loop,
TLockedToken<CCharLayoutInfo> layout, TToken<CSkinnedModel> model,
const std::optional<TToken<CMorphableSkinnedModel>>& iceModel,
const std::optional<TToken<CSkinnedModelWithAvgNormals>>& iceModel,
const std::weak_ptr<CAnimSysContext>& ctx, std::shared_ptr<CAnimationManager> animMgr,
std::shared_ptr<CTransitionManager> transMgr, TLockedToken<CCharacterFactory> charFactory)
: x0_charFactory(charFactory)
@ -543,20 +543,19 @@ void CAnimData::RecalcPoseBuilder(const CCharAnimTime* time) {
void CAnimData::RenderAuxiliary(const zeus::CFrustum& frustum) const { x120_particleDB.AddToRendererClipped(frustum); }
void CAnimData::Render(CSkinnedModel& model, const CModelFlags& drawFlags,
const std::optional<CVertexMorphEffect>& morphEffect, TVectorRef morphMagnitudes) {
SetupRender(model, morphEffect, morphMagnitudes);
void CAnimData::Render(CSkinnedModel& model, const CModelFlags& drawFlags, CVertexMorphEffect* morphEffect,
TConstVectorRef averagedNormals) {
SetupRender(model, morphEffect, averagedNormals);
DrawSkinnedModel(model, drawFlags);
}
void CAnimData::SetupRender(CSkinnedModel& model, const std::optional<CVertexMorphEffect>& morphEffect,
TVectorRef morphMagnitudes) {
void CAnimData::SetupRender(CSkinnedModel& model, CVertexMorphEffect* morphEffect, TConstVectorRef averagedNormals) {
OPTICK_EVENT();
if (!x220_30_poseBuilt) {
x2fc_poseBuilder.BuildNoScale(x224_pose);
x220_30_poseBuilt = true;
}
PoseSkinnedModel(model, x224_pose, morphEffect, morphMagnitudes);
PoseSkinnedModel(model, x224_pose, morphEffect, averagedNormals);
}
void CAnimData::DrawSkinnedModel(CSkinnedModel& model, const CModelFlags& flags) {
@ -805,9 +804,9 @@ void CAnimData::SetInfraModel(const TLockedToken<CModel>& model, const TLockedTo
xf8_infraModel = std::make_shared<CSkinnedModel>(model, skinRules, xd8_modelData->GetLayoutInfo());
}
void CAnimData::PoseSkinnedModel(CSkinnedModel& model, const CPoseAsTransforms& pose,
const std::optional<CVertexMorphEffect>& morphEffect, TVectorRef morphMagnitudes) {
model.Calculate(pose, morphEffect, morphMagnitudes, nullptr);
void CAnimData::PoseSkinnedModel(CSkinnedModel& model, const CPoseAsTransforms& pose, CVertexMorphEffect* morphEffect,
TConstVectorRef averagedNormals) {
model.Calculate(pose, morphEffect, averagedNormals, nullptr);
}
void CAnimData::AdvanceParticles(const zeus::CTransform& xf, float dt, const zeus::CVector3f& vec,

View File

@ -68,7 +68,7 @@ class CCharAnimTime;
class CCharLayoutInfo;
class CInt32POINode;
class CModel;
class CMorphableSkinnedModel;
class CSkinnedModelWithAvgNormals;
class CParticlePOINode;
class CPrimitive;
class CRandom16;
@ -102,7 +102,7 @@ private:
CCharacterInfo xc_charInfo;
TLockedToken<CCharLayoutInfo> xcc_layoutData;
TLockedToken<CSkinnedModel> xd8_modelData;
TLockedToken<CMorphableSkinnedModel> xe4_iceModelData;
TLockedToken<CSkinnedModelWithAvgNormals> xe4_iceModelData;
std::shared_ptr<CSkinnedModel> xf4_xrayModel;
std::shared_ptr<CSkinnedModel> xf8_infraModel;
std::shared_ptr<CAnimSysContext> xfc_animCtx;
@ -147,9 +147,9 @@ private:
public:
CAnimData(CAssetId, const CCharacterInfo& character, int defaultAnim, int charIdx, bool loop,
TLockedToken<CCharLayoutInfo> layout, TToken<CSkinnedModel> model,
const std::optional<TToken<CMorphableSkinnedModel>>& iceModel, const std::weak_ptr<CAnimSysContext>& ctx,
std::shared_ptr<CAnimationManager> animMgr, std::shared_ptr<CTransitionManager> transMgr,
TLockedToken<CCharacterFactory> charFactory);
const std::optional<TToken<CSkinnedModelWithAvgNormals>>& iceModel,
const std::weak_ptr<CAnimSysContext>& ctx, std::shared_ptr<CAnimationManager> animMgr,
std::shared_ptr<CTransitionManager> transMgr, TLockedToken<CCharacterFactory> charFactory);
void SetParticleEffectState(std::string_view effectName, bool active, CStateManager& mgr);
void InitializeEffects(CStateManager& mgr, TAreaId aId, const zeus::CVector3f& scale);
@ -194,10 +194,9 @@ public:
std::shared_ptr<CAnimationManager> GetAnimationManager() const;
void RecalcPoseBuilder(const CCharAnimTime* time);
void RenderAuxiliary(const zeus::CFrustum& frustum) const;
void Render(CSkinnedModel& model, const CModelFlags& drawFlags, const std::optional<CVertexMorphEffect>& morphEffect,
TVectorRef morphMagnitudes);
void SetupRender(CSkinnedModel& model, const std::optional<CVertexMorphEffect>& morphEffect,
TVectorRef morphMagnitudes);
void Render(CSkinnedModel& model, const CModelFlags& drawFlags, CVertexMorphEffect* morphEffect,
TConstVectorRef averagedNormals);
void SetupRender(CSkinnedModel& model, CVertexMorphEffect* morphEffect, TConstVectorRef averagedNormals);
static void DrawSkinnedModel(CSkinnedModel& model, const CModelFlags& flags);
void PreRender();
void BuildPose();
@ -218,8 +217,8 @@ public:
TLockedToken<CSkinnedModel>& GetModelData() { return xd8_modelData; }
const TLockedToken<CSkinnedModel>& GetModelData() const { return xd8_modelData; }
static void PoseSkinnedModel(CSkinnedModel& model, const CPoseAsTransforms& pose,
const std::optional<CVertexMorphEffect>& morphEffect, TVectorRef morphMagnitudes);
static void PoseSkinnedModel(CSkinnedModel& model, const CPoseAsTransforms& pose, CVertexMorphEffect* morphEffect,
TConstVectorRef averagedNormals);
void AdvanceParticles(const zeus::CTransform& xf, float dt, const zeus::CVector3f&, CStateManager& stateMgr);
float GetAverageVelocity(int animIn) const;
void ResetPOILists();
@ -243,8 +242,8 @@ public:
s32 GetCharacterIndex() const { return x204_charIdx; }
u16 GetDefaultAnimation() const { return x208_defaultAnim; }
TLockedToken<CMorphableSkinnedModel>& GetIceModel() { return xe4_iceModelData; }
const TLockedToken<CMorphableSkinnedModel>& GetIceModel() const { return xe4_iceModelData; }
TLockedToken<CSkinnedModelWithAvgNormals>& GetIceModel() { return xe4_iceModelData; }
const TLockedToken<CSkinnedModelWithAvgNormals>& GetIceModel() const { return xe4_iceModelData; }
void SetParticleLightIdx(s32 idx) { x21c_particleLightIdx = idx; }
void MarkPoseDirty() { x220_30_poseBuilt = false; }

View File

@ -27,7 +27,7 @@ CFactoryFnReturn CCharacterFactory::CDummyFactory::Build(const SObjectTag& tag,
return TToken<CSkinnedModel>::GetIObjObjectFor(std::make_unique<CSkinnedModel>(
*g_SimplePool, charInfo.GetModelId(), charInfo.GetSkinRulesId(), charInfo.GetCharLayoutInfoId()));
case 1:
return TToken<CSkinnedModel>::GetIObjObjectFor(std::make_unique<CMorphableSkinnedModel>(
return TToken<CSkinnedModel>::GetIObjObjectFor(std::make_unique<CSkinnedModelWithAvgNormals>(
*g_SimplePool, charInfo.GetIceModelId(), charInfo.GetIceSkinRulesId(), charInfo.GetCharLayoutInfoId()));
default:
break;
@ -82,7 +82,7 @@ std::unique_ptr<CAnimData> CCharacterFactory::CreateCharacter(int charIdx, bool
TToken<CSkinnedModel> skinnedModel = x70_cacheResPool.GetObj({FourCC(0u), charInfo.GetModelId()}, charParm);
std::optional<TToken<CMorphableSkinnedModel>> iceModel;
std::optional<TToken<CSkinnedModelWithAvgNormals>> iceModel;
if (charInfo.GetIceModelId().IsValid() && charInfo.GetIceSkinRulesId().IsValid()) {
iceModel.emplace(x70_cacheResPool.GetObj({FourCC(1u), charInfo.GetIceModelId()}, charParm));
}

View File

@ -293,7 +293,7 @@ void CModelData::RenderThermal(const zeus::CTransform& xf, const zeus::CColor& m
if (x10_animData) {
CSkinnedModel& model = PickAnimatedModel(EWhichModel::ThermalHot);
x10_animData->SetupRender(model, {}, nullptr);
x10_animData->SetupRender(model, nullptr, nullptr);
ThermalDraw(mulColor, addColor, flags);
} else {
auto& model = PickStaticModel(EWhichModel::ThermalHot);
@ -342,7 +342,7 @@ void CModelData::Render(EWhichModel which, const zeus::CTransform& xf, const CAc
}
if (x10_animData) {
x10_animData->Render(PickAnimatedModel(which), drawFlags, {}, nullptr);
x10_animData->Render(PickAnimatedModel(which), drawFlags, nullptr, nullptr);
} else {
// TODO supposed to be optional_object?
if (x1c_normalModel) {
@ -387,7 +387,7 @@ void CModelData::MultiLightingDraw(EWhichModel which, const zeus::CTransform& xf
CGraphics::SetModelMatrix(xf * zeus::CTransform::Scale(x0_scale));
if (x10_animData) {
auto& skinnedModel = PickAnimatedModel(which);
x10_animData->SetupRender(skinnedModel, {}, nullptr);
x10_animData->SetupRender(skinnedModel, nullptr, nullptr);
model = skinnedModel.GetModel().GetObj();
skinnedModel.DoDrawCallback(callback);
} else {
@ -407,7 +407,7 @@ void CModelData::MultiPassDraw(EWhichModel which, const zeus::CTransform& xf, co
}
if (x10_animData) {
auto& skinnedModel = PickAnimatedModel(which);
x10_animData->SetupRender(skinnedModel, {}, nullptr);
x10_animData->SetupRender(skinnedModel, nullptr, nullptr);
auto& model = *skinnedModel.GetModel();
skinnedModel.DoDrawCallback([&](auto positions, auto normals) {
for (int i = 0; i < count; ++i) {

View File

@ -36,8 +36,8 @@ void CSkinnedModel::AllocateStorage() {
}
}
void CSkinnedModel::Calculate(const CPoseAsTransforms& pose, const std::optional<CVertexMorphEffect>& morphEffect,
TConstVectorRef morphMagnitudes, SSkinningWorkspace* workspace) {
void CSkinnedModel::Calculate(const CPoseAsTransforms& pose, CVertexMorphEffect* morphEffect,
TConstVectorRef averagedNormals, SSkinningWorkspace* workspace) {
if (workspace == nullptr) {
if (x35_disableWorkspaces) {
x10_skinRules->BuildAccumulatedTransforms(pose, *x1c_layoutInfo);
@ -52,7 +52,7 @@ void CSkinnedModel::Calculate(const CPoseAsTransforms& pose, const std::optional
x10_skinRules->BuildNormals(x4_model->GetNormals(), &workspace->m_normalWorkspace);
if (morphEffect) {
morphEffect->MorphVertices(*workspace, morphMagnitudes, x10_skinRules, pose);
morphEffect->MorphVertices(*workspace, averagedNormals, x10_skinRules, pose, x10_skinRules->GetVertexCount());
}
if (g_PointGenFunc != nullptr) {
g_PointGenFunc(*workspace);
@ -93,13 +93,13 @@ void CSkinnedModel::DoDrawCallback(const FCustomDraw& func) const {
}
}
CMorphableSkinnedModel::CMorphableSkinnedModel(IObjectStore& store, CAssetId model, CAssetId skinRules,
CSkinnedModelWithAvgNormals::CSkinnedModelWithAvgNormals(IObjectStore& store, CAssetId model, CAssetId skinRules,
CAssetId layoutInfo)
: CSkinnedModel(store, model, skinRules, layoutInfo) {
const auto vertexCount = GetSkinRules()->GetVertexCount();
const auto& modelPositions = *GetModel()->GetPositions();
x40_morphMagnitudes.resize(vertexCount);
x40_averagedNormals.resize(vertexCount);
std::vector<std::pair<zeus::CVector3f, std::list<u32>>> vertMap;
for (int vertIdx = 0; vertIdx < vertexCount; ++vertIdx) {
const auto curPos = modelPositions[vertIdx];
@ -123,7 +123,7 @@ CMorphableSkinnedModel::CMorphableSkinnedModel(IObjectStore& store, CAssetId mod
}
averagedNormal.normalize();
for (const auto idx : idxs) {
x40_morphMagnitudes[idx] = averagedNormal;
x40_averagedNormals[idx] = averagedNormal;
}
}
}

View File

@ -60,8 +60,11 @@ public:
const TLockedToken<CCharLayoutInfo>& GetLayoutInfo() const { return x1c_layoutInfo; }
void AllocateStorage();
void Calculate(const CPoseAsTransforms& pose, const std::optional<CVertexMorphEffect>& morphEffect,
TConstVectorRef averagedNormals, SSkinningWorkspace* workspace);
// Metaforce addition: Originally morphEffect is rstl::optional_object<CVertexMorphEffect>*
// This prevents constructing it as a reference to the held pointer in CPatterned, thus in
// retail it's copied in every invocation of RenderIceModelWithFlags.
void Calculate(const CPoseAsTransforms& pose, CVertexMorphEffect* morphEffect, TConstVectorRef averagedNormals,
SSkinningWorkspace* workspace);
void Draw(TConstVectorRef verts, TConstVectorRef normals, const CModelFlags& drawFlags);
void Draw(const CModelFlags& drawFlags);
void DoDrawCallback(const FCustomDraw& func) const;
@ -71,13 +74,12 @@ public:
static FPointGenerator g_PointGenFunc;
};
class CMorphableSkinnedModel : public CSkinnedModel {
std::vector<zeus::CVector3f> x40_morphMagnitudes; // was rstl::auto_ptr<float[]>
class CSkinnedModelWithAvgNormals : public CSkinnedModel {
std::vector<zeus::CVector3f> x40_averagedNormals; // was rstl::auto_ptr<float[]>
public:
CMorphableSkinnedModel(IObjectStore& store, CAssetId model, CAssetId skinRules, CAssetId layoutInfo);
TConstVectorRef GetMorphMagnitudes() const { return &x40_morphMagnitudes; }
TVectorRef GetMorphMagnitudes() { return &x40_morphMagnitudes; }
CSkinnedModelWithAvgNormals(IObjectStore& store, CAssetId model, CAssetId skinRules, CAssetId layoutInfo);
TConstVectorRef GetAveragedNormals() const { return &x40_averagedNormals; }
};
} // namespace metaforce

View File

@ -1,15 +1,46 @@
#include "Runtime/Graphics/CVertexMorphEffect.hpp"
#include "Runtime/Character/CSkinRules.hpp"
#include "Runtime/Graphics/CSkinnedModel.hpp"
namespace metaforce {
CVertexMorphEffect::CVertexMorphEffect(const zeus::CUnitVector3f& v1, const zeus::CVector3f& v2, float diagExtent,
float f2, CRandom16& random)
: x0_(v1), x20_diagExtent(diagExtent), x24_random(random) {}
CVertexMorphEffect::CVertexMorphEffect(const zeus::CUnitVector3f& dir, const zeus::CVector3f& pos, float duration,
float diagExtent, CRandom16& random)
: x0_dir(dir), xc_pos(pos), x18_duration(duration), x20_diagExtent(diagExtent), x24_random(random) {}
void CVertexMorphEffect::MorphVertices(SSkinningWorkspace& workspace, TConstVectorRef magnitudes,
const TLockedToken<CSkinRules>& skinRules, const CPoseAsTransforms& pose) const {
void CVertexMorphEffect::MorphVertices(SSkinningWorkspace& workspace, TConstVectorRef averagedNormals,
TLockedToken<CSkinRules>& skinRules, const CPoseAsTransforms& pose,
u32 vertexCount) {
if (x28_indices.empty()) {
std::vector<zeus::CVector3f> normalsOut;
normalsOut.reserve(vertexCount);
skinRules->BuildNormals(averagedNormals, &normalsOut);
for (int i = 0; i < vertexCount; ++i) {
float dist = normalsOut[i].dot(x0_dir);
if (dist > 0.5f) {
x28_indices.emplace_back(i);
const auto vert = workspace.m_vertexWorkspace[i];
const auto length = vert.x() + vert.y() + vert.z();
x38_floats.emplace_back((length - std::trunc(length)) * (dist - 0.5f));
}
}
}
for (int i = 0; i < x28_indices.size(); ++i) {
const auto scale = x1c_elapsed / x18_duration;
workspace.m_vertexWorkspace[x28_indices[i]] += scale * x20_diagExtent * x38_floats[i] * x0_dir;
}
}
void CVertexMorphEffect::Reset(const zeus::CVector3f& dir, const zeus::CVector3f& pos, float duration) {
x0_dir = dir;
xc_pos = pos;
x18_duration = duration;
x1c_elapsed = 0.f;
x28_indices.clear();
x38_floats.clear();
}
void CVertexMorphEffect::Update(float dt) { x1c_elapsed = std::min(x1c_elapsed + dt, x18_duration); }
} // namespace metaforce

View File

@ -16,24 +16,22 @@ class CSkinRules;
struct SSkinningWorkspace;
class CVertexMorphEffect {
zeus::CUnitVector3f x0_;
float xc_ = 0.f;
float x10_ = 0.f;
float x14_ = 0.f;
float x18_ = 0.f;
float x1c_ = 0.f;
zeus::CUnitVector3f x0_dir;
zeus::CVector3f xc_pos;
float x18_duration;
float x1c_elapsed = 0.f;
float x20_diagExtent;
CRandom16& x24_random;
std::vector<u32> x28_;
std::vector<u32> x38_;
std::vector<u32> x28_indices;
std::vector<float> x38_floats;
public:
CVertexMorphEffect(const zeus::CUnitVector3f& v1, const zeus::CVector3f& v2, float diagExtent, float f2,
CVertexMorphEffect(const zeus::CUnitVector3f& dir, const zeus::CVector3f& pos, float duration, float diagExtent,
CRandom16& random);
void MorphVertices(SSkinningWorkspace& workspace, TConstVectorRef magnitudes,
const TLockedToken<CSkinRules>& skinRules, const CPoseAsTransforms& pose) const;
void Reset(const zeus::CVector3f& dir, const zeus::CVector3f& pos, float duration) {}
void Update(float) {}
void MorphVertices(SSkinningWorkspace& workspace, TConstVectorRef averagedNormals,
TLockedToken<CSkinRules>& skinRules, const CPoseAsTransforms& pose, u32 vertexCount);
void Reset(const zeus::CVector3f& dir, const zeus::CVector3f& pos, float duration);
void Update(float dt);
};
} // namespace metaforce

View File

@ -643,7 +643,7 @@ void COmegaPirate::Render(CStateManager& mgr) {
if (mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::XRay && xa2c_skeletonAlpha > 0.f) {
const CModelFlags flags{5, 0, 3, zeus::CColor{1.f, xa2c_skeletonAlpha}};
animData->Render(x9f0_skeletonModel, flags, std::nullopt, nullptr);
animData->Render(x9f0_skeletonModel, flags, nullptr, nullptr);
}
if (x9a0_visible) {
bool isXRay = mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::XRay;
@ -651,7 +651,7 @@ void COmegaPirate::Render(CStateManager& mgr) {
g_Renderer->SetWorldFog(ERglFogMode::None, 0.f, 1.f, zeus::skBlack);
const CModelFlags flags{5, 0, 1, zeus::CColor{1.f, 0.2f}};
auto& model = *animData->GetModelData().GetObj();
animData->Render(model, flags, std::nullopt, nullptr);
animData->Render(model, flags, nullptr, nullptr);
}
CPatterned::Render(mgr);
if (isXRay) {

View File

@ -586,7 +586,7 @@ void CFishCloud::RenderBoid(int idx, const CBoid& boid, u32& drawMask, bool ther
constexpr CModelFlags thermFlags(0, 0, 3, zeus::skWhite);
mData.RenderThermal(zeus::skWhite, zeus::CColor(0.f, 0.25f), thermFlags);
} else {
mData.GetAnimationData()->Render(model, flags, std::nullopt, nullptr);
mData.GetAnimationData()->Render(model, flags, nullptr, nullptr);
}
}

View File

@ -123,7 +123,7 @@ void CPatterned::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CState
if (HasModelData() && GetModelData()->HasAnimData() && GetModelData()->GetAnimationData()->GetIceModel()) {
const auto& baseAABB = GetBaseBoundingBox();
float diagExtent = (baseAABB.max - baseAABB.min).magnitude() * 0.5f;
x510_vertexMorph = std::make_shared<CVertexMorphEffect>(zeus::skRight, zeus::CVector3f{}, diagExtent, 0.f,
x510_vertexMorph = std::make_shared<CVertexMorphEffect>(zeus::skRight, zeus::CVector3f{}, 0.f, diagExtent,
*mgr.GetActiveRandom());
}
@ -1697,8 +1697,8 @@ void CPatterned::RenderIceModelWithFlags(const CModelFlags& flags) const {
CModelFlags useFlags = flags;
useFlags.x1_matSetIdx = 0;
CAnimData* animData = x64_modelData->GetAnimationData();
if (CMorphableSkinnedModel* iceModel = animData->GetIceModel().GetObj()) {
animData->Render(*iceModel, useFlags, {*x510_vertexMorph}, iceModel->GetMorphMagnitudes());
if (CSkinnedModelWithAvgNormals* iceModel = animData->GetIceModel().GetObj()) {
animData->Render(*iceModel, useFlags, x510_vertexMorph.get(), iceModel->GetAveragedNormals());
}
}

View File

@ -416,13 +416,13 @@ void CSnakeWeedSwarm::RenderBoid(u32 idx, const CBoid& boid, u32& posesToBuild)
if (posesToBuild & 1 << modelIdx) {
posesToBuild &= ~(1 << modelIdx);
animData.BuildPose();
model.Calculate(animData.GetPose(), std::nullopt, nullptr, nullptr); // TODO x178_workspaces[modelIdx]
model.Calculate(animData.GetPose(), nullptr, nullptr, nullptr); // TODO x178_workspaces[modelIdx]
}
CGraphics::SetModelMatrix(
zeus::CTransform::Translate(boid.GetPosition() - zeus::CVector3f(0.f, 0.f, boid.GetZOffset())) *
zeus::CTransform::Scale(boid.GetScale()));
constexpr CModelFlags useFlags{0, 0, 3, zeus::skWhite};
animData.Render(model, useFlags, std::nullopt, nullptr);
animData.Render(model, useFlags, nullptr, nullptr);
}
void CSnakeWeedSwarm::ApplyRadiusDamage(const zeus::CVector3f& pos, const CDamageInfo& info, CStateManager& mgr) {

View File

@ -1057,7 +1057,7 @@ void CWallCrawlerSwarm::RenderBoid(const CBoid* boid, u32& drawMask, bool therma
CGraphics::SetModelMatrix(boid->GetTransform());
if (boid->x48_timeToDie > 0.f && !thermalHot) {
constexpr CModelFlags useFlags(0, 0, 3, zeus::skWhite);
mData.GetAnimationData()->Render(model, useFlags, std::nullopt, nullptr);
mData.GetAnimationData()->Render(model, useFlags, nullptr, nullptr);
if (auto iceModel = mData.GetAnimationData()->GetIceModel()) {
// if (!iceModel->GetModelInst()->TryLockTextures()) {
// return;
@ -1066,13 +1066,13 @@ void CWallCrawlerSwarm::RenderBoid(const CBoid* boid, u32& drawMask, bool therma
const float alpha = 1.f - boid->x48_timeToDie;
const zeus::CColor color(1.f, alpha > 0.f ? boid->x48_timeToDie : 1.f);
const CModelFlags iceFlags(5, 0, 3, color);
mData.GetAnimationData()->Render(*iceModel, iceFlags, std::nullopt, nullptr);
mData.GetAnimationData()->Render(*iceModel, iceFlags, nullptr, nullptr);
}
} else if (thermalHot) {
constexpr CModelFlags thermFlags(5, 0, 3, zeus::skWhite);
mData.RenderThermal(zeus::skWhite, zeus::CColor(0.f, 0.25f), thermFlags);
} else {
mData.GetAnimationData()->Render(model, flags, std::nullopt, nullptr);
mData.GetAnimationData()->Render(model, flags, nullptr, nullptr);
}
}