diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index 6e3fae4f47..90fca02cb8 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -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]. + The alignment of particles. Use this for billboarding and aligning to velocity. + + + When using transform align local billboard, which axis to use for the billboarding. Supports only X or Y. + + + In the case of billboarded particles, which custom channel to read from to calculate their angle. 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 @@ Align each particle's Z axis to face the camera and Y axis to the velocity vector. + + Align each particle's Z axis to face the camera, while preserving a given axis (X or Y). + diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index 9145a14c8b..ab53a7a689 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -321,6 +321,15 @@ The minimum 3D orientation, in degrees. Works only in 3D and if [member use_rotation_3d] is enabled. + + Rotation velocity curve over lifetime, per-axis. Enable [member use_rotation_velocity_3d] to use this. + + + Maximum 3D rotation velocity on the particle's local axis. Enable [member use_rotation_velocity_3d] to use this. + + + Minimum 3D rotation velocity on the particle's local axis. Enable [member use_rotation_velocity_3d] to use this. + The maximum value of the random scale vector for each particle. Works only if [member use_scale_3d] is enabled. @@ -424,6 +433,9 @@ Enable the usage of [member rotation_3d_min] and [member rotation_3d_max]. + + Enable 3D rotation velocity. + Enable the usage of [member scale_3d_min] and [member scale_3d_max]. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index c85a4e4327..89b9c149b8 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3326,6 +3326,22 @@ Sets the transform alignment for the particle system. Equivalent to [member GPUParticles3D.transform_align]. + + + + + + Sets which axis to use for transform alignment. + + + + + + + + When using Z-Billboarding, which CUSTOM channel to read from. + + @@ -5183,6 +5199,30 @@ Align each particle's Z axis to face the camera and Y axis to the velocity vector. + + Billboard each particles around a local axis. + + + Do not read from CUSTOM when performing billboarding. + + + Read from [code]CUSTOM.x[/code] when performing billboarding and use it as an angle, in radians. + + + Read from [code]CUSTOM.y[/code] when performing billboarding and use it as an angle, in radians. + + + Read from [code]CUSTOM.z[/code] when performing billboarding and use it as an angle, in radians. + + + Read from [code]CUSTOM.w[/code] when performing billboarding and use it as an angle, in radians. + + + Use the X axis for local billboarding. + + + Use the Y axis for local billboarding. + Particle starts at the specified position. diff --git a/drivers/gles3/shaders/particles_copy.glsl b/drivers/gles3/shaders/particles_copy.glsl index 55b5e6d7ce..a2632ca977 100644 --- a/drivers/gles3/shaders/particles_copy.glsl +++ b/drivers/gles3/shaders/particles_copy.glsl @@ -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; diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 9917d70a52..1a44ad9f40 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -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) { diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index 60b3b646dc..22884c67bd 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -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 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; diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 15564fd818..78039aa158 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -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 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(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); } diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index afe389d986..e94a8622f9 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -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 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); diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index fd8fa71503..ffbf2ed991 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -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 &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 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"); diff --git a/scene/resources/particle_process_material.h b/scene/resources/particle_process_material.h index 5d7dfeb007..93b071e05d 100644 --- a/scene/resources/particle_process_material.h +++ b/scene/resources/particle_process_material.h @@ -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(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 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 &p_texture); + Ref get_rotation_velocity_3d_curve() const; + void set_attractor_interaction_enabled(bool p_enable); bool is_attractor_interaction_enabled() const; diff --git a/servers/rendering/dummy/storage/particles_storage.h b/servers/rendering/dummy/storage/particles_storage.h index 63862a6048..6c91a6246c 100644 --- a/servers/rendering/dummy/storage/particles_storage.h +++ b/servers/rendering/dummy/storage/particles_storage.h @@ -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 &p_bind_poses) override {} diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl index 93c4d1009d..809d894a1b 100644 --- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl +++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl @@ -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 diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 45b465eafa..19a58cb21e 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -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(); diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index 2c5008f000..9b3e225405 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -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; diff --git a/servers/rendering/rendering_server.cpp b/servers/rendering/rendering_server.cpp index f7a0f8cabb..3b251d6a71 100644 --- a/servers/rendering/rendering_server.cpp +++ b/servers/rendering/rendering_server.cpp @@ -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); diff --git a/servers/rendering/rendering_server.h b/servers/rendering/rendering_server.h index 5efdbd3be1..093338d0bb 100644 --- a/servers/rendering/rendering_server.h +++ b/servers/rendering/rendering_server.h @@ -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 &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); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 158d54b47c..fc0cf25bc2 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -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) diff --git a/servers/rendering/rendering_server_enums.h b/servers/rendering/rendering_server_enums.h index 7fcf210ab6..c1271fa6ff 100644 --- a/servers/rendering/rendering_server_enums.h +++ b/servers/rendering/rendering_server_enums.h @@ -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 { diff --git a/servers/rendering/storage/particles_storage.h b/servers/rendering/storage/particles_storage.h index f5b38942f0..abb55b662e 100644 --- a/servers/rendering/storage/particles_storage.h +++ b/servers/rendering/storage/particles_storage.h @@ -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;