/*************************************************************************/ /* particle_system_sw.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "particle_system_sw.h" #include "sort.h" ParticleSystemSW::ParticleSystemSW() { amount=8; emitting=true; for (int i=0;iamount<=0) { ERR_EXPLAIN("Invalid amount of particles: "+itos(p_system->amount)); ERR_FAIL_COND(p_system->amount<=0); } if (p_system->attractor_count<0 || p_system->attractor_count>VS::MAX_PARTICLE_ATTRACTORS) { ERR_EXPLAIN("Invalid amount of particle attractors."); ERR_FAIL_COND(p_system->attractor_count<0 || p_system->attractor_count>VS::MAX_PARTICLE_ATTRACTORS); } float lifetime = p_system->particle_vars[VS::PARTICLE_LIFETIME]; if (lifetimeamount,ParticleSystemSW::MAX_PARTICLES);; int emission_point_count = p_system->emission_points.size(); DVector::Read r; if (emission_point_count) r=p_system->emission_points.read(); if (particle_count!=particle_data.size()) { //clear the whole system if particle amount changed particle_data.clear(); particle_data.resize(p_system->amount); particle_system_time=0; } float next_time = particle_system_time+p_time; if (next_time > lifetime) next_time=Math::fmod(next_time,lifetime); ParticleData *pdata=&particle_data[0]; Vector3 attractor_positions[VS::MAX_PARTICLE_ATTRACTORS]; for(int i=0;iattractor_count;i++) { attractor_positions[i]=p_transform.xform(p_system->attractors[i].pos); } for(int i=0;iamount); bool restart=false; if ( next_time < particle_system_time ) { if (restart_time > particle_system_time || restart_time < next_time ) restart=true; } else if (restart_time > particle_system_time && restart_time < next_time ) { restart=true; } if (restart) { if (p_system->emitting) { if (emission_point_count==0) { //use AABB if (p_system->local_coordinates) p.pos = p_system->emission_half_extents * Vector3( _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed) ); else p.pos = p_transform.xform( p_system->emission_half_extents * Vector3( _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed) ) ); } else { //use preset positions if (p_system->local_coordinates) p.pos = r[_irand_from_seed(&rand_seed)%emission_point_count]; else p.pos = p_transform.xform( r[_irand_from_seed(&rand_seed)%emission_point_count] ); } float angle1 = _rand_from_seed(&rand_seed)*p_system->particle_vars[VS::PARTICLE_SPREAD]*Math_PI; float angle2 = _rand_from_seed(&rand_seed)*20.0*Math_PI; // make it more random like Vector3 rot_xz=Vector3( Math::sin(angle1), 0.0, Math::cos(angle1) ); Vector3 rot = Vector3( Math::cos(angle2)*rot_xz.x,Math::sin(angle2)*rot_xz.x, rot_xz.z); p.vel=(rot*p_system->particle_vars[VS::PARTICLE_LINEAR_VELOCITY]+rot*p_system->particle_randomness[VS::PARTICLE_LINEAR_VELOCITY]*_rand_from_seed(&rand_seed)); if (!p_system->local_coordinates) p.vel=p_transform.basis.xform( p.vel ); p.vel+=p_system->emission_base_velocity; p.rot=p_system->particle_vars[VS::PARTICLE_INITIAL_ANGLE]+p_system->particle_randomness[VS::PARTICLE_INITIAL_ANGLE]*_rand_from_seed(&rand_seed); p.active=true; for(int r=0;rgravity_normal * (p_system->particle_vars[VS::PARTICLE_GRAVITY]+(p_system->particle_randomness[VS::PARTICLE_GRAVITY]*p.random[0])); //apply linear acceleration force+=p.vel.normalized() * (p_system->particle_vars[VS::PARTICLE_LINEAR_ACCELERATION]+p_system->particle_randomness[VS::PARTICLE_LINEAR_ACCELERATION]*p.random[1]); //apply radial acceleration Vector3 org; if (!p_system->local_coordinates) org=p_transform.origin; force+=(p.pos-org).normalized() * (p_system->particle_vars[VS::PARTICLE_RADIAL_ACCELERATION]+p_system->particle_randomness[VS::PARTICLE_RADIAL_ACCELERATION]*p.random[2]); //apply tangential acceleration force+=(p.pos-org).cross(p_system->gravity_normal).normalized() * (p_system->particle_vars[VS::PARTICLE_TANGENTIAL_ACCELERATION]+p_system->particle_randomness[VS::PARTICLE_TANGENTIAL_ACCELERATION]*p.random[3]); //apply attractor forces for(int a=0;aattractor_count;a++) { force+=(p.pos-attractor_positions[a]).normalized() * p_system->attractors[a].force; } p.vel+=force * p_time; if (p_system->particle_vars[VS::PARTICLE_DAMPING]) { float v = p.vel.length(); float damp = p_system->particle_vars[VS::PARTICLE_DAMPING] + p_system->particle_vars[VS::PARTICLE_DAMPING] * p_system->particle_randomness[VS::PARTICLE_DAMPING]; v -= damp * p_time; if (v<0) { p.vel=Vector3(); } else { p.vel=p.vel.normalized() * v; } } p.rot+=(p_system->particle_vars[VS::PARTICLE_ANGULAR_VELOCITY]+p_system->particle_randomness[VS::PARTICLE_ANGULAR_VELOCITY]*p.random[4]) *p_time; p.pos+=p.vel * p_time; } } particle_system_time=Math::fmod( particle_system_time+p_time, lifetime ); } ParticleSystemProcessSW::ParticleSystemProcessSW() { particle_system_time=0; rand_seed=1234567; valid=false; } struct _ParticleSorterSW { _FORCE_INLINE_ bool operator()(const ParticleSystemDrawInfoSW::ParticleDrawInfo *p_a,const ParticleSystemDrawInfoSW::ParticleDrawInfo *p_b) const { return p_a->d > p_b->d; // draw from further away to closest } }; void ParticleSystemDrawInfoSW::prepare(const ParticleSystemSW *p_system,const ParticleSystemProcessSW *p_process,const Transform& p_system_transform,const Transform& p_camera_transform) { ERR_FAIL_COND(p_process->particle_data.size() != p_system->amount); ERR_FAIL_COND(p_system->amount<=0 || p_system->amount>=ParticleSystemSW::MAX_PARTICLES); const ParticleSystemProcessSW::ParticleData *pdata=&p_process->particle_data[0]; float time_pos=p_process->particle_system_time/p_system->particle_vars[VS::PARTICLE_LIFETIME]; ParticleSystemSW::ColorPhase cphase[VS::MAX_PARTICLE_COLOR_PHASES]; float last=-1; int col_count=0; for(int i=0;icolor_phase_count;i++) { if (p_system->color_phases[i].pos<=last) break; cphase[i]=p_system->color_phases[i]; col_count++; } Vector3 camera_z_axis = p_camera_transform.basis.get_axis(2); for(int i=0;iamount;i++) { ParticleDrawInfo &pdi=draw_info[i]; pdi.data=&pdata[i]; pdi.transform.origin=pdi.data->pos; if (p_system->local_coordinates) pdi.transform.origin=p_system_transform.xform(pdi.transform.origin); pdi.d=-camera_z_axis.dot(pdi.transform.origin); // adjust particle size, color and rotation float time = ((float)i / p_system->amount); if (timeheight_from_velocity) { Vector3 veld = pdi.data->vel; Vector3 cam_z = camera_z_axis.normalized(); float vc = Math::abs(veld.normalized().dot(cam_z)); if (vc<(1.0-CMP_EPSILON)) { up = Plane(cam_z,0).project(veld).normalized(); float h = p_system->particle_vars[VS::PARTICLE_HEIGHT]+p_system->particle_randomness[VS::PARTICLE_HEIGHT]*pdi.data->random[7]; float velh = veld.length(); h+=velh*(p_system->particle_vars[VS::PARTICLE_HEIGHT_SPEED_SCALE]+p_system->particle_randomness[VS::PARTICLE_HEIGHT_SPEED_SCALE]*pdi.data->random[7]); up_scale=Math::lerp(1.0,h,(1.0-vc)); } } else if (pdi.data->rot) { up.rotate(camera_z_axis,pdi.data->rot); } { // matrix Vector3 v_z = (p_camera_transform.origin-pdi.transform.origin).normalized(); // Vector3 v_z = (p_camera_transform.origin-pdi.data->pos).normalized(); Vector3 v_y = up; Vector3 v_x = v_y.cross(v_z); v_y = v_z.cross(v_x); v_x.normalize(); v_y.normalize(); float initial_scale, final_scale; initial_scale = p_system->particle_vars[VS::PARTICLE_INITIAL_SIZE]+p_system->particle_randomness[VS::PARTICLE_INITIAL_SIZE]*pdi.data->random[5]; final_scale = p_system->particle_vars[VS::PARTICLE_FINAL_SIZE]+p_system->particle_randomness[VS::PARTICLE_FINAL_SIZE]*pdi.data->random[6]; float scale = initial_scale + time * (final_scale - initial_scale); pdi.transform.basis.set_axis(0,v_x * scale); pdi.transform.basis.set_axis(1,v_y * scale * up_scale); pdi.transform.basis.set_axis(2,v_z * scale); } int cpos=0; while(cpos time) break; cpos++; } cpos--; if (cpos==-1) pdi.color=Color(1,1,1,1); else { if (cpos==col_count-1) pdi.color=cphase[cpos].color; else { float diff = (cphase[cpos+1].pos-cphase[cpos].pos); if (diff>0) pdi.color=cphase[cpos].color.linear_interpolate(cphase[cpos+1].color, (time - cphase[cpos].pos) / diff ); else pdi.color=cphase[cpos+1].color; } } draw_info_order[i]=&pdi; } SortArray particle_sort; particle_sort.sort(&draw_info_order[0],p_system->amount); }