Brenta Engine 1.2
Loading...
Searching...
No Matches
particles.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/renderer/opengl/gl.hpp>
7#include <brenta/renderer/camera.hpp>
8#include <brenta/renderer/particles.hpp>
9#include <brenta/renderer/opengl/shader.hpp>
10#include <brenta/renderer/opengl/texture.hpp>
11#include <brenta/asset.hpp>
12#include <brenta/logger.hpp>
13
14#include <iostream>
15#include <time.h>
16
17#include "./shaders/c/particle_render_fs.c"
18#include "./shaders/c/particle_render_vs.c"
19#include "./shaders/c/particle_render_gs.c"
20#include "./shaders/c/particle_update_vs.c"
21
22namespace brenta
23{
24
26 glm::vec3 gravity;
27 float deltaTime;
28 glm::vec3 emitterVel;
29 float emitterTTL;
30 glm::vec3 emitterPos;
31 float spawnProbability;
32 glm::vec3 emitterSpread;
33 float __padding;
34};
35
36}
37
38using namespace brenta;
39
40ParticleEmitter::ParticleEmitter(Config conf)
41{
42 this->starting_position = conf.starting_position;
43 this->starting_velocity = conf.starting_velocity;
44 this->starting_spread = conf.starting_spread;
45 this->starting_time_to_live = conf.starting_time_to_live;
46 this->num_particles = conf.num_particles;
47 this->spawn_rate = conf.spawn_rate;
48 this->scale = conf.scale;
49 this->atlas_width = conf.atlas_width;
50 this->atlas_height = conf.atlas_height;
51 this->atlas_index = conf.atlas_index;
52 this->current_fbo_index = 0;
53 this->cam = conf.cam;
54
55 // Load Texture Atlas
56 this->atlas =
58 .target(Texture::Target::Texture2D)
59 .path(conf.atlas_path)
60 .properties(Texture::Properties()
61 .set_wrapping(Texture::Wrapping::Repeat)
62 .set_filtering_min(Texture::Filtering::Nearest)
63 .set_filtering_mag(Texture::Filtering::Nearest)
64 .set_has_mipmap(Gl::True)
65 .set_mipmap_min(Texture::Filtering::NearestMipmapNearest)
66 .set_mipmap_mag(Texture::Filtering::Nearest))
67 .build();
68
69 // Create shaders
70 const GLchar *varyings[] = {"outPosition", "outVelocity", "outTTL"};
71 this->shader_update =
72 AssetManager::new_asset<Shader>("particle_update_shader",
74 .feedback(varyings,
75 sizeof(varyings) / sizeof(varyings[0]))
76 .object({
77 Shader::Type::Vertex,
78 particle_update_vs
79 }));
80 if (!this->shader_update) return;
81
82 this->shader_render = AssetManager::new_asset<Shader>("particole_render_shader",
84 .objects({
85 { Shader::Type::Vertex, particle_render_vs },
86 { Shader::Type::Geometry, particle_render_gs },
87 { Shader::Type::Fragment, particle_render_fs }}));
88 if (!this->shader_render) return;
89
90 // This is needed to render points
91 glEnable(GL_PROGRAM_POINT_SIZE);
92
93 this->vao.init();
94 this->vao.bind();
95 Gl::check_error();
96
97 // Create fbos
98 this->fbo[0].init(Buffer::Target::TransformFeedback);
99 this->fbo[1].init(Buffer::Target::TransformFeedback);
100
101 this->fbo[0].bind();
102 this->fbo[0].copy_data(NULL, this->num_particles * 2 * sizeof(glm::vec3)
103 + this->num_particles * sizeof(float),
104 Buffer::DataUsage::DynamicCopy);
105 this->fbo[1].bind();
106 this->fbo[1].copy_data(NULL,
107 this->num_particles * 2 * sizeof(glm::vec3)
108 + this->num_particles * sizeof(float),
109 Buffer::DataUsage::DynamicCopy);
110 Gl::check_error();
111
112 // Unbind buffers
113 this->fbo[0].unbind();
114 this->vao.unbind();
115
116 // Setup UBO
117 this->ubo.init(*this->shader_update, "settings", 3, sizeof(ParticleSettings));
118 this->ubo.bind();
119 this->ubo.copy_data(NULL,
120 sizeof(ParticleSettings),
121 Buffer::DataUsage::DynamicDraw);
122 this->ubo.unbind();
123
124 return;
125}
126
127// Update particles using Transform Feedback
128void ParticleEmitter::update(float delta_time)
129{
130 if (!this->shader_update) return;
131 this->shader_update->use();
132
133 ParticleSettings settings = {
134 .gravity = glm::vec3(0.0f, -9.81f, 0.0f),
135 .deltaTime = delta_time,
136 .emitterVel = this->starting_velocity,
137 .emitterTTL = this->starting_time_to_live,
138 .emitterPos = this->starting_position,
139 .spawnProbability = this->spawn_rate,
140 .emitterSpread = this->starting_spread,
141 .__padding = 0.0f,
142 };
143
144 // ubo
145
146 this->ubo.bind();
147 this->ubo.copy_data(&settings, sizeof(ParticleSettings),
148 Buffer::DataUsage::DynamicDraw);
149 this->ubo.unbind();
150 Gl::check_error();
151
152 // Vao
153
154 this->vao.bind();
155 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fbo[current_fbo_index].get_id());
156 Gl::check_error();
157 glBindBuffer(GL_ARRAY_BUFFER, fbo[!current_fbo_index].get_id());
158 this->vao.link_buffer(this->fbo[!current_fbo_index],
159 0, 3, Gl::Float, Gl::False,
160 2 * sizeof(glm::vec3) + sizeof(float), (void *) 0);
161 this->vao.link_buffer(this->fbo[!current_fbo_index],
162 1, 3, Gl::Float, Gl::False,
163 2 * sizeof(glm::vec3) + sizeof(float),
164 (void *) sizeof(glm::vec3));
165 this->vao.link_buffer(this->fbo[!current_fbo_index],
166 2, 1, Gl::Float, Gl::False,
167 2 * sizeof(glm::vec3) + sizeof(float),
168 (void *) (2 * sizeof(glm::vec3)));
169
170 // Start transform feedback
171 glEnable(GL_RASTERIZER_DISCARD); // Disable rasterization
172 glBeginTransformFeedback(GL_POINTS); // Enter transform feedback mode
173 Gl::check_error();
174
175 glDrawArrays(GL_POINTS, 0, num_particles);
176 Gl::check_error();
177
178 glEndTransformFeedback(); // Exit transform feedback mode
179 glDisable(GL_RASTERIZER_DISCARD); // Enable rasterization
180
181 // Unbind buffers
182 glBindBuffer(GL_ARRAY_BUFFER, 0);
183 this->vao.unbind();
184 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
185 return;
186}
187
188// Render particles
189void ParticleEmitter::render(int width, int height)
190{
191 tenno::shared_ptr<Camera> camera = this->cam.lock();
192 if (!camera)
193 {
194 ERROR("ParticleEmitter::render_particles: Camera not set or null for emitter");
195 return;
196 }
197
198 if (!this->shader_render) return;
199
200 this->vao.bind();
201
202 glBindBuffer(GL_ARRAY_BUFFER, fbo[current_fbo_index].get_id());
203 this->vao.link_buffer(this->fbo[current_fbo_index],
204 0, 3, Gl::Float, Gl::False,
205 2 * sizeof(glm::vec3) + sizeof(float), (void *) 0);
206 this->vao.link_buffer(this->fbo[current_fbo_index],
207 1, 1, Gl::Float, Gl::False,
208 2 * sizeof(glm::vec3) + sizeof(float),
209 (void *) (2 * sizeof(glm::vec3)));
210 Gl::check_error();
211
212 // Set uniforms
213 auto projection =
214 camera->get_projection_matrix(width, height);
215
216 shader_render->use();
217 shader_render->set_mat4("view", camera->get_view_matrix());
218 shader_render->set_mat4("projection", projection);
219 shader_render->set_mat4("model", glm::mat4(1.0f));
220 shader_render->set_int("atlas_width", this->atlas_width);
221 shader_render->set_int("atlas_height", this->atlas_height);
222 shader_render->set_int("atlas_index", this->atlas_index);
223 shader_render->set_float("scale", this->scale);
224 shader_render->set_float("aspect_ratio",
225 (float) width / (float) height);
226
227 // Set Textures
228 Texture::active_texture(0);
229 this->atlas.bind();
230
231 glDrawArrays(GL_POINTS, 0, num_particles);
232 Gl::check_error();
233
234 glBindBuffer(GL_ARRAY_BUFFER, 0);
235 this->vao.unbind();
236
237 // Swap buffers
238 current_fbo_index = !current_fbo_index; // Swap buffers
239 return;
240}
241
242//
243// Builder functions
244//
245
247ParticleEmitter::Builder::starting_position(glm::vec3 starting_position)
248{
249 this->conf.starting_position = starting_position;
250 return *this;
251}
252
254ParticleEmitter::Builder::starting_velocity(glm::vec3 starting_velocity)
255{
256 this->conf.starting_velocity = starting_velocity;
257 return *this;
258}
259
261ParticleEmitter::Builder::starting_spread(glm::vec3 starting_spread)
262{
263 this->conf.starting_spread = starting_spread;
264 return *this;
265}
266
267ParticleEmitter::Builder &ParticleEmitter::Builder::starting_time_to_live(
268 float starting_time_to_live)
269{
270 this->conf.starting_time_to_live = starting_time_to_live;
271 return *this;
272}
273
275ParticleEmitter::Builder::num_particles(int num_particles)
276{
277 this->conf.num_particles = num_particles;
278 return *this;
279}
280
282ParticleEmitter::Builder::spawn_rate(float spawn_rate)
283{
284 this->conf.spawn_rate = spawn_rate;
285 return *this;
286}
287
288ParticleEmitter::Builder &ParticleEmitter::Builder::scale(float scale)
289{
290 this->conf.scale = scale;
291 return *this;
292}
293
295ParticleEmitter::Builder::atlas_path(const std::filesystem::path &atlas_path)
296{
297 this->conf.atlas_path = atlas_path;
298 return *this;
299}
300
302ParticleEmitter::Builder::atlas_width(int atlas_width)
303{
304 this->conf.atlas_width = atlas_width;
305 return *this;
306}
307
309ParticleEmitter::Builder::atlas_height(int atlas_height)
310{
311 this->conf.atlas_height = atlas_height;
312 return *this;
313}
314
316ParticleEmitter::Builder::atlas_index(int atlas_index)
317{
318 this->conf.atlas_index = atlas_index;
319 return *this;
320}
321
323ParticleEmitter::Builder::with_camera(tenno::weak_ptr<Camera> cam)
324{
325 this->conf.cam = cam;
326 return *this;
327}
328
329ParticleEmitter ParticleEmitter::Builder::build()
330{
331 return ParticleEmitter(this->conf);
332}