diff options
Diffstat (limited to 'servers/visual/particle_system_sw.cpp')
-rw-r--r-- | servers/visual/particle_system_sw.cpp | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/servers/visual/particle_system_sw.cpp b/servers/visual/particle_system_sw.cpp new file mode 100644 index 0000000000..49a68f8e80 --- /dev/null +++ b/servers/visual/particle_system_sw.cpp @@ -0,0 +1,412 @@ +/*************************************************************************/ +/* particle_system_sw.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 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;i<VS::PARTICLE_VAR_MAX;i++) { + particle_randomness[i]=0.0; + } + + particle_vars[VS::PARTICLE_LIFETIME]=2.0;// + particle_vars[VS::PARTICLE_SPREAD]=0.2;// + particle_vars[VS::PARTICLE_GRAVITY]=9.8;// + particle_vars[VS::PARTICLE_LINEAR_VELOCITY]=0.2;// + particle_vars[VS::PARTICLE_ANGULAR_VELOCITY]=0.0;// + particle_vars[VS::PARTICLE_LINEAR_ACCELERATION]=0.0;// + particle_vars[VS::PARTICLE_RADIAL_ACCELERATION]=0.0;// + particle_vars[VS::PARTICLE_TANGENTIAL_ACCELERATION]=1.0;// + particle_vars[VS::PARTICLE_DAMPING]=0.0;// + particle_vars[VS::PARTICLE_INITIAL_SIZE]=1.0; + particle_vars[VS::PARTICLE_FINAL_SIZE]=0.8; + particle_vars[VS::PARTICLE_HEIGHT]=1; + particle_vars[VS::PARTICLE_HEIGHT_SPEED_SCALE]=1; + + height_from_velocity=false; + local_coordinates=false; + + particle_vars[VS::PARTICLE_INITIAL_ANGLE]=0.0;// + + gravity_normal=Vector3(0,-1.0,0); + //emission_half_extents=Vector3(0.1,0.1,0.1); + emission_half_extents=Vector3(1,1,1); + color_phase_count=0; + color_phases[0].pos=0.0; + color_phases[0].color=Color(1.0,0.0,0.0); + visibility_aabb=AABB(Vector3(-64,-64,-64),Vector3(128,128,128)); + + attractor_count=0; + +} + + +ParticleSystemSW::~ParticleSystemSW() +{ +} + + +#define DEFAULT_SEED 1234567 + +_FORCE_INLINE_ static float _rand_from_seed(uint32_t *seed) { + + uint32_t k; + uint32_t s = (*seed); + if (s == 0) + s = 0x12345987; + k = s / 127773; + s = 16807 * (s - k * 127773) - 2836 * k; + if (s < 0) + s += 2147483647; + (*seed) = s; + + float v=((float)((*seed) & 0xFFFFF))/(float)0xFFFFF; + v=v*2.0-1.0; + return v; +} + +_FORCE_INLINE_ static uint32_t _irand_from_seed(uint32_t *seed) { + + uint32_t k; + uint32_t s = (*seed); + if (s == 0) + s = 0x12345987; + k = s / 127773; + s = 16807 * (s - k * 127773) - 2836 * k; + if (s < 0) + s += 2147483647; + (*seed) = s; + + return s; +} + +void ParticleSystemProcessSW::process(const ParticleSystemSW *p_system,const Transform& p_transform,float p_time) { + + valid=false; + if (p_system->amount<=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 (lifetime<CMP_EPSILON) { + ERR_EXPLAIN("Particle system lifetime too small."); + ERR_FAIL_COND(lifetime<CMP_EPSILON); + } + valid=true; + int particle_count=MIN(p_system->amount,ParticleSystemSW::MAX_PARTICLES);; + + + int emission_point_count = p_system->emission_points.size(); + DVector<Vector3>::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;i<p_system->attractor_count;i++) { + + attractor_positions[i]=p_transform.xform(p_system->attractors[i].pos); + } + + + for(int i=0;i<particle_count;i++) { + + ParticleData &p=pdata[i]; + + float restart_time = (i * lifetime / p_system->amount); + + 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;r<PARTICLE_RANDOM_NUMBERS;r++) + p.random[r]=_rand_from_seed(&rand_seed); + + } else { + + p.pos=Vector3(); + p.rot=0; + p.vel=Vector3(); + p.active=false; + } + + } else { + + if (!p.active) + continue; + + Vector3 force; + //apply gravity + force=p_system->gravity_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;a<p_system->attractor_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;i<p_system->color_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;i<p_system->amount;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 (time<time_pos) + time=time_pos-time; + else + time=(1.0-time)+time_pos; + + Vector3 up=p_camera_transform.basis.get_axis(1); // up determines the rotation + float up_scale=1.0; + + if (p_system->height_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<col_count) { + + if (cphase[cpos].pos > 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<ParticleDrawInfo*,_ParticleSorterSW> particle_sort; + particle_sort.sort(&draw_info_order[0],p_system->amount); + +} |