mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 10:03:45 +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(
|
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 ¶ms,
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 ¶ms,
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user