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