From 12e98678f64a798f1ad38a50b820e60f96eca321 Mon Sep 17 00:00:00 2001 From: DS Date: Wed, 11 Oct 2023 17:07:30 +0200 Subject: [PATCH] Particle cleanup (#13394) --- src/client/particles.cpp | 442 ++++++++++++++++++++------------------- src/client/particles.h | 81 ++++--- 2 files changed, 262 insertions(+), 261 deletions(-) diff --git a/src/client/particles.cpp b/src/client/particles.cpp index b63c19c64..0697763ca 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -38,105 +38,98 @@ with this program; if not, write to the Free Software Foundation, Inc., */ Particle::Particle( - IGameDef *gamedef, - LocalPlayer *player, - ClientEnvironment *env, - const ParticleParameters &p, - const ClientTexRef& texture, - v2f texpos, - v2f texsize, - video::SColor color -): - scene::ISceneNode(((Client *)gamedef)->getSceneManager()->getRootSceneNode(), - ((Client *)gamedef)->getSceneManager()), - m_texture(texture) + IGameDef *gamedef, + LocalPlayer *player, + ClientEnvironment *env, + const ParticleParameters &p, + const ClientParticleTexRef &texture, + v2f texpos, + v2f texsize, + video::SColor color, + ParticleSpawner *parent, + std::unique_ptr owned_texture + ) : + scene::ISceneNode(((Client *)gamedef)->getSceneManager()->getRootSceneNode(), + ((Client *)gamedef)->getSceneManager()), + + m_expiration(p.expirationtime), + + m_env(env), + m_gamedef(gamedef), + m_collisionbox(aabb3f(v3f(-p.size / 2.0f), v3f(p.size / 2.0f))), + m_texture(texture), + m_texpos(texpos), + m_texsize(texsize), + m_pos(p.pos), + m_velocity(p.vel), + m_acceleration(p.acc), + m_p(p), + m_player(player), + + m_base_color(color), + m_color(color), + + m_parent(parent), + m_owned_texture(std::move(owned_texture)) { - // Misc - m_gamedef = gamedef; - m_env = env; + // Set material + { + // translate blend modes to GL blend functions + video::E_BLEND_FACTOR bfsrc, bfdst; + video::E_BLEND_OPERATION blendop; + const auto blendmode = texture.tex != nullptr + ? texture.tex->blendmode + : ParticleParamTypes::BlendMode::alpha; - // translate blend modes to GL blend functions - video::E_BLEND_FACTOR bfsrc, bfdst; - video::E_BLEND_OPERATION blendop; - const auto blendmode = texture.tex != nullptr - ? texture.tex -> blendmode - : ParticleParamTypes::BlendMode::alpha; + switch (blendmode) { + case ParticleParamTypes::BlendMode::add: + bfsrc = video::EBF_SRC_ALPHA; + bfdst = video::EBF_DST_ALPHA; + blendop = video::EBO_ADD; + break; - switch (blendmode) { - case ParticleParamTypes::BlendMode::add: - bfsrc = video::EBF_SRC_ALPHA; - bfdst = video::EBF_DST_ALPHA; - blendop = video::EBO_ADD; - break; + case ParticleParamTypes::BlendMode::sub: + bfsrc = video::EBF_SRC_ALPHA; + bfdst = video::EBF_DST_ALPHA; + blendop = video::EBO_REVSUBTRACT; + break; - case ParticleParamTypes::BlendMode::sub: - bfsrc = video::EBF_SRC_ALPHA; - bfdst = video::EBF_DST_ALPHA; - blendop = video::EBO_REVSUBTRACT; - break; + case ParticleParamTypes::BlendMode::screen: + bfsrc = video::EBF_ONE; + bfdst = video::EBF_ONE_MINUS_SRC_COLOR; + blendop = video::EBO_ADD; + break; - case ParticleParamTypes::BlendMode::screen: - bfsrc = video::EBF_ONE; - bfdst = video::EBF_ONE_MINUS_SRC_COLOR; - blendop = video::EBO_ADD; - break; + default: // includes ParticleParamTypes::BlendMode::alpha + bfsrc = video::EBF_SRC_ALPHA; + bfdst = video::EBF_ONE_MINUS_SRC_ALPHA; + blendop = video::EBO_ADD; + break; + } - default: // includes ParticleParamTypes::BlendMode::alpha - bfsrc = video::EBF_SRC_ALPHA; - bfdst = video::EBF_ONE_MINUS_SRC_ALPHA; - blendop = video::EBO_ADD; - break; + // Texture + m_material.Lighting = false; + m_material.BackfaceCulling = false; + m_material.FogEnable = true; + m_material.forEachTexture([] (auto &tex) { + tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; + tex.MagFilter = video::ETMAGF_NEAREST; + }); + + // correctly render layered transparent particles -- see #10398 + m_material.ZWriteEnable = video::EZW_AUTO; + + // enable alpha blending and set blend mode + m_material.MaterialType = video::EMT_ONETEXTURE_BLEND; + m_material.MaterialTypeParam = video::pack_textureBlendFunc( + bfsrc, bfdst, + video::EMFN_MODULATE_1X, + video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); + m_material.BlendOperation = blendop; + m_material.setTexture(0, m_texture.ref); } - // Texture - m_material.Lighting = false; - m_material.BackfaceCulling = false; - m_material.FogEnable = true; - m_material.forEachTexture([] (auto &tex) { - tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; - tex.MagFilter = video::ETMAGF_NEAREST; - }); - - // correctly render layered transparent particles -- see #10398 - m_material.ZWriteEnable = video::EZW_AUTO; - - // enable alpha blending and set blend mode - m_material.MaterialType = video::EMT_ONETEXTURE_BLEND; - m_material.MaterialTypeParam = video::pack_textureBlendFunc( - bfsrc, bfdst, - video::EMFN_MODULATE_1X, - video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); - m_material.BlendOperation = blendop; - m_material.setTexture(0, m_texture.ref); - m_texpos = texpos; - m_texsize = texsize; - m_animation = p.animation; - - // Color - m_base_color = color; - m_color = color; - - // Particle related - m_pos = p.pos; - m_velocity = p.vel; - m_acceleration = p.acc; - m_drag = p.drag; - m_jitter = p.jitter; - m_bounce = p.bounce; - m_expiration = p.expirationtime; - m_player = player; - m_size = p.size; - m_collisiondetection = p.collisiondetection; - m_collision_removal = p.collision_removal; - m_object_collision = p.object_collision; - m_vertical = p.vertical; - m_glow = p.glow; - m_alpha = 0; - m_parent = nullptr; - // Irrlicht stuff - const float c = p.size / 2; - m_collisionbox = aabb3f(-c, -c, -c, c, c, c); this->setAutomaticCulling(scene::EAC_OFF); // Init lighting @@ -146,14 +139,6 @@ Particle::Particle( updateVertices(); } -Particle::~Particle() -{ - /* if our textures aren't owned by a particlespawner, we need to clean - * them up ourselves when the particle dies */ - if (m_parent == nullptr) - delete m_texture.tex; -} - void Particle::OnRegisterSceneNode() { if (IsVisible) @@ -180,20 +165,20 @@ void Particle::step(float dtime) // apply drag (not handled by collisionMoveSimple) and brownian motion v3f av = vecAbsolute(m_velocity); - av -= av * (m_drag * dtime); - m_velocity = av*vecSign(m_velocity) + v3f(m_jitter.pickWithin())*dtime; + av -= av * (m_p.drag * dtime); + m_velocity = av*vecSign(m_velocity) + v3f(m_p.jitter.pickWithin())*dtime; - if (m_collisiondetection) { + if (m_p.collisiondetection) { aabb3f box = m_collisionbox; v3f p_pos = m_pos * BS; v3f p_velocity = m_velocity * BS; collisionMoveResult r = collisionMoveSimple(m_env, m_gamedef, BS * 0.5f, box, 0.0f, dtime, &p_pos, &p_velocity, m_acceleration * BS, nullptr, - m_object_collision); + m_p.object_collision); - f32 bounciness = m_bounce.pickWithin(); - if (r.collides && (m_collision_removal || bounciness > 0)) { - if (m_collision_removal) { + f32 bounciness = m_p.bounce.pickWithin(); + if (r.collides && (m_p.collision_removal || bounciness > 0)) { + if (m_p.collision_removal) { // force expiration of the particle m_expiration = -1.0f; } else if (bounciness > 0) { @@ -226,10 +211,10 @@ void Particle::step(float dtime) m_velocity += m_acceleration * dtime; } - if (m_animation.type != TAT_NONE) { + if (m_p.animation.type != TAT_NONE) { m_animation_time += dtime; int frame_length_i, frame_count; - m_animation.determineParams( + m_p.animation.determineParams( m_material.getTexture(0)->getSize(), &frame_count, &frame_length_i, NULL); float frame_length = frame_length_i / 1000.0; @@ -273,7 +258,7 @@ void Particle::updateLight() else light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); - u8 m_light = decode_light(light + m_glow); + u8 m_light = decode_light(light + m_p.glow); m_color.set(m_alpha*255, m_light * m_base_color.getRed() / 255, m_light * m_base_color.getGreen() / 255, @@ -290,12 +275,12 @@ void Particle::updateVertices() else scale = v2f(1.f, 1.f); - if (m_animation.type != TAT_NONE) { + if (m_p.animation.type != TAT_NONE) { const v2u32 texsize = m_material.getTexture(0)->getSize(); v2f texcoord, framesize_f; v2u32 framesize; - texcoord = m_animation.getTextureCoords(texsize, m_animation_frame); - m_animation.determineParams(texsize, NULL, NULL, &framesize); + texcoord = m_p.animation.getTextureCoords(texsize, m_animation_frame); + m_p.animation.determineParams(texsize, NULL, NULL, &framesize); framesize_f = v2f(framesize.X / (float) texsize.X, framesize.Y / (float) texsize.Y); tx0 = m_texpos.X + texcoord.X; @@ -309,7 +294,7 @@ void Particle::updateVertices() ty1 = m_texpos.Y + m_texsize.Y; } - auto half = m_size * .5f, + auto half = m_p.size * .5f, hx = half * scale.X, hy = half * scale.Y; m_vertices[0] = video::S3DVertex(-hx, -hy, @@ -328,7 +313,7 @@ void Particle::updateVertices() m_box.reset(v3f()); for (video::S3DVertex &vertex : m_vertices) { - if (m_vertical) { + if (m_p.vertical) { v3f ppos = m_player->getPosition()/BS; vertex.Pos.rotateXZBy(std::atan2(ppos.Z - m_pos.Z, ppos.X - m_pos.X) / core::DEGTORAD + 90); @@ -345,25 +330,22 @@ void Particle::updateVertices() */ ParticleSpawner::ParticleSpawner( - IGameDef *gamedef, - LocalPlayer *player, - const ParticleSpawnerParameters &p, - u16 attached_id, - std::unique_ptr& texpool, - size_t texcount, - ParticleManager *p_manager -): - m_particlemanager(p_manager), p(p) + IGameDef *gamedef, + LocalPlayer *player, + const ParticleSpawnerParameters ¶ms, + u16 attached_id, + std::vector &&texpool, + ParticleManager *p_manager + ) : + m_active(0), + m_particlemanager(p_manager), + m_time(0.0f), + m_gamedef(gamedef), + m_player(player), + p(params), + m_texpool(std::move(texpool)), + m_attached_id(attached_id) { - m_gamedef = gamedef; - m_player = player; - m_attached_id = attached_id; - m_texpool = std::move(texpool); - m_texcount = texcount; - m_time = 0; - m_active = 0; - m_dying = false; - m_spawntimes.reserve(p.amount + 1); for (u16 i = 0; i <= p.amount; i++) { float spawntime = myrand_float() * p.time; @@ -398,22 +380,22 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, fac = m_time / (p.time+0.1f); } - auto r_pos = p.pos.blend(fac); - auto r_vel = p.vel.blend(fac); - auto r_acc = p.acc.blend(fac); - auto r_drag = p.drag.blend(fac); + auto r_pos = p.pos.blend(fac); + auto r_vel = p.vel.blend(fac); + auto r_acc = p.acc.blend(fac); + auto r_drag = p.drag.blend(fac); auto r_radius = p.radius.blend(fac); auto r_jitter = p.jitter.blend(fac); auto r_bounce = p.bounce.blend(fac); - v3f attractor_origin = p.attractor_origin.blend(fac); + v3f attractor_origin = p.attractor_origin.blend(fac); v3f attractor_direction = p.attractor_direction.blend(fac); - auto attractor_obj = findObjectByID(env, p.attractor_attachment); + auto attractor_obj = findObjectByID(env, p.attractor_attachment); auto attractor_direction_obj = findObjectByID(env, p.attractor_direction_attachment); - auto r_exp = p.exptime.blend(fac); - auto r_size = p.size.blend(fac); + auto r_exp = p.exptime.blend(fac); + auto r_size = p.size.blend(fac); auto r_attract = p.attract.blend(fac); - auto attract = r_attract.pickWithin(); + auto attract = r_attract.pickWithin(); v3f ppos = m_player->getPosition() / BS; v3f pos = r_pos.pickWithin(); @@ -538,7 +520,7 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, p.copyCommon(pp); - ClientTexRef texture; + ClientParticleTexRef texture; v2f texpos, texsize; video::SColor color(0xFFFFFFFF); @@ -549,9 +531,10 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, texpos, texsize, &color, p.node_tile)) return; } else { - if (m_texcount == 0) + if (m_texpool.size() == 0) return; - texture = decltype(texture)(m_texpool[m_texcount == 1 ? 0 : myrand_range(0,m_texcount-1)]); + texture = ClientParticleTexRef(m_texpool[m_texpool.size() == 1 ? 0 + : myrand_range(0, m_texpool.size()-1)]); texpos = v2f(0.0f, 0.0f); texsize = v2f(1.0f, 1.0f); if (texture.tex->animated) @@ -581,18 +564,17 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, pp.size = r_size.pickWithin(); ++m_active; - auto pa = new Particle( - m_gamedef, - m_player, - env, - pp, - texture, - texpos, - texsize, - color - ); - pa->m_parent = this; - m_particlemanager->addParticle(pa); + m_particlemanager->addParticle(std::make_unique( + m_gamedef, + m_player, + env, + pp, + texture, + texpos, + texsize, + color, + this + )); } void ParticleSpawner::step(float dtime, ClientEnvironment *env) @@ -664,37 +646,51 @@ void ParticleManager::step(float dtime) void ParticleManager::stepSpawners(float dtime) { MutexAutoLock lock(m_spawner_list_lock); - for (auto i = m_particle_spawners.begin(); i != m_particle_spawners.end();) { - if (i->second->getExpired()) { - // the particlespawner owns the textures, so we need to make - // sure there are no active particles before we free it - if (i->second->m_active == 0) { - delete i->second; - m_particle_spawners.erase(i++); - } else { - ++i; - } + + for (size_t i = 0; i < m_dying_particle_spawners.size();) { + // the particlespawner owns the textures, so we need to make + // sure there are no active particles before we free it + if (!m_dying_particle_spawners[i]->hasActive()) { + m_dying_particle_spawners[i] = std::move(m_dying_particle_spawners.back()); + m_dying_particle_spawners.pop_back(); } else { - i->second->step(dtime, m_env); ++i; } } + + for (auto it = m_particle_spawners.begin(); it != m_particle_spawners.end();) { + auto &ps = it->second; + if (ps->getExpired()) { + // same as above + if (ps->hasActive()) + m_dying_particle_spawners.push_back(std::move(ps)); + it = m_particle_spawners.erase(it); + } else { + ps->step(dtime, m_env); + ++it; + } + } } void ParticleManager::stepParticles(float dtime) { MutexAutoLock lock(m_particle_list_lock); - for (auto i = m_particles.begin(); i != m_particles.end();) { - if ((*i)->get_expired()) { - if ((*i)->m_parent) { - assert((*i)->m_parent->m_active != 0); - --(*i)->m_parent->m_active; + + for (size_t i = 0; i < m_particles.size();) { + Particle &p = *m_particles[i]; + if (p.isExpired()) { + ParticleSpawner *parent = p.getParent(); + if (parent) { + assert(parent->hasActive()); + parent->decrActive(); } - (*i)->remove(); - delete *i; - i = m_particles.erase(i); + // remove scene node + p.remove(); + // delete + m_particles[i] = std::move(m_particles.back()); + m_particles.pop_back(); } else { - (*i)->step(dtime); + p.step(dtime); ++i; } } @@ -704,17 +700,19 @@ void ParticleManager::clearAll() { MutexAutoLock lock(m_spawner_list_lock); MutexAutoLock lock2(m_particle_list_lock); - for (auto i = m_particle_spawners.begin(); i != m_particle_spawners.end();) { - delete i->second; - m_particle_spawners.erase(i++); - } - for(auto i = m_particles.begin(); i != m_particles.end();) - { - (*i)->remove(); - delete *i; - i = m_particles.erase(i); + // clear particle spawners + m_particle_spawners.clear(); + m_dying_particle_spawners.clear(); + + // clear particles + for (std::unique_ptr &p : m_particles) { + // remove scene node + p->remove(); + // delete + p.reset(); } + m_particles.clear(); } void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, @@ -732,31 +730,27 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, const ParticleSpawnerParameters &p = *event->add_particlespawner.p; // texture pool - std::unique_ptr texpool = nullptr; - size_t txpsz = 0; + std::vector texpool; if (!p.texpool.empty()) { - txpsz = p.texpool.size(); - texpool = decltype(texpool)(new ClientTexture [txpsz]); - + size_t txpsz = p.texpool.size(); + texpool.reserve(txpsz); for (size_t i = 0; i < txpsz; ++i) { - texpool[i] = ClientTexture(p.texpool[i], client->tsrc()); + texpool.emplace_back(p.texpool[i], client->tsrc()); } } else { // no texpool in use, use fallback texture - txpsz = 1; - texpool = decltype(texpool)(new ClientTexture[1] { - ClientTexture(p.texture, client->tsrc()) - }); + texpool.emplace_back(p.texture, client->tsrc()); } - auto toadd = new ParticleSpawner(client, player, - p, - event->add_particlespawner.attached_id, - texpool, - txpsz, - this); - - addParticleSpawner(event->add_particlespawner.id, toadd); + addParticleSpawner(event->add_particlespawner.id, + std::make_unique( + client, + player, + p, + event->add_particlespawner.attached_id, + std::move(texpool), + this) + ); delete event->add_particlespawner.p; break; @@ -764,7 +758,8 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, case CE_SPAWN_PARTICLE: { ParticleParameters &p = *event->spawn_particle; - ClientTexRef texture; + ClientParticleTexRef texture; + std::unique_ptr texstore; v2f texpos, texsize; video::SColor color(0xFFFFFFFF); @@ -778,9 +773,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, /* with no particlespawner to own the texture, we need * to save it on the heap. it will be freed when the * particle is destroyed */ - auto texstore = new ClientTexture(p.texture, client->tsrc()); + texstore = std::make_unique(p.texture, client->tsrc()); - texture = ClientTexRef(*texstore); + texture = ClientParticleTexRef(*texstore); texpos = v2f(0.0f, 0.0f); texsize = v2f(1.0f, 1.0f); } @@ -790,10 +785,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, p.size = oldsize; if (texture.ref) { - Particle *toadd = new Particle(client, player, m_env, - p, texture, texpos, texsize, color); - - addParticle(toadd); + addParticle(std::make_unique(client, player, m_env, + p, texture, texpos, texsize, color, nullptr, + std::move(texstore))); } delete event->spawn_particle; @@ -890,43 +884,53 @@ void ParticleManager::addNodeParticle(IGameDef *gamedef, (f32)pos.Z + myrand_range(0.f, .5f) - .25f ); - Particle *toadd = new Particle( + addParticle(std::make_unique( gamedef, player, m_env, p, - ClientTexRef(ref), + ClientParticleTexRef(ref), texpos, texsize, - color); - - addParticle(toadd); + color)); } void ParticleManager::reserveParticleSpace(size_t max_estimate) { MutexAutoLock lock(m_particle_list_lock); + m_particles.reserve(m_particles.size() + max_estimate); } -void ParticleManager::addParticle(Particle *toadd) +void ParticleManager::addParticle(std::unique_ptr toadd) { MutexAutoLock lock(m_particle_list_lock); - m_particles.push_back(toadd); + + m_particles.push_back(std::move(toadd)); } -void ParticleManager::addParticleSpawner(u64 id, ParticleSpawner *toadd) +void ParticleManager::addParticleSpawner(u64 id, std::unique_ptr toadd) { MutexAutoLock lock(m_spawner_list_lock); - m_particle_spawners[id] = toadd; + + auto &slot = m_particle_spawners[id]; + if (slot) { + // do not kill spawners here. children are still alive + errorstream << "ParticleManager: Failed to add spawner with id " << id + << ". Id already in use." << std::endl; + return; + } + slot = std::move(toadd); } void ParticleManager::deleteParticleSpawner(u64 id) { MutexAutoLock lock(m_spawner_list_lock); + auto it = m_particle_spawners.find(id); if (it != m_particle_spawners.end()) { - it->second->setDying(); + m_dying_particle_spawners.push_back(std::move(it->second)); + m_particle_spawners.erase(it); } } diff --git a/src/client/particles.h b/src/client/particles.h index 0818b796b..283267eb5 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -31,33 +31,34 @@ class ClientEnvironment; struct MapNode; struct ContentFeatures; -struct ClientTexture +struct ClientParticleTexture { /* per-spawner structure used to store the ParticleTexture structs - * that spawned particles will refer to through ClientTexRef */ + * that spawned particles will refer to through ClientParticleTexRef */ ParticleTexture tex; video::ITexture *ref = nullptr; - ClientTexture() = default; - ClientTexture(const ServerParticleTexture& p, ITextureSource *t): + ClientParticleTexture() = default; + ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *t): tex(p), ref(t->getTextureForMesh(p.string)) {}; }; -struct ClientTexRef +struct ClientParticleTexRef { /* per-particle structure used to avoid massively duplicating the * fairly large ParticleTexture struct */ - ParticleTexture* tex = nullptr; - video::ITexture* ref = nullptr; - ClientTexRef() = default; + ParticleTexture *tex = nullptr; + video::ITexture *ref = nullptr; + + ClientParticleTexRef() = default; /* constructor used by particles spawned from a spawner */ - ClientTexRef(ClientTexture& t): + explicit ClientParticleTexRef(ClientParticleTexture &t): tex(&t.tex), ref(t.ref) {}; /* constructor used for node particles */ - ClientTexRef(decltype(ref) tp): ref(tp) {}; + explicit ClientParticleTexRef(video::ITexture *tp): ref(tp) {}; }; class ParticleSpawner; @@ -70,12 +71,13 @@ public: LocalPlayer *player, ClientEnvironment *env, const ParticleParameters &p, - const ClientTexRef &texture, + const ClientParticleTexRef &texture, v2f texpos, v2f texsize, - video::SColor color + video::SColor color, + ParticleSpawner *parent = nullptr, + std::unique_ptr owned_texture = nullptr ); - ~Particle(); virtual const aabb3f &getBoundingBox() const { @@ -97,10 +99,10 @@ public: void step(float dtime); - bool get_expired () + bool isExpired () { return m_expiration < m_time; } - ParticleSpawner *m_parent; + ParticleSpawner *getParent() { return m_parent; } private: void updateLight(); @@ -115,33 +117,27 @@ private: IGameDef *m_gamedef; aabb3f m_box; aabb3f m_collisionbox; - ClientTexRef m_texture; + ClientParticleTexRef m_texture; video::SMaterial m_material; v2f m_texpos; v2f m_texsize; v3f m_pos; v3f m_velocity; v3f m_acceleration; - v3f m_drag; - ParticleParamTypes::v3fRange m_jitter; - ParticleParamTypes::f32Range m_bounce; + const ParticleParameters m_p; LocalPlayer *m_player; - float m_size; //! Color without lighting video::SColor m_base_color; //! Final rendered color video::SColor m_color; - bool m_collisiondetection; - bool m_collision_removal; - bool m_object_collision; - bool m_vertical; - v3s16 m_camera_offset; - struct TileAnimationParams m_animation; float m_animation_time = 0.0f; int m_animation_frame = 0; - u8 m_glow; float m_alpha = 0.0f; + + ParticleSpawner *m_parent = nullptr; + // Used if not spawned from a particlespawner + std::unique_ptr m_owned_texture; }; class ParticleSpawner @@ -149,31 +145,30 @@ class ParticleSpawner public: ParticleSpawner(IGameDef *gamedef, LocalPlayer *player, - const ParticleSpawnerParameters &p, + const ParticleSpawnerParameters ¶ms, u16 attached_id, - std::unique_ptr &texpool, - size_t texcount, - ParticleManager* p_manager); + std::vector &&texpool, + ParticleManager *p_manager); void step(float dtime, ClientEnvironment *env); - size_t m_active; - bool getExpired() const - { return m_dying || (p.amount <= 0 && p.time != 0); } - void setDying() { m_dying = true; } + { return p.amount <= 0 && p.time != 0; } + + bool hasActive() const { return m_active != 0; } + void decrActive() { m_active -= 1; } private: void spawnParticle(ClientEnvironment *env, float radius, const core::matrix4 *attached_absolute_pos_rot_matrix); + size_t m_active; ParticleManager *m_particlemanager; float m_time; - bool m_dying; IGameDef *m_gamedef; LocalPlayer *m_player; ParticleSpawnerParameters p; - std::unique_ptr m_texpool; + std::vector m_texpool; size_t m_texcount; std::vector m_spawntimes; u16 m_attached_id; @@ -187,6 +182,7 @@ class ParticleManager friend class ParticleSpawner; public: ParticleManager(ClientEnvironment* env); + DISABLE_CLASS_COPY(ParticleManager) ~ParticleManager(); void step (float dtime); @@ -219,10 +215,10 @@ protected: ParticleParameters &p, video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color, u8 tilenum = 0); - void addParticle(Particle* toadd); + void addParticle(std::unique_ptr toadd); private: - void addParticleSpawner(u64 id, ParticleSpawner *toadd); + void addParticleSpawner(u64 id, std::unique_ptr toadd); void deleteParticleSpawner(u64 id); void stepParticles(float dtime); @@ -230,13 +226,14 @@ private: void clearAll(); - std::vector m_particles; - std::unordered_map m_particle_spawners; + std::vector> m_particles; + std::unordered_map> m_particle_spawners; + std::vector> m_dying_particle_spawners; // Start the particle spawner ids generated from here after u32_max. lower values are // for server sent spawners. u64 m_next_particle_spawner_id = U32_MAX + 1; - ClientEnvironment* m_env; + ClientEnvironment *m_env; std::mutex m_particle_list_lock; std::mutex m_spawner_list_lock; };