|
@@ -51,6 +51,7 @@ void CPUParticles2D::set_amount(int p_amount) {
|
|
|
ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles must be greater than 0.");
|
|
|
|
|
|
particles.resize(p_amount);
|
|
|
+ particles_prev.resize(p_amount);
|
|
|
{
|
|
|
PoolVector<Particle>::Write w = particles.write();
|
|
|
|
|
@@ -59,13 +60,19 @@ void CPUParticles2D::set_amount(int p_amount) {
|
|
|
memset(static_cast<void *>(&w[0]), 0, p_amount * sizeof(Particle));
|
|
|
// cast to prevent compiler warning .. note this relies on Particle not containing any complex types.
|
|
|
// an alternative is to use some zero method per item but the generated code will be far less efficient.
|
|
|
+
|
|
|
+ for (int i = 0; i < p_amount; i++) {
|
|
|
+ particles_prev[i].blank();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
particle_data.resize((8 + 4 + 1) * p_amount);
|
|
|
+ particle_data_prev.resize(particle_data.size());
|
|
|
// We must fill immediately to prevent garbage data and Nans
|
|
|
// being sent to the visual server with set_as_bulk_array,
|
|
|
// if this is sent before being regularly updated.
|
|
|
particle_data.fill(0);
|
|
|
+ particle_data_prev.fill(0);
|
|
|
|
|
|
VS::get_singleton()->multimesh_allocate(multimesh, p_amount, VS::MULTIMESH_TRANSFORM_2D, VS::MULTIMESH_COLOR_8BIT, VS::MULTIMESH_CUSTOM_DATA_FLOAT);
|
|
|
|
|
@@ -95,6 +102,18 @@ void CPUParticles2D::set_lifetime_randomness(float p_random) {
|
|
|
void CPUParticles2D::set_use_local_coordinates(bool p_enable) {
|
|
|
local_coords = p_enable;
|
|
|
set_notify_transform(!p_enable);
|
|
|
+
|
|
|
+ // Prevent sending item transforms when using global coords,
|
|
|
+ // and inform VisualServer to use identity mode.
|
|
|
+#ifdef GODOT_CPU_PARTICLES_2D_LEGACY_COMPATIBILITY
|
|
|
+ set_canvas_item_use_identity_transform((_interpolated) && (!p_enable));
|
|
|
+
|
|
|
+ // Always reset this, as it is unused when interpolation is on.
|
|
|
+ // (i.e. We do particles in global space, rather than pseudo globalspace.)
|
|
|
+ inv_emission_transform = Transform2D();
|
|
|
+#else
|
|
|
+ set_canvas_item_use_identity_transform(!p_enable);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
void CPUParticles2D::set_speed_scale(float p_scale) {
|
|
@@ -519,13 +538,24 @@ static float rand_from_seed(uint32_t &seed) {
|
|
|
return float(seed % uint32_t(65536)) / 65535.0;
|
|
|
}
|
|
|
|
|
|
-void CPUParticles2D::_update_internal() {
|
|
|
+void CPUParticles2D::_update_internal(bool p_on_physics_tick) {
|
|
|
if (particles.size() == 0 || !is_visible_in_tree()) {
|
|
|
_set_redraw(false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- float delta = get_process_delta_time();
|
|
|
+ // Change update mode?
|
|
|
+ _refresh_interpolation_state();
|
|
|
+
|
|
|
+ float delta = 0.0f;
|
|
|
+
|
|
|
+ // Is this update occurring on a physics tick (i.e. interpolated), or a frame tick?
|
|
|
+ if (p_on_physics_tick) {
|
|
|
+ delta = get_physics_process_delta_time();
|
|
|
+ } else {
|
|
|
+ delta = get_process_delta_time();
|
|
|
+ }
|
|
|
+
|
|
|
if (emitting) {
|
|
|
inactive_time = 0;
|
|
|
} else {
|
|
@@ -584,6 +614,117 @@ void CPUParticles2D::_update_internal() {
|
|
|
}
|
|
|
|
|
|
_update_particle_data_buffer();
|
|
|
+
|
|
|
+ // If we are interpolating, we send the data to the VisualServer
|
|
|
+ // right away on a physics tick instead of waiting until a render frame.
|
|
|
+ if (p_on_physics_tick && redraw) {
|
|
|
+ _update_render_thread();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CPUParticles2D::_particle_process(Particle &r_p, const Transform2D &p_emission_xform, float p_local_delta, float &r_tv) {
|
|
|
+ uint32_t alt_seed = r_p.seed;
|
|
|
+
|
|
|
+ r_p.time += p_local_delta;
|
|
|
+ r_p.custom[1] = r_p.time / lifetime;
|
|
|
+ r_tv = r_p.time / r_p.lifetime;
|
|
|
+
|
|
|
+ float tex_linear_velocity = 0.0;
|
|
|
+ if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
|
|
|
+ tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_orbit_velocity = 0.0;
|
|
|
+ if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
|
|
|
+ tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_angular_velocity = 0.0;
|
|
|
+ if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
|
|
|
+ tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_linear_accel = 0.0;
|
|
|
+ if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
|
|
|
+ tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_tangential_accel = 0.0;
|
|
|
+ if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
|
|
|
+ tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_radial_accel = 0.0;
|
|
|
+ if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
|
|
|
+ tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_damping = 0.0;
|
|
|
+ if (curve_parameters[PARAM_DAMPING].is_valid()) {
|
|
|
+ tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_angle = 0.0;
|
|
|
+ if (curve_parameters[PARAM_ANGLE].is_valid()) {
|
|
|
+ tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+ float tex_anim_speed = 0.0;
|
|
|
+ if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) {
|
|
|
+ tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ float tex_anim_offset = 0.0;
|
|
|
+ if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) {
|
|
|
+ tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(r_tv);
|
|
|
+ }
|
|
|
+
|
|
|
+ Vector2 force = gravity;
|
|
|
+ Vector2 pos = r_p.transform[2];
|
|
|
+
|
|
|
+ // Apply linear acceleration.
|
|
|
+ force += r_p.velocity.length() > 0.0 ? r_p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector2();
|
|
|
+
|
|
|
+ // Apply radial acceleration.
|
|
|
+ Vector2 org = p_emission_xform[2];
|
|
|
+ Vector2 diff = pos - org;
|
|
|
+ force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector2();
|
|
|
+
|
|
|
+ // Apply tangential acceleration.
|
|
|
+ Vector2 yx = Vector2(diff.y, diff.x);
|
|
|
+ force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
|
|
|
+
|
|
|
+ // Apply attractor forces.
|
|
|
+ r_p.velocity += force * p_local_delta;
|
|
|
+
|
|
|
+ // Orbit velocity.
|
|
|
+ float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
|
|
|
+ if (orbit_amount != 0.0) {
|
|
|
+ float ang = orbit_amount * p_local_delta * Math_PI * 2.0;
|
|
|
+ // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix,
|
|
|
+ // but we use -ang here to reproduce its behavior.
|
|
|
+ Transform2D rot = Transform2D(-ang, Vector2());
|
|
|
+ r_p.transform[2] -= diff;
|
|
|
+ r_p.transform[2] += rot.basis_xform(diff);
|
|
|
+ }
|
|
|
+ if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
|
|
|
+ r_p.velocity = r_p.velocity.normalized() * tex_linear_velocity;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parameters[PARAM_DAMPING] + tex_damping > 0.0) {
|
|
|
+ float v = r_p.velocity.length();
|
|
|
+ float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]);
|
|
|
+ v -= damp * p_local_delta;
|
|
|
+ if (v < 0.0) {
|
|
|
+ r_p.velocity = Vector2();
|
|
|
+ } else {
|
|
|
+ r_p.velocity = r_p.velocity.normalized() * v;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, r_p.angle_rand, randomness[PARAM_ANGLE]);
|
|
|
+ base_angle += r_p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
|
|
|
+ r_p.rotation = Math::deg2rad(base_angle); //angle
|
|
|
+ float animation_phase = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, r_p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + r_tv * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]);
|
|
|
+ r_p.custom[2] = animation_phase;
|
|
|
}
|
|
|
|
|
|
void CPUParticles2D::_particles_process(float p_delta) {
|
|
@@ -622,6 +763,12 @@ void CPUParticles2D::_particles_process(float p_delta) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ // For interpolation we need to keep a record of previous particles.
|
|
|
+ if (_interpolated) {
|
|
|
+ DEV_ASSERT((uint32_t)particles.size() == particles_prev.size());
|
|
|
+ p.copy_to(particles_prev[i]);
|
|
|
+ }
|
|
|
+
|
|
|
float local_delta = p_delta;
|
|
|
|
|
|
// The phase is a ratio between 0 (birth) and 1 (end of life) for each particle.
|
|
@@ -777,104 +924,7 @@ void CPUParticles2D::_particles_process(float p_delta) {
|
|
|
p.active = false;
|
|
|
tv = 1.0;
|
|
|
} else {
|
|
|
- uint32_t alt_seed = p.seed;
|
|
|
-
|
|
|
- p.time += local_delta;
|
|
|
- p.custom[1] = p.time / lifetime;
|
|
|
- tv = p.time / p.lifetime;
|
|
|
-
|
|
|
- float tex_linear_velocity = 0.0;
|
|
|
- if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
|
|
|
- tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_orbit_velocity = 0.0;
|
|
|
- if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
|
|
|
- tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_angular_velocity = 0.0;
|
|
|
- if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
|
|
|
- tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_linear_accel = 0.0;
|
|
|
- if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
|
|
|
- tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_tangential_accel = 0.0;
|
|
|
- if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
|
|
|
- tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_radial_accel = 0.0;
|
|
|
- if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
|
|
|
- tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_damping = 0.0;
|
|
|
- if (curve_parameters[PARAM_DAMPING].is_valid()) {
|
|
|
- tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_angle = 0.0;
|
|
|
- if (curve_parameters[PARAM_ANGLE].is_valid()) {
|
|
|
- tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv);
|
|
|
- }
|
|
|
- float tex_anim_speed = 0.0;
|
|
|
- if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) {
|
|
|
- tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- float tex_anim_offset = 0.0;
|
|
|
- if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) {
|
|
|
- tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv);
|
|
|
- }
|
|
|
-
|
|
|
- Vector2 force = gravity;
|
|
|
- Vector2 pos = p.transform[2];
|
|
|
-
|
|
|
- //apply linear acceleration
|
|
|
- force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector2();
|
|
|
- //apply radial acceleration
|
|
|
- Vector2 org = emission_xform[2];
|
|
|
- Vector2 diff = pos - org;
|
|
|
- force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector2();
|
|
|
- //apply tangential acceleration;
|
|
|
- Vector2 yx = Vector2(diff.y, diff.x);
|
|
|
- force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
|
|
|
- //apply attractor forces
|
|
|
- p.velocity += force * local_delta;
|
|
|
- //orbit velocity
|
|
|
- float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
|
|
|
- if (orbit_amount != 0.0) {
|
|
|
- float ang = orbit_amount * local_delta * Math_PI * 2.0;
|
|
|
- // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix,
|
|
|
- // but we use -ang here to reproduce its behavior.
|
|
|
- Transform2D rot = Transform2D(-ang, Vector2());
|
|
|
- p.transform[2] -= diff;
|
|
|
- p.transform[2] += rot.basis_xform(diff);
|
|
|
- }
|
|
|
- if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
|
|
|
- p.velocity = p.velocity.normalized() * tex_linear_velocity;
|
|
|
- }
|
|
|
-
|
|
|
- if (parameters[PARAM_DAMPING] + tex_damping > 0.0) {
|
|
|
- float v = p.velocity.length();
|
|
|
- float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]);
|
|
|
- v -= damp * local_delta;
|
|
|
- if (v < 0.0) {
|
|
|
- p.velocity = Vector2();
|
|
|
- } else {
|
|
|
- p.velocity = p.velocity.normalized() * v;
|
|
|
- }
|
|
|
- }
|
|
|
- float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
|
|
|
- base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
|
|
|
- p.rotation = Math::deg2rad(base_angle); //angle
|
|
|
- float animation_phase = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + tv * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]);
|
|
|
- p.custom[2] = animation_phase;
|
|
|
+ _particle_process(p, emission_xform, local_delta, tv);
|
|
|
}
|
|
|
//apply color
|
|
|
//apply hue rotation
|
|
@@ -937,6 +987,13 @@ void CPUParticles2D::_particles_process(float p_delta) {
|
|
|
p.transform.elements[1] *= base_scale;
|
|
|
|
|
|
p.transform[2] += p.velocity * local_delta;
|
|
|
+
|
|
|
+ // Teleport if starting a new particle, so
|
|
|
+ // we don't get a streak from the old position
|
|
|
+ // to this new start.
|
|
|
+ if (restart && _interpolated) {
|
|
|
+ p.copy_to(particles_prev[i]);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -953,6 +1010,15 @@ void CPUParticles2D::_update_particle_data_buffer() {
|
|
|
PoolVector<Particle>::Read r = particles.read();
|
|
|
float *ptr = w.ptr();
|
|
|
|
|
|
+ PoolVector<float>::Write w_prev;
|
|
|
+ float *ptr_prev = nullptr;
|
|
|
+
|
|
|
+ if (_interpolated) {
|
|
|
+ DEV_ASSERT(particle_data.size() == particle_data_prev.size());
|
|
|
+ w_prev = particle_data_prev.write();
|
|
|
+ ptr_prev = w_prev.ptr();
|
|
|
+ }
|
|
|
+
|
|
|
if (draw_order != DRAW_ORDER_INDEX) {
|
|
|
ow = particle_order.write();
|
|
|
order = ow.ptr();
|
|
@@ -967,63 +1033,94 @@ void CPUParticles2D::_update_particle_data_buffer() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- for (int i = 0; i < pc; i++) {
|
|
|
- int idx = order ? order[i] : i;
|
|
|
-
|
|
|
- Transform2D t = r[idx].transform;
|
|
|
-
|
|
|
- if (!local_coords) {
|
|
|
- t = inv_emission_transform * t;
|
|
|
+ if (_interpolated) {
|
|
|
+ for (int i = 0; i < pc; i++) {
|
|
|
+ int idx = order ? order[i] : i;
|
|
|
+ _fill_particle_data<false>(r[idx], ptr, r[idx].active);
|
|
|
+ ptr += 13;
|
|
|
+ _fill_particle_data<false>(particles_prev[idx], ptr_prev, r[idx].active);
|
|
|
+ ptr_prev += 13;
|
|
|
}
|
|
|
-
|
|
|
- if (r[idx].active) {
|
|
|
- ptr[0] = t.elements[0][0];
|
|
|
- ptr[1] = t.elements[1][0];
|
|
|
- ptr[2] = 0;
|
|
|
- ptr[3] = t.elements[2][0];
|
|
|
- ptr[4] = t.elements[0][1];
|
|
|
- ptr[5] = t.elements[1][1];
|
|
|
- ptr[6] = 0;
|
|
|
- ptr[7] = t.elements[2][1];
|
|
|
-
|
|
|
- Color c = r[idx].color;
|
|
|
- uint8_t *data8 = (uint8_t *)&ptr[8];
|
|
|
- data8[0] = CLAMP(c.r * 255.0, 0, 255);
|
|
|
- data8[1] = CLAMP(c.g * 255.0, 0, 255);
|
|
|
- data8[2] = CLAMP(c.b * 255.0, 0, 255);
|
|
|
- data8[3] = CLAMP(c.a * 255.0, 0, 255);
|
|
|
-
|
|
|
- ptr[9] = r[idx].custom[0];
|
|
|
- ptr[10] = r[idx].custom[1];
|
|
|
- ptr[11] = r[idx].custom[2];
|
|
|
- ptr[12] = r[idx].custom[3];
|
|
|
-
|
|
|
+ } else {
|
|
|
+#ifdef GODOT_CPU_PARTICLES_2D_LEGACY_COMPATIBILITY
|
|
|
+ if (!local_coords) {
|
|
|
+ inv_emission_transform = get_global_transform().affine_inverse();
|
|
|
+ for (int i = 0; i < pc; i++) {
|
|
|
+ int idx = order ? order[i] : i;
|
|
|
+ _fill_particle_data<true>(r[idx], ptr, r[idx].active);
|
|
|
+ ptr += 13;
|
|
|
+ }
|
|
|
} else {
|
|
|
- memset(ptr, 0, sizeof(float) * 13);
|
|
|
+ for (int i = 0; i < pc; i++) {
|
|
|
+ int idx = order ? order[i] : i;
|
|
|
+ _fill_particle_data<false>(r[idx], ptr, r[idx].active);
|
|
|
+ ptr += 13;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- ptr += 13;
|
|
|
+#else
|
|
|
+ for (int i = 0; i < pc; i++) {
|
|
|
+ int idx = order ? order[i] : i;
|
|
|
+ _fill_particle_data<false>(r[idx], ptr, r[idx].active);
|
|
|
+ ptr += 13;
|
|
|
+ }
|
|
|
+#endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
update_mutex.unlock();
|
|
|
}
|
|
|
|
|
|
+void CPUParticles2D::_refresh_interpolation_state() {
|
|
|
+ if (!is_inside_tree()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ bool interpolated = is_physics_interpolated_and_enabled();
|
|
|
+
|
|
|
+ if (_interpolated == interpolated) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool curr_redraw = redraw;
|
|
|
+
|
|
|
+ // Remove all connections.
|
|
|
+ // This isn't super efficient, but should only happen rarely.
|
|
|
+ _set_redraw(false);
|
|
|
+
|
|
|
+ _interpolated = interpolated;
|
|
|
+
|
|
|
+#ifdef GODOT_CPU_PARTICLES_2D_LEGACY_COMPATIBILITY
|
|
|
+ // Refresh local coords state, blank inv_emission_transform.
|
|
|
+ set_use_local_coordinates(local_coords);
|
|
|
+#endif
|
|
|
+
|
|
|
+ set_process_internal(!_interpolated);
|
|
|
+ set_physics_process_internal(_interpolated);
|
|
|
+
|
|
|
+ // Re-establish all connections.
|
|
|
+ _set_redraw(curr_redraw);
|
|
|
+}
|
|
|
+
|
|
|
void CPUParticles2D::_set_redraw(bool p_redraw) {
|
|
|
if (redraw == p_redraw) {
|
|
|
return;
|
|
|
}
|
|
|
redraw = p_redraw;
|
|
|
update_mutex.lock();
|
|
|
+ if (!_interpolated) {
|
|
|
+ if (redraw) {
|
|
|
+ VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
|
|
|
+ } else {
|
|
|
+ if (VS::get_singleton()->is_connected("frame_pre_draw", this, "_update_render_thread")) {
|
|
|
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (redraw) {
|
|
|
- VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
|
|
|
VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), true);
|
|
|
|
|
|
VS::get_singleton()->multimesh_set_visible_instances(multimesh, -1);
|
|
|
} else {
|
|
|
- if (VS::get_singleton()->is_connected("frame_pre_draw", this, "_update_render_thread")) {
|
|
|
- VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
|
|
|
- }
|
|
|
VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), false);
|
|
|
|
|
|
VS::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
|
|
@@ -1035,7 +1132,11 @@ void CPUParticles2D::_set_redraw(bool p_redraw) {
|
|
|
void CPUParticles2D::_update_render_thread() {
|
|
|
if (OS::get_singleton()->is_update_pending(true)) {
|
|
|
update_mutex.lock();
|
|
|
- VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data);
|
|
|
+ if (_interpolated) {
|
|
|
+ VS::get_singleton()->multimesh_set_as_bulk_array_interpolated(multimesh, particle_data, particle_data_prev);
|
|
|
+ } else {
|
|
|
+ VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data);
|
|
|
+ }
|
|
|
update_mutex.unlock();
|
|
|
}
|
|
|
}
|
|
@@ -1043,6 +1144,17 @@ void CPUParticles2D::_update_render_thread() {
|
|
|
void CPUParticles2D::_notification(int p_what) {
|
|
|
if (p_what == NOTIFICATION_ENTER_TREE) {
|
|
|
set_process_internal(emitting);
|
|
|
+
|
|
|
+ // For interpolated version to update the particles right away,
|
|
|
+ // we need a sequence of events.
|
|
|
+ // First ensure we are in _interpolated mode if the Node is set to interpolated.
|
|
|
+ _refresh_interpolation_state();
|
|
|
+
|
|
|
+ // Now, if we are interpolating, we want to force a single tick update.
|
|
|
+ // If we don't do this, it may be an entire tick before the first update happens.
|
|
|
+ if (_interpolated) {
|
|
|
+ _update_internal(true);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (p_what == NOTIFICATION_EXIT_TREE) {
|
|
@@ -1051,8 +1163,8 @@ void CPUParticles2D::_notification(int p_what) {
|
|
|
|
|
|
if (p_what == NOTIFICATION_DRAW) {
|
|
|
// first update before rendering to avoid one frame delay after emitting starts
|
|
|
- if (emitting && (time == 0)) {
|
|
|
- _update_internal();
|
|
|
+ if (emitting && (time == 0) && !_interpolated) {
|
|
|
+ _update_internal(false);
|
|
|
}
|
|
|
|
|
|
if (!redraw) {
|
|
@@ -1073,39 +1185,10 @@ void CPUParticles2D::_notification(int p_what) {
|
|
|
}
|
|
|
|
|
|
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
|
|
|
- _update_internal();
|
|
|
+ _update_internal(false);
|
|
|
}
|
|
|
-
|
|
|
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
|
|
|
- inv_emission_transform = get_global_transform().affine_inverse();
|
|
|
-
|
|
|
- if (!local_coords) {
|
|
|
- int pc = particles.size();
|
|
|
-
|
|
|
- PoolVector<float>::Write w = particle_data.write();
|
|
|
- PoolVector<Particle>::Read r = particles.read();
|
|
|
- float *ptr = w.ptr();
|
|
|
-
|
|
|
- for (int i = 0; i < pc; i++) {
|
|
|
- Transform2D t = inv_emission_transform * r[i].transform;
|
|
|
-
|
|
|
- if (r[i].active) {
|
|
|
- ptr[0] = t.elements[0][0];
|
|
|
- ptr[1] = t.elements[1][0];
|
|
|
- ptr[2] = 0;
|
|
|
- ptr[3] = t.elements[2][0];
|
|
|
- ptr[4] = t.elements[0][1];
|
|
|
- ptr[5] = t.elements[1][1];
|
|
|
- ptr[6] = 0;
|
|
|
- ptr[7] = t.elements[2][1];
|
|
|
-
|
|
|
- } else {
|
|
|
- memset(ptr, 0, sizeof(float) * 8);
|
|
|
- }
|
|
|
-
|
|
|
- ptr += 13;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
|
|
|
+ _update_internal(true);
|
|
|
}
|
|
|
}
|
|
|
|