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"
23 #include "main.h" // For g_profiler and g_settings
27 #include "collision.h"
29 #include "util/numeric.h"
31 #include "environment.h"
32 #include "clientmap.h"
40 v3f random_v3f(v3f min, v3f max)
42 return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X,
43 rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y,
44 rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
49 scene::ISceneManager* smgr,
51 ClientEnvironment *env,
57 bool collisiondetection,
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_vertical = vertical;
92 m_collisionbox = core::aabbox3d<f32>
93 (-size/2,-size/2,-size/2,size/2,size/2,size/2);
94 this->setAutomaticCulling(scene::EAC_OFF);
103 Particle::~Particle()
107 void Particle::OnRegisterSceneNode()
111 SceneManager->registerNodeForRendering
112 (this, scene::ESNRP_TRANSPARENT);
113 SceneManager->registerNodeForRendering
114 (this, scene::ESNRP_SOLID);
117 ISceneNode::OnRegisterSceneNode();
120 void Particle::render()
122 // TODO: Render particles in front of water and the selectionbox
124 video::IVideoDriver* driver = SceneManager->getVideoDriver();
125 driver->setMaterial(m_material);
126 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
128 u16 indices[] = {0,1,2, 2,3,0};
129 driver->drawVertexPrimitiveList(m_vertices, 4,
130 indices, 2, video::EVT_STANDARD,
131 scene::EPT_TRIANGLES, video::EIT_16BIT);
134 void Particle::step(float dtime)
137 if (m_collisiondetection)
139 core::aabbox3d<f32> box = m_collisionbox;
140 v3f p_pos = m_pos*BS;
141 v3f p_velocity = m_velocity*BS;
142 v3f p_acceleration = m_acceleration*BS;
143 collisionMoveSimple(m_env, m_gamedef,
146 p_pos, p_velocity, p_acceleration);
148 m_velocity = p_velocity/BS;
149 m_acceleration = p_acceleration/BS;
153 m_velocity += m_acceleration * dtime;
154 m_pos += m_velocity * dtime;
164 void Particle::updateLight()
174 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
176 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
178 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
180 m_light = decode_light(light);
183 void Particle::updateVertices()
185 video::SColor c(255, m_light, m_light, m_light);
186 f32 tx0 = m_texpos.X;
187 f32 tx1 = m_texpos.X + m_texsize.X;
188 f32 ty0 = m_texpos.Y;
189 f32 ty1 = m_texpos.Y + m_texsize.Y;
191 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
193 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
195 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
197 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
200 v3s16 camera_offset = m_env->getCameraOffset();
201 for(u16 i=0; i<4; i++)
204 v3f ppos = m_player->getPosition()/BS;
205 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
207 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
208 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
210 m_box.addInternalPoint(m_vertices[i].Pos);
211 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
219 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
220 u16 amount, float time,
221 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
222 float minexptime, float maxexptime, float minsize, float maxsize,
223 bool collisiondetection, bool vertical, video::ITexture *texture, u32 id,
224 ParticleManager *p_manager) :
225 m_particlemanager(p_manager)
238 m_minexptime = minexptime;
239 m_maxexptime = maxexptime;
242 m_collisiondetection = collisiondetection;
243 m_vertical = vertical;
247 for (u16 i = 0; i<=m_amount; i++)
249 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
250 m_spawntimes.push_back(spawntime);
254 ParticleSpawner::~ParticleSpawner() {}
256 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
260 if (m_spawntime != 0) // Spawner exists for a predefined timespan
262 for(std::vector<float>::iterator i = m_spawntimes.begin();
263 i != m_spawntimes.end();)
265 if ((*i) <= m_time && m_amount > 0)
269 v3f pos = random_v3f(m_minpos, m_maxpos);
270 v3f vel = random_v3f(m_minvel, m_maxvel);
271 v3f acc = random_v3f(m_minacc, m_maxacc);
272 float exptime = rand()/(float)RAND_MAX
273 *(m_maxexptime-m_minexptime)
275 float size = rand()/(float)RAND_MAX
276 *(m_maxsize-m_minsize)
279 Particle* toadd = new Particle(
289 m_collisiondetection,
294 m_particlemanager->addParticle(toadd);
295 i = m_spawntimes.erase(i);
303 else // Spawner exists for an infinity timespan, spawn on a per-second base
305 for (int i = 0; i <= m_amount; i++)
307 if (rand()/(float)RAND_MAX < dtime)
309 v3f pos = random_v3f(m_minpos, m_maxpos);
310 v3f vel = random_v3f(m_minvel, m_maxvel);
311 v3f acc = random_v3f(m_minacc, m_maxacc);
312 float exptime = rand()/(float)RAND_MAX
313 *(m_maxexptime-m_minexptime)
315 float size = rand()/(float)RAND_MAX
316 *(m_maxsize-m_minsize)
329 m_collisiondetection,
340 ParticleManager::ParticleManager(ClientEnvironment* env) :
344 ParticleManager::~ParticleManager()
349 void ParticleManager::step(float dtime)
351 stepParticles (dtime);
352 stepSpawners (dtime);
355 void ParticleManager::stepSpawners (float dtime)
357 JMutexAutoLock lock(m_spawner_list_lock);
358 for(std::map<u32, ParticleSpawner*>::iterator i =
359 m_particle_spawners.begin();
360 i != m_particle_spawners.end();)
362 if (i->second->get_expired())
365 m_particle_spawners.erase(i++);
369 i->second->step(dtime, m_env);
375 void ParticleManager::stepParticles (float dtime)
377 JMutexAutoLock lock(m_particle_list_lock);
378 for(std::vector<Particle*>::iterator i = m_particles.begin();
379 i != m_particles.end();)
381 if ((*i)->get_expired())
385 i = m_particles.erase(i);
395 void ParticleManager::clearAll ()
397 JMutexAutoLock lock(m_spawner_list_lock);
398 JMutexAutoLock lock2(m_particle_list_lock);
399 for(std::map<u32, ParticleSpawner*>::iterator i =
400 m_particle_spawners.begin();
401 i != m_particle_spawners.end();)
404 m_particle_spawners.erase(i++);
407 for(std::vector<Particle*>::iterator i =
409 i != m_particles.end();)
413 i = m_particles.erase(i);
417 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
418 scene::ISceneManager* smgr, LocalPlayer *player)
420 if (event->type == CE_DELETE_PARTICLESPAWNER) {
421 JMutexAutoLock lock(m_spawner_list_lock);
422 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
423 m_particle_spawners.end())
425 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
426 m_particle_spawners.erase(event->delete_particlespawner.id);
428 // no allocated memory in delete event
432 if (event->type == CE_ADD_PARTICLESPAWNER) {
435 JMutexAutoLock lock(m_spawner_list_lock);
436 if (m_particle_spawners.find(event->add_particlespawner.id) !=
437 m_particle_spawners.end())
439 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
440 m_particle_spawners.erase(event->add_particlespawner.id);
443 video::ITexture *texture =
444 gamedef->tsrc()->getTexture(*(event->add_particlespawner.texture));
446 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
447 event->add_particlespawner.amount,
448 event->add_particlespawner.spawntime,
449 *event->add_particlespawner.minpos,
450 *event->add_particlespawner.maxpos,
451 *event->add_particlespawner.minvel,
452 *event->add_particlespawner.maxvel,
453 *event->add_particlespawner.minacc,
454 *event->add_particlespawner.maxacc,
455 event->add_particlespawner.minexptime,
456 event->add_particlespawner.maxexptime,
457 event->add_particlespawner.minsize,
458 event->add_particlespawner.maxsize,
459 event->add_particlespawner.collisiondetection,
460 event->add_particlespawner.vertical,
462 event->add_particlespawner.id,
465 /* delete allocated content of event */
466 delete event->add_particlespawner.minpos;
467 delete event->add_particlespawner.maxpos;
468 delete event->add_particlespawner.minvel;
469 delete event->add_particlespawner.maxvel;
470 delete event->add_particlespawner.minacc;
471 delete event->add_particlespawner.texture;
472 delete event->add_particlespawner.maxacc;
475 JMutexAutoLock lock(m_spawner_list_lock);
476 m_particle_spawners.insert(
477 std::pair<u32, ParticleSpawner*>(
478 event->add_particlespawner.id,
485 if (event->type == CE_SPAWN_PARTICLE) {
486 video::ITexture *texture =
487 gamedef->tsrc()->getTexture(*(event->spawn_particle.texture));
489 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
490 *event->spawn_particle.pos,
491 *event->spawn_particle.vel,
492 *event->spawn_particle.acc,
493 event->spawn_particle.expirationtime,
494 event->spawn_particle.size,
495 event->spawn_particle.collisiondetection,
496 event->spawn_particle.vertical,
503 delete event->spawn_particle.pos;
504 delete event->spawn_particle.vel;
505 delete event->spawn_particle.acc;
511 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
512 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
514 for (u16 j = 0; j < 32; j++) // set the amount of particles here
516 addNodeParticle(gamedef, smgr, player, pos, tiles);
520 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
521 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
523 addNodeParticle(gamedef, smgr, player, pos, tiles);
526 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
527 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
530 u8 texid = myrand_range(0, 5);
531 video::ITexture *texture = tiles[texid].texture;
533 // Only use first frame of animated texture
535 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
536 ymax /= tiles[texid].animation_frame_count;
538 float size = rand() % 64 / 512.;
539 float visual_size = BS * size;
540 v2f texsize(size * 2, ymax * size * 2);
542 texpos.X = ((rand() % 64) / 64. - texsize.X);
543 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
546 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
548 (rand() % 100 / 50. - 1) / 1.5);
550 v3f acceleration(0,-9,0);
551 v3f particlepos = v3f(
552 (f32) pos.X + rand() %100 /200. - 0.25,
553 (f32) pos.Y + rand() %100 /200. - 0.25,
554 (f32) pos.Z + rand() %100 /200. - 0.25
557 Particle* toadd = new Particle(
565 rand() % 100 / 100., // expiration time
576 void ParticleManager::addParticle(Particle* toadd)
578 JMutexAutoLock lock(m_particle_list_lock);
579 m_particles.push_back(toadd);