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, u16 attached_id, 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_attached_id = attached_id;
238 m_vertical = vertical;
242 for (u16 i = 0; i<=m_amount; i++)
244 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
245 m_spawntimes.push_back(spawntime);
249 ParticleSpawner::~ParticleSpawner() {}
251 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
255 bool unloaded = false;
256 v3f attached_offset = v3f(0,0,0);
257 if (m_attached_id != 0) {
258 if (ClientActiveObject *attached = env->getActiveObject(m_attached_id))
259 attached_offset = attached->getPosition() / BS;
264 if (m_spawntime != 0) // Spawner exists for a predefined timespan
266 for(std::vector<float>::iterator i = m_spawntimes.begin();
267 i != m_spawntimes.end();)
269 if ((*i) <= m_time && m_amount > 0)
273 // Pretend to, but don't actually spawn a
274 // particle if it is attached to an unloaded
277 v3f pos = random_v3f(m_minpos, m_maxpos)
279 v3f vel = random_v3f(m_minvel, m_maxvel);
280 v3f acc = random_v3f(m_minacc, m_maxacc);
281 // Make relative to offest
282 pos += attached_offset;
283 float exptime = rand()/(float)RAND_MAX
284 *(m_maxexptime-m_minexptime)
286 float size = rand()/(float)RAND_MAX
287 *(m_maxsize-m_minsize)
290 Particle* toadd = new Particle(
300 m_collisiondetection,
306 m_particlemanager->addParticle(toadd);
308 i = m_spawntimes.erase(i);
316 else // Spawner exists for an infinity timespan, spawn on a per-second base
318 // Skip this step if attached to an unloaded object
321 for (int i = 0; i <= m_amount; i++)
323 if (rand()/(float)RAND_MAX < dtime)
325 v3f pos = random_v3f(m_minpos, m_maxpos)
327 v3f vel = random_v3f(m_minvel, m_maxvel);
328 v3f acc = random_v3f(m_minacc, m_maxacc);
329 float exptime = rand()/(float)RAND_MAX
330 *(m_maxexptime-m_minexptime)
332 float size = rand()/(float)RAND_MAX
333 *(m_maxsize-m_minsize)
336 Particle* toadd = new Particle(
346 m_collisiondetection,
352 m_particlemanager->addParticle(toadd);
359 ParticleManager::ParticleManager(ClientEnvironment* env) :
363 ParticleManager::~ParticleManager()
368 void ParticleManager::step(float dtime)
370 stepParticles (dtime);
371 stepSpawners (dtime);
374 void ParticleManager::stepSpawners (float dtime)
376 MutexAutoLock lock(m_spawner_list_lock);
377 for (std::map<u32, ParticleSpawner*>::iterator i =
378 m_particle_spawners.begin();
379 i != m_particle_spawners.end();)
381 if (i->second->get_expired())
384 m_particle_spawners.erase(i++);
388 i->second->step(dtime, m_env);
394 void ParticleManager::stepParticles (float dtime)
396 MutexAutoLock lock(m_particle_list_lock);
397 for(std::vector<Particle*>::iterator i = m_particles.begin();
398 i != m_particles.end();)
400 if ((*i)->get_expired())
404 i = m_particles.erase(i);
414 void ParticleManager::clearAll ()
416 MutexAutoLock lock(m_spawner_list_lock);
417 MutexAutoLock lock2(m_particle_list_lock);
418 for(std::map<u32, ParticleSpawner*>::iterator i =
419 m_particle_spawners.begin();
420 i != m_particle_spawners.end();)
423 m_particle_spawners.erase(i++);
426 for(std::vector<Particle*>::iterator i =
428 i != m_particles.end();)
432 i = m_particles.erase(i);
436 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
437 scene::ISceneManager* smgr, LocalPlayer *player)
439 switch (event->type) {
440 case CE_DELETE_PARTICLESPAWNER: {
441 MutexAutoLock lock(m_spawner_list_lock);
442 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
443 m_particle_spawners.end()) {
444 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
445 m_particle_spawners.erase(event->delete_particlespawner.id);
447 // no allocated memory in delete event
450 case CE_ADD_PARTICLESPAWNER: {
452 MutexAutoLock lock(m_spawner_list_lock);
453 if (m_particle_spawners.find(event->add_particlespawner.id) !=
454 m_particle_spawners.end()) {
455 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
456 m_particle_spawners.erase(event->add_particlespawner.id);
460 video::ITexture *texture =
461 gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
463 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
464 event->add_particlespawner.amount,
465 event->add_particlespawner.spawntime,
466 *event->add_particlespawner.minpos,
467 *event->add_particlespawner.maxpos,
468 *event->add_particlespawner.minvel,
469 *event->add_particlespawner.maxvel,
470 *event->add_particlespawner.minacc,
471 *event->add_particlespawner.maxacc,
472 event->add_particlespawner.minexptime,
473 event->add_particlespawner.maxexptime,
474 event->add_particlespawner.minsize,
475 event->add_particlespawner.maxsize,
476 event->add_particlespawner.collisiondetection,
477 event->add_particlespawner.collision_removal,
478 event->add_particlespawner.attached_id,
479 event->add_particlespawner.vertical,
481 event->add_particlespawner.id,
484 /* delete allocated content of event */
485 delete event->add_particlespawner.minpos;
486 delete event->add_particlespawner.maxpos;
487 delete event->add_particlespawner.minvel;
488 delete event->add_particlespawner.maxvel;
489 delete event->add_particlespawner.minacc;
490 delete event->add_particlespawner.texture;
491 delete event->add_particlespawner.maxacc;
494 MutexAutoLock lock(m_spawner_list_lock);
495 m_particle_spawners.insert(
496 std::pair<u32, ParticleSpawner*>(
497 event->add_particlespawner.id,
502 case CE_SPAWN_PARTICLE: {
503 video::ITexture *texture =
504 gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
506 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
507 *event->spawn_particle.pos,
508 *event->spawn_particle.vel,
509 *event->spawn_particle.acc,
510 event->spawn_particle.expirationtime,
511 event->spawn_particle.size,
512 event->spawn_particle.collisiondetection,
513 event->spawn_particle.collision_removal,
514 event->spawn_particle.vertical,
521 delete event->spawn_particle.pos;
522 delete event->spawn_particle.vel;
523 delete event->spawn_particle.acc;
531 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
532 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
534 for (u16 j = 0; j < 32; j++) // set the amount of particles here
536 addNodeParticle(gamedef, smgr, player, pos, tiles);
540 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
541 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
543 addNodeParticle(gamedef, smgr, player, pos, tiles);
546 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
547 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
550 u8 texid = myrand_range(0, 5);
551 video::ITexture *texture = tiles[texid].texture;
553 // Only use first frame of animated texture
555 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
556 ymax /= tiles[texid].animation_frame_count;
558 float size = rand() % 64 / 512.;
559 float visual_size = BS * size;
560 v2f texsize(size * 2, ymax * size * 2);
562 texpos.X = ((rand() % 64) / 64. - texsize.X);
563 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
566 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
568 (rand() % 100 / 50. - 1) / 1.5);
570 v3f acceleration(0,-9,0);
571 v3f particlepos = v3f(
572 (f32) pos.X + rand() %100 /200. - 0.25,
573 (f32) pos.Y + rand() %100 /200. - 0.25,
574 (f32) pos.Z + rand() %100 /200. - 0.25
577 Particle* toadd = new Particle(
585 rand() % 100 / 100., // expiration time
597 void ParticleManager::addParticle(Particle* toadd)
599 MutexAutoLock lock(m_particle_list_lock);
600 m_particles.push_back(toadd);