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, 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_vertical = vertical;
241 for (u16 i = 0; i<=m_amount; i++)
243 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
244 m_spawntimes.push_back(spawntime);
248 ParticleSpawner::~ParticleSpawner() {}
250 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
254 if (m_spawntime != 0) // Spawner exists for a predefined timespan
256 for(std::vector<float>::iterator i = m_spawntimes.begin();
257 i != m_spawntimes.end();)
259 if ((*i) <= m_time && m_amount > 0)
263 v3f pos = random_v3f(m_minpos, m_maxpos);
264 v3f vel = random_v3f(m_minvel, m_maxvel);
265 v3f acc = random_v3f(m_minacc, m_maxacc);
266 float exptime = rand()/(float)RAND_MAX
267 *(m_maxexptime-m_minexptime)
269 float size = rand()/(float)RAND_MAX
270 *(m_maxsize-m_minsize)
273 Particle* toadd = new Particle(
283 m_collisiondetection,
289 m_particlemanager->addParticle(toadd);
290 i = m_spawntimes.erase(i);
298 else // Spawner exists for an infinity timespan, spawn on a per-second base
300 for (int i = 0; i <= m_amount; i++)
302 if (rand()/(float)RAND_MAX < dtime)
304 v3f pos = random_v3f(m_minpos, m_maxpos);
305 v3f vel = random_v3f(m_minvel, m_maxvel);
306 v3f acc = random_v3f(m_minacc, m_maxacc);
307 float exptime = rand()/(float)RAND_MAX
308 *(m_maxexptime-m_minexptime)
310 float size = rand()/(float)RAND_MAX
311 *(m_maxsize-m_minsize)
314 Particle* toadd = new Particle(
324 m_collisiondetection,
330 m_particlemanager->addParticle(toadd);
337 ParticleManager::ParticleManager(ClientEnvironment* env) :
341 ParticleManager::~ParticleManager()
346 void ParticleManager::step(float dtime)
348 stepParticles (dtime);
349 stepSpawners (dtime);
352 void ParticleManager::stepSpawners (float dtime)
354 MutexAutoLock lock(m_spawner_list_lock);
355 for (std::map<u32, ParticleSpawner*>::iterator i =
356 m_particle_spawners.begin();
357 i != m_particle_spawners.end();)
359 if (i->second->get_expired())
362 m_particle_spawners.erase(i++);
366 i->second->step(dtime, m_env);
372 void ParticleManager::stepParticles (float dtime)
374 MutexAutoLock lock(m_particle_list_lock);
375 for(std::vector<Particle*>::iterator i = m_particles.begin();
376 i != m_particles.end();)
378 if ((*i)->get_expired())
382 i = m_particles.erase(i);
392 void ParticleManager::clearAll ()
394 MutexAutoLock lock(m_spawner_list_lock);
395 MutexAutoLock lock2(m_particle_list_lock);
396 for(std::map<u32, ParticleSpawner*>::iterator i =
397 m_particle_spawners.begin();
398 i != m_particle_spawners.end();)
401 m_particle_spawners.erase(i++);
404 for(std::vector<Particle*>::iterator i =
406 i != m_particles.end();)
410 i = m_particles.erase(i);
414 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
415 scene::ISceneManager* smgr, LocalPlayer *player)
417 switch (event->type) {
418 case CE_DELETE_PARTICLESPAWNER: {
419 MutexAutoLock lock(m_spawner_list_lock);
420 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
421 m_particle_spawners.end()) {
422 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
423 m_particle_spawners.erase(event->delete_particlespawner.id);
425 // no allocated memory in delete event
428 case CE_ADD_PARTICLESPAWNER: {
430 MutexAutoLock lock(m_spawner_list_lock);
431 if (m_particle_spawners.find(event->add_particlespawner.id) !=
432 m_particle_spawners.end()) {
433 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
434 m_particle_spawners.erase(event->add_particlespawner.id);
438 video::ITexture *texture =
439 gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
441 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
442 event->add_particlespawner.amount,
443 event->add_particlespawner.spawntime,
444 *event->add_particlespawner.minpos,
445 *event->add_particlespawner.maxpos,
446 *event->add_particlespawner.minvel,
447 *event->add_particlespawner.maxvel,
448 *event->add_particlespawner.minacc,
449 *event->add_particlespawner.maxacc,
450 event->add_particlespawner.minexptime,
451 event->add_particlespawner.maxexptime,
452 event->add_particlespawner.minsize,
453 event->add_particlespawner.maxsize,
454 event->add_particlespawner.collisiondetection,
455 event->add_particlespawner.collision_removal,
456 event->add_particlespawner.vertical,
458 event->add_particlespawner.id,
461 /* delete allocated content of event */
462 delete event->add_particlespawner.minpos;
463 delete event->add_particlespawner.maxpos;
464 delete event->add_particlespawner.minvel;
465 delete event->add_particlespawner.maxvel;
466 delete event->add_particlespawner.minacc;
467 delete event->add_particlespawner.texture;
468 delete event->add_particlespawner.maxacc;
471 MutexAutoLock lock(m_spawner_list_lock);
472 m_particle_spawners.insert(
473 std::pair<u32, ParticleSpawner*>(
474 event->add_particlespawner.id,
479 case CE_SPAWN_PARTICLE: {
480 video::ITexture *texture =
481 gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
483 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
484 *event->spawn_particle.pos,
485 *event->spawn_particle.vel,
486 *event->spawn_particle.acc,
487 event->spawn_particle.expirationtime,
488 event->spawn_particle.size,
489 event->spawn_particle.collisiondetection,
490 event->spawn_particle.collision_removal,
491 event->spawn_particle.vertical,
498 delete event->spawn_particle.pos;
499 delete event->spawn_particle.vel;
500 delete event->spawn_particle.acc;
508 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
509 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
511 for (u16 j = 0; j < 32; j++) // set the amount of particles here
513 addNodeParticle(gamedef, smgr, player, pos, tiles);
517 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
518 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
520 addNodeParticle(gamedef, smgr, player, pos, tiles);
523 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
524 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
527 u8 texid = myrand_range(0, 5);
528 video::ITexture *texture = tiles[texid].texture;
530 // Only use first frame of animated texture
532 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
533 ymax /= tiles[texid].animation_frame_count;
535 float size = rand() % 64 / 512.;
536 float visual_size = BS * size;
537 v2f texsize(size * 2, ymax * size * 2);
539 texpos.X = ((rand() % 64) / 64. - texsize.X);
540 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
543 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
545 (rand() % 100 / 50. - 1) / 1.5);
547 v3f acceleration(0,-9,0);
548 v3f particlepos = v3f(
549 (f32) pos.X + rand() %100 /200. - 0.25,
550 (f32) pos.Y + rand() %100 /200. - 0.25,
551 (f32) pos.Z + rand() %100 /200. - 0.25
554 Particle* toadd = new Particle(
562 rand() % 100 / 100., // expiration time
574 void ParticleManager::addParticle(Particle* toadd)
576 MutexAutoLock lock(m_particle_list_lock);
577 m_particles.push_back(toadd);