mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-11-04 09:15:29 +01:00 
			
		
		
		
	Particle cleanup (#13394)
This commit is contained in:
		@@ -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;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user