]> git.lizzy.rs Git - dragonfireclient.git/blob - src/particles.cpp
Clean up threading
[dragonfireclient.git] / src / particles.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "particles.h"
21 #include "constants.h"
22 #include "debug.h"
23 #include "settings.h"
24 #include "client/tile.h"
25 #include "gamedef.h"
26 #include "collision.h"
27 #include <stdlib.h>
28 #include "util/numeric.h"
29 #include "light.h"
30 #include "environment.h"
31 #include "clientmap.h"
32 #include "mapnode.h"
33 #include "client.h"
34
35 /*
36         Utility
37 */
38
39 v3f random_v3f(v3f min, v3f max)
40 {
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);
44 }
45
46 Particle::Particle(
47         IGameDef *gamedef,
48         scene::ISceneManager* smgr,
49         LocalPlayer *player,
50         ClientEnvironment *env,
51         v3f pos,
52         v3f velocity,
53         v3f acceleration,
54         float expirationtime,
55         float size,
56         bool collisiondetection,
57         bool vertical,
58         video::ITexture *texture,
59         v2f texpos,
60         v2f texsize
61 ):
62         scene::ISceneNode(smgr->getRootSceneNode(), smgr)
63 {
64         // Misc
65         m_gamedef = gamedef;
66         m_env = env;
67
68         // Texture
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);
75         m_texpos = texpos;
76         m_texsize = texsize;
77
78
79         // Particle related
80         m_pos = pos;
81         m_velocity = velocity;
82         m_acceleration = acceleration;
83         m_expiration = expirationtime;
84         m_time = 0;
85         m_player = player;
86         m_size = size;
87         m_collisiondetection = collisiondetection;
88         m_vertical = vertical;
89
90         // Irrlicht stuff
91         m_collisionbox = core::aabbox3d<f32>
92                         (-size/2,-size/2,-size/2,size/2,size/2,size/2);
93         this->setAutomaticCulling(scene::EAC_OFF);
94
95         // Init lighting
96         updateLight();
97
98         // Init model
99         updateVertices();
100 }
101
102 Particle::~Particle()
103 {
104 }
105
106 void Particle::OnRegisterSceneNode()
107 {
108         if (IsVisible)
109                 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
110
111         ISceneNode::OnRegisterSceneNode();
112 }
113
114 void Particle::render()
115 {
116         video::IVideoDriver* driver = SceneManager->getVideoDriver();
117         driver->setMaterial(m_material);
118         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
119
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);
124 }
125
126 void Particle::step(float dtime)
127 {
128         m_time += dtime;
129         if (m_collisiondetection)
130         {
131                 core::aabbox3d<f32> box = m_collisionbox;
132                 v3f p_pos = m_pos*BS;
133                 v3f p_velocity = m_velocity*BS;
134                 v3f p_acceleration = m_acceleration*BS;
135                 collisionMoveSimple(m_env, m_gamedef,
136                         BS*0.5, box,
137                         0, dtime,
138                         p_pos, p_velocity, p_acceleration);
139                 m_pos = p_pos/BS;
140                 m_velocity = p_velocity/BS;
141                 m_acceleration = p_acceleration/BS;
142         }
143         else
144         {
145                 m_velocity += m_acceleration * dtime;
146                 m_pos += m_velocity * dtime;
147         }
148
149         // Update lighting
150         updateLight();
151
152         // Update model
153         updateVertices();
154 }
155
156 void Particle::updateLight()
157 {
158         u8 light = 0;
159         bool pos_ok;
160
161         v3s16 p = v3s16(
162                 floor(m_pos.X+0.5),
163                 floor(m_pos.Y+0.5),
164                 floor(m_pos.Z+0.5)
165         );
166         MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
167         if (pos_ok)
168                 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
169         else
170                 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
171
172         m_light = decode_light(light);
173 }
174
175 void Particle::updateVertices()
176 {
177         video::SColor c(255, m_light, m_light, m_light);
178         f32 tx0 = m_texpos.X;
179         f32 tx1 = m_texpos.X + m_texsize.X;
180         f32 ty0 = m_texpos.Y;
181         f32 ty1 = m_texpos.Y + m_texsize.Y;
182
183         m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
184                         c, tx0, ty1);
185         m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
186                         c, tx1, ty1);
187         m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
188                         c, tx1, ty0);
189         m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
190                         c, tx0, ty0);
191
192         v3s16 camera_offset = m_env->getCameraOffset();
193         for(u16 i=0; i<4; i++)
194         {
195                 if (m_vertical) {
196                         v3f ppos = m_player->getPosition()/BS;
197                         m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
198                 } else {
199                         m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
200                         m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
201                 }
202                 m_box.addInternalPoint(m_vertices[i].Pos);
203                 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
204         }
205 }
206
207 /*
208         ParticleSpawner
209 */
210
211 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
212         u16 amount, float time,
213         v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
214         float minexptime, float maxexptime, float minsize, float maxsize,
215         bool collisiondetection, bool vertical, video::ITexture *texture, u32 id,
216         ParticleManager *p_manager) :
217         m_particlemanager(p_manager)
218 {
219         m_gamedef = gamedef;
220         m_smgr = smgr;
221         m_player = player;
222         m_amount = amount;
223         m_spawntime = time;
224         m_minpos = minpos;
225         m_maxpos = maxpos;
226         m_minvel = minvel;
227         m_maxvel = maxvel;
228         m_minacc = minacc;
229         m_maxacc = maxacc;
230         m_minexptime = minexptime;
231         m_maxexptime = maxexptime;
232         m_minsize = minsize;
233         m_maxsize = maxsize;
234         m_collisiondetection = collisiondetection;
235         m_vertical = vertical;
236         m_texture = texture;
237         m_time = 0;
238
239         for (u16 i = 0; i<=m_amount; i++)
240         {
241                 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
242                 m_spawntimes.push_back(spawntime);
243         }
244 }
245
246 ParticleSpawner::~ParticleSpawner() {}
247
248 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
249 {
250         m_time += dtime;
251
252         if (m_spawntime != 0) // Spawner exists for a predefined timespan
253         {
254                 for(std::vector<float>::iterator i = m_spawntimes.begin();
255                                 i != m_spawntimes.end();)
256                 {
257                         if ((*i) <= m_time && m_amount > 0)
258                         {
259                                 m_amount--;
260
261                                 v3f pos = random_v3f(m_minpos, m_maxpos);
262                                 v3f vel = random_v3f(m_minvel, m_maxvel);
263                                 v3f acc = random_v3f(m_minacc, m_maxacc);
264                                 float exptime = rand()/(float)RAND_MAX
265                                                 *(m_maxexptime-m_minexptime)
266                                                 +m_minexptime;
267                                 float size = rand()/(float)RAND_MAX
268                                                 *(m_maxsize-m_minsize)
269                                                 +m_minsize;
270
271                                 Particle* toadd = new Particle(
272                                         m_gamedef,
273                                         m_smgr,
274                                         m_player,
275                                         env,
276                                         pos,
277                                         vel,
278                                         acc,
279                                         exptime,
280                                         size,
281                                         m_collisiondetection,
282                                         m_vertical,
283                                         m_texture,
284                                         v2f(0.0, 0.0),
285                                         v2f(1.0, 1.0));
286                                 m_particlemanager->addParticle(toadd);
287                                 i = m_spawntimes.erase(i);
288                         }
289                         else
290                         {
291                                 i++;
292                         }
293                 }
294         }
295         else // Spawner exists for an infinity timespan, spawn on a per-second base
296         {
297                 for (int i = 0; i <= m_amount; i++)
298                 {
299                         if (rand()/(float)RAND_MAX < dtime)
300                         {
301                                 v3f pos = random_v3f(m_minpos, m_maxpos);
302                                 v3f vel = random_v3f(m_minvel, m_maxvel);
303                                 v3f acc = random_v3f(m_minacc, m_maxacc);
304                                 float exptime = rand()/(float)RAND_MAX
305                                                 *(m_maxexptime-m_minexptime)
306                                                 +m_minexptime;
307                                 float size = rand()/(float)RAND_MAX
308                                                 *(m_maxsize-m_minsize)
309                                                 +m_minsize;
310
311                                 Particle* toadd = new Particle(
312                                         m_gamedef,
313                                         m_smgr,
314                                         m_player,
315                                         env,
316                                         pos,
317                                         vel,
318                                         acc,
319                                         exptime,
320                                         size,
321                                         m_collisiondetection,
322                                         m_vertical,
323                                         m_texture,
324                                         v2f(0.0, 0.0),
325                                         v2f(1.0, 1.0));
326                                 m_particlemanager->addParticle(toadd);
327                         }
328                 }
329         }
330 }
331
332
333 ParticleManager::ParticleManager(ClientEnvironment* env) :
334         m_env(env)
335 {}
336
337 ParticleManager::~ParticleManager()
338 {
339         clearAll();
340 }
341
342 void ParticleManager::step(float dtime)
343 {
344         stepParticles (dtime);
345         stepSpawners (dtime);
346 }
347
348 void ParticleManager::stepSpawners (float dtime)
349 {
350         MutexAutoLock lock(m_spawner_list_lock);
351         for(std::map<u32, ParticleSpawner*>::iterator i = 
352                         m_particle_spawners.begin();
353                         i != m_particle_spawners.end();)
354         {
355                 if (i->second->get_expired())
356                 {
357                         delete i->second;
358                         m_particle_spawners.erase(i++);
359                 }
360                 else
361                 {
362                         i->second->step(dtime, m_env);
363                         i++;
364                 }
365         }
366 }
367
368 void ParticleManager::stepParticles (float dtime)
369 {
370         MutexAutoLock lock(m_particle_list_lock);
371         for(std::vector<Particle*>::iterator i = m_particles.begin();
372                         i != m_particles.end();)
373         {
374                 if ((*i)->get_expired())
375                 {
376                         (*i)->remove();
377                         delete *i;
378                         i = m_particles.erase(i);
379                 }
380                 else
381                 {
382                         (*i)->step(dtime);
383                         i++;
384                 }
385         }
386 }
387
388 void ParticleManager::clearAll ()
389 {
390         MutexAutoLock lock(m_spawner_list_lock);
391         MutexAutoLock lock2(m_particle_list_lock);
392         for(std::map<u32, ParticleSpawner*>::iterator i =
393                         m_particle_spawners.begin();
394                         i != m_particle_spawners.end();)
395         {
396                 delete i->second;
397                 m_particle_spawners.erase(i++);
398         }
399
400         for(std::vector<Particle*>::iterator i =
401                         m_particles.begin();
402                         i != m_particles.end();)
403         {
404                 (*i)->remove();
405                 delete *i;
406                 i = m_particles.erase(i);
407         }
408 }
409
410 void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
411                 scene::ISceneManager* smgr, LocalPlayer *player)
412 {
413         if (event->type == CE_DELETE_PARTICLESPAWNER) {
414                 MutexAutoLock lock(m_spawner_list_lock);
415                 if (m_particle_spawners.find(event->delete_particlespawner.id) !=
416                                 m_particle_spawners.end())
417                 {
418                         delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
419                         m_particle_spawners.erase(event->delete_particlespawner.id);
420                 }
421                 // no allocated memory in delete event
422                 return;
423         }
424
425         if (event->type == CE_ADD_PARTICLESPAWNER) {
426
427                 {
428                         MutexAutoLock lock(m_spawner_list_lock);
429                         if (m_particle_spawners.find(event->add_particlespawner.id) !=
430                                                         m_particle_spawners.end())
431                         {
432                                 delete m_particle_spawners.find(event->add_particlespawner.id)->second;
433                                 m_particle_spawners.erase(event->add_particlespawner.id);
434                         }
435                 }
436                 video::ITexture *texture =
437                         gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
438
439                 ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
440                                 event->add_particlespawner.amount,
441                                 event->add_particlespawner.spawntime,
442                                 *event->add_particlespawner.minpos,
443                                 *event->add_particlespawner.maxpos,
444                                 *event->add_particlespawner.minvel,
445                                 *event->add_particlespawner.maxvel,
446                                 *event->add_particlespawner.minacc,
447                                 *event->add_particlespawner.maxacc,
448                                 event->add_particlespawner.minexptime,
449                                 event->add_particlespawner.maxexptime,
450                                 event->add_particlespawner.minsize,
451                                 event->add_particlespawner.maxsize,
452                                 event->add_particlespawner.collisiondetection,
453                                 event->add_particlespawner.vertical,
454                                 texture,
455                                 event->add_particlespawner.id,
456                                 this);
457
458                 /* delete allocated content of event */
459                 delete event->add_particlespawner.minpos;
460                 delete event->add_particlespawner.maxpos;
461                 delete event->add_particlespawner.minvel;
462                 delete event->add_particlespawner.maxvel;
463                 delete event->add_particlespawner.minacc;
464                 delete event->add_particlespawner.texture;
465                 delete event->add_particlespawner.maxacc;
466
467                 {
468                         MutexAutoLock lock(m_spawner_list_lock);
469                         m_particle_spawners.insert(
470                                         std::pair<u32, ParticleSpawner*>(
471                                                         event->add_particlespawner.id,
472                                                         toadd));
473                 }
474
475                 return;
476         }
477
478         if (event->type == CE_SPAWN_PARTICLE) {
479                 video::ITexture *texture =
480                         gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
481
482                 Particle* toadd = new Particle(gamedef, smgr, player, m_env,
483                                 *event->spawn_particle.pos,
484                                 *event->spawn_particle.vel,
485                                 *event->spawn_particle.acc,
486                                 event->spawn_particle.expirationtime,
487                                 event->spawn_particle.size,
488                                 event->spawn_particle.collisiondetection,
489                                 event->spawn_particle.vertical,
490                                 texture,
491                                 v2f(0.0, 0.0),
492                                 v2f(1.0, 1.0));
493
494                 addParticle(toadd);
495
496                 delete event->spawn_particle.pos;
497                 delete event->spawn_particle.vel;
498                 delete event->spawn_particle.acc;
499
500                 return;
501         }
502 }
503
504 void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
505                 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
506 {
507         for (u16 j = 0; j < 32; j++) // set the amount of particles here
508         {
509                 addNodeParticle(gamedef, smgr, player, pos, tiles);
510         }
511 }
512
513 void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
514                 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
515 {
516         addNodeParticle(gamedef, smgr, player, pos, tiles);
517 }
518
519 void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
520                 LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
521 {
522         // Texture
523         u8 texid = myrand_range(0, 5);
524         video::ITexture *texture = tiles[texid].texture;
525
526         // Only use first frame of animated texture
527         f32 ymax = 1;
528         if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
529                 ymax /= tiles[texid].animation_frame_count;
530
531         float size = rand() % 64 / 512.;
532         float visual_size = BS * size;
533         v2f texsize(size * 2, ymax * size * 2);
534         v2f texpos;
535         texpos.X = ((rand() % 64) / 64. - texsize.X);
536         texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
537
538         // Physics
539         v3f velocity((rand() % 100 / 50. - 1) / 1.5,
540                         rand() % 100 / 35.,
541                         (rand() % 100 / 50. - 1) / 1.5);
542
543         v3f acceleration(0,-9,0);
544         v3f particlepos = v3f(
545                 (f32) pos.X + rand() %100 /200. - 0.25,
546                 (f32) pos.Y + rand() %100 /200. - 0.25,
547                 (f32) pos.Z + rand() %100 /200. - 0.25
548         );
549
550         Particle* toadd = new Particle(
551                 gamedef,
552                 smgr,
553                 player,
554                 m_env,
555                 particlepos,
556                 velocity,
557                 acceleration,
558                 rand() % 100 / 100., // expiration time
559                 visual_size,
560                 true,
561                 false,
562                 texture,
563                 texpos,
564                 texsize);
565
566         addParticle(toadd);
567 }
568
569 void ParticleManager::addParticle(Particle* toadd)
570 {
571         MutexAutoLock lock(m_particle_list_lock);
572         m_particles.push_back(toadd);
573 }