summaryrefslogtreecommitdiff
path: root/servers/visual/particle_system_sw.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'servers/visual/particle_system_sw.cpp')
-rw-r--r--servers/visual/particle_system_sw.cpp412
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);
+
+}