3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "particles.h"
21 #include "constants.h"
24 #include "client/tile.h"
26 #include "collision.h"
28 #include "util/numeric.h"
30 #include "environment.h"
31 #include "clientmap.h"
39 v3f
random_v3f(v3f min
, v3f max
)
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
);
48 scene::ISceneManager
* smgr
,
50 ClientEnvironment
*env
,
56 bool collisiondetection
,
58 video::ITexture
*texture
,
62 scene::ISceneNode(smgr
->getRootSceneNode(), smgr
)
69 m_material
.setFlag(video::EMF_LIGHTING
, false);
70 m_material
.setFlag(video::EMF_BACK_FACE_CULLING
, false);
71 m_material
.setFlag(video::EMF_BILINEAR_FILTER
, false);
72 m_material
.setFlag(video::EMF_FOG_ENABLE
, true);
73 m_material
.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL
;
74 m_material
.setTexture(0, texture
);
81 m_velocity
= velocity
;
82 m_acceleration
= acceleration
;
83 m_expiration
= expirationtime
;
87 m_collisiondetection
= collisiondetection
;
88 m_vertical
= vertical
;
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
);
102 Particle::~Particle()
106 void Particle::OnRegisterSceneNode()
109 SceneManager
->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT
);
111 ISceneNode::OnRegisterSceneNode();
114 void Particle::render()
116 video::IVideoDriver
* driver
= SceneManager
->getVideoDriver();
117 driver
->setMaterial(m_material
);
118 driver
->setTransform(video::ETS_WORLD
, AbsoluteTransformation
);
120 u16 indices
[] = {0,1,2, 2,3,0};
121 driver
->drawVertexPrimitiveList(m_vertices
, 4,
122 indices
, 2, video::EVT_STANDARD
,
123 scene::EPT_TRIANGLES
, video::EIT_16BIT
);
126 void Particle::step(float dtime
)
129 if (m_collisiondetection
)
131 core::aabbox3d
<f32
> box
= m_collisionbox
;
132 v3f p_pos
= m_pos
*BS
;
133 v3f p_velocity
= m_velocity
*BS
;
134 v3f p_acceleration
= m_acceleration
*BS
;
135 collisionMoveSimple(m_env
, m_gamedef
,
138 p_pos
, p_velocity
, p_acceleration
);
140 m_velocity
= p_velocity
/BS
;
141 m_acceleration
= p_acceleration
/BS
;
145 m_velocity
+= m_acceleration
* dtime
;
146 m_pos
+= m_velocity
* dtime
;
156 void Particle::updateLight()
166 MapNode n
= m_env
->getClientMap().getNodeNoEx(p
, &pos_ok
);
168 light
= n
.getLightBlend(m_env
->getDayNightRatio(), m_gamedef
->ndef());
170 light
= blend_light(m_env
->getDayNightRatio(), LIGHT_SUN
, 0);
172 m_light
= decode_light(light
);
175 void Particle::updateVertices()
177 video::SColor
c(255, m_light
, m_light
, m_light
);
178 f32 tx0
= m_texpos
.X
;
179 f32 tx1
= m_texpos
.X
+ m_texsize
.X
;
180 f32 ty0
= m_texpos
.Y
;
181 f32 ty1
= m_texpos
.Y
+ m_texsize
.Y
;
183 m_vertices
[0] = video::S3DVertex(-m_size
/2,-m_size
/2,0, 0,0,0,
185 m_vertices
[1] = video::S3DVertex(m_size
/2,-m_size
/2,0, 0,0,0,
187 m_vertices
[2] = video::S3DVertex(m_size
/2,m_size
/2,0, 0,0,0,
189 m_vertices
[3] = video::S3DVertex(-m_size
/2,m_size
/2,0, 0,0,0,
192 v3s16 camera_offset
= m_env
->getCameraOffset();
193 for(u16 i
=0; i
<4; i
++)
196 v3f ppos
= m_player
->getPosition()/BS
;
197 m_vertices
[i
].Pos
.rotateXZBy(atan2(ppos
.Z
-m_pos
.Z
, ppos
.X
-m_pos
.X
)/core::DEGTORAD
+90);
199 m_vertices
[i
].Pos
.rotateYZBy(m_player
->getPitch());
200 m_vertices
[i
].Pos
.rotateXZBy(m_player
->getYaw());
202 m_box
.addInternalPoint(m_vertices
[i
].Pos
);
203 m_vertices
[i
].Pos
+= m_pos
*BS
- intToFloat(camera_offset
, BS
);
211 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 m_particlemanager(p_manager
)
230 m_minexptime
= minexptime
;
231 m_maxexptime
= maxexptime
;
234 m_collisiondetection
= collisiondetection
;
235 m_vertical
= vertical
;
239 for (u16 i
= 0; i
<=m_amount
; i
++)
241 float spawntime
= (float)rand()/(float)RAND_MAX
*m_spawntime
;
242 m_spawntimes
.push_back(spawntime
);
246 ParticleSpawner::~ParticleSpawner() {}
248 void ParticleSpawner::step(float dtime
, ClientEnvironment
* env
)
252 if (m_spawntime
!= 0) // Spawner exists for a predefined timespan
254 for(std::vector
<float>::iterator i
= m_spawntimes
.begin();
255 i
!= m_spawntimes
.end();)
257 if ((*i
) <= m_time
&& m_amount
> 0)
261 v3f pos
= random_v3f(m_minpos
, m_maxpos
);
262 v3f vel
= random_v3f(m_minvel
, m_maxvel
);
263 v3f acc
= random_v3f(m_minacc
, m_maxacc
);
264 float exptime
= rand()/(float)RAND_MAX
265 *(m_maxexptime
-m_minexptime
)
267 float size
= rand()/(float)RAND_MAX
268 *(m_maxsize
-m_minsize
)
271 Particle
* toadd
= new Particle(
281 m_collisiondetection
,
286 m_particlemanager
->addParticle(toadd
);
287 i
= m_spawntimes
.erase(i
);
295 else // Spawner exists for an infinity timespan, spawn on a per-second base
297 for (int i
= 0; i
<= m_amount
; i
++)
299 if (rand()/(float)RAND_MAX
< dtime
)
301 v3f pos
= random_v3f(m_minpos
, m_maxpos
);
302 v3f vel
= random_v3f(m_minvel
, m_maxvel
);
303 v3f acc
= random_v3f(m_minacc
, m_maxacc
);
304 float exptime
= rand()/(float)RAND_MAX
305 *(m_maxexptime
-m_minexptime
)
307 float size
= rand()/(float)RAND_MAX
308 *(m_maxsize
-m_minsize
)
311 Particle
* toadd
= new Particle(
321 m_collisiondetection
,
326 m_particlemanager
->addParticle(toadd
);
333 ParticleManager::ParticleManager(ClientEnvironment
* env
) :
337 ParticleManager::~ParticleManager()
342 void ParticleManager::step(float dtime
)
344 stepParticles (dtime
);
345 stepSpawners (dtime
);
348 void ParticleManager::stepSpawners (float dtime
)
350 MutexAutoLock
lock(m_spawner_list_lock
);
351 for(std::map
<u32
, ParticleSpawner
*>::iterator i
=
352 m_particle_spawners
.begin();
353 i
!= m_particle_spawners
.end();)
355 if (i
->second
->get_expired())
358 m_particle_spawners
.erase(i
++);
362 i
->second
->step(dtime
, m_env
);
368 void ParticleManager::stepParticles (float dtime
)
370 MutexAutoLock
lock(m_particle_list_lock
);
371 for(std::vector
<Particle
*>::iterator i
= m_particles
.begin();
372 i
!= m_particles
.end();)
374 if ((*i
)->get_expired())
378 i
= m_particles
.erase(i
);
388 void ParticleManager::clearAll ()
390 MutexAutoLock
lock(m_spawner_list_lock
);
391 MutexAutoLock
lock2(m_particle_list_lock
);
392 for(std::map
<u32
, ParticleSpawner
*>::iterator i
=
393 m_particle_spawners
.begin();
394 i
!= m_particle_spawners
.end();)
397 m_particle_spawners
.erase(i
++);
400 for(std::vector
<Particle
*>::iterator i
=
402 i
!= m_particles
.end();)
406 i
= m_particles
.erase(i
);
410 void ParticleManager::handleParticleEvent(ClientEvent
*event
, IGameDef
*gamedef
,
411 scene::ISceneManager
* smgr
, LocalPlayer
*player
)
413 if (event
->type
== CE_DELETE_PARTICLESPAWNER
) {
414 MutexAutoLock
lock(m_spawner_list_lock
);
415 if (m_particle_spawners
.find(event
->delete_particlespawner
.id
) !=
416 m_particle_spawners
.end())
418 delete m_particle_spawners
.find(event
->delete_particlespawner
.id
)->second
;
419 m_particle_spawners
.erase(event
->delete_particlespawner
.id
);
421 // no allocated memory in delete event
425 if (event
->type
== CE_ADD_PARTICLESPAWNER
) {
428 MutexAutoLock
lock(m_spawner_list_lock
);
429 if (m_particle_spawners
.find(event
->add_particlespawner
.id
) !=
430 m_particle_spawners
.end())
432 delete m_particle_spawners
.find(event
->add_particlespawner
.id
)->second
;
433 m_particle_spawners
.erase(event
->add_particlespawner
.id
);
436 video::ITexture
*texture
=
437 gamedef
->tsrc()->getTextureForMesh(*(event
->add_particlespawner
.texture
));
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
,
455 event
->add_particlespawner
.id
,
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
;
468 MutexAutoLock
lock(m_spawner_list_lock
);
469 m_particle_spawners
.insert(
470 std::pair
<u32
, ParticleSpawner
*>(
471 event
->add_particlespawner
.id
,
478 if (event
->type
== CE_SPAWN_PARTICLE
) {
479 video::ITexture
*texture
=
480 gamedef
->tsrc()->getTextureForMesh(*(event
->spawn_particle
.texture
));
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
,
496 delete event
->spawn_particle
.pos
;
497 delete event
->spawn_particle
.vel
;
498 delete event
->spawn_particle
.acc
;
504 void ParticleManager::addDiggingParticles(IGameDef
* gamedef
, scene::ISceneManager
* smgr
,
505 LocalPlayer
*player
, v3s16 pos
, const TileSpec tiles
[])
507 for (u16 j
= 0; j
< 32; j
++) // set the amount of particles here
509 addNodeParticle(gamedef
, smgr
, player
, pos
, tiles
);
513 void ParticleManager::addPunchingParticles(IGameDef
* gamedef
, scene::ISceneManager
* smgr
,
514 LocalPlayer
*player
, v3s16 pos
, const TileSpec tiles
[])
516 addNodeParticle(gamedef
, smgr
, player
, pos
, tiles
);
519 void ParticleManager::addNodeParticle(IGameDef
* gamedef
, scene::ISceneManager
* smgr
,
520 LocalPlayer
*player
, v3s16 pos
, const TileSpec tiles
[])
523 u8 texid
= myrand_range(0, 5);
524 video::ITexture
*texture
= tiles
[texid
].texture
;
526 // Only use first frame of animated texture
528 if(tiles
[texid
].material_flags
& MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES
)
529 ymax
/= tiles
[texid
].animation_frame_count
;
531 float size
= rand() % 64 / 512.;
532 float visual_size
= BS
* size
;
533 v2f
texsize(size
* 2, ymax
* size
* 2);
535 texpos
.X
= ((rand() % 64) / 64. - texsize
.X
);
536 texpos
.Y
= ymax
* ((rand() % 64) / 64. - texsize
.Y
);
539 v3f
velocity((rand() % 100 / 50. - 1) / 1.5,
541 (rand() % 100 / 50. - 1) / 1.5);
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
550 Particle
* toadd
= new Particle(
558 rand() % 100 / 100., // expiration time
569 void ParticleManager::addParticle(Particle
* toadd
)
571 MutexAutoLock
lock(m_particle_list_lock
);
572 m_particles
.push_back(toadd
);