]> git.lizzy.rs Git - dragonfireclient.git/blob - src/particles.cpp
Remove debug output
[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 "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
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 std::vector<Particle*> all_particles;
47 std::map<u32, ParticleSpawner*> all_particlespawners;
48
49 Particle::Particle(
50         IGameDef *gamedef,
51         scene::ISceneManager* smgr,
52         LocalPlayer *player,
53         ClientEnvironment &env,
54         v3f pos,
55         v3f velocity,
56         v3f acceleration,
57         float expirationtime,
58         float size,
59         bool collisiondetection,
60         video::ITexture *texture,
61         v2f texpos,
62         v2f texsize
63 ):
64         scene::ISceneNode(smgr->getRootSceneNode(), smgr)
65 {
66         // Misc
67         m_gamedef = gamedef;
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
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(env);
97
98         // Init model
99         updateVertices();
100
101         all_particles.push_back(this);
102 }
103
104 Particle::~Particle()
105 {
106 }
107
108 void Particle::OnRegisterSceneNode()
109 {
110         if (IsVisible)
111         {
112                 SceneManager->registerNodeForRendering
113                                 (this, scene::ESNRP_TRANSPARENT);
114                 SceneManager->registerNodeForRendering
115                                 (this, scene::ESNRP_SOLID);
116         }
117
118         ISceneNode::OnRegisterSceneNode();
119 }
120
121 void Particle::render()
122 {
123         // TODO: Render particles in front of water and the selectionbox
124
125         video::IVideoDriver* driver = SceneManager->getVideoDriver();
126         driver->setMaterial(m_material);
127         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
128
129         u16 indices[] = {0,1,2, 2,3,0};
130         driver->drawVertexPrimitiveList(m_vertices, 4,
131                         indices, 2, video::EVT_STANDARD,
132                         scene::EPT_TRIANGLES, video::EIT_16BIT);
133 }
134
135 void Particle::step(float dtime, ClientEnvironment &env)
136 {
137         m_time += dtime;
138         if (m_collisiondetection)
139         {
140                 core::aabbox3d<f32> box = m_collisionbox;
141                 v3f p_pos = m_pos*BS;
142                 v3f p_velocity = m_velocity*BS;
143                 v3f p_acceleration = m_acceleration*BS;
144                 collisionMoveSimple(&env, m_gamedef,
145                         BS*0.5, box,
146                         0, dtime,
147                         p_pos, p_velocity, p_acceleration);
148                 m_pos = p_pos/BS;
149                 m_velocity = p_velocity/BS;
150                 m_acceleration = p_acceleration/BS;
151         }
152         else
153         {
154                 m_velocity += m_acceleration * dtime;
155                 m_pos += m_velocity * dtime;
156         }
157
158         // Update lighting
159         updateLight(env);
160
161         // Update model
162         updateVertices();
163 }
164
165 void Particle::updateLight(ClientEnvironment &env)
166 {
167         u8 light = 0;
168         try{
169                 v3s16 p = v3s16(
170                         floor(m_pos.X+0.5),
171                         floor(m_pos.Y+0.5),
172                         floor(m_pos.Z+0.5)
173                 );
174                 MapNode n = env.getClientMap().getNode(p);
175                 light = n.getLightBlend(env.getDayNightRatio(), m_gamedef->ndef());
176         }
177         catch(InvalidPositionException &e){
178                 light = blend_light(env.getDayNightRatio(), LIGHT_SUN, 0);
179         }
180         m_light = decode_light(light);
181 }
182
183 void Particle::updateVertices()
184 {
185         video::SColor c(255, m_light, m_light, m_light);
186         f32 tx0 = m_texpos.X;
187         f32 tx1 = m_texpos.X + m_texsize.X;
188         f32 ty0 = m_texpos.Y;
189         f32 ty1 = m_texpos.Y + m_texsize.Y;
190
191         m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
192                         c, tx0, ty1);
193         m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
194                         c, tx1, ty1);
195         m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
196                         c, tx1, ty0);
197         m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
198                         c, tx0, ty0);
199
200         for(u16 i=0; i<4; i++)
201         {
202                 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
203                 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
204                 m_box.addInternalPoint(m_vertices[i].Pos);
205                 m_vertices[i].Pos += m_pos*BS;
206         }
207 }
208
209
210 /*
211         Helpers
212 */
213
214
215 void allparticles_step (float dtime, ClientEnvironment &env)
216 {
217         for(std::vector<Particle*>::iterator i = all_particles.begin();
218                         i != all_particles.end();)
219         {
220                 if ((*i)->get_expired())
221                 {
222                         (*i)->remove();
223                         delete *i;
224                         all_particles.erase(i);
225                 }
226                 else
227                 {
228                         (*i)->step(dtime, env);
229                         i++;
230                 }
231         }
232 }
233
234 void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
235                 LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
236                 const TileSpec tiles[])
237 {
238         for (u16 j = 0; j < 32; j++) // set the amount of particles here
239         {
240                 addNodeParticle(gamedef, smgr, player, env, pos, tiles);
241         }
242 }
243
244 void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
245                 LocalPlayer *player, ClientEnvironment &env,
246                 v3s16 pos, const TileSpec tiles[])
247 {
248         addNodeParticle(gamedef, smgr, player, env, pos, tiles);
249 }
250
251 // add a particle of a node
252 // used by digging and punching particles
253 void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
254                 LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
255                 const TileSpec tiles[])
256 {
257         // Texture
258         u8 texid = myrand_range(0,5);
259         video::ITexture *texture = tiles[texid].texture;
260
261         // Only use first frame of animated texture
262         f32 ymax = 1;
263         if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
264                 ymax /= tiles[texid].animation_frame_count;
265
266         float size = rand()%64/512.;
267         float visual_size = BS*size;
268         v2f texsize(size*2, ymax*size*2);
269         v2f texpos;
270         texpos.X = ((rand()%64)/64.-texsize.X);
271         texpos.Y = ymax*((rand()%64)/64.-texsize.Y);
272
273         // Physics
274         v3f velocity(   (rand()%100/50.-1)/1.5,
275                         rand()%100/35.,
276                         (rand()%100/50.-1)/1.5);
277
278         v3f acceleration(0,-9,0);
279         v3f particlepos = v3f(
280                 (f32)pos.X+rand()%100/200.-0.25,
281                 (f32)pos.Y+rand()%100/200.-0.25,
282                 (f32)pos.Z+rand()%100/200.-0.25
283         );
284
285         new Particle(
286                 gamedef,
287                 smgr,
288                 player,
289                 env,
290                 particlepos,
291                 velocity,
292                 acceleration,
293                 rand()%100/100., // expiration time
294                 visual_size,
295                 true,
296                 texture,
297                 texpos,
298                 texsize);
299 }
300
301 /*
302         ParticleSpawner
303 */
304
305 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
306         u16 amount, float time,
307         v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
308         float minexptime, float maxexptime, float minsize, float maxsize,
309         bool collisiondetection, video::ITexture *texture, u32 id)
310 {
311         m_gamedef = gamedef;
312         m_smgr = smgr;
313         m_player = player;
314         m_amount = amount;
315         m_spawntime = time;
316         m_minpos = minpos;
317         m_maxpos = maxpos;
318         m_minvel = minvel;
319         m_maxvel = maxvel;
320         m_minacc = minacc;
321         m_maxacc = maxacc;
322         m_minexptime = minexptime;
323         m_maxexptime = maxexptime;
324         m_minsize = minsize;
325         m_maxsize = maxsize;
326         m_collisiondetection = collisiondetection;
327         m_texture = texture;
328         m_time = 0;
329
330         for (u16 i = 0; i<=m_amount; i++)
331         {
332                 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
333                 m_spawntimes.push_back(spawntime);
334         }
335
336         all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this));
337 }
338
339 ParticleSpawner::~ParticleSpawner() {}
340
341 void ParticleSpawner::step(float dtime, ClientEnvironment &env)
342 {
343         m_time += dtime;
344
345         if (m_spawntime != 0) // Spawner exists for a predefined timespan
346         {
347                 for(std::vector<float>::iterator i = m_spawntimes.begin();
348                                 i != m_spawntimes.end();)
349                 {
350                         if ((*i) <= m_time && m_amount > 0)
351                         {
352                                 m_amount--;
353
354                                 v3f pos = random_v3f(m_minpos, m_maxpos);
355                                 v3f vel = random_v3f(m_minvel, m_maxvel);
356                                 v3f acc = random_v3f(m_minacc, m_maxacc);
357                                 float exptime = rand()/(float)RAND_MAX
358                                                 *(m_maxexptime-m_minexptime)
359                                                 +m_minexptime;
360                                 float size = rand()/(float)RAND_MAX
361                                                 *(m_maxsize-m_minsize)
362                                                 +m_minsize;
363
364                                 new Particle(
365                                         m_gamedef,
366                                         m_smgr,
367                                         m_player,
368                                         env,
369                                         pos,
370                                         vel,
371                                         acc,
372                                         exptime,
373                                         size,
374                                         m_collisiondetection,
375                                         m_texture,
376                                         v2f(0.0, 0.0),
377                                         v2f(1.0, 1.0));
378                                 m_spawntimes.erase(i);
379                         }
380                         else
381                         {
382                                 i++;
383                         }
384                 }
385         }
386         else // Spawner exists for an infinity timespan, spawn on a per-second base
387         {
388                 for (int i = 0; i <= m_amount; i++)
389                 {
390                         if (rand()/(float)RAND_MAX < dtime)
391                         {
392                                 v3f pos = random_v3f(m_minpos, m_maxpos);
393                                 v3f vel = random_v3f(m_minvel, m_maxvel);
394                                 v3f acc = random_v3f(m_minacc, m_maxacc);
395                                 float exptime = rand()/(float)RAND_MAX
396                                                 *(m_maxexptime-m_minexptime)
397                                                 +m_minexptime;
398                                 float size = rand()/(float)RAND_MAX
399                                                 *(m_maxsize-m_minsize)
400                                                 +m_minsize;
401
402                                 new Particle(
403                                         m_gamedef,
404                                         m_smgr,
405                                         m_player,
406                                         env,
407                                         pos,
408                                         vel,
409                                         acc,
410                                         exptime,
411                                         size,
412                                         m_collisiondetection,
413                                         m_texture,
414                                         v2f(0.0, 0.0),
415                                         v2f(1.0, 1.0));
416                         }
417                 }
418         }
419 }
420
421 void allparticlespawners_step (float dtime, ClientEnvironment &env)
422 {
423         for(std::map<u32, ParticleSpawner*>::iterator i = 
424                         all_particlespawners.begin();
425                         i != all_particlespawners.end();)
426         {
427                 if (i->second->get_expired())
428                 {
429                         delete i->second;
430                         all_particlespawners.erase(i++);
431                 }
432                 else
433                 {
434                         i->second->step(dtime, env);
435                         i++;
436                 }
437         }
438 }
439
440 void delete_particlespawner (u32 id)
441 {
442         if (all_particlespawners.find(id) != all_particlespawners.end())
443         {
444                 delete all_particlespawners.find(id)->second;
445                 all_particlespawners.erase(id);
446         }
447 }
448
449 void clear_particles ()
450 {
451         for(std::map<u32, ParticleSpawner*>::iterator i =
452                         all_particlespawners.begin();
453                         i != all_particlespawners.end();)
454         {
455                 delete i->second;
456                 all_particlespawners.erase(i++);
457         }
458
459         for(std::vector<Particle*>::iterator i =
460                         all_particles.begin();
461                         i != all_particles.end();)
462         {
463                 (*i)->remove();
464                 delete *i;
465                 all_particles.erase(i);
466         }       
467 }