Implemented tools around particles seed randomization.
The purpose of this code is to expose the necessary functions for users and engine devs to develop tooling for properly timing and seeking inside particles. Co-authored-by: Hugo Locurcio <hugo.locurcio@hugo.pro> Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Co-authored-by: Tomasz Chabora <kobewi4e@gmail.com> Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
/**************************************************************************/
|
||||
|
||||
#include "cpu_particles_3d.h"
|
||||
#include "cpu_particles_3d.compat.inc"
|
||||
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/3d/gpu_particles_3d.h"
|
||||
@@ -231,7 +232,7 @@ PackedStringArray CPUParticles3D::get_configuration_warnings() const {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void CPUParticles3D::restart() {
|
||||
void CPUParticles3D::restart(bool p_keep_seed) {
|
||||
time = 0;
|
||||
frame_remainder = 0;
|
||||
cycle = 0;
|
||||
@@ -245,6 +246,9 @@ void CPUParticles3D::restart() {
|
||||
w[i].active = false;
|
||||
}
|
||||
}
|
||||
if (!p_keep_seed && !use_fixed_seed) {
|
||||
seed = Math::rand();
|
||||
}
|
||||
|
||||
set_emitting(true);
|
||||
}
|
||||
@@ -549,6 +553,30 @@ AABB CPUParticles3D::capture_aabb() const {
|
||||
return RS::get_singleton()->multimesh_get_aabb(multimesh);
|
||||
}
|
||||
|
||||
void CPUParticles3D::set_use_fixed_seed(bool p_use_fixed_seed) {
|
||||
if (p_use_fixed_seed == use_fixed_seed) {
|
||||
return;
|
||||
}
|
||||
use_fixed_seed = p_use_fixed_seed;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool CPUParticles3D::get_use_fixed_seed() const {
|
||||
return use_fixed_seed;
|
||||
}
|
||||
|
||||
void CPUParticles3D::set_seed(uint32_t p_seed) {
|
||||
seed = p_seed;
|
||||
}
|
||||
|
||||
uint32_t CPUParticles3D::get_seed() const {
|
||||
return seed;
|
||||
}
|
||||
|
||||
void CPUParticles3D::request_particles_process(real_t p_requested_process_time) {
|
||||
_requested_process_time = p_requested_process_time;
|
||||
}
|
||||
|
||||
void CPUParticles3D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "emitting") {
|
||||
p_property.hint = one_shot ? PROPERTY_HINT_ONESHOT : PROPERTY_HINT_NONE;
|
||||
@@ -581,6 +609,10 @@ void CPUParticles3D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name.begins_with("scale_curve_") && !split_scale) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
|
||||
if (p_property.name == "seed" && !use_fixed_seed) {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t idhash(uint32_t x) {
|
||||
@@ -626,25 +658,27 @@ void CPUParticles3D::_update_internal() {
|
||||
|
||||
bool processed = false;
|
||||
|
||||
if (time == 0 && pre_process_time > 0.0) {
|
||||
double frame_time;
|
||||
if (fixed_fps > 0) {
|
||||
frame_time = 1.0 / fixed_fps;
|
||||
} else {
|
||||
frame_time = 1.0 / 30.0;
|
||||
}
|
||||
|
||||
double todo = pre_process_time;
|
||||
|
||||
while (todo >= 0) {
|
||||
_particles_process(frame_time);
|
||||
processed = true;
|
||||
todo -= frame_time;
|
||||
}
|
||||
double frame_time;
|
||||
if (fixed_fps > 0) {
|
||||
frame_time = 1.0 / fixed_fps;
|
||||
} else {
|
||||
frame_time = 1.0 / 30.0;
|
||||
}
|
||||
double todo = _requested_process_time;
|
||||
_requested_process_time = 0.;
|
||||
if (time == 0 && pre_process_time > 0.0) {
|
||||
todo += pre_process_time;
|
||||
}
|
||||
real_t tmp_speed = speed_scale;
|
||||
speed_scale = 1.0;
|
||||
while (todo > 0) {
|
||||
_particles_process(frame_time);
|
||||
todo -= frame_time;
|
||||
}
|
||||
speed_scale = tmp_speed;
|
||||
todo = 0.0;
|
||||
|
||||
if (fixed_fps > 0) {
|
||||
double frame_time = 1.0 / fixed_fps;
|
||||
double decr = frame_time;
|
||||
|
||||
double ldelta = delta;
|
||||
@@ -653,7 +687,7 @@ void CPUParticles3D::_update_internal() {
|
||||
} else if (ldelta <= 0.0) { //unlikely but..
|
||||
ldelta = 0.001;
|
||||
}
|
||||
double todo = frame_remainder + ldelta;
|
||||
todo = frame_remainder + ldelta;
|
||||
|
||||
while (todo >= frame_time) {
|
||||
_particles_process(frame_time);
|
||||
@@ -717,13 +751,13 @@ void CPUParticles3D::_particles_process(double p_delta) {
|
||||
double restart_phase = double(i) / double(pcount);
|
||||
|
||||
if (randomness_ratio > 0.0) {
|
||||
uint32_t seed = cycle;
|
||||
uint32_t _seed = cycle;
|
||||
if (restart_phase >= system_phase) {
|
||||
seed -= uint32_t(1);
|
||||
_seed -= uint32_t(1);
|
||||
}
|
||||
seed *= uint32_t(pcount);
|
||||
seed += uint32_t(i);
|
||||
double random = double(idhash(seed) % uint32_t(65536)) / 65536.0;
|
||||
_seed *= uint32_t(pcount);
|
||||
_seed += uint32_t(i);
|
||||
double random = double(idhash(_seed) % uint32_t(65536)) / 65536.0;
|
||||
restart_phase += randomness_ratio * random * 1.0 / double(pcount);
|
||||
}
|
||||
|
||||
@@ -784,27 +818,27 @@ void CPUParticles3D::_particles_process(double p_delta) {
|
||||
tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv);
|
||||
}
|
||||
|
||||
p.seed = Math::rand();
|
||||
|
||||
p.angle_rand = Math::randf();
|
||||
p.scale_rand = Math::randf();
|
||||
p.hue_rot_rand = Math::randf();
|
||||
p.anim_offset_rand = Math::randf();
|
||||
p.seed = seed;
|
||||
uint32_t _seed = seed + uint32_t(1) + i + cycle;
|
||||
p.angle_rand = rand_from_seed(_seed);
|
||||
p.scale_rand = rand_from_seed(_seed);
|
||||
p.hue_rot_rand = rand_from_seed(_seed);
|
||||
p.anim_offset_rand = rand_from_seed(_seed);
|
||||
|
||||
if (color_initial_ramp.is_valid()) {
|
||||
p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf());
|
||||
p.start_color_rand = color_initial_ramp->get_color_at_offset(rand_from_seed(_seed));
|
||||
} else {
|
||||
p.start_color_rand = Color(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
|
||||
real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg_to_rad((Math::randf() * 2.0 - 1.0) * spread);
|
||||
real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg_to_rad((rand_from_seed(_seed) * 2.0 - 1.0) * spread);
|
||||
Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0);
|
||||
p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf());
|
||||
p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rand_from_seed(_seed));
|
||||
} else {
|
||||
//initiate velocity spread in 3D
|
||||
real_t angle1_rad = Math::deg_to_rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * spread);
|
||||
real_t angle2_rad = Math::deg_to_rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread);
|
||||
real_t angle1_rad = Math::deg_to_rad((rand_from_seed(_seed) * (real_t)2.0 - (real_t)1.0) * spread);
|
||||
real_t angle2_rad = Math::deg_to_rad((rand_from_seed(_seed) * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread);
|
||||
|
||||
Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad));
|
||||
Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad));
|
||||
@@ -824,14 +858,14 @@ void CPUParticles3D::_particles_process(double p_delta) {
|
||||
binormal.normalize();
|
||||
Vector3 normal = binormal.cross(direction_nrm);
|
||||
spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;
|
||||
p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf());
|
||||
p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rand_from_seed(_seed));
|
||||
}
|
||||
|
||||
real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
|
||||
p.custom[0] = Math::deg_to_rad(base_angle); //angle
|
||||
p.custom[1] = 0.0; //phase
|
||||
p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand); //animation offset (0-1)
|
||||
p.custom[3] = (1.0 - Math::randf() * lifetime_randomness);
|
||||
p.custom[3] = (1.0 - rand_from_seed(_seed) * lifetime_randomness);
|
||||
p.transform = Transform3D();
|
||||
p.time = 0;
|
||||
p.lifetime = lifetime * p.custom[3];
|
||||
@@ -842,20 +876,20 @@ void CPUParticles3D::_particles_process(double p_delta) {
|
||||
//do none
|
||||
} break;
|
||||
case EMISSION_SHAPE_SPHERE: {
|
||||
real_t s = 2.0 * Math::randf() - 1.0;
|
||||
real_t t = Math_TAU * Math::randf();
|
||||
real_t x = Math::randf();
|
||||
real_t s = 2.0 * rand_from_seed(_seed) - 1.0;
|
||||
real_t t = Math_TAU * rand_from_seed(_seed);
|
||||
real_t x = rand_from_seed(_seed);
|
||||
real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s);
|
||||
p.transform.origin = Vector3(0, 0, 0).lerp(Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s), x);
|
||||
} break;
|
||||
case EMISSION_SHAPE_SPHERE_SURFACE: {
|
||||
real_t s = 2.0 * Math::randf() - 1.0;
|
||||
real_t t = Math_TAU * Math::randf();
|
||||
real_t s = 2.0 * rand_from_seed(_seed) - 1.0;
|
||||
real_t t = Math_TAU * rand_from_seed(_seed);
|
||||
real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s);
|
||||
p.transform.origin = Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s);
|
||||
} break;
|
||||
case EMISSION_SHAPE_BOX: {
|
||||
p.transform.origin = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * emission_box_extents;
|
||||
p.transform.origin = Vector3(rand_from_seed(_seed) * 2.0 - 1.0, rand_from_seed(_seed) * 2.0 - 1.0, rand_from_seed(_seed) * 2.0 - 1.0) * emission_box_extents;
|
||||
} break;
|
||||
case EMISSION_SHAPE_POINTS:
|
||||
case EMISSION_SHAPE_DIRECTED_POINTS: {
|
||||
@@ -899,11 +933,11 @@ void CPUParticles3D::_particles_process(double p_delta) {
|
||||
case EMISSION_SHAPE_RING: {
|
||||
real_t radius_clamped = MAX(0.001, emission_ring_radius);
|
||||
real_t top_radius = MAX(radius_clamped - Math::tan(Math::deg_to_rad(90.0 - emission_ring_cone_angle)) * emission_ring_height, 0.0);
|
||||
real_t y_pos = Math::randf();
|
||||
real_t y_pos = rand_from_seed(_seed);
|
||||
real_t skew = MAX(MIN(radius_clamped, top_radius) / MAX(radius_clamped, top_radius), 0.5);
|
||||
y_pos = radius_clamped < top_radius ? Math::pow(y_pos, skew) : 1.0 - Math::pow(y_pos, skew);
|
||||
real_t ring_random_angle = Math::randf() * Math_TAU;
|
||||
real_t ring_random_radius = Math::sqrt(Math::randf() * (radius_clamped * radius_clamped - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius);
|
||||
real_t ring_random_angle = rand_from_seed(_seed) * Math_TAU;
|
||||
real_t ring_random_radius = Math::sqrt(rand_from_seed(_seed) * (radius_clamped * radius_clamped - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius);
|
||||
ring_random_radius = Math::lerp(ring_random_radius, ring_random_radius * (top_radius / radius_clamped), y_pos);
|
||||
Vector3 axis = emission_ring_axis == Vector3(0.0, 0.0, 0.0) ? Vector3(0.0, 0.0, 1.0) : emission_ring_axis.normalized();
|
||||
Vector3 ortho_axis;
|
||||
@@ -1489,7 +1523,14 @@ void CPUParticles3D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CPUParticles3D::set_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_mesh"), &CPUParticles3D::get_mesh);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart);
|
||||
ClassDB::bind_method(D_METHOD("set_use_fixed_seed", "use_fixed_seed"), &CPUParticles3D::set_use_fixed_seed);
|
||||
ClassDB::bind_method(D_METHOD("get_use_fixed_seed"), &CPUParticles3D::get_use_fixed_seed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_seed", "seed"), &CPUParticles3D::set_seed);
|
||||
ClassDB::bind_method(D_METHOD("get_seed"), &CPUParticles3D::get_seed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("restart", "keep_seed"), &CPUParticles3D::restart, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("request_particles_process", "process_time"), &CPUParticles3D::request_particles_process);
|
||||
ClassDB::bind_method(D_METHOD("capture_aabb"), &CPUParticles3D::capture_aabb);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting");
|
||||
@@ -1498,9 +1539,12 @@ void CPUParticles3D::_bind_methods() {
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,10.0,0.01,or_greater,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_fixed_seed"), "set_use_fixed_seed", "get_use_fixed_seed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed", PROPERTY_HINT_RANGE, "0," + itos(UINT32_MAX) + ",1"), "set_seed", "get_seed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
|
||||
|
||||
Reference in New Issue
Block a user