Merge pull request #116620 from QbieShay/qbe/rework-align-rotation

Improve options for orienting particles in space
This commit is contained in:
Rémi Verschelde
2026-03-23 22:34:16 +01:00
19 changed files with 673 additions and 63 deletions
+10
View File
@@ -165,6 +165,13 @@
The amount of time the particle's trail should represent (in seconds). Only effective if [member trail_enabled] is [code]true[/code].
</member>
<member name="transform_align" type="int" setter="set_transform_align" getter="get_transform_align" enum="GPUParticles3D.TransformAlign" default="0">
The alignment of particles. Use this for billboarding and aligning to velocity.
</member>
<member name="transform_align_axis" type="int" setter="set_transform_align_axis" getter="get_transform_align_axis" enum="RenderingServer.ParticlesTransformAlignAxis">
When using transform align local billboard, which axis to use for the billboarding. Supports only X or Y.
</member>
<member name="transform_align_channel_filter" type="int" setter="set_transform_align_channel_filter" getter="get_transform_align_channel_filter" enum="RenderingServer.ParticlesTransformAlignCustomSrc">
In the case of billboarded particles, which custom channel to read from to calculate their angle.
</member>
<member name="use_fixed_seed" type="bool" setter="set_use_fixed_seed" getter="get_use_fixed_seed" default="false">
If [code]true[/code], particles will use the same seed for every simulation using the seed defined in [member seed]. This is useful for situations where the visual outcome should be consistent across replays, for example when using Movie Maker mode.
@@ -227,5 +234,8 @@
<constant name="TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY" value="3" enum="TransformAlign">
Align each particle's Z axis to face the camera and Y axis to the velocity vector.
</constant>
<constant name="TRANSFORM_ALIGN_LOCAL_BILLBOARD" value="4" enum="TransformAlign">
Align each particle's Z axis to face the camera, while preserving a given axis (X or Y).
</constant>
</constants>
</class>
+12
View File
@@ -321,6 +321,15 @@
<member name="rotation_3d_min" type="Vector3" setter="set_rotation_3d_min" getter="get_rotation_3d_min">
The minimum 3D orientation, in degrees. Works only in 3D and if [member use_rotation_3d] is enabled.
</member>
<member name="rotation_velocity_3d_curve" type="Texture2D" setter="set_rotation_velocity_3d_curve" getter="get_rotation_velocity_3d_curve">
Rotation velocity curve over lifetime, per-axis. Enable [member use_rotation_velocity_3d] to use this.
</member>
<member name="rotation_velocity_3d_max" type="Vector3" setter="set_rotation_velocity_3d_max" getter="get_rotation_velocity_3d_max">
Maximum 3D rotation velocity on the particle's local axis. Enable [member use_rotation_velocity_3d] to use this.
</member>
<member name="rotation_velocity_3d_min" type="Vector3" setter="set_rotation_velocity_3d_min" getter="get_rotation_velocity_3d_min">
Minimum 3D rotation velocity on the particle's local axis. Enable [member use_rotation_velocity_3d] to use this.
</member>
<member name="scale_3d_max" type="Vector3" setter="set_scale_3d_max" getter="get_scale_3d_max">
The maximum value of the random scale vector for each particle.
Works only if [member use_scale_3d] is enabled.
@@ -424,6 +433,9 @@
<member name="use_rotation_3d" type="bool" setter="set_use_rotation_3d" getter="is_using_rotation_3d" default="false">
Enable the usage of [member rotation_3d_min] and [member rotation_3d_max].
</member>
<member name="use_rotation_velocity_3d" type="bool" setter="set_using_rotation_velocity_3d" getter="is_using_rotation_velocity_3d" default="false">
Enable 3D rotation velocity.
</member>
<member name="use_scale_3d" type="bool" setter="set_use_scale_3d" getter="is_using_scale_3d" default="false">
Enable the usage of [member scale_3d_min] and [member scale_3d_max].
</member>
+40
View File
@@ -3326,6 +3326,22 @@
Sets the transform alignment for the particle system. Equivalent to [member GPUParticles3D.transform_align].
</description>
</method>
<method name="particles_set_transform_align_axis">
<return type="void" />
<param index="0" name="particles" type="RID" />
<param index="1" name="p_rotation_axis" type="int" enum="RenderingServer.ParticlesTransformAlignAxis" />
<description>
Sets which axis to use for transform alignment.
</description>
</method>
<method name="particles_set_transform_align_channel_filter">
<return type="void" />
<param index="0" name="particles" type="RID" />
<param index="1" name="channel_filter" type="int" enum="RenderingServer.ParticlesTransformAlignCustomSrc" />
<description>
When using Z-Billboarding, which CUSTOM channel to read from.
</description>
</method>
<method name="particles_set_use_local_coordinates">
<return type="void" />
<param index="0" name="particles" type="RID" />
@@ -5183,6 +5199,30 @@
<constant name="PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY" value="3" enum="ParticlesTransformAlign">
Align each particle's Z axis to face the camera and Y axis to the velocity vector.
</constant>
<constant name="PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD" value="4" enum="ParticlesTransformAlign">
Billboard each particles around a local axis.
</constant>
<constant name="PARTICLES_ALIGN_CHANNEL_FILTER_DISABLED" value="0" enum="ParticlesTransformAlignCustomSrc">
Do not read from CUSTOM when performing billboarding.
</constant>
<constant name="PARTICLES_ALIGN_CHANNEL_FILTER_X" value="1" enum="ParticlesTransformAlignCustomSrc">
Read from [code]CUSTOM.x[/code] when performing billboarding and use it as an angle, in radians.
</constant>
<constant name="PARTICLES_ALIGN_CHANNEL_FILTER_Y" value="2" enum="ParticlesTransformAlignCustomSrc">
Read from [code]CUSTOM.y[/code] when performing billboarding and use it as an angle, in radians.
</constant>
<constant name="PARTICLES_ALIGN_CHANNEL_FILTER_Z" value="3" enum="ParticlesTransformAlignCustomSrc">
Read from [code]CUSTOM.z[/code] when performing billboarding and use it as an angle, in radians.
</constant>
<constant name="PARTICLES_ALIGN_CHANNEL_FILTER_W" value="4" enum="ParticlesTransformAlignCustomSrc">
Read from [code]CUSTOM.w[/code] when performing billboarding and use it as an angle, in radians.
</constant>
<constant name="PARTICLES_ALIGN_AXIS_X" value="0" enum="ParticlesTransformAlignAxis">
Use the X axis for local billboarding.
</constant>
<constant name="PARTICLES_ALIGN_AXIS_Y" value="1" enum="ParticlesTransformAlignAxis">
Use the Y axis for local billboarding.
</constant>
<constant name="PARTICLES_EMIT_FLAG_POSITION" value="1">
Particle starts at the specified position.
</constant>
+84 -13
View File
@@ -37,10 +37,24 @@ uniform highp uint align_mode;
uniform highp mat4 inv_emission_transform;
#define TRANSFORM_ALIGN_DISABLED uint(0)
#define TRANSFORM_ALIGN_Z_BILLBOARD uint(1)
#define TRANSFORM_ALIGN_Y_TO_VELOCITY uint(2)
#define TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY uint(3)
uniform uint align_channel_filter;
uniform uint align_axis;
#define ALIGN_DISABLED uint(0)
#define ALIGN_BILLBOARD uint(1)
#define ALIGN_Y_TO_VELOCITY uint(2)
#define ALIGN_Z_BILLBOARD_Y_TO_VELOCITY uint(3)
#define ALIGN_LOCAL_BILLBOARD uint(4)
#define CHANNEL_FILTER_NONE uint(0)
#define CHANNEL_FILTER_X uint(1)
#define CHANNEL_FILTER_Y uint(2)
#define CHANNEL_FILTER_Z uint(3)
#define CHANNEL_FILTER_W uint(4)
#define ALIGN_AXIS_X uint(0)
#define ALIGN_AXIS_Y uint(1)
#define ALIGN_AXIS_Z uint(2)
#define PARTICLE_FLAG_ACTIVE uint(1)
@@ -57,15 +71,46 @@ void main() {
txform = transpose(mat4(xform_1, xform_2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
#endif
if (align_mode == TRANSFORM_ALIGN_DISABLED) {
if (align_mode == ALIGN_DISABLED) {
// nothing
} else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD) {
mat3 local = mat3(normalize(cross(align_up, sort_direction)), align_up, sort_direction);
local = local * mat3(txform);
txform[0].xyz = local[0];
txform[1].xyz = local[1];
txform[2].xyz = local[2];
} else if (align_mode == TRANSFORM_ALIGN_Y_TO_VELOCITY) {
} else if (align_mode == ALIGN_BILLBOARD) {
float angle = 0.;
if (align_channel_filter == CHANNEL_FILTER_NONE) {
mat3 local = mat3(normalize(cross(align_up, sort_direction)), align_up, sort_direction);
local = local * mat3(txform);
txform[0].xyz = local[0];
txform[1].xyz = local[1];
txform[2].xyz = local[2];
} else {
if (align_channel_filter == CHANNEL_FILTER_X) {
angle = custom.x;
} else if (align_channel_filter == CHANNEL_FILTER_Y) {
angle = custom.y;
} else if (align_channel_filter == CHANNEL_FILTER_Z) {
angle = custom.z;
} else if (align_channel_filter == CHANNEL_FILTER_W) {
angle = custom.w;
}
vec3 axis = normalize(sort_direction);
float s = sin(angle);
float c = cos(angle);
float oc = 1.0 - c;
mat3 rotated = mat3(
oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c);
vec3 new_up = rotated * align_up;
mat3 local = mat3(normalize(cross(new_up, sort_direction)), new_up, sort_direction);
local = local * mat3(txform);
txform[0].xyz = local[0];
txform[1].xyz = local[1];
txform[2].xyz = local[2];
}
} else if (align_mode == ALIGN_Y_TO_VELOCITY) {
vec3 v = velocity_flags.xyz;
float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0;
if (length(v) > 0.0) {
@@ -78,7 +123,7 @@ void main() {
txform[2].xyz = vec3(0.0, 0.0, 1.0) * s;
txform[0].xyz *= s;
txform[1].xyz *= s;
} else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
} else if (align_mode == ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
vec3 sv = velocity_flags.xyz - sort_direction * dot(sort_direction, velocity_flags.xyz); //screen velocity
if (length(sv) == 0.0) {
@@ -90,6 +135,32 @@ void main() {
txform[0].xyz = normalize(cross(sv, sort_direction)) * length(txform[0]);
txform[1].xyz = sv * length(txform[1]);
txform[2].xyz = sort_direction * length(txform[2]);
} else if (align_mode == ALIGN_LOCAL_BILLBOARD) {
if (align_axis == ALIGN_AXIS_X) {
vec3 len = vec3(
length(txform[0].xyz),
length(txform[1].xyz),
length(txform[2].xyz));
txform[0].xyz = normalize(txform[0].xyz);
txform[1].xyz = normalize(cross(sort_direction, txform[0].xyz));
txform[2].xyz = cross(txform[0].xyz, txform[1].xyz);
txform[0].xyz *= len.x;
txform[1].xyz *= len.y;
txform[2].xyz *= len.z;
} else if (align_axis == ALIGN_AXIS_Y) {
vec3 len = vec3(
length(txform[0].xyz),
length(txform[1].xyz),
length(txform[2].xyz));
txform[1].xyz = normalize(txform[1].xyz);
txform[0].xyz = normalize(cross(txform[1].xyz, sort_direction));
txform[2].xyz = cross(txform[0].xyz, txform[1].xyz);
txform[0].xyz *= len.x;
txform[1].xyz *= len.y;
txform[2].xyz *= len.z;
}
}
txform[3].xyz += velocity_flags.xyz * frame_remainder;
+19 -2
View File
@@ -323,6 +323,20 @@ void ParticlesStorage::particles_set_transform_align(RID p_particles, RSE::Parti
particles->transform_align = p_transform_align;
}
void ParticlesStorage::particles_set_transform_align_channel_filter(RID p_particles, RSE::ParticlesTransformAlignCustomSrc p_channel_filter) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->transform_align_channel_filter = p_channel_filter;
}
void ParticlesStorage::particles_set_transform_align_axis(RID p_particles, RSE::ParticlesTransformAlignAxis p_rotation_axis) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->transform_align_axis = p_rotation_axis;
}
void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
@@ -792,7 +806,7 @@ void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD) {
return;
}
@@ -960,6 +974,8 @@ void ParticlesStorage::_particles_update_instance_buffer(Particles *particles, c
particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_MODE, uint32_t(particles->transform_align), particles_shader.copy_shader_version, variant, specialization);
particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_UP, p_up_axis, particles_shader.copy_shader_version, variant, specialization);
particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::SORT_DIRECTION, p_axis, particles_shader.copy_shader_version, variant, specialization);
particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_AXIS, uint32_t(particles->transform_align_axis), particles_shader.copy_shader_version, variant, specialization);
particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_CHANNEL_FILTER, uint32_t(particles->transform_align_channel_filter), particles_shader.copy_shader_version, variant, specialization);
glBindVertexArray(particles->back_vertex_array);
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particles->front_instance_buffer, 0, particles->instance_buffer_size_cache);
@@ -1155,7 +1171,8 @@ void ParticlesStorage::update_particles() {
// Copy particles to instance buffer and pack Color/Custom.
// We don't have camera information here, so don't copy here if we need camera information for view depth or align mode.
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD) {
_particles_update_instance_buffer(particles, Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0));
if (particles->draw_order == RSE::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME && particles->sort_buffer_filled) {
+6 -1
View File
@@ -171,8 +171,10 @@ private:
RID process_material;
uint32_t frame_counter = 0;
RSE::ParticlesTransformAlign transform_align = RSE::PARTICLES_TRANSFORM_ALIGN_DISABLED;
RSE::ParticlesTransformAlign transform_align = RSE::PARTICLES_TRANSFORM_ALIGN_DISABLED;
RSE::ParticlesTransformAlignAxis transform_align_axis = RSE::PARTICLES_ALIGN_AXIS_Y;
RSE::ParticlesTransformAlignCustomSrc transform_align_channel_filter = RSE::PARTICLES_ALIGN_CHANNEL_FILTER_X;
RSE::ParticlesDrawOrder draw_order = RSE::PARTICLES_DRAW_ORDER_INDEX;
Vector<RID> draw_passes;
@@ -345,6 +347,9 @@ public:
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override;
virtual void particles_set_transform_align(RID p_particles, RSE::ParticlesTransformAlign p_transform_align) override;
virtual void particles_set_transform_align_channel_filter(RID p_particles, RSE::ParticlesTransformAlignCustomSrc p_channel_filter) override;
virtual void particles_set_transform_align_axis(RID p_particles, RSE::ParticlesTransformAlignAxis p_rotation_axis) override;
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) override;
virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override;
+45 -2
View File
@@ -470,6 +470,15 @@ void GPUParticles3D::_validate_property(PropertyInfo &p_property) const {
}
} else if (p_property.name == "seed" && !use_fixed_seed) {
p_property.usage = PROPERTY_USAGE_NONE;
} else if (p_property.name == "transform_align_axis") {
if (transform_align == TRANSFORM_ALIGN_DISABLED || transform_align == TRANSFORM_ALIGN_Z_BILLBOARD ||
transform_align == TRANSFORM_ALIGN_Y_TO_VELOCITY || transform_align == TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "transform_align_channel_filter") {
if (!(transform_align == TRANSFORM_ALIGN_Z_BILLBOARD)) {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
}
@@ -629,7 +638,9 @@ Ref<Skin> GPUParticles3D::get_skin() const {
}
void GPUParticles3D::set_transform_align(TransformAlign p_align) {
ERR_FAIL_INDEX(uint32_t(p_align), 4);
if (p_align != transform_align) {
notify_property_list_changed();
}
transform_align = p_align;
RS::get_singleton()->particles_set_transform_align(particles, RSE::ParticlesTransformAlign(transform_align));
}
@@ -638,6 +649,25 @@ GPUParticles3D::TransformAlign GPUParticles3D::get_transform_align() const {
return transform_align;
}
void GPUParticles3D::set_transform_align_channel_filter(RSE::ParticlesTransformAlignCustomSrc p_align_channel_filter) {
ERR_FAIL_INDEX(uint32_t(p_align_channel_filter), uint32_t(RSE::ParticlesTransformAlignCustomSrc::PARTICLES_ALIGN_CHANNEL_FILTER_MAX));
transform_align_channel_filter = p_align_channel_filter;
RS::get_singleton()->particles_set_transform_align_channel_filter(particles, transform_align_channel_filter);
}
RSE::ParticlesTransformAlignCustomSrc GPUParticles3D::get_transform_align_channel_filter() const {
return transform_align_channel_filter;
}
void GPUParticles3D::set_transform_align_axis(RSE::ParticlesTransformAlignAxis p_axis) {
transform_align_axis = p_axis;
RS::get_singleton()->particles_set_transform_align_axis(particles, p_axis);
}
RSE::ParticlesTransformAlignAxis GPUParticles3D::get_transform_align_axis() const {
return transform_align_axis;
}
void GPUParticles3D::convert_from_particles(Node *p_particles) {
CPUParticles3D *cpu_particles = Object::cast_to<CPUParticles3D>(p_particles);
ERR_FAIL_NULL_MSG(cpu_particles, "Only CPUParticles3D nodes can be converted to GPUParticles3D.");
@@ -809,6 +839,12 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align);
ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align);
ClassDB::bind_method(D_METHOD("set_transform_align_channel_filter", "channel_filter"), &GPUParticles3D::set_transform_align_channel_filter);
ClassDB::bind_method(D_METHOD("get_transform_align_channel_filter"), &GPUParticles3D::get_transform_align_channel_filter);
ClassDB::bind_method(D_METHOD("set_transform_align_axis", "align"), &GPUParticles3D::set_transform_align_axis);
ClassDB::bind_method(D_METHOD("get_transform_align_axis"), &GPUParticles3D::get_transform_align_axis);
ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &GPUParticles3D::convert_from_particles);
ClassDB::bind_method(D_METHOD("set_amount_ratio", "ratio"), &GPUParticles3D::set_amount_ratio);
@@ -843,7 +879,11 @@ void GPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_visibility_aabb", "get_visibility_aabb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,Reverse Lifetime,View Depth"), "set_draw_order", "get_draw_order");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,Z-Billboard,Y to Velocity,Z-Billboard + Y to Velocity"), "set_transform_align", "get_transform_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,Billboard,Align to Velocity,Trails,Local Billboard"), "set_transform_align", "get_transform_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align_axis", PROPERTY_HINT_ENUM, "X,Y"), "set_transform_align_axis", "get_transform_align_axis");
ADD_SUBGROUP("Advanced", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align_channel_filter", PROPERTY_HINT_ENUM, "Disabled,X,Y,Z,W"), "set_transform_align_channel_filter", "get_transform_align_channel_filter");
ADD_GROUP("Trails", "trail_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_trail_enabled", "is_trail_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_lifetime", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater,suffix:s"), "set_trail_lifetime", "get_trail_lifetime");
@@ -873,6 +913,7 @@ void GPUParticles3D::_bind_methods() {
BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD);
BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Y_TO_VELOCITY);
BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY);
BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_LOCAL_BILLBOARD);
ADD_PROPERTY_DEFAULT("seed", 0);
}
@@ -902,6 +943,8 @@ GPUParticles3D::GPUParticles3D() {
set_speed_scale(1);
set_collision_base_size(collision_base_size);
set_transform_align(TRANSFORM_ALIGN_DISABLED);
set_transform_align_channel_filter(RSE::ParticlesTransformAlignCustomSrc::PARTICLES_ALIGN_CHANNEL_FILTER_X);
set_transform_align_axis(RSE::ParticlesTransformAlignAxis::PARTICLES_ALIGN_AXIS_Y);
set_use_fixed_seed(false);
}
+14 -4
View File
@@ -48,10 +48,11 @@ public:
};
enum TransformAlign {
TRANSFORM_ALIGN_DISABLED,
TRANSFORM_ALIGN_Z_BILLBOARD,
TRANSFORM_ALIGN_Y_TO_VELOCITY,
TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY
TRANSFORM_ALIGN_DISABLED = RSE::PARTICLES_TRANSFORM_ALIGN_DISABLED,
TRANSFORM_ALIGN_Z_BILLBOARD = RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD,
TRANSFORM_ALIGN_Y_TO_VELOCITY = RSE::PARTICLES_TRANSFORM_ALIGN_Y_TO_VELOCITY,
TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY = RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY,
TRANSFORM_ALIGN_LOCAL_BILLBOARD = RSE::PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD,
};
enum {
@@ -86,6 +87,9 @@ private:
double trail_lifetime = 0.3;
TransformAlign transform_align = TRANSFORM_ALIGN_DISABLED;
RSE::ParticlesTransformAlignCustomSrc transform_align_channel_filter = RSE::ParticlesTransformAlignCustomSrc::PARTICLES_ALIGN_CHANNEL_FILTER_X;
RSE::ParticlesTransformAlignAxis transform_align_axis = RSE::ParticlesTransformAlignAxis::PARTICLES_ALIGN_AXIS_Y;
bool transform_align_use_velocity = true;
Ref<Material> process_material;
@@ -183,6 +187,12 @@ public:
void set_transform_align(TransformAlign p_align);
TransformAlign get_transform_align() const;
void set_transform_align_channel_filter(RSE::ParticlesTransformAlignCustomSrc p_align_channel_filter);
RSE::ParticlesTransformAlignCustomSrc get_transform_align_channel_filter() const;
void set_transform_align_axis(RSE::ParticlesTransformAlignAxis p_axis);
RSE::ParticlesTransformAlignAxis get_transform_align_axis() const;
void restart(bool p_keep_seed = false);
void set_use_fixed_seed(bool p_use_fixed_seed);
+202 -17
View File
@@ -107,6 +107,9 @@ void ParticleProcessMaterial::init_shaders() {
shader_names->velocity_limit_curve = "velocity_limit_curve";
shader_names->inherit_emitter_velocity_ratio = "inherit_emitter_velocity_ratio";
shader_names->velocity_pivot = "velocity_pivot";
shader_names->rotation_velocity_3d_max = "rotation_velocity_3d_max";
shader_names->rotation_velocity_3d_min = "rotation_velocity_3d_min";
shader_names->rotation_velocity_3d_curve = "rotation_velocity_3d_curve";
shader_names->emission_sphere_radius = "emission_sphere_radius";
shader_names->emission_box_extents = "emission_box_extents";
@@ -266,6 +269,14 @@ void ParticleProcessMaterial::_update_shader() {
code += "uniform vec3 velocity_pivot = vec3(0.0);\n";
if (using_rotation_velocity_3d) {
code += "uniform vec3 rotation_velocity_3d_min = vec3(0.0);\n";
code += "uniform vec3 rotation_velocity_3d_max = vec3(0.0);\n";
if (rotation_velocity_3d_curve.is_valid()) {
code += "uniform sampler2D rotation_velocity_3d_curve : repeat_disable;\n";
}
}
if (tex_parameters[PARAM_SCALE_OVER_VELOCITY].is_valid()) {
code += "uniform float scale_over_velocity_min = 0.0;\n";
code += "uniform float scale_over_velocity_max = 5.0;\n";
@@ -579,6 +590,9 @@ void ParticleProcessMaterial::_update_shader() {
if (turbulence_enabled) {
code += " float turb_influence;\n";
}
if (using_rotation_velocity_3d) {
code += " vec3 rotation_velocity_3d;\n";
}
code += "};\n\n";
code += "struct PhysicalParameters {\n";
@@ -613,6 +627,12 @@ void ParticleProcessMaterial::_update_shader() {
code += " params.rotation_3d.z = mix(rotation_3d_min.z, rotation_3d_max.z, rand_from_seed(alt_seed));\n";
code += " params.rotation_3d *= 3.14159 / 180.0;\n";
}
if (using_rotation_velocity_3d) {
code += " params.rotation_velocity_3d.x = mix(rotation_velocity_3d_min.x, rotation_velocity_3d_max.x, rand_from_seed(alt_seed));\n";
code += " params.rotation_velocity_3d.y = mix(rotation_velocity_3d_min.y, rotation_velocity_3d_max.y, rand_from_seed(alt_seed));\n";
code += " params.rotation_velocity_3d.z = mix(rotation_velocity_3d_min.z, rotation_velocity_3d_max.z, rand_from_seed(alt_seed));\n";
code += " params.rotation_velocity_3d *= 3.14159 / 180.0;\n";
}
code += "}\n\n";
code += "void calculate_initial_display_params(inout DisplayParameters params, inout uint alt_seed) {\n";
@@ -1222,6 +1242,59 @@ void ParticleProcessMaterial::_update_shader() {
code += " }\n";
code += "\n";
code += " CUSTOM.z = params.animation_offset + lifetime_percent * params.animation_speed;\n\n";
if (using_rotation_velocity_3d && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
if (rotation_velocity_3d_curve.is_valid()) {
code += " dynamic_params.rotation_velocity_3d *= texture(rotation_velocity_3d_curve, vec2(lifetime_percent)).xyz;\n";
}
code += R"(
if (length(dynamic_params.rotation_velocity_3d) > 0.0) {
vec3 r = dynamic_params.rotation_velocity_3d * DELTA;
vec4 origin = TRANSFORM[3];
vec4 norm_axis = normalize(TRANSFORM[0]);
float ux = norm_axis.x;
float uy = norm_axis.y;
float uz = norm_axis.z;
float s = sin(r.x);
float c = cos(r.x);
float cm = 1.0 - c;
mat4 rx = mat4(
vec4(ux * ux * cm + c, ux * uy * cm - uz * s, ux * uz * cm + uy * s, 0.0),
vec4(ux * uy * cm + uz * s, uy * uy * cm + c, uy * uz * cm - ux * s, 0.0),
vec4(ux * uz * cm - uy * s, uy * uz * cm + ux * s, uz * uz * cm + c, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
norm_axis = normalize(TRANSFORM[1]);
ux = norm_axis.x;
uy = norm_axis.y;
uz = norm_axis.z;
s = sin(r.y);
c = cos(r.y);
cm = 1.0 - c;
mat4 ry = mat4(
vec4(ux * ux * cm + c, ux * uy * cm - uz * s, ux * uz * cm + uy * s, 0.0),
vec4(ux * uy * cm + uz * s, uy * uy * cm + c, uy * uz * cm - ux * s, 0.0),
vec4(ux * uz * cm - uy * s, uy * uz * cm + ux * s, uz * uz * cm + c, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
norm_axis = normalize(TRANSFORM[2]);
ux = norm_axis.x;
uy = norm_axis.y;
uz = norm_axis.z;
s = sin(r.z);
c = cos(r.z);
cm = 1.0 - c;
mat4 rz = mat4(
vec4(ux * ux * cm + c, ux * uy * cm - uz * s, ux * uz * cm + uy * s, 0.0),
vec4(ux * uy * cm + uz * s, uy * uy * cm + c, uy * uz * cm - ux * s, 0.0),
vec4(ux * uz * cm - uy * s, uy * uz * cm + ux * s, uz * uz * cm + c, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
TRANSFORM[3].xyz = vec3(0.);
TRANSFORM = rz * ry * rx * TRANSFORM;
TRANSFORM[3] = origin;
}
)";
}
if (sub_emitter_mode != SUB_EMITTER_DISABLED && !RenderingServer::get_singleton()->is_low_end()) {
code += " int emit_count = 0;\n";
@@ -1873,6 +1946,7 @@ void ParticleProcessMaterial::set_use_scale_3d(const bool p_use_scale_3d) {
use_scale_3d = p_use_scale_3d;
notify_property_list_changed();
}
bool ParticleProcessMaterial::is_using_scale_3d() const {
return use_scale_3d;
}
@@ -1936,6 +2010,46 @@ void ParticleProcessMaterial::set_rotation_3d_max(const Vector3 &p_rotation_3d_m
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->rotation_3d_max, p_rotation_3d_max);
}
void ParticleProcessMaterial::set_use_rotation_velocity_3d(bool p_use_rotation_velocity_3d) {
using_rotation_velocity_3d = p_use_rotation_velocity_3d;
_queue_shader_change();
notify_property_list_changed();
}
bool ParticleProcessMaterial::is_using_rotation_velocity_3d() const {
return using_rotation_velocity_3d;
}
void ParticleProcessMaterial::set_rotation_velocity_3d_min(const Vector3 &p_rotation_velocity_3d_min) {
rotation_velocity_3d_min = p_rotation_velocity_3d_min;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->rotation_velocity_3d_min, p_rotation_velocity_3d_min);
}
Vector3 ParticleProcessMaterial::get_rotation_velocity_3d_min() const {
return rotation_velocity_3d_min;
}
void ParticleProcessMaterial::set_rotation_velocity_3d_max(const Vector3 &p_rotation_velocity_3d_max) {
rotation_velocity_3d_max = p_rotation_velocity_3d_max;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->rotation_velocity_3d_max, p_rotation_velocity_3d_max);
}
Vector3 ParticleProcessMaterial::get_rotation_velocity_3d_max() const {
return rotation_velocity_3d_max;
}
void ParticleProcessMaterial::set_rotation_velocity_3d_curve(const Ref<Texture2D> &p_texture) {
rotation_velocity_3d_curve = p_texture;
Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->rotation_velocity_3d_curve, tex_rid);
_queue_shader_change();
notify_property_list_changed();
}
Ref<Texture2D> ParticleProcessMaterial::get_rotation_velocity_3d_curve() const {
return rotation_velocity_3d_curve;
}
void ParticleProcessMaterial::set_turbulence_enabled(const bool p_turbulence_enabled) {
turbulence_enabled = p_turbulence_enabled;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_enabled, turbulence_enabled);
@@ -2029,8 +2143,8 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const
if (emission_shape != EMISSION_SHAPE_BOX) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "emission_point_texture" || p_property.name == "emission_color_texture") {
if (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
} else if ((p_property.name == "emission_point_texture" || p_property.name == "emission_color_texture")) {
if ((emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "emission_normal_texture") {
@@ -2038,7 +2152,7 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "emission_point_count") {
if (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
if ((emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name.begins_with("emission_ring_")) {
@@ -2069,12 +2183,69 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const
if (collision_mode != COLLISION_RIGID) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "directional_velocity_min" || p_property.name == "directional_velocity_max") {
} else if ((p_property.name == "directional_velocity_min" || p_property.name == "directional_velocity_max")) {
if (!tex_parameters[PARAM_DIRECTIONAL_VELOCITY].is_valid()) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (Engine::get_singleton()->is_editor_hint()) {
if (p_property.name == "scale_over_velocity_min" || p_property.name == "scale_over_velocity_max") {
} else if ((p_property.name == "rotation_velocity_3d_min" || p_property.name == "rotation_velocity_3d_max" || p_property.name == "rotation_velocity_3d_curve")) {
if (!using_rotation_velocity_3d) {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
if (Engine::get_singleton()->is_editor_hint()) {
if ((p_property.name == "scale_over_velocity_min" || p_property.name == "scale_over_velocity_max") && !tex_parameters[PARAM_SCALE_OVER_VELOCITY].is_valid()) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
} else if (p_property.name == "emission_box_extents") {
if (emission_shape != EMISSION_SHAPE_BOX) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "emission_point_texture" || p_property.name == "emission_color_texture") {
if (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "emission_normal_texture") {
if (emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "emission_point_count") {
if (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name.begins_with("emission_ring_")) {
if (emission_shape != EMISSION_SHAPE_RING) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "sub_emitter_frequency") {
if (sub_emitter_mode != SUB_EMITTER_CONSTANT) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "sub_emitter_amount_at_end") {
if (sub_emitter_mode != SUB_EMITTER_AT_END) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "sub_emitter_amount_at_collision") {
if (sub_emitter_mode != SUB_EMITTER_AT_COLLISION) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "sub_emitter_amount_at_start") {
if (sub_emitter_mode != SUB_EMITTER_AT_START) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "collision_friction") {
if (collision_mode != COLLISION_RIGID) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "collision_bounce") {
if (collision_mode != COLLISION_RIGID) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "directional_velocity_min" || p_property.name == "directional_velocity_max") {
if (!tex_parameters[PARAM_DIRECTIONAL_VELOCITY].is_valid()) {
p_property.usage = PROPERTY_USAGE_NONE;
}
} else if (p_property.name == "scale_over_velocity_min" || p_property.name == "scale_over_velocity_max") {
if (!tex_parameters[PARAM_SCALE_OVER_VELOCITY].is_valid()) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
@@ -2083,20 +2254,19 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
} else if (p_property.usage & PROPERTY_USAGE_EDITOR && (p_property.name.ends_with("_min") || p_property.name.ends_with("_max")) &&
!(p_property.name == "scale_3d_max" || p_property.name == "scale_3d_min") &&
!(p_property.name == "rotation_3d_max" || p_property.name == "rotation_3d_min")) {
!p_property.name.begins_with("rotation_velocity") &&
!p_property.name.begins_with("scale_3d") &&
!p_property.name.begins_with("rotation_3d")) {
p_property.usage &= ~PROPERTY_USAGE_EDITOR;
} else if ((p_property.name == "scale_3d_min" || p_property.name == "scale_3d_max") && !use_scale_3d) {
p_property.usage = PROPERTY_USAGE_NONE;
} else if (p_property.name == "scale" && use_scale_3d) {
p_property.usage = PROPERTY_USAGE_NONE;
} else if ((p_property.name == "rotation_3d_max" || p_property.name == "rotation_3d_min") && (particle_flags[PARTICLE_FLAG_DISABLE_Z] || !use_rotation_3d)) {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
if ((p_property.name == "scale_3d_min" || p_property.name == "scale_3d_max") && !use_scale_3d) {
p_property.usage = PROPERTY_USAGE_NONE;
}
if (p_property.name == "scale" && use_scale_3d) {
p_property.usage = PROPERTY_USAGE_NONE;
}
if ((p_property.name == "rotation_3d_max" || p_property.name == "rotation_3d_min") && (particle_flags[PARTICLE_FLAG_DISABLE_Z] || !use_rotation_3d)) {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
void ParticleProcessMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) {
@@ -2368,6 +2538,15 @@ void ParticleProcessMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_bounce", "bounce"), &ParticleProcessMaterial::set_collision_bounce);
ClassDB::bind_method(D_METHOD("get_collision_bounce"), &ParticleProcessMaterial::get_collision_bounce);
ClassDB::bind_method(D_METHOD("set_using_rotation_velocity_3d", "use_rotation_velocity_3d"), &ParticleProcessMaterial::set_use_rotation_velocity_3d);
ClassDB::bind_method(D_METHOD("is_using_rotation_velocity_3d"), &ParticleProcessMaterial::is_using_rotation_velocity_3d);
ClassDB::bind_method(D_METHOD("set_rotation_velocity_3d_max", "rotation_velocity_3d_max"), &ParticleProcessMaterial::set_rotation_velocity_3d_max);
ClassDB::bind_method(D_METHOD("get_rotation_velocity_3d_max"), &ParticleProcessMaterial::get_rotation_velocity_3d_max);
ClassDB::bind_method(D_METHOD("set_rotation_velocity_3d_min", "rotation_velocity_3d_min"), &ParticleProcessMaterial::set_rotation_velocity_3d_min);
ClassDB::bind_method(D_METHOD("get_rotation_velocity_3d_min"), &ParticleProcessMaterial::get_rotation_velocity_3d_min);
ClassDB::bind_method(D_METHOD("set_rotation_velocity_3d_curve", "rotation_velocity_3d_curve"), &ParticleProcessMaterial::set_rotation_velocity_3d_curve);
ClassDB::bind_method(D_METHOD("get_rotation_velocity_3d_curve"), &ParticleProcessMaterial::get_rotation_velocity_3d_curve);
#define ADD_MIN_MAX_PROPERTY(m_property, m_range, m_parameter_name) \
ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, m_property, PROPERTY_HINT_RANGE, m_range, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "set_param", "get_param", m_parameter_name); \
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, m_property "_min", PROPERTY_HINT_RANGE, m_range), "set_param_min", "get_param_min", m_parameter_name); \
@@ -2426,6 +2605,12 @@ void ParticleProcessMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, CurveTexture::get_class_static()), "set_param_texture", "get_param_texture", PARAM_RADIAL_VELOCITY);
ADD_SUBGROUP("Velocity Limit", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "velocity_limit_curve", PROPERTY_HINT_RESOURCE_TYPE, CurveTexture::get_class_static()), "set_velocity_limit_curve", "get_velocity_limit_curve");
ADD_SUBGROUP("Rotation Velocity 3D", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_rotation_velocity_3d", PROPERTY_HINT_GROUP_ENABLE), "set_using_rotation_velocity_3d", "is_using_rotation_velocity_3d");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_velocity_3d_min"), "set_rotation_velocity_3d_min", "get_rotation_velocity_3d_min");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_velocity_3d_max"), "set_rotation_velocity_3d_max", "get_rotation_velocity_3d_max");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "rotation_velocity_3d_curve", PROPERTY_HINT_RESOURCE_TYPE, CurveXYZTexture::get_class_static()), "set_rotation_velocity_3d_curve", "get_rotation_velocity_3d_curve");
ADD_GROUP("Accelerations", "");
ADD_SUBGROUP("Gravity", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
@@ -130,6 +130,8 @@ private:
uint64_t orbit_uses_curve_xyz : 1;
uint64_t use_scale_3d : 1;
uint64_t use_rotation_3d : 1;
uint64_t use_rotation_velocity_3d : 1;
uint64_t use_rotation_velocity_3d_curve : 1;
MaterialKey() {
memset(this, 0, sizeof(MaterialKey));
@@ -177,6 +179,11 @@ private:
mk.orbit_uses_curve_xyz = texture ? 1 : 0;
mk.use_scale_3d = use_scale_3d ? 1 : 0;
mk.use_rotation_3d = use_rotation_3d ? 1 : 0;
mk.use_rotation_velocity_3d = using_rotation_velocity_3d;
if (using_rotation_velocity_3d) {
texture = Object::cast_to<CurveXYZTexture>(rotation_velocity_3d_curve.ptr());
mk.use_rotation_velocity_3d_curve = texture ? 1 : 0;
}
for (int i = 0; i < PARAM_MAX; i++) {
if (tex_parameters[i].is_valid()) {
@@ -216,6 +223,7 @@ private:
StringName directional_velocity_min;
StringName scale_3d_min;
StringName rotation_3d_min;
StringName rotation_velocity_3d_min;
StringName initial_linear_velocity_max;
StringName initial_angle_max;
@@ -234,6 +242,7 @@ private:
StringName directional_velocity_max;
StringName scale_3d_max;
StringName rotation_3d_max;
StringName rotation_velocity_3d_max;
StringName angle_texture;
StringName angular_velocity_texture;
@@ -250,6 +259,7 @@ private:
StringName anim_offset_texture;
StringName velocity_limiter_texture;
StringName directional_velocity_texture;
StringName rotation_velocity_3d_curve;
StringName color;
StringName color_ramp;
@@ -361,6 +371,10 @@ private:
Vector3 scale_3d_max;
Vector3 rotation_3d_min;
Vector3 rotation_3d_max;
bool using_rotation_velocity_3d = false;
Vector3 rotation_velocity_3d_min;
Vector3 rotation_velocity_3d_max;
Ref<Texture2D> rotation_velocity_3d_curve;
SubEmitterMode sub_emitter_mode;
double sub_emitter_frequency = 0.0;
@@ -492,6 +506,15 @@ public:
void set_inherit_velocity_ratio(double p_ratio);
double get_inherit_velocity_ratio();
void set_use_rotation_velocity_3d(bool p_use_rotation_velocity_3d);
bool is_using_rotation_velocity_3d() const;
void set_rotation_velocity_3d_min(const Vector3 &p_rotation_velocity_3d_min);
Vector3 get_rotation_velocity_3d_min() const;
void set_rotation_velocity_3d_max(const Vector3 &p_rotation_velocity_3d_max);
Vector3 get_rotation_velocity_3d_max() const;
void set_rotation_velocity_3d_curve(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_rotation_velocity_3d_curve() const;
void set_attractor_interaction_enabled(bool p_enable);
bool is_attractor_interaction_enabled() const;
@@ -69,6 +69,8 @@ public:
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override {}
virtual void particles_set_transform_align(RID p_particles, RSE::ParticlesTransformAlign p_transform_align) override {}
virtual void particles_set_transform_align_channel_filter(RID p_particles, RSE::ParticlesTransformAlignCustomSrc p_transform_align_channel_filter) override {}
virtual void particles_set_transform_align_axis(RID p_particles, RSE::ParticlesTransformAlignAxis p_rotation_axis) override {}
virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override {}
virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override {}
@@ -65,14 +65,30 @@ layout(push_constant, std430) uniform Params {
uint motion_vectors_current_offset;
uint flags;
mat4 inv_emission_transform;
float inv_emission_transform[12];
uint align_channel_filter;
uint align_axis;
uint pad1;
uint pad2;
}
params;
#define TRANSFORM_ALIGN_DISABLED 0
#define TRANSFORM_ALIGN_Z_BILLBOARD 1
#define TRANSFORM_ALIGN_Y_TO_VELOCITY 2
#define TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY 3
#define ALIGN_DISABLED 0
#define ALIGN_BILLBOARD 1
#define ALIGN_Y_TO_VELOCITY 2
#define ALIGN_Z_BILLBOARD_Y_TO_VELOCITY 3
#define ALIGN_LOCAL_BILLBOARD 4
#define CHANNEL_FILTER_NONE 0
#define CHANNEL_FILTER_X 1
#define CHANNEL_FILTER_Y 2
#define CHANNEL_FILTER_Z 3
#define CHANNEL_FILTER_W 4
#define ALIGN_AXIS_X 0
#define ALIGN_AXIS_Y 1
#define ALIGN_AXIS_Z 2
void main() {
#ifdef MODE_FILL_SORT_BUFFER
@@ -155,31 +171,89 @@ void main() {
}
switch (params.align_mode) {
case TRANSFORM_ALIGN_DISABLED: {
case ALIGN_DISABLED: {
} break; //nothing
case TRANSFORM_ALIGN_Z_BILLBOARD: {
mat3 local = mat3(normalize(cross(params.align_up, params.sort_direction)), params.align_up, params.sort_direction);
case ALIGN_BILLBOARD: {
float angle = 0.;
switch (params.align_channel_filter) {
case CHANNEL_FILTER_X: {
angle = particles.data[particle].custom.x;
} break;
case CHANNEL_FILTER_Y: {
angle = particles.data[particle].custom.y;
} break;
case CHANNEL_FILTER_Z: {
angle = particles.data[particle].custom.z;
} break;
case CHANNEL_FILTER_W: {
angle = particles.data[particle].custom.w;
} break;
}
vec3 axis = normalize(params.sort_direction);
float s = sin(angle);
float c = cos(angle);
float oc = 1.0 - c;
mat3 rotated = mat3(
oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c);
vec3 new_up = rotated * params.align_up;
mat3 local = mat3(normalize(cross(new_up, params.sort_direction)), new_up, params.sort_direction);
local = local * mat3(txform);
txform[0].xyz = local[0];
txform[1].xyz = local[1];
txform[2].xyz = local[2];
} break;
case TRANSFORM_ALIGN_Y_TO_VELOCITY: {
case ALIGN_Y_TO_VELOCITY: {
vec3 v = particles.data[particle].velocity;
float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0;
if (length(v) > 0.0) {
txform[1].xyz = normalize(v);
} else {
txform[1].xyz = normalize(txform[1].xyz);
}
v = normalize(v);
switch (params.align_axis) {
case ALIGN_AXIS_X: {
vec3 len = vec3(
length(txform[0].xyz),
length(txform[1].xyz),
length(txform[2].xyz));
txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz));
txform[2].xyz = vec3(0.0, 0.0, 1.0) * s;
txform[0].xyz *= s;
txform[1].xyz *= s;
txform[0].xyz = v;
txform[1].xyz = normalize(cross(txform[2].xyz / len.z, txform[0].xyz));
txform[2].xyz = cross(txform[0].xyz, txform[1].xyz);
txform[0].xyz *= len.x;
txform[1].xyz *= len.y;
txform[2].xyz *= len.z;
} break;
case ALIGN_AXIS_Y: {
vec3 len = vec3(
length(txform[0].xyz),
length(txform[1].xyz),
length(txform[2].xyz));
txform[0].xyz = normalize(cross(v, txform[2].xyz / len.z));
txform[1].xyz = v;
txform[2].xyz = cross(txform[0].xyz, txform[1].xyz);
txform[0].xyz *= len.x;
txform[1].xyz *= len.y;
txform[2].xyz *= len.z;
} break;
case ALIGN_AXIS_Z: {
vec3 len = vec3(
length(txform[0].xyz),
length(txform[1].xyz),
length(txform[2].xyz));
txform[0].xyz = normalize(cross(txform[1].xyz / len.y, v));
txform[2].xyz = v;
txform[1].xyz = normalize(cross(txform[2].xyz, txform[0].xyz));
txform[0].xyz *= len.x;
txform[1].xyz *= len.y;
txform[2].xyz *= len.z;
} break;
}
} break;
case TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY: {
case ALIGN_Z_BILLBOARD_Y_TO_VELOCITY: {
vec3 v = particles.data[particle].velocity;
vec3 sv = v - params.sort_direction * dot(params.sort_direction, v); //screen velocity
@@ -194,6 +268,39 @@ void main() {
txform[2].xyz = params.sort_direction * length(txform[2]);
} break;
case ALIGN_LOCAL_BILLBOARD: {
switch (params.align_axis) {
case ALIGN_AXIS_X: {
vec3 len = vec3(
length(txform[0].xyz),
length(txform[1].xyz),
length(txform[2].xyz));
txform[0].xyz = normalize(txform[0].xyz);
txform[1].xyz = normalize(cross(params.sort_direction, txform[0].xyz));
txform[2].xyz = cross(txform[0].xyz, txform[1].xyz);
txform[0].xyz *= len.x;
txform[1].xyz *= len.y;
txform[2].xyz *= len.z;
} break;
case ALIGN_AXIS_Y: {
vec3 len = vec3(
length(txform[0].xyz),
length(txform[1].xyz),
length(txform[2].xyz));
txform[1].xyz = normalize(txform[1].xyz);
txform[0].xyz = normalize(cross(txform[1].xyz, params.sort_direction));
txform[2].xyz = cross(txform[0].xyz, txform[1].xyz);
txform[0].xyz *= len.x;
txform[1].xyz *= len.y;
txform[2].xyz *= len.z;
} break;
}
} break;
}
txform[3].xyz += particles.data[particle].velocity * params.frame_remainder;
@@ -206,7 +313,13 @@ void main() {
if (bool(params.flags & PARAMS_FLAG_COPY_MODE_2D)) {
// In global mode, bring 2D particles to local coordinates
// as they will be drawn with the node position as origin.
txform = params.inv_emission_transform * txform;
mat4 inv_emission_transform;
inv_emission_transform[0] = vec4(params.inv_emission_transform[0], params.inv_emission_transform[1], params.inv_emission_transform[2], 0.0);
inv_emission_transform[1] = vec4(params.inv_emission_transform[3], params.inv_emission_transform[4], params.inv_emission_transform[5], 0.0);
inv_emission_transform[2] = vec4(params.inv_emission_transform[6], params.inv_emission_transform[7], params.inv_emission_transform[8], 0.0);
inv_emission_transform[3] = vec4(params.inv_emission_transform[9], params.inv_emission_transform[10], params.inv_emission_transform[11], 1.0);
inv_emission_transform = transpose(inv_emission_transform);
txform = inv_emission_transform * txform;
}
} else {
// Set scale to zero and translate to -INF so particle will be invisible
@@ -493,6 +493,20 @@ void ParticlesStorage::particles_set_transform_align(RID p_particles, RSE::Parti
particles->transform_align = p_transform_align;
}
void ParticlesStorage::particles_set_transform_align_channel_filter(RID p_particles, RSE::ParticlesTransformAlignCustomSrc p_transform_align_channel_filter) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->transform_align_src = p_transform_align_channel_filter;
}
void ParticlesStorage::particles_set_transform_align_axis(RID p_particles, RSE::ParticlesTransformAlignAxis p_rotation_axis) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->transform_align_axis = p_rotation_axis;
}
void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
@@ -1222,7 +1236,7 @@ void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD) {
return;
}
@@ -1297,6 +1311,9 @@ void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p
copy_push_constant.align_up[2] = p_up_axis.z;
copy_push_constant.align_mode = particles->transform_align;
copy_push_constant.align_src = particles->transform_align_src;
copy_push_constant.align_axis = uint32_t(particles->transform_align_axis);
copy_push_constant.pad2 = uint32_t(0);
if (do_sort) {
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
@@ -1621,7 +1638,18 @@ void ParticlesStorage::update_particles() {
// So, we need to pass the inverse of the emission transform to bring the
// particles to local coordinates before drawing.
Transform3D inv = particles->emission_transform.affine_inverse();
RendererRD::MaterialStorage::store_transform(inv, copy_push_constant.inv_emission_transform);
copy_push_constant.inv_emission_transform[0] = inv.basis.rows[0][0];
copy_push_constant.inv_emission_transform[1] = inv.basis.rows[1][0];
copy_push_constant.inv_emission_transform[2] = inv.basis.rows[2][0];
copy_push_constant.inv_emission_transform[3] = inv.basis.rows[0][1];
copy_push_constant.inv_emission_transform[4] = inv.basis.rows[1][1];
copy_push_constant.inv_emission_transform[5] = inv.basis.rows[2][1];
copy_push_constant.inv_emission_transform[6] = inv.basis.rows[0][2];
copy_push_constant.inv_emission_transform[7] = inv.basis.rows[1][2];
copy_push_constant.inv_emission_transform[8] = inv.basis.rows[2][2];
copy_push_constant.inv_emission_transform[9] = inv.origin.x;
copy_push_constant.inv_emission_transform[10] = inv.origin.y;
copy_push_constant.inv_emission_transform[11] = inv.origin.z;
}
copy_push_constant.total_particles = total_amount;
@@ -1630,6 +1658,8 @@ void ParticlesStorage::update_particles() {
copy_push_constant.align_up[0] = 0;
copy_push_constant.align_up[1] = 0;
copy_push_constant.align_up[2] = 0;
copy_push_constant.align_src = particles->transform_align_src;
copy_push_constant.align_axis = uint32_t(particles->transform_align_axis);
if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) {
copy_push_constant.trail_size = particles->trail_bind_poses.size();
@@ -183,7 +183,10 @@ private:
RID process_material;
uint32_t frame_counter = 0;
RSE::ParticlesTransformAlign transform_align = RSE::PARTICLES_TRANSFORM_ALIGN_DISABLED;
RSE::ParticlesTransformAlignCustomSrc transform_align_src = RSE::PARTICLES_ALIGN_CHANNEL_FILTER_X;
RSE::ParticlesTransformAlignAxis transform_align_axis = RSE::ParticlesTransformAlignAxis::PARTICLES_ALIGN_AXIS_Y;
RSE::ParticlesDrawOrder draw_order = RSE::PARTICLES_DRAW_ORDER_INDEX;
@@ -317,7 +320,12 @@ private:
uint32_t copy_mode_2d : 1;
};
float inv_emission_transform[16];
float inv_emission_transform[12];
uint32_t align_src;
uint32_t align_axis;
uint32_t pad1;
uint32_t pad2;
};
enum {
@@ -460,7 +468,11 @@ public:
virtual void particles_set_interpolate(RID p_particles, bool p_enable) override;
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) override;
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override;
virtual void particles_set_transform_align(RID p_particles, RSE::ParticlesTransformAlign p_transform_align) override;
virtual void particles_set_transform_align_channel_filter(RID p_particles, RSE::ParticlesTransformAlignCustomSrc p_transform_align_channel_filter) override;
virtual void particles_set_transform_align_axis(RID p_particles, RSE::ParticlesTransformAlignAxis p_rotation_axis) override;
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) override;
virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override;
+12
View File
@@ -2724,6 +2724,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("particles_set_fractional_delta", "particles", "enable"), &RenderingServer::particles_set_fractional_delta);
ClassDB::bind_method(D_METHOD("particles_set_collision_base_size", "particles", "size"), &RenderingServer::particles_set_collision_base_size);
ClassDB::bind_method(D_METHOD("particles_set_transform_align", "particles", "align"), &RenderingServer::particles_set_transform_align);
ClassDB::bind_method(D_METHOD("particles_set_transform_align_channel_filter", "particles", "channel_filter"), &RenderingServer::particles_set_transform_align_channel_filter);
ClassDB::bind_method(D_METHOD("particles_set_transform_align_axis", "particles", "p_rotation_axis"), &RenderingServer::particles_set_transform_align_axis);
ClassDB::bind_method(D_METHOD("particles_set_trails", "particles", "enable", "length_sec"), &RenderingServer::particles_set_trails);
ClassDB::bind_method(D_METHOD("particles_set_trail_bind_poses", "particles", "bind_poses"), &RenderingServer::_particles_set_trail_bind_poses);
@@ -2747,6 +2749,16 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD);
BIND_ENUM_CONSTANT(RSE::PARTICLES_TRANSFORM_ALIGN_Y_TO_VELOCITY);
BIND_ENUM_CONSTANT(RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY);
BIND_ENUM_CONSTANT(RSE::PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD);
BIND_ENUM_CONSTANT(RSE::PARTICLES_ALIGN_CHANNEL_FILTER_DISABLED);
BIND_ENUM_CONSTANT(RSE::PARTICLES_ALIGN_CHANNEL_FILTER_X);
BIND_ENUM_CONSTANT(RSE::PARTICLES_ALIGN_CHANNEL_FILTER_Y);
BIND_ENUM_CONSTANT(RSE::PARTICLES_ALIGN_CHANNEL_FILTER_Z);
BIND_ENUM_CONSTANT(RSE::PARTICLES_ALIGN_CHANNEL_FILTER_W);
BIND_ENUM_CONSTANT(RSE::PARTICLES_ALIGN_AXIS_X)
BIND_ENUM_CONSTANT(RSE::PARTICLES_ALIGN_AXIS_Y)
BIND_CONSTANT(RSE::PARTICLES_EMIT_FLAG_POSITION);
BIND_CONSTANT(RSE::PARTICLES_EMIT_FLAG_ROTATION_SCALE);
+5
View File
@@ -450,6 +450,8 @@ public:
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) = 0;
virtual void particles_set_transform_align(RID p_particles, RSE::ParticlesTransformAlign p_transform_align) = 0;
virtual void particles_set_transform_align_channel_filter(RID p_particles, RSE::ParticlesTransformAlignCustomSrc p_transform_align_channel_filter) = 0;
virtual void particles_set_transform_align_axis(RID p_particles, RSE::ParticlesTransformAlignAxis p_rotation_axis) = 0;
virtual void particles_set_trails(RID p_particles, bool p_enable, float p_length_sec) = 0;
virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) = 0;
@@ -1070,6 +1072,7 @@ private:
};
// Make variant understand the enums.
VARIANT_ENUM_CAST_EXT(RSE::TextureType, RenderingServer::TextureType);
VARIANT_ENUM_CAST_EXT(RSE::TextureLayeredType, RenderingServer::TextureLayeredType);
VARIANT_ENUM_CAST_EXT(RSE::CubeMapLayer, RenderingServer::CubeMapLayer);
@@ -1097,6 +1100,8 @@ VARIANT_ENUM_CAST_EXT(RSE::DecalTexture, RenderingServer::DecalTexture);
VARIANT_ENUM_CAST_EXT(RSE::DecalFilter, RenderingServer::DecalFilter);
VARIANT_ENUM_CAST_EXT(RSE::ParticlesMode, RenderingServer::ParticlesMode);
VARIANT_ENUM_CAST_EXT(RSE::ParticlesTransformAlign, RenderingServer::ParticlesTransformAlign);
VARIANT_ENUM_CAST_EXT(RSE::ParticlesTransformAlignCustomSrc, RenderingServer::ParticlesTransformAlignCustomSrc);
VARIANT_ENUM_CAST_EXT(RSE::ParticlesTransformAlignAxis, RenderingServer::ParticlesTransformAlignAxis);
VARIANT_ENUM_CAST_EXT(RSE::ParticlesDrawOrder, RenderingServer::ParticlesDrawOrder);
VARIANT_ENUM_CAST_EXT(RSE::ParticlesEmitFlags, RenderingServer::ParticlesEmitFlags);
VARIANT_ENUM_CAST_EXT(RSE::ParticlesCollisionType, RenderingServer::ParticlesCollisionType);
@@ -639,6 +639,8 @@ public:
FUNC2(particles_set_collision_base_size, RID, float)
FUNC2(particles_set_transform_align, RID, RSE::ParticlesTransformAlign)
FUNC2(particles_set_transform_align_channel_filter, RID, RSE::ParticlesTransformAlignCustomSrc)
FUNC2(particles_set_transform_align_axis, RID, RSE::ParticlesTransformAlignAxis)
FUNC2(particles_set_draw_order, RID, RSE::ParticlesDrawOrder)
@@ -365,6 +365,7 @@ enum ParticlesTransformAlign {
PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD,
PARTICLES_TRANSFORM_ALIGN_Y_TO_VELOCITY,
PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY,
PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD,
};
enum ParticlesEmitFlags {
@@ -382,6 +383,21 @@ enum ParticlesDrawOrder {
PARTICLES_DRAW_ORDER_VIEW_DEPTH,
};
enum ParticlesTransformAlignCustomSrc {
PARTICLES_ALIGN_CHANNEL_FILTER_DISABLED,
PARTICLES_ALIGN_CHANNEL_FILTER_X,
PARTICLES_ALIGN_CHANNEL_FILTER_Y,
PARTICLES_ALIGN_CHANNEL_FILTER_Z,
PARTICLES_ALIGN_CHANNEL_FILTER_W,
PARTICLES_ALIGN_CHANNEL_FILTER_MAX,
};
enum ParticlesTransformAlignAxis {
PARTICLES_ALIGN_AXIS_X,
PARTICLES_ALIGN_AXIS_Y,
PARTICLES_ALIGN_AXIS_MAX,
};
/* PARTICLES COLLISION API */
enum ParticlesCollisionType {
@@ -76,6 +76,8 @@ public:
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) = 0;
virtual void particles_set_transform_align(RID p_particles, RSE::ParticlesTransformAlign p_transform_align) = 0;
virtual void particles_set_transform_align_channel_filter(RID p_particles, RSE::ParticlesTransformAlignCustomSrc p_channel_filter) = 0;
virtual void particles_set_transform_align_axis(RID p_particles, RSE::ParticlesTransformAlignAxis p_rotation_axis) = 0;
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) = 0;