diff --git a/source/graphics/ObjectEntry.cpp b/source/graphics/ObjectEntry.cpp index 5d8f095827..40272cfa58 100644 --- a/source/graphics/ObjectEntry.cpp +++ b/source/graphics/ObjectEntry.cpp @@ -112,7 +112,14 @@ bool CObjectEntry::BuildVariation(const std::vector*>& comp if (!variation.particles.empty()) { - m_Model = std::make_unique(g_Renderer.GetSceneRenderer().GetParticleManager().LoadEmitterType(variation.particles)); + CParticleEmitterType* particleEmitterType{ + g_Renderer.GetSceneRenderer().GetParticleManager().LoadEmitterType(variation.particles)}; + if (!particleEmitterType) + { + LOGERROR("CObjectEntry::BuildVariation(): Failed to load particles %s", variation.particles.string8()); + return false; + } + m_Model = std::make_unique(*particleEmitterType); return true; } diff --git a/source/graphics/ParticleEmitter.cpp b/source/graphics/ParticleEmitter.cpp index c3ef498551..f3c4ac5736 100644 --- a/source/graphics/ParticleEmitter.cpp +++ b/source/graphics/ParticleEmitter.cpp @@ -75,14 +75,14 @@ struct ParticleClosestInFrontCompare } // anonymous namespace -CParticleEmitter::CParticleEmitter(const CParticleEmitterTypePtr& type) : - m_Type(type), m_LastUpdateTime(type->m_Manager.GetCurrentTime()), +CParticleEmitter::CParticleEmitter(const CParticleEmitterType& type) : + m_Type(type), m_LastUpdateTime(type.m_Manager.GetCurrentTime()), m_IndexArray(Renderer::Backend::IBuffer::Usage::TRANSFER_DST), m_VertexArray(Renderer::Backend::IBuffer::Type::VERTEX, Renderer::Backend::IBuffer::Usage::DYNAMIC | Renderer::Backend::IBuffer::Usage::TRANSFER_DST), m_VertexUVArray(Renderer::Backend::IBuffer::Type::VERTEX, Renderer::Backend::IBuffer::Usage::TRANSFER_DST), - m_LastFrameNumber(-1), m_UseInstancing{type->m_Manager.ShouldUseInstancing()} + m_LastFrameNumber(-1), m_UseInstancing{type.m_Manager.ShouldUseInstancing()} { // If we should start with particles fully emitted, pretend that we // were created in the past so the first update will produce lots of @@ -91,10 +91,10 @@ CParticleEmitter::CParticleEmitter(const CParticleEmitterTypePtr& type) : // lifetime-length update of all emitters when the game first starts // (so that e.g. buildings constructed later on won't have fully-started // emitters, but those at the start will)? - if (m_Type->m_StartFull) - m_LastUpdateTime -= m_Type->m_MaxLifetime; + if (m_Type.m_StartFull) + m_LastUpdateTime -= m_Type.m_MaxLifetime; - m_Particles.reserve(m_Type->m_MaxParticles); + m_Particles.reserve(m_Type.m_MaxParticles); m_AttributePos.format = Renderer::Backend::Format::R32G32B32_SFLOAT; m_VertexArray.AddAttribute(&m_AttributePos); @@ -108,17 +108,17 @@ CParticleEmitter::CParticleEmitter(const CParticleEmitterTypePtr& type) : m_AttributeAxisY.format = Renderer::Backend::Format::R32G32B32A32_SFLOAT; m_VertexArray.AddAttribute(&m_AttributeAxisY); - m_VertexArray.SetNumberOfVertices(m_UseInstancing ? m_Type->m_MaxParticles : m_Type->m_MaxParticles * 4); + m_VertexArray.SetNumberOfVertices(m_UseInstancing ? m_Type.m_MaxParticles : m_Type.m_MaxParticles * 4); m_VertexArray.Layout(); m_AttributeUV.format = Renderer::Backend::Format::R32G32_SFLOAT; m_VertexUVArray.AddAttribute(&m_AttributeUV); - m_VertexUVArray.SetNumberOfVertices(m_UseInstancing ? 4 : m_Type->m_MaxParticles * 4); + m_VertexUVArray.SetNumberOfVertices(m_UseInstancing ? 4 : m_Type.m_MaxParticles * 4); m_VertexUVArray.Layout(); VertexArrayIterator attrUV = m_AttributeUV.GetIterator(); - for (uint32_t index{0}; index < (m_UseInstancing ? 1u : m_Type->m_MaxParticles); ++index) + for (uint32_t index{0}; index < (m_UseInstancing ? 1u : m_Type.m_MaxParticles); ++index) { (*attrUV)[0] = 1; (*attrUV)[1] = 0; @@ -137,10 +137,10 @@ CParticleEmitter::CParticleEmitter(const CParticleEmitterTypePtr& type) : m_VertexUVArray.Upload(); m_VertexUVArray.FreeBackingStore(); - m_IndexArray.SetNumberOfVertices(m_UseInstancing ? 6 : m_Type->m_MaxParticles * 6); + m_IndexArray.SetNumberOfVertices(m_UseInstancing ? 6 : m_Type.m_MaxParticles * 6); m_IndexArray.Layout(); VertexArrayIterator index = m_IndexArray.GetIterator(); - for (u16 i = 0; i < (m_UseInstancing ? 1 : m_Type->m_MaxParticles); ++i) + for (u16 i = 0; i < (m_UseInstancing ? 1 : m_Type.m_MaxParticles); ++i) { *index++ = i*4 + 0; *index++ = i*4 + 1; @@ -190,8 +190,8 @@ void CParticleEmitter::UpdateArrayData(int frameNumber) m_LastFrameNumber = frameNumber; // Update m_Particles - m_Type->UpdateEmitter(*this, m_Type->m_Manager.GetCurrentTime() - m_LastUpdateTime); - m_LastUpdateTime = m_Type->m_Manager.GetCurrentTime(); + m_Type.UpdateEmitter(*this, m_Type.m_Manager.GetCurrentTime() - m_LastUpdateTime); + m_LastUpdateTime = m_Type.m_Manager.GetCurrentTime(); // Regenerate the vertex array data: @@ -200,15 +200,15 @@ void CParticleEmitter::UpdateArrayData(int frameNumber) VertexArrayIterator attrAxisX = m_AttributeAxisX.GetIterator(); VertexArrayIterator attrAxisY = m_AttributeAxisY.GetIterator(); - ENSURE(m_Particles.size() <= m_Type->m_MaxParticles); + ENSURE(m_Particles.size() <= m_Type.m_MaxParticles); CBoundingBoxAligned bounds; - if (m_Type->m_SortMode != CParticleEmitterType::SortMode::UNSPECIFIED) + if (m_Type.m_SortMode != CParticleEmitterType::SortMode::UNSPECIFIED) { sortedParticles.insert(sortedParticles.end(), m_Particles.begin(), m_Particles.end()); - switch (m_Type->m_SortMode) + switch (m_Type.m_SortMode) { case CParticleEmitterType::SortMode::YOUNGEST_IN_FRONT: std::sort(sortedParticles.begin(), sortedParticles.end(), ParticleYoungestInFrontCompare{}); @@ -228,7 +228,7 @@ void CParticleEmitter::UpdateArrayData(int frameNumber) m_NumberOfVisibleParticles = 0; const std::span particles{ - m_Type->m_SortMode == CParticleEmitterType::SortMode::UNSPECIFIED ? + m_Type.m_SortMode == CParticleEmitterType::SortMode::UNSPECIFIED ? std::span{m_Particles} : std::span{sortedParticles}}; if (m_UseInstancing) @@ -248,8 +248,8 @@ void CParticleEmitter::UpdateArrayData(int frameNumber) // Special case: If the blending depends on the source color, not the source alpha, // then pre-multiply by the alpha. (This is kind of a hack.) - if (m_Type->m_BlendMode == CParticleEmitterType::BlendMode::OVERLAY || - m_Type->m_BlendMode == CParticleEmitterType::BlendMode::MULTIPLY) + if (m_Type.m_BlendMode == CParticleEmitterType::BlendMode::OVERLAY || + m_Type.m_BlendMode == CParticleEmitterType::BlendMode::MULTIPLY) { color.R = (color.R * color.A) / 255; color.G = (color.G * color.A) / 255; @@ -287,8 +287,8 @@ void CParticleEmitter::UpdateArrayData(int frameNumber) // Special case: If the blending depends on the source color, not the source alpha, // then pre-multiply by the alpha. (This is kind of a hack.) - if (m_Type->m_BlendMode == CParticleEmitterType::BlendMode::OVERLAY || - m_Type->m_BlendMode == CParticleEmitterType::BlendMode::MULTIPLY) + if (m_Type.m_BlendMode == CParticleEmitterType::BlendMode::OVERLAY || + m_Type.m_BlendMode == CParticleEmitterType::BlendMode::MULTIPLY) { color.R = (color.R * color.A) / 255; color.G = (color.G * color.A) / 255; @@ -339,7 +339,7 @@ void CParticleEmitter::Bind( Renderer::Backend::IDeviceCommandContext* deviceCommandContext, Renderer::Backend::IShaderProgram* shader) { - m_Type->m_Texture->UploadBackendTextureIfNeeded(deviceCommandContext); + m_Type.m_Texture->UploadBackendTextureIfNeeded(deviceCommandContext); CLOSTexture& los = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture(); deviceCommandContext->SetTexture( @@ -351,7 +351,7 @@ void CParticleEmitter::Bind( g_Renderer.GetSceneRenderer().GetLightEnv().Bind(deviceCommandContext, shader); deviceCommandContext->SetTexture( - shader->GetBindingSlot(str_baseTex), m_Type->m_Texture->GetBackendTexture()); + shader->GetBindingSlot(str_baseTex), m_Type.m_Texture->GetBackendTexture()); } void CParticleEmitter::RenderArray( @@ -388,7 +388,7 @@ void CParticleEmitter::AddParticle(const SParticle& particle) else m_Particles[m_NextParticleIdx] = particle; - m_NextParticleIdx = (m_NextParticleIdx + 1) % m_Type->m_MaxParticles; + m_NextParticleIdx = (m_NextParticleIdx + 1) % m_Type.m_MaxParticles; } void CParticleEmitter::SetEntityVariable(const std::string& name, float value) @@ -396,15 +396,14 @@ void CParticleEmitter::SetEntityVariable(const std::string& name, float value) m_EntityVariables[name] = value; } -CModelParticleEmitter::CModelParticleEmitter(const CParticleEmitterTypePtr& type) : - m_Type(type) +CModelParticleEmitter::CModelParticleEmitter(const CParticleEmitterType& type) + : m_Type{type}, m_Emitter{std::make_unique(m_Type)} { - m_Emitter = CParticleEmitterPtr(new CParticleEmitter(m_Type)); } CModelParticleEmitter::~CModelParticleEmitter() { - m_Type->m_Manager.AddUnattachedEmitter(std::move(m_Emitter)); + m_Type.m_Manager.AddUnattachedEmitter(std::move(m_Emitter)); } void CModelParticleEmitter::SetEntityVariable(const std::string& name, float value) @@ -423,7 +422,7 @@ void CModelParticleEmitter::CalcBounds() // current computed particle positions plus the emitter type's largest // potential bounding box at the current position - m_WorldBounds = m_Type->CalculateBounds(m_Emitter->GetPosition(), m_Emitter->GetParticleBounds()); + m_WorldBounds = m_Type.CalculateBounds(m_Emitter->GetPosition(), m_Emitter->GetParticleBounds()); } void CModelParticleEmitter::ValidatePosition() diff --git a/source/graphics/ParticleEmitter.h b/source/graphics/ParticleEmitter.h index 0704087ebb..29673c1036 100644 --- a/source/graphics/ParticleEmitter.h +++ b/source/graphics/ParticleEmitter.h @@ -55,8 +55,6 @@ struct SParticle CVector3D axisX, axisY; }; -typedef std::shared_ptr CParticleEmitterPtr; - /** * Particle emitter. * @@ -80,7 +78,7 @@ typedef std::shared_ptr CParticleEmitterPtr; class CParticleEmitter { public: - CParticleEmitter(const CParticleEmitterTypePtr& type); + CParticleEmitter(const CParticleEmitterType& type); /** * Set the position to be used for emission of new particles. @@ -152,7 +150,7 @@ public: void SetEntityVariable(const std::string& name, float value); - CParticleEmitterTypePtr m_Type; + const CParticleEmitterType& m_Type; /// Whether this emitter is still emitting new particles bool m_Active{true}; @@ -194,7 +192,7 @@ private: class CModelParticleEmitter : public CModelAbstract { public: - CModelParticleEmitter(const CParticleEmitterTypePtr& type); + CModelParticleEmitter(const CParticleEmitterType& type); ~CModelParticleEmitter() override; /// Dynamic cast @@ -216,8 +214,8 @@ public: void InvalidatePosition() override; void SetTransform(const CMatrix3D& transform) override; - CParticleEmitterTypePtr m_Type; - CParticleEmitterPtr m_Emitter; + const CParticleEmitterType& m_Type; + std::unique_ptr m_Emitter; }; #endif // INCLUDED_PARTICLEEMITTER diff --git a/source/graphics/ParticleEmitterType.cpp b/source/graphics/ParticleEmitterType.cpp index 1f41083acf..68dcc7265a 100644 --- a/source/graphics/ParticleEmitterType.cpp +++ b/source/graphics/ParticleEmitterType.cpp @@ -58,7 +58,7 @@ public: /// Computes and returns a new value. float Evaluate(CParticleEmitter& emitter) { - m_LastValue = Compute(*emitter.m_Type, emitter); + m_LastValue = Compute(emitter.m_Type, emitter); return m_LastValue; } @@ -268,11 +268,15 @@ private: -CParticleEmitterType::CParticleEmitterType(const VfsPath& path, CParticleManager& manager) : +CParticleEmitterType::CParticleEmitterType(CParticleManager& manager) : m_Manager(manager) { - LoadXML(path); - // TODO: handle load failure +} + +bool CParticleEmitterType::Load(const VfsPath& path) +{ + if (!LoadXML(path)) + return false; // Upper bound on number of particles depends on maximum rate and lifetime m_MaxLifetime = m_Variables[VAR_LIFETIME]->Max(*this); @@ -336,8 +340,12 @@ CParticleEmitterType::CParticleEmitterType(const VfsPath& path, CParticleManager // Offset by the initial positions m_MaxBounds[0] += CVector3D(m_Variables[VAR_POSITION_X]->Min(*this), m_Variables[VAR_POSITION_Y]->Min(*this), m_Variables[VAR_POSITION_Z]->Min(*this)); m_MaxBounds[1] += CVector3D(m_Variables[VAR_POSITION_X]->Max(*this), m_Variables[VAR_POSITION_Y]->Max(*this), m_Variables[VAR_POSITION_Z]->Max(*this)); + + return true; } +CParticleEmitterType::~CParticleEmitterType() = default; + int CParticleEmitterType::GetVariableID(const std::string& name) { if (name == "emissionrate") return VAR_EMISSIONRATE; @@ -364,21 +372,21 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path) // Initialise with sane defaults m_Variables.clear(); m_Variables.resize(VAR__MAX); - m_Variables[VAR_EMISSIONRATE] = IParticleVarPtr(new CParticleVarConstant(10.f)); - m_Variables[VAR_LIFETIME] = IParticleVarPtr(new CParticleVarConstant(3.f)); - m_Variables[VAR_POSITION_X] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_POSITION_Y] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_POSITION_Z] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_ANGLE] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_VELOCITY_X] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_VELOCITY_Y] = IParticleVarPtr(new CParticleVarConstant(1.f)); - m_Variables[VAR_VELOCITY_Z] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_VELOCITY_ANGLE] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_SIZE] = IParticleVarPtr(new CParticleVarConstant(1.f)); - m_Variables[VAR_SIZE_GROWTHRATE] = IParticleVarPtr(new CParticleVarConstant(0.f)); - m_Variables[VAR_COLOR_R] = IParticleVarPtr(new CParticleVarConstant(1.f)); - m_Variables[VAR_COLOR_G] = IParticleVarPtr(new CParticleVarConstant(1.f)); - m_Variables[VAR_COLOR_B] = IParticleVarPtr(new CParticleVarConstant(1.f)); + m_Variables[VAR_EMISSIONRATE] = std::make_unique(10.f); + m_Variables[VAR_LIFETIME] = std::make_unique(3.f); + m_Variables[VAR_POSITION_X] = std::make_unique(0.f); + m_Variables[VAR_POSITION_Y] = std::make_unique(0.f); + m_Variables[VAR_POSITION_Z] = std::make_unique(0.f); + m_Variables[VAR_ANGLE] = std::make_unique(0.f); + m_Variables[VAR_VELOCITY_X] = std::make_unique(0.f); + m_Variables[VAR_VELOCITY_Y] = std::make_unique(1.f); + m_Variables[VAR_VELOCITY_Z] = std::make_unique(0.f); + m_Variables[VAR_VELOCITY_ANGLE] = std::make_unique(0.f); + m_Variables[VAR_SIZE] = std::make_unique(1.f); + m_Variables[VAR_SIZE_GROWTHRATE] = std::make_unique(0.f); + m_Variables[VAR_COLOR_R] = std::make_unique(1.f); + m_Variables[VAR_COLOR_G] = std::make_unique(1.f); + m_Variables[VAR_COLOR_B] = std::make_unique(1.f); m_BlendMode = BlendMode::ADD; m_SortMode = SortMode::UNSPECIFIED; m_StartFull = false; @@ -390,7 +398,10 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path) CXeromyces XeroFile; PSRETURN ret = XeroFile.Load(g_VFS, path, "particle"); if (ret != PSRETURN_OK) + { + LOGERROR("Failed to load particle: '%s'", path.string8()); return false; + } // Define all the elements and attributes used in the XML file #define EL(x) int el_##x = XeroFile.GetElementID(#x) @@ -476,9 +487,9 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path) int id = GetVariableID(Child.GetAttributes().GetNamedItem(at_name)); if (id != -1) { - m_Variables[id] = IParticleVarPtr(new CParticleVarConstant( + m_Variables[id] = std::make_unique( Child.GetAttributes().GetNamedItem(at_value).ToFloat() - )); + ); } } else if (Child.GetNodeName() == el_uniform) @@ -490,9 +501,9 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path) float max = Child.GetAttributes().GetNamedItem(at_max).ToFloat(); // To avoid hangs in the RNG, only use it if [min, max) is non-empty if (min < max) - m_Variables[id] = IParticleVarPtr(new CParticleVarUniform(min, max)); + m_Variables[id] = std::make_unique(min, max); else - m_Variables[id] = IParticleVarPtr(new CParticleVarConstant(min)); + m_Variables[id] = std::make_unique(min); } } else if (Child.GetNodeName() == el_copy) @@ -500,7 +511,7 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path) int id = GetVariableID(Child.GetAttributes().GetNamedItem(at_name)); int from = GetVariableID(Child.GetAttributes().GetNamedItem(at_from)); if (id != -1 && from != -1) - m_Variables[id] = IParticleVarPtr(new CParticleVarCopy(from)); + m_Variables[id] = std::make_unique(from); } else if (Child.GetNodeName() == el_expr) { @@ -509,14 +520,14 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path) float mul = Child.GetAttributes().GetNamedItem(at_mul).ToFloat(); float max = Child.GetAttributes().GetNamedItem(at_max).ToFloat(); if (id != -1) - m_Variables[id] = IParticleVarPtr(new CParticleVarExpr(from, mul, max)); + m_Variables[id] = std::make_unique(from, mul, max); } else if (Child.GetNodeName() == el_force) { float x = Child.GetAttributes().GetNamedItem(at_x).ToFloat(); float y = Child.GetAttributes().GetNamedItem(at_y).ToFloat(); float z = Child.GetAttributes().GetNamedItem(at_z).ToFloat(); - m_Effectors.push_back(IParticleEffectorPtr(new CParticleEffectorForce(x, y, z))); + m_Effectors.push_back(std::make_unique(x, y, z)); } else if (Child.GetNodeName() == el_particle) { @@ -576,7 +587,7 @@ void CParticleEmitterType::UpdateEmitter(CParticleEmitter& emitter, float dt) co void CParticleEmitterType::UpdateEmitterStep(CParticleEmitter& emitter, float dt) const { - ENSURE(emitter.m_Type.get() == this); + ENSURE(&emitter.m_Type == this); if (emitter.m_Active) { diff --git a/source/graphics/ParticleEmitterType.h b/source/graphics/ParticleEmitterType.h index d65abf6dea..34987a22e6 100644 --- a/source/graphics/ParticleEmitterType.h +++ b/source/graphics/ParticleEmitterType.h @@ -49,7 +49,13 @@ class CParticleEmitterType { NONCOPYABLE(CParticleEmitterType); // reference member public: - CParticleEmitterType(const VfsPath& path, CParticleManager& manager); + CParticleEmitterType(CParticleManager& manager); + ~CParticleEmitterType(); + + /** + * @return True if the emitter type was successfuly loaded. + */ + bool Load(const VfsPath& path); private: friend class CModelParticleEmitter; @@ -130,15 +136,10 @@ private: u16 m_MaxParticles; CBoundingBoxAligned m_MaxBounds; - typedef std::shared_ptr IParticleVarPtr; - std::vector m_Variables; - - typedef std::shared_ptr IParticleEffectorPtr; - std::vector m_Effectors; + std::vector> m_Variables; + std::vector> m_Effectors; CParticleManager& m_Manager; }; -typedef std::shared_ptr CParticleEmitterTypePtr; - #endif // INCLUDED_PARTICLEEMITTERTYPE diff --git a/source/graphics/ParticleManager.cpp b/source/graphics/ParticleManager.cpp index 57e8e00125..d0eb1c1a31 100644 --- a/source/graphics/ParticleManager.cpp +++ b/source/graphics/ParticleManager.cpp @@ -24,6 +24,7 @@ #include "renderer/backend/IDevice.h" #include "renderer/Scene.h" +#include #include #include #include @@ -48,21 +49,23 @@ CParticleManager::~CParticleManager() UnregisterFileReloadFunc(ReloadChangedFileCB, this); } -CParticleEmitterTypePtr CParticleManager::LoadEmitterType(const VfsPath& path) +CParticleEmitterType* CParticleManager::LoadEmitterType(const VfsPath& path) { - std::unordered_map::iterator it = m_EmitterTypes.find(path); - if (it != m_EmitterTypes.end()) - return it->second; + if (auto it{m_EmitterTypes.find(path)}; it != m_EmitterTypes.end()) + return it->second.get(); - CParticleEmitterTypePtr emitterType(new CParticleEmitterType(path, *this)); - m_EmitterTypes[path] = emitterType; - return emitterType; + std::unique_ptr emitterType{ + std::make_unique(*this)}; + if (!emitterType->Load(path)) + return nullptr; + + return m_EmitterTypes.emplace(path, std::move(emitterType)).first->second.get(); } -void CParticleManager::AddUnattachedEmitter(const CParticleEmitterPtr& emitter) +void CParticleManager::AddUnattachedEmitter(std::unique_ptr emitter) { emitter->m_Active = false; - m_UnattachedEmitters.push_back(emitter); + m_UnattachedEmitters.emplace_back(std::move(emitter)); } void CParticleManager::ClearUnattachedEmitters() @@ -75,18 +78,19 @@ void CParticleManager::Interpolate(const float simFrameLength) m_CurrentTime += simFrameLength; } +struct ParticleAlive +{ + bool operator()(const SParticle& particle) const + { + return particle.age < particle.maxAge; + } +}; + struct EmitterHasNoParticles { - bool operator()(const CParticleEmitterPtr& emitterPtr) + bool operator()(const std::unique_ptr& emitter) const { - CParticleEmitter& emitter = *emitterPtr.get(); - for (size_t i = 0; i < emitter.m_Particles.size(); ++i) - { - SParticle& p = emitter.m_Particles[i]; - if (p.age < p.maxAge) - return false; - } - return true; + return std::none_of(emitter->m_Particles.begin(), emitter->m_Particles.end(), ParticleAlive{}); } }; @@ -95,16 +99,21 @@ void CParticleManager::RenderSubmit(SceneCollector& collector, const CFrustum&) PROFILE("submit unattached particles"); // Delete any unattached emitters that have no particles left - m_UnattachedEmitters.remove_if(EmitterHasNoParticles()); + std::erase_if(m_UnattachedEmitters, EmitterHasNoParticles()); // TODO: should do some frustum culling - for (std::list::iterator it = m_UnattachedEmitters.begin(); it != m_UnattachedEmitters.end(); ++it) - collector.Submit(it->get()); + for (std::unique_ptr& emitter : m_UnattachedEmitters) + collector.Submit(emitter.get()); } Status CParticleManager::ReloadChangedFile(const VfsPath& path) { - m_EmitterTypes.erase(path); + if (auto it = m_EmitterTypes.find(path); it != m_EmitterTypes.end()) + { + // We can't erase an emitter type because emitters might hold + // a reference to it. + it->second->Load(path); + } return INFO::OK; } diff --git a/source/graphics/ParticleManager.h b/source/graphics/ParticleManager.h index 5f1cc7d7f9..b73dff4090 100644 --- a/source/graphics/ParticleManager.h +++ b/source/graphics/ParticleManager.h @@ -24,9 +24,9 @@ #include "lib/path.h" #include "lib/status.h" -#include #include #include +#include class CFrustum; class SceneCollector; @@ -39,7 +39,7 @@ public: CParticleManager(Renderer::Backend::IDevice& device); ~CParticleManager(); - CParticleEmitterTypePtr LoadEmitterType(const VfsPath& path); + CParticleEmitterType* LoadEmitterType(const VfsPath& path); /** * Tell the manager to handle rendering of an emitter that is no longer @@ -49,7 +49,7 @@ public: * will carry on rendering (until all particles have dissipated) * even when it's no longer attached to a model. */ - void AddUnattachedEmitter(const CParticleEmitterPtr& emitter); + void AddUnattachedEmitter(std::unique_ptr emitter); /** * Delete unattached emitters if we don't wish to see them anymore (like in actor viewer) @@ -72,9 +72,9 @@ public: private: float m_CurrentTime{0.0f}; - std::list m_UnattachedEmitters; + std::vector> m_UnattachedEmitters; - std::unordered_map m_EmitterTypes; + std::unordered_map> m_EmitterTypes; bool m_UseInstancing{false}; }; diff --git a/source/renderer/ParticleRenderer.cpp b/source/renderer/ParticleRenderer.cpp index 6745b53046..4d30c1073f 100644 --- a/source/renderer/ParticleRenderer.cpp +++ b/source/renderer/ParticleRenderer.cpp @@ -161,7 +161,7 @@ void ParticleRenderer::RenderParticles( } else { - switch (emitter->m_Type->m_BlendMode) + switch (emitter->m_Type.m_BlendMode) { case CParticleEmitterType::BlendMode::ADD: currentTech = m->techAdd.get(); break; case CParticleEmitterType::BlendMode::SUBTRACT: currentTech = m->techSubtract.get(); break; @@ -195,7 +195,7 @@ void ParticleRenderer::RenderParticles( const CMatrix3D rotationMatrix{emitter->GetRotation().ToMatrix()}; CMatrix3D spaceTransform; - if (emitter->m_Type->m_UseLocalSpace) + if (emitter->m_Type.m_UseLocalSpace) { spaceTransform = rotationMatrix; spaceTransform.Translate(emitter->GetPosition()); @@ -219,7 +219,7 @@ void ParticleRenderer::RenderBounds(Renderer::Backend::IDeviceCommandContext& de for (const CParticleEmitter* emitter : m->emitters[cullGroup]) { const CBoundingBoxAligned bounds = - emitter->m_Type->CalculateBounds(emitter->GetPosition(), emitter->GetParticleBounds()); + emitter->m_Type.CalculateBounds(emitter->GetPosition(), emitter->GetParticleBounds()); g_Renderer.GetDebugRenderer().DrawBoundingBox(deviceCommandContext, bounds, CColor(0.0f, 1.0f, 0.0f, 1.0f), true); }