mirror of
https://github.com/minetest/minetest.git
synced 2024-12-24 15:12:23 +01:00
Particle cleanup (#13394)
This commit is contained in:
parent
352a403bd0
commit
12e98678f6
@ -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<ClientParticleTexture> 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<ClientTexture[]>& 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<ClientParticleTexture> &&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<Particle>(
|
||||
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<Particle> &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<ClientTexture[]> texpool = nullptr;
|
||||
size_t txpsz = 0;
|
||||
std::vector<ClientParticleTexture> 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<ParticleSpawner>(
|
||||
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<ClientParticleTexture> 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<ClientParticleTexture>(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<Particle>(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<Particle>(
|
||||
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<Particle> 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<ParticleSpawner> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<ClientParticleTexture> 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<ClientParticleTexture> 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<ClientTexture[]> &texpool,
|
||||
size_t texcount,
|
||||
ParticleManager* p_manager);
|
||||
std::vector<ClientParticleTexture> &&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<ClientTexture[]> m_texpool;
|
||||
std::vector<ClientParticleTexture> m_texpool;
|
||||
size_t m_texcount;
|
||||
std::vector<float> 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<Particle> toadd);
|
||||
|
||||
private:
|
||||
void addParticleSpawner(u64 id, ParticleSpawner *toadd);
|
||||
void addParticleSpawner(u64 id, std::unique_ptr<ParticleSpawner> toadd);
|
||||
void deleteParticleSpawner(u64 id);
|
||||
|
||||
void stepParticles(float dtime);
|
||||
@ -230,13 +226,14 @@ private:
|
||||
|
||||
void clearAll();
|
||||
|
||||
std::vector<Particle*> m_particles;
|
||||
std::unordered_map<u64, ParticleSpawner*> m_particle_spawners;
|
||||
std::vector<std::unique_ptr<Particle>> m_particles;
|
||||
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
|
||||
// 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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user