summaryrefslogtreecommitdiff
path: root/drivers/gles3/storage/particles_storage.h
blob: b220c48de98785e9cca0da979b67cabfe1dcf3da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/**************************************************************************/
/*  particles_storage.h                                                   */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* 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.                 */
/**************************************************************************/

#ifndef PARTICLES_STORAGE_GLES3_H
#define PARTICLES_STORAGE_GLES3_H

#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;

	virtual void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override;
	virtual void particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override;
	virtual void particles_set_emitting(RID p_particles, bool p_emitting) override;
	virtual void particles_set_amount(RID p_particles, int p_amount) override;
	virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override;
	virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override;
	virtual void particles_set_pre_process_time(RID p_particles, double p_time) override;
	virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override;
	virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override;
	virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override;
	virtual void particles_set_speed_scale(RID p_particles, double p_scale) override;
	virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override;
	virtual void particles_set_process_material(RID p_particles, RID p_material) override;
	virtual RID particles_get_process_material(RID p_particles) const override;
	virtual void particles_set_fixed_fps(RID p_particles, int p_fps) override;
	virtual void particles_set_interpolate(RID p_particles, bool p_enable) override;
	virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) override;
	virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override;
	virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override;
	virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override;

	virtual void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override;

	virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override;
	virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override;

	virtual void particles_restart(RID p_particles) override;

	virtual void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override;

	virtual void particles_set_draw_passes(RID p_particles, int p_count) override;
	virtual void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override;

	virtual void particles_request_process(RID p_particles) override;
	virtual AABB particles_get_current_aabb(RID p_particles) override;
	virtual AABB particles_get_aabb(RID p_particles) const override;

	virtual void particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) override;

	virtual bool particles_get_emitting(RID p_particles) override;
	virtual int particles_get_draw_passes(RID p_particles) const override;
	virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override;

	virtual void particles_add_collision(RID p_particles, RID p_instance) override;
	virtual void particles_remove_collision(RID p_particles, RID p_instance) 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;
	virtual void particles_collision_free(RID p_rid) override;

	virtual void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override;
	virtual void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override;
	virtual void particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) override;
	virtual void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override;
	virtual void particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) override;
	virtual void particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) override;
	virtual void particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) override;
	virtual void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override;
	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;
	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;
	virtual void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override;
	virtual void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override;
};

} // namespace GLES3

#endif // GLES3_ENABLED

#endif // PARTICLES_STORAGE_GLES3_H