Skip to content
Permalink
Browse files

Particles: Add option to remove particles on collision

Adds the particle option `collision_removal = bool`

Some particles are hard to use right now since they either go through
solid blocks (without collision detection), and with collision
detection enabled they (e.g. raindrops) would just stop dead on the
floor and sit there until they expire, or worse, scrape along a wall
or ceiling.

We can solve the problem by adding a boolean flag that tells the
particle to be removed if it ever collides with something. This will
make it easier to add rain that doesn't fall through your roof or stick
on the top of it. Or clouds and smoke that don't go through trees.

Particles that collide with this flag are marked expired
unconditionally, causing them to be treated like normal expired
particles and cleaned up normally.

Documentation is adjusted accordingly.

An added bonus of this patch is that particles can potentially collide
many times with nodes, and this reduces the amount of collisions to 1
(max), which may end up reducing particle load on the client.
  • Loading branch information...
sofar authored and kwolekr committed May 28, 2016
1 parent 62d15ac commit d499ec483837fa7210176ef39beba2d5a3a5a61d
@@ -3885,6 +3885,9 @@ Definition tables
size = 1,
collisiondetection = false,
-- ^ collisiondetection: if true collides with physical objects
collision_removal = false,
-- ^ collision_removal: if true then particle is removed when it collides,
-- ^ requires collisiondetection = true to have any effect
vertical = false,
-- ^ vertical: if true faces player using y axis only
texture = "image.png",
@@ -3914,6 +3917,9 @@ Definition tables
-- ^ minsize/maxsize, minexptime/maxexptime (expirationtime)
collisiondetection = false,
-- ^ collisiondetection: if true uses collision detection
collision_removal = false,
-- ^ collision_removal: if true then particle is removed when it collides,
-- ^ requires collisiondetection = true to have any effect
vertical = false,
-- ^ vertical: if true faces player using y axis only
texture = "image.png",
@@ -182,6 +182,7 @@ struct ClientEvent
f32 expirationtime;
f32 size;
bool collisiondetection;
bool collision_removal;
bool vertical;
std::string *texture;
} spawn_particle;
@@ -199,6 +200,7 @@ struct ClientEvent
f32 minsize;
f32 maxsize;
bool collisiondetection;
bool collision_removal;
bool vertical;
std::string *texture;
u32 id;
@@ -898,8 +898,10 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
bool collisiondetection = readU8(is);
std::string texture = deSerializeLongString(is);
bool vertical = false;
bool collision_removal = false;
try {
vertical = readU8(is);
collision_removal = readU8(is);
} catch (...) {}

ClientEvent event;
@@ -910,6 +912,7 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
event.spawn_particle.expirationtime = expirationtime;
event.spawn_particle.size = size;
event.spawn_particle.collisiondetection = collisiondetection;
event.spawn_particle.collision_removal = collision_removal;
event.spawn_particle.vertical = vertical;
event.spawn_particle.texture = new std::string(texture);

@@ -942,8 +945,11 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
*pkt >> id;

bool vertical = false;
bool collision_removal = false;
try {
*pkt >> vertical;
*pkt >> collision_removal;

} catch (...) {}

ClientEvent event;
@@ -961,6 +967,7 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
event.add_particlespawner.minsize = minsize;
event.add_particlespawner.maxsize = maxsize;
event.add_particlespawner.collisiondetection = collisiondetection;
event.add_particlespawner.collision_removal = collision_removal;
event.add_particlespawner.vertical = vertical;
event.add_particlespawner.texture = new std::string(texture);
event.add_particlespawner.id = id;
@@ -474,6 +474,7 @@ enum ToClientCommand
u8 bool vertical
u32 len
u8[len] texture
u8 collision_removal
*/

TOCLIENT_ADD_PARTICLESPAWNER = 0x47,
@@ -495,6 +496,7 @@ enum ToClientCommand
u32 len
u8[len] texture
u32 id
u8 collision_removal
*/

TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY = 0x48,
@@ -54,6 +54,7 @@ Particle::Particle(
float expirationtime,
float size,
bool collisiondetection,
bool collision_removal,
bool vertical,
video::ITexture *texture,
v2f texpos,
@@ -85,6 +86,7 @@ Particle::Particle(
m_player = player;
m_size = size;
m_collisiondetection = collisiondetection;
m_collision_removal = collision_removal;
m_vertical = vertical;

// Irrlicht stuff
@@ -126,20 +128,21 @@ void Particle::render()
void Particle::step(float dtime)
{
m_time += dtime;
if (m_collisiondetection)
{
if (m_collisiondetection) {
aabb3f box = m_collisionbox;
v3f p_pos = m_pos*BS;
v3f p_velocity = m_velocity*BS;
collisionMoveSimple(m_env, m_gamedef,
BS*0.5, box,
0, dtime,
&p_pos, &p_velocity, m_acceleration * BS);
m_pos = p_pos/BS;
m_velocity = p_velocity/BS;
}
else
{
v3f p_pos = m_pos * BS;
v3f p_velocity = m_velocity * BS;
collisionMoveResult r = collisionMoveSimple(m_env,
m_gamedef, BS * 0.5, box, 0, dtime, &p_pos,
&p_velocity, m_acceleration * BS);
if (m_collision_removal && r.collides) {
// force expiration of the particle
m_expiration = -1.0;
} else {
m_pos = p_pos / BS;
m_velocity = p_velocity / BS;
}
} else {
m_velocity += m_acceleration * dtime;
m_pos += m_velocity * dtime;
}
@@ -210,8 +213,8 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
u16 amount, float time,
v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
float minexptime, float maxexptime, float minsize, float maxsize,
bool collisiondetection, bool vertical, video::ITexture *texture, u32 id,
ParticleManager *p_manager) :
bool collisiondetection, bool collision_removal, bool vertical,
video::ITexture *texture, u32 id, ParticleManager *p_manager) :
m_particlemanager(p_manager)
{
m_gamedef = gamedef;
@@ -230,6 +233,7 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
m_minsize = minsize;
m_maxsize = maxsize;
m_collisiondetection = collisiondetection;
m_collision_removal = collision_removal;
m_vertical = vertical;
m_texture = texture;
m_time = 0;
@@ -277,6 +281,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
exptime,
size,
m_collisiondetection,
m_collision_removal,
m_vertical,
m_texture,
v2f(0.0, 0.0),
@@ -317,6 +322,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
exptime,
size,
m_collisiondetection,
m_collision_removal,
m_vertical,
m_texture,
v2f(0.0, 0.0),
@@ -446,6 +452,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
event->add_particlespawner.minsize,
event->add_particlespawner.maxsize,
event->add_particlespawner.collisiondetection,
event->add_particlespawner.collision_removal,
event->add_particlespawner.vertical,
texture,
event->add_particlespawner.id,
@@ -480,6 +487,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
event->spawn_particle.expirationtime,
event->spawn_particle.size,
event->spawn_particle.collisiondetection,
event->spawn_particle.collision_removal,
event->spawn_particle.vertical,
texture,
v2f(0.0, 0.0),
@@ -555,6 +563,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
visual_size,
true,
false,
false,
texture,
texpos,
texsize);
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

struct ClientEvent;
class ParticleManager;
class ClientEnvironment;

class Particle : public scene::ISceneNode
{
@@ -45,6 +46,7 @@ class Particle : public scene::ISceneNode
float expirationtime,
float size,
bool collisiondetection,
bool collision_removal,
bool vertical,
video::ITexture *texture,
v2f texpos,
@@ -97,6 +99,7 @@ class Particle : public scene::ISceneNode
float m_size;
u8 m_light;
bool m_collisiondetection;
bool m_collision_removal;
bool m_vertical;
v3s16 m_camera_offset;
};
@@ -115,6 +118,7 @@ class ParticleSpawner
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection,
bool collision_removal,
bool vertical,
video::ITexture *texture,
u32 id,
@@ -148,6 +152,7 @@ class ParticleSpawner
video::ITexture *m_texture;
std::vector<float> m_spawntimes;
bool m_collisiondetection;
bool m_collision_removal;
bool m_vertical;

};
@@ -21,13 +21,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_internal.h"
#include "common/c_converter.h"
#include "server.h"
#include "particles.h"

// add_particle({pos=, velocity=, acceleration=, expirationtime=,
// size=, collisiondetection=, vertical=, texture=, player=})
// size=, collisiondetection=, collision_removal=, vertical=,
// texture=, player=})
// pos/velocity/acceleration = {x=num, y=num, z=num}
// expirationtime = num (seconds)
// size = num
// collisiondetection = bool
// collision_removal = bool
// vertical = bool
// texture = e.g."default_wood.png"
int ModApiParticles::l_add_particle(lua_State *L)
@@ -41,8 +44,8 @@ int ModApiParticles::l_add_particle(lua_State *L)
float expirationtime, size;
expirationtime = size = 1;

bool collisiondetection, vertical;
collisiondetection = vertical = false;
bool collisiondetection, vertical, collision_removal;
collisiondetection = vertical = collision_removal = false;

std::string texture = "";
std::string playername = "";
@@ -94,12 +97,14 @@ int ModApiParticles::l_add_particle(lua_State *L)
size = getfloatfield_default(L, 1, "size", 1);
collisiondetection = getboolfield_default(L, 1,
"collisiondetection", collisiondetection);
collision_removal = getboolfield_default(L, 1,
"collision_removal", collision_removal);
vertical = getboolfield_default(L, 1, "vertical", vertical);
texture = getstringfield_default(L, 1, "texture", "");
playername = getstringfield_default(L, 1, "playername", "");
}
getServer(L)->spawnParticle(playername, pos, vel, acc,
expirationtime, size, collisiondetection, vertical, texture);
getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size,
collisiondetection, collision_removal, vertical, texture);
return 1;
}

