Merge pull request #41810 from reduz/particle-improvements
Implement manual particle emission and particle sub emitters.
This commit is contained in:
commit
39f58c7412
|
@ -301,6 +301,36 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const {
|
|||
}
|
||||
}
|
||||
|
||||
void GPUParticles3D::emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
|
||||
RS::get_singleton()->particles_emit(particles, p_transform, p_velocity, p_color, p_custom, p_emit_flags);
|
||||
}
|
||||
|
||||
void GPUParticles3D::_attach_sub_emitter() {
|
||||
Node *n = get_node_or_null(sub_emitter);
|
||||
if (n) {
|
||||
GPUParticles3D *sen = Object::cast_to<GPUParticles3D>(n);
|
||||
if (sen && sen != this) {
|
||||
RS::get_singleton()->particles_set_subemitter(particles, sen->particles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPUParticles3D::set_sub_emitter(const NodePath &p_path) {
|
||||
if (is_inside_tree()) {
|
||||
RS::get_singleton()->particles_set_subemitter(particles, RID());
|
||||
}
|
||||
|
||||
sub_emitter = p_path;
|
||||
|
||||
if (is_inside_tree() && sub_emitter != NodePath()) {
|
||||
_attach_sub_emitter();
|
||||
}
|
||||
}
|
||||
|
||||
NodePath GPUParticles3D::get_sub_emitter() const {
|
||||
return sub_emitter;
|
||||
}
|
||||
|
||||
void GPUParticles3D::_notification(int p_what) {
|
||||
if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
|
||||
if (can_process()) {
|
||||
|
@ -319,6 +349,16 @@ void GPUParticles3D::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
if (p_what == NOTIFICATION_ENTER_TREE) {
|
||||
if (sub_emitter != NodePath()) {
|
||||
_attach_sub_emitter();
|
||||
}
|
||||
}
|
||||
|
||||
if (p_what == NOTIFICATION_EXIT_TREE) {
|
||||
RS::get_singleton()->particles_set_subemitter(particles, RID());
|
||||
}
|
||||
|
||||
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
|
||||
// make sure particles are updated before rendering occurs if they were active before
|
||||
if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) {
|
||||
|
@ -369,8 +409,14 @@ void GPUParticles3D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles3D::restart);
|
||||
ClassDB::bind_method(D_METHOD("capture_aabb"), &GPUParticles3D::capture_aabb);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_sub_emitter", "path"), &GPUParticles3D::set_sub_emitter);
|
||||
ClassDB::bind_method(D_METHOD("get_sub_emitter"), &GPUParticles3D::get_sub_emitter);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter");
|
||||
ADD_GROUP("Time", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
|
||||
|
@ -396,6 +442,12 @@ void GPUParticles3D::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
|
||||
BIND_ENUM_CONSTANT(DRAW_ORDER_VIEW_DEPTH);
|
||||
|
||||
BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
|
||||
BIND_ENUM_CONSTANT(EMIT_FLAG_ROTATION_SCALE);
|
||||
BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
|
||||
BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
|
||||
BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
|
||||
|
||||
BIND_CONSTANT(MAX_DRAW_PASSES);
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ private:
|
|||
bool local_coords;
|
||||
int fixed_fps;
|
||||
bool fractional_delta;
|
||||
NodePath sub_emitter;
|
||||
|
||||
Ref<Material> process_material;
|
||||
|
||||
|
@ -71,6 +72,8 @@ private:
|
|||
|
||||
Vector<Ref<Mesh>> draw_passes;
|
||||
|
||||
void _attach_sub_emitter();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
@ -121,13 +124,27 @@ public:
|
|||
|
||||
virtual String get_configuration_warning() const override;
|
||||
|
||||
void set_sub_emitter(const NodePath &p_path);
|
||||
NodePath get_sub_emitter() const;
|
||||
|
||||
void restart();
|
||||
|
||||
enum EmitFlags {
|
||||
EMIT_FLAG_POSITION = RS::PARTICLES_EMIT_FLAG_POSITION,
|
||||
EMIT_FLAG_ROTATION_SCALE = RS::PARTICLES_EMIT_FLAG_ROTATION_SCALE,
|
||||
EMIT_FLAG_VELOCITY = RS::PARTICLES_EMIT_FLAG_VELOCITY,
|
||||
EMIT_FLAG_COLOR = RS::PARTICLES_EMIT_FLAG_COLOR,
|
||||
EMIT_FLAG_CUSTOM = RS::PARTICLES_EMIT_FLAG_CUSTOM
|
||||
};
|
||||
|
||||
void emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
|
||||
|
||||
AABB capture_aabb() const;
|
||||
GPUParticles3D();
|
||||
~GPUParticles3D();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder)
|
||||
VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags)
|
||||
|
||||
#endif // PARTICLES_H
|
||||
|
|
|
@ -91,13 +91,13 @@ void ParticlesMaterial::init_shaders() {
|
|||
shader_names->emission_texture_normal = "emission_texture_normal";
|
||||
shader_names->emission_texture_color = "emission_texture_color";
|
||||
|
||||
shader_names->trail_divisor = "trail_divisor";
|
||||
shader_names->trail_size_modifier = "trail_size_modifier";
|
||||
shader_names->trail_color_modifier = "trail_color_modifier";
|
||||
|
||||
shader_names->gravity = "gravity";
|
||||
|
||||
shader_names->lifetime_randomness = "lifetime_randomness";
|
||||
|
||||
shader_names->sub_emitter_frequency = "sub_emitter_frequency";
|
||||
shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end";
|
||||
shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity";
|
||||
}
|
||||
|
||||
void ParticlesMaterial::finish_shaders() {
|
||||
|
@ -192,9 +192,17 @@ void ParticlesMaterial::_update_shader() {
|
|||
}
|
||||
}
|
||||
|
||||
code += "uniform vec4 color_value : hint_color;\n";
|
||||
if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
|
||||
if (sub_emitter_mode == SUB_EMITTER_CONSTANT) {
|
||||
code += "uniform float sub_emitter_frequency;\n";
|
||||
}
|
||||
if (sub_emitter_mode == SUB_EMITTER_AT_END) {
|
||||
code += "uniform int sub_emitter_amount_at_end;\n";
|
||||
}
|
||||
code += "uniform bool sub_emitter_keep_velocity;\n";
|
||||
}
|
||||
|
||||
code += "uniform int trail_divisor;\n";
|
||||
code += "uniform vec4 color_value : hint_color;\n";
|
||||
|
||||
code += "uniform vec3 gravity;\n";
|
||||
|
||||
|
@ -239,14 +247,6 @@ void ParticlesMaterial::_update_shader() {
|
|||
code += "uniform sampler2D anim_offset_texture;\n";
|
||||
}
|
||||
|
||||
if (trail_size_modifier.is_valid()) {
|
||||
code += "uniform sampler2D trail_size_modifier;\n";
|
||||
}
|
||||
|
||||
if (trail_color_modifier.is_valid()) {
|
||||
code += "uniform sampler2D trail_color_modifier;\n";
|
||||
}
|
||||
|
||||
//need a random function
|
||||
code += "\n\n";
|
||||
code += "float rand_from_seed(inout uint seed) {\n";
|
||||
|
@ -278,7 +278,7 @@ void ParticlesMaterial::_update_shader() {
|
|||
code += "\n";
|
||||
|
||||
code += "void compute() {\n";
|
||||
code += " uint base_number = NUMBER / uint(trail_divisor);\n";
|
||||
code += " uint base_number = NUMBER;\n";
|
||||
code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
|
||||
code += " float angle_rand = rand_from_seed(alt_seed);\n";
|
||||
code += " float scale_rand = rand_from_seed(alt_seed);\n";
|
||||
|
@ -293,17 +293,7 @@ void ParticlesMaterial::_update_shader() {
|
|||
code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
|
||||
code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
|
||||
}
|
||||
code += " bool restart = false;\n";
|
||||
code += " if (CUSTOM.y > CUSTOM.w) {\n";
|
||||
code += " restart = true;\n";
|
||||
code += " }\n\n";
|
||||
code += " if (RESTART || restart) {\n";
|
||||
|
||||
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
|
||||
code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
|
||||
} else {
|
||||
code += " float tex_linear_velocity = 0.0;\n";
|
||||
}
|
||||
code += " if (RESTART) {\n";
|
||||
|
||||
if (tex_parameters[PARAM_ANGLE].is_valid()) {
|
||||
code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n";
|
||||
|
@ -319,25 +309,34 @@ void ParticlesMaterial::_update_shader() {
|
|||
|
||||
code += " float spread_rad = spread * degree_to_rad;\n";
|
||||
|
||||
code += " if (RESTART_VELOCITY) {\n";
|
||||
|
||||
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
|
||||
code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
|
||||
} else {
|
||||
code += " float tex_linear_velocity = 0.0;\n";
|
||||
}
|
||||
|
||||
if (flags[FLAG_DISABLE_Z]) {
|
||||
code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
|
||||
code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
|
||||
code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
|
||||
code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
|
||||
code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
|
||||
code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
|
||||
code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
|
||||
code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
|
||||
|
||||
} else {
|
||||
//initiate velocity spread in 3D
|
||||
code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
|
||||
code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
|
||||
code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n";
|
||||
code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n";
|
||||
code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
|
||||
code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
|
||||
code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
|
||||
code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
|
||||
code += " vec_direction = normalize(vec_direction);\n";
|
||||
code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
|
||||
code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
|
||||
code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
|
||||
code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n";
|
||||
code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n";
|
||||
code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
|
||||
code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
|
||||
code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
|
||||
code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
|
||||
code += " vec_direction = normalize(vec_direction);\n";
|
||||
code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
|
||||
}
|
||||
code += " }\n";
|
||||
|
||||
code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
|
||||
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
|
||||
|
@ -345,35 +344,38 @@ void ParticlesMaterial::_update_shader() {
|
|||
code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
|
||||
code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
|
||||
|
||||
code += " if (RESTART_POSITION) {\n";
|
||||
|
||||
switch (emission_shape) {
|
||||
case EMISSION_SHAPE_POINT: {
|
||||
//do none
|
||||
//do none, identity (will later be multiplied by emission transform)
|
||||
code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n";
|
||||
} break;
|
||||
case EMISSION_SHAPE_SPHERE: {
|
||||
code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
|
||||
code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
|
||||
code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
|
||||
code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
|
||||
code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
|
||||
code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
|
||||
code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
|
||||
code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
|
||||
} break;
|
||||
case EMISSION_SHAPE_BOX: {
|
||||
code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
|
||||
code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
|
||||
} break;
|
||||
case EMISSION_SHAPE_POINTS:
|
||||
case EMISSION_SHAPE_DIRECTED_POINTS: {
|
||||
code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
|
||||
code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
|
||||
|
||||
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
|
||||
if (flags[FLAG_DISABLE_Z]) {
|
||||
code += " mat2 rotm;";
|
||||
code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
|
||||
code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
|
||||
code += " VELOCITY.xy = rotm * VELOCITY.xy;\n";
|
||||
code += " mat2 rotm;";
|
||||
code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
|
||||
code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
|
||||
code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
|
||||
} else {
|
||||
code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
|
||||
code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
|
||||
code += " vec3 tangent = normalize(cross(v0, normal));\n";
|
||||
code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
|
||||
code += " VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
|
||||
code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
|
||||
code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
|
||||
code += " vec3 tangent = normalize(cross(v0, normal));\n";
|
||||
code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
|
||||
code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
@ -381,12 +383,14 @@ void ParticlesMaterial::_update_shader() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
|
||||
code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
|
||||
|
||||
code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
|
||||
code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
|
||||
if (flags[FLAG_DISABLE_Z]) {
|
||||
code += " VELOCITY.z = 0.0;\n";
|
||||
code += " TRANSFORM[3].z = 0.0;\n";
|
||||
code += " VELOCITY.z = 0.0;\n";
|
||||
code += " TRANSFORM[3].z = 0.0;\n";
|
||||
}
|
||||
code += " }\n";
|
||||
|
||||
code += " } else {\n";
|
||||
|
||||
|
@ -540,11 +544,6 @@ void ParticlesMaterial::_update_shader() {
|
|||
if (emission_color_texture.is_valid() && (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS)) {
|
||||
code += " COLOR *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n";
|
||||
}
|
||||
if (trail_color_modifier.is_valid()) {
|
||||
code += " if (trail_divisor > 1) {\n";
|
||||
code += " COLOR *= textureLod(trail_color_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0);\n";
|
||||
code += " }\n";
|
||||
}
|
||||
code += "\n";
|
||||
|
||||
if (flags[FLAG_DISABLE_Z]) {
|
||||
|
@ -592,11 +591,6 @@ void ParticlesMaterial::_update_shader() {
|
|||
code += " if (base_scale < 0.000001) {\n";
|
||||
code += " base_scale = 0.000001;\n";
|
||||
code += " }\n";
|
||||
if (trail_size_modifier.is_valid()) {
|
||||
code += " if (trail_divisor > 1) {\n";
|
||||
code += " base_scale *= textureLod(trail_size_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0).r;\n";
|
||||
code += " }\n";
|
||||
}
|
||||
|
||||
code += " TRANSFORM[0].xyz *= base_scale;\n";
|
||||
code += " TRANSFORM[1].xyz *= base_scale;\n";
|
||||
|
@ -605,6 +599,33 @@ void ParticlesMaterial::_update_shader() {
|
|||
code += " VELOCITY.z = 0.0;\n";
|
||||
code += " TRANSFORM[3].z = 0.0;\n";
|
||||
}
|
||||
if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
|
||||
code += " int emit_count = 0;\n";
|
||||
switch (sub_emitter_mode) {
|
||||
case SUB_EMITTER_CONSTANT: {
|
||||
code += " float interval_from = CUSTOM.y * LIFETIME - DELTA;\n";
|
||||
code += " float interval_rem = sub_emitter_frequency - mod(interval_from,sub_emitter_frequency);\n";
|
||||
code += " if (DELTA >= interval_rem) emit_count = 1;\n";
|
||||
} break;
|
||||
case SUB_EMITTER_AT_COLLISION: {
|
||||
//not implemented yet
|
||||
} break;
|
||||
case SUB_EMITTER_AT_END: {
|
||||
//not implemented yet
|
||||
code += " float unit_delta = DELTA/LIFETIME;\n";
|
||||
code += " float end_time = CUSTOM.w * 0.95;\n"; // if we do at the end we might miss it, as it can just get deactivated by emitter
|
||||
code += " if (CUSTOM.y < end_time && (CUSTOM.y + unit_delta) >= end_time) emit_count = sub_emitter_amount_at_end;\n";
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
code += " for(int i=0;i<emit_count;i++) {\n";
|
||||
code += " uint flags = FLAG_EMIT_POSITION|FLAG_EMIT_ROT_SCALE;\n";
|
||||
code += " if (sub_emitter_keep_velocity) flags|=FLAG_EMIT_VELOCITY;\n";
|
||||
code += " emit_particle(TRANSFORM,VELOCITY,vec4(0.0),vec4(0.0),flags);\n";
|
||||
code += " }";
|
||||
}
|
||||
|
||||
code += " if (CUSTOM.y > CUSTOM.w) {";
|
||||
code += " ACTIVE = false;\n";
|
||||
code += " }\n";
|
||||
|
@ -951,41 +972,6 @@ int ParticlesMaterial::get_emission_point_count() const {
|
|||
return emission_point_count;
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_trail_divisor(int p_divisor) {
|
||||
trail_divisor = p_divisor;
|
||||
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_divisor, p_divisor);
|
||||
}
|
||||
|
||||
int ParticlesMaterial::get_trail_divisor() const {
|
||||
return trail_divisor;
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier) {
|
||||
trail_size_modifier = p_trail_size_modifier;
|
||||
|
||||
Ref<CurveTexture> curve = trail_size_modifier;
|
||||
if (curve.is_valid()) {
|
||||
curve->ensure_default_setup();
|
||||
}
|
||||
|
||||
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve);
|
||||
_queue_shader_change();
|
||||
}
|
||||
|
||||
Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const {
|
||||
return trail_size_modifier;
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) {
|
||||
trail_color_modifier = p_trail_color_modifier;
|
||||
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier);
|
||||
_queue_shader_change();
|
||||
}
|
||||
|
||||
Ref<GradientTexture> ParticlesMaterial::get_trail_color_modifier() const {
|
||||
return trail_color_modifier;
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) {
|
||||
gravity = p_gravity;
|
||||
Vector3 gset = gravity;
|
||||
|
@ -1038,11 +1024,54 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
|
|||
property.usage = 0;
|
||||
}
|
||||
|
||||
if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) {
|
||||
property.usage = 0;
|
||||
}
|
||||
|
||||
if (property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) {
|
||||
property.usage = 0;
|
||||
}
|
||||
|
||||
if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
|
||||
property.usage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) {
|
||||
sub_emitter_mode = p_sub_emitter_mode;
|
||||
_queue_shader_change();
|
||||
_change_notify();
|
||||
}
|
||||
|
||||
ParticlesMaterial::SubEmitterMode ParticlesMaterial::get_sub_emitter_mode() const {
|
||||
return sub_emitter_mode;
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_sub_emitter_frequency(float p_frequency) {
|
||||
sub_emitter_frequency = p_frequency;
|
||||
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pas delta instead of frequency, since its easier to compute
|
||||
}
|
||||
float ParticlesMaterial::get_sub_emitter_frequency() const {
|
||||
return sub_emitter_frequency;
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_sub_emitter_amount_at_end(int p_amount) {
|
||||
sub_emitter_amount_at_end = p_amount;
|
||||
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_amount_at_end, p_amount);
|
||||
}
|
||||
|
||||
int ParticlesMaterial::get_sub_emitter_amount_at_end() const {
|
||||
return sub_emitter_amount_at_end;
|
||||
}
|
||||
|
||||
void ParticlesMaterial::set_sub_emitter_keep_velocity(bool p_enable) {
|
||||
sub_emitter_keep_velocity = p_enable;
|
||||
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_keep_velocity, p_enable);
|
||||
}
|
||||
bool ParticlesMaterial::get_sub_emitter_keep_velocity() const {
|
||||
return sub_emitter_keep_velocity;
|
||||
}
|
||||
|
||||
Shader::Mode ParticlesMaterial::get_shader_mode() const {
|
||||
return Shader::MODE_PARTICLES;
|
||||
}
|
||||
|
@ -1096,27 +1125,27 @@ void ParticlesMaterial::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count);
|
||||
ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_trail_divisor", "divisor"), &ParticlesMaterial::set_trail_divisor);
|
||||
ClassDB::bind_method(D_METHOD("get_trail_divisor"), &ParticlesMaterial::get_trail_divisor);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_trail_size_modifier", "texture"), &ParticlesMaterial::set_trail_size_modifier);
|
||||
ClassDB::bind_method(D_METHOD("get_trail_size_modifier"), &ParticlesMaterial::get_trail_size_modifier);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_trail_color_modifier", "texture"), &ParticlesMaterial::set_trail_color_modifier);
|
||||
ClassDB::bind_method(D_METHOD("get_trail_color_modifier"), &ParticlesMaterial::get_trail_color_modifier);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity);
|
||||
ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "randomness"), &ParticlesMaterial::set_lifetime_randomness);
|
||||
ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &ParticlesMaterial::get_lifetime_randomness);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_sub_emitter_mode"), &ParticlesMaterial::get_sub_emitter_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_sub_emitter_mode", "mode"), &ParticlesMaterial::set_sub_emitter_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_sub_emitter_frequency"), &ParticlesMaterial::get_sub_emitter_frequency);
|
||||
ClassDB::bind_method(D_METHOD("set_sub_emitter_frequency", "hz"), &ParticlesMaterial::set_sub_emitter_frequency);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_end"), &ParticlesMaterial::get_sub_emitter_amount_at_end);
|
||||
ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_end", "amount"), &ParticlesMaterial::set_sub_emitter_amount_at_end);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticlesMaterial::get_sub_emitter_keep_velocity);
|
||||
ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticlesMaterial::set_sub_emitter_keep_velocity);
|
||||
|
||||
ADD_GROUP("Time", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
|
||||
ADD_GROUP("Trail", "trail_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_divisor", PROPERTY_HINT_RANGE, "1,1000000,1"), "set_trail_divisor", "get_trail_divisor");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_size_modifier", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_trail_size_modifier", "get_trail_size_modifier");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_color_modifier", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_trail_color_modifier", "get_trail_color_modifier");
|
||||
|
||||
ADD_GROUP("Emission Shape", "emission_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
|
||||
|
@ -1186,6 +1215,12 @@ void ParticlesMaterial::_bind_methods() {
|
|||
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
|
||||
|
||||
ADD_GROUP("Sub Emitter", "sub_emitter_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,AtEnd,AtCollision"), "set_sub_emitter_mode", "get_sub_emitter_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_sub_emitter_frequency", "get_sub_emitter_frequency");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity");
|
||||
|
||||
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
|
||||
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
|
||||
BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
|
||||
|
@ -1233,11 +1268,15 @@ ParticlesMaterial::ParticlesMaterial() :
|
|||
set_emission_shape(EMISSION_SHAPE_POINT);
|
||||
set_emission_sphere_radius(1);
|
||||
set_emission_box_extents(Vector3(1, 1, 1));
|
||||
set_trail_divisor(1);
|
||||
set_gravity(Vector3(0, -9.8, 0));
|
||||
set_lifetime_randomness(0);
|
||||
emission_point_count = 1;
|
||||
|
||||
set_sub_emitter_mode(SUB_EMITTER_DISABLED);
|
||||
set_sub_emitter_frequency(4);
|
||||
set_sub_emitter_amount_at_end(1);
|
||||
set_sub_emitter_keep_velocity(false);
|
||||
|
||||
for (int i = 0; i < PARAM_MAX; i++) {
|
||||
set_param_randomness(Parameter(i), 0);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,17 @@
|
|||
#ifndef PARTICLES_MATERIAL_H
|
||||
#define PARTICLES_MATERIAL_H
|
||||
|
||||
/*
|
||||
TODO:
|
||||
-Path following
|
||||
*Manual emission
|
||||
-Sub Emitters
|
||||
-Attractors
|
||||
-Emitter positions deformable by bones
|
||||
-Collision
|
||||
-Proper trails
|
||||
*/
|
||||
|
||||
class ParticlesMaterial : public Material {
|
||||
GDCLASS(ParticlesMaterial, Material);
|
||||
|
||||
|
@ -71,6 +82,14 @@ public:
|
|||
EMISSION_SHAPE_MAX
|
||||
};
|
||||
|
||||
enum SubEmitterMode {
|
||||
SUB_EMITTER_DISABLED,
|
||||
SUB_EMITTER_CONSTANT,
|
||||
SUB_EMITTER_AT_END,
|
||||
SUB_EMITTER_AT_COLLISION,
|
||||
SUB_EMITTER_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
union MaterialKey {
|
||||
struct {
|
||||
|
@ -78,10 +97,9 @@ private:
|
|||
uint32_t texture_color : 1;
|
||||
uint32_t flags : 4;
|
||||
uint32_t emission_shape : 2;
|
||||
uint32_t trail_size_texture : 1;
|
||||
uint32_t trail_color_texture : 1;
|
||||
uint32_t invalid_key : 1;
|
||||
uint32_t has_emission_color : 1;
|
||||
uint32_t sub_emitter : 2;
|
||||
};
|
||||
|
||||
uint32_t key;
|
||||
|
@ -116,9 +134,8 @@ private:
|
|||
|
||||
mk.texture_color = color_ramp.is_valid() ? 1 : 0;
|
||||
mk.emission_shape = emission_shape;
|
||||
mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0;
|
||||
mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0;
|
||||
mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
|
||||
mk.sub_emitter = sub_emitter_mode;
|
||||
|
||||
return mk;
|
||||
}
|
||||
|
@ -178,13 +195,13 @@ private:
|
|||
StringName emission_texture_normal;
|
||||
StringName emission_texture_color;
|
||||
|
||||
StringName trail_divisor;
|
||||
StringName trail_size_modifier;
|
||||
StringName trail_color_modifier;
|
||||
|
||||
StringName gravity;
|
||||
|
||||
StringName lifetime_randomness;
|
||||
|
||||
StringName sub_emitter_frequency;
|
||||
StringName sub_emitter_amount_at_end;
|
||||
StringName sub_emitter_keep_velocity;
|
||||
};
|
||||
|
||||
static ShaderNames *shader_names;
|
||||
|
@ -218,15 +235,14 @@ private:
|
|||
|
||||
bool anim_loop;
|
||||
|
||||
int trail_divisor;
|
||||
|
||||
Ref<CurveTexture> trail_size_modifier;
|
||||
Ref<GradientTexture> trail_color_modifier;
|
||||
|
||||
Vector3 gravity;
|
||||
|
||||
float lifetime_randomness;
|
||||
|
||||
SubEmitterMode sub_emitter_mode;
|
||||
float sub_emitter_frequency;
|
||||
int sub_emitter_amount_at_end;
|
||||
bool sub_emitter_keep_velocity;
|
||||
//do not save emission points here
|
||||
|
||||
protected:
|
||||
|
@ -277,15 +293,6 @@ public:
|
|||
Ref<Texture2D> get_emission_color_texture() const;
|
||||
int get_emission_point_count() const;
|
||||
|
||||
void set_trail_divisor(int p_divisor);
|
||||
int get_trail_divisor() const;
|
||||
|
||||
void set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier);
|
||||
Ref<CurveTexture> get_trail_size_modifier() const;
|
||||
|
||||
void set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier);
|
||||
Ref<GradientTexture> get_trail_color_modifier() const;
|
||||
|
||||
void set_gravity(const Vector3 &p_gravity);
|
||||
Vector3 get_gravity() const;
|
||||
|
||||
|
@ -296,6 +303,18 @@ public:
|
|||
static void finish_shaders();
|
||||
static void flush_changes();
|
||||
|
||||
void set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode);
|
||||
SubEmitterMode get_sub_emitter_mode() const;
|
||||
|
||||
void set_sub_emitter_frequency(float p_frequency);
|
||||
float get_sub_emitter_frequency() const;
|
||||
|
||||
void set_sub_emitter_amount_at_end(int p_amount);
|
||||
int get_sub_emitter_amount_at_end() const;
|
||||
|
||||
void set_sub_emitter_keep_velocity(bool p_enable);
|
||||
bool get_sub_emitter_keep_velocity() const;
|
||||
|
||||
RID get_shader_rid() const;
|
||||
|
||||
virtual Shader::Mode get_shader_mode() const override;
|
||||
|
@ -307,5 +326,6 @@ public:
|
|||
VARIANT_ENUM_CAST(ParticlesMaterial::Parameter)
|
||||
VARIANT_ENUM_CAST(ParticlesMaterial::Flags)
|
||||
VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape)
|
||||
VARIANT_ENUM_CAST(ParticlesMaterial::SubEmitterMode)
|
||||
|
||||
#endif // PARTICLES_MATERIAL_H
|
||||
|
|
|
@ -661,6 +661,8 @@ public:
|
|||
virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0;
|
||||
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0;
|
||||
virtual void particles_restart(RID p_particles) = 0;
|
||||
virtual void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) = 0;
|
||||
virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) = 0;
|
||||
|
||||
virtual bool particles_is_inactive(RID p_particles) const = 0;
|
||||
|
||||
|
|
|
@ -3119,52 +3119,47 @@ bool RasterizerStorageRD::particles_get_emitting(RID p_particles) {
|
|||
return particles->emitting;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::_particles_free_data(Particles *particles) {
|
||||
if (!particles->particle_buffer.is_valid()) {
|
||||
return;
|
||||
}
|
||||
RD::get_singleton()->free(particles->particle_buffer);
|
||||
RD::get_singleton()->free(particles->frame_params_buffer);
|
||||
RD::get_singleton()->free(particles->particle_instance_buffer);
|
||||
particles->particles_transforms_buffer_uniform_set = RID();
|
||||
particles->particle_buffer = RID();
|
||||
|
||||
if (particles->particles_sort_buffer.is_valid()) {
|
||||
RD::get_singleton()->free(particles->particles_sort_buffer);
|
||||
particles->particles_sort_buffer = RID();
|
||||
}
|
||||
|
||||
if (particles->emission_buffer != nullptr) {
|
||||
particles->emission_buffer = nullptr;
|
||||
particles->emission_buffer_data.clear();
|
||||
RD::get_singleton()->free(particles->emission_storage_buffer);
|
||||
particles->emission_storage_buffer = RID();
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_amount(RID p_particles, int p_amount) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->amount = p_amount;
|
||||
|
||||
if (particles->particle_buffer.is_valid()) {
|
||||
RD::get_singleton()->free(particles->particle_buffer);
|
||||
RD::get_singleton()->free(particles->frame_params_buffer);
|
||||
RD::get_singleton()->free(particles->particle_instance_buffer);
|
||||
particles->particles_transforms_buffer_uniform_set = RID();
|
||||
particles->particle_buffer = RID();
|
||||
|
||||
if (particles->particles_sort_buffer.is_valid()) {
|
||||
RD::get_singleton()->free(particles->particles_sort_buffer);
|
||||
particles->particles_sort_buffer = RID();
|
||||
}
|
||||
if (particles->amount == p_amount) {
|
||||
return;
|
||||
}
|
||||
|
||||
_particles_free_data(particles);
|
||||
|
||||
particles->amount = p_amount;
|
||||
|
||||
if (particles->amount > 0) {
|
||||
particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * p_amount);
|
||||
particles->frame_params_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticlesFrameParams) * 1);
|
||||
particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (3 + 1 + 1) * p_amount);
|
||||
//needs to clear it
|
||||
|
||||
{
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 0;
|
||||
u.ids.push_back(particles->frame_params_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 1;
|
||||
u.ids.push_back(particles->particle_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
|
||||
}
|
||||
|
||||
{
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
||||
|
@ -3290,6 +3285,79 @@ void RasterizerStorageRD::particles_restart(RID p_particles) {
|
|||
particles->restart_request = true;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::_particles_allocate_emission_buffer(Particles *particles) {
|
||||
ERR_FAIL_COND(particles->emission_buffer != nullptr);
|
||||
|
||||
particles->emission_buffer_data.resize(sizeof(ParticleEmissionBuffer::Data) * particles->amount + sizeof(uint32_t) * 4);
|
||||
zeromem(particles->emission_buffer_data.ptrw(), particles->emission_buffer_data.size());
|
||||
particles->emission_buffer = (ParticleEmissionBuffer *)particles->emission_buffer_data.ptrw();
|
||||
particles->emission_buffer->particle_max = particles->amount;
|
||||
|
||||
particles->emission_storage_buffer = RD::get_singleton()->storage_buffer_create(particles->emission_buffer_data.size(), particles->emission_buffer_data);
|
||||
|
||||
if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) {
|
||||
//will need to be re-created
|
||||
RD::get_singleton()->free(particles->particles_material_uniform_set);
|
||||
particles->particles_material_uniform_set = RID();
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
ERR_FAIL_COND(p_particles == p_subemitter_particles);
|
||||
|
||||
particles->sub_emitter = p_subemitter_particles;
|
||||
|
||||
if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) {
|
||||
RD::get_singleton()->free(particles->particles_material_uniform_set);
|
||||
particles->particles_material_uniform_set = RID(); //clear and force to re create sub emitting
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
ERR_FAIL_COND(particles->amount == 0);
|
||||
|
||||
if (particles->emitting) {
|
||||
particles->clear = true;
|
||||
particles->emitting = false;
|
||||
}
|
||||
|
||||
if (particles->emission_buffer == nullptr) {
|
||||
_particles_allocate_emission_buffer(particles);
|
||||
}
|
||||
|
||||
if (particles->inactive) {
|
||||
//in case it was inactive, make active again
|
||||
particles->inactive = false;
|
||||
particles->inactive_time = 0;
|
||||
}
|
||||
|
||||
int32_t idx = particles->emission_buffer->particle_count;
|
||||
if (idx < particles->emission_buffer->particle_max) {
|
||||
store_transform(p_transform, particles->emission_buffer->data[idx].xform);
|
||||
|
||||
particles->emission_buffer->data[idx].velocity[0] = p_velocity.x;
|
||||
particles->emission_buffer->data[idx].velocity[1] = p_velocity.y;
|
||||
particles->emission_buffer->data[idx].velocity[2] = p_velocity.z;
|
||||
|
||||
particles->emission_buffer->data[idx].custom[0] = p_custom.r;
|
||||
particles->emission_buffer->data[idx].custom[1] = p_custom.g;
|
||||
particles->emission_buffer->data[idx].custom[2] = p_custom.b;
|
||||
particles->emission_buffer->data[idx].custom[3] = p_custom.a;
|
||||
|
||||
particles->emission_buffer->data[idx].color[0] = p_color.r;
|
||||
particles->emission_buffer->data[idx].color[1] = p_color.g;
|
||||
particles->emission_buffer->data[idx].color[2] = p_color.b;
|
||||
particles->emission_buffer->data[idx].color[3] = p_color.a;
|
||||
|
||||
particles->emission_buffer->data[idx].flags = p_emit_flags;
|
||||
particles->emission_buffer->particle_count++;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_request_process(RID p_particles) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
@ -3375,6 +3443,54 @@ RID RasterizerStorageRD::particles_get_draw_pass_mesh(RID p_particles, int p_pas
|
|||
}
|
||||
|
||||
void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_delta) {
|
||||
if (p_particles->particles_material_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_particles->particles_material_uniform_set)) {
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 0;
|
||||
u.ids.push_back(p_particles->frame_params_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 1;
|
||||
u.ids.push_back(p_particles->particle_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 2;
|
||||
if (p_particles->emission_storage_buffer.is_valid()) {
|
||||
u.ids.push_back(p_particles->emission_storage_buffer);
|
||||
} else {
|
||||
u.ids.push_back(default_rd_storage_buffer);
|
||||
}
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 3;
|
||||
Particles *sub_emitter = particles_owner.getornull(p_particles->sub_emitter);
|
||||
if (sub_emitter) {
|
||||
if (sub_emitter->emission_buffer == nullptr) { //no emission buffer, allocate emission buffer
|
||||
_particles_allocate_emission_buffer(sub_emitter);
|
||||
}
|
||||
u.ids.push_back(sub_emitter->emission_storage_buffer);
|
||||
} else {
|
||||
u.ids.push_back(default_rd_storage_buffer);
|
||||
}
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
p_particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
|
||||
}
|
||||
|
||||
float new_phase = Math::fmod((float)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, (float)1.0);
|
||||
|
||||
ParticlesFrameParams &frame_params = p_particles->frame_params;
|
||||
|
@ -3416,6 +3532,36 @@ void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_del
|
|||
push_constant.lifetime = p_particles->lifetime;
|
||||
push_constant.trail_size = 1;
|
||||
push_constant.use_fractional_delta = p_particles->fractional_delta;
|
||||
push_constant.sub_emitter_mode = !p_particles->emitting && p_particles->emission_buffer && (p_particles->emission_buffer->particle_count > 0 || p_particles->force_sub_emit);
|
||||
|
||||
p_particles->force_sub_emit = false; //reset
|
||||
|
||||
Particles *sub_emitter = particles_owner.getornull(p_particles->sub_emitter);
|
||||
|
||||
if (sub_emitter && sub_emitter->emission_storage_buffer.is_valid()) {
|
||||
// print_line("updating subemitter buffer");
|
||||
int32_t zero[4] = { 0, sub_emitter->amount, 0, 0 };
|
||||
RD::get_singleton()->buffer_update(sub_emitter->emission_storage_buffer, 0, sizeof(uint32_t) * 4, zero, true);
|
||||
push_constant.can_emit = true;
|
||||
|
||||
if (sub_emitter->emitting) {
|
||||
sub_emitter->emitting = false;
|
||||
sub_emitter->clear = true; //will need to clear if it was emitting, sorry
|
||||
}
|
||||
//make sure the sub emitter processes particles too
|
||||
sub_emitter->inactive = false;
|
||||
sub_emitter->inactive_time = 0;
|
||||
|
||||
sub_emitter->force_sub_emit = true;
|
||||
|
||||
} else {
|
||||
push_constant.can_emit = false;
|
||||
}
|
||||
|
||||
if (p_particles->emission_buffer && p_particles->emission_buffer->particle_count) {
|
||||
RD::get_singleton()->buffer_update(p_particles->emission_storage_buffer, 0, sizeof(uint32_t) * 4 + sizeof(ParticleEmissionBuffer::Data) * p_particles->emission_buffer->particle_count, p_particles->emission_buffer, true);
|
||||
p_particles->emission_buffer->particle_count = 0;
|
||||
}
|
||||
|
||||
p_particles->clear = false;
|
||||
|
||||
|
@ -3617,9 +3763,6 @@ void RasterizerStorageRD::update_particles() {
|
|||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
|
||||
particle_update_list = particles->update_list;
|
||||
particles->update_list = nullptr;
|
||||
|
||||
particles->instance_dependency.instance_notify_changed(true, false); //make sure shadows are updated
|
||||
}
|
||||
}
|
||||
|
@ -6519,6 +6662,11 @@ bool RasterizerStorageRD::free(RID p_rid) {
|
|||
light->instance_dependency.instance_notify_deleted(p_rid);
|
||||
light_owner.free(p_rid);
|
||||
|
||||
} else if (particles_owner.owns(p_rid)) {
|
||||
Particles *particles = particles_owner.getornull(p_rid);
|
||||
_particles_free_data(particles);
|
||||
particles->instance_dependency.instance_notify_deleted(p_rid);
|
||||
particles_owner.free(p_rid);
|
||||
} else if (render_target_owner.owns(p_rid)) {
|
||||
RenderTarget *rt = render_target_owner.getornull(p_rid);
|
||||
|
||||
|
@ -7013,6 +7161,17 @@ RasterizerStorageRD::RasterizerStorageRD() {
|
|||
//actions.renames["GRAVITY"] = "current_gravity";
|
||||
actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform";
|
||||
actions.renames["RANDOM_SEED"] = "FRAME.random_seed";
|
||||
actions.renames["FLAG_EMIT_POSITION"] = "EMISSION_FLAG_HAS_POSITION";
|
||||
actions.renames["FLAG_EMIT_ROT_SCALE"] = "EMISSION_FLAG_HAS_ROTATION_SCALE";
|
||||
actions.renames["FLAG_EMIT_VELOCITY"] = "EMISSION_FLAG_HAS_VELOCITY";
|
||||
actions.renames["FLAG_EMIT_COLOR"] = "EMISSION_FLAG_HAS_COLOR";
|
||||
actions.renames["FLAG_EMIT_CUSTOM"] = "EMISSION_FLAG_HAS_CUSTOM";
|
||||
actions.renames["RESTART_POSITION"] = "restart_position";
|
||||
actions.renames["RESTART_ROT_SCALE"] = "restart_rotation_scale";
|
||||
actions.renames["RESTART_VELOCITY"] = "restart_velocity";
|
||||
actions.renames["RESTART_COLOR"] = "restart_color";
|
||||
actions.renames["RESTART_CUSTOM"] = "restart_custom";
|
||||
actions.renames["emit_particle"] = "emit_particle";
|
||||
|
||||
actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
|
||||
actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
|
||||
|
@ -7075,6 +7234,8 @@ RasterizerStorageRD::RasterizerStorageRD() {
|
|||
particles_shader.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 0);
|
||||
}
|
||||
|
||||
default_rd_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4);
|
||||
|
||||
{
|
||||
Vector<String> copy_modes;
|
||||
copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n");
|
||||
|
@ -7115,6 +7276,8 @@ RasterizerStorageRD::~RasterizerStorageRD() {
|
|||
}
|
||||
giprobe_sdf_shader.version_free(giprobe_sdf_shader_version);
|
||||
|
||||
RD::get_singleton()->free(default_rd_storage_buffer);
|
||||
|
||||
if (decal_atlas.textures.size()) {
|
||||
ERR_PRINT("Decal Atlas: " + itos(decal_atlas.textures.size()) + " textures were not removed from the atlas.");
|
||||
}
|
||||
|
|
|
@ -249,6 +249,7 @@ private:
|
|||
|
||||
RID default_rd_textures[DEFAULT_RD_TEXTURE_MAX];
|
||||
RID default_rd_samplers[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX];
|
||||
RID default_rd_storage_buffer;
|
||||
|
||||
/* DECAL ATLAS */
|
||||
|
||||
|
@ -482,6 +483,25 @@ private:
|
|||
float emission_transform[16];
|
||||
};
|
||||
|
||||
struct ParticleEmissionBufferData {
|
||||
};
|
||||
|
||||
struct ParticleEmissionBuffer {
|
||||
struct Data {
|
||||
float xform[16];
|
||||
float velocity[3];
|
||||
uint32_t flags;
|
||||
float color[4];
|
||||
float custom[4];
|
||||
};
|
||||
|
||||
int32_t particle_count;
|
||||
int32_t particle_max;
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
Data data[1]; //its 2020 and empty arrays are still non standard in C++
|
||||
};
|
||||
|
||||
struct Particles {
|
||||
bool inactive;
|
||||
float inactive_time;
|
||||
|
@ -515,6 +535,8 @@ private:
|
|||
bool dirty = false;
|
||||
Particles *update_list = nullptr;
|
||||
|
||||
RID sub_emitter;
|
||||
|
||||
float phase;
|
||||
float prev_phase;
|
||||
uint64_t prev_ticks;
|
||||
|
@ -530,8 +552,15 @@ private:
|
|||
|
||||
bool clear;
|
||||
|
||||
bool force_sub_emit = false;
|
||||
|
||||
Transform emission_transform;
|
||||
|
||||
Vector<uint8_t> emission_buffer_data;
|
||||
|
||||
ParticleEmissionBuffer *emission_buffer = nullptr;
|
||||
RID emission_storage_buffer;
|
||||
|
||||
Particles() :
|
||||
inactive(true),
|
||||
inactive_time(0.0),
|
||||
|
@ -562,6 +591,8 @@ private:
|
|||
};
|
||||
|
||||
void _particles_process(Particles *p_particles, float p_delta);
|
||||
void _particles_allocate_emission_buffer(Particles *particles);
|
||||
void _particles_free_data(Particles *particles);
|
||||
|
||||
struct ParticlesShader {
|
||||
struct PushConstant {
|
||||
|
@ -569,8 +600,11 @@ private:
|
|||
uint32_t clear;
|
||||
uint32_t total_particles;
|
||||
uint32_t trail_size;
|
||||
|
||||
uint32_t use_fractional_delta;
|
||||
uint32_t pad[3];
|
||||
uint32_t sub_emitter_mode;
|
||||
uint32_t can_emit;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
ParticlesShaderRD shader;
|
||||
|
@ -1650,6 +1684,8 @@ public:
|
|||
void particles_set_fixed_fps(RID p_particles, int p_fps);
|
||||
void particles_set_fractional_delta(RID p_particles, bool p_enable);
|
||||
void particles_restart(RID p_particles);
|
||||
void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
|
||||
void particles_set_subemitter(RID p_particles, RID p_subemitter_particles);
|
||||
|
||||
void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order);
|
||||
|
||||
|
|
|
@ -66,6 +66,38 @@ layout(set = 1, binding = 1, std430) restrict buffer Particles {
|
|||
}
|
||||
particles;
|
||||
|
||||
#define EMISSION_FLAG_HAS_POSITION 1
|
||||
#define EMISSION_FLAG_HAS_ROTATION_SCALE 2
|
||||
#define EMISSION_FLAG_HAS_VELOCITY 4
|
||||
#define EMISSION_FLAG_HAS_COLOR 8
|
||||
#define EMISSION_FLAG_HAS_CUSTOM 16
|
||||
|
||||
struct ParticleEmission {
|
||||
mat4 xform;
|
||||
vec3 velocity;
|
||||
uint flags;
|
||||
vec4 color;
|
||||
vec4 custom;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmission {
|
||||
int particle_count;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
ParticleEmission data[];
|
||||
}
|
||||
src_particles;
|
||||
|
||||
layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmission {
|
||||
int particle_count;
|
||||
int particle_max;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
ParticleEmission data[];
|
||||
}
|
||||
dst_particles;
|
||||
|
||||
/* SET 2: MATERIAL */
|
||||
|
||||
#ifdef USE_MATERIAL_UNIFORMS
|
||||
|
@ -82,7 +114,9 @@ layout(push_constant, binding = 0, std430) uniform Params {
|
|||
uint total_particles;
|
||||
uint trail_size;
|
||||
bool use_fractional_delta;
|
||||
uint pad[3];
|
||||
bool sub_emitter_mode;
|
||||
bool can_emit;
|
||||
uint pad;
|
||||
}
|
||||
params;
|
||||
|
||||
|
@ -93,6 +127,51 @@ uint hash(uint x) {
|
|||
return x;
|
||||
}
|
||||
|
||||
bool emit_particle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {
|
||||
if (!params.can_emit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool valid = false;
|
||||
|
||||
int dst_index = atomicAdd(dst_particles.particle_count, 1);
|
||||
|
||||
if (dst_index >= dst_particles.particle_max) {
|
||||
atomicAdd(dst_particles.particle_count, -1);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
valid = true;
|
||||
|
||||
int attempts = 256; // never trust compute
|
||||
while(attempts-- > 0) {
|
||||
dst_index = dst_particles.particle_count;
|
||||
if (dst_index == dst_particles.particle_max) {
|
||||
return false; //cant emit anymore
|
||||
}
|
||||
|
||||
if (atomicCompSwap(dst_particles.particle_count, dst_index, dst_index +1 ) != dst_index) {
|
||||
continue;
|
||||
}
|
||||
valid=true;
|
||||
break;
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
if (!valid) {
|
||||
return false; //gave up (attempts exhausted)
|
||||
}
|
||||
*/
|
||||
dst_particles.data[dst_index].xform = p_xform;
|
||||
dst_particles.data[dst_index].velocity = p_velocity;
|
||||
dst_particles.data[dst_index].color = p_color;
|
||||
dst_particles.data[dst_index].custom = p_custom;
|
||||
dst_particles.data[dst_index].flags = p_flags;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
COMPUTE_SHADER_GLOBALS
|
||||
|
@ -118,71 +197,19 @@ void main() {
|
|||
|
||||
float mass = 1.0;
|
||||
|
||||
float restart_phase = float(index) / float(params.total_particles);
|
||||
|
||||
if (FRAME.randomness > 0.0) {
|
||||
uint seed = FRAME.cycle;
|
||||
if (restart_phase >= FRAME.system_phase) {
|
||||
seed -= uint(1);
|
||||
}
|
||||
seed *= uint(params.total_particles);
|
||||
seed += uint(index);
|
||||
float random = float(hash(seed) % uint(65536)) / 65536.0;
|
||||
restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
|
||||
}
|
||||
|
||||
restart_phase *= (1.0 - FRAME.explosiveness);
|
||||
|
||||
bool restart = false;
|
||||
|
||||
if (FRAME.system_phase > FRAME.prev_system_phase) {
|
||||
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
|
||||
bool restart_position = false;
|
||||
bool restart_rotation_scale = false;
|
||||
bool restart_velocity = false;
|
||||
bool restart_color = false;
|
||||
bool restart_custom = false;
|
||||
|
||||
if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
|
||||
restart = true;
|
||||
if (params.use_fractional_delta) {
|
||||
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (FRAME.delta > 0.0) {
|
||||
if (restart_phase >= FRAME.prev_system_phase) {
|
||||
restart = true;
|
||||
if (params.use_fractional_delta) {
|
||||
local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
|
||||
}
|
||||
|
||||
} else if (restart_phase < FRAME.system_phase) {
|
||||
restart = true;
|
||||
if (params.use_fractional_delta) {
|
||||
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint current_cycle = FRAME.cycle;
|
||||
|
||||
if (FRAME.system_phase < restart_phase) {
|
||||
current_cycle -= uint(1);
|
||||
}
|
||||
|
||||
uint particle_number = current_cycle * uint(params.total_particles) + particle;
|
||||
|
||||
if (restart) {
|
||||
PARTICLE.is_active = FRAME.emitting;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_KEEP_DATA
|
||||
if (params.clear) {
|
||||
#else
|
||||
if (params.clear || restart) {
|
||||
#endif
|
||||
PARTICLE.color = vec4(1.0);
|
||||
PARTICLE.custom = vec4(0.0);
|
||||
PARTICLE.velocity = vec3(0.0);
|
||||
if (!restart) {
|
||||
PARTICLE.is_active = false;
|
||||
}
|
||||
PARTICLE.is_active = false;
|
||||
PARTICLE.xform = mat4(
|
||||
vec4(1.0, 0.0, 0.0, 0.0),
|
||||
vec4(0.0, 1.0, 0.0, 0.0),
|
||||
|
@ -190,6 +217,111 @@ void main() {
|
|||
vec4(0.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
|
||||
if (params.sub_emitter_mode) {
|
||||
if (!PARTICLE.is_active) {
|
||||
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
|
||||
|
||||
if (src_index >= 0) {
|
||||
PARTICLE.is_active = true;
|
||||
restart = true;
|
||||
|
||||
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
|
||||
PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
|
||||
} else {
|
||||
PARTICLE.xform[3] = vec4(0, 0, 0, 1);
|
||||
restart_position = true;
|
||||
}
|
||||
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
|
||||
PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
|
||||
PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
|
||||
PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
|
||||
} else {
|
||||
PARTICLE.xform[0] = vec4(1, 0, 0, 0);
|
||||
PARTICLE.xform[1] = vec4(0, 1, 0, 0);
|
||||
PARTICLE.xform[2] = vec4(0, 0, 1, 0);
|
||||
restart_rotation_scale = true;
|
||||
}
|
||||
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
|
||||
PARTICLE.velocity = src_particles.data[src_index].velocity;
|
||||
} else {
|
||||
PARTICLE.velocity = vec3(0);
|
||||
restart_velocity = true;
|
||||
}
|
||||
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
|
||||
PARTICLE.color = src_particles.data[src_index].color;
|
||||
} else {
|
||||
PARTICLE.color = vec4(1);
|
||||
restart_color = true;
|
||||
}
|
||||
|
||||
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
|
||||
PARTICLE.custom = src_particles.data[src_index].custom;
|
||||
} else {
|
||||
PARTICLE.custom = vec4(0);
|
||||
restart_custom = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (FRAME.emitting) {
|
||||
float restart_phase = float(index) / float(params.total_particles);
|
||||
|
||||
if (FRAME.randomness > 0.0) {
|
||||
uint seed = FRAME.cycle;
|
||||
if (restart_phase >= FRAME.system_phase) {
|
||||
seed -= uint(1);
|
||||
}
|
||||
seed *= uint(params.total_particles);
|
||||
seed += uint(index);
|
||||
float random = float(hash(seed) % uint(65536)) / 65536.0;
|
||||
restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
|
||||
}
|
||||
|
||||
restart_phase *= (1.0 - FRAME.explosiveness);
|
||||
|
||||
if (FRAME.system_phase > FRAME.prev_system_phase) {
|
||||
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
|
||||
|
||||
if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
|
||||
restart = true;
|
||||
if (params.use_fractional_delta) {
|
||||
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (FRAME.delta > 0.0) {
|
||||
if (restart_phase >= FRAME.prev_system_phase) {
|
||||
restart = true;
|
||||
if (params.use_fractional_delta) {
|
||||
local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
|
||||
}
|
||||
|
||||
} else if (restart_phase < FRAME.system_phase) {
|
||||
restart = true;
|
||||
if (params.use_fractional_delta) {
|
||||
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint current_cycle = FRAME.cycle;
|
||||
|
||||
if (FRAME.system_phase < restart_phase) {
|
||||
current_cycle -= uint(1);
|
||||
}
|
||||
|
||||
uint particle_number = current_cycle * uint(params.total_particles) + particle;
|
||||
|
||||
if (restart) {
|
||||
PARTICLE.is_active = FRAME.emitting;
|
||||
restart_position = true;
|
||||
restart_rotation_scale = true;
|
||||
restart_velocity = true;
|
||||
restart_color = true;
|
||||
restart_custom = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (PARTICLE.is_active) {
|
||||
/* clang-format off */
|
||||
|
||||
|
|
|
@ -442,6 +442,8 @@ public:
|
|||
BIND1R(bool, particles_is_inactive, RID)
|
||||
BIND1(particles_request_process, RID)
|
||||
BIND1(particles_restart, RID)
|
||||
BIND6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t)
|
||||
BIND2(particles_set_subemitter, RID, RID)
|
||||
|
||||
BIND2(particles_set_draw_order, RID, RS::ParticlesDrawOrder)
|
||||
|
||||
|
|
|
@ -360,11 +360,14 @@ public:
|
|||
FUNC1(particles_request_process, RID)
|
||||
FUNC1(particles_restart, RID)
|
||||
|
||||
FUNC6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t)
|
||||
|
||||
FUNC2(particles_set_draw_order, RID, RS::ParticlesDrawOrder)
|
||||
|
||||
FUNC2(particles_set_draw_passes, RID, int)
|
||||
FUNC3(particles_set_draw_pass_mesh, RID, int, RID)
|
||||
FUNC2(particles_set_emission_transform, RID, const Transform &)
|
||||
FUNC2(particles_set_subemitter, RID, RID)
|
||||
|
||||
FUNC1R(AABB, particles_get_current_aabb, RID)
|
||||
|
||||
|
|
|
@ -920,13 +920,13 @@ void ShaderLanguage::clear() {
|
|||
}
|
||||
}
|
||||
|
||||
bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) {
|
||||
if (p_builtin_types.has(p_identifier)) {
|
||||
bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) {
|
||||
if (p_function_info.built_ins.has(p_identifier)) {
|
||||
if (r_data_type) {
|
||||
*r_data_type = p_builtin_types[p_identifier].type;
|
||||
*r_data_type = p_function_info.built_ins[p_identifier].type;
|
||||
}
|
||||
if (r_is_const) {
|
||||
*r_is_const = p_builtin_types[p_identifier].constant;
|
||||
*r_is_const = p_function_info.built_ins[p_identifier].constant;
|
||||
}
|
||||
if (r_type) {
|
||||
*r_type = IDENTIFIER_BUILTIN_VAR;
|
||||
|
@ -935,6 +935,20 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
|
|||
return true;
|
||||
}
|
||||
|
||||
if (p_function_info.stage_functions.has(p_identifier)) {
|
||||
if (r_data_type) {
|
||||
*r_data_type = p_function_info.stage_functions[p_identifier].return_type;
|
||||
}
|
||||
if (r_is_const) {
|
||||
*r_is_const = true;
|
||||
}
|
||||
if (r_type) {
|
||||
*r_type = IDENTIFIER_FUNCTION;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FunctionNode *function = nullptr;
|
||||
|
||||
while (p_block) {
|
||||
|
@ -2152,7 +2166,7 @@ const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[]
|
|||
{ nullptr, 0 }
|
||||
};
|
||||
|
||||
bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) {
|
||||
bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) {
|
||||
ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, false);
|
||||
|
||||
Vector<DataType> args;
|
||||
|
@ -2169,6 +2183,30 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin
|
|||
|
||||
int argcount = args.size();
|
||||
|
||||
if (p_function_info.stage_functions.has(name)) {
|
||||
//stage based function
|
||||
const StageFunctionInfo &sf = p_function_info.stage_functions[name];
|
||||
if (argcount != sf.arguments.size()) {
|
||||
_set_error(vformat("Invalid number of arguments when calling stage function '%s', which expects %d arguments.", String(name), sf.arguments.size()));
|
||||
return false;
|
||||
}
|
||||
//validate arguments
|
||||
for (int i = 0; i < argcount; i++) {
|
||||
if (args[i] != sf.arguments[i].type) {
|
||||
_set_error(vformat("Invalid argument type when calling stage function '%s', type expected is '%s'.", String(name), String(get_datatype_name(sf.arguments[i].type))));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_ret_type) {
|
||||
*r_ret_type = sf.return_type;
|
||||
}
|
||||
if (r_ret_type_str) {
|
||||
*r_ret_type_str = "";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool failed_builtin = false;
|
||||
bool unsupported_builtin = false;
|
||||
int builtin_idx = 0;
|
||||
|
@ -2241,8 +2279,8 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin
|
|||
if (shader->uniforms.has(varname)) {
|
||||
fail = true;
|
||||
} else {
|
||||
if (p_builtin_types.has(varname)) {
|
||||
BuiltInInfo info = p_builtin_types[varname];
|
||||
if (p_function_info.built_ins.has(varname)) {
|
||||
BuiltInInfo info = p_function_info.built_ins[varname];
|
||||
if (info.constant) {
|
||||
fail = true;
|
||||
}
|
||||
|
@ -2278,7 +2316,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<Strin
|
|||
const BlockNode *b = p_block;
|
||||
bool valid = false;
|
||||
while (b) {
|
||||
if (b->variables.has(var_name) || p_builtin_types.has(var_name)) {
|
||||
if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
|
@ -2492,7 +2530,7 @@ bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg) {
|
||||
bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg) {
|
||||
TkPos pos = _get_tkpos();
|
||||
Token tk = _get_token();
|
||||
|
||||
|
@ -2514,7 +2552,7 @@ bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<Str
|
|||
}
|
||||
}
|
||||
|
||||
Node *arg = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *arg = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
|
||||
if (!arg) {
|
||||
return false;
|
||||
|
@ -3053,16 +3091,16 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message) {
|
||||
bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) {
|
||||
if (p_node->type == Node::TYPE_OPERATOR) {
|
||||
OperatorNode *op = static_cast<OperatorNode *>(p_node);
|
||||
|
||||
if (op->op == OP_INDEX) {
|
||||
return _validate_assign(op->arguments[0], p_builtin_types, r_message);
|
||||
return _validate_assign(op->arguments[0], p_function_info, r_message);
|
||||
|
||||
} else if (_is_operator_assign(op->op)) {
|
||||
//chained assignment
|
||||
return _validate_assign(op->arguments[1], p_builtin_types, r_message);
|
||||
return _validate_assign(op->arguments[1], p_function_info, r_message);
|
||||
|
||||
} else if (op->op == OP_CALL) {
|
||||
if (r_message) {
|
||||
|
@ -3081,7 +3119,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI
|
|||
return false;
|
||||
}
|
||||
|
||||
return _validate_assign(member->owner, p_builtin_types, r_message);
|
||||
return _validate_assign(member->owner, p_function_info, r_message);
|
||||
|
||||
} else if (p_node->type == Node::TYPE_VARIABLE) {
|
||||
VariableNode *var = static_cast<VariableNode *>(p_node);
|
||||
|
@ -3107,7 +3145,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!(p_builtin_types.has(var->name) && p_builtin_types[var->name].constant)) {
|
||||
if (!(p_function_info.built_ins.has(var->name) && p_function_info.built_ins[var->name].constant)) {
|
||||
return true;
|
||||
}
|
||||
} else if (p_node->type == Node::TYPE_ARRAY) {
|
||||
|
@ -3204,7 +3242,7 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa
|
|||
ERR_FAIL_V(false); //bug? function not found
|
||||
}
|
||||
|
||||
ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) {
|
||||
ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info) {
|
||||
Vector<Expression> expression;
|
||||
|
||||
//Vector<TokenType> operators;
|
||||
|
@ -3220,7 +3258,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
if (tk.type == TK_PARENTHESIS_OPEN) {
|
||||
//handle subexpression
|
||||
|
||||
expr = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
expr = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!expr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3293,7 +3331,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
int carg = -1;
|
||||
|
||||
bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg);
|
||||
bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg);
|
||||
|
||||
if (carg >= 0) {
|
||||
completion_type = COMPLETION_CALL_ARGUMENTS;
|
||||
|
@ -3307,7 +3345,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) {
|
||||
if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
|
||||
_set_error("No matching constructor found for: '" + String(funcname->name) + "'");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3383,7 +3421,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
} else {
|
||||
_set_tkpos(pos2);
|
||||
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
|
||||
_set_error("Expected single integer constant > 0");
|
||||
return nullptr;
|
||||
|
@ -3444,7 +3482,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization
|
||||
while (true) {
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3484,7 +3522,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
nexpr = an;
|
||||
} else {
|
||||
nexpr = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
nexpr = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!nexpr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3526,7 +3564,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
int carg = -1;
|
||||
|
||||
bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg);
|
||||
bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg);
|
||||
|
||||
// Check if block has a variable with the same name as function to prevent shader crash.
|
||||
ShaderLanguage::BlockNode *bnode = p_block;
|
||||
|
@ -3568,7 +3606,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) {
|
||||
if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
|
||||
_set_error("No matching function found for: '" + String(funcname->name) + "'");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3620,8 +3658,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
} else if (shader->uniforms.has(varname)) {
|
||||
error = true;
|
||||
} else {
|
||||
if (p_builtin_types.has(varname)) {
|
||||
BuiltInInfo info = p_builtin_types[varname];
|
||||
if (p_function_info.built_ins.has(varname)) {
|
||||
BuiltInInfo info = p_function_info.built_ins[varname];
|
||||
if (info.constant) {
|
||||
error = true;
|
||||
}
|
||||
|
@ -3653,7 +3691,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (p_builtin_types.has(varname)) {
|
||||
} else if (p_function_info.built_ins.has(varname)) {
|
||||
//a built-in
|
||||
if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) {
|
||||
return nullptr;
|
||||
|
@ -3708,7 +3746,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!_find_identifier(p_block, false, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) {
|
||||
if (!_find_identifier(p_block, false, p_function_info, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) {
|
||||
_set_error("Unknown identifier in expression: " + String(identifier));
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3733,7 +3771,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
if (tk.type == TK_PERIOD) {
|
||||
completion_class = TAG_ARRAY;
|
||||
p_block->block_tag = SubClassTag::TAG_ARRAY;
|
||||
call_expression = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
call_expression = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
p_block->block_tag = SubClassTag::TAG_GLOBAL;
|
||||
if (!call_expression) {
|
||||
return nullptr;
|
||||
|
@ -3741,7 +3779,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
data_type = call_expression->get_datatype();
|
||||
} else { // indexing
|
||||
|
||||
index_expression = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
index_expression = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!index_expression) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4121,7 +4159,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
_set_error("Nested array length() is not yet implemented");
|
||||
return nullptr;
|
||||
} else if (tk.type == TK_BRACKET_OPEN) {
|
||||
Node *index_expression = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *index_expression = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!index_expression) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4170,7 +4208,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
*/
|
||||
} else if (tk.type == TK_BRACKET_OPEN) {
|
||||
Node *index = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *index = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4312,7 +4350,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!_validate_assign(expr, p_builtin_types)) {
|
||||
if (!_validate_assign(expr, p_function_info)) {
|
||||
_set_error("Invalid use of increment/decrement operator in constant expression.");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4610,7 +4648,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
for (int i = expr_pos - 1; i >= next_op; i--) {
|
||||
OperatorNode *op = alloc_node<OperatorNode>();
|
||||
op->op = expression[i].op;
|
||||
if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_builtin_types)) {
|
||||
if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_function_info)) {
|
||||
_set_error("Can't use increment/decrement operator in constant expression.");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4684,7 +4722,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
if (_is_operator_assign(op->op)) {
|
||||
String assign_message;
|
||||
if (!_validate_assign(expression[next_op - 1].node, p_builtin_types, &assign_message)) {
|
||||
if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) {
|
||||
_set_error(assign_message);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4838,8 +4876,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
|
|||
return p_node;
|
||||
}
|
||||
|
||||
ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) {
|
||||
ShaderLanguage::Node *expr = _parse_expression(p_block, p_builtin_types);
|
||||
ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info) {
|
||||
ShaderLanguage::Node *expr = _parse_expression(p_block, p_function_info);
|
||||
if (!expr) { //errored
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4849,7 +4887,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_
|
|||
return expr;
|
||||
}
|
||||
|
||||
Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one, bool p_can_break, bool p_can_continue) {
|
||||
Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one, bool p_can_break, bool p_can_continue) {
|
||||
while (true) {
|
||||
TkPos pos = _get_tkpos();
|
||||
|
||||
|
@ -4933,7 +4971,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
|
||||
StringName name = tk.text;
|
||||
ShaderLanguage::IdentifierType itype;
|
||||
if (_find_identifier(p_block, true, p_builtin_types, name, (ShaderLanguage::DataType *)nullptr, &itype)) {
|
||||
if (_find_identifier(p_block, true, p_function_info, name, (ShaderLanguage::DataType *)nullptr, &itype)) {
|
||||
if (itype != IDENTIFIER_FUNCTION) {
|
||||
_set_error("Redefinition of '" + String(name) + "'");
|
||||
return ERR_PARSE_ERROR;
|
||||
|
@ -5052,7 +5090,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
} else {
|
||||
_set_tkpos(pos2);
|
||||
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
|
||||
_set_error("Expected single integer constant > 0");
|
||||
return ERR_PARSE_ERROR;
|
||||
|
@ -5133,7 +5171,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
|
||||
if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
|
||||
while (true) {
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5205,7 +5243,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
decl.initializer = nullptr;
|
||||
|
||||
//variable created with assignment! must parse an expression
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5265,7 +5303,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
//a sub block, just because..
|
||||
BlockNode *block = alloc_node<BlockNode>();
|
||||
block->parent_block = p_block;
|
||||
if (_parse_block(block, p_builtin_types, false, p_can_break, p_can_continue) != OK) {
|
||||
if (_parse_block(block, p_function_info, false, p_can_break, p_can_continue) != OK) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
p_block->statements.push_back(block);
|
||||
|
@ -5279,7 +5317,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
|
||||
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
|
||||
cf->flow_op = FLOW_OP_IF;
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5301,7 +5339,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
cf->blocks.push_back(block);
|
||||
p_block->statements.push_back(cf);
|
||||
|
||||
Error err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue);
|
||||
Error err = _parse_block(block, p_function_info, true, p_can_break, p_can_continue);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
@ -5312,7 +5350,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
block = alloc_node<BlockNode>();
|
||||
block->parent_block = p_block;
|
||||
cf->blocks.push_back(block);
|
||||
err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue);
|
||||
err = _parse_block(block, p_function_info, true, p_can_break, p_can_continue);
|
||||
|
||||
} else {
|
||||
_set_tkpos(pos); //rollback
|
||||
|
@ -5331,7 +5369,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
}
|
||||
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
|
||||
cf->flow_op = FLOW_OP_SWITCH;
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5359,7 +5397,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
int prev_type = TK_CF_CASE;
|
||||
while (true) { // Go-through multiple cases.
|
||||
|
||||
if (_parse_block(switch_block, p_builtin_types, true, true, false) != OK) {
|
||||
if (_parse_block(switch_block, p_function_info, true, true, false) != OK) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
pos = _get_tkpos();
|
||||
|
@ -5460,7 +5498,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
cf->blocks.push_back(case_block);
|
||||
p_block->statements.push_back(cf);
|
||||
|
||||
Error err = _parse_block(case_block, p_builtin_types, false, true, false);
|
||||
Error err = _parse_block(case_block, p_function_info, false, true, false);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
@ -5494,7 +5532,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
cf->blocks.push_back(default_block);
|
||||
p_block->statements.push_back(cf);
|
||||
|
||||
Error err = _parse_block(default_block, p_builtin_types, false, true, false);
|
||||
Error err = _parse_block(default_block, p_function_info, false, true, false);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
@ -5511,7 +5549,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
do_block = alloc_node<BlockNode>();
|
||||
do_block->parent_block = p_block;
|
||||
|
||||
Error err = _parse_block(do_block, p_builtin_types, true, true, true);
|
||||
Error err = _parse_block(do_block, p_function_info, true, true, true);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
@ -5535,7 +5573,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
} else {
|
||||
cf->flow_op = FLOW_OP_WHILE;
|
||||
}
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5552,7 +5590,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
cf->blocks.push_back(block);
|
||||
p_block->statements.push_back(cf);
|
||||
|
||||
Error err = _parse_block(block, p_builtin_types, true, true, true);
|
||||
Error err = _parse_block(block, p_function_info, true, true, true);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
@ -5583,11 +5621,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
init_block->parent_block = p_block;
|
||||
init_block->single_statement = true;
|
||||
cf->blocks.push_back(init_block);
|
||||
if (_parse_block(init_block, p_builtin_types, true, false, false) != OK) {
|
||||
if (_parse_block(init_block, p_function_info, true, false, false) != OK) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
Node *n = _parse_and_reduce_expression(init_block, p_builtin_types);
|
||||
Node *n = _parse_and_reduce_expression(init_block, p_function_info);
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5605,7 +5643,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
|
||||
cf->expressions.push_back(n);
|
||||
|
||||
n = _parse_and_reduce_expression(init_block, p_builtin_types);
|
||||
n = _parse_and_reduce_expression(init_block, p_function_info);
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5623,7 +5661,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
cf->blocks.push_back(block);
|
||||
p_block->statements.push_back(cf);
|
||||
|
||||
Error err = _parse_block(block, p_builtin_types, true, true, true);
|
||||
Error err = _parse_block(block, p_function_info, true, true, true);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
@ -5659,7 +5697,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
}
|
||||
} else {
|
||||
_set_tkpos(pos); //rollback, wants expression
|
||||
Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!expr) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -5761,7 +5799,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||
} else {
|
||||
//nothing else, so expression
|
||||
_set_tkpos(pos); //rollback
|
||||
Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types);
|
||||
Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
|
||||
if (!expr) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -6102,7 +6140,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
|
||||
name = tk.text;
|
||||
|
||||
if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) {
|
||||
if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
|
||||
_set_error("Redefinition of '" + String(name) + "'");
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -6351,7 +6389,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
//reset scope for next uniform
|
||||
|
||||
if (tk.type == TK_OP_ASSIGN) {
|
||||
Node *expr = _parse_and_reduce_expression(nullptr, Map<StringName, BuiltInInfo>());
|
||||
Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo());
|
||||
if (!expr) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -6476,7 +6514,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) {
|
||||
if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
|
||||
_set_error("Redefinition of '" + String(name) + "'");
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -6589,7 +6627,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
} else {
|
||||
_set_tkpos(pos2);
|
||||
|
||||
Node *n = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
|
||||
Node *n = _parse_and_reduce_expression(NULL, FunctionInfo());
|
||||
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
|
||||
_set_error("Expected single integer constant > 0");
|
||||
return ERR_PARSE_ERROR;
|
||||
|
@ -6670,7 +6708,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
|
||||
if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
|
||||
while (true) {
|
||||
Node *n = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
|
||||
Node *n = _parse_and_reduce_expression(NULL, FunctionInfo());
|
||||
if (!n) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -6725,7 +6763,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
constant.initializer = static_cast<ConstantNode *>(expr);
|
||||
} else {
|
||||
//variable created with assignment! must parse an expression
|
||||
Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
|
||||
Node *expr = _parse_and_reduce_expression(NULL, FunctionInfo());
|
||||
if (!expr)
|
||||
return ERR_PARSE_ERROR;
|
||||
if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) {
|
||||
|
@ -6762,7 +6800,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
}
|
||||
|
||||
name = tk.text;
|
||||
if (_find_identifier(nullptr, false, Map<StringName, BuiltInInfo>(), name)) {
|
||||
if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
|
||||
_set_error("Redefinition of '" + String(name) + "'");
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
@ -6785,14 +6823,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
break;
|
||||
}
|
||||
|
||||
Map<StringName, BuiltInInfo> builtin_types;
|
||||
FunctionInfo builtins;
|
||||
if (p_functions.has(name)) {
|
||||
builtin_types = p_functions[name].built_ins;
|
||||
builtins = p_functions[name];
|
||||
}
|
||||
|
||||
if (p_functions.has("global")) { // Adds global variables: 'TIME'
|
||||
for (Map<StringName, BuiltInInfo>::Element *E = p_functions["global"].built_ins.front(); E; E = E->next()) {
|
||||
builtin_types.insert(E->key(), E->value());
|
||||
builtins.built_ins.insert(E->key(), E->value());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6915,7 +6953,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
pname = tk.text;
|
||||
|
||||
ShaderLanguage::IdentifierType itype;
|
||||
if (_find_identifier(func_node->body, false, builtin_types, pname, (ShaderLanguage::DataType *)nullptr, &itype)) {
|
||||
if (_find_identifier(func_node->body, false, builtins, pname, (ShaderLanguage::DataType *)nullptr, &itype)) {
|
||||
if (itype != IDENTIFIER_FUNCTION) {
|
||||
_set_error("Redefinition of '" + String(pname) + "'");
|
||||
return ERR_PARSE_ERROR;
|
||||
|
@ -6977,7 +7015,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
|
||||
current_function = name;
|
||||
|
||||
Error err = _parse_block(func_node->body, builtin_types);
|
||||
Error err = _parse_block(func_node->body, builtins);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -730,8 +730,25 @@ public:
|
|||
constant(p_constant) {}
|
||||
};
|
||||
|
||||
struct StageFunctionInfo {
|
||||
struct Argument {
|
||||
StringName name;
|
||||
DataType type;
|
||||
|
||||
Argument(const StringName &p_name = StringName(), DataType p_type = TYPE_VOID) {
|
||||
name = p_name;
|
||||
type = p_type;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Argument> arguments;
|
||||
DataType return_type = TYPE_VOID;
|
||||
};
|
||||
|
||||
struct FunctionInfo {
|
||||
Map<StringName, BuiltInInfo> built_ins;
|
||||
Map<StringName, StageFunctionInfo> stage_functions;
|
||||
|
||||
bool can_discard;
|
||||
};
|
||||
static bool has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name);
|
||||
|
@ -802,9 +819,9 @@ private:
|
|||
IDENTIFIER_CONSTANT,
|
||||
};
|
||||
|
||||
bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr);
|
||||
bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr);
|
||||
bool _is_operator_assign(Operator p_op) const;
|
||||
bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message = nullptr);
|
||||
bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr);
|
||||
bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr);
|
||||
|
||||
struct BuiltinFuncDef {
|
||||
|
@ -837,16 +854,16 @@ private:
|
|||
Error _validate_datatype(DataType p_type);
|
||||
bool _compare_datatypes_in_nodes(Node *a, Node *b) const;
|
||||
|
||||
bool _validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str);
|
||||
bool _parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg = nullptr);
|
||||
bool _validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str);
|
||||
bool _parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg = nullptr);
|
||||
bool _propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat);
|
||||
bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin);
|
||||
|
||||
Node *_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types);
|
||||
Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info);
|
||||
ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node);
|
||||
|
||||
Node *_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types);
|
||||
Error _parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false);
|
||||
Node *_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info);
|
||||
Error _parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false);
|
||||
String _get_shader_type_list(const Set<String> &p_shader_types) const;
|
||||
String _get_qualifier_str(ArgumentQualifier p_qualifier) const;
|
||||
|
||||
|
|
|
@ -283,8 +283,29 @@ ShaderTypes::ShaderTypes() {
|
|||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_POSITION"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_ROT_SCALE"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_VELOCITY"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_COLOR"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["FLAG_EMIT_CUSTOM"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].can_discard = false;
|
||||
|
||||
{
|
||||
ShaderLanguage::StageFunctionInfo emit_vertex_func;
|
||||
emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("xform", ShaderLanguage::TYPE_MAT4));
|
||||
emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("velocity", ShaderLanguage::TYPE_VEC3));
|
||||
emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("color", ShaderLanguage::TYPE_VEC4));
|
||||
emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("custom", ShaderLanguage::TYPE_VEC4));
|
||||
emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("flags", ShaderLanguage::TYPE_UINT));
|
||||
emit_vertex_func.return_type = ShaderLanguage::TYPE_BOOL; //whether it could emit
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].stage_functions["emit_particle"] = emit_vertex_func;
|
||||
}
|
||||
|
||||
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_force");
|
||||
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity");
|
||||
shader_modes[RS::SHADER_PARTICLES].modes.push_back("keep_data");
|
||||
|
|
|
@ -563,7 +563,7 @@ public:
|
|||
|
||||
virtual RID particles_create() = 0;
|
||||
|
||||
virtual void particles_set_emitting(RID p_particles, bool p_emitting) = 0;
|
||||
virtual void particles_set_emitting(RID p_particles, bool p_enable) = 0;
|
||||
virtual bool particles_get_emitting(RID p_particles) = 0;
|
||||
virtual void particles_set_amount(RID p_particles, int p_amount) = 0;
|
||||
virtual void particles_set_lifetime(RID p_particles, float p_lifetime) = 0;
|
||||
|
@ -581,6 +581,18 @@ public:
|
|||
virtual void particles_request_process(RID p_particles) = 0;
|
||||
virtual void particles_restart(RID p_particles) = 0;
|
||||
|
||||
virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) = 0;
|
||||
|
||||
enum ParticlesEmitFlags {
|
||||
PARTICLES_EMIT_FLAG_POSITION = 1,
|
||||
PARTICLES_EMIT_FLAG_ROTATION_SCALE = 2,
|
||||
PARTICLES_EMIT_FLAG_VELOCITY = 4,
|
||||
PARTICLES_EMIT_FLAG_COLOR = 8,
|
||||
PARTICLES_EMIT_FLAG_CUSTOM = 16
|
||||
};
|
||||
|
||||
virtual void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) = 0;
|
||||
|
||||
enum ParticlesDrawOrder {
|
||||
PARTICLES_DRAW_ORDER_INDEX,
|
||||
PARTICLES_DRAW_ORDER_LIFETIME,
|
||||
|
|
Loading…
Reference in New Issue