Particle cleanup (#13394)

This commit is contained in:
DS 2023-10-11 17:07:30 +02:00 committed by GitHub
parent 352a403bd0
commit 12e98678f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 262 additions and 261 deletions

View File

@ -38,105 +38,98 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
Particle::Particle( Particle::Particle(
IGameDef *gamedef, IGameDef *gamedef,
LocalPlayer *player, LocalPlayer *player,
ClientEnvironment *env, ClientEnvironment *env,
const ParticleParameters &p, const ParticleParameters &p,
const ClientTexRef& texture, const ClientParticleTexRef &texture,
v2f texpos, v2f texpos,
v2f texsize, v2f texsize,
video::SColor color video::SColor color,
): ParticleSpawner *parent,
scene::ISceneNode(((Client *)gamedef)->getSceneManager()->getRootSceneNode(), std::unique_ptr<ClientParticleTexture> owned_texture
((Client *)gamedef)->getSceneManager()), ) :
m_texture(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 // Set material
m_gamedef = gamedef; {
m_env = env; // 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 switch (blendmode) {
video::E_BLEND_FACTOR bfsrc, bfdst; case ParticleParamTypes::BlendMode::add:
video::E_BLEND_OPERATION blendop; bfsrc = video::EBF_SRC_ALPHA;
const auto blendmode = texture.tex != nullptr bfdst = video::EBF_DST_ALPHA;
? texture.tex -> blendmode blendop = video::EBO_ADD;
: ParticleParamTypes::BlendMode::alpha; break;
switch (blendmode) { case ParticleParamTypes::BlendMode::sub:
case ParticleParamTypes::BlendMode::add: bfsrc = video::EBF_SRC_ALPHA;
bfsrc = video::EBF_SRC_ALPHA; bfdst = video::EBF_DST_ALPHA;
bfdst = video::EBF_DST_ALPHA; blendop = video::EBO_REVSUBTRACT;
blendop = video::EBO_ADD; break;
break;
case ParticleParamTypes::BlendMode::sub: case ParticleParamTypes::BlendMode::screen:
bfsrc = video::EBF_SRC_ALPHA; bfsrc = video::EBF_ONE;
bfdst = video::EBF_DST_ALPHA; bfdst = video::EBF_ONE_MINUS_SRC_COLOR;
blendop = video::EBO_REVSUBTRACT; blendop = video::EBO_ADD;
break; break;
case ParticleParamTypes::BlendMode::screen: default: // includes ParticleParamTypes::BlendMode::alpha
bfsrc = video::EBF_ONE; bfsrc = video::EBF_SRC_ALPHA;
bfdst = video::EBF_ONE_MINUS_SRC_COLOR; bfdst = video::EBF_ONE_MINUS_SRC_ALPHA;
blendop = video::EBO_ADD; blendop = video::EBO_ADD;
break; break;
}
default: // includes ParticleParamTypes::BlendMode::alpha // Texture
bfsrc = video::EBF_SRC_ALPHA; m_material.Lighting = false;
bfdst = video::EBF_ONE_MINUS_SRC_ALPHA; m_material.BackfaceCulling = false;
blendop = video::EBO_ADD; m_material.FogEnable = true;
break; 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 // Irrlicht stuff
const float c = p.size / 2;
m_collisionbox = aabb3f(-c, -c, -c, c, c, c);
this->setAutomaticCulling(scene::EAC_OFF); this->setAutomaticCulling(scene::EAC_OFF);
// Init lighting // Init lighting
@ -146,14 +139,6 @@ Particle::Particle(
updateVertices(); 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() void Particle::OnRegisterSceneNode()
{ {
if (IsVisible) if (IsVisible)
@ -180,20 +165,20 @@ void Particle::step(float dtime)
// apply drag (not handled by collisionMoveSimple) and brownian motion // apply drag (not handled by collisionMoveSimple) and brownian motion
v3f av = vecAbsolute(m_velocity); v3f av = vecAbsolute(m_velocity);
av -= av * (m_drag * dtime); av -= av * (m_p.drag * dtime);
m_velocity = av*vecSign(m_velocity) + v3f(m_jitter.pickWithin())*dtime; m_velocity = av*vecSign(m_velocity) + v3f(m_p.jitter.pickWithin())*dtime;
if (m_collisiondetection) { if (m_p.collisiondetection) {
aabb3f box = m_collisionbox; aabb3f box = m_collisionbox;
v3f p_pos = m_pos * BS; v3f p_pos = m_pos * BS;
v3f p_velocity = m_velocity * BS; v3f p_velocity = m_velocity * BS;
collisionMoveResult r = collisionMoveSimple(m_env, m_gamedef, BS * 0.5f, collisionMoveResult r = collisionMoveSimple(m_env, m_gamedef, BS * 0.5f,
box, 0.0f, dtime, &p_pos, &p_velocity, m_acceleration * BS, nullptr, box, 0.0f, dtime, &p_pos, &p_velocity, m_acceleration * BS, nullptr,
m_object_collision); m_p.object_collision);
f32 bounciness = m_bounce.pickWithin(); f32 bounciness = m_p.bounce.pickWithin();
if (r.collides && (m_collision_removal || bounciness > 0)) { if (r.collides && (m_p.collision_removal || bounciness > 0)) {
if (m_collision_removal) { if (m_p.collision_removal) {
// force expiration of the particle // force expiration of the particle
m_expiration = -1.0f; m_expiration = -1.0f;
} else if (bounciness > 0) { } else if (bounciness > 0) {
@ -226,10 +211,10 @@ void Particle::step(float dtime)
m_velocity += m_acceleration * dtime; m_velocity += m_acceleration * dtime;
} }
if (m_animation.type != TAT_NONE) { if (m_p.animation.type != TAT_NONE) {
m_animation_time += dtime; m_animation_time += dtime;
int frame_length_i, frame_count; int frame_length_i, frame_count;
m_animation.determineParams( m_p.animation.determineParams(
m_material.getTexture(0)->getSize(), m_material.getTexture(0)->getSize(),
&frame_count, &frame_length_i, NULL); &frame_count, &frame_length_i, NULL);
float frame_length = frame_length_i / 1000.0; float frame_length = frame_length_i / 1000.0;
@ -273,7 +258,7 @@ void Particle::updateLight()
else else
light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); 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_color.set(m_alpha*255,
m_light * m_base_color.getRed() / 255, m_light * m_base_color.getRed() / 255,
m_light * m_base_color.getGreen() / 255, m_light * m_base_color.getGreen() / 255,
@ -290,12 +275,12 @@ void Particle::updateVertices()
else else
scale = v2f(1.f, 1.f); 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(); const v2u32 texsize = m_material.getTexture(0)->getSize();
v2f texcoord, framesize_f; v2f texcoord, framesize_f;
v2u32 framesize; v2u32 framesize;
texcoord = m_animation.getTextureCoords(texsize, m_animation_frame); texcoord = m_p.animation.getTextureCoords(texsize, m_animation_frame);
m_animation.determineParams(texsize, NULL, NULL, &framesize); m_p.animation.determineParams(texsize, NULL, NULL, &framesize);
framesize_f = v2f(framesize.X / (float) texsize.X, framesize.Y / (float) texsize.Y); framesize_f = v2f(framesize.X / (float) texsize.X, framesize.Y / (float) texsize.Y);
tx0 = m_texpos.X + texcoord.X; tx0 = m_texpos.X + texcoord.X;
@ -309,7 +294,7 @@ void Particle::updateVertices()
ty1 = m_texpos.Y + m_texsize.Y; ty1 = m_texpos.Y + m_texsize.Y;
} }
auto half = m_size * .5f, auto half = m_p.size * .5f,
hx = half * scale.X, hx = half * scale.X,
hy = half * scale.Y; hy = half * scale.Y;
m_vertices[0] = video::S3DVertex(-hx, -hy, m_vertices[0] = video::S3DVertex(-hx, -hy,
@ -328,7 +313,7 @@ void Particle::updateVertices()
m_box.reset(v3f()); m_box.reset(v3f());
for (video::S3DVertex &vertex : m_vertices) { for (video::S3DVertex &vertex : m_vertices) {
if (m_vertical) { if (m_p.vertical) {
v3f ppos = m_player->getPosition()/BS; v3f ppos = m_player->getPosition()/BS;
vertex.Pos.rotateXZBy(std::atan2(ppos.Z - m_pos.Z, ppos.X - m_pos.X) / vertex.Pos.rotateXZBy(std::atan2(ppos.Z - m_pos.Z, ppos.X - m_pos.X) /
core::DEGTORAD + 90); core::DEGTORAD + 90);
@ -345,25 +330,22 @@ void Particle::updateVertices()
*/ */
ParticleSpawner::ParticleSpawner( ParticleSpawner::ParticleSpawner(
IGameDef *gamedef, IGameDef *gamedef,
LocalPlayer *player, LocalPlayer *player,
const ParticleSpawnerParameters &p, const ParticleSpawnerParameters &params,
u16 attached_id, u16 attached_id,
std::unique_ptr<ClientTexture[]>& texpool, std::vector<ClientParticleTexture> &&texpool,
size_t texcount, ParticleManager *p_manager
ParticleManager *p_manager ) :
): m_active(0),
m_particlemanager(p_manager), p(p) 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); m_spawntimes.reserve(p.amount + 1);
for (u16 i = 0; i <= p.amount; i++) { for (u16 i = 0; i <= p.amount; i++) {
float spawntime = myrand_float() * p.time; float spawntime = myrand_float() * p.time;
@ -398,22 +380,22 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
fac = m_time / (p.time+0.1f); fac = m_time / (p.time+0.1f);
} }
auto r_pos = p.pos.blend(fac); auto r_pos = p.pos.blend(fac);
auto r_vel = p.vel.blend(fac); auto r_vel = p.vel.blend(fac);
auto r_acc = p.acc.blend(fac); auto r_acc = p.acc.blend(fac);
auto r_drag = p.drag.blend(fac); auto r_drag = p.drag.blend(fac);
auto r_radius = p.radius.blend(fac); auto r_radius = p.radius.blend(fac);
auto r_jitter = p.jitter.blend(fac); auto r_jitter = p.jitter.blend(fac);
auto r_bounce = p.bounce.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); 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 attractor_direction_obj = findObjectByID(env, p.attractor_direction_attachment);
auto r_exp = p.exptime.blend(fac); auto r_exp = p.exptime.blend(fac);
auto r_size = p.size.blend(fac); auto r_size = p.size.blend(fac);
auto r_attract = p.attract.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 ppos = m_player->getPosition() / BS;
v3f pos = r_pos.pickWithin(); v3f pos = r_pos.pickWithin();
@ -538,7 +520,7 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
p.copyCommon(pp); p.copyCommon(pp);
ClientTexRef texture; ClientParticleTexRef texture;
v2f texpos, texsize; v2f texpos, texsize;
video::SColor color(0xFFFFFFFF); video::SColor color(0xFFFFFFFF);
@ -549,9 +531,10 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
texpos, texsize, &color, p.node_tile)) texpos, texsize, &color, p.node_tile))
return; return;
} else { } else {
if (m_texcount == 0) if (m_texpool.size() == 0)
return; 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); texpos = v2f(0.0f, 0.0f);
texsize = v2f(1.0f, 1.0f); texsize = v2f(1.0f, 1.0f);
if (texture.tex->animated) if (texture.tex->animated)
@ -581,18 +564,17 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
pp.size = r_size.pickWithin(); pp.size = r_size.pickWithin();
++m_active; ++m_active;
auto pa = new Particle( m_particlemanager->addParticle(std::make_unique<Particle>(
m_gamedef, m_gamedef,
m_player, m_player,
env, env,
pp, pp,
texture, texture,
texpos, texpos,
texsize, texsize,
color color,
); this
pa->m_parent = this; ));
m_particlemanager->addParticle(pa);
} }
void ParticleSpawner::step(float dtime, ClientEnvironment *env) void ParticleSpawner::step(float dtime, ClientEnvironment *env)
@ -664,37 +646,51 @@ void ParticleManager::step(float dtime)
void ParticleManager::stepSpawners(float dtime) void ParticleManager::stepSpawners(float dtime)
{ {
MutexAutoLock lock(m_spawner_list_lock); MutexAutoLock lock(m_spawner_list_lock);
for (auto i = m_particle_spawners.begin(); i != m_particle_spawners.end();) {
if (i->second->getExpired()) { for (size_t i = 0; i < m_dying_particle_spawners.size();) {
// the particlespawner owns the textures, so we need to make // the particlespawner owns the textures, so we need to make
// sure there are no active particles before we free it // sure there are no active particles before we free it
if (i->second->m_active == 0) { if (!m_dying_particle_spawners[i]->hasActive()) {
delete i->second; m_dying_particle_spawners[i] = std::move(m_dying_particle_spawners.back());
m_particle_spawners.erase(i++); m_dying_particle_spawners.pop_back();
} else {
++i;
}
} else { } else {
i->second->step(dtime, m_env);
++i; ++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) void ParticleManager::stepParticles(float dtime)
{ {
MutexAutoLock lock(m_particle_list_lock); MutexAutoLock lock(m_particle_list_lock);
for (auto i = m_particles.begin(); i != m_particles.end();) {
if ((*i)->get_expired()) { for (size_t i = 0; i < m_particles.size();) {
if ((*i)->m_parent) { Particle &p = *m_particles[i];
assert((*i)->m_parent->m_active != 0); if (p.isExpired()) {
--(*i)->m_parent->m_active; ParticleSpawner *parent = p.getParent();
if (parent) {
assert(parent->hasActive());
parent->decrActive();
} }
(*i)->remove(); // remove scene node
delete *i; p.remove();
i = m_particles.erase(i); // delete
m_particles[i] = std::move(m_particles.back());
m_particles.pop_back();
} else { } else {
(*i)->step(dtime); p.step(dtime);
++i; ++i;
} }
} }
@ -704,17 +700,19 @@ void ParticleManager::clearAll()
{ {
MutexAutoLock lock(m_spawner_list_lock); MutexAutoLock lock(m_spawner_list_lock);
MutexAutoLock lock2(m_particle_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();) // clear particle spawners
{ m_particle_spawners.clear();
(*i)->remove(); m_dying_particle_spawners.clear();
delete *i;
i = m_particles.erase(i); // clear particles
for (std::unique_ptr<Particle> &p : m_particles) {
// remove scene node
p->remove();
// delete
p.reset();
} }
m_particles.clear();
} }
void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, 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; const ParticleSpawnerParameters &p = *event->add_particlespawner.p;
// texture pool // texture pool
std::unique_ptr<ClientTexture[]> texpool = nullptr; std::vector<ClientParticleTexture> texpool;
size_t txpsz = 0;
if (!p.texpool.empty()) { if (!p.texpool.empty()) {
txpsz = p.texpool.size(); size_t txpsz = p.texpool.size();
texpool = decltype(texpool)(new ClientTexture [txpsz]); texpool.reserve(txpsz);
for (size_t i = 0; i < txpsz; ++i) { 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 { } else {
// no texpool in use, use fallback texture // no texpool in use, use fallback texture
txpsz = 1; texpool.emplace_back(p.texture, client->tsrc());
texpool = decltype(texpool)(new ClientTexture[1] {
ClientTexture(p.texture, client->tsrc())
});
} }
auto toadd = new ParticleSpawner(client, player, addParticleSpawner(event->add_particlespawner.id,
p, std::make_unique<ParticleSpawner>(
event->add_particlespawner.attached_id, client,
texpool, player,
txpsz, p,
this); event->add_particlespawner.attached_id,
std::move(texpool),
addParticleSpawner(event->add_particlespawner.id, toadd); this)
);
delete event->add_particlespawner.p; delete event->add_particlespawner.p;
break; break;
@ -764,7 +758,8 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
case CE_SPAWN_PARTICLE: { case CE_SPAWN_PARTICLE: {
ParticleParameters &p = *event->spawn_particle; ParticleParameters &p = *event->spawn_particle;
ClientTexRef texture; ClientParticleTexRef texture;
std::unique_ptr<ClientParticleTexture> texstore;
v2f texpos, texsize; v2f texpos, texsize;
video::SColor color(0xFFFFFFFF); video::SColor color(0xFFFFFFFF);
@ -778,9 +773,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
/* with no particlespawner to own the texture, we need /* with no particlespawner to own the texture, we need
* to save it on the heap. it will be freed when the * to save it on the heap. it will be freed when the
* particle is destroyed */ * particle is destroyed */
auto texstore = new ClientTexture(p.texture, client->tsrc()); texstore = std::make_unique<ClientParticleTexture>(p.texture, client->tsrc());
texture = ClientTexRef(*texstore); texture = ClientParticleTexRef(*texstore);
texpos = v2f(0.0f, 0.0f); texpos = v2f(0.0f, 0.0f);
texsize = v2f(1.0f, 1.0f); texsize = v2f(1.0f, 1.0f);
} }
@ -790,10 +785,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
p.size = oldsize; p.size = oldsize;
if (texture.ref) { if (texture.ref) {
Particle *toadd = new Particle(client, player, m_env, addParticle(std::make_unique<Particle>(client, player, m_env,
p, texture, texpos, texsize, color); p, texture, texpos, texsize, color, nullptr,
std::move(texstore)));
addParticle(toadd);
} }
delete event->spawn_particle; delete event->spawn_particle;
@ -890,43 +884,53 @@ void ParticleManager::addNodeParticle(IGameDef *gamedef,
(f32)pos.Z + myrand_range(0.f, .5f) - .25f (f32)pos.Z + myrand_range(0.f, .5f) - .25f
); );
Particle *toadd = new Particle( addParticle(std::make_unique<Particle>(
gamedef, gamedef,
player, player,
m_env, m_env,
p, p,
ClientTexRef(ref), ClientParticleTexRef(ref),
texpos, texpos,
texsize, texsize,
color); color));
addParticle(toadd);
} }
void ParticleManager::reserveParticleSpace(size_t max_estimate) void ParticleManager::reserveParticleSpace(size_t max_estimate)
{ {
MutexAutoLock lock(m_particle_list_lock); MutexAutoLock lock(m_particle_list_lock);
m_particles.reserve(m_particles.size() + max_estimate); m_particles.reserve(m_particles.size() + max_estimate);
} }
void ParticleManager::addParticle(Particle *toadd) void ParticleManager::addParticle(std::unique_ptr<Particle> toadd)
{ {
MutexAutoLock lock(m_particle_list_lock); 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<ParticleSpawner> toadd)
{ {
MutexAutoLock lock(m_spawner_list_lock); 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) void ParticleManager::deleteParticleSpawner(u64 id)
{ {
MutexAutoLock lock(m_spawner_list_lock); MutexAutoLock lock(m_spawner_list_lock);
auto it = m_particle_spawners.find(id); auto it = m_particle_spawners.find(id);
if (it != m_particle_spawners.end()) { if (it != m_particle_spawners.end()) {
it->second->setDying(); m_dying_particle_spawners.push_back(std::move(it->second));
m_particle_spawners.erase(it);
} }
} }

View File

@ -31,33 +31,34 @@ class ClientEnvironment;
struct MapNode; struct MapNode;
struct ContentFeatures; struct ContentFeatures;
struct ClientTexture struct ClientParticleTexture
{ {
/* per-spawner structure used to store the ParticleTexture structs /* 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; ParticleTexture tex;
video::ITexture *ref = nullptr; video::ITexture *ref = nullptr;
ClientTexture() = default; ClientParticleTexture() = default;
ClientTexture(const ServerParticleTexture& p, ITextureSource *t): ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *t):
tex(p), tex(p),
ref(t->getTextureForMesh(p.string)) {}; ref(t->getTextureForMesh(p.string)) {};
}; };
struct ClientTexRef struct ClientParticleTexRef
{ {
/* per-particle structure used to avoid massively duplicating the /* per-particle structure used to avoid massively duplicating the
* fairly large ParticleTexture struct */ * fairly large ParticleTexture struct */
ParticleTexture* tex = nullptr; ParticleTexture *tex = nullptr;
video::ITexture* ref = nullptr; video::ITexture *ref = nullptr;
ClientTexRef() = default;
ClientParticleTexRef() = default;
/* constructor used by particles spawned from a spawner */ /* constructor used by particles spawned from a spawner */
ClientTexRef(ClientTexture& t): explicit ClientParticleTexRef(ClientParticleTexture &t):
tex(&t.tex), ref(t.ref) {}; tex(&t.tex), ref(t.ref) {};
/* constructor used for node particles */ /* constructor used for node particles */
ClientTexRef(decltype(ref) tp): ref(tp) {}; explicit ClientParticleTexRef(video::ITexture *tp): ref(tp) {};
}; };
class ParticleSpawner; class ParticleSpawner;
@ -70,12 +71,13 @@ public:
LocalPlayer *player, LocalPlayer *player,
ClientEnvironment *env, ClientEnvironment *env,
const ParticleParameters &p, const ParticleParameters &p,
const ClientTexRef &texture, const ClientParticleTexRef &texture,
v2f texpos, v2f texpos,
v2f texsize, v2f texsize,
video::SColor color video::SColor color,
ParticleSpawner *parent = nullptr,
std::unique_ptr<ClientParticleTexture> owned_texture = nullptr
); );
~Particle();
virtual const aabb3f &getBoundingBox() const virtual const aabb3f &getBoundingBox() const
{ {
@ -97,10 +99,10 @@ public:
void step(float dtime); void step(float dtime);
bool get_expired () bool isExpired ()
{ return m_expiration < m_time; } { return m_expiration < m_time; }
ParticleSpawner *m_parent; ParticleSpawner *getParent() { return m_parent; }
private: private:
void updateLight(); void updateLight();
@ -115,33 +117,27 @@ private:
IGameDef *m_gamedef; IGameDef *m_gamedef;
aabb3f m_box; aabb3f m_box;
aabb3f m_collisionbox; aabb3f m_collisionbox;
ClientTexRef m_texture; ClientParticleTexRef m_texture;
video::SMaterial m_material; video::SMaterial m_material;
v2f m_texpos; v2f m_texpos;
v2f m_texsize; v2f m_texsize;
v3f m_pos; v3f m_pos;
v3f m_velocity; v3f m_velocity;
v3f m_acceleration; v3f m_acceleration;
v3f m_drag; const ParticleParameters m_p;
ParticleParamTypes::v3fRange m_jitter;
ParticleParamTypes::f32Range m_bounce;
LocalPlayer *m_player; LocalPlayer *m_player;
float m_size;
//! Color without lighting //! Color without lighting
video::SColor m_base_color; video::SColor m_base_color;
//! Final rendered color //! Final rendered color
video::SColor m_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; float m_animation_time = 0.0f;
int m_animation_frame = 0; int m_animation_frame = 0;
u8 m_glow;
float m_alpha = 0.0f; float m_alpha = 0.0f;
ParticleSpawner *m_parent = nullptr;
// Used if not spawned from a particlespawner
std::unique_ptr<ClientParticleTexture> m_owned_texture;
}; };
class ParticleSpawner class ParticleSpawner
@ -149,31 +145,30 @@ class ParticleSpawner
public: public:
ParticleSpawner(IGameDef *gamedef, ParticleSpawner(IGameDef *gamedef,
LocalPlayer *player, LocalPlayer *player,
const ParticleSpawnerParameters &p, const ParticleSpawnerParameters &params,
u16 attached_id, u16 attached_id,
std::unique_ptr<ClientTexture[]> &texpool, std::vector<ClientParticleTexture> &&texpool,
size_t texcount, ParticleManager *p_manager);
ParticleManager* p_manager);
void step(float dtime, ClientEnvironment *env); void step(float dtime, ClientEnvironment *env);
size_t m_active;
bool getExpired() const bool getExpired() const
{ return m_dying || (p.amount <= 0 && p.time != 0); } { return p.amount <= 0 && p.time != 0; }
void setDying() { m_dying = true; }
bool hasActive() const { return m_active != 0; }
void decrActive() { m_active -= 1; }
private: private:
void spawnParticle(ClientEnvironment *env, float radius, void spawnParticle(ClientEnvironment *env, float radius,
const core::matrix4 *attached_absolute_pos_rot_matrix); const core::matrix4 *attached_absolute_pos_rot_matrix);
size_t m_active;
ParticleManager *m_particlemanager; ParticleManager *m_particlemanager;
float m_time; float m_time;
bool m_dying;
IGameDef *m_gamedef; IGameDef *m_gamedef;
LocalPlayer *m_player; LocalPlayer *m_player;
ParticleSpawnerParameters p; ParticleSpawnerParameters p;
std::unique_ptr<ClientTexture[]> m_texpool; std::vector<ClientParticleTexture> m_texpool;
size_t m_texcount; size_t m_texcount;
std::vector<float> m_spawntimes; std::vector<float> m_spawntimes;
u16 m_attached_id; u16 m_attached_id;
@ -187,6 +182,7 @@ class ParticleManager
friend class ParticleSpawner; friend class ParticleSpawner;
public: public:
ParticleManager(ClientEnvironment* env); ParticleManager(ClientEnvironment* env);
DISABLE_CLASS_COPY(ParticleManager)
~ParticleManager(); ~ParticleManager();
void step (float dtime); void step (float dtime);
@ -219,10 +215,10 @@ protected:
ParticleParameters &p, video::ITexture **texture, v2f &texpos, ParticleParameters &p, video::ITexture **texture, v2f &texpos,
v2f &texsize, video::SColor *color, u8 tilenum = 0); v2f &texsize, video::SColor *color, u8 tilenum = 0);
void addParticle(Particle* toadd); void addParticle(std::unique_ptr<Particle> toadd);
private: private:
void addParticleSpawner(u64 id, ParticleSpawner *toadd); void addParticleSpawner(u64 id, std::unique_ptr<ParticleSpawner> toadd);
void deleteParticleSpawner(u64 id); void deleteParticleSpawner(u64 id);
void stepParticles(float dtime); void stepParticles(float dtime);
@ -230,13 +226,14 @@ private:
void clearAll(); void clearAll();
std::vector<Particle*> m_particles; std::vector<std::unique_ptr<Particle>> m_particles;
std::unordered_map<u64, ParticleSpawner*> m_particle_spawners; std::unordered_map<u64, std::unique_ptr<ParticleSpawner>> m_particle_spawners;
std::vector<std::unique_ptr<ParticleSpawner>> m_dying_particle_spawners;
// Start the particle spawner ids generated from here after u32_max. lower values are // Start the particle spawner ids generated from here after u32_max. lower values are
// for server sent spawners. // for server sent spawners.
u64 m_next_particle_spawner_id = U32_MAX + 1; u64 m_next_particle_spawner_id = U32_MAX + 1;
ClientEnvironment* m_env; ClientEnvironment *m_env;
std::mutex m_particle_list_lock; std::mutex m_particle_list_lock;
std::mutex m_spawner_list_lock; std::mutex m_spawner_list_lock;
}; };