void Particles2D::_notification(int p_what) { switch(p_what) { case NOTIFICATION_PROCESS: { _process_particles( get_process_delta_time() ); } break; case NOTIFICATION_ENTER_TREE: { float ppt=preprocess; while(ppt>0) { _process_particles(0.1); ppt-=0.1; } } break; case NOTIFICATION_DRAW: { if (particles.size()==0 || lifetime==0) return; RID ci=get_canvas_item(); Size2 size(1,1); Point2 center; int total_frames=1; if (!texture.is_null()) { size=texture->get_size(); size.x/=h_frames; size.y/=v_frames; total_frames=h_frames*v_frames; } float time_pos=(time/lifetime); Particle *pdata=&particles[0]; int particle_count=particles.size(); RID texrid; if (texture.is_valid()) texrid = texture->get_rid(); Matrix32 invxform; if (!local_space) invxform=get_global_transform().affine_inverse(); int start_particle = (int)(time * (float)particle_count / lifetime); for (int id=0;id<particle_count;++id) { int i = start_particle + id; if (i >= particle_count) { i -= particle_count; } Particle &p=pdata[i]; if (!p.active) continue; float ptime = ((float)i / particle_count)*explosiveness; if (ptime<time_pos) ptime=time_pos-ptime; else ptime=(1.0-ptime)+time_pos; uint32_t rand_seed=p.seed*(i+1); Color color; if(color_ramp.is_valid()) { color = color_ramp->get_color_at_offset(ptime); } else { color = default_color; } { float huerand=_rand_from_seed(&rand_seed); float huerot = param[PARAM_HUE_VARIATION] + randomness[PARAM_HUE_VARIATION] * huerand; if (Math::abs(huerot) > CMP_EPSILON) { float h=color.get_h(); float s=color.get_s(); float v=color.get_v(); float a=color.a; //float preh=h; h+=huerot; h=Math::abs(Math::fposmod(h,1.0)); //print_line("rand: "+rtos(randomness[PARAM_HUE_VARIATION])+" rand: "+rtos(huerand)); //print_line(itos(i)+":hue: "+rtos(preh)+" + "+rtos(huerot)+" = "+rtos(h)); color.set_hsv(h,s,v); color.a=a; } } float initial_size = param[PARAM_INITIAL_SIZE]+param[PARAM_INITIAL_SIZE]*_rand_from_seed(&rand_seed)*randomness[PARAM_FINAL_SIZE]; float final_size = param[PARAM_FINAL_SIZE]+param[PARAM_FINAL_SIZE]*_rand_from_seed(&rand_seed)*randomness[PARAM_FINAL_SIZE]; float size_mult=initial_size*(1.0-ptime) + final_size*ptime; //Size2 rectsize=size * size_mult; //rectsize=rectsize.floor(); //Rect2 r = Rect2(Vecto,rectsize); Matrix32 xform; if (p.rot) { xform.set_rotation(p.rot); xform.translate(-size*size_mult/2.0); xform.elements[2]+=p.pos; } else { xform.elements[2]=-size*size_mult/2.0; xform.elements[2]+=p.pos; } if (!local_space) { xform = invxform * xform; } xform.scale_basis(Size2(size_mult,size_mult)); VisualServer::get_singleton()->canvas_item_add_set_transform(ci,xform); if (texrid.is_valid()) { Rect2 src_rect; src_rect.size=size; if (total_frames>1) { int frame = Math::fast_ftoi(Math::floor(p.frame*total_frames)) % total_frames; src_rect.pos.x = size.x * (frame%h_frames); src_rect.pos.y = size.y * (frame/h_frames); } texture->draw_rect_region(ci,Rect2(Point2(),size),src_rect,color); //VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color); } else { VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2(),size),color); } } } break; } }
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 ); }
void Particles2D::_process_particles(float p_delta) { if (particles.size()==0 || lifetime==0) return; p_delta*=time_scale; float frame_time=p_delta; if (emit_timeout > 0) { time_to_live -= frame_time; if (time_to_live < 0) { emitting = false; _change_notify("config/emitting"); }; }; float next_time = time+frame_time; if (next_time > lifetime) next_time=Math::fmod(next_time,lifetime); Particle *pdata=&particles[0]; int particle_count=particles.size(); Matrix32 xform; if (!local_space) xform=get_global_transform(); active_count=0; DVector<Point2>::Read r; int emission_point_count=0; if (emission_points.size()) { emission_point_count=emission_points.size(); r=emission_points.read(); } int attractor_count=0; AttractorCache *attractor_ptr=NULL; if (attractors.size()) { if (attractors.size()!=attractor_cache.size()) { attractor_cache.resize(attractors.size()); } int idx=0; Matrix32 m; if (local_space) { m= get_global_transform().affine_inverse(); } for (Set<ParticleAttractor2D*>::Element *E=attractors.front();E;E=E->next()) { attractor_cache[idx].pos=m.xform( E->get()->get_global_pos() ); attractor_cache[idx].attractor=E->get(); idx++; } attractor_ptr=attractor_cache.ptr(); attractor_count=attractor_cache.size(); } for(int i=0;i<particle_count;i++) { Particle &p=pdata[i]; float restart_time = (i * lifetime / particle_count) * explosiveness; bool restart=false; if ( next_time < time ) { if (restart_time > time || restart_time < next_time ) restart=true; } else if (restart_time > time && restart_time < next_time ) { restart=true; } if (restart) { if (emitting) { p.pos=emissor_offset; if (emission_point_count) { Vector2 ep = r[Math::rand()%emission_point_count]; if (!local_space) { p.pos=xform.xform(p.pos+ep*extents); } else { p.pos+=ep*extents; } } else { if (!local_space) { p.pos=xform.xform(p.pos+Vector2(Math::random(-extents.x,extents.x),Math::random(-extents.y,extents.y))); } else { p.pos+=Vector2(Math::random(-extents.x,extents.x),Math::random(-extents.y,extents.y)); } } p.seed=Math::rand() % 12345678; uint32_t rand_seed=p.seed*(i+1); float angle = Math::deg2rad(param[PARAM_DIRECTION]+_rand_from_seed(&rand_seed)*param[PARAM_SPREAD]); p.velocity=Vector2( Math::sin(angle), Math::cos(angle) ); if (!local_space) { p.velocity = xform.basis_xform(p.velocity).normalized(); } p.velocity*=param[PARAM_LINEAR_VELOCITY]+param[PARAM_LINEAR_VELOCITY]*_rand_from_seed(&rand_seed)*randomness[PARAM_LINEAR_VELOCITY]; p.velocity+=initial_velocity; p.active=true; p.rot=Math::deg2rad(param[PARAM_INITIAL_ANGLE]+param[PARAM_INITIAL_ANGLE]*randomness[PARAM_INITIAL_ANGLE]*_rand_from_seed(&rand_seed)); active_count++; p.frame=Math::fmod(param[PARAM_ANIM_INITIAL_POS]+randomness[PARAM_ANIM_INITIAL_POS]*_rand_from_seed(&rand_seed),1.0); } else { p.active=false; } } else { if (!p.active) continue; uint32_t rand_seed=p.seed*(i+1); Vector2 force; //apply gravity float gravity_dir = Math::deg2rad( param[PARAM_GRAVITY_DIRECTION]+180*randomness[PARAM_GRAVITY_DIRECTION]*_rand_from_seed(&rand_seed)); force+=Vector2( Math::sin(gravity_dir), Math::cos(gravity_dir) ) * (param[PARAM_GRAVITY_STRENGTH]+param[PARAM_GRAVITY_STRENGTH]*randomness[PARAM_GRAVITY_STRENGTH]*_rand_from_seed(&rand_seed)); //apply radial Vector2 rvec = (p.pos - emissor_offset).normalized(); force+=rvec*(param[PARAM_RADIAL_ACCEL]+param[PARAM_RADIAL_ACCEL]*randomness[PARAM_RADIAL_ACCEL]*_rand_from_seed(&rand_seed)); //apply orbit float orbitvel = (param[PARAM_ORBIT_VELOCITY]+param[PARAM_ORBIT_VELOCITY]*randomness[PARAM_ORBIT_VELOCITY]*_rand_from_seed(&rand_seed)); if (orbitvel!=0) { Vector2 rel = p.pos - xform.elements[2]; Matrix32 rot(orbitvel*frame_time,Vector2()); p.pos = rot.xform(rel) + xform.elements[2]; } Vector2 tvec=rvec.tangent(); force+=tvec*(param[PARAM_TANGENTIAL_ACCEL]+param[PARAM_TANGENTIAL_ACCEL]*randomness[PARAM_TANGENTIAL_ACCEL]*_rand_from_seed(&rand_seed)); for(int j=0;j<attractor_count;j++) { Vector2 vec = (attractor_ptr[j].pos - p.pos); float vl = vec.length(); if (!attractor_ptr[j].attractor->enabled || vl==0 || vl > attractor_ptr[j].attractor->radius) continue; force+=vec*attractor_ptr[j].attractor->gravity; float fvl = p.velocity.length(); if (fvl && attractor_ptr[j].attractor->absorption) { Vector2 target = vec.normalized(); p.velocity = p.velocity.normalized().linear_interpolate(target,MIN(frame_time*attractor_ptr[j].attractor->absorption,1))*fvl; } if (attractor_ptr[j].attractor->disable_radius && vl < attractor_ptr[j].attractor->disable_radius) { p.active=false; } } p.velocity+=force*frame_time; if (param[PARAM_DAMPING]) { float dmp = param[PARAM_DAMPING]+param[PARAM_DAMPING]*randomness[PARAM_DAMPING]*_rand_from_seed(&rand_seed); float v = p.velocity.length(); v -= dmp * frame_time; if (v<=0) { p.velocity=Vector2(); } else { p.velocity=p.velocity.normalized() * v; } } p.pos+=p.velocity*frame_time; p.rot+=Math::lerp(param[PARAM_SPIN_VELOCITY],param[PARAM_SPIN_VELOCITY]*randomness[PARAM_SPIN_VELOCITY]*_rand_from_seed(&rand_seed),randomness[PARAM_SPIN_VELOCITY])*frame_time; float anim_spd=param[PARAM_ANIM_SPEED_SCALE]+param[PARAM_ANIM_SPEED_SCALE]*randomness[PARAM_ANIM_SPEED_SCALE]*_rand_from_seed(&rand_seed); p.frame=Math::fposmod(p.frame+(frame_time/lifetime)*anim_spd,1.0); active_count++; } } time=Math::fmod( time+frame_time, lifetime ); if (!emitting && active_count==0) { set_process(false); } update(); }