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