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