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"
23 #include "main.h" // For g_profiler and g_settings
27 #include "collision.h"
29 #include "util/numeric.h"
31 #include "environment.h"
32 #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);
46 std::vector<Particle*> all_particles;
47 std::map<u32, ParticleSpawner*> all_particlespawners;
51 scene::ISceneManager* smgr,
53 ClientEnvironment &env,
59 bool collisiondetection,
61 video::ITexture *texture,
65 scene::ISceneNode(smgr->getRootSceneNode(), smgr)
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);
83 m_velocity = velocity;
84 m_acceleration = acceleration;
85 m_expiration = expirationtime;
89 m_collisiondetection = collisiondetection;
90 m_vertical = vertical;
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);
103 all_particles.push_back(this);
106 Particle::~Particle()
110 void Particle::OnRegisterSceneNode()
114 SceneManager->registerNodeForRendering
115 (this, scene::ESNRP_TRANSPARENT);
116 SceneManager->registerNodeForRendering
117 (this, scene::ESNRP_SOLID);
120 ISceneNode::OnRegisterSceneNode();
123 void Particle::render()
125 // TODO: Render particles in front of water and the selectionbox
127 video::IVideoDriver* driver = SceneManager->getVideoDriver();
128 driver->setMaterial(m_material);
129 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
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);
137 void Particle::step(float dtime, ClientEnvironment &env)
140 if (m_collisiondetection)
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,
149 p_pos, p_velocity, p_acceleration);
151 m_velocity = p_velocity/BS;
152 m_acceleration = p_acceleration/BS;
156 m_velocity += m_acceleration * dtime;
157 m_pos += m_velocity * dtime;
167 void Particle::updateLight(ClientEnvironment &env)
176 MapNode n = env.getClientMap().getNode(p);
177 light = n.getLightBlend(env.getDayNightRatio(), m_gamedef->ndef());
179 catch(InvalidPositionException &e){
180 light = blend_light(env.getDayNightRatio(), LIGHT_SUN, 0);
182 m_light = decode_light(light);
185 void Particle::updateVertices()
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;
193 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
195 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
197 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
199 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
202 for(u16 i=0; i<4; i++)
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);
208 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
209 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
211 m_box.addInternalPoint(m_vertices[i].Pos);
212 m_vertices[i].Pos += m_pos*BS;
222 void allparticles_step (float dtime, ClientEnvironment &env)
224 for(std::vector<Particle*>::iterator i = all_particles.begin();
225 i != all_particles.end();)
227 if ((*i)->get_expired())
231 i = all_particles.erase(i);
235 (*i)->step(dtime, env);
241 void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
242 LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
243 const TileSpec tiles[])
245 for (u16 j = 0; j < 32; j++) // set the amount of particles here
247 addNodeParticle(gamedef, smgr, player, env, pos, tiles);
251 void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
252 LocalPlayer *player, ClientEnvironment &env,
253 v3s16 pos, const TileSpec tiles[])
255 addNodeParticle(gamedef, smgr, player, env, pos, tiles);
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[])
265 u8 texid = myrand_range(0,5);
266 video::ITexture *texture = tiles[texid].texture;
268 // Only use first frame of animated texture
270 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
271 ymax /= tiles[texid].animation_frame_count;
273 float size = rand()%64/512.;
274 float visual_size = BS*size;
275 v2f texsize(size*2, ymax*size*2);
277 texpos.X = ((rand()%64)/64.-texsize.X);
278 texpos.Y = ymax*((rand()%64)/64.-texsize.Y);
281 v3f velocity( (rand()%100/50.-1)/1.5,
283 (rand()%100/50.-1)/1.5);
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
300 rand()%100/100., // expiration time
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)
330 m_minexptime = minexptime;
331 m_maxexptime = maxexptime;
334 m_collisiondetection = collisiondetection;
335 m_vertical = vertical;
339 for (u16 i = 0; i<=m_amount; i++)
341 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
342 m_spawntimes.push_back(spawntime);
345 all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this));
348 ParticleSpawner::~ParticleSpawner() {}
350 void ParticleSpawner::step(float dtime, ClientEnvironment &env)
354 if (m_spawntime != 0) // Spawner exists for a predefined timespan
356 for(std::vector<float>::iterator i = m_spawntimes.begin();
357 i != m_spawntimes.end();)
359 if ((*i) <= m_time && m_amount > 0)
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)
369 float size = rand()/(float)RAND_MAX
370 *(m_maxsize-m_minsize)
383 m_collisiondetection,
388 i = m_spawntimes.erase(i);
396 else // Spawner exists for an infinity timespan, spawn on a per-second base
398 for (int i = 0; i <= m_amount; i++)
400 if (rand()/(float)RAND_MAX < dtime)
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)
408 float size = rand()/(float)RAND_MAX
409 *(m_maxsize-m_minsize)
422 m_collisiondetection,
432 void allparticlespawners_step (float dtime, ClientEnvironment &env)
434 for(std::map<u32, ParticleSpawner*>::iterator i =
435 all_particlespawners.begin();
436 i != all_particlespawners.end();)
438 if (i->second->get_expired())
441 all_particlespawners.erase(i++);
445 i->second->step(dtime, env);
451 void delete_particlespawner (u32 id)
453 if (all_particlespawners.find(id) != all_particlespawners.end())
455 delete all_particlespawners.find(id)->second;
456 all_particlespawners.erase(id);
460 void clear_particles ()
462 for(std::map<u32, ParticleSpawner*>::iterator i =
463 all_particlespawners.begin();
464 i != all_particlespawners.end();)
467 all_particlespawners.erase(i++);
470 for(std::vector<Particle*>::iterator i =
471 all_particles.begin();
472 i != all_particles.end();)
476 i = all_particles.erase(i);