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 bool is_attached = false;
257 v3f attached_pos = v3f(0,0,0);
258 float attached_yaw = 0;
259 if (m_attached_id != 0) {
260 if (ClientActiveObject *attached = env->getActiveObject(m_attached_id)) {
261 attached_pos = attached->getPosition() / BS;
262 attached_yaw = attached->getYaw();
269 if (m_spawntime != 0) // Spawner exists for a predefined timespan
271 for(std::vector<float>::iterator i = m_spawntimes.begin();
272 i != m_spawntimes.end();)
274 if ((*i) <= m_time && m_amount > 0)
278 // Pretend to, but don't actually spawn a
279 // particle if it is attached to an unloaded
282 v3f pos = random_v3f(m_minpos, m_maxpos);
283 v3f vel = random_v3f(m_minvel, m_maxvel);
284 v3f acc = random_v3f(m_minacc, m_maxacc);
287 // Apply attachment yaw and position
288 pos.rotateXZBy(attached_yaw);
290 vel.rotateXZBy(attached_yaw);
291 acc.rotateXZBy(attached_yaw);
294 float exptime = rand()/(float)RAND_MAX
295 *(m_maxexptime-m_minexptime)
297 float size = rand()/(float)RAND_MAX
298 *(m_maxsize-m_minsize)
301 Particle* toadd = new Particle(
311 m_collisiondetection,
317 m_particlemanager->addParticle(toadd);
319 i = m_spawntimes.erase(i);
327 else // Spawner exists for an infinity timespan, spawn on a per-second base
329 // Skip this step if attached to an unloaded object
332 for (int i = 0; i <= m_amount; i++)
334 if (rand()/(float)RAND_MAX < dtime)
336 v3f pos = random_v3f(m_minpos, m_maxpos);
337 v3f vel = random_v3f(m_minvel, m_maxvel);
338 v3f acc = random_v3f(m_minacc, m_maxacc);
341 // Apply attachment yaw and position
342 pos.rotateXZBy(attached_yaw);
344 vel.rotateXZBy(attached_yaw);
345 acc.rotateXZBy(attached_yaw);
348 float exptime = rand()/(float)RAND_MAX
349 *(m_maxexptime-m_minexptime)
351 float size = rand()/(float)RAND_MAX
352 *(m_maxsize-m_minsize)
355 Particle* toadd = new Particle(
365 m_collisiondetection,
371 m_particlemanager->addParticle(toadd);
378 ParticleManager::ParticleManager(ClientEnvironment* env) :
382 ParticleManager::~ParticleManager()
387 void ParticleManager::step(float dtime)
389 stepParticles (dtime);
390 stepSpawners (dtime);
393 void ParticleManager::stepSpawners (float dtime)
395 MutexAutoLock lock(m_spawner_list_lock);
396 for (std::map<u32, ParticleSpawner*>::iterator i =
397 m_particle_spawners.begin();
398 i != m_particle_spawners.end();)
400 if (i->second->get_expired())
403 m_particle_spawners.erase(i++);
407 i->second->step(dtime, m_env);
413 void ParticleManager::stepParticles (float dtime)
415 MutexAutoLock lock(m_particle_list_lock);
416 for(std::vector<Particle*>::iterator i = m_particles.begin();
417 i != m_particles.end();)
419 if ((*i)->get_expired())
423 i = m_particles.erase(i);
433 void ParticleManager::clearAll ()
435 MutexAutoLock lock(m_spawner_list_lock);
436 MutexAutoLock lock2(m_particle_list_lock);
437 for(std::map<u32, ParticleSpawner*>::iterator i =
438 m_particle_spawners.begin();
439 i != m_particle_spawners.end();)
442 m_particle_spawners.erase(i++);
445 for(std::vector<Particle*>::iterator i =
447 i != m_particles.end();)
451 i = m_particles.erase(i);
455 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
456 scene::ISceneManager* smgr, LocalPlayer *player)
458 switch (event->type) {
459 case CE_DELETE_PARTICLESPAWNER: {
460 MutexAutoLock lock(m_spawner_list_lock);
461 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
462 m_particle_spawners.end()) {
463 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
464 m_particle_spawners.erase(event->delete_particlespawner.id);
466 // no allocated memory in delete event
469 case CE_ADD_PARTICLESPAWNER: {
471 MutexAutoLock lock(m_spawner_list_lock);
472 if (m_particle_spawners.find(event->add_particlespawner.id) !=
473 m_particle_spawners.end()) {
474 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
475 m_particle_spawners.erase(event->add_particlespawner.id);
479 video::ITexture *texture =
480 gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
482 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
483 event->add_particlespawner.amount,
484 event->add_particlespawner.spawntime,
485 *event->add_particlespawner.minpos,
486 *event->add_particlespawner.maxpos,
487 *event->add_particlespawner.minvel,
488 *event->add_particlespawner.maxvel,
489 *event->add_particlespawner.minacc,
490 *event->add_particlespawner.maxacc,
491 event->add_particlespawner.minexptime,
492 event->add_particlespawner.maxexptime,
493 event->add_particlespawner.minsize,
494 event->add_particlespawner.maxsize,
495 event->add_particlespawner.collisiondetection,
496 event->add_particlespawner.collision_removal,
497 event->add_particlespawner.attached_id,
498 event->add_particlespawner.vertical,
500 event->add_particlespawner.id,
503 /* delete allocated content of event */
504 delete event->add_particlespawner.minpos;
505 delete event->add_particlespawner.maxpos;
506 delete event->add_particlespawner.minvel;
507 delete event->add_particlespawner.maxvel;
508 delete event->add_particlespawner.minacc;
509 delete event->add_particlespawner.texture;
510 delete event->add_particlespawner.maxacc;
513 MutexAutoLock lock(m_spawner_list_lock);
514 m_particle_spawners.insert(
515 std::pair<u32, ParticleSpawner*>(
516 event->add_particlespawner.id,
521 case CE_SPAWN_PARTICLE: {
522 video::ITexture *texture =
523 gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
525 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
526 *event->spawn_particle.pos,
527 *event->spawn_particle.vel,
528 *event->spawn_particle.acc,
529 event->spawn_particle.expirationtime,
530 event->spawn_particle.size,
531 event->spawn_particle.collisiondetection,
532 event->spawn_particle.collision_removal,
533 event->spawn_particle.vertical,
540 delete event->spawn_particle.pos;
541 delete event->spawn_particle.vel;
542 delete event->spawn_particle.acc;
550 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
551 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
553 for (u16 j = 0; j < 32; j++) // set the amount of particles here
555 addNodeParticle(gamedef, smgr, player, pos, tiles);
559 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
560 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
562 addNodeParticle(gamedef, smgr, player, pos, tiles);
565 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
566 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
569 u8 texid = myrand_range(0, 5);
570 video::ITexture *texture;
572 // Only use first frame of animated texture
573 if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION)
574 texture = tiles[texid].frames[0].texture;
576 texture = tiles[texid].texture;
578 float size = rand() % 64 / 512.;
579 float visual_size = BS * size;
580 v2f texsize(size * 2, size * 2);
582 texpos.X = ((rand() % 64) / 64. - texsize.X);
583 texpos.Y = ((rand() % 64) / 64. - texsize.Y);
586 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
588 (rand() % 100 / 50. - 1) / 1.5);
590 v3f acceleration(0,-9,0);
591 v3f particlepos = v3f(
592 (f32) pos.X + rand() %100 /200. - 0.25,
593 (f32) pos.Y + rand() %100 /200. - 0.25,
594 (f32) pos.Z + rand() %100 /200. - 0.25
597 Particle* toadd = new Particle(
605 rand() % 100 / 100., // expiration time
617 void ParticleManager::addParticle(Particle* toadd)
619 MutexAutoLock lock(m_particle_list_lock);
620 m_particles.push_back(toadd);