particle_system.hpp 10 KB
Newer Older
1
2
#pragma once

3
4
#include <mirrage/renderer/model.hpp>

5
#include <mirrage/asset/asset_manager.hpp>
6
#include <mirrage/ecs/entity_manager.hpp>
7
8
#include <mirrage/graphic/texture.hpp>
#include <mirrage/utils/sf2_glm.hpp>
9
#include <mirrage/utils/small_vector.hpp>
10
11
12
13
14
#include <mirrage/utils/units.hpp>

#include <glm/vec3.hpp>
#include <sf2/sf2.hpp>

15
16
17
#include <random>
#include <tuple>

18
19
20

namespace mirrage::renderer {

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
	// TODO: only for reference during IMPL, delete before merge
	// emitter: {Particle_emitter_config, offset, relative-rotation, follow_entity, time-acc, toSpawn, particle-data, particle-cout, userdata?}
	// affector: {position, rotation, relative/absolute, follow_entity, force:float, dir, decay:float}
	//			gravity: {..., force=10, dir={0,-1,0}, decay=0}
	//			point: {position=? dir={0,0,0}, decay=2}
	//			flow: {position=? dir={1,0,0}, decay=2}
	// particle_system: {shared_ptr<emitter>[], offset, relative-rotation, follow_entity, affector}


	class Particle_script {
	  public:
		explicit Particle_script(vk::UniquePipeline pipeline) : _pipeline(std::move(pipeline)) {}

		void bind(vk::CommandBuffer);

	  private:
		vk::UniquePipeline _pipeline;
	};

	enum class Particle_blend_mode { solid, volumn, transparent };
	sf2_enumDef(Particle_blend_mode, solid, volumn, transparent);
42

43
44
	enum class Particle_geometry { billboard, ribbon, mesh };
	sf2_enumDef(Particle_geometry, billboard, ribbon, mesh);
45
46
47
48
49
50
51
52
53
54
55

	template <typename T = std::uint8_t>
	struct Particle_color {
		T hue;
		T saturation;
		T value;
		T alpha;
	};
	sf2_structDef(Particle_color<float>, hue, saturation, value, alpha);
	sf2_structDef(Particle_color<std::uint8_t>, hue, saturation, value, alpha);

56
57
58
59
60
61
62
	struct Particle_emitter_spawn {
		float spawn_rate_mean     = 10.f;
		float spawn_rate_variance = 1.f;
		float time                = -1.f;
	};
	sf2_structDef(Particle_emitter_spawn, spawn_rate_mean, spawn_rate_variance, time);

63
64
65
	struct Particle_emitter_config {
		float time = 0;

66
		util::small_vector<Particle_emitter_spawn, 4> spawn;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

		Particle_color<float> color_mean            = {1, 1, 1, 1};
		Particle_color<float> color_variance        = {0, 0, 0, 0};
		Particle_color<float> color_change_mean     = {0, 0, 0, 0};
		Particle_color<float> color_change_variance = {0, 0, 0, 0};

		float size_mean            = 0.1f;
		float size_variance        = 0.f;
		float size_change_mean     = 0.f;
		float size_change_variance = 0.f;

		float rotation_mean            = 0.1f;
		float rotation_variance        = 0.1f;
		float rotation_change_mean     = 0.f;
		float rotation_change_variance = 0.f;

		float ttl_mean     = 1.f;
		float ttl_variance = 0.f;

86
87
		float velocity_mean     = 1.f;
		float velocity_variance = 0.f;
88

89
90
91
92
93
94
95
96
97
98
99
		Particle_blend_mode blend    = Particle_blend_mode::transparent;
		Particle_geometry   geometry = Particle_geometry::billboard;

		std::string            material_id;
		renderer::Material_ptr material;

		std::string                 model_id;
		asset::Ptr<renderer::Model> model;

		float drag            = 0.f;
		float parent_velocity = 0.f;
100

101
102
		glm::vec3 offset{0, 0, 0};
		glm::quat rotation{1, 0, 0, 0};
103

104
105
106
107
108
		std::string emit_script_id;
		std::string update_script_id;

