3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "particles.h"
21 #include "constants.h"
24 #include "client/tile.h"
26 #include "collision.h"
28 #include "util/numeric.h"
30 #include "environment.h"
31 #include "clientmap.h"
39 v3f random_v3f(v3f min, v3f max)
41 return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X,
42 rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y,
43 rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
48 scene::ISceneManager* smgr,
50 ClientEnvironment *env,
56 bool collisiondetection,
57 bool collision_removal,
59 video::ITexture *texture,
63 scene::ISceneNode(smgr->getRootSceneNode(), smgr)
70 m_material.setFlag(video::EMF_LIGHTING, false);
71 m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
72 m_material.setFlag(video::EMF_BILINEAR_FILTER, false);
73 m_material.setFlag(video::EMF_FOG_ENABLE, true);
74 m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
75 m_material.setTexture(0, texture);
82 m_velocity = velocity;
83 m_acceleration = acceleration;
84 m_expiration = expirationtime;
88 m_collisiondetection = collisiondetection;
89 m_collision_removal = collision_removal;
90 m_vertical = vertical;
93 m_collisionbox = aabb3f
94 (-size/2,-size/2,-size/2,size/2,size/2,size/2);
95 this->setAutomaticCulling(scene::EAC_OFF);
104 Particle::~Particle()
108 void Particle::OnRegisterSceneNode()
111 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
113 ISceneNode::OnRegisterSceneNode();
116 void Particle::render()
118 video::IVideoDriver* driver = SceneManager->getVideoDriver();
119 driver->setMaterial(m_material);
120 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
122 u16 indices[] = {0,1,2, 2,3,0};
123 driver->drawVertexPrimitiveList(m_vertices, 4,
124 indices, 2, video::EVT_STANDARD,
125 scene::EPT_TRIANGLES, video::EIT_16BIT);
128 void Particle::step(float dtime)
131 if (m_collisiondetection) {
132 aabb3f box = m_collisionbox;
133 v3f p_pos = m_pos * BS;
134 v3f p_velocity = m_velocity * BS;
135 collisionMoveResult r = collisionMoveSimple(m_env,
136 m_gamedef, BS * 0.5, box, 0, dtime, &p_pos,
137 &p_velocity, m_acceleration * BS);
138 if (m_collision_removal && r.collides) {
139 // force expiration of the particle
143 m_velocity = p_velocity / BS;
146 m_velocity += m_acceleration * dtime;
147 m_pos += m_velocity * dtime;
157 void Particle::updateLight()
167 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
169 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
171 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
173 m_light = decode_light(light);
176 void Particle::updateVertices()
178 video::SColor c(255, m_light, m_light, m_light);
179 f32 tx0 = m_texpos.X;
180 f32 tx1 = m_texpos.X + m_texsize.X;
181 f32 ty0 = m_texpos.Y;
182 f32 ty1 = m_texpos.Y + m_texsize.Y;
184 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
186 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
188 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
190 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
193 v3s16 camera_offset = m_env->getCameraOffset();
194 for(u16 i=0; i<4; i++)
197 v3f ppos = m_player->getPosition()/BS;
198 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
200 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
201 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
203 m_box.addInternalPoint(m_vertices[i].Pos);
204 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
212 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
213 u16 amount, float time,
214 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
215 float minexptime, float maxexptime, float minsize, float maxsize,
216 bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical,
217 video::ITexture *texture, u32 id, ParticleManager *p_manager) :
218 m_particlemanager(p_manager)
231 m_minexptime = minexptime;
232 m_maxexptime = maxexptime;
235 m_collisiondetection = collisiondetection;
236 m_collision_removal = collision_removal;
237 m_attached_id = attached_id;
238 m_vertical = vertical;
242 for (u16 i = 0; i<=m_amount; i++)
244 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
245 m_spawntimes.push_back(spawntime);
249 ParticleSpawner::~ParticleSpawner() {}
251 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
255 bool unloaded = false;
256 v3f attached_offset = v3f(0,0,0);
257 if (m_attached_id != 0) {
258 if (ClientActiveObject *attached = env->getActiveObject(m_attached_id))
259 attached_offset = attached->getPosition() / BS;
264 if (m_spawntime != 0) // Spawner exists for a predefined timespan
266 for(std::vector<float>::iterator i = m_spawntimes.begin();
267 i != m_spawntimes.end();)
269 if ((*i) <= m_time && m_amount > 0)
273 // Pretend to, but don't actually spawn a
274 // particle if it is attached to an unloaded
277 v3f pos = random_v3f(m_minpos, m_maxpos);
278 v3f vel = random_v3f(m_minvel, m_maxvel);
279 v3f acc = random_v3f(m_minacc, m_maxacc);
280 // Make relative to offest
281 pos += attached_offset;
282 float exptime = rand()/(float)RAND_MAX
283 *(m_maxexptime-m_minexptime)
285 float size = rand()/(float)RAND_MAX
286 *(m_maxsize-m_minsize)
289 Particle* toadd = new Particle(
299 m_collisiondetection,
305 m_particlemanager->addParticle(toadd);
307 i = m_spawntimes.erase(i);
315 else // Spawner exists for an infinity timespan, spawn on a per-second base
317 // Skip this step if attached to an unloaded object
320 for (int i = 0; i <= m_amount; i++)
322 if (rand()/(float)RAND_MAX < dtime)
324 v3f pos = random_v3f(m_minpos, m_maxpos)
326 v3f vel = random_v3f(m_minvel, m_maxvel);
327 v3f acc = random_v3f(m_minacc, m_maxacc);
328 float exptime = rand()/(float)RAND_MAX
329 *(m_maxexptime-m_minexptime)
331 float size = rand()/(float)RAND_MAX
332 *(m_maxsize-m_minsize)
335 Particle* toadd = new Particle(
345 m_collisiondetection,
351 m_particlemanager->addParticle(toadd);
358 ParticleManager::ParticleManager(ClientEnvironment* env) :
362 ParticleManager::~ParticleManager()
367 void ParticleManager::step(float dtime)
369 stepParticles (dtime);
370 stepSpawners (dtime);
373 void ParticleManager::stepSpawners (float dtime)
375 MutexAutoLock lock(m_spawner_list_lock);
376 for (std::map<u32, ParticleSpawner*>::iterator i =
377 m_particle_spawners.begin();
378 i != m_particle_spawners.end();)
380 if (i->second->get_expired())
383 m_particle_spawners.erase(i++);
387 i->second->step(dtime, m_env);
393 void ParticleManager::stepParticles (float dtime)
395 MutexAutoLock lock(m_particle_list_lock);
396 for(std::vector<Particle*>::iterator i = m_particles.begin();
397 i != m_particles.end();)
399 if ((*i)->get_expired())
403 i = m_particles.erase(i);
413 void ParticleManager::clearAll ()
415 MutexAutoLock lock(m_spawner_list_lock);
416 MutexAutoLock lock2(m_particle_list_lock);
417 for(std::map<u32, ParticleSpawner*>::iterator i =
418 m_particle_spawners.begin();
419 i != m_particle_spawners.end();)
422 m_particle_spawners.erase(i++);
425 for(std::vector<Particle*>::iterator i =
427 i != m_particles.end();)
431 i = m_particles.erase(i);
435 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
436 scene::ISceneManager* smgr, LocalPlayer *player)
438 switch (event->type) {
439 case CE_DELETE_PARTICLESPAWNER: {
440 MutexAutoLock lock(m_spawner_list_lock);
441 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
442 m_particle_spawners.end()) {
443 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
444 m_particle_spawners.erase(event->delete_particlespawner.id);
446 // no allocated memory in delete event
449 case CE_ADD_PARTICLESPAWNER: {
451 MutexAutoLock lock(m_spawner_list_lock);
452 if (m_particle_spawners.find(event->add_particlespawner.id) !=
453 m_particle_spawners.end()) {
454 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
455 m_particle_spawners.erase(event->add_particlespawner.id);
459 video::ITexture *texture =
460 gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
462 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
463 event->add_particlespawner.amount,
464 event->add_particlespawner.spawntime,
465 *event->add_particlespawner.minpos,
466 *event->add_particlespawner.maxpos,
467 *event->add_particlespawner.minvel,
468 *event->add_particlespawner.maxvel,
469 *event->add_particlespawner.minacc,
470 *event->add_particlespawner.maxacc,
471 event->add_particlespawner.minexptime,
472 event->add_particlespawner.maxexptime,
473 event->add_particlespawner.minsize,
474 event->add_particlespawner.maxsize,
475 event->add_particlespawner.collisiondetection,
476 event->add_particlespawner.collision_removal,
477 event->add_particlespawner.attached_id,
478 event->add_particlespawner.vertical,
480 event->add_particlespawner.id,
483 /* delete allocated content of event */
484 delete event->add_particlespawner.minpos;
485 delete event->add_particlespawner.maxpos;
486 delete event->add_particlespawner.minvel;
487 delete event->add_particlespawner.maxvel;
488 delete event->add_particlespawner.minacc;
489 delete event->add_particlespawner.texture;
490 delete event->add_particlespawner.maxacc;
493 MutexAutoLock lock(m_spawner_list_lock);
494 m_particle_spawners.insert(
495 std::pair<u32, ParticleSpawner*>(
496 event->add_particlespawner.id,
501 case CE_SPAWN_PARTICLE: {
502 video::ITexture *texture =
503 gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
505 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
506 *event->spawn_particle.pos,
507 *event->spawn_particle.vel,
508 *event->spawn_particle.acc,
509 event->spawn_particle.expirationtime,
510 event->spawn_particle.size,
511 event->spawn_particle.collisiondetection,
512 event->spawn_particle.collision_removal,
513 event->spawn_particle.vertical,
520 delete event->spawn_particle.pos;
521 delete event->spawn_particle.vel;
522 delete event->spawn_particle.acc;
530 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
531 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
533 for (u16 j = 0; j < 32; j++) // set the amount of particles here
535 addNodeParticle(gamedef, smgr, player, pos, tiles);
539 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
540 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
542 addNodeParticle(gamedef, smgr, player, pos, tiles);
545 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
546 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
549 u8 texid = myrand_range(0, 5);
550 video::ITexture *texture = tiles[texid].texture;
552 // Only use first frame of animated texture
554 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
555 ymax /= tiles[texid].animation_frame_count;
557 float size = rand() % 64 / 512.;
558 float visual_size = BS * size;
559 v2f texsize(size * 2, ymax * size * 2);
561 texpos.X = ((rand() % 64) / 64. - texsize.X);
562 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
565 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
567 (rand() % 100 / 50. - 1) / 1.5);
569 v3f acceleration(0,-9,0);
570 v3f particlepos = v3f(
571 (f32) pos.X + rand() %100 /200. - 0.25,
572 (f32) pos.Y + rand() %100 /200. - 0.25,
573 (f32) pos.Z + rand() %100 /200. - 0.25
576 Particle* toadd = new Particle(
584 rand() % 100 / 100., // expiration time
596 void ParticleManager::addParticle(Particle* toadd)
598 MutexAutoLock lock(m_particle_list_lock);
599 m_particles.push_back(toadd);