three_d/renderer/geometry/
particles.rs1use super::BaseMesh;
2use crate::core::*;
3use crate::renderer::*;
4
5#[derive(Clone, Debug, Default)]
11pub struct Particles {
12 pub start_positions: Vec<Vec3>,
14 pub start_velocities: Vec<Vec3>,
16 pub texture_transforms: Option<Vec<Mat3>>,
18 pub colors: Option<Vec<Srgba>>,
20}
21
22impl Particles {
23 pub fn validate(&self) -> Result<(), RendererError> {
27 let instance_count = self.count();
28 let buffer_check = |length: Option<usize>, name: &str| -> Result<(), RendererError> {
29 if let Some(length) = length {
30 if length < instance_count as usize {
31 Err(RendererError::InvalidBufferLength(
32 name.to_string(),
33 instance_count as usize,
34 length,
35 ))?;
36 }
37 }
38 Ok(())
39 };
40
41 buffer_check(
42 self.texture_transforms.as_ref().map(|b| b.len()),
43 "texture transforms",
44 )?;
45 buffer_check(self.colors.as_ref().map(|b| b.len()), "colors")?;
46 buffer_check(Some(self.start_positions.len()), "start_positions")?;
47 buffer_check(Some(self.start_velocities.len()), "start_velocities")?;
48
49 Ok(())
50 }
51
52 pub fn count(&self) -> u32 {
54 self.start_positions.len() as u32
55 }
56}
57
58pub struct ParticleSystem {
71 context: Context,
72 base_mesh: BaseMesh,
73 start_position: InstanceBuffer<Vec3>,
74 start_velocity: InstanceBuffer<Vec3>,
75 tex_transform: Option<(InstanceBuffer<Vec3>, InstanceBuffer<Vec3>)>,
76 instance_color: Option<InstanceBuffer<Vec4>>,
77 pub acceleration: Vec3,
79 instance_count: u32,
80 transformation: Mat4,
81 time: f32,
82}
83
84impl ParticleSystem {
85 pub fn new(
90 context: &Context,
91 particles: &Particles,
92 acceleration: Vec3,
93 cpu_mesh: &CpuMesh,
94 ) -> Self {
95 #[cfg(debug_assertions)]
96 cpu_mesh.validate().expect("invalid cpu mesh");
97
98 let mut particles_system = Self {
99 context: context.clone(),
100 base_mesh: BaseMesh::new(context, cpu_mesh),
101 acceleration,
102 instance_count: 0,
103 transformation: Mat4::identity(),
104 time: 0.0,
105 start_position: InstanceBuffer::<Vec3>::new(context),
106 start_velocity: InstanceBuffer::<Vec3>::new(context),
107 tex_transform: None,
108 instance_color: None,
109 };
110 particles_system.set_particles(particles);
111 particles_system
112 }
113
114 pub fn transformation(&self) -> Mat4 {
118 self.transformation
119 }
120
121 pub fn set_transformation(&mut self, transformation: Mat4) {
125 self.transformation = transformation;
126 }
127
128 pub fn set_particles(&mut self, particles: &Particles) {
132 #[cfg(debug_assertions)]
133 particles.validate().expect("invalid particles");
134 self.instance_count = particles.count();
135
136 self.start_position =
137 InstanceBuffer::new_with_data(&self.context, &particles.start_positions);
138 self.start_velocity =
139 InstanceBuffer::new_with_data(&self.context, &particles.start_velocities);
140 self.tex_transform = particles
141 .texture_transforms
142 .as_ref()
143 .map(|texture_transforms| {
144 let mut instance_tex_transform1 = Vec::new();
145 let mut instance_tex_transform2 = Vec::new();
146 for texture_transform in texture_transforms.iter() {
147 instance_tex_transform1.push(vec3(
148 texture_transform.x.x,
149 texture_transform.y.x,
150 texture_transform.z.x,
151 ));
152 instance_tex_transform2.push(vec3(
153 texture_transform.x.y,
154 texture_transform.y.y,
155 texture_transform.z.y,
156 ));
157 }
158 (
159 InstanceBuffer::new_with_data(&self.context, &instance_tex_transform1),
160 InstanceBuffer::new_with_data(&self.context, &instance_tex_transform2),
161 )
162 });
163 self.instance_color = particles.colors.as_ref().map(|instance_colors| {
164 InstanceBuffer::new_with_data(
165 &self.context,
166 &instance_colors
167 .iter()
168 .map(|c| c.to_linear_srgb())
169 .collect::<Vec<_>>(),
170 )
171 });
172 }
173}
174
175impl<'a> IntoIterator for &'a ParticleSystem {
176 type Item = &'a dyn Geometry;
177 type IntoIter = std::iter::Once<&'a dyn Geometry>;
178
179 fn into_iter(self) -> Self::IntoIter {
180 std::iter::once(self)
181 }
182}
183
184impl Geometry for ParticleSystem {
185 fn id(&self) -> GeometryId {
186 GeometryId::ParticleSystem(
187 self.base_mesh.normals.is_some(),
188 self.base_mesh.tangents.is_some(),
189 self.base_mesh.uvs.is_some(),
190 self.base_mesh.colors.is_some(),
191 self.instance_color.is_some(),
192 self.tex_transform.is_some(),
193 )
194 }
195
196 fn vertex_shader_source(&self) -> String {
197 format!(
198 "#define PARTICLES\n{}{}{}",
199 if self.instance_color.is_some() {
200 "#define USE_INSTANCE_COLORS\n"
201 } else {
202 ""
203 },
204 if self.tex_transform.is_some() {
205 "#define USE_INSTANCE_TEXTURE_TRANSFORMATION\n"
206 } else {
207 ""
208 },
209 self.base_mesh.vertex_shader_source()
210 )
211 }
212
213 fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
214 if let Some(inverse) = self.transformation.invert() {
215 program.use_uniform_if_required("normalMatrix", inverse.transpose());
216 } else {
217 return;
219 }
220 program.use_uniform("viewProjection", viewer.projection() * viewer.view());
221 program.use_uniform("modelMatrix", self.transformation);
222 program.use_uniform("acceleration", self.acceleration);
223 program.use_uniform("time", self.time);
224
225 program.use_instance_attribute("start_position", &self.start_position);
226 program.use_instance_attribute("start_velocity", &self.start_velocity);
227
228 if program.requires_attribute("tex_transform_row1") {
229 if let Some((row1, row2)) = &self.tex_transform {
230 program.use_instance_attribute("tex_transform_row1", row1);
231 program.use_instance_attribute("tex_transform_row2", row2);
232 }
233 }
234
235 if program.requires_attribute("instance_color") {
236 if let Some(color) = &self.instance_color {
237 program.use_instance_attribute("instance_color", color);
238 }
239 }
240
241 self.base_mesh
242 .draw_instanced(program, render_states, viewer, self.instance_count);
243 }
244
245 fn aabb(&self) -> AxisAlignedBoundingBox {
246 AxisAlignedBoundingBox::INFINITE
247 }
248
249 fn render_with_material(
250 &self,
251 material: &dyn Material,
252 viewer: &dyn Viewer,
253 lights: &[&dyn Light],
254 ) {
255 render_with_material(&self.context, viewer, &self, material, lights)
256 }
257
258 fn render_with_effect(
259 &self,
260 material: &dyn Effect,
261 viewer: &dyn Viewer,
262 lights: &[&dyn Light],
263 color_texture: Option<ColorTexture>,
264 depth_texture: Option<DepthTexture>,
265 ) {
266 render_with_effect(
267 &self.context,
268 viewer,
269 self,
270 material,
271 lights,
272 color_texture,
273 depth_texture,
274 )
275 }
276
277 fn animate(&mut self, time: f32) {
278 self.time = time;
279 }
280}