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 if (event->type == CE_DELETE_PARTICLESPAWNER) {
412 MutexAutoLock lock(m_spawner_list_lock);
413 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
414 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
423 if (event->type == CE_ADD_PARTICLESPAWNER) {
426 MutexAutoLock lock(m_spawner_list_lock);
427 if (m_particle_spawners.find(event->add_particlespawner.id) !=
428 m_particle_spawners.end())
430 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
431 m_particle_spawners.erase(event->add_particlespawner.id);
434 video::ITexture *texture =
435 gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
437 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
438 event->add_particlespawner.amount,
439 event->add_particlespawner.spawntime,
440 *event->add_particlespawner.minpos,
441 *event->add_particlespawner.maxpos,
442 *event->add_particlespawner.minvel,
443 *event->add_particlespawner.maxvel,
444 *event->add_particlespawner.minacc,
445 *event->add_particlespawner.maxacc,
446 event->add_particlespawner.minexptime,
447 event->add_particlespawner.maxexptime,
448 event->add_particlespawner.minsize,
449 event->add_particlespawner.maxsize,
450 event->add_particlespawner.collisiondetection,
451 event->add_particlespawner.vertical,
453 event->add_particlespawner.id,
456 /* delete allocated content of event */
457 delete event->add_particlespawner.minpos;
458 delete event->add_particlespawner.maxpos;
459 delete event->add_particlespawner.minvel;
460 delete event->add_particlespawner.maxvel;
461 delete event->add_particlespawner.minacc;
462 delete event->add_particlespawner.texture;
463 delete event->add_particlespawner.maxacc;
466 MutexAutoLock lock(m_spawner_list_lock);
467 m_particle_spawners.insert(
468 std::pair<u32, ParticleSpawner*>(
469 event->add_particlespawner.id,
476 if (event->type == CE_SPAWN_PARTICLE) {
477 video::ITexture *texture =
478 gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
480 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
481 *event->spawn_particle.pos,
482 *event->spawn_particle.vel,
483 *event->spawn_particle.acc,
484 event->spawn_particle.expirationtime,
485 event->spawn_particle.size,
486 event->spawn_particle.collisiondetection,
487 event->spawn_particle.vertical,
494 delete event->spawn_particle.pos;
495 delete event->spawn_particle.vel;
496 delete event->spawn_particle.acc;
502 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
503 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
505 for (u16 j = 0; j < 32; j++) // set the amount of particles here
507 addNodeParticle(gamedef, smgr, player, pos, tiles);
511 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
512 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
514 addNodeParticle(gamedef, smgr, player, pos, tiles);
517 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
518 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
521 u8 texid = myrand_range(0, 5);
522 video::ITexture *texture = tiles[texid].texture;
524 // Only use first frame of animated texture
526 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
527 ymax /= tiles[texid].animation_frame_count;
529 float size = rand() % 64 / 512.;
530 float visual_size = BS * size;
531 v2f texsize(size * 2, ymax * size * 2);
533 texpos.X = ((rand() % 64) / 64. - texsize.X);
534 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
537 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
539 (rand() % 100 / 50. - 1) / 1.5);
541 v3f acceleration(0,-9,0);
542 v3f particlepos = v3f(
543 (f32) pos.X + rand() %100 /200. - 0.25,
544 (f32) pos.Y + rand() %100 /200. - 0.25,
545 (f32) pos.Z + rand() %100 /200. - 0.25
548 Particle* toadd = new Particle(
556 rand() % 100 / 100., // expiration time
567 void ParticleManager::addParticle(Particle* toadd)
569 MutexAutoLock lock(m_particle_list_lock);
570 m_particles.push_back(toadd);