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 = aabb3f
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 aabb3f box = m_collisionbox;
132 v3f p_pos = m_pos*BS;
133 v3f p_velocity = m_velocity*BS;
134 collisionMoveSimple(m_env, m_gamedef,
137 &p_pos, &p_velocity, m_acceleration * BS);
139 m_velocity = p_velocity/BS;
143 m_velocity += m_acceleration * dtime;
144 m_pos += m_velocity * dtime;
154 void Particle::updateLight()
164 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
166 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
168 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
170 m_light = decode_light(light);
173 void Particle::updateVertices()
175 video::SColor c(255, m_light, m_light, m_light);
176 f32 tx0 = m_texpos.X;
177 f32 tx1 = m_texpos.X + m_texsize.X;
178 f32 ty0 = m_texpos.Y;
179 f32 ty1 = m_texpos.Y + m_texsize.Y;
181 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
183 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
185 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
187 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
190 v3s16 camera_offset = m_env->getCameraOffset();
191 for(u16 i=0; i<4; i++)
194 v3f ppos = m_player->getPosition()/BS;
195 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
197 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
198 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
200 m_box.addInternalPoint(m_vertices[i].Pos);
201 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
209 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
210 u16 amount, float time,
211 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
212 float minexptime, float maxexptime, float minsize, float maxsize,
213 bool collisiondetection, bool vertical, video::ITexture *texture, u32 id,
214 ParticleManager *p_manager) :
215 m_particlemanager(p_manager)
228 m_minexptime = minexptime;
229 m_maxexptime = maxexptime;
232 m_collisiondetection = collisiondetection;
233 m_vertical = vertical;
237 for (u16 i = 0; i<=m_amount; i++)
239 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
240 m_spawntimes.push_back(spawntime);
244 ParticleSpawner::~ParticleSpawner() {}
246 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
250 if (m_spawntime != 0) // Spawner exists for a predefined timespan
252 for(std::vector<float>::iterator i = m_spawntimes.begin();
253 i != m_spawntimes.end();)
255 if ((*i) <= m_time && m_amount > 0)
259 v3f pos = random_v3f(m_minpos, m_maxpos);
260 v3f vel = random_v3f(m_minvel, m_maxvel);
261 v3f acc = random_v3f(m_minacc, m_maxacc);
262 float exptime = rand()/(float)RAND_MAX
263 *(m_maxexptime-m_minexptime)
265 float size = rand()/(float)RAND_MAX
266 *(m_maxsize-m_minsize)
269 Particle* toadd = new Particle(
279 m_collisiondetection,
284 m_particlemanager->addParticle(toadd);
285 i = m_spawntimes.erase(i);
293 else // Spawner exists for an infinity timespan, spawn on a per-second base
295 for (int i = 0; i <= m_amount; i++)
297 if (rand()/(float)RAND_MAX < dtime)
299 v3f pos = random_v3f(m_minpos, m_maxpos);
300 v3f vel = random_v3f(m_minvel, m_maxvel);
301 v3f acc = random_v3f(m_minacc, m_maxacc);
302 float exptime = rand()/(float)RAND_MAX
303 *(m_maxexptime-m_minexptime)
305 float size = rand()/(float)RAND_MAX
306 *(m_maxsize-m_minsize)
309 Particle* toadd = new Particle(
319 m_collisiondetection,
324 m_particlemanager->addParticle(toadd);
331 ParticleManager::ParticleManager(ClientEnvironment* env) :
335 ParticleManager::~ParticleManager()
340 void ParticleManager::step(float dtime)
342 stepParticles (dtime);
343 stepSpawners (dtime);
346 void ParticleManager::stepSpawners (float dtime)
348 MutexAutoLock lock(m_spawner_list_lock);
349 for (std::map<u32, ParticleSpawner*>::iterator i =
350 m_particle_spawners.begin();
351 i != m_particle_spawners.end();)
353 if (i->second->get_expired())
356 m_particle_spawners.erase(i++);
360 i->second->step(dtime, m_env);
366 void ParticleManager::stepParticles (float dtime)
368 MutexAutoLock lock(m_particle_list_lock);
369 for(std::vector<Particle*>::iterator i = m_particles.begin();
370 i != m_particles.end();)
372 if ((*i)->get_expired())
376 i = m_particles.erase(i);
386 void ParticleManager::clearAll ()
388 MutexAutoLock lock(m_spawner_list_lock);
389 MutexAutoLock lock2(m_particle_list_lock);
390 for(std::map<u32, ParticleSpawner*>::iterator i =
391 m_particle_spawners.begin();
392 i != m_particle_spawners.end();)
395 m_particle_spawners.erase(i++);
398 for(std::vector<Particle*>::iterator i =
400 i != m_particles.end();)
404 i = m_particles.erase(i);
408 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
409 scene::ISceneManager* smgr, LocalPlayer *player)
411 switch (event->type) {
412 case CE_DELETE_PARTICLESPAWNER: {
413 MutexAutoLock lock(m_spawner_list_lock);
414 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
415 m_particle_spawners.end()) {
416 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
417 m_particle_spawners.erase(event->delete_particlespawner.id);
419 // no allocated memory in delete event
422 case CE_ADD_PARTICLESPAWNER: {
424 MutexAutoLock lock(m_spawner_list_lock);
425 if (m_particle_spawners.find(event->add_particlespawner.id) !=
426 m_particle_spawners.end()) {
427 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
428 m_particle_spawners.erase(event->add_particlespawner.id);
432 video::ITexture *texture =
433 gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
435 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
436 event->add_particlespawner.amount,
437 event->add_particlespawner.spawntime,
438 *event->add_particlespawner.minpos,
439 *event->add_particlespawner.maxpos,
440 *event->add_particlespawner.minvel,
441 *event->add_particlespawner.maxvel,
442 *event->add_particlespawner.minacc,
443 *event->add_particlespawner.maxacc,
444 event->add_particlespawner.minexptime,
445 event->add_particlespawner.maxexptime,
446 event->add_particlespawner.minsize,
447 event->add_particlespawner.maxsize,
448 event->add_particlespawner.collisiondetection,
449 event->add_particlespawner.vertical,
451 event->add_particlespawner.id,
454 /* delete allocated content of event */
455 delete event->add_particlespawner.minpos;
456 delete event->add_particlespawner.maxpos;
457 delete event->add_particlespawner.minvel;
458 delete event->add_particlespawner.maxvel;
459 delete event->add_particlespawner.minacc;
460 delete event->add_particlespawner.texture;
461 delete event->add_particlespawner.maxacc;
464 MutexAutoLock lock(m_spawner_list_lock);
465 m_particle_spawners.insert(
466 std::pair<u32, ParticleSpawner*>(
467 event->add_particlespawner.id,
472 case CE_SPAWN_PARTICLE: {
473 video::ITexture *texture =
474 gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
476 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
477 *event->spawn_particle.pos,
478 *event->spawn_particle.vel,
479 *event->spawn_particle.acc,
480 event->spawn_particle.expirationtime,
481 event->spawn_particle.size,
482 event->spawn_particle.collisiondetection,
483 event->spawn_particle.vertical,
490 delete event->spawn_particle.pos;
491 delete event->spawn_particle.vel;
492 delete event->spawn_particle.acc;
500 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
501 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
503 for (u16 j = 0; j < 32; j++) // set the amount of particles here
505 addNodeParticle(gamedef, smgr, player, pos, tiles);
509 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
510 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
512 addNodeParticle(gamedef, smgr, player, pos, tiles);
515 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
516 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
519 u8 texid = myrand_range(0, 5);
520 video::ITexture *texture = tiles[texid].texture;
522 // Only use first frame of animated texture
524 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
525 ymax /= tiles[texid].animation_frame_count;
527 float size = rand() % 64 / 512.;
528 float visual_size = BS * size;
529 v2f texsize(size * 2, ymax * size * 2);
531 texpos.X = ((rand() % 64) / 64. - texsize.X);
532 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
535 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
537 (rand() % 100 / 50. - 1) / 1.5);
539 v3f acceleration(0,-9,0);
540 v3f particlepos = v3f(
541 (f32) pos.X + rand() %100 /200. - 0.25,
542 (f32) pos.Y + rand() %100 /200. - 0.25,
543 (f32) pos.Z + rand() %100 /200. - 0.25
546 Particle* toadd = new Particle(
554 rand() % 100 / 100., // expiration time
565 void ParticleManager::addParticle(Particle* toadd)
567 MutexAutoLock lock(m_particle_list_lock);
568 m_particles.push_back(toadd);