		asset::Ptr<Particle_script> emit_script;
		asset::Ptr<Particle_script> update_script;
109
110
111
	};
	sf2_structDef(Particle_emitter_config,
	              time,
112
	              spawn,
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
	              color_mean,
	              color_variance,
	              color_change_mean,
	              color_change_variance,
	              size_mean,
	              size_variance,
	              size_change_mean,
	              size_change_variance,
	              rotation_mean,
	              rotation_variance,
	              rotation_change_mean,
	              rotation_change_variance,
	              ttl_mean,
	              ttl_variance,
	              velocity_mean,
	              velocity_variance,
	              blend,
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
	              geometry,
	              material_id,
	              model_id,
	              drag,
	              parent_velocity,
	              emit_script_id,
	              update_script_id);

	struct Particle_effector_config {
		glm::vec3 position{0, 0, 0};
		glm::quat rotation{1, 0, 0, 0};

		float     force = 0.f;
		glm::vec3 force_dir{0, 0, 0};
		float     distance_decay = 2.f;
	};
	sf2_structDef(Particle_effector_config, position, rotation, force, force_dir, distance_decay);

	struct Particle_system_config {
		util::small_vector<Particle_emitter_config, 1>  emitter;
		util::small_vector<Particle_effector_config, 1> effector;
	};
	sf2_structDef(Particle_system_config, emitter, effector);
153
154
155
156


	class Particle_emitter {
	  public:
157
		explicit Particle_emitter(const Particle_emitter_config& cfg) : _cfg(cfg) {}
158

159
		void absolute(bool a) noexcept
160
		{
161
162
			_absolute = a;
			_follow   = {};
163
		}
164
		auto absolute() const noexcept { return _absolute; }
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
		void position(glm::vec3 p) noexcept
		{
			_position = p;
			_follow   = {};
		}
		auto position() const noexcept { return _position; }

		void rotation(glm::quat r) noexcept
		{
			_rotation = r;
			_follow   = {};
		}
		auto rotation() const noexcept { return _rotation; }

		void follow(ecs::Entity_handle e) { _follow = e; }
		auto follow() const { return _follow; }

		void incr_time(float dt) { _time_accumulator += dt; }
184

185
186
		/// returns old buffer (that might still be needed by the last frame)
		auto update_data(int count, graphic::Backed_buffer data) -> graphic::Backed_buffer;
187
188

	  private:
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
		const Particle_emitter_config& _cfg;

		// TODO: userdata?
		glm::vec3          _position{0, 0, 0};
		glm::quat          _rotation{1, 0, 0, 0};
		bool               _absolute = false;
		ecs::Entity_handle _follow;

		float                  _time_accumulator = 0.f;
		int                    _particle_count   = 0;
		graphic::Backed_buffer _particle_data;
	};

	class Particle_effector {
	  public:
		explicit Particle_effector(const Particle_effector_config& cfg) : _cfg(cfg) {}
205

206
		auto cfg() const noexcept -> auto& { return _cfg; }
207

208
209
210
211
212
213
		void absolute(bool a) noexcept
		{
			_absolute = a;
			_follow   = {};
		}
		auto absolute() const noexcept { return _absolute; }
214

215
216
217
218
219
220
		void position(glm::vec3 p) noexcept
		{
			_position = p;
			_follow   = {};
		}
		auto position() const noexcept { return _position; }
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
		void rotation(glm::quat r) noexcept
		{
			_rotation = r;
			_follow   = {};
		}
		auto rotation() const noexcept { return _rotation; }

		void follow(ecs::Entity_handle e) { _follow = e; }
		auto follow() const { return _follow; }

	  private:
		const Particle_effector_config& _cfg;

		glm::vec3          _position{0, 0, 0};
		glm::quat          _rotation{1, 0, 0, 0};
		bool               _absolute = false;
		ecs::Entity_handle _follow;
239
240
	};

241
242
243
	class Particle_emitter_ref;

