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"
22 #include "collision.h"
24 #include "util/numeric.h"
26 #include "environment.h"
27 #include "clientmap.h"
35 v3f random_v3f(v3f min, v3f max)
37 return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X,
38 rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y,
39 rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
44 scene::ISceneManager* smgr,
46 ClientEnvironment *env,
52 bool collisiondetection,
53 bool collision_removal,
55 video::ITexture *texture,
59 scene::ISceneNode(smgr->getRootSceneNode(), smgr)
66 m_material.setFlag(video::EMF_LIGHTING, false);
67 m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
68 m_material.setFlag(video::EMF_BILINEAR_FILTER, false);
69 m_material.setFlag(video::EMF_FOG_ENABLE, true);
70 m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
71 m_material.setTexture(0, texture);
78 m_velocity = velocity;
79 m_acceleration = acceleration;
80 m_expiration = expirationtime;
84 m_collisiondetection = collisiondetection;
85 m_collision_removal = collision_removal;
86 m_vertical = vertical;
89 m_collisionbox = aabb3f
90 (-size/2,-size/2,-size/2,size/2,size/2,size/2);
91 this->setAutomaticCulling(scene::EAC_OFF);
100 Particle::~Particle()
104 void Particle::OnRegisterSceneNode()
107 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
109 ISceneNode::OnRegisterSceneNode();
112 void Particle::render()
114 video::IVideoDriver* driver = SceneManager->getVideoDriver();
115 driver->setMaterial(m_material);
116 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
118 u16 indices[] = {0,1,2, 2,3,0};
119 driver->drawVertexPrimitiveList(m_vertices, 4,
120 indices, 2, video::EVT_STANDARD,
121 scene::EPT_TRIANGLES, video::EIT_16BIT);
124 void Particle::step(float dtime)
127 if (m_collisiondetection) {
128 aabb3f box = m_collisionbox;
129 v3f p_pos = m_pos * BS;
130 v3f p_velocity = m_velocity * BS;
131 collisionMoveResult r = collisionMoveSimple(m_env,
132 m_gamedef, BS * 0.5, box, 0, dtime, &p_pos,
133 &p_velocity, m_acceleration * BS);
134 if (m_collision_removal && r.collides) {
135 // force expiration of the particle
139 m_velocity = p_velocity / BS;
142 m_velocity += m_acceleration * dtime;
143 m_pos += m_velocity * dtime;
153 void Particle::updateLight()
163 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
165 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
167 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
169 m_light = decode_light(light);
172 void Particle::updateVertices()
174 video::SColor c(255, m_light, m_light, m_light);
175 f32 tx0 = m_texpos.X;
176 f32 tx1 = m_texpos.X + m_texsize.X;
177 f32 ty0 = m_texpos.Y;
178 f32 ty1 = m_texpos.Y + m_texsize.Y;
180 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
182 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
184 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
186 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
189 v3s16 camera_offset = m_env->getCameraOffset();
190 for(u16 i=0; i<4; i++)
193 v3f ppos = m_player->getPosition()/BS;
194 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
196 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
197 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
199 m_box.addInternalPoint(m_vertices[i].Pos);
200 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
208 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
209 u16 amount, float time,
210 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
211 float minexptime, float maxexptime, float minsize, float maxsize,
212 bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical,
213 video::ITexture *texture, u32 id, ParticleManager *p_manager) :
214 m_particlemanager(p_manager)
227 m_minexptime = minexptime;
228 m_maxexptime = maxexptime;
231 m_collisiondetection = collisiondetection;
232 m_collision_removal = collision_removal;
233 m_attached_id = attached_id;
234 m_vertical = vertical;
238 for (u16 i = 0; i<=m_amount; i++)
240 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
241 m_spawntimes.push_back(spawntime);
245 ParticleSpawner::~ParticleSpawner() {}
247 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
251 bool unloaded = false;
252 bool is_attached = false;
253 v3f attached_pos = v3f(0,0,0);
254 float attached_yaw = 0;
255 if (m_attached_id != 0) {
256 if (ClientActiveObject *attached = env->getActiveObject(m_attached_id)) {
257 attached_pos = attached->getPosition() / BS;
258 attached_yaw = attached->getYaw();
265 if (m_spawntime != 0) // Spawner exists for a predefined timespan
267 for(std::vector<float>::iterator i = m_spawntimes.begin();
268 i != m_spawntimes.end();)
270 if ((*i) <= m_time && m_amount > 0)
274 // Pretend to, but don't actually spawn a
275 // particle if it is attached to an unloaded
278 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);
283 // Apply attachment yaw and position
284 pos.rotateXZBy(attached_yaw);
286 vel.rotateXZBy(attached_yaw);
287 acc.rotateXZBy(attached_yaw);
290 float exptime = rand()/(float)RAND_MAX
291 *(m_maxexptime-m_minexptime)
293 float size = rand()/(float)RAND_MAX
294 *(m_maxsize-m_minsize)
297 Particle* toadd = new Particle(
307 m_collisiondetection,
313 m_particlemanager->addParticle(toadd);
315 i = m_spawntimes.erase(i);
323 else // Spawner exists for an infinity timespan, spawn on a per-second base
325 // Skip this step if attached to an unloaded object
328 for (int i = 0; i <= m_amount; i++)
330 if (rand()/(float)RAND_MAX < dtime)
332 v3f pos = random_v3f(m_minpos, m_maxpos);
333 v3f vel = random_v3f(m_minvel, m_maxvel);
334 v3f acc = random_v3f(m_minacc, m_maxacc);
337 // Apply attachment yaw and position
338 pos.rotateXZBy(attached_yaw);
340 vel.rotateXZBy(attached_yaw);
341 acc.rotateXZBy(attached_yaw);
344 float exptime = rand()/(float)RAND_MAX
345 *(m_maxexptime-m_minexptime)
347 float size = rand()/(float)RAND_MAX
348 *(m_maxsize-m_minsize)
351 Particle* toadd = new Particle(
361 m_collisiondetection,
367 m_particlemanager->addParticle(toadd);
374 ParticleManager::ParticleManager(ClientEnvironment* env) :
378 ParticleManager::~ParticleManager()
383 void ParticleManager::step(float dtime)
385 stepParticles (dtime);
386 stepSpawners (dtime);
389 void ParticleManager::stepSpawners (float dtime)
391 MutexAutoLock lock(m_spawner_list_lock);
392 for (std::map<u32, ParticleSpawner*>::iterator i =
393 m_particle_spawners.begin();
394 i != m_particle_spawners.end();)
396 if (i->second->get_expired())
399 m_particle_spawners.erase(i++);
403 i->second->step(dtime, m_env);
409 void ParticleManager::stepParticles (float dtime)
411 MutexAutoLock lock(m_particle_list_lock);
412 for(std::vector<Particle*>::iterator i = m_particles.begin();
413 i != m_particles.end();)
415 if ((*i)->get_expired())
419 i = m_particles.erase(i);
429 void ParticleManager::clearAll ()
431 MutexAutoLock lock(m_spawner_list_lock);
432 MutexAutoLock lock2(m_particle_list_lock);
433 for(std::map<u32, ParticleSpawner*>::iterator i =
434 m_particle_spawners.begin();
435 i != m_particle_spawners.end();)
438 m_particle_spawners.erase(i++);
441 for(std::vector<Particle*>::iterator i =
443 i != m_particles.end();)
447 i = m_particles.erase(i);
451 void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
452 scene::ISceneManager* smgr, LocalPlayer *player)
454 switch (event->type) {
455 case CE_DELETE_PARTICLESPAWNER: {
456 MutexAutoLock lock(m_spawner_list_lock);
457 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
458 m_particle_spawners.end()) {
459 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
460 m_particle_spawners.erase(event->delete_particlespawner.id);
462 // no allocated memory in delete event
465 case CE_ADD_PARTICLESPAWNER: {
467 MutexAutoLock lock(m_spawner_list_lock);
468 if (m_particle_spawners.find(event->add_particlespawner.id) !=
469 m_particle_spawners.end()) {
470 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
471 m_particle_spawners.erase(event->add_particlespawner.id);
475 video::ITexture *texture =
476 client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
478 ParticleSpawner* toadd = new ParticleSpawner(client, smgr, player,
479 event->add_particlespawner.amount,
480 event->add_particlespawner.spawntime,
481 *event->add_particlespawner.minpos,
482 *event->add_particlespawner.maxpos,
483 *event->add_particlespawner.minvel,
484 *event->add_particlespawner.maxvel,
485 *event->add_particlespawner.minacc,
486 *event->add_particlespawner.maxacc,
487 event->add_particlespawner.minexptime,
488 event->add_particlespawner.maxexptime,
489 event->add_particlespawner.minsize,
490 event->add_particlespawner.maxsize,
491 event->add_particlespawner.collisiondetection,
492 event->add_particlespawner.collision_removal,
493 event->add_particlespawner.attached_id,
494 event->add_particlespawner.vertical,
496 event->add_particlespawner.id,
499 /* delete allocated content of event */
500 delete event->add_particlespawner.minpos;
501 delete event->add_particlespawner.maxpos;
502 delete event->add_particlespawner.minvel;
503 delete event->add_particlespawner.maxvel;
504 delete event->add_particlespawner.minacc;
505 delete event->add_particlespawner.texture;
506 delete event->add_particlespawner.maxacc;
509 MutexAutoLock lock(m_spawner_list_lock);
510 m_particle_spawners.insert(
511 std::pair<u32, ParticleSpawner*>(
512 event->add_particlespawner.id,
517 case CE_SPAWN_PARTICLE: {
518 video::ITexture *texture =
519 client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
521 Particle* toadd = new Particle(client, smgr, player, m_env,
522 *event->spawn_particle.pos,
523 *event->spawn_particle.vel,
524 *event->spawn_particle.acc,
525 event->spawn_particle.expirationtime,
526 event->spawn_particle.size,
527 event->spawn_particle.collisiondetection,
528 event->spawn_particle.collision_removal,
529 event->spawn_particle.vertical,
536 delete event->spawn_particle.pos;
537 delete event->spawn_particle.vel;
538 delete event->spawn_particle.acc;
546 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
547 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
549 for (u16 j = 0; j < 32; j++) // set the amount of particles here
551 addNodeParticle(gamedef, smgr, player, pos, tiles);
555 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
556 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
558 addNodeParticle(gamedef, smgr, player, pos, tiles);
561 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
562 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
565 u8 texid = myrand_range(0, 5);
566 video::ITexture *texture;
568 // Only use first frame of animated texture
569 if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION)
570 texture = tiles[texid].frames[0].texture;
572 texture = tiles[texid].texture;
574 float size = rand() % 64 / 512.;
575 float visual_size = BS * size;
576 v2f texsize(size * 2, size * 2);
578 texpos.X = ((rand() % 64) / 64. - texsize.X);
579 texpos.Y = ((rand() % 64) / 64. - texsize.Y);
582 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
584 (rand() % 100 / 50. - 1) / 1.5);
586 v3f acceleration(0,-9,0);
587 v3f particlepos = v3f(
588 (f32) pos.X + rand() %100 /200. - 0.25,
589 (f32) pos.Y + rand() %100 /200. - 0.25,
590 (f32) pos.Z + rand() %100 /200. - 0.25
593 Particle* toadd = new Particle(
601 rand() % 100 / 100., // expiration time
613 void ParticleManager::addParticle(Particle* toadd)
615 MutexAutoLock lock(m_particle_list_lock);
616 m_particles.push_back(toadd);