Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cpuparticles randomness regression #101872

Merged
merged 1 commit into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions scene/2d/cpu_particles_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "cpu_particles_2d.h"
#include "cpu_particles_2d.compat.inc"

#include "core/math/random_number_generator.h"
#include "scene/2d/gpu_particles_2d.h"
#include "scene/resources/atlas_texture.h"
#include "scene/resources/canvas_item_material.h"
Expand Down Expand Up @@ -769,30 +770,30 @@ void CPUParticles2D::_particles_process(double p_delta) {
}

p.seed = seed + uint32_t(i) + i + cycle;
uint32_t _seed = p.seed;
rng->set_seed(p.seed);

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);
p.angle_rand = rng->randf();
p.scale_rand = rng->randf();
p.hue_rot_rand = rng->randf();
p.anim_offset_rand = rng->randf();

if (color_initial_ramp.is_valid()) {
p.start_color_rand = color_initial_ramp->get_color_at_offset(rand_from_seed(_seed));
p.start_color_rand = color_initial_ramp->get_color_at_offset(rng->randf());
} else {
p.start_color_rand = Color(1, 1, 1, 1);
}

real_t angle1_rad = direction.angle() + Math::deg_to_rad((rand_from_seed(_seed) * 2.0 - 1.0) * spread);
real_t angle1_rad = direction.angle() + Math::deg_to_rad((rng->randf() * 2.0 - 1.0) * spread);
Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad));
p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)rand_from_seed(_seed));
p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rng->randf());

real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
p.rotation = Math::deg_to_rad(base_angle);

p.custom[0] = 0.0; // unused
p.custom[1] = 0.0; // phase [0..1]
p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand);
p.custom[3] = (1.0 - rand_from_seed(_seed) * lifetime_randomness);
p.custom[3] = (1.0 - rng->randf() * lifetime_randomness);
p.transform = Transform2D();
p.time = 0;
p.lifetime = lifetime * p.custom[3];
Expand All @@ -803,17 +804,17 @@ void CPUParticles2D::_particles_process(double p_delta) {
//do none
} break;
case EMISSION_SHAPE_SPHERE: {
real_t t = Math_TAU * rand_from_seed(_seed);
real_t radius = emission_sphere_radius * rand_from_seed(_seed);
real_t t = Math_TAU * rng->randf();
real_t radius = emission_sphere_radius * rng->randf();
p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius;
} break;
case EMISSION_SHAPE_SPHERE_SURFACE: {
real_t s = rand_from_seed(_seed), t = Math_TAU * rand_from_seed(_seed);
real_t s = rng->randf(), t = Math_TAU * rng->randf();
real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s);
p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius;
} break;
case EMISSION_SHAPE_RECTANGLE: {
p.transform[2] = Vector2(rand_from_seed(_seed) * 2.0 - 1.0, rand_from_seed(_seed) * 2.0 - 1.0) * emission_rect_extents;
p.transform[2] = Vector2(rng->randf() * 2.0 - 1.0, rng->randf() * 2.0 - 1.0) * emission_rect_extents;
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
Expand Down Expand Up @@ -1523,6 +1524,8 @@ CPUParticles2D::CPUParticles2D() {
set_amount(8);
set_use_local_coordinates(false);

rng.instantiate();

set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
set_param_min(PARAM_ANGULAR_VELOCITY, 0);
set_param_min(PARAM_ORBIT_VELOCITY, 0);
Expand Down
4 changes: 4 additions & 0 deletions scene/2d/cpu_particles_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

#include "scene/2d/node_2d.h"

class RandomNumberGenerator;

class CPUParticles2D : public Node2D {
private:
GDCLASS(CPUParticles2D, Node2D);
Expand Down Expand Up @@ -179,6 +181,8 @@ class CPUParticles2D : public Node2D {

Vector2 gravity = Vector2(0, 980);

Ref<RandomNumberGenerator> rng;

void _update_internal();
void _particles_process(double p_delta);
void _update_particle_data_buffer();
Expand Down
47 changes: 25 additions & 22 deletions scene/3d/cpu_particles_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "cpu_particles_3d.h"
#include "cpu_particles_3d.compat.inc"

#include "core/math/random_number_generator.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/main/viewport.h"
Expand Down Expand Up @@ -818,27 +819,27 @@ void CPUParticles3D::_particles_process(double p_delta) {
tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv);
}

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);
p.seed = seed + uint32_t(1) + i + cycle;
rng->set_seed(p.seed);
p.angle_rand = rng->randf();
p.scale_rand = rng->randf();
p.hue_rot_rand = rng->randf();
p.anim_offset_rand = rng->randf();

if (color_initial_ramp.is_valid()) {
p.start_color_rand = color_initial_ramp->get_color_at_offset(rand_from_seed(_seed));
p.start_color_rand = color_initial_ramp->get_color_at_offset(rng->randf());
} 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((rand_from_seed(_seed) * 2.0 - 1.0) * spread);
real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg_to_rad((rng->randf() * 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], rand_from_seed(_seed));
p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rng->randf());
} else {
//initiate velocity spread in 3D
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);
real_t angle1_rad = Math::deg_to_rad((rng->randf() * (real_t)2.0 - (real_t)1.0) * spread);
real_t angle2_rad = Math::deg_to_rad((rng->randf() * (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));
Expand All @@ -858,14 +859,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], rand_from_seed(_seed));
p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rng->randf());
}

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 - rand_from_seed(_seed) * lifetime_randomness);
p.custom[3] = (1.0 - rng->randf() * lifetime_randomness);
p.transform = Transform3D();
p.time = 0;
p.lifetime = lifetime * p.custom[3];
Expand All @@ -876,20 +877,20 @@ void CPUParticles3D::_particles_process(double p_delta) {
//do none
} break;
case EMISSION_SHAPE_SPHERE: {
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 s = 2.0 * rng->randf() - 1.0;
real_t t = Math_TAU * rng->randf();
real_t x = rng->randf();
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 * rand_from_seed(_seed) - 1.0;
real_t t = Math_TAU * rand_from_seed(_seed);
real_t s = 2.0 * rng->randf() - 1.0;
real_t t = Math_TAU * rng->randf();
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(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;
p.transform.origin = Vector3(rng->randf() * 2.0 - 1.0, rng->randf() * 2.0 - 1.0, rng->randf() * 2.0 - 1.0) * emission_box_extents;
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
Expand Down Expand Up @@ -933,11 +934,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 = rand_from_seed(_seed);
real_t y_pos = rng->randf();
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 = 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);
real_t ring_random_angle = rng->randf() * Math_TAU;
real_t ring_random_radius = Math::sqrt(rng->randf() * (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;
Expand Down Expand Up @@ -1764,6 +1765,8 @@ CPUParticles3D::CPUParticles3D() {
set_emitting(true);
set_amount(8);

rng.instantiate();

set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
set_param_min(PARAM_ANGULAR_VELOCITY, 0);
set_param_min(PARAM_ORBIT_VELOCITY, 0);
Expand Down
4 changes: 4 additions & 0 deletions scene/3d/cpu_particles_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

#include "scene/3d/visual_instance_3d.h"

class RandomNumberGenerator;

class CPUParticles3D : public GeometryInstance3D {
private:
GDCLASS(CPUParticles3D, GeometryInstance3D);
Expand Down Expand Up @@ -190,6 +192,8 @@ class CPUParticles3D : public GeometryInstance3D {

Vector3 gravity = Vector3(0, -9.8, 0);

Ref<RandomNumberGenerator> rng;

void _update_internal();
void _particles_process(double p_delta);
void _update_particle_data_buffer();
Expand Down