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()
178 MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
180 light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
182 light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
184 m_light = decode_light(light);
187 void Particle::updateVertices()
189 video::SColor c(255, m_light, m_light, m_light);
190 f32 tx0 = m_texpos.X;
191 f32 tx1 = m_texpos.X + m_texsize.X;
192 f32 ty0 = m_texpos.Y;
193 f32 ty1 = m_texpos.Y + m_texsize.Y;
195 m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
197 m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
199 m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
201 m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
204 v3s16 camera_offset = m_env->getCameraOffset();
205 for(u16 i=0; i<4; i++)
208 v3f ppos = m_player->getPosition()/BS;
209 m_vertices[i].Pos.rotateXZBy(atan2(ppos.Z-m_pos.Z, ppos.X-m_pos.X)/core::DEGTORAD+90);
211 m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
212 m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
214 m_box.addInternalPoint(m_vertices[i].Pos);
215 m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
224 void allparticles_step (float dtime)
226 for(std::vector<Particle*>::iterator i = all_particles.begin();
227 i != all_particles.end();)
229 if ((*i)->get_expired())
233 i = all_particles.erase(i);
243 void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
244 LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
245 const TileSpec tiles[])
247 for (u16 j = 0; j < 32; j++) // set the amount of particles here
249 addNodeParticle(gamedef, smgr, player, env, pos, tiles);
253 void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
254 LocalPlayer *player, ClientEnvironment &env,
255 v3s16 pos, const TileSpec tiles[])
257 addNodeParticle(gamedef, smgr, player, env, pos, tiles);
260 // add a particle of a node
261 // used by digging and punching particles
262 void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
263 LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
264 const TileSpec tiles[])
267 u8 texid = myrand_range(0,5);
268 video::ITexture *texture = tiles[texid].texture;
270 // Only use first frame of animated texture
272 if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
273 ymax /= tiles[texid].animation_frame_count;
275 float size = rand()%64/512.;
276 float visual_size = BS*size;
277 v2f texsize(size*2, ymax*size*2);
279 texpos.X = ((rand()%64)/64.-texsize.X);
280 texpos.Y = ymax*((rand()%64)/64.-texsize.Y);
283 v3f velocity( (rand()%100/50.-1)/1.5,
285 (rand()%100/50.-1)/1.5);
287 v3f acceleration(0,-9,0);
288 v3f particlepos = v3f(
289 (f32)pos.X+rand()%100/200.-0.25,
290 (f32)pos.Y+rand()%100/200.-0.25,
291 (f32)pos.Z+rand()%100/200.-0.25
302 rand()%100/100., // expiration time
315 ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
316 u16 amount, float time,
317 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
318 float minexptime, float maxexptime, float minsize, float maxsize,
319 bool collisiondetection, bool vertical, video::ITexture *texture, u32 id)
332 m_minexptime = minexptime;
333 m_maxexptime = maxexptime;
336 m_collisiondetection = collisiondetection;
337 m_vertical = vertical;
341 for (u16 i = 0; i<=m_amount; i++)
343 float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
344 m_spawntimes.push_back(spawntime);
347 all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this));
350 ParticleSpawner::~ParticleSpawner() {}
352 void ParticleSpawner::step(float dtime, ClientEnvironment &env)
356 if (m_spawntime != 0) // Spawner exists for a predefined timespan
358 for(std::vector<float>::iterator i = m_spawntimes.begin();
359 i != m_spawntimes.end();)
361 if ((*i) <= m_time && m_amount > 0)
365 v3f pos = random_v3f(m_minpos, m_maxpos);
366 v3f vel = random_v3f(m_minvel, m_maxvel);
367 v3f acc = random_v3f(m_minacc, m_maxacc);
368 float exptime = rand()/(float)RAND_MAX
369 *(m_maxexptime-m_minexptime)
371 float size = rand()/(float)RAND_MAX
372 *(m_maxsize-m_minsize)
385 m_collisiondetection,
390 i = m_spawntimes.erase(i);
398 else // Spawner exists for an infinity timespan, spawn on a per-second base
400 for (int i = 0; i <= m_amount; i++)
402 if (rand()/(float)RAND_MAX < dtime)
404 v3f pos = random_v3f(m_minpos, m_maxpos);
405 v3f vel = random_v3f(m_minvel, m_maxvel);
406 v3f acc = random_v3f(m_minacc, m_maxacc);
407 float exptime = rand()/(float)RAND_MAX
408 *(m_maxexptime-m_minexptime)
410 float size = rand()/(float)RAND_MAX
411 *(m_maxsize-m_minsize)
424 m_collisiondetection,
434 void allparticlespawners_step (float dtime, ClientEnvironment &env)
436 for(std::map<u32, ParticleSpawner*>::iterator i =
437 all_particlespawners.begin();
438 i != all_particlespawners.end();)
440 if (i->second->get_expired())
443 all_particlespawners.erase(i++);
447 i->second->step(dtime, env);
453 void delete_particlespawner (u32 id)
455 if (all_particlespawners.find(id) != all_particlespawners.end())
457 delete all_particlespawners.find(id)->second;
458 all_particlespawners.erase(id);
462 void clear_particles ()
464 for(std::map<u32, ParticleSpawner*>::iterator i =
465 all_particlespawners.begin();
466 i != all_particlespawners.end();)
469 all_particlespawners.erase(i++);
472 for(std::vector<Particle*>::iterator i =
473 all_particles.begin();
474 i != all_particles.end();)
478 i = all_particles.erase(i);