Removes std::shared_ptr usages from particles

This commit is contained in:
Vladislav Belov 2026-07-13 00:58:25 +02:00
parent d2375de187
commit 5778ef6cac
No known key found for this signature in database
GPG key ID: 353545E45DB9CCB3
8 changed files with 127 additions and 102 deletions

View file

@ -112,7 +112,14 @@ bool CObjectEntry::BuildVariation(const std::vector<const std::set<CStr>*>& comp
if (!variation.particles.empty())
{
m_Model = std::make_unique<CModelParticleEmitter>(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<CModelParticleEmitter>(*particleEmitterType);
return true;
}

View file

@ -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<float[2]> attrUV = m_AttributeUV.GetIterator<float[2]>();
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<u16> 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<CVector4D> attrAxisX = m_AttributeAxisX.GetIterator<CVector4D>();
VertexArrayIterator<CVector4D> attrAxisY = m_AttributeAxisY.GetIterator<CVector4D>();
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<SParticle> particles{
m_Type->m_SortMode == CParticleEmitterType::SortMode::UNSPECIFIED ?
m_Type.m_SortMode == CParticleEmitterType::SortMode::UNSPECIFIED ?
std::span<SParticle>{m_Particles} : std::span<SParticle>{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<CParticleEmitter>(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()

View file

@ -55,8 +55,6 @@ struct SParticle
CVector3D axisX, axisY;
};
typedef std::shared_ptr<CParticleEmitter> CParticleEmitterPtr;
/**
* Particle emitter.
*
@ -80,7 +78,7 @@ typedef std::shared_ptr<CParticleEmitter> 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<CParticleEmitter> m_Emitter;
};
#endif // INCLUDED_PARTICLEEMITTER

View file

@ -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<CParticleVarConstant>(10.f);
m_Variables[VAR_LIFETIME] = std::make_unique<CParticleVarConstant>(3.f);
m_Variables[VAR_POSITION_X] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_POSITION_Y] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_POSITION_Z] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_ANGLE] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_VELOCITY_X] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_VELOCITY_Y] = std::make_unique<CParticleVarConstant>(1.f);
m_Variables[VAR_VELOCITY_Z] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_VELOCITY_ANGLE] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_SIZE] = std::make_unique<CParticleVarConstant>(1.f);
m_Variables[VAR_SIZE_GROWTHRATE] = std::make_unique<CParticleVarConstant>(0.f);
m_Variables[VAR_COLOR_R] = std::make_unique<CParticleVarConstant>(1.f);
m_Variables[VAR_COLOR_G] = std::make_unique<CParticleVarConstant>(1.f);
m_Variables[VAR_COLOR_B] = std::make_unique<CParticleVarConstant>(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<CParticleVarConstant>(
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<CParticleVarUniform>(min, max);
else
m_Variables[id] = IParticleVarPtr(new CParticleVarConstant(min));
m_Variables[id] = std::make_unique<CParticleVarConstant>(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<CParticleVarCopy>(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<CParticleVarExpr>(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<CParticleEffectorForce>(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)
{

View file

@ -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<IParticleVar> IParticleVarPtr;
std::vector<IParticleVarPtr> m_Variables;
typedef std::shared_ptr<IParticleEffector> IParticleEffectorPtr;
std::vector<IParticleEffectorPtr> m_Effectors;
std::vector<std::unique_ptr<IParticleVar>> m_Variables;
std::vector<std::unique_ptr<IParticleEffector>> m_Effectors;
CParticleManager& m_Manager;
};
typedef std::shared_ptr<CParticleEmitterType> CParticleEmitterTypePtr;
#endif // INCLUDED_PARTICLEEMITTERTYPE

View file

@ -24,6 +24,7 @@
#include "renderer/backend/IDevice.h"
#include "renderer/Scene.h"
#include <algorithm>
#include <cstddef>
#include <memory>
#include <unordered_map>
@ -48,21 +49,23 @@ CParticleManager::~CParticleManager()
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
}
CParticleEmitterTypePtr CParticleManager::LoadEmitterType(const VfsPath& path)
CParticleEmitterType* CParticleManager::LoadEmitterType(const VfsPath& path)
{
std::unordered_map<VfsPath, CParticleEmitterTypePtr>::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<CParticleEmitterType> emitterType{
std::make_unique<CParticleEmitterType>(*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<CParticleEmitter> 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<CParticleEmitter>& 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<CParticleEmitterPtr>::iterator it = m_UnattachedEmitters.begin(); it != m_UnattachedEmitters.end(); ++it)
collector.Submit(it->get());
for (std::unique_ptr<CParticleEmitter>& 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;
}

View file

@ -24,9 +24,9 @@
#include "lib/path.h"
#include "lib/status.h"
#include <list>
#include <random>
#include <unordered_map>
#include <vector>
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<CParticleEmitter> 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<CParticleEmitterPtr> m_UnattachedEmitters;
std::vector<std::unique_ptr<CParticleEmitter>> m_UnattachedEmitters;
std::unordered_map<VfsPath, CParticleEmitterTypePtr> m_EmitterTypes;
std::unordered_map<VfsPath, std::unique_ptr<CParticleEmitterType>> m_EmitterTypes;
bool m_UseInstancing{false};
};

View file

@ -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);
}