diff options
Diffstat (limited to 'drivers/gles3/storage/particles_storage.h')
| -rw-r--r-- | drivers/gles3/storage/particles_storage.h | 315 |
1 files changed, 313 insertions, 2 deletions
diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index 84d1f94d8c..434718006e 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -33,25 +33,283 @@ #ifdef GLES3_ENABLED +#include "../shaders/particles_copy.glsl.gen.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "servers/rendering/storage/particles_storage.h" +#include "servers/rendering/storage/utilities.h" + +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif namespace GLES3 { +enum ParticlesUniformLocation { + PARTICLES_FRAME_UNIFORM_LOCATION, + PARTICLES_GLOBALS_UNIFORM_LOCATION, + PARTICLES_MATERIAL_UNIFORM_LOCATION, +}; + class ParticlesStorage : public RendererParticlesStorage { private: static ParticlesStorage *singleton; + /* PARTICLES */ + + struct ParticleInstanceData3D { + float xform[12]; + float color[2]; // Color and custom are packed together into one vec4; + float custom[2]; + }; + + struct ParticleInstanceData2D { + float xform[8]; + float color[2]; // Color and custom are packed together into one vec4; + float custom[2]; + }; + + struct ParticlesViewSort { + Vector3 z_dir; + bool operator()(const ParticleInstanceData3D &p_a, const ParticleInstanceData3D &p_b) const { + return z_dir.dot(Vector3(p_a.xform[3], p_a.xform[7], p_a.xform[11])) < z_dir.dot(Vector3(p_b.xform[3], p_b.xform[7], p_b.xform[11])); + } + }; + + struct ParticlesFrameParams { + enum { + MAX_ATTRACTORS = 32, + MAX_COLLIDERS = 32, + MAX_3D_TEXTURES = 0 // GLES3 renderer doesn't support using 3D textures for flow field or collisions. + }; + + enum AttractorType { + ATTRACTOR_TYPE_SPHERE, + ATTRACTOR_TYPE_BOX, + ATTRACTOR_TYPE_VECTOR_FIELD, + }; + + struct Attractor { + float transform[16]; + float extents[4]; // Extents or radius. w-channel is padding. + + uint32_t type; + float strength; + float attenuation; + float directionality; + }; + + enum CollisionType { + COLLISION_TYPE_SPHERE, + COLLISION_TYPE_BOX, + COLLISION_TYPE_SDF, + COLLISION_TYPE_HEIGHT_FIELD, + COLLISION_TYPE_2D_SDF, + + }; + + struct Collider { + float transform[16]; + float extents[4]; // Extents or radius. w-channel is padding. + + uint32_t type; + float scale; + float pad0; + float pad1; + }; + + uint32_t emitting; + uint32_t cycle; + float system_phase; + float prev_system_phase; + + float explosiveness; + float randomness; + float time; + float delta; + + float particle_size; + float pad0; + float pad1; + float pad2; + + uint32_t random_seed; + uint32_t attractor_count; + uint32_t collider_count; + uint32_t frame; + + float emission_transform[16]; + + Attractor attractors[MAX_ATTRACTORS]; + Collider colliders[MAX_COLLIDERS]; + }; + + struct Particles { + RS::ParticlesMode mode = RS::PARTICLES_MODE_3D; + bool inactive = true; + double inactive_time = 0.0; + bool emitting = false; + bool one_shot = false; + int amount = 0; + double lifetime = 1.0; + double pre_process_time = 0.0; + real_t explosiveness = 0.0; + real_t randomness = 0.0; + bool restart_request = false; + AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)); + bool use_local_coords = false; + bool has_collision_cache = false; + + bool has_sdf_collision = false; + Transform2D sdf_collision_transform; + Rect2 sdf_collision_to_screen; + GLuint sdf_collision_texture = 0; + + RID process_material; + uint32_t frame_counter = 0; + RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED; + + RS::ParticlesDrawOrder draw_order = RS::PARTICLES_DRAW_ORDER_INDEX; + + Vector<RID> draw_passes; + + GLuint frame_params_ubo = 0; + + // We may process particles multiple times each frame (if they have a fixed FPS higher than the game FPS). + // Unfortunately, this means we can't just use a round-robin system of 3 buffers. + // To ensure the sort buffer is accurate, we copy the last frame instance buffer just before processing. + + // Transform Feedback buffer and VAO for rendering. + // Each frame we render to this one. + GLuint front_vertex_array = 0; // Binds process buffer. Used for processing. + GLuint front_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing. + GLuint front_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering. + + // VAO for transform feedback, contains last frame's data. + // Read from this one for particles process and then copy to last frame buffer. + GLuint back_vertex_array = 0; // Binds process buffer. Used for processing. + GLuint back_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing. + GLuint back_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering. + + uint32_t instance_buffer_size_cache = 0; + uint32_t instance_buffer_stride_cache = 0; + uint32_t num_attrib_arrays_cache = 0; + uint32_t process_buffer_stride_cache = 0; + + // Only ever copied to, holds last frame's instance data, then swaps with sort_buffer. + GLuint last_frame_buffer = 0; + bool last_frame_buffer_filled = false; + float last_frame_phase = 0.0; + + // The frame-before-last's instance buffer. + // Use this to copy data back for sorting or computing AABB. + GLuint sort_buffer = 0; + bool sort_buffer_filled = false; + float sort_buffer_phase = 0.0; + + uint32_t userdata_count = 0; + + bool dirty = false; + Particles *update_list = nullptr; + + double phase = 0.0; + double prev_phase = 0.0; + uint64_t prev_ticks = 0; + uint32_t random_seed = 0; + + uint32_t cycle_number = 0; + + double speed_scale = 1.0; + + int fixed_fps = 30; + bool interpolate = true; + bool fractional_delta = false; + double frame_remainder = 0; + real_t collision_base_size = 0.01; + + bool clear = true; + + Transform3D emission_transform; + + HashSet<RID> collisions; + + Dependency dependency; + + double trail_length = 1.0; + bool trails_enabled = false; + + Particles() { + } + }; + + void _particles_process(Particles *p_particles, double p_delta); + void _particles_free_data(Particles *particles); + void _particles_update_buffers(Particles *particles); + void _particles_allocate_history_buffers(Particles *particles); + void _particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis); + + template <typename T> + void _particles_reverse_lifetime_sort(Particles *particles); + + struct ParticlesShader { + RID default_shader; + RID default_material; + RID default_shader_version; + + ParticlesCopyShaderGLES3 copy_shader; + RID copy_shader_version; + } particles_shader; + + Particles *particle_update_list = nullptr; + + mutable RID_Owner<Particles, true> particles_owner; + + /* Particles Collision */ + + struct ParticlesCollision { + RS::ParticlesCollisionType type = RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT; + uint32_t cull_mask = 0xFFFFFFFF; + float radius = 1.0; + Vector3 extents = Vector3(1, 1, 1); + float attractor_strength = 1.0; + float attractor_attenuation = 1.0; + float attractor_directionality = 0.0; + GLuint field_texture = 0; + GLuint heightfield_texture = 0; + GLuint heightfield_fb = 0; + Size2i heightfield_fb_size; + + RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024; + + Dependency dependency; + }; + + struct ParticlesCollisionInstance { + RID collision; + Transform3D transform; + bool active = false; + }; + + mutable RID_Owner<ParticlesCollision, true> particles_collision_owner; + + mutable RID_Owner<ParticlesCollisionInstance> particles_collision_instance_owner; + public: static ParticlesStorage *get_singleton(); ParticlesStorage(); virtual ~ParticlesStorage(); + bool free(RID p_rid); + /* PARTICLES */ + bool owns_particles(RID p_rid) { return particles_owner.owns(p_rid); } + virtual RID particles_allocate() override; virtual void particles_initialize(RID p_rid) override; virtual void particles_free(RID p_rid) override; @@ -102,12 +360,51 @@ public: virtual void particles_add_collision(RID p_particles, RID p_instance) override; virtual void particles_remove_collision(RID p_particles, RID p_instance) override; - virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override; + void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture); virtual void update_particles() override; virtual bool particles_is_inactive(RID p_particles) const override; + _FORCE_INLINE_ RS::ParticlesMode particles_get_mode(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, RS::PARTICLES_MODE_2D); + return particles->mode; + } + + _FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->amount; + } + + _FORCE_INLINE_ GLuint particles_get_gl_buffer(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + + if ((particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) && particles->sort_buffer_filled) { + return particles->sort_buffer; + } + return particles->back_instance_buffer; + } + + _FORCE_INLINE_ bool particles_has_collision(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->has_collision_cache; + } + + _FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + + return particles->use_local_coords; + } + + Dependency *particles_get_dependency(RID p_particles) const; + /* PARTICLES COLLISION */ + bool owns_particles_collision(RID p_rid) { return particles_collision_owner.owns(p_rid); } virtual RID particles_collision_allocate() override; virtual void particles_collision_initialize(RID p_rid) override; @@ -124,8 +421,22 @@ public: virtual void particles_collision_height_field_update(RID p_particles_collision) override; virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override; virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override; + Vector3 particles_collision_get_extents(RID p_particles_collision) const; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override; - virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override; + GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; + + _FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_COND_V(!particles_collision, Size2i()); + ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, Size2i()); + + return particles_collision->heightfield_fb_size; + } + + Dependency *particles_collision_get_dependency(RID p_particles) const; + + /* PARTICLES COLLISION INSTANCE*/ + bool owns_particles_collision_instance(RID p_rid) { return particles_collision_instance_owner.owns(p_rid); } virtual RID particles_collision_instance_create(RID p_collision) override; virtual void particles_collision_instance_free(RID p_rid) override; |
