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,
58 const struct TileAnimationParams &anim,
61 scene::ISceneNode(smgr->getRootSceneNode(), smgr)
68 m_material.setFlag(video::EMF_LIGHTING, false);
69 m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
70 m_material.setFlag(video::EMF_BILINEAR_FILTER, false);
71 m_material.setFlag(video::EMF_FOG_ENABLE, true);
72 m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
73 m_material.setTexture(0, texture);
77 m_animation_frame = 0;
78 m_animation_time = 0.0;
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;
94 m_collisionbox = aabb3f
95 (-size/2,-size/2,-size/2,size/2,size/2,size/2);
96 this->setAutomaticCulling(scene::EAC_OFF);
105 Particle::~Particle()
109 void Particle::OnRegisterSceneNode()
112 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
114 ISceneNode::OnRegisterSceneNode();
117 void Particle::render()
119 video::IVideoDriver* driver = SceneManager->getVideoDriver();
120 driver->setMaterial(m_material);
121 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
123 u16 indices[] = {0,1,2, 2,3,0};
124 driver->drawVertexPrimitiveList(m_vertices, 4,
125 indices, 2, video::EVT_STANDARD,
126 scene::EPT_TRIANGLES, video::EIT_16BIT);
129 void Particle::step(float dtime)
132 if (m_collisiondetection) {
133 aabb3f box = m_collisionbox;
134 v3f p_pos = m_pos * BS;
135 v3f p_velocity = m_velocity * BS;
136 collisionMoveResult r = collisionMoveSimple(m_env,
137 m_gamedef, BS * 0.5, box, 0, dtime, &p_pos,
138 &p_velocity, m_acceleration * BS);
139 if (m_collision_removal && r.collides) {
140 // force expiration of the particle
144 m_velocity = p_velocity / BS;
147 m_velocity += m_acceleration * dtime;
148 m_pos += m_velocity * dtime;
150 if (m_animation.type != TAT_NONE) {
151 m_animation_time += dtime;
152 int frame_length_i, frame_count;
153 m_animation.determineParams(
154 m_material.getTexture(0)->getSize(),
155 &frame_count, &frame_length_i, NULL);
156 float frame_length = frame_length_i / 1000.0;
157 while (m_animation_time > frame_length) {
159 m_animation_time -= frame_length;
170 void Particle::updateLight()
180 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
182 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
184 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
186 m_light = decode_light(light + m_glow);
189 void Particle::updateVertices()
191 video::SColor c(255, m_light, m_light, m_light);
192 f32 tx0, tx1, ty0, ty1;
194 if (m_animation.type != TAT_NONE) {
195 const v2u32 texsize = m_material.getTexture(0)->getSize();
196 v2f texcoord, framesize_f;
198 texcoord = m_animation.getTextureCoords(texsize, m_animation_frame);
199 m_animation.determineParams(texsize, NULL, NULL, &framesize);
200 framesize_f = v2f(framesize.X / (float) texsize.X, framesize.Y / (float) texsize.Y);
202 tx0 = m_texpos.X + texcoord.X;
203 tx1 = m_texpos.X + texcoord.X + framesize_f.X * m_texsize.X;
204 ty0 = m_texpos.Y + texcoord.Y;
205 ty1 = m_texpos.Y + texcoord.Y + framesize_f.Y * m_texsize.Y;
208 tx1 = m_texpos.X + m_texsize.X;
210 ty1 = m_texpos.Y + m_texsize.Y;
213 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
215 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
217 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
219 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
222 v3s16 camera_offset = m_env->getCameraOffset();
223 for(u16 i=0; i<4; i++)
226 v3f ppos = m_player->getPosition()/BS;
227 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
229 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
230 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
232 m_box.addInternalPoint(m_vertices[i].Pos);
233 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
241 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
242 u16 amount, float time,
243 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
244 float minexptime, float maxexptime, float minsize, float maxsize,
245 bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical,
246 video::ITexture *texture, u32 id, const struct TileAnimationParams &anim,
248 ParticleManager *p_manager) :
249 m_particlemanager(p_manager)
262 m_minexptime = minexptime;
263 m_maxexptime = maxexptime;
266 m_collisiondetection = collisiondetection;
267 m_collision_removal = collision_removal;
268 m_attached_id = attached_id;
269 m_vertical = vertical;
275 for (u16 i = 0; i<=m_amount; i++)
277 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
278 m_spawntimes.push_back(spawntime);
282 ParticleSpawner::~ParticleSpawner() {}
284 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
288 bool unloaded = false;
289 bool is_attached = false;
290 v3f attached_pos = v3f(0,0,0);
291 float attached_yaw = 0;
292 if (m_attached_id != 0) {
293 if (ClientActiveObject *attached = env->getActiveObject(m_attached_id)) {
294 attached_pos = attached->getPosition() / BS;
295 attached_yaw = attached->getYaw();
302 if (m_spawntime != 0) // Spawner exists for a predefined timespan
304 for(std::vector<float>::iterator i = m_spawntimes.begin();
305 i != m_spawntimes.end();)
307 if ((*i) <= m_time && m_amount > 0)
311 // Pretend to, but don't actually spawn a
312 // particle if it is attached to an unloaded
315 v3f pos = random_v3f(m_minpos, m_maxpos);
316 v3f vel = random_v3f(m_minvel, m_maxvel);
317 v3f acc = random_v3f(m_minacc, m_maxacc);
320 // Apply attachment yaw and position
321 pos.rotateXZBy(attached_yaw);
323 vel.rotateXZBy(attached_yaw);
324 acc.rotateXZBy(attached_yaw);
327 float exptime = rand()/(float)RAND_MAX
328 *(m_maxexptime-m_minexptime)
330 float size = rand()/(float)RAND_MAX
331 *(m_maxsize-m_minsize)
334 Particle* toadd = new Particle(
344 m_collisiondetection,
352 m_particlemanager->addParticle(toadd);
354 i = m_spawntimes.erase(i);
362 else // Spawner exists for an infinity timespan, spawn on a per-second base
364 // Skip this step if attached to an unloaded object
367 for (int i = 0; i <= m_amount; i++)
369 if (rand()/(float)RAND_MAX < dtime)
371 v3f pos = random_v3f(m_minpos, m_maxpos);
372 v3f vel = random_v3f(m_minvel, m_maxvel);
373 v3f acc = random_v3f(m_minacc, m_maxacc);
376 // Apply attachment yaw and position
377 pos.rotateXZBy(attached_yaw);
379 vel.rotateXZBy(attached_yaw);
380 acc.rotateXZBy(attached_yaw);
383 float exptime = rand()/(float)RAND_MAX
384 *(m_maxexptime-m_minexptime)
386 float size = rand()/(float)RAND_MAX
387 *(m_maxsize-m_minsize)
390 Particle* toadd = new Particle(
400 m_collisiondetection,
408 m_particlemanager->addParticle(toadd);
415 ParticleManager::ParticleManager(ClientEnvironment* env) :
419 ParticleManager::~ParticleManager()
424 void ParticleManager::step(float dtime)
426 stepParticles (dtime);
427 stepSpawners (dtime);
430 void ParticleManager::stepSpawners (float dtime)
432 MutexAutoLock lock(m_spawner_list_lock);
433 for (std::map<u32, ParticleSpawner*>::iterator i =
434 m_particle_spawners.begin();
435 i != m_particle_spawners.end();)
437 if (i->second->get_expired())
440 m_particle_spawners.erase(i++);
444 i->second->step(dtime, m_env);
450 void ParticleManager::stepParticles (float dtime)
452 MutexAutoLock lock(m_particle_list_lock);
453 for(std::vector<Particle*>::iterator i = m_particles.begin();
454 i != m_particles.end();)
456 if ((*i)->get_expired())
460 i = m_particles.erase(i);
470 void ParticleManager::clearAll ()
472 MutexAutoLock lock(m_spawner_list_lock);
473 MutexAutoLock lock2(m_particle_list_lock);
474 for(std::map<u32, ParticleSpawner*>::iterator i =
475 m_particle_spawners.begin();
476 i != m_particle_spawners.end();)
479 m_particle_spawners.erase(i++);
482 for(std::vector<Particle*>::iterator i =
484 i != m_particles.end();)
488 i = m_particles.erase(i);
492 void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
493 scene::ISceneManager* smgr, LocalPlayer *player)
495 switch (event->type) {
496 case CE_DELETE_PARTICLESPAWNER: {
497 MutexAutoLock lock(m_spawner_list_lock);
498 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
499 m_particle_spawners.end()) {
500 delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
501 m_particle_spawners.erase(event->delete_particlespawner.id);
503 // no allocated memory in delete event
506 case CE_ADD_PARTICLESPAWNER: {
508 MutexAutoLock lock(m_spawner_list_lock);
509 if (m_particle_spawners.find(event->add_particlespawner.id) !=
510 m_particle_spawners.end()) {
511 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
512 m_particle_spawners.erase(event->add_particlespawner.id);
516 video::ITexture *texture =
517 client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
519 ParticleSpawner* toadd = new ParticleSpawner(client, smgr, player,
520 event->add_particlespawner.amount,
521 event->add_particlespawner.spawntime,
522 *event->add_particlespawner.minpos,
523 *event->add_particlespawner.maxpos,
524 *event->add_particlespawner.minvel,
525 *event->add_particlespawner.maxvel,
526 *event->add_particlespawner.minacc,
527 *event->add_particlespawner.maxacc,
528 event->add_particlespawner.minexptime,
529 event->add_particlespawner.maxexptime,
530 event->add_particlespawner.minsize,
531 event->add_particlespawner.maxsize,
532 event->add_particlespawner.collisiondetection,
533 event->add_particlespawner.collision_removal,
534 event->add_particlespawner.attached_id,
535 event->add_particlespawner.vertical,
537 event->add_particlespawner.id,
538 event->add_particlespawner.animation,
539 event->add_particlespawner.glow,
542 /* delete allocated content of event */
543 delete event->add_particlespawner.minpos;
544 delete event->add_particlespawner.maxpos;
545 delete event->add_particlespawner.minvel;
546 delete event->add_particlespawner.maxvel;
547 delete event->add_particlespawner.minacc;
548 delete event->add_particlespawner.texture;
549 delete event->add_particlespawner.maxacc;
552 MutexAutoLock lock(m_spawner_list_lock);
553 m_particle_spawners.insert(
554 std::pair<u32, ParticleSpawner*>(
555 event->add_particlespawner.id,
560 case CE_SPAWN_PARTICLE: {
561 video::ITexture *texture =
562 client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
564 Particle* toadd = new Particle(client, smgr, player, m_env,
565 *event->spawn_particle.pos,
566 *event->spawn_particle.vel,
567 *event->spawn_particle.acc,
568 event->spawn_particle.expirationtime,
569 event->spawn_particle.size,
570 event->spawn_particle.collisiondetection,
571 event->spawn_particle.collision_removal,
572 event->spawn_particle.vertical,
576 event->spawn_particle.animation,
577 event->spawn_particle.glow);
581 delete event->spawn_particle.pos;
582 delete event->spawn_particle.vel;
583 delete event->spawn_particle.acc;
584 delete event->spawn_particle.texture;
592 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
593 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
595 for (u16 j = 0; j < 32; j++) // set the amount of particles here
597 addNodeParticle(gamedef, smgr, player, pos, tiles);
601 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
602 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
604 addNodeParticle(gamedef, smgr, player, pos, tiles);
607 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
608 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
611 u8 texid = myrand_range(0, 5);
612 video::ITexture *texture;
613 struct TileAnimationParams anim;
614 anim.type = TAT_NONE;
616 // Only use first frame of animated texture
617 if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION)
618 texture = tiles[texid].frames[0].texture;
620 texture = tiles[texid].texture;
622 float size = rand() % 64 / 512.;
623 float visual_size = BS * size;
624 v2f texsize(size * 2, size * 2);
626 texpos.X = ((rand() % 64) / 64. - texsize.X);
627 texpos.Y = ((rand() % 64) / 64. - texsize.Y);
630 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
632 (rand() % 100 / 50. - 1) / 1.5);
634 v3f acceleration(0,-9,0);
635 v3f particlepos = v3f(
636 (f32) pos.X + rand() %100 /200. - 0.25,
637 (f32) pos.Y + rand() %100 /200. - 0.25,
638 (f32) pos.Z + rand() %100 /200. - 0.25
641 Particle* toadd = new Particle(
649 rand() % 100 / 100., // expiration time
663 void ParticleManager::addParticle(Particle* toadd)
665 MutexAutoLock lock(m_particle_list_lock);
666 m_particles.push_back(toadd);