Include backtrace in error message when LuaErrors occur
[minetest-c55.git] / src / particles.cpp
blobebb54a49a1207a61ae1d8fdfba39fab6a774c327
1 /*
2 Minetest
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"
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"
36 Utility
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);
46 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
62 scene::ISceneNode(smgr->getRootSceneNode(), smgr)
64 // Misc
65 m_gamedef = gamedef;
66 m_env = env;
68 // Texture
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);
75 m_texpos = texpos;
76 m_texsize = texsize;
79 // Particle related
80 m_pos = pos;
81 m_velocity = velocity;
82 m_acceleration = acceleration;
83 m_expiration = expirationtime;
84 m_time = 0;
85 m_player = player;
86 m_size = size;
87 m_collisiondetection = collisiondetection;
88 m_vertical = vertical;
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);
95 // Init lighting
96 updateLight();
98 // Init model
99 updateVertices();
102 Particle::~Particle()
106 void Particle::OnRegisterSceneNode()
108 if (IsVisible)
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)
128 m_time += 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,
136 BS*0.5, box,
137 0, dtime,
138 p_pos, p_velocity, p_acceleration);
139 m_pos = p_pos/BS;
140 m_velocity = p_velocity/BS;
141 m_acceleration = p_acceleration/BS;
143 else
145 m_velocity += m_acceleration * dtime;
146 m_pos += m_velocity * dtime;
149 // Update lighting
150 updateLight();
152 // Update model
153 updateVertices();
156 void Particle::updateLight()
158 u8 light = 0;
159 bool pos_ok;
161 v3s16 p = v3s16(
162 floor(m_pos.X+0.5),
163 floor(m_pos.Y+0.5),
164 floor(m_pos.Z+0.5)
166 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
167 if (pos_ok)
168 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
169 else
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,
184 c, tx0, ty1);
185 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
186 c, tx1, ty1);
187 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
188 c, tx1, ty0);
189 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
190 c, tx0, ty0);
192 v3s16 camera_offset = m_env->getCameraOffset();
193 for(u16 i=0; i<4; i++)
195 if (m_vertical) {
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);
198 } else {
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);
208 ParticleSpawner
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)
219 m_gamedef = gamedef;
220 m_smgr = smgr;
221 m_player = player;
222 m_amount = amount;
223 m_spawntime = time;
224 m_minpos = minpos;
225 m_maxpos = maxpos;
226 m_minvel = minvel;
227 m_maxvel = maxvel;
228 m_minacc = minacc;
229 m_maxacc = maxacc;
230 m_minexptime = minexptime;
231 m_maxexptime = maxexptime;
232 m_minsize = minsize;
233 m_maxsize = maxsize;
234 m_collisiondetection = collisiondetection;
235 m_vertical = vertical;
236 m_texture = texture;
237 m_time = 0;
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)
250 m_time += dtime;
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)
259 m_amount--;
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)
266 +m_minexptime;
267 float size = rand()/(float)RAND_MAX
268 *(m_maxsize-m_minsize)
269 +m_minsize;
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 m_collisiondetection,
282 m_vertical,
283 m_texture,
284 v2f(0.0, 0.0),
285 v2f(1.0, 1.0));
286 m_particlemanager->addParticle(toadd);
287 i = m_spawntimes.erase(i);
289 else
291 ++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)
306 +m_minexptime;
307 float size = rand()/(float)RAND_MAX
308 *(m_maxsize-m_minsize)
309 +m_minsize;
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 m_collisiondetection,
322 m_vertical,
323 m_texture,
324 v2f(0.0, 0.0),
325 v2f(1.0, 1.0));
326 m_particlemanager->addParticle(toadd);
333 ParticleManager::ParticleManager(ClientEnvironment* env) :
334 m_env(env)
337 ParticleManager::~ParticleManager()
339 clearAll();
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())
357 delete i->second;
358 m_particle_spawners.erase(i++);
360 else
362 i->second->step(dtime, m_env);
363 ++i;
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())
376 (*i)->remove();
377 delete *i;
378 i = m_particles.erase(i);
380 else
382 (*i)->step(dtime);
383 ++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();)
396 delete i->second;
397 m_particle_spawners.erase(i++);
400 for(std::vector<Particle*>::iterator i =
401 m_particles.begin();
402 i != m_particles.end();)
404 (*i)->remove();
405 delete *i;
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
422 return;
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,
454 texture,
455 event->add_particlespawner.id,
456 this);
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,
472 toadd));
475 return;
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,
490 texture,
491 v2f(0.0, 0.0),
492 v2f(1.0, 1.0));
494 addParticle(toadd);
496 delete event->spawn_particle.pos;
497 delete event->spawn_particle.vel;
498 delete event->spawn_particle.acc;
500 return;
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[])
522 // Texture
523 u8 texid = myrand_range(0, 5);
524 video::ITexture *texture = tiles[texid].texture;
526 // Only use first frame of animated texture
527 f32 ymax = 1;
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);
534 v2f texpos;
535 texpos.X = ((rand() % 64) / 64. - texsize.X);
536 texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y);
538 // Physics
539 v3f velocity((rand() % 100 / 50. - 1) / 1.5,
540 rand() % 100 / 35.,
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(
551 gamedef,
552 smgr,
553 player,
554 m_env,
555 particlepos,
556 velocity,
557 acceleration,
558 rand() % 100 / 100., // expiration time
559 visual_size,
560 true,
561 false,
562 texture,
563 texpos,
564 texsize);
566 addParticle(toadd);
569 void ParticleManager::addParticle(Particle* toadd)
571 MutexAutoLock lock(m_particle_list_lock);
572 m_particles.push_back(toadd);