]> git.lizzy.rs Git - dragonfireclient.git/blob - src/particles.cpp
Move texture_min_size even further down the pipe. Now, textures are JIT-upscaled...
[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 "main.h" // For g_profiler and g_settings
24 #include "settings.h"
25 #include "client/tile.h"
26 #include "gamedef.h"
27 #include "collision.h"
28 #include <stdlib.h>
29 #include "util/numeric.h"
30 #include "light.h"
31 #include "environment.h"
32 #include "clientmap.h"
33 #include "mapnode.h"
34 #include "client.h"
35
36 /*
37         Utility
38 */
39
40 v3f random_v3f(v3f min, v3f max)
41 {
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);
45 }
46
47 Particle::Particle(
48         IGameDef *gamedef,
49         scene::ISceneManager* smgr,
50         LocalPlayer *player,
51         ClientEnvironment *env,
52         v3f pos,
53         v3f velocity,
54         v3f acceleration,
55         float expirationtime,
56         float size,
57         bool collisiondetection,
58         bool vertical,
59         video::ITexture *texture,
60         v2f texpos,
61         v2f texsize
62 ):
63         scene::ISceneNode(smgr->getRootSceneNode(), smgr)
64 {
65         // Misc
66         m_gamedef = gamedef;
67         m_env = env;
68
69         // Texture
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);
76         m_texpos = texpos;
77         m_texsize = texsize;
78
79
80         // Particle related
81         m_pos = pos;
82         m_velocity = velocity;
83         m_acceleration = acceleration;
84         m_expiration = expirationtime;
85         m_time = 0;
86         m_player = player;
87         m_size = size;
88         m_collisiondetection = collisiondetection;
89         m_vertical = vertical;
90
91         // Irrlicht stuff
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);
95
96         // Init lighting
97         updateLight();
98
99         // Init model
100         updateVertices();
101 }
102
103 Particle::~Particle()
104 {
105 }
106
107 void Particle::OnRegisterSceneNode()
108 {
109         if (IsVisible)
110                 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
111
112         ISceneNode::OnRegisterSceneNode();
113 }
114
115 void Particle::render()
116 {
117         video::IVideoDriver* driver = SceneManager->getVideoDriver();
118         driver->setMaterial(m_material);
119         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
120
121         u16 indices[] = {0,1,2, 2,3,0};
122         driver->drawVertexPrimitiveList(m_vertices, 4,
123                         indices, 2, video::EVT_STANDARD,
124                         scene::EPT_TRIANGLES, video::EIT_16BIT);
125 }
126
127 void Particle::step(float dtime)
128 {
129         m_time += dtime;
130         if (m_collisiondetection)
131         {
132                 core::aabbox3d<f32> box = m_collisionbox;
133                 v3f p_pos = m_pos*BS;
134                 v3f p_velocity = m_velocity*BS;
135                 v3f p_acceleration = m_acceleration*BS;
136                 collisionMoveSimple(m_env, m_gamedef,
137                         BS*0.5, box,
138                         0, dtime,
139                         p_pos, p_velocity, p_acceleration);
140                 m_pos = p_pos/BS;
141                 m_velocity = p_velocity/BS;
142                 m_acceleration = p_acceleration/BS;
143         }
144         else
145         {
146                 m_velocity += m_acceleration * dtime;
147                 m_pos += m_velocity * dtime;
148         }
149
150         // Update lighting
151         updateLight();
152
153         // Update model
154         updateVertices();
155 }
156
157 void Particle::updateLight()
158 {
159         u8 light = 0;
160         bool pos_ok;
161
162         v3s16 p = v3s16(
163                 floor(m_pos.X+0.5),
164                 floor(m_pos.Y+0.5),
165                 floor(m_pos.Z+0.5)
166         );
167         MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
168         if (pos_ok)
169                 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
170         else
171                 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
172
173         m_light = decode_light(light);
174 }
175
176 void Particle::updateVertices()
177 {
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;
183
184         m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
185                         c, tx0, ty1);
186         m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
187                         c, tx1, ty1);
188         m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
189                         c, tx1, ty0);
190         m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
191                         c, tx0, ty0);
192
193         v3s16 camera_offset = m_env->getCameraOffset();
194         for(u16 i=0; i<4; i++)
195         {
196                 if (m_vertical) {
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);
199                 } else {
200                         m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
201                         m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
202                 }
203                 m_box.addInternalPoint(m_vertices[i].Pos);
204                 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
205         }
206 }
207
208 /*
209         ParticleSpawner
210 */
211
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 vertical, video::ITexture *texture, u32 id,
217         ParticleManager *p_manager) :
218         m_particlemanager(p_manager)
219 {
220         m_gamedef = gamedef;
221         m_smgr = smgr;
222         m_player = player;
223         m_amount = amount;
224         m_spawntime = time;
225         m_minpos = minpos;
226         m_maxpos = maxpos;
227         m_minvel = minvel;
228         m_maxvel = maxvel;
229         m_minacc = minacc;
230         m_maxacc = maxacc;
231         m_minexptime = minexptime;
232         m_maxexptime = maxexptime;
233         m_minsize = minsize;
234         m_maxsize = maxsize;
235         m_collisiondetection = collisiondetection;
236         m_vertical = vertical;
237         m_texture = texture;
238         m_time = 0;
239
240         for (u16 i = 0; i<=m_amount; i++)
241         {
242                 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
243                 m_spawntimes.push_back(spawntime);
244         }
245 }
246
247 ParticleSpawner::~ParticleSpawner() {}
248
249 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
250 {
251         m_time += dtime;
252
253         if (m_spawntime != 0) // Spawner exists for a predefined timespan
254         {
255                 for(std::vector<float>::iterator i = m_spawntimes.begin();
256                                 i != m_spawntimes.end();)
257                 {
258                         if ((*i) <= m_time && m_amount > 0)
259                         {
260                                 m_amount--;
261
262                                 v3f pos = random_v3f(m_minpos, m_maxpos);
263                                 v3f vel = random_v3f(m_minvel, m_maxvel);
264                                 v3f acc = random_v3f(m_minacc, m_maxacc);
265                                 float exptime = rand()/(float)RAND_MAX
266                                                 *(m_maxexptime-m_minexptime)
267                                                 +m_minexptime;
268                                 float size = rand()/(float)RAND_MAX
269                                                 *(m_maxsize-m_minsize)
270                                                 +m_minsize;
271
272                                 Particle* toadd = new Particle(
273                                         m_gamedef,
274                                         m_smgr,
275                                         m_player,
276                                         env,
277                                         pos,
278                                         vel,
279                                         acc,
280                                         exptime,
281                                         size,
282                                         m_collisiondetection,
283                                         m_vertical,
284                                         m_texture,
285                                         v2f(0.0, 0.0),
286                                         v2f(1.0, 1.0));
287                                 m_particlemanager->addParticle(toadd);
288                                 i = m_spawntimes.erase(i);
289                         }
290                         else
291                         {
292                                 i++;
293                         }
294                 }
295         }
296         else // Spawner exists for an infinity timespan, spawn on a per-second base
297         {
298                 for (int i = 0; i <= m_amount; i++)
299                 {
300                         if (rand()/(float)RAND_MAX < dtime)
301                         {
302                                 v3f pos = random_v3f(m_minpos, m_maxpos);
303                                 v3f vel = random_v3f(m_minvel, m_maxvel);
304                                 v3f acc = random_v3f(m_minacc, m_maxacc);
305                                 float exptime = rand()/(float)RAND_MAX
306                                                 *(m_maxexptime-m_minexptime)
307                                                 +m_minexptime;
308                                 float size = rand()/(float)RAND_MAX
309                                                 *(m_maxsize-m_minsize)
310                                                 +m_minsize;
311
312                                 new Particle(
313                                         m_gamedef,
314                                         m_smgr,
315                                         m_player,
316                                         env,
317                                         pos,
318                                         vel,
319                                         acc,
320                                         exptime,
321                                         size,
322                                         m_collisiondetection,
323                                         m_vertical,
324                                         m_texture,
325                                         v2f(0.0, 0.0),
326                                         v2f(1.0, 1.0));
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         JMutexAutoLock 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         JMutexAutoLock 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         JMutexAutoLock lock(m_spawner_list_lock);
391         JMutexAutoLock 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                 JMutexAutoLock 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                         JMutexAutoLock 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                         JMutexAutoLock 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         JMutexAutoLock lock(m_particle_list_lock);
572         m_particles.push_back(toadd);
573 }