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)
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);
84 m_velocity = velocity;
85 m_acceleration = acceleration;
86 m_expiration = expirationtime;
90 m_collisiondetection = collisiondetection;
91 m_vertical = vertical;
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);
104 all_particles.push_back(this);
107 Particle::~Particle()
111 void Particle::OnRegisterSceneNode()
115 SceneManager->registerNodeForRendering
116 (this, scene::ESNRP_TRANSPARENT);
117 SceneManager->registerNodeForRendering
118 (this, scene::ESNRP_SOLID);
121 ISceneNode::OnRegisterSceneNode();
124 void Particle::render()
126 // TODO: Render particles in front of water and the selectionbox
128 video::IVideoDriver* driver = SceneManager->getVideoDriver();
129 driver->setMaterial(m_material);
130 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
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);
138 void Particle::step(float dtime)
141 if (m_collisiondetection)
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,
150 p_pos, p_velocity, p_acceleration);
152 m_velocity = p_velocity/BS;
153 m_acceleration = p_acceleration/BS;
157 m_velocity += m_acceleration * dtime;
158 m_pos += m_velocity * dtime;
168 void Particle::updateLight()
177 MapNode n = m_env->getClientMap().getNode(p);
178 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
180 catch(InvalidPositionException &e){
181 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
183 m_light = decode_light(light);
186 void Particle::updateVertices()
188 video::SColor c(255, m_light, m_light, m_light);
189 f32 tx0 = m_texpos.X;
190 f32 tx1 = m_texpos.X + m_texsize.X;
191 f32 ty0 = m_texpos.Y;
192 f32 ty1 = m_texpos.Y + m_texsize.Y;
194 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
196 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
198 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
200 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
203 v3s16 camera_offset = m_env->getCameraOffset();
204 for(u16 i=0; i<4; i++)
207 v3f ppos = m_player->getPosition()/BS;
208 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
210 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
211 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
213 m_box.addInternalPoint(m_vertices[i].Pos);
214 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
223 void allparticles_step (float dtime)
225 for(std::vector<Particle*>::iterator i = all_particles.begin();
226 i != all_particles.end();)
228 if ((*i)->get_expired())
232 i = all_particles.erase(i);
242 void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
243 LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
244 const TileSpec tiles[])
246 for (u16 j = 0; j < 32; j++) // set the amount of particles here
248 addNodeParticle(gamedef, smgr, player, env, pos, tiles);
252 void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
253 LocalPlayer *player, ClientEnvironment &env,
254 v3s16 pos, const TileSpec tiles[])
256 addNodeParticle(gamedef, smgr, player, env, pos, tiles);
259 // add a particle of a node
260 // used by digging and punching particles
261 void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
262 LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
263 const TileSpec tiles[])
266 u8 texid = myrand_range(0,5);
267 video::ITexture *texture = tiles[texid].texture;
269 // Only use first frame of animated texture
271 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
272 ymax /= tiles[texid].animation_frame_count;
274 float size = rand()%64/512.;
275 float visual_size = BS*size;
276 v2f texsize(size*2, ymax*size*2);
278 texpos.X = ((rand()%64)/64.-texsize.X);
279 texpos.Y = ymax*((rand()%64)/64.-texsize.Y);
282 v3f velocity( (rand()%100/50.-1)/1.5,
284 (rand()%100/50.-1)/1.5);
286 v3f acceleration(0,-9,0);
287 v3f particlepos = v3f(
288 (f32)pos.X+rand()%100/200.-0.25,
289 (f32)pos.Y+rand()%100/200.-0.25,
290 (f32)pos.Z+rand()%100/200.-0.25
301 rand()%100/100., // expiration time
314 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
315 u16 amount, float time,
316 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
317 float minexptime, float maxexptime, float minsize, float maxsize,
318 bool collisiondetection, bool vertical, video::ITexture *texture, u32 id)
331 m_minexptime = minexptime;
332 m_maxexptime = maxexptime;
335 m_collisiondetection = collisiondetection;
336 m_vertical = vertical;
340 for (u16 i = 0; i<=m_amount; i++)
342 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
343 m_spawntimes.push_back(spawntime);
346 all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this));
349 ParticleSpawner::~ParticleSpawner() {}
351 void ParticleSpawner::step(float dtime, ClientEnvironment &env)
355 if (m_spawntime != 0) // Spawner exists for a predefined timespan
357 for(std::vector<float>::iterator i = m_spawntimes.begin();
358 i != m_spawntimes.end();)
360 if ((*i) <= m_time && m_amount > 0)
364 v3f pos = random_v3f(m_minpos, m_maxpos);
365 v3f vel = random_v3f(m_minvel, m_maxvel);
366 v3f acc = random_v3f(m_minacc, m_maxacc);
367 float exptime = rand()/(float)RAND_MAX
368 *(m_maxexptime-m_minexptime)
370 float size = rand()/(float)RAND_MAX
371 *(m_maxsize-m_minsize)
384 m_collisiondetection,
389 i = m_spawntimes.erase(i);
397 else // Spawner exists for an infinity timespan, spawn on a per-second base
399 for (int i = 0; i <= m_amount; i++)
401 if (rand()/(float)RAND_MAX < dtime)
403 v3f pos = random_v3f(m_minpos, m_maxpos);
404 v3f vel = random_v3f(m_minvel, m_maxvel);
405 v3f acc = random_v3f(m_minacc, m_maxacc);
406 float exptime = rand()/(float)RAND_MAX
407 *(m_maxexptime-m_minexptime)
409 float size = rand()/(float)RAND_MAX
410 *(m_maxsize-m_minsize)
423 m_collisiondetection,
433 void allparticlespawners_step (float dtime, ClientEnvironment &env)
435 for(std::map<u32, ParticleSpawner*>::iterator i =
436 all_particlespawners.begin();
437 i != all_particlespawners.end();)
439 if (i->second->get_expired())
442 all_particlespawners.erase(i++);
446 i->second->step(dtime, env);
452 void delete_particlespawner (u32 id)
454 if (all_particlespawners.find(id) != all_particlespawners.end())
456 delete all_particlespawners.find(id)->second;
457 all_particlespawners.erase(id);
461 void clear_particles ()
463 for(std::map<u32, ParticleSpawner*>::iterator i =
464 all_particlespawners.begin();
465 i != all_particlespawners.end();)
468 all_particlespawners.erase(i++);
471 for(std::vector<Particle*>::iterator i =
472 all_particles.begin();
473 i != all_particles.end();)
477 i = all_particles.erase(i);