commit
d0913370b5
16 changed files with 1919 additions and 63 deletions
|
@ -277,7 +277,7 @@ void ParticlesMaterial::_update_shader() {
|
|||
code += "}\n";
|
||||
code += "\n";
|
||||
|
||||
code += "void vertex() {\n";
|
||||
code += "void compute() {\n";
|
||||
code += " uint base_number = NUMBER / uint(trail_divisor);\n";
|
||||
code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
|
||||
code += " float angle_rand = rand_from_seed(alt_seed);\n";
|
||||
|
|
|
@ -678,6 +678,8 @@ public:
|
|||
virtual int particles_get_draw_passes(RID p_particles) const = 0;
|
||||
virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const = 0;
|
||||
|
||||
virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis) = 0;
|
||||
|
||||
/* GLOBAL VARIABLES */
|
||||
|
||||
virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) = 0;
|
||||
|
|
|
@ -1273,6 +1273,76 @@ void RasterizerEffectsRD::filter_shadow(RID p_shadow, RID p_backing_shadow, cons
|
|||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_source_rect.size.width, p_source_rect.size.height, 1, 8, 8, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerEffectsRD::sort_buffer(RID p_uniform_set, int p_size) {
|
||||
Sort::PushConstant push_constant;
|
||||
push_constant.total_elements = p_size;
|
||||
|
||||
bool done = true;
|
||||
|
||||
int numThreadGroups = ((p_size - 1) >> 9) + 1;
|
||||
|
||||
if (numThreadGroups > 1) {
|
||||
done = false;
|
||||
}
|
||||
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sort.pipelines[SORT_MODE_BLOCK]);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_uniform_set, 1);
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(Sort::PushConstant));
|
||||
RD::get_singleton()->compute_list_dispatch(compute_list, numThreadGroups, 1, 1);
|
||||
|
||||
int presorted = 512;
|
||||
|
||||
while (!done) {
|
||||
RD::get_singleton()->compute_list_add_barrier(compute_list);
|
||||
|
||||
done = true;
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sort.pipelines[SORT_MODE_STEP]);
|
||||
|
||||
numThreadGroups = 0;
|
||||
|
||||
if (p_size > presorted) {
|
||||
if (p_size > presorted * 2) {
|
||||
done = false;
|
||||
}
|
||||
|
||||
int pow2 = presorted;
|
||||
while (pow2 < p_size) {
|
||||
pow2 *= 2;
|
||||
}
|
||||
numThreadGroups = pow2 >> 9;
|
||||
}
|
||||
|
||||
unsigned int nMergeSize = presorted * 2;
|
||||
|
||||
for (unsigned int nMergeSubSize = nMergeSize >> 1; nMergeSubSize > 256; nMergeSubSize = nMergeSubSize >> 1) {
|
||||
push_constant.job_params[0] = nMergeSubSize;
|
||||
if (nMergeSubSize == nMergeSize >> 1) {
|
||||
push_constant.job_params[1] = (2 * nMergeSubSize - 1);
|
||||
push_constant.job_params[2] = -1;
|
||||
} else {
|
||||
push_constant.job_params[1] = nMergeSubSize;
|
||||
push_constant.job_params[2] = 1;
|
||||
}
|
||||
push_constant.job_params[3] = 0;
|
||||
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(Sort::PushConstant));
|
||||
RD::get_singleton()->compute_list_dispatch(compute_list, numThreadGroups, 1, 1);
|
||||
RD::get_singleton()->compute_list_add_barrier(compute_list);
|
||||
}
|
||||
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sort.pipelines[SORT_MODE_INNER]);
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(Sort::PushConstant));
|
||||
RD::get_singleton()->compute_list_dispatch(compute_list, numThreadGroups, 1, 1);
|
||||
|
||||
presorted *= 2;
|
||||
}
|
||||
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
|
||||
RasterizerEffectsRD::RasterizerEffectsRD() {
|
||||
{ // Initialize copy
|
||||
Vector<String> copy_modes;
|
||||
|
@ -1618,6 +1688,21 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
Vector<String> sort_modes;
|
||||
sort_modes.push_back("\n#define MODE_SORT_BLOCK\n");
|
||||
sort_modes.push_back("\n#define MODE_SORT_STEP\n");
|
||||
sort_modes.push_back("\n#define MODE_SORT_INNER\n");
|
||||
|
||||
sort.shader.initialize(sort_modes);
|
||||
|
||||
sort.shader_version = sort.shader.version_create();
|
||||
|
||||
for (int i = 0; i < SORT_MODE_MAX; i++) {
|
||||
sort.pipelines[i] = RD::get_singleton()->compute_pipeline_create(sort.shader.version_get_shader(sort.shader_version, i));
|
||||
}
|
||||
}
|
||||
|
||||
RD::SamplerState sampler;
|
||||
sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
sampler.min_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection_filter.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection_scale.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/sort.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/specular_merge.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/ssao.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl.gen.h"
|
||||
|
@ -545,9 +546,28 @@ class RasterizerEffectsRD {
|
|||
struct ShadowReduce {
|
||||
ShadowReduceShaderRD shader;
|
||||
RID shader_version;
|
||||
RID pipelines[2];
|
||||
RID pipelines[SHADOW_REDUCE_MAX];
|
||||
} shadow_reduce;
|
||||
|
||||
enum SortMode {
|
||||
SORT_MODE_BLOCK,
|
||||
SORT_MODE_STEP,
|
||||
SORT_MODE_INNER,
|
||||
SORT_MODE_MAX
|
||||
};
|
||||
|
||||
struct Sort {
|
||||
struct PushConstant {
|
||||
uint32_t total_elements;
|
||||
uint32_t pad[3];
|
||||
int32_t job_params[4];
|
||||
};
|
||||
|
||||
SortShaderRD shader;
|
||||
RID shader_version;
|
||||
RID pipelines[SORT_MODE_MAX];
|
||||
} sort;
|
||||
|
||||
RID default_sampler;
|
||||
RID default_mipmap_sampler;
|
||||
RID index_buffer;
|
||||
|
@ -650,6 +670,8 @@ public:
|
|||
void reduce_shadow(RID p_source_shadow, RID p_dest_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, int p_shrink_limit, RenderingDevice::ComputeListID compute_list);
|
||||
void filter_shadow(RID p_shadow, RID p_backing_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, RS::EnvVolumetricFogShadowFilter p_filter, RenderingDevice::ComputeListID compute_list, bool p_vertical = true, bool p_horizontal = true);
|
||||
|
||||
void sort_buffer(RID p_uniform_set, int p_size);
|
||||
|
||||
RasterizerEffectsRD();
|
||||
~RasterizerEffectsRD();
|
||||
};
|
||||
|
|
|
@ -782,8 +782,7 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements,
|
|||
for (int i = 0; i < p_element_count; i++) {
|
||||
const RenderList::Element *e = p_elements[i];
|
||||
InstanceData &id = scene_state.instances[i];
|
||||
RasterizerStorageRD::store_transform(e->instance->transform, id.transform);
|
||||
RasterizerStorageRD::store_transform(Transform(e->instance->transform.basis.inverse().transposed()), id.normal_transform);
|
||||
bool store_transform = true;
|
||||
id.flags = 0;
|
||||
id.mask = e->instance->layer_mask;
|
||||
id.instance_uniforms_ofs = e->instance->instance_allocated_shader_parameters_offset >= 0 ? e->instance->instance_allocated_shader_parameters_offset : 0;
|
||||
|
@ -807,12 +806,42 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements,
|
|||
}
|
||||
|
||||
id.flags |= (stride << INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT);
|
||||
} else if (e->instance->base_type == RS::INSTANCE_PARTICLES) {
|
||||
id.flags |= INSTANCE_DATA_FLAG_MULTIMESH;
|
||||
uint32_t stride;
|
||||
if (false) { // 2D particles
|
||||
id.flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D;
|
||||
stride = 2;
|
||||
} else {
|
||||
stride = 3;
|
||||
}
|
||||
|
||||
id.flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR;
|
||||
stride += 1;
|
||||
|
||||
id.flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA;
|
||||
stride += 1;
|
||||
|
||||
id.flags |= (stride << INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT);
|
||||
|
||||
if (!storage->particles_is_using_local_coords(e->instance->base)) {
|
||||
store_transform = false;
|
||||
}
|
||||
|
||||
} else if (e->instance->base_type == RS::INSTANCE_MESH) {
|
||||
if (e->instance->skeleton.is_valid()) {
|
||||
id.flags |= INSTANCE_DATA_FLAG_SKELETON;
|
||||
}
|
||||
}
|
||||
|
||||
if (store_transform) {
|
||||
RasterizerStorageRD::store_transform(e->instance->transform, id.transform);
|
||||
RasterizerStorageRD::store_transform(Transform(e->instance->transform.basis.inverse().transposed()), id.normal_transform);
|
||||
} else {
|
||||
RasterizerStorageRD::store_transform(Transform(), id.transform);
|
||||
RasterizerStorageRD::store_transform(Transform(), id.normal_transform);
|
||||
}
|
||||
|
||||
if (p_for_depth) {
|
||||
id.gi_offset = 0xFFFFFFFF;
|
||||
continue;
|
||||
|
@ -967,7 +996,12 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
|
|||
ERR_CONTINUE(true); //should be a bug
|
||||
} break;
|
||||
case RS::INSTANCE_PARTICLES: {
|
||||
ERR_CONTINUE(true); //should be a bug
|
||||
RID mesh = storage->particles_get_draw_pass_mesh(e->instance->base, e->surface_index >> 16);
|
||||
ERR_CONTINUE(!mesh.is_valid()); //should be a bug
|
||||
primitive = storage->mesh_surface_get_primitive(mesh, e->surface_index & 0xFFFF);
|
||||
|
||||
xforms_uniform_set = storage->particles_get_instance_buffer_uniform_set(e->instance->base, default_shader_rd, TRANSFORMS_UNIFORM_SET);
|
||||
|
||||
} break;
|
||||
default: {
|
||||
ERR_CONTINUE(true); //should be a bug
|
||||
|
@ -1036,7 +1070,9 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
|
|||
ERR_CONTINUE(true); //should be a bug
|
||||
} break;
|
||||
case RS::INSTANCE_PARTICLES: {
|
||||
ERR_CONTINUE(true); //should be a bug
|
||||
RID mesh = storage->particles_get_draw_pass_mesh(e->instance->base, e->surface_index >> 16);
|
||||
ERR_CONTINUE(!mesh.is_valid()); //should be a bug
|
||||
storage->mesh_surface_get_arrays_and_format(mesh, e->surface_index & 0xFFFF, pipeline->get_vertex_input_mask(), vertex_array_rd, index_array_rd, vertex_format);
|
||||
} break;
|
||||
default: {
|
||||
ERR_CONTINUE(true); //should be a bug
|
||||
|
@ -1092,6 +1128,8 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
|
|||
case RS::INSTANCE_IMMEDIATE: {
|
||||
} break;
|
||||
case RS::INSTANCE_PARTICLES: {
|
||||
uint32_t instances = storage->particles_get_amount(e->instance->base);
|
||||
RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instances);
|
||||
} break;
|
||||
default: {
|
||||
ERR_CONTINUE(true); //should be a bug
|
||||
|
@ -1524,31 +1562,31 @@ void RasterizerSceneHighEndRD::_fill_render_list(InstanceBase **p_cull_result, i
|
|||
_add_geometry(immediate, inst, nullptr, -1, p_depth_pass, p_shadow_pass);
|
||||
|
||||
} break;
|
||||
#endif
|
||||
case RS::INSTANCE_PARTICLES: {
|
||||
int draw_passes = storage->particles_get_draw_passes(inst->base);
|
||||
|
||||
RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(inst->base);
|
||||
ERR_CONTINUE(!particles);
|
||||
|
||||
for (int j = 0; j < particles->draw_passes.size(); j++) {
|
||||
|
||||
RID pmesh = particles->draw_passes[j];
|
||||
if (!pmesh.is_valid())
|
||||
for (int j = 0; j < draw_passes; j++) {
|
||||
RID mesh = storage->particles_get_draw_pass_mesh(inst->base, j);
|
||||
if (!mesh.is_valid())
|
||||
continue;
|
||||
RasterizerStorageGLES3::Mesh *mesh = storage->mesh_owner.getornull(pmesh);
|
||||
if (!mesh)
|
||||
continue; //mesh not assigned
|
||||
|
||||
int ssize = mesh->surfaces.size();
|
||||
const RID *materials = nullptr;
|
||||
uint32_t surface_count;
|
||||
|
||||
for (int k = 0; k < ssize; k++) {
|
||||
materials = storage->mesh_get_surface_count_and_materials(mesh, surface_count);
|
||||
if (!materials) {
|
||||
continue; //nothing to do
|
||||
}
|
||||
|
||||
RasterizerStorageGLES3::Surface *s = mesh->surfaces[k];
|
||||
_add_geometry(s, inst, particles, -1, p_depth_pass, p_shadow_pass);
|
||||
for (uint32_t k = 0; k < surface_count; k++) {
|
||||
uint32_t surface_index = storage->mesh_surface_get_particles_render_pass_index(mesh, j, render_pass, &geometry_index);
|
||||
_add_geometry(inst, (j << 16) | k, materials[j], p_pass_mode, surface_index, p_using_sdfgi);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
#endif
|
||||
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -359,7 +359,7 @@ private:
|
|||
|
||||
mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
|
||||
|
||||
/* REFLECTION PROBE INSTANCE */
|
||||
/* DECAL INSTANCE */
|
||||
|
||||
struct DecalInstance {
|
||||
RID decal;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/engine.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "rasterizer_rd.h"
|
||||
#include "servers/rendering/shader_language.h"
|
||||
|
||||
Ref<Image> RasterizerStorageRD::_validate_texture_format(const Ref<Image> &p_image, TextureToRDFormat &r_format) {
|
||||
|
@ -3098,8 +3099,771 @@ void RasterizerStorageRD::_update_dirty_multimeshes() {
|
|||
multimesh_dirty_list = nullptr;
|
||||
}
|
||||
|
||||
/* SKELETON */
|
||||
/* PARTICLES */
|
||||
|
||||
RID RasterizerStorageRD::particles_create() {
|
||||
return particles_owner.make_rid(Particles());
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_emitting(RID p_particles, bool p_emitting) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->emitting = p_emitting;
|
||||
}
|
||||
|
||||
bool RasterizerStorageRD::particles_get_emitting(RID p_particles) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, false);
|
||||
|
||||
return particles->emitting;
|
||||
}
|
||||
|
||||
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 > 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;
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 1;
|
||||
u.ids.push_back(particles->particle_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 2;
|
||||
u.ids.push_back(particles->particle_instance_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
particles->particles_copy_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, 0), 0);
|
||||
}
|
||||
}
|
||||
|
||||
particles->prev_ticks = 0;
|
||||
particles->phase = 0;
|
||||
particles->prev_phase = 0;
|
||||
particles->clear = true;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_lifetime(RID p_particles, float p_lifetime) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
particles->lifetime = p_lifetime;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_one_shot(RID p_particles, bool p_one_shot) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
particles->one_shot = p_one_shot;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_pre_process_time(RID p_particles, float p_time) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
particles->pre_process_time = p_time;
|
||||
}
|
||||
void RasterizerStorageRD::particles_set_explosiveness_ratio(RID p_particles, float p_ratio) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
particles->explosiveness = p_ratio;
|
||||
}
|
||||
void RasterizerStorageRD::particles_set_randomness_ratio(RID p_particles, float p_ratio) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
particles->randomness = p_ratio;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
particles->custom_aabb = p_aabb;
|
||||
particles->instance_dependency.instance_notify_changed(true, false);
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_speed_scale(RID p_particles, float p_scale) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->speed_scale = p_scale;
|
||||
}
|
||||
void RasterizerStorageRD::particles_set_use_local_coordinates(RID p_particles, bool p_enable) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->use_local_coords = p_enable;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_fixed_fps(RID p_particles, int p_fps) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->fixed_fps = p_fps;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_fractional_delta(RID p_particles, bool p_enable) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->fractional_delta = p_enable;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_process_material(RID p_particles, RID p_material) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->process_material = p_material;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->draw_order = p_order;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_draw_passes(RID p_particles, int p_passes) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->draw_passes.resize(p_passes);
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
ERR_FAIL_INDEX(p_pass, particles->draw_passes.size());
|
||||
particles->draw_passes.write[p_pass] = p_mesh;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_restart(RID p_particles) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->restart_request = true;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_request_process(RID p_particles) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
if (!particles->dirty) {
|
||||
particles->dirty = true;
|
||||
particles->update_list = particle_update_list;
|
||||
particle_update_list = particles;
|
||||
}
|
||||
}
|
||||
|
||||
AABB RasterizerStorageRD::particles_get_current_aabb(RID p_particles) {
|
||||
const Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, AABB());
|
||||
|
||||
Vector<ParticleData> data;
|
||||
data.resize(particles->amount);
|
||||
|
||||
Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(particles->particle_buffer);
|
||||
|
||||
Transform inv = particles->emission_transform.affine_inverse();
|
||||
|
||||
AABB aabb;
|
||||
if (buffer.size()) {
|
||||
bool first = true;
|
||||
const ParticleData *particle_data = (const ParticleData *)data.ptr();
|
||||
for (int i = 0; i < particles->amount; i++) {
|
||||
if (particle_data[i].active) {
|
||||
Vector3 pos = Vector3(particle_data[i].xform[12], particle_data[i].xform[13], particle_data[i].xform[14]);
|
||||
if (!particles->use_local_coords) {
|
||||
pos = inv.xform(pos);
|
||||
}
|
||||
if (first) {
|
||||
aabb.position = pos;
|
||||
first = false;
|
||||
} else {
|
||||
aabb.expand_to(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float longest_axis_size = 0;
|
||||
for (int i = 0; i < particles->draw_passes.size(); i++) {
|
||||
if (particles->draw_passes[i].is_valid()) {
|
||||
AABB maabb = mesh_get_aabb(particles->draw_passes[i], RID());
|
||||
longest_axis_size = MAX(maabb.get_longest_axis_size(), longest_axis_size);
|
||||
}
|
||||
}
|
||||
|
||||
aabb.grow_by(longest_axis_size);
|
||||
|
||||
return aabb;
|
||||
}
|
||||
|
||||
AABB RasterizerStorageRD::particles_get_aabb(RID p_particles) const {
|
||||
const Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, AABB());
|
||||
|
||||
return particles->custom_aabb;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_emission_transform(RID p_particles, const Transform &p_transform) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
particles->emission_transform = p_transform;
|
||||
}
|
||||
|
||||
int RasterizerStorageRD::particles_get_draw_passes(RID p_particles) const {
|
||||
const Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, 0);
|
||||
|
||||
return particles->draw_passes.size();
|
||||
}
|
||||
|
||||
RID RasterizerStorageRD::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const {
|
||||
const Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, RID());
|
||||
ERR_FAIL_INDEX_V(p_pass, particles->draw_passes.size(), RID());
|
||||
|
||||
return particles->draw_passes[p_pass];
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_delta) {
|
||||
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;
|
||||
|
||||
if (p_particles->clear) {
|
||||
p_particles->cycle_number = 0;
|
||||
p_particles->random_seed = Math::rand();
|
||||
} else if (new_phase < p_particles->phase) {
|
||||
if (p_particles->one_shot) {
|
||||
p_particles->emitting = false;
|
||||
}
|
||||
p_particles->cycle_number++;
|
||||
}
|
||||
|
||||
frame_params.emitting = p_particles->emitting;
|
||||
frame_params.system_phase = new_phase;
|
||||
frame_params.prev_system_phase = p_particles->phase;
|
||||
|
||||
p_particles->phase = new_phase;
|
||||
|
||||
frame_params.time = RasterizerRD::singleton->get_total_time();
|
||||
frame_params.delta = p_delta * p_particles->speed_scale;
|
||||
frame_params.random_seed = p_particles->random_seed;
|
||||
frame_params.explosiveness = p_particles->explosiveness;
|
||||
frame_params.randomness = p_particles->randomness;
|
||||
|
||||
if (p_particles->use_local_coords) {
|
||||
store_transform(Transform(), frame_params.emission_transform);
|
||||
} else {
|
||||
store_transform(p_particles->emission_transform, frame_params.emission_transform);
|
||||
}
|
||||
|
||||
frame_params.cycle = p_particles->cycle_number;
|
||||
|
||||
ParticlesShader::PushConstant push_constant;
|
||||
|
||||
push_constant.clear = p_particles->clear;
|
||||
push_constant.total_particles = p_particles->amount;
|
||||
push_constant.lifetime = p_particles->lifetime;
|
||||
push_constant.trail_size = 1;
|
||||
push_constant.use_fractional_delta = p_particles->fractional_delta;
|
||||
|
||||
p_particles->clear = false;
|
||||
|
||||
RD::get_singleton()->buffer_update(p_particles->frame_params_buffer, 0, sizeof(ParticlesFrameParams), &frame_params, true);
|
||||
|
||||
ParticlesMaterialData *m = (ParticlesMaterialData *)material_get_data(p_particles->process_material, SHADER_TYPE_PARTICLES);
|
||||
if (!m) {
|
||||
m = (ParticlesMaterialData *)material_get_data(particles_shader.default_material, SHADER_TYPE_PARTICLES);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(!m);
|
||||
|
||||
//todo should maybe compute all particle systems together?
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, m->shader_data->pipeline);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles_shader.base_uniform_set, 0);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->particles_material_uniform_set, 1);
|
||||
if (m->uniform_set.is_valid()) {
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 2);
|
||||
}
|
||||
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ParticlesShader::PushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_particles->amount, 1, 1, 64, 1, 1);
|
||||
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::particles_set_view_axis(RID p_particles, const Vector3 &p_axis) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND(!particles);
|
||||
|
||||
if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH) {
|
||||
return; //uninteresting for other modes
|
||||
}
|
||||
|
||||
//copy to sort buffer
|
||||
if (particles->particles_sort_buffer == RID()) {
|
||||
uint32_t size = particles->amount;
|
||||
if (size & 1) {
|
||||
size++; //make multiple of 16
|
||||
}
|
||||
size *= sizeof(float) * 2;
|
||||
particles->particles_sort_buffer = RD::get_singleton()->storage_buffer_create(size);
|
||||
{
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 0;
|
||||
u.ids.push_back(particles->particles_sort_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
particles->particles_sort_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, ParticlesShader::COPY_MODE_FILL_SORT_BUFFER), 1);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 axis = -p_axis; // cameras look to z negative
|
||||
|
||||
if (particles->use_local_coords) {
|
||||
axis = particles->emission_transform.basis.xform_inv(axis).normalized();
|
||||
}
|
||||
|
||||
ParticlesShader::CopyPushConstant copy_push_constant;
|
||||
copy_push_constant.total_particles = particles->amount;
|
||||
copy_push_constant.sort_direction[0] = axis.x;
|
||||
copy_push_constant.sort_direction[1] = axis.y;
|
||||
copy_push_constant.sort_direction[2] = axis.z;
|
||||
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_SORT_BUFFER]);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1);
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1, 64, 1, 1);
|
||||
|
||||
RD::get_singleton()->compute_list_end();
|
||||
|
||||
effects.sort_buffer(particles->particles_sort_uniform_set, particles->amount);
|
||||
|
||||
compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER]);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1);
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1, 64, 1, 1);
|
||||
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::update_particles() {
|
||||
while (particle_update_list) {
|
||||
//use transform feedback to process particles
|
||||
|
||||
Particles *particles = particle_update_list;
|
||||
|
||||
//take and remove
|
||||
particle_update_list = particles->update_list;
|
||||
particles->update_list = nullptr;
|
||||
particles->dirty = false;
|
||||
|
||||
if (particles->restart_request) {
|
||||
particles->prev_ticks = 0;
|
||||
particles->phase = 0;
|
||||
particles->prev_phase = 0;
|
||||
particles->clear = true;
|
||||
particles->restart_request = false;
|
||||
}
|
||||
|
||||
if (particles->inactive && !particles->emitting) {
|
||||
//go next
|
||||
continue;
|
||||
}
|
||||
|
||||
if (particles->emitting) {
|
||||
if (particles->inactive) {
|
||||
//restart system from scratch
|
||||
particles->prev_ticks = 0;
|
||||
particles->phase = 0;
|
||||
particles->prev_phase = 0;
|
||||
particles->clear = true;
|
||||
}
|
||||
particles->inactive = false;
|
||||
particles->inactive_time = 0;
|
||||
} else {
|
||||
particles->inactive_time += particles->speed_scale * RasterizerRD::singleton->get_frame_delta_time();
|
||||
if (particles->inactive_time > particles->lifetime * 1.2) {
|
||||
particles->inactive = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0;
|
||||
|
||||
if (particles->clear && particles->pre_process_time > 0.0) {
|
||||
float frame_time;
|
||||
if (particles->fixed_fps > 0)
|
||||
frame_time = 1.0 / particles->fixed_fps;
|
||||
else
|
||||
frame_time = 1.0 / 30.0;
|
||||
|
||||
float todo = particles->pre_process_time;
|
||||
|
||||
while (todo >= 0) {
|
||||
_particles_process(particles, frame_time);
|
||||
todo -= frame_time;
|
||||
}
|
||||
}
|
||||
|
||||
if (particles->fixed_fps > 0) {
|
||||
float frame_time;
|
||||
float decr;
|
||||
if (zero_time_scale) {
|
||||
frame_time = 0.0;
|
||||
decr = 1.0 / particles->fixed_fps;
|
||||
} else {
|
||||
frame_time = 1.0 / particles->fixed_fps;
|
||||
decr = frame_time;
|
||||
}
|
||||
float delta = RasterizerRD::singleton->get_frame_delta_time();
|
||||
if (delta > 0.1) { //avoid recursive stalls if fps goes below 10
|
||||
delta = 0.1;
|
||||
} else if (delta <= 0.0) { //unlikely but..
|
||||
delta = 0.001;
|
||||
}
|
||||
float todo = particles->frame_remainder + delta;
|
||||
|
||||
while (todo >= frame_time) {
|
||||
_particles_process(particles, frame_time);
|
||||
todo -= decr;
|
||||
}
|
||||
|
||||
particles->frame_remainder = todo;
|
||||
|
||||
} else {
|
||||
if (zero_time_scale)
|
||||
_particles_process(particles, 0.0);
|
||||
else
|
||||
_particles_process(particles, RasterizerRD::singleton->get_frame_delta_time());
|
||||
}
|
||||
|
||||
//copy particles to instance buffer
|
||||
|
||||
if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH) {
|
||||
ParticlesShader::CopyPushConstant copy_push_constant;
|
||||
copy_push_constant.total_particles = particles->amount;
|
||||
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_INSTANCES]);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0);
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1, 64, 1, 1);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
bool RasterizerStorageRD::particles_is_inactive(RID p_particles) const {
|
||||
const Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, false);
|
||||
return !particles->emitting && particles->inactive;
|
||||
}
|
||||
|
||||
/* SKY SHADER */
|
||||
|
||||
void RasterizerStorageRD::ParticlesShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
code = p_code;
|
||||
valid = false;
|
||||
ubo_size = 0;
|
||||
uniforms.clear();
|
||||
|
||||
if (code == String()) {
|
||||
return; //just invalid, but no error
|
||||
}
|
||||
|
||||
ShaderCompilerRD::GeneratedCode gen_code;
|
||||
ShaderCompilerRD::IdentifierActions actions;
|
||||
|
||||
/*
|
||||
uses_time = false;
|
||||
|
||||
actions.render_mode_flags["use_half_res_pass"] = &uses_half_res;
|
||||
actions.render_mode_flags["use_quarter_res_pass"] = &uses_quarter_res;
|
||||
|
||||
actions.usage_flag_pointers["TIME"] = &uses_time;
|
||||
*/
|
||||
|
||||
actions.uniforms = &uniforms;
|
||||
|
||||
Error err = base_singleton->particles_shader.compiler.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code);
|
||||
|
||||
ERR_FAIL_COND(err != OK);
|
||||
|
||||
if (version.is_null()) {
|
||||
version = base_singleton->particles_shader.shader.version_create();
|
||||
}
|
||||
|
||||
base_singleton->particles_shader.shader.version_set_compute_code(version, gen_code.uniforms, gen_code.compute_global, gen_code.compute, gen_code.defines);
|
||||
ERR_FAIL_COND(!base_singleton->particles_shader.shader.version_is_valid(version));
|
||||
|
||||
ubo_size = gen_code.uniform_total_size;
|
||||
ubo_offsets = gen_code.uniform_offsets;
|
||||
texture_uniforms = gen_code.texture_uniforms;
|
||||
|
||||
//update pipelines
|
||||
|
||||
pipeline = RD::get_singleton()->compute_pipeline_create(base_singleton->particles_shader.shader.version_get_shader(version, 0));
|
||||
|
||||
valid = true;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::ParticlesShaderData::set_default_texture_param(const StringName &p_name, RID p_texture) {
|
||||
if (!p_texture.is_valid()) {
|
||||
default_texture_params.erase(p_name);
|
||||
} else {
|
||||
default_texture_params[p_name] = p_texture;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::ParticlesShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
|
||||
Map<int, StringName> order;
|
||||
|
||||
for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
|
||||
if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (E->get().texture_order >= 0) {
|
||||
order[E->get().texture_order + 100000] = E->key();
|
||||
} else {
|
||||
order[E->get().order] = E->key();
|
||||
}
|
||||
}
|
||||
|
||||
for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) {
|
||||
PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E->get()]);
|
||||
pi.name = E->get();
|
||||
p_param_list->push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::ParticlesShaderData::get_instance_param_list(List<RasterizerStorage::InstanceShaderParam> *p_param_list) const {
|
||||
for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
|
||||
if (E->get().scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RasterizerStorage::InstanceShaderParam p;
|
||||
p.info = ShaderLanguage::uniform_to_property_info(E->get());
|
||||
p.info.name = E->key(); //supply name
|
||||
p.index = E->get().instance_index;
|
||||
p.default_value = ShaderLanguage::constant_value_to_variant(E->get().default_value, E->get().type, E->get().hint);
|
||||
p_param_list->push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
bool RasterizerStorageRD::ParticlesShaderData::is_param_texture(const StringName &p_param) const {
|
||||
if (!uniforms.has(p_param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return uniforms[p_param].texture_order >= 0;
|
||||
}
|
||||
|
||||
bool RasterizerStorageRD::ParticlesShaderData::is_animated() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RasterizerStorageRD::ParticlesShaderData::casts_shadows() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant RasterizerStorageRD::ParticlesShaderData::get_default_parameter(const StringName &p_parameter) const {
|
||||
if (uniforms.has(p_parameter)) {
|
||||
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
|
||||
Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
|
||||
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint);
|
||||
}
|
||||
return Variant();
|
||||
}
|
||||
|
||||
RasterizerStorageRD::ParticlesShaderData::ParticlesShaderData() {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
RasterizerStorageRD::ParticlesShaderData::~ParticlesShaderData() {
|
||||
//pipeline variants will clear themselves if shader is gone
|
||||
if (version.is_valid()) {
|
||||
base_singleton->particles_shader.shader.version_free(version);
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerStorageRD::ShaderData *RasterizerStorageRD::_create_particles_shader_func() {
|
||||
ParticlesShaderData *shader_data = memnew(ParticlesShaderData);
|
||||
return shader_data;
|
||||
}
|
||||
|
||||
void RasterizerStorageRD::ParticlesMaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
|
||||
uniform_set_updated = true;
|
||||
|
||||
if ((uint32_t)ubo_data.size() != shader_data->ubo_size) {
|
||||
p_uniform_dirty = true;
|
||||
if (uniform_buffer.is_valid()) {
|
||||
RD::get_singleton()->free(uniform_buffer);
|
||||
uniform_buffer = RID();
|
||||
}
|
||||
|
||||
ubo_data.resize(shader_data->ubo_size);
|
||||
if (ubo_data.size()) {
|
||||
uniform_buffer = RD::get_singleton()->uniform_buffer_create(ubo_data.size());
|
||||
memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear
|
||||
}
|
||||
|
||||
//clear previous uniform set
|
||||
if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
|
||||
RD::get_singleton()->free(uniform_set);
|
||||
uniform_set = RID();
|
||||
}
|
||||
}
|
||||
|
||||
//check whether buffer changed
|
||||
if (p_uniform_dirty && ubo_data.size()) {
|
||||
update_uniform_buffer(shader_data->uniforms, shader_data->ubo_offsets.ptr(), p_parameters, ubo_data.ptrw(), ubo_data.size(), false);
|
||||
RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw());
|
||||
}
|
||||
|
||||
uint32_t tex_uniform_count = shader_data->texture_uniforms.size();
|
||||
|
||||
if ((uint32_t)texture_cache.size() != tex_uniform_count) {
|
||||
texture_cache.resize(tex_uniform_count);
|
||||
p_textures_dirty = true;
|
||||
|
||||
//clear previous uniform set
|
||||
if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
|
||||
RD::get_singleton()->free(uniform_set);
|
||||
uniform_set = RID();
|
||||
}
|
||||
}
|
||||
|
||||
if (p_textures_dirty && tex_uniform_count) {
|
||||
update_textures(p_parameters, shader_data->default_texture_params, shader_data->texture_uniforms, texture_cache.ptrw(), true);
|
||||
}
|
||||
|
||||
if (shader_data->ubo_size == 0 && shader_data->texture_uniforms.size() == 0) {
|
||||
// This material does not require an uniform set, so don't create it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!p_textures_dirty && uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
|
||||
//no reason to update uniform set, only UBO (or nothing) was needed to update
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
||||
{
|
||||
if (shader_data->ubo_size) {
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
|
||||
u.binding = 0;
|
||||
u.ids.push_back(uniform_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
const RID *textures = texture_cache.ptrw();
|
||||
for (uint32_t i = 0; i < tex_uniform_count; i++) {
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_TEXTURE;
|
||||
u.binding = 1 + i;
|
||||
u.ids.push_back(textures[i]);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
}
|
||||
|
||||
uniform_set = RD::get_singleton()->uniform_set_create(uniforms, base_singleton->particles_shader.shader.version_get_shader(shader_data->version, 0), 2);
|
||||
}
|
||||
|
||||
RasterizerStorageRD::ParticlesMaterialData::~ParticlesMaterialData() {
|
||||
if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
|
||||
RD::get_singleton()->free(uniform_set);
|
||||
}
|
||||
|
||||
if (uniform_buffer.is_valid()) {
|
||||
RD::get_singleton()->free(uniform_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerStorageRD::MaterialData *RasterizerStorageRD::_create_particles_material_func(ParticlesShaderData *p_shader) {
|
||||
ParticlesMaterialData *material_data = memnew(ParticlesMaterialData);
|
||||
material_data->shader_data = p_shader;
|
||||
material_data->last_frame = false;
|
||||
//update will happen later anyway so do nothing.
|
||||
return material_data;
|
||||
}
|
||||
////////
|
||||
/* SKELETON API */
|
||||
|
||||
RID RasterizerStorageRD::skeleton_create() {
|
||||
|
@ -4683,6 +5447,9 @@ void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::In
|
|||
} else if (light_owner.owns(p_base)) {
|
||||
Light *l = light_owner.getornull(p_base);
|
||||
p_instance->update_dependency(&l->instance_dependency);
|
||||
} else if (particles_owner.owns(p_base)) {
|
||||
Particles *p = particles_owner.getornull(p_base);
|
||||
p_instance->update_dependency(&p->instance_dependency);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4715,6 +5482,9 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
|
|||
if (lightmap_owner.owns(p_rid)) {
|
||||
return RS::INSTANCE_LIGHTMAP;
|
||||
}
|
||||
if (particles_owner.owns(p_rid)) {
|
||||
return RS::INSTANCE_PARTICLES;
|
||||
}
|
||||
|
||||
return RS::INSTANCE_NONE;
|
||||
}
|
||||
|
@ -5618,6 +6388,8 @@ void RasterizerStorageRD::update_dirty_resources() {
|
|||
_update_dirty_multimeshes();
|
||||
_update_dirty_skeletons();
|
||||
_update_decal_atlas();
|
||||
|
||||
update_particles();
|
||||
}
|
||||
|
||||
bool RasterizerStorageRD::has_os_feature(const String &p_feature) const {
|
||||
|
@ -6211,6 +6983,112 @@ RasterizerStorageRD::RasterizerStorageRD() {
|
|||
}
|
||||
|
||||
lightmap_probe_capture_update_speed = GLOBAL_GET("rendering/lightmapper/probe_capture_update_speed");
|
||||
|
||||
/* Particles */
|
||||
|
||||
{
|
||||
// Initialize particles
|
||||
Vector<String> particles_modes;
|
||||
particles_modes.push_back("");
|
||||
particles_shader.shader.initialize(particles_modes, String());
|
||||
}
|
||||
shader_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_PARTICLES, _create_particles_shader_funcs);
|
||||
material_set_data_request_function(RasterizerStorageRD::SHADER_TYPE_PARTICLES, _create_particles_material_funcs);
|
||||
|
||||
{
|
||||
ShaderCompilerRD::DefaultIdentifierActions actions;
|
||||
|
||||
actions.renames["COLOR"] = "PARTICLE.color";
|
||||
actions.renames["VELOCITY"] = "PARTICLE.velocity";
|
||||
//actions.renames["MASS"] = "mass"; ?
|
||||
actions.renames["ACTIVE"] = "PARTICLE.is_active";
|
||||
actions.renames["RESTART"] = "restart";
|
||||
actions.renames["CUSTOM"] = "PARTICLE.custom";
|
||||
actions.renames["TRANSFORM"] = "PARTICLE.xform";
|
||||
actions.renames["TIME"] = "FRAME.time";
|
||||
actions.renames["LIFETIME"] = "params.lifetime";
|
||||
actions.renames["DELTA"] = "local_delta";
|
||||
actions.renames["NUMBER"] = "particle";
|
||||
actions.renames["INDEX"] = "index";
|
||||
//actions.renames["GRAVITY"] = "current_gravity";
|
||||
actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform";
|
||||
actions.renames["RANDOM_SEED"] = "FRAME.random_seed";
|
||||
|
||||
actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
|
||||
actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
|
||||
actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
|
||||
|
||||
actions.sampler_array_name = "material_samplers";
|
||||
actions.base_texture_binding_index = 1;
|
||||
actions.texture_layout_set = 2;
|
||||
actions.base_uniform_string = "material.";
|
||||
actions.base_varying_index = 10;
|
||||
|
||||
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
|
||||
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
|
||||
actions.global_buffer_array_variable = "global_variables.data";
|
||||
|
||||
particles_shader.compiler.initialize(actions);
|
||||
}
|
||||
|
||||
{
|
||||
// default material and shader for particles shader
|
||||
particles_shader.default_shader = shader_create();
|
||||
shader_set_code(particles_shader.default_shader, "shader_type particles; void compute() { COLOR = vec4(1.0); } \n");
|
||||
particles_shader.default_material = material_create();
|
||||
material_set_shader(particles_shader.default_material, particles_shader.default_shader);
|
||||
|
||||
ParticlesMaterialData *md = (ParticlesMaterialData *)material_get_data(particles_shader.default_material, RasterizerStorageRD::SHADER_TYPE_PARTICLES);
|
||||
particles_shader.default_shader_rd = particles_shader.shader.version_get_shader(md->shader_data->version, 0);
|
||||
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_SAMPLER;
|
||||
u.binding = 1;
|
||||
u.ids.resize(12);
|
||||
RID *ids_ptr = u.ids.ptrw();
|
||||
ids_ptr[0] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
ids_ptr[1] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
ids_ptr[2] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
ids_ptr[3] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
ids_ptr[4] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
ids_ptr[5] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
ids_ptr[6] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
|
||||
ids_ptr[7] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
|
||||
ids_ptr[8] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
|
||||
ids_ptr[9] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
|
||||
ids_ptr[10] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
|
||||
ids_ptr[11] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 2;
|
||||
u.ids.push_back(global_variables_get_storage_buffer());
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
particles_shader.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 0);
|
||||
}
|
||||
|
||||
{
|
||||
Vector<String> copy_modes;
|
||||
copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n");
|
||||
copy_modes.push_back("\n#define MODE_FILL_SORT_BUFFER\n#define USE_SORT_BUFFER\n");
|
||||
copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n#define USE_SORT_BUFFER\n");
|
||||
|
||||
particles_shader.copy_shader.initialize(copy_modes);
|
||||
|
||||
particles_shader.copy_shader_version = particles_shader.copy_shader.version_create();
|
||||
|
||||
for (int i = 0; i < ParticlesShader::COPY_MODE_MAX; i++) {
|
||||
particles_shader.copy_pipelines[i] = RD::get_singleton()->compute_pipeline_create(particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerStorageRD::~RasterizerStorageRD() {
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "servers/rendering/rasterizer_rd/rasterizer_effects_rd.h"
|
||||
#include "servers/rendering/rasterizer_rd/shader_compiler_rd.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/giprobe_sdf.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/particles.glsl.gen.h"
|
||||
#include "servers/rendering/rasterizer_rd/shaders/particles_copy.glsl.gen.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
class RasterizerStorageRD : public RasterizerStorage {
|
||||
|
@ -386,6 +388,9 @@ private:
|
|||
|
||||
uint32_t multimesh_render_index = 0;
|
||||
uint64_t multimesh_render_pass = 0;
|
||||
|
||||
uint32_t particles_render_index = 0;
|
||||
uint64_t particles_render_pass = 0;
|
||||
};
|
||||
|
||||
uint32_t blend_shape_count = 0;
|
||||
|
@ -448,6 +453,215 @@ private:
|
|||
_FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances);
|
||||
void _update_dirty_multimeshes();
|
||||
|
||||
/* PARTICLES */
|
||||
|
||||
struct ParticleData {
|
||||
float xform[16];
|
||||
float velocity[3];
|
||||
uint32_t active;
|
||||
float color[4];
|
||||
float custom[3];
|
||||
float lifetime;
|
||||
uint32_t pad[3];
|
||||
};
|
||||
|
||||
struct ParticlesFrameParams {
|
||||
uint32_t emitting;
|
||||
float system_phase;
|
||||
float prev_system_phase;
|
||||
uint32_t cycle;
|
||||
|
||||
float explosiveness;
|
||||
float randomness;
|
||||
float time;
|
||||
float delta;
|
||||
|
||||
uint32_t random_seed;
|
||||
uint32_t pad[3];
|
||||
|
||||
float emission_transform[16];
|
||||
};
|
||||
|
||||
struct Particles {
|
||||
bool inactive;
|
||||
float inactive_time;
|
||||
bool emitting;
|
||||
bool one_shot;
|
||||
int amount;
|
||||
float lifetime;
|
||||
float pre_process_time;
|
||||
float explosiveness;
|
||||
float randomness;
|
||||
bool restart_request;
|
||||
AABB custom_aabb;
|
||||
bool use_local_coords;
|
||||
RID process_material;
|
||||
|
||||
RS::ParticlesDrawOrder draw_order;
|
||||
|
||||
Vector<RID> draw_passes;
|
||||
|
||||
RID particle_buffer;
|
||||
RID particle_instance_buffer;
|
||||
RID frame_params_buffer;
|
||||
|
||||
RID particles_material_uniform_set;
|
||||
RID particles_copy_uniform_set;
|
||||
RID particles_transforms_buffer_uniform_set;
|
||||
|
||||
RID particles_sort_buffer;
|
||||
RID particles_sort_uniform_set;
|
||||
|
||||
bool dirty = false;
|
||||
Particles *update_list = nullptr;
|
||||
|
||||
float phase;
|
||||
float prev_phase;
|
||||
uint64_t prev_ticks;
|
||||
uint32_t random_seed;
|
||||
|
||||
uint32_t cycle_number;
|
||||
|
||||
float speed_scale;
|
||||
|
||||
int fixed_fps;
|
||||
bool fractional_delta;
|
||||
float frame_remainder;
|
||||
|
||||
bool clear;
|
||||
|
||||
Transform emission_transform;
|
||||
|
||||
Particles() :
|
||||
inactive(true),
|
||||
inactive_time(0.0),
|
||||
emitting(false),
|
||||
one_shot(false),
|
||||
amount(0),
|
||||
lifetime(1.0),
|
||||
pre_process_time(0.0),
|
||||
explosiveness(0.0),
|
||||
randomness(0.0),
|
||||
restart_request(false),
|
||||
custom_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8))),
|
||||
use_local_coords(true),
|
||||
draw_order(RS::PARTICLES_DRAW_ORDER_INDEX),
|
||||
prev_ticks(0),
|
||||
random_seed(0),
|
||||
cycle_number(0),
|
||||
speed_scale(1.0),
|
||||
fixed_fps(0),
|
||||
fractional_delta(false),
|
||||
frame_remainder(0),
|
||||
clear(true) {
|
||||
}
|
||||
|
||||
RasterizerScene::InstanceDependency instance_dependency;
|
||||
|
||||
ParticlesFrameParams frame_params;
|
||||
};
|
||||
|
||||
void _particles_process(Particles *p_particles, float p_delta);
|
||||
|
||||
struct ParticlesShader {
|
||||
struct PushConstant {
|
||||
float lifetime;
|
||||
uint32_t clear;
|
||||
uint32_t total_particles;
|
||||
uint32_t trail_size;
|
||||
uint32_t use_fractional_delta;
|
||||
uint32_t pad[3];
|
||||
};
|
||||
|
||||
ParticlesShaderRD shader;
|
||||
ShaderCompilerRD compiler;
|
||||
|
||||
RID default_shader;
|
||||
RID default_material;
|
||||
RID default_shader_rd;
|
||||
|
||||
RID base_uniform_set;
|
||||
|
||||
struct CopyPushConstant {
|
||||
float sort_direction[3];
|
||||
uint32_t total_particles;
|
||||
};
|
||||
|
||||
enum {
|
||||
COPY_MODE_FILL_INSTANCES,
|
||||
COPY_MODE_FILL_SORT_BUFFER,
|
||||
COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER,
|
||||
COPY_MODE_MAX,
|
||||
};
|
||||
|
||||
ParticlesCopyShaderRD copy_shader;
|
||||
RID copy_shader_version;
|
||||
RID copy_pipelines[COPY_MODE_MAX];
|
||||
|
||||
} particles_shader;
|
||||
|
||||
Particles *particle_update_list = nullptr;
|
||||
|
||||
struct ParticlesShaderData : public ShaderData {
|
||||
bool valid;
|
||||
RID version;
|
||||
|
||||
//RenderPipelineVertexFormatCacheRD pipelines[SKY_VERSION_MAX];
|
||||
Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
|
||||
Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms;
|
||||
|
||||
Vector<uint32_t> ubo_offsets;
|
||||
uint32_t ubo_size;
|
||||
|
||||
String path;
|
||||
String code;
|
||||
Map<StringName, RID> default_texture_params;
|
||||
|
||||
RID pipeline;
|
||||
|
||||
bool uses_time;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture);
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
|
||||
virtual void get_instance_param_list(List<RasterizerStorage::InstanceShaderParam> *p_param_list) const;
|
||||
virtual bool is_param_texture(const StringName &p_param) const;
|
||||
virtual bool is_animated() const;
|
||||
virtual bool casts_shadows() const;
|
||||
virtual Variant get_default_parameter(const StringName &p_parameter) const;
|
||||
ParticlesShaderData();
|
||||
virtual ~ParticlesShaderData();
|
||||
};
|
||||
|
||||
ShaderData *_create_particles_shader_func();
|
||||
static RasterizerStorageRD::ShaderData *_create_particles_shader_funcs() {
|
||||
return base_singleton->_create_particles_shader_func();
|
||||
}
|
||||
|
||||
struct ParticlesMaterialData : public MaterialData {
|
||||
uint64_t last_frame;
|
||||
ParticlesShaderData *shader_data;
|
||||
RID uniform_buffer;
|
||||
RID uniform_set;
|
||||
Vector<RID> texture_cache;
|
||||
Vector<uint8_t> ubo_data;
|
||||
bool uniform_set_updated;
|
||||
|
||||
virtual void set_render_priority(int p_priority) {}
|
||||
virtual void set_next_pass(RID p_pass) {}
|
||||
virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
|
||||
virtual ~ParticlesMaterialData();
|
||||
};
|
||||
|
||||
MaterialData *_create_particles_material_func(ParticlesShaderData *p_shader);
|
||||
static RasterizerStorageRD::MaterialData *_create_particles_material_funcs(ShaderData *p_shader) {
|
||||
return base_singleton->_create_particles_material_func(static_cast<ParticlesShaderData *>(p_shader));
|
||||
}
|
||||
|
||||
void update_particles();
|
||||
|
||||
mutable RID_Owner<Particles> particles_owner;
|
||||
|
||||
/* Skeleton */
|
||||
|
||||
struct Skeleton {
|
||||
|
@ -977,6 +1191,19 @@ public:
|
|||
return s->multimesh_render_index;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t mesh_surface_get_particles_render_pass_index(RID p_mesh, uint32_t p_surface_index, uint64_t p_render_pass, uint32_t *r_index) {
|
||||
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
||||
Mesh::Surface *s = mesh->surfaces[p_surface_index];
|
||||
|
||||
if (s->particles_render_pass != p_render_pass) {
|
||||
(*r_index)++;
|
||||
s->particles_render_pass = p_render_pass;
|
||||
s->particles_render_index = *r_index;
|
||||
}
|
||||
|
||||
return s->particles_render_index;
|
||||
}
|
||||
|
||||
/* MULTIMESH API */
|
||||
|
||||
RID multimesh_create();
|
||||
|
@ -1407,39 +1634,75 @@ public:
|
|||
|
||||
/* PARTICLES */
|
||||
|
||||
RID particles_create() { return RID(); }
|
||||
RID particles_create();
|
||||
|
||||
void particles_set_emitting(RID p_particles, bool p_emitting) {}
|
||||
void particles_set_amount(RID p_particles, int p_amount) {}
|
||||
void particles_set_lifetime(RID p_particles, float p_lifetime) {}
|
||||
void particles_set_one_shot(RID p_particles, bool p_one_shot) {}
|
||||
void particles_set_pre_process_time(RID p_particles, float p_time) {}
|
||||
void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) {}
|
||||
void particles_set_randomness_ratio(RID p_particles, float p_ratio) {}
|
||||
void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) {}
|
||||
void particles_set_speed_scale(RID p_particles, float p_scale) {}
|
||||
void particles_set_use_local_coordinates(RID p_particles, bool p_enable) {}
|
||||
void particles_set_process_material(RID p_particles, RID p_material) {}
|
||||
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_set_emitting(RID p_particles, bool p_emitting);
|
||||
void particles_set_amount(RID p_particles, int p_amount);
|
||||
void particles_set_lifetime(RID p_particles, float p_lifetime);
|
||||
void particles_set_one_shot(RID p_particles, bool p_one_shot);
|
||||
void particles_set_pre_process_time(RID p_particles, float p_time);
|
||||
void particles_set_explosiveness_ratio(RID p_particles, float p_ratio);
|
||||
void particles_set_randomness_ratio(RID p_particles, float p_ratio);
|
||||
void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb);
|
||||
void particles_set_speed_scale(RID p_particles, float p_scale);
|
||||
void particles_set_use_local_coordinates(RID p_particles, bool p_enable);
|
||||
void particles_set_process_material(RID p_particles, RID p_material);
|
||||
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_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) {}
|
||||
void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order);
|
||||
|
||||
void particles_set_draw_passes(RID p_particles, int p_count) {}
|
||||
void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) {}
|
||||
void particles_set_draw_passes(RID p_particles, int p_count);
|
||||
void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh);
|
||||
|
||||
void particles_request_process(RID p_particles) {}
|
||||
AABB particles_get_current_aabb(RID p_particles) { return AABB(); }
|
||||
AABB particles_get_aabb(RID p_particles) const { return AABB(); }
|
||||
void particles_request_process(RID p_particles);
|
||||
AABB particles_get_current_aabb(RID p_particles);
|
||||
AABB particles_get_aabb(RID p_particles) const;
|
||||
|
||||
void particles_set_emission_transform(RID p_particles, const Transform &p_transform) {}
|
||||
void particles_set_emission_transform(RID p_particles, const Transform &p_transform);
|
||||
|
||||
bool particles_get_emitting(RID p_particles) { return false; }
|
||||
int particles_get_draw_passes(RID p_particles) const { return 0; }
|
||||
RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { return RID(); }
|
||||
bool particles_get_emitting(RID p_particles);
|
||||
int particles_get_draw_passes(RID p_particles) const;
|
||||
RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const;
|
||||
|
||||
virtual bool particles_is_inactive(RID p_particles) const { return false; }
|
||||
void particles_set_view_axis(RID p_particles, const Vector3 &p_axis);
|
||||
|
||||
virtual bool particles_is_inactive(RID p_particles) const;
|
||||
|
||||
_FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, 0);
|
||||
|
||||
return particles->amount;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, false);
|
||||
|
||||
return particles->use_local_coords;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ RID particles_get_instance_buffer_uniform_set(RID p_particles, RID p_shader, uint32_t p_set) {
|
||||
Particles *particles = particles_owner.getornull(p_particles);
|
||||
ERR_FAIL_COND_V(!particles, RID());
|
||||
if (particles->particles_transforms_buffer_uniform_set.is_null()) {
|
||||
Vector<RD::Uniform> uniforms;
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 0;
|
||||
u.ids.push_back(particles->particle_instance_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
particles->particles_transforms_buffer_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set);
|
||||
}
|
||||
|
||||
return particles->particles_transforms_buffer_uniform_set;
|
||||
}
|
||||
|
||||
/* GLOBAL VARIABLES API */
|
||||
|
||||
|
|
|
@ -537,6 +537,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
|||
|
||||
r_gen_code.vertex_global += struct_code;
|
||||
r_gen_code.fragment_global += struct_code;
|
||||
r_gen_code.compute_global += struct_code;
|
||||
}
|
||||
|
||||
int max_texture_uniforms = 0;
|
||||
|
@ -591,6 +592,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
|||
if (SL::is_sampler_type(E->get().type)) {
|
||||
r_gen_code.vertex_global += ucode;
|
||||
r_gen_code.fragment_global += ucode;
|
||||
r_gen_code.compute_global += ucode;
|
||||
|
||||
GeneratedCode::Texture texture;
|
||||
texture.name = E->key();
|
||||
|
@ -700,6 +702,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
|||
vcode += ";\n";
|
||||
r_gen_code.vertex_global += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode;
|
||||
r_gen_code.fragment_global += "layout(location=" + itos(index) + ") " + interp_mode + "in " + vcode;
|
||||
r_gen_code.compute_global += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode;
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -724,6 +727,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
|||
gcode += ";\n";
|
||||
r_gen_code.vertex_global += gcode;
|
||||
r_gen_code.fragment_global += gcode;
|
||||
r_gen_code.compute_global += gcode;
|
||||
}
|
||||
|
||||
Map<StringName, String> function_code;
|
||||
|
@ -741,6 +745,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
|||
|
||||
Set<StringName> added_vtx;
|
||||
Set<StringName> added_fragment; //share for light
|
||||
Set<StringName> added_compute; //share for light
|
||||
|
||||
for (int i = 0; i < pnode->functions.size(); i++) {
|
||||
SL::FunctionNode *fnode = pnode->functions[i].function;
|
||||
|
@ -763,6 +768,12 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
|||
_dump_function_deps(pnode, fnode->name, function_code, r_gen_code.fragment_global, added_fragment);
|
||||
r_gen_code.light = function_code[light_name];
|
||||
}
|
||||
|
||||
if (fnode->name == compute_name) {
|
||||
_dump_function_deps(pnode, fnode->name, function_code, r_gen_code.compute_global, added_compute);
|
||||
r_gen_code.compute = function_code[compute_name];
|
||||
}
|
||||
|
||||
function = nullptr;
|
||||
}
|
||||
|
||||
|
@ -1245,6 +1256,8 @@ Error ShaderCompilerRD::compile(RS::ShaderMode p_mode, const String &p_code, Ide
|
|||
r_gen_code.vertex_global = String();
|
||||
r_gen_code.fragment = String();
|
||||
r_gen_code.fragment_global = String();
|
||||
r_gen_code.compute = String();
|
||||
r_gen_code.compute_global = String();
|
||||
r_gen_code.light = String();
|
||||
r_gen_code.uses_fragment_time = false;
|
||||
r_gen_code.uses_vertex_time = false;
|
||||
|
@ -1266,6 +1279,7 @@ void ShaderCompilerRD::initialize(DefaultIdentifierActions p_actions) {
|
|||
|
||||
vertex_name = "vertex";
|
||||
fragment_name = "fragment";
|
||||
compute_name = "compute";
|
||||
light_name = "light";
|
||||
time_name = "TIME";
|
||||
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
String fragment_global;
|
||||
String fragment;
|
||||
String light;
|
||||
String compute_global;
|
||||
String compute;
|
||||
|
||||
bool uses_global_textures;
|
||||
bool uses_fragment_time;
|
||||
|
@ -104,6 +106,7 @@ private:
|
|||
StringName vertex_name;
|
||||
StringName fragment_name;
|
||||
StringName light_name;
|
||||
StringName compute_name;
|
||||
StringName time_name;
|
||||
Set<StringName> texture_functions;
|
||||
|
||||
|
|
|
@ -37,3 +37,6 @@ if "RD_GLSL" in env["BUILDERS"]:
|
|||
env.RD_GLSL("sdfgi_debug_probes.glsl")
|
||||
env.RD_GLSL("volumetric_fog.glsl")
|
||||
env.RD_GLSL("shadow_reduce.glsl")
|
||||
env.RD_GLSL("particles.glsl")
|
||||
env.RD_GLSL("particles_copy.glsl")
|
||||
env.RD_GLSL("sort.glsl")
|
||||
|
|
262
servers/rendering/rasterizer_rd/shaders/particles.glsl
Normal file
262
servers/rendering/rasterizer_rd/shaders/particles.glsl
Normal file
|
@ -0,0 +1,262 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#define SAMPLER_NEAREST_CLAMP 0
|
||||
#define SAMPLER_LINEAR_CLAMP 1
|
||||
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
|
||||
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
|
||||
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
|
||||
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
|
||||
#define SAMPLER_NEAREST_REPEAT 6
|
||||
#define SAMPLER_LINEAR_REPEAT 7
|
||||
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
|
||||
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
|
||||
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
|
||||
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
|
||||
|
||||
/* SET 0: GLOBAL DATA */
|
||||
|
||||
layout(set = 0, binding = 1) uniform sampler material_samplers[12];
|
||||
|
||||
layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalVariableData {
|
||||
vec4 data[];
|
||||
}
|
||||
global_variables;
|
||||
|
||||
/* Set 1: FRAME AND PARTICLE DATA */
|
||||
|
||||
// a frame history is kept for trail deterministic behavior
|
||||
struct FrameParams {
|
||||
bool emitting;
|
||||
float system_phase;
|
||||
float prev_system_phase;
|
||||
uint cycle;
|
||||
|
||||
float explosiveness;
|
||||
float randomness;
|
||||
float time;
|
||||
float delta;
|
||||
|
||||
uint random_seed;
|
||||
uint pad[3];
|
||||
|
||||
mat4 emission_transform;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
|
||||
FrameParams data[];
|
||||
}
|
||||
frame_history;
|
||||
|
||||
struct ParticleData {
|
||||
mat4 xform;
|
||||
vec3 velocity;
|
||||
bool is_active;
|
||||
vec4 color;
|
||||
vec4 custom;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 1, std430) restrict buffer Particles {
|
||||
ParticleData data[];
|
||||
}
|
||||
particles;
|
||||
|
||||
/* SET 2: MATERIAL */
|
||||
|
||||
#ifdef USE_MATERIAL_UNIFORMS
|
||||
layout(set = 2, binding = 0, std140) uniform MaterialUniforms{
|
||||
/* clang-format off */
|
||||
MATERIAL_UNIFORMS
|
||||
/* clang-format on */
|
||||
} material;
|
||||
#endif
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
float lifetime;
|
||||
bool clear;
|
||||
uint total_particles;
|
||||
uint trail_size;
|
||||
bool use_fractional_delta;
|
||||
uint pad[3];
|
||||
}
|
||||
params;
|
||||
|
||||
uint hash(uint x) {
|
||||
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
|
||||
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
|
||||
x = (x >> uint(16)) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
COMPUTE_SHADER_GLOBALS
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
uint particle = gl_GlobalInvocationID.x;
|
||||
|
||||
if (particle >= params.total_particles * params.trail_size) {
|
||||
return; //discard
|
||||
}
|
||||
|
||||
uint index = particle / params.trail_size;
|
||||
uint frame = (particle % params.trail_size);
|
||||
|
||||
#define FRAME frame_history.data[frame]
|
||||
#define PARTICLE particles.data[particle]
|
||||
|
||||
bool apply_forces = true;
|
||||
bool apply_velocity = true;
|
||||
float local_delta = FRAME.delta;
|
||||
|
||||
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
|
||||
|
||||
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.xform = mat4(
|
||||
vec4(1.0, 0.0, 0.0, 0.0),
|
||||
vec4(0.0, 1.0, 0.0, 0.0),
|
||||
vec4(0.0, 0.0, 1.0, 0.0),
|
||||
vec4(0.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
|
||||
if (PARTICLE.is_active) {
|
||||
/* clang-format off */
|
||||
|
||||
COMPUTE_SHADER_CODE
|
||||
|
||||
/* clang-format on */
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_VELOCITY)
|
||||
|
||||
if (PARTICLE.is_active) {
|
||||
PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (PARTICLE.is_active) {
|
||||
//execute shader
|
||||
|
||||
|
||||
|
||||
|
||||
//!defined(DISABLE_FORCE)
|
||||
|
||||
if (false) {
|
||||
vec3 force = vec3(0.0);
|
||||
for (int i = 0; i < attractor_count; i++) {
|
||||
vec3 rel_vec = xform[3].xyz - attractors[i].pos;
|
||||
float dist = length(rel_vec);
|
||||
if (attractors[i].radius < dist)
|
||||
continue;
|
||||
if (attractors[i].eat_radius > 0.0 && attractors[i].eat_radius > dist) {
|
||||
out_velocity_active.a = 0.0;
|
||||
}
|
||||
|
||||
rel_vec = normalize(rel_vec);
|
||||
|
||||
float attenuation = pow(dist / attractors[i].radius, attractors[i].attenuation);
|
||||
|
||||
if (attractors[i].dir == vec3(0.0)) {
|
||||
//towards center
|
||||
force += attractors[i].strength * rel_vec * attenuation * mass;
|
||||
} else {
|
||||
force += attractors[i].strength * attractors[i].dir * attenuation * mass;
|
||||
}
|
||||
}
|
||||
|
||||
out_velocity_active.xyz += force * local_delta;
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_VELOCITY)
|
||||
|
||||
if (true) {
|
||||
xform[3].xyz += out_velocity_active.xyz * local_delta;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
xform = mat4(0.0);
|
||||
}
|
||||
|
||||
|
||||
xform = transpose(xform);
|
||||
|
||||
out_velocity_active.a = mix(0.0, 1.0, shader_active);
|
||||
|
||||
out_xform_1 = xform[0];
|
||||
out_xform_2 = xform[1];
|
||||
out_xform_3 = xform[2];
|
||||
#endif
|
||||
}
|
82
servers/rendering/rasterizer_rd/shaders/particles_copy.glsl
Normal file
82
servers/rendering/rasterizer_rd/shaders/particles_copy.glsl
Normal file
|
@ -0,0 +1,82 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
struct ParticleData {
|
||||
mat4 xform;
|
||||
vec3 velocity;
|
||||
bool is_active;
|
||||
vec4 color;
|
||||
vec4 custom;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 1, std430) restrict readonly buffer Particles {
|
||||
ParticleData data[];
|
||||
}
|
||||
particles;
|
||||
|
||||
layout(set = 0, binding = 2, std430) restrict writeonly buffer Transforms {
|
||||
vec4 data[];
|
||||
}
|
||||
instances;
|
||||
|
||||
#ifdef USE_SORT_BUFFER
|
||||
|
||||
layout(set = 1, binding = 0, std430) restrict buffer SortBuffer {
|
||||
vec2 data[];
|
||||
}
|
||||
sort_buffer;
|
||||
|
||||
#endif // USE_SORT_BUFFER
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
vec3 sort_direction;
|
||||
uint total_particles;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_FILL_SORT_BUFFER
|
||||
|
||||
uint particle = gl_GlobalInvocationID.x;
|
||||
if (particle >= params.total_particles) {
|
||||
return; //discard
|
||||
}
|
||||
|
||||
sort_buffer.data[particle].x = dot(params.sort_direction, particles.data[particle].xform[3].xyz);
|
||||
sort_buffer.data[particle].y = float(particle);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_FILL_INSTANCES
|
||||
|
||||
uint particle = gl_GlobalInvocationID.x;
|
||||
uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom
|
||||
|
||||
if (particle >= params.total_particles) {
|
||||
return; //discard
|
||||
}
|
||||
|
||||
#ifdef USE_SORT_BUFFER
|
||||
particle = uint(sort_buffer.data[particle].y); //use index from sort buffer
|
||||
#endif
|
||||
|
||||
mat4 txform;
|
||||
|
||||
if (particles.data[particle].is_active) {
|
||||
txform = transpose(particles.data[particle].xform);
|
||||
} else {
|
||||
txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); //zero scale, becomes invisible
|
||||
}
|
||||
|
||||
instances.data[write_offset + 0] = txform[0];
|
||||
instances.data[write_offset + 1] = txform[1];
|
||||
instances.data[write_offset + 2] = txform[2];
|
||||
instances.data[write_offset + 3] = particles.data[particle].color;
|
||||
instances.data[write_offset + 4] = particles.data[particle].custom;
|
||||
|
||||
#endif
|
||||
}
|
203
servers/rendering/rasterizer_rd/shaders/sort.glsl
Normal file
203
servers/rendering/rasterizer_rd/shaders/sort.glsl
Normal file
|
@ -0,0 +1,203 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
VERSION_DEFINES
|
||||
|
||||
// Original version here:
|
||||
// https://github.com/GPUOpen-LibrariesAndSDKs/GPUParticles11/blob/master/gpuparticles11/src/Shaders
|
||||
|
||||
//
|
||||
// Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#define SORT_SIZE 512
|
||||
#define NUM_THREADS (SORT_SIZE / 2)
|
||||
#define INVERSION (16 * 2 + 8 * 3)
|
||||
#define ITERATIONS 1
|
||||
|
||||
layout(local_size_x = NUM_THREADS, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#ifndef MODE_SORT_STEP
|
||||
|
||||
shared vec2 g_LDS[SORT_SIZE];
|
||||
|
||||
#endif
|
||||
|
||||
layout(set = 1, binding = 0, std430) restrict buffer SortBuffer {
|
||||
vec2 data[];
|
||||
}
|
||||
sort_buffer;
|
||||
|
||||
layout(push_constant, binding = 0, std430) uniform Params {
|
||||
uint total_elements;
|
||||
uint pad[3];
|
||||
ivec4 job_params;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_SORT_BLOCK
|
||||
|
||||
uvec3 Gid = gl_WorkGroupID;
|
||||
uvec3 DTid = gl_GlobalInvocationID;
|
||||
uvec3 GTid = gl_LocalInvocationID;
|
||||
uint GI = gl_LocalInvocationIndex;
|
||||
|
||||
int GlobalBaseIndex = int((Gid.x * SORT_SIZE) + GTid.x);
|
||||
int LocalBaseIndex = int(GI);
|
||||
int numElementsInThreadGroup = int(min(SORT_SIZE, params.total_elements - (Gid.x * SORT_SIZE)));
|
||||
|
||||
// Load shared data
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 2 * ITERATIONS; ++i) {
|
||||
if (GI + i * NUM_THREADS < numElementsInThreadGroup)
|
||||
g_LDS[LocalBaseIndex + i * NUM_THREADS] = sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS];
|
||||
}
|
||||
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
|
||||
// Bitonic sort
|
||||
for (int nMergeSize = 2; nMergeSize <= SORT_SIZE; nMergeSize = nMergeSize * 2) {
|
||||
for (int nMergeSubSize = nMergeSize >> 1; nMergeSubSize > 0; nMergeSubSize = nMergeSubSize >> 1) {
|
||||
for (i = 0; i < ITERATIONS; ++i) {
|
||||
int tmp_index = int(GI + NUM_THREADS * i);
|
||||
int index_low = tmp_index & (nMergeSubSize - 1);
|
||||
int index_high = 2 * (tmp_index - index_low);
|
||||
int index = index_high + index_low;
|
||||
|
||||
int nSwapElem = nMergeSubSize == nMergeSize >> 1 ? index_high + (2 * nMergeSubSize - 1) - index_low : index_high + nMergeSubSize + index_low;
|
||||
if (nSwapElem < numElementsInThreadGroup) {
|
||||
vec2 a = g_LDS[index];
|
||||
vec2 b = g_LDS[nSwapElem];
|
||||
|
||||
if (a.x > b.x) {
|
||||
g_LDS[index] = b;
|
||||
g_LDS[nSwapElem] = a;
|
||||
}
|
||||
}
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store shared data
|
||||
for (i = 0; i < 2 * ITERATIONS; ++i) {
|
||||
if (GI + i * NUM_THREADS < numElementsInThreadGroup) {
|
||||
sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS] = g_LDS[LocalBaseIndex + i * NUM_THREADS];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_SORT_STEP
|
||||
|
||||
uvec3 Gid = gl_WorkGroupID;
|
||||
uvec3 GTid = gl_LocalInvocationID;
|
||||
|
||||
ivec4 tgp;
|
||||
|
||||
tgp.x = int(Gid.x) * 256;
|
||||
tgp.y = 0;
|
||||
tgp.z = int(params.total_elements);
|
||||
tgp.w = min(512, max(0, tgp.z - int(Gid.x) * 512));
|
||||
|
||||
uint localID = int(tgp.x) + GTid.x; // calculate threadID within this sortable-array
|
||||
|
||||
uint index_low = localID & (params.job_params.x - 1);
|
||||
uint index_high = 2 * (localID - index_low);
|
||||
|
||||
uint index = tgp.y + index_high + index_low;
|
||||
uint nSwapElem = tgp.y + index_high + params.job_params.y + params.job_params.z * index_low;
|
||||
|
||||
if (nSwapElem < tgp.y + tgp.z) {
|
||||
vec2 a = sort_buffer.data[index];
|
||||
vec2 b = sort_buffer.data[nSwapElem];
|
||||
|
||||
if (a.x > b.x) {
|
||||
sort_buffer.data[index] = b;
|
||||
sort_buffer.data[nSwapElem] = a;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_SORT_INNER
|
||||
|
||||
uvec3 Gid = gl_WorkGroupID;
|
||||
uvec3 DTid = gl_GlobalInvocationID;
|
||||
uvec3 GTid = gl_LocalInvocationID;
|
||||
uint GI = gl_LocalInvocationIndex;
|
||||
|
||||
ivec4 tgp;
|
||||
|
||||
tgp.x = int(Gid.x * 256);
|
||||
tgp.y = 0;
|
||||
tgp.z = int(params.total_elements.x);
|
||||
tgp.w = int(min(512, max(0, params.total_elements - Gid.x * 512)));
|
||||
|
||||
int GlobalBaseIndex = int(tgp.y + tgp.x * 2 + GTid.x);
|
||||
int LocalBaseIndex = int(GI);
|
||||
int i;
|
||||
|
||||
// Load shared data
|
||||
for (i = 0; i < 2; ++i) {
|
||||
if (GI + i * NUM_THREADS < tgp.w)
|
||||
g_LDS[LocalBaseIndex + i * NUM_THREADS] = sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS];
|
||||
}
|
||||
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
|
||||
// sort threadgroup shared memory
|
||||
for (int nMergeSubSize = SORT_SIZE >> 1; nMergeSubSize > 0; nMergeSubSize = nMergeSubSize >> 1) {
|
||||
int tmp_index = int(GI);
|
||||
int index_low = tmp_index & (nMergeSubSize - 1);
|
||||
int index_high = 2 * (tmp_index - index_low);
|
||||
int index = index_high + index_low;
|
||||
|
||||
int nSwapElem = index_high + nMergeSubSize + index_low;
|
||||
|
||||
if (nSwapElem < tgp.w) {
|
||||
vec2 a = g_LDS[index];
|
||||
vec2 b = g_LDS[nSwapElem];
|
||||
|
||||
if (a.x > b.x) {
|
||||
g_LDS[index] = b;
|
||||
g_LDS[nSwapElem] = a;
|
||||
}
|
||||
}
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
}
|
||||
|
||||
// Store shared data
|
||||
for (i = 0; i < 2; ++i) {
|
||||
if (GI + i * NUM_THREADS < tgp.w) {
|
||||
sort_buffer.data[GlobalBaseIndex + i * NUM_THREADS] = g_LDS[LocalBaseIndex + i * NUM_THREADS];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
|
@ -2044,6 +2044,7 @@ void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const
|
|||
keep = false;
|
||||
} else {
|
||||
RSG::storage->particles_request_process(ins->base);
|
||||
RSG::storage->particles_set_view_axis(ins->base, -p_cam_transform.basis.get_axis(2).normalized());
|
||||
//particles visible? request redraw
|
||||
RenderingServerRaster::redraw_request();
|
||||
}
|
||||
|
|
|
@ -270,20 +270,20 @@ ShaderTypes::ShaderTypes() {
|
|||
/************ PARTICLES **************************/
|
||||
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["ACTIVE"] = ShaderLanguage::TYPE_BOOL;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["RESTART"] = constt(ShaderLanguage::TYPE_BOOL);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["CUSTOM"] = ShaderLanguage::TYPE_VEC4;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["vertex"].can_discard = false;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["ACTIVE"] = ShaderLanguage::TYPE_BOOL;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART"] = constt(ShaderLanguage::TYPE_BOOL);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["CUSTOM"] = ShaderLanguage::TYPE_VEC4;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT);
|
||||
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT);
|
||||
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"].can_discard = false;
|
||||
|
||||
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_force");
|
||||
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity");
|
||||
|
|
Loading…
Reference in a new issue