@@ -110,13 +115,15 @@ int ModApiParticles::l_add_particle(lua_State *L)
// minexptime=, maxexptime=,
// minsize=, maxsize=,
// collisiondetection=,
// collision_removal=,
// vertical=,
// texture=,
// player=})
// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num}
// minexptime/maxexptime = num (seconds)
// minsize/maxsize = num
// collisiondetection = bool
// collision_removal = bool
// vertical = bool
// texture = e.g."default_wood.png"
int ModApiParticles::l_add_particlespawner(lua_State *L)
@@ -129,8 +136,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
minpos= maxpos= minvel= maxvel= minacc= maxacc= v3f(0, 0, 0);
float time, minexptime, maxexptime, minsize, maxsize;
time= minexptime= maxexptime= minsize= maxsize= 1;
bool collisiondetection, vertical;
collisiondetection= vertical= false;
bool collisiondetection, vertical, collision_removal;
collisiondetection = vertical = collision_removal = false;
std::string texture = "";
std::string playername = "";

@@ -189,6 +196,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
maxsize = getfloatfield_default(L, 1, "maxsize", maxsize);
collisiondetection = getboolfield_default(L, 1,
"collisiondetection", collisiondetection);
collision_removal = getboolfield_default(L, 1,
"collision_removal", collision_removal);
vertical = getboolfield_default(L, 1, "vertical", vertical);
texture = getstringfield_default(L, 1, "texture", "");
playername = getstringfield_default(L, 1, "playername", "");
@@ -201,6 +210,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
minexptime, maxexptime,
minsize, maxsize,
collisiondetection,
collision_removal,
vertical,
texture, playername);
lua_pushnumber(L, id);
Oops, something went wrong.

0 comments on commit d499ec4

Please sign in to comment.
You can’t perform that action at this time.