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,
58 video::ITexture *texture,
62 scene::ISceneNode(smgr->getRootSceneNode(), smgr)
69 m_material.setFlag(video::EMF_LIGHTING, false);
70 m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
71 m_material.setFlag(video::EMF_BILINEAR_FILTER, false);
72 m_material.setFlag(video::EMF_FOG_ENABLE, true);
73 m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
74 m_material.setTexture(0, texture);
81 m_velocity = velocity;
82 m_acceleration = acceleration;
83 m_expiration = expirationtime;
87 m_collisiondetection = collisiondetection;
88 m_vertical = vertical;
91 m_collisionbox = core::aabbox3d<f32>
92 (-size/2,-size/2,-size/2,size/2,size/2,size/2);
93 this->setAutomaticCulling(scene::EAC_OFF);
102 Particle::~Particle()
106 void Particle::OnRegisterSceneNode()
109 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
111 ISceneNode::OnRegisterSceneNode();
114 void Particle::render()
116 video::IVideoDriver* driver = SceneManager->getVideoDriver();
117 driver->setMaterial(m_material);
118 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
120 u16 indices[] = {0,1,2, 2,3,0};
121 driver->drawVertexPrimitiveList(m_vertices, 4,
122 indices, 2, video::EVT_STANDARD,
123 scene::EPT_TRIANGLES, video::EIT_16BIT);
126 void Particle::step(float dtime)
129 if (m_collisiondetection)
131 core::aabbox3d<f32> box = m_collisionbox;
132 v3f p_pos = m_pos*BS;
133 v3f p_velocity = m_velocity*BS;
134 v3f p_acceleration = m_acceleration*BS;
135 collisionMoveSimple(m_env, m_gamedef,
138 p_pos, p_velocity, p_acceleration);
140 m_velocity = p_velocity/BS;
141 m_acceleration = p_acceleration/BS;
145 m_velocity += m_acceleration * dtime;
146 m_pos += m_velocity * dtime;
156 void Particle::updateLight()
166 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
168 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
170 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
172 m_light = decode_light(light);
175 void Particle::updateVertices()
177 video::SColor c(255, m_light, m_light, m_light);
178 f32 tx0 = m_texpos.X;
179 f32 tx1 = m_texpos.X + m_texsize.X;
180 f32 ty0 = m_texpos.Y;
181 f32 ty1 = m_texpos.Y + m_texsize.Y;
183 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
185 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
187 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
189 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
192 v3s16 camera_offset = m_env->getCameraOffset();
193 for(u16 i=0; i<4; i++)
196 v3f ppos = m_player->getPosition()/BS;
197 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
199 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
200 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
202 m_box.addInternalPoint(m_vertices[i].Pos);
203 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
211 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
212 u16 amount, float time,
213 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
214 float minexptime, float maxexptime, float minsize, float maxsize,
215 bool collisiondetection, bool vertical, video::ITexture *texture, u32 id,
216 ParticleManager *p_manager) :
217 m_particlemanager(p_manager)
230 m_minexptime = minexptime;
231 m_maxexptime = maxexptime;
234 m_collisiondetection = collisiondetection;
235 m_vertical = vertical;
239 for (u16 i = 0; i<=m_amount; i++)
241 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
242 m_spawntimes.push_back(spawntime);
246 ParticleSpawner::~ParticleSpawner() {}
248 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
252 if (m_spawntime != 0) // Spawner exists for a predefined timespan
254 for(std::vector<float>::iterator i = m_spawntimes.begin();
255 i != m_spawntimes.end();)
257 if ((*i) <= m_time && m_amount > 0)
261 v3f pos = random_v3f(m_minpos, m_maxpos);
262 v3f vel = random_v3f(m_minvel, m_maxvel);
263 v3f acc = random_v3f(m_minacc, m_maxacc);
264 float exptime = rand()/(float)RAND_MAX
265 *(m_maxexptime-m_minexptime)
267 float size = rand()/(float)RAND_MAX
268 *(m_maxsize-m_minsize)
271 Particle* toadd = new Particle(
281 m_collisiondetection,
286 m_particlemanager->addParticle(toadd);
287 i = m_spawntimes.erase(i);
295 else // Spawner exists for an infinity timespan, spawn on a per-second base
297 for (int i = 0; i <= m_amount; i++)
299 if (rand()/(float)RAND_MAX < dtime)
301 v3f pos = random_v3f(m_minpos, m_maxpos);
302 v3f vel = random_v3f(m_minvel, m_maxvel);
303 v3f acc = random_v3f(m_minacc, m_maxacc);
304 float exptime = rand()/(float)RAND_MAX
305 *(m_maxexptime-m_minexptime)
307 float size = rand()/(float)RAND_MAX
308 *(m_maxsize-m_minsize)
311 Particle* toadd = new Particle(
321 m_collisiondetection,
326 m_particlemanager->addParticle(toadd);
333 ParticleManager::ParticleManager(ClientEnvironment* env) :
337 ParticleManager::~ParticleManager()
342 void ParticleManager::step(float dtime)
344 stepParticles (dtime);
345 stepSpawners (dtime);
348 void ParticleManager::stepSpawners (float dtime)
350 JMutexAutoLock lock(m_spawner_list_lock);
351 for(std::map<u32, ParticleSpawner*>::iterator i =
352 m_particle_spawners.begin();
353 i != m_particle_spawners.end();)
355 if (i->second->get_expired())
358 m_particle_spawners.erase(i++);
362 i->second->step(dtime, m_env);
368 void ParticleManager::stepParticles (float dtime)
370 JMutexAutoLock lock(m_particle_list_lock);
371 for(std::vector<Particle*>::iterator i = m_particles.begin();
372 i != m_particles.end();)
374 if ((*i)->get_expired())
378 i = m_particles.erase(i);
388 void ParticleManager::clearAll ()
390 JMutexAutoLock lock(m_spawner_list_lock);
391 JMutexAutoLock lock2(m_particle_list_lock);
392 for(std::map<u32, ParticleSpawner*>::iterator i =
393 m_particle_spawners.begin();
394 i != m_particle_spawners.end();)
397 m_particle_spawners.erase(i++);
400 for(std::vector<Particle*>::iterator i =
402 i != m_particles.end();)
406 i = m_particles.erase(i);
410 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
411 scene::ISceneManager* smgr, LocalPlayer *player)
413 if (event->type == CE_DELETE_PARTICLESPAWNER) {
414 JMutexAutoLock lock(m_spawner_list_lock);
415 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
416 m_particle_spawners.end())
418 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
419 m_particle_spawners.erase(event->delete_particlespawner.id);
421 // no allocated memory in delete event
425 if (event->type == CE_ADD_PARTICLESPAWNER) {
428 JMutexAutoLock lock(m_spawner_list_lock);
429 if (m_particle_spawners.find(event->add_particlespawner.id) !=
430 m_particle_spawners.end())
432 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
433 m_particle_spawners.erase(event->add_particlespawner.id);
436 video::ITexture *texture =
437 gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
439 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
440 event->add_particlespawner.amount,
441 event->add_particlespawner.spawntime,
442 *event->add_particlespawner.minpos,
443 *event->add_particlespawner.maxpos,
444 *event->add_particlespawner.minvel,
445 *event->add_particlespawner.maxvel,
446 *event->add_particlespawner.minacc,
447 *event->add_particlespawner.maxacc,
448 event->add_particlespawner.minexptime,
449 event->add_particlespawner.maxexptime,
450 event->add_particlespawner.minsize,
451 event->add_particlespawner.maxsize,
452 event->add_particlespawner.collisiondetection,
453 event->add_particlespawner.vertical,
455 event->add_particlespawner.id,
458 /* delete allocated content of event */
459 delete event->add_particlespawner.minpos;
460 delete event->add_particlespawner.maxpos;
461 delete event->add_particlespawner.minvel;
462 delete event->add_particlespawner.maxvel;
463 delete event->add_particlespawner.minacc;
464 delete event->add_particlespawner.texture;
465 delete event->add_particlespawner.maxacc;
468 JMutexAutoLock lock(m_spawner_list_lock);
469 m_particle_spawners.insert(
470 std::pair<u32, ParticleSpawner*>(
471 event->add_particlespawner.id,
478 if (event->type == CE_SPAWN_PARTICLE) {
479 video::ITexture *texture =
480 gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
482 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
483 *event->spawn_particle.pos,
484 *event->spawn_particle.vel,
485 *event->spawn_particle.acc,
486 event->spawn_particle.expirationtime,
487 event->spawn_particle.size,
488 event->spawn_particle.collisiondetection,
489 event->spawn_particle.vertical,
496 delete event->spawn_particle.pos;
497 delete event->spawn_particle.vel;
498 delete event->spawn_particle.acc;
504 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
505 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
507 for (u16 j = 0; j < 32; j++) // set the amount of particles here
509 addNodeParticle(gamedef, smgr, player, pos, tiles);
513 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
514 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
516 addNodeParticle(gamedef, smgr, player, pos, tiles);
519 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
520 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
523 u8 texid = myrand_range(0, 5);
524 video::ITexture *texture = tiles[texid].texture;
526 // Only use first frame of animated texture
528 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
529 ymax /= tiles[texid].animation_frame_count;
531 float size = rand() % 64 / 512.;
532 float visual_size = BS * size;
533 v2f texsize(size * 2, ymax * size * 2);
535 texpos.X = ((rand() % 64) / 64. - texsize.X);
536 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
539 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
541 (rand() % 100 / 50. - 1) / 1.5);
543 v3f acceleration(0,-9,0);
544 v3f particlepos = v3f(
545 (f32) pos.X + rand() %100 /200. - 0.25,
546 (f32) pos.Y + rand() %100 /200. - 0.25,
547 (f32) pos.Z + rand() %100 /200. - 0.25
550 Particle* toadd = new Particle(
558 rand() % 100 / 100., // expiration time
569 void ParticleManager::addParticle(Particle* toadd)
571 JMutexAutoLock lock(m_particle_list_lock);
572 m_particles.push_back(toadd);