	class Particle_system : private std::enable_shared_from_this<Particle_system> {
244
	  public:
245
		using Emitter_list = util::small_vector<Particle_emitter, 1>;
246

247
248
		Particle_system(asset::Ptr<Particle_system_config> cfg, glm::vec3 position, glm::quat rotation);
		explicit Particle_system(asset::Ptr<Particle_system_config> cfg, ecs::Entity_handle follow = {});
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
		auto cfg_aid() const { return _cfg.aid(); }

		auto emitters() noexcept -> auto& { return _emitters; }
		auto emitters() const noexcept -> auto& { return _emitters; }

		auto emitter(int i) -> Particle_emitter_ref;
		auto emitter_count() const noexcept { return _emitters.size(); }

		auto effectors() noexcept -> auto& { return _effectors; }
		auto effectors() const noexcept -> auto& { return _effectors; }

		void position(glm::vec3 p) noexcept
		{
			_position = p;
			_follow   = {};
		}
		auto position() const noexcept { return _position; }

		void rotation(glm::quat r) noexcept
		{
			_rotation = r;
			_follow   = {};
		}
		auto rotation() const noexcept { return _rotation; }

		void follow(ecs::Entity_handle e) { _follow = e; }
		auto follow() const { return _follow; }

	  private:
		asset::Ptr<Particle_system_config> _cfg;
		Emitter_list                       _emitters;
		std::vector<Particle_effector>     _effectors;

		glm::vec3          _position{0, 0, 0};
		glm::quat          _rotation{1, 0, 0, 0};
		ecs::Entity_handle _follow;
	};

	using Particle_system_ptr = std::shared_ptr<Particle_system>;


	class Particle_emitter_ref {
	  public:
		auto operator-> () -> Particle_emitter* { return &**this; }
		auto operator-> () const -> Particle_emitter* { return &**this; }
		auto operator*() -> Particle_emitter& { return (*_system_emitters)[_index]; }
		auto operator*() const -> Particle_emitter& { return (*_system_emitters)[_index]; }
297
298

	  private:
299
300
301
302
303
304
305
306
		using Emitter_ptr = std::shared_ptr<Particle_system::Emitter_list>;

		friend class Particle_system;

		Particle_emitter_ref(Emitter_ptr emitters, int index)
		  : _system_emitters(std::move(emitters)), _index(gsl::narrow<std::size_t>(index))
		{
		}
307

308
309
		Emitter_ptr _system_emitters;
		std::size_t _index;
310
	};
311
312
313
314
315
316
317
318
319
320
321
322
323
324


	class Particle_system_comp : public ecs::Component<Particle_system_comp> {
	  public:
		static constexpr const char* name() { return "Particle_system"; }
		friend void                  load_component(ecs::Deserializer& state, Particle_system_comp&);
		friend void                  save_component(ecs::Serializer& state, const Particle_system_comp&);

		Particle_system_comp() = default;
		Particle_system_comp(ecs::Entity_handle owner, ecs::Entity_manager& em) : Component(owner, em) {}

		Particle_system_ptr particle_system;
	};

325
} // namespace mirrage::renderer
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

namespace mirrage::asset {

	template <>
	struct Loader<renderer::Particle_script> {
	  public:
		Loader(graphic::Device& device, vk::DescriptorSetLayout);

		auto              load(istream in) -> renderer::Particle_script;
		[[noreturn]] void save(ostream, const renderer::Particle_script&)
		{
			MIRRAGE_FAIL("Save of materials is not supported!");
		}

	  private:
		graphic::Device&         _device;
		vk::UniquePipelineLayout _layout;
	};

	template <>
	struct Loader<renderer::Particle_system_config> {
	  public:
		static auto              load(istream in) -> async::task<renderer::Particle_system_config>;
		[[noreturn]] static void save(ostream, const renderer::Particle_system_config&)
		{
			MIRRAGE_FAIL("Save of materials is not supported!");
		}
	};

} // namespace mirrage::asset