particle_system.hpp 9.31 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

	struct Particle_color {
47
48
49
50
		float hue        = 0.f;
		float saturation = 0.f;
		float value      = 0.f;
		float alpha      = 0.f;
51
	};
52
	sf2_structDef(Particle_color, hue, saturation, value, alpha);
53

54
55
56
57
	template <typename T>
	struct Random_value {
		T mean{};
		T stddev{};
58
	};
59
60
61
	sf2_structDef(Random_value<float>, mean, stddev);
	sf2_structDef(Random_value<glm::vec4>, mean, stddev);
	sf2_structDef(Random_value<Particle_color>, mean, stddev);
62

63

64
65
66
67
68
69
70
71
	/// modify velocities of living particles
	/// e.g.
	/// gravity: {..., force=10, dir={0,-1,0}, decay=0, fixed_dir=true}
	/// point: {position=? dir={0,0,0}, decay=2}
	/// flow: {position=? dir={1,0,0}, decay=2}
	struct Particle_effector_config {
		glm::vec3 position{0, 0, 0};
		glm::quat rotation{1, 0, 0, 0};
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
		float     force = 0.f;
		glm::vec3 force_dir{0, 0, 0};
		float     distance_decay = 2.f;
		bool      fixed_dir      = false;

		bool scale_with_mass = true;
	};
	sf2_structDef(Particle_effector_config,
	              position,
	              rotation,
	              force,
	              force_dir,
	              distance_decay,
	              fixed_dir,
	              scale_with_mass);
88

89
90
91
92
	/// describes how living particles are updated and drawn
	struct Particle_type_config {
		Random_value<Particle_color> color        = {{1, 1, 1, 1}};
		Random_value<Particle_color> color_change = {{0, 0, 0, 0}};
93

94
95
		Random_value<glm::vec4> size        = {{1.f, 1.f, 1.f, 0.f}};
		Random_value<glm::vec4> size_change = {{0.f, 0.f, 0.f, 0.f}};
96

97
98
		float base_mass = 1.f;
		float density   = 0.f;
99

100
101
		Random_value<float> sprite_rotation        = {0.0f};
		Random_value<float> sprite_rotation_change = {0.0f};
102

103
104
105
		Particle_blend_mode blend    = Particle_blend_mode::transparent;
		Particle_geometry   geometry = Particle_geometry::billboard;

106
107
108
109
		float update_range = -1.f;
		float draw_range   = -1.f;
		bool  shadowcaster = false;

110
111
112
113
114
115
		std::string            material_id;
		renderer::Material_ptr material;

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

116
		float drag = 0.f;
117

118
		std::string                 update_script_id;
119
		asset::Ptr<Particle_script> update_script;
120
	};
121
122
123
124
125
126
127
128
129
	sf2_structDef(Particle_type_config,
	              color,
	              color_change,
	              size,
	              size_change,
	              base_mass,
	              density,
	              sprite_rotation,
	              sprite_rotation_change,
130
	              blend,
131
	              geometry,
132
133
134
	              update_range,
	              draw_range,
	              shadowcaster,
135
136
137
138
139
	              material_id,
	              model_id,
	              drag,
	              update_script_id);

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

	struct Particle_emitter_spawn {
		float particles_per_second = 10.f;
		float variance             = 0.f;
		float time                 = -1.f;
	};
	sf2_structDef(Particle_emitter_spawn, particles_per_second, variance, time);

	// describes how new particles are created
	struct Particle_emitter_config {
		util::small_vector<Particle_emitter_spawn, 4> spawn;
		bool                                          spawn_loop = true;

		Random_value<float> ttl = {1.f};

		Random_value<float> velocity = {1.f};

		float parent_velocity = 0.f;

		glm::vec3 offset{0, 0, 0};
160
161
		glm::quat rotation{1, 0, 0, 0};

162
163
164
165
166
		std::string                 emit_script_id;
		asset::Ptr<Particle_script> emit_script;

		std::string                      type_id;
		asset::Ptr<Particle_type_config> type;
167
	};
168
169
170
171
172
173
174
175
176
177
	sf2_structDef(Particle_emitter_config,
	              spawn,
	              spawn_loop,
	              ttl,
	              velocity,
	              parent_velocity,
	              offset,
	              rotation,
	              emit_script_id,
	              type_id);
178
179

	struct Particle_system_config {
180
181
		util::small_vector<Particle_emitter_config, 1> emitters;
		std::vector<Particle_effector_config>          effectors;
182
	};
183
	sf2_structDef(Particle_system_config, emitters, effectors);
184
185
186
187


	class Particle_emitter {
	  public:
188
		explicit Particle_emitter(const Particle_emitter_config& cfg) : _cfg(&cfg) {}
189

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

193
		void rotation(glm::quat r) noexcept { _rotation = r; }
194
195
196
		auto rotation() const noexcept { return _rotation; }

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

198
		auto cfg() const noexcept -> auto& { return *_cfg; }
199
200

	  private:
201
		const Particle_emitter_config* _cfg;
202
203

		// TODO: userdata?
204
205
		glm::vec3 _position{0, 0, 0};
		glm::quat _rotation{1, 0, 0, 0};
206

207
		float _time_accumulator = 0.f;
208
209
	};

210
	class Particle_system : private std::enable_shared_from_this<Particle_system> {
211
	  public:
212
		using Emitter_list = util::small_vector<Particle_emitter, 1>;
213

214
215
216
217
		Particle_system() = default;
		Particle_system(asset::Ptr<Particle_system_config> cfg,
		                glm::vec3                          position = {0, 0, 0},
		                glm::quat                          rotation = {1, 0, 0, 0});
218

219
		auto cfg_aid() const { return _cfg ? util::just(_cfg.aid()) : util::nothing; }
220
221
222
223
224
225
226

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

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

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

230
		void rotation(glm::quat r) noexcept { _rotation = r; }
231
232
233
		auto rotation() const noexcept { return _rotation; }

	  private:
234
235
236
		asset::Ptr<Particle_system_config>    _cfg;
		Emitter_list                          _emitters;
		std::vector<Particle_effector_config> _effectors;
237

238
239
		glm::vec3 _position{0, 0, 0};
		glm::quat _rotation{1, 0, 0, 0};
240
	};
241
242
243
244
245
246
247
248
249
250
251


	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) {}

252
253
254
255
256
257
258
259
260
261
262
263
264
		Particle_system particle_system;
	};

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

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

		Particle_effector_config effector;
265
266
	};

267
} // namespace mirrage::renderer
268
269
270
271
272
273

namespace mirrage::asset {

	template <>
	struct Loader<renderer::Particle_script> {
	  public:
274
275
276
277
		Loader(graphic::Device&        device,
		       vk::DescriptorSetLayout global_uniforms,
		       vk::DescriptorSetLayout storage_buffer,
		       vk::DescriptorSetLayout uniform_buffer);
278
279
280
281

		auto              load(istream in) -> renderer::Particle_script;
		[[noreturn]] void save(ostream, const renderer::Particle_script&)
		{
282
			MIRRAGE_FAIL("Save of Particle_script is not supported!");
283
284
285
286
287
288
289
290
291
292
293
294
295
		}

	  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&)
		{
296
297
298
299
300
301
302
303
304
305
306
			MIRRAGE_FAIL("Save of Particle_system_config is not supported!");
		}
	};

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

} // namespace mirrage::asset