tiny_game_framework/graphics/
particle.rs1use std::{ptr, ffi::CString};
2
3use gl::{*, types::*};
4use glam::{vec2, vec3, Mat4, Quat, Vec3, Vec4};
5use once_cell::sync::Lazy;
6
7use crate::{bind_buffer, cstr, gen_attrib_pointers, rand_betw, rand_vec3, EventLoop, InstanceData, Renderer, Shader, Vertex, PARTICLE_SHADER_FS, PARTICLE_SHADER_VS};
8
9pub static PARTICLE_SHADER: Lazy<Shader> = Lazy::new(|| {
10 Shader::new_pipeline(PARTICLE_SHADER_VS, PARTICLE_SHADER_FS)
11});
12
13#[derive(PartialEq, Debug, Copy, Clone)]
14pub struct ParticleInstanceData {
15 pub position: Vec3,
16 pub lifespan: f32,
17 pub velocity: Vec3,
18}
19
20
21#[derive(Default)]
22pub struct Particle {
23 pub position: Vec3,
25 pub velocity: Vec3,
26
27 pub has_gravity: bool,
28 pub size: f32,
29
30 pub count: i32,
31
32 pub spread: f32,
33
34 pub mesh: ParticleMesh,
38}
39
40pub struct ParticleMesh {
41 vertices: Vec<Vertex>,
42 indices: Vec<u32>,
43
44 pub VAO: u32,
45 EBO: u32,
46 VBO: u32,
47
48 pub instance_data: Vec<ParticleInstanceData>,
49
50 pub instance_buffer: u32,
51
52 shader: Shader,
53}
54
55impl Default for ParticleMesh {
56 fn default() -> Self {
57 Self::new(5, Vec3::ZERO)
58 }
59}
60
61impl ParticleMesh {
62 pub fn new(count: i32, initial_position: Vec3) -> Self {
63 let vertices = vec![
64 Vertex::new(vec3(0.0, 0.0, 0.0), Vec4::ONE, vec2(0.0, 0.0), vec3(0., 0., 1.)), Vertex::new(vec3(0.0, 1.0, 0.0), Vec4::ONE, vec2(0.0, 1.0), vec3(0., 0., 1.)), Vertex::new(vec3(1.0, 0.0, 0.0), Vec4::ONE, vec2(1.0, 0.0), vec3(0., 0., 1.)), Vertex::new(vec3(1.0, 1.0, 0.0), Vec4::ONE, vec2(1.0, 1.0), vec3(0., 0., 1.)), ];
69
70 let indices = vec![0, 2, 1, 2, 3, 1];
71
72 let mut instance_data = vec![];
73 for _ in 0..count {
74 instance_data.push(ParticleInstanceData {
75 position: initial_position,
76 lifespan: 1.0,
77 velocity: Vec3::ZERO,
78 });
79 }
80
81 let mut mesh = Self {
82 vertices,
83 indices,
84 VAO: 0,
85 VBO: 0,
86 EBO: 0,
87 instance_buffer: 0,
88 instance_data,
89 shader: *PARTICLE_SHADER,
90 };
91
92 mesh
93 }
94
95 pub fn update_instance_data(&mut self, data: Vec<ParticleInstanceData>) {
96 self.instance_data = data;
97 unsafe {
98 BindBuffer(ARRAY_BUFFER, self.instance_buffer);
99 BufferSubData(
100 ARRAY_BUFFER,
101 0,
102 (self.instance_data.len() * std::mem::size_of::<ParticleInstanceData>()) as isize,
103 self.instance_data.as_ptr() as *const _
104 );
105 }
106 }
107
108 pub unsafe fn setup_mesh(&mut self) {
109 GenVertexArrays(1, &mut self.VAO);
110 GenBuffers(1, &mut self.VBO);
111 GenBuffers(1, &mut self.EBO);
112
113 BindVertexArray(self.VAO);
114
115 bind_buffer!(ARRAY_BUFFER, self.VBO, self.vertices);
116 bind_buffer!(ELEMENT_ARRAY_BUFFER, self.EBO, self.indices);
117 gen_attrib_pointers!(Vertex, 0 => position: 3, 1 => color: 4);
118
119 GenBuffers(1, &mut self.instance_buffer);
120 bind_buffer!(ARRAY_BUFFER, self.instance_buffer, self.instance_data);
121
122 gen_attrib_pointers!(ParticleInstanceData, 2 => position: 3);
123 VertexAttribDivisor(2, 1);
124
125 BindVertexArray(0);
126 }
127
128 pub fn destroy(&mut self) {
129 unsafe {
130 DeleteVertexArrays(1, &self.VAO);
131 DeleteBuffers(1, &self.EBO);
132 DeleteBuffers(1, &self.VBO);
133 }
134 }
135
136 pub unsafe fn draw(&self, model_matrix: Mat4) {
137 BindVertexArray(self.VAO);
138 self.shader.use_shader();
139
140 self.shader.uniform_mat4fv(cstr!("model"), &model_matrix.to_cols_array());
141
142 DrawElementsInstanced(TRIANGLES, self.indices.len() as i32, UNSIGNED_INT, ptr::null(), self.instance_data.len() as i32);
143 BindVertexArray(0);
144 UseProgram(0);
145 }
146}
147
148
149impl Particle {
150 pub fn new(
151 position: Vec3,
152 velocity: Vec3,
153 count: i32,
154 size: f32,
155 has_gravity: bool,
156 spread: f32,
157 ) -> Self {
158 let mut mesh = ParticleMesh::new(count, position);
159
160 let mut subscribed_instance_data = vec![];
161 for _ in 0..count {
162 subscribed_instance_data.push(ParticleInstanceData {
163 position: Vec3::ZERO,
164 velocity: velocity * rand_vec3() * spread,
165 lifespan: rand_betw(0.0, 5.0), });
167 }
168
169 mesh.update_instance_data(subscribed_instance_data);
170 unsafe { mesh.setup_mesh(); };
171
172 Self {
173 position,
174 velocity,
175 count,
176 size,
177 has_gravity,
178 spread,
179 mesh,
180 }
181 }
182
183 pub fn update(&mut self, el: &EventLoop) {
184 let instance_data = &self.mesh.instance_data;
185 let mut subscribed_instance_data = vec![];
186
187 for i in 0..self.count {
188 let mut particle = instance_data[i as usize];
189 particle.position += particle.velocity * el.dt * 1.0 / self.size;
190 particle.lifespan -= el.dt;
191
192 if particle.lifespan <= 0.0 {
193 particle.position = Vec3::ZERO;
194 particle.velocity = self.velocity * rand_vec3() * self.spread;
195 particle.lifespan = rand_betw(0.5, 5.0);
196 }
197 if self.has_gravity {
198 particle.velocity.y -= 1.0 * el.dt;
199 }
200 subscribed_instance_data.push(particle);
201 }
202
203 self.mesh.update_instance_data(subscribed_instance_data);
204 }
206
207
208 pub fn draw(&self) {
209 let model_matrix =
210 Mat4::from_translation(self.position) *
211 Mat4::from_quat(Quat::default()) *
212 Mat4::from_scale(vec3(self.size, self.size, self.size));
213
214
215 unsafe {
216 self.mesh.draw(model_matrix);
217 }
218 }
219}
220
221impl Renderer {
222 pub fn add_particle(&mut self, name: &str, particle: Particle) {
223 self.particles.insert(name.to_owned(), particle);}
225
226 pub fn remove_particle(&mut self, name: &str) {
227 let particle = self.particles.get_mut(name).unwrap();
228 particle.mesh.destroy(); self.particles.remove(name).unwrap();
230 }
231}