#include "MetroidPrime/CActor.hpp" #include "MetaRender/CCubeRenderer.hpp" #include "MetroidPrime/CActorLights.hpp" #include "MetroidPrime/CActorParameters.hpp" #include "MetroidPrime/CAnimData.hpp" #include "MetroidPrime/CGameArea.hpp" #include "MetroidPrime/CModelData.hpp" #include "MetroidPrime/CSimpleShadow.hpp" #include "MetroidPrime/CStateManager.hpp" #include "MetroidPrime/CWorld.hpp" #include "MetroidPrime/Cameras/CCameraManager.hpp" #include "MetroidPrime/Cameras/CGameCamera.hpp" #include "MetroidPrime/Player/CPlayerState.hpp" #include "Kyoto/Audio/CAudioSys.hpp" #include "Kyoto/Audio/CSfxManager.hpp" #include "Kyoto/Graphics/CGraphics.hpp" #include "Kyoto/Math/CFrustumPlanes.hpp" static CMaterialList MakeActorMaterialList(const CMaterialList& in, const CActorParameters& params) { CMaterialList ret = in; if (params.GetVisorParameters().GetBool1()) { ret.Add(kMT_Unknown46); } if (params.GetVisorParameters().GetScanPassthrough()) { ret.Add(kMT_ScanPassthrough); } return ret; } CActor::CActor(TUniqueId uid, bool active, const rstl::string& name, const CEntityInfo& info, const CTransform4f& xf, const CModelData& mData, const CMaterialList& list, const CActorParameters& params, TUniqueId nextDrawNode) : CEntity(uid, info, active, name) , x34_transform(xf) , x64_modelData(mData.IsNull() ? nullptr : new CModelData(mData)) , x68_material(MakeActorMaterialList(list, params)) , x70_materialFilter(CMaterialFilter::MakeIncludeExclude(CMaterialList().Union(SolidMaterial), CMaterialList(0))) , x88_sfxId(InvalidSfxId) , x8c_loopingSfxHandle(0) , x90_actorLights(mData.IsNull() ? nullptr : params.GetLighting().MakeActorLights().release()) , x9c_renderBounds(CAABox::mskInvertedBox) , xb4_drawFlags(CModelFlags::Normal()) , xbc_time(0.f) , xc0_pitchBend(8192) , xc4_fluidId(kInvalidUniqueId) , xc6_nextDrawNode(nextDrawNode) , xc8_drawnToken(-1) , xcc_addedToken(-1) , xd0_damageMag(params.GetThermalMag()) , xd4_maxVol(CAudioSys::kMaxVolume) , xd8_nonLoopingSfxHandles(CSfxHandle(0)) , xe4_24_nextNonLoopingSfxHandle(0) , xe4_27_notInSortedLists(true) , xe4_28_transformDirty(true) , xe4_29_actorLightsDirty(true) , xe4_30_outOfFrustum(false) , xe4_31_calculateLighting(true) , xe5_24_shadowEnabled(false) , xe5_25_shadowDirty(false) , xe5_26_muted(false) , xe5_27_useInSortedLists(true) , xe5_28_callTouch(true) , xe5_29_globalTimeProvider(params.GetUseGlobalRenderTime()) , xe5_30_renderUnsorted(params.ForceRenderUnsorted()) , xe5_31_pointGeneratorParticles(false) , xe6_24_fluidCounter(0) , xe6_27_thermalVisorFlags(params.IsHotInThermal() ? kTF_Hot : kTF_Cold) , xe6_29_renderParticleDBInside(true) , xe6_30_enablePitchBend(false) , xe6_31_targetableVisorFlags(params.GetVisorParameters().GetMask()) , xe7_27_enableRender(true) , xe7_28_worldLightingDirty(false) , xe7_29_drawEnabled(active) , xe7_30_doTargetDistanceTest(true) , xe7_31_targetable(true) { if (!x64_modelData.null()) { if (params.GetXRay().first != 0) { x64_modelData->SetXRayModel(params.GetXRay()); } if (params.GetInfra().first != 0) { x64_modelData->SetInfraModel(params.GetInfra()); } const CLightParameters& lighting = params.GetLighting(); if (!lighting.ShouldMakeLights() || lighting.GetMaxAreaLights() == 0) { x64_modelData->SetAmbientColor(lighting.GetAmbientColor()); } x64_modelData->SetSortThermal(!params.NoSortThermal()); } const CAssetId scanId = params.GetScannable().GetScannableObject0(); if (scanId != kInvalidAssetId) { x98_scanObjectInfo = new TCachedToken< CScannableObjectInfo >(gpSimplePool->GetObj(SObjectTag('SCAN', scanId))); x98_scanObjectInfo->Lock(); } } CActor::~CActor() { RemoveEmitter(); } SAdvancementDeltas CActor::UpdateAnimation(float dt, CStateManager& mgr, bool advTree) { SAdvancementDeltas result = ModelData()->AdvanceAnimation(dt, mgr, GetAreaId(), advTree); ModelData()->AdvanceParticles(GetTransform(), dt, mgr); UpdateSfxEmitters(); if (HasAnimation()) { u16 maxVol = xd4_maxVol; int aid = x4_areaId.Value(); const CGameCamera* camera = mgr.GetCameraManager()->GetCurrentCamera(mgr); const CVector3f origin = GetTranslation(); const CVector3f toCamera = camera->GetTranslation() - origin; const CInt32POINode* intNode; const CSoundPOINode* soundNode; const CParticlePOINode* particleNode; int soundNodeCount = 0; if (HasAnimation()) { soundNode = GetAnimationData()->GetSoundPOIList(soundNodeCount); } else { soundNode = nullptr; } if (soundNodeCount > 0 && soundNode != nullptr) { for (int i = 0; i < soundNodeCount; ++soundNode, ++i) { int charIdx = soundNode->GetCharacterIndex(); if (soundNode->GetPoiType() != kPT_Sound || GetMuted()) continue; if (charIdx != -1 && GetAnimationData()->GetCharacterIndex() != charIdx) continue; ProcessSoundEvent(soundNode->GetSoundId(), soundNode->GetWeight(), soundNode->GetFlags(), soundNode->GetFallOff(), soundNode->GetMaxDistance(), 20, maxVol, toCamera, origin, aid, mgr, true); } } int intNodeCount = 0; if (HasAnimation()) { intNode = GetAnimationData()->GetInt32POIList(intNodeCount); } else { intNode = nullptr; } if (intNodeCount > 0 && intNode != nullptr) { for (int i = 0; i < intNodeCount; ++intNode, ++i) { int charIdx = intNode->GetCharacterIndex(); if (intNode->GetPoiType() == kPT_SoundInt32 && !GetMuted() && (charIdx == -1 || GetAnimationData()->GetCharacterIndex() == charIdx)) { ProcessSoundEvent(intNode->GetValue(), intNode->GetWeight(), intNode->GetFlags(), 0.1f, 150.f, 20, maxVol, toCamera, origin, aid, mgr, true); } else if (intNode->GetPoiType() == kPT_UserEvent) { DoUserAnimEvent(mgr, *intNode, static_cast< EUserEventType >(intNode->GetValue()), dt); } } } int particleNodeCount = 0; if (HasAnimation()) { particleNode = GetAnimationData()->GetParticlePOIList(particleNodeCount); } else { particleNode = nullptr; } if (particleNodeCount > 0 && particleNode != nullptr) { for (int i = 0; i < particleNodeCount; ++particleNode, ++i) { int charIdx = particleNode->GetCharacterIndex(); if (charIdx != -1 && GetAnimationData()->GetCharacterIndex() != charIdx) continue; AnimationData()->SetParticleEffectState(particleNode->GetString(), true, mgr); } } } return result; } void CActor::RemoveEmitter() { if (CSfxHandle handle = x8c_loopingSfxHandle) { CSfxManager::RemoveEmitter(handle); x88_sfxId = -1; x8c_loopingSfxHandle = 0; } } void CActor::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { if (type == kUE_LoopedSoundStop) { RemoveEmitter(); } } f32 CActor::GetAverageAnimVelocity(int anim) { return HasAnimation() ? GetAnimationData()->GetAverageVelocity(anim) : 0.f; } void CActor::CalculateRenderBounds() { if (HasModelData()) { SetRenderBounds(GetModelData()->GetBounds(GetTransform())); } else { const CVector3f origin = GetTranslation(); SetRenderBounds(CAABox(origin, origin)); } } void CActor::SetModelData(const CModelData& modelData) { x64_modelData = modelData.IsNull() ? nullptr : new CModelData(modelData); } // Unreferenced extern "C" { void sub_8005502c() {} } // TODO nonmatching void CActor::PreRender(CStateManager& mgr, const CFrustumPlanes& planes) { if (HasModelData()) { SetPreRenderClipped(!planes.BoxInFrustumPlanes(x9c_renderBounds)); if (!GetPreRenderClipped()) { bool lightsDirty = false; if (GetPreRenderHasMoved()) { SetPreRenderHasMoved(false); SetShadowDirty(true); lightsDirty = true; } else if (xe7_28_worldLightingDirty) { lightsDirty = true; } else if (HasActorLights() && GetActorLights()->GetNeedsRelight()) { lightsDirty = true; } if (GetShadowDirty() && GetDrawShadow() && HasShadow()) { Shadow()->Calculate(GetModelData()->GetBounds(), GetTransform(), mgr); SetShadowDirty(false); } if (GetCalculateLighting()) { CAABox bounds = GetModelData()->GetBounds(GetTransform()); if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::kPV_Thermal) { ActorLights()->BuildConstantAmbientLighting(); } else { if (lightsDirty) { TAreaId aid = x4_areaId; if (aid != kInvalidAreaId && mgr.GetWorld()->IsAreaValid(aid) && ActorLights()->BuildAreaLightList(mgr, *mgr.GetWorld()->GetArea(aid), bounds)) { xe7_28_worldLightingDirty = false; } } ActorLights()->BuildDynamicLightList(mgr, bounds); } } if (GetModelData()->HasAnimation()) { AnimationData()->PreRender(); } } else { if (GetPreRenderHasMoved()) { SetPreRenderHasMoved(false); SetShadowDirty(true); } if (GetShadowDirty() && GetDrawShadow() && HasShadow()) { if (planes.BoxInFrustumPlanes(GetShadow()->GetMaxShadowBox(GetModelData()->GetBounds(GetTransform()))) == true) { Shadow()->Calculate(GetModelData()->GetBounds(), GetTransform(), mgr); SetShadowDirty(false); } } } } } void CActor::AddToRenderer(const CFrustumPlanes& planes, const CStateManager& mgr) const { if (HasModelData()) { if (GetRenderParticleDatabaseInside()) { GetModelData()->RenderParticles(planes); } if (!GetPreRenderClipped()) { if (CanRenderUnsorted(mgr)) { Render(mgr); } else { EnsureRendered(mgr); } } if (mgr.GetPlayerState()->GetActiveVisor(mgr) != CPlayerState::kPV_XRay && mgr.GetPlayerState()->GetActiveVisor(mgr) != CPlayerState::kPV_Thermal && GetDrawShadow() && GetShadow()->Valid() && planes.BoxInFrustumPlanes(GetShadow()->GetBounds()) == true) { gpRender->AddDrawable(GetShadow(), GetShadow()->GetTransform().GetTranslation(), GetShadow()->GetBounds(), 1, IRenderer::kDS_SortedCallback); } } } void CActor::EnsureRendered(const CStateManager& mgr, const CVector3f& pos, const CAABox& bounds) const { if (GetModelData()) { const CModelData::EWhichModel which = CModelData::GetRenderingModel(mgr); GetModelData()->RenderUnsortedParts(which, GetTransform(), GetActorLights(), GetModelFlags()); } mgr.AddDrawableActor(*this, pos, bounds); } void CActor::EnsureRendered(const CStateManager& mgr) const { const CAABox bounds = GetSortingBounds(mgr); const CVector3f viewForward = CGraphics::GetViewMatrix().GetForward(); const CVector3f pos = bounds.ClosestPointAlongVector(viewForward); EnsureRendered(mgr, pos, bounds); } void CActor::DrawTouchBounds() const {} bool CActor::CanRenderUnsorted(const CStateManager& mgr) const { // if (GetModelData()->IsNull() && ) return false; }