three_d/renderer/geometry/
instanced_mesh.rs

1use crate::core::*;
2use crate::renderer::*;
3use std::sync::RwLock;
4
5use super::BaseMesh;
6
7///
8/// Similar to [Mesh], except it is possible to render many instances of the same mesh efficiently.
9///
10pub struct InstancedMesh {
11    context: Context,
12    base_mesh: BaseMesh,
13    transform: RwLock<(
14        InstanceBuffer<Vec4>,
15        InstanceBuffer<Vec4>,
16        InstanceBuffer<Vec4>,
17    )>,
18    indices: RwLock<Vec<usize>>,
19    tex_transform: RwLock<Option<(InstanceBuffer<Vec3>, InstanceBuffer<Vec3>)>>,
20    instance_color: RwLock<Option<InstanceBuffer<Vec4>>>,
21    last_camera_position: RwLock<Option<Vec3>>,
22    aabb: AxisAlignedBoundingBox, // The AABB for the base mesh without transformations applied
23    transformation: Mat4,
24    current_transformation: Mat4,
25    animation: Option<Box<dyn Fn(f32) -> Mat4 + Send + Sync>>,
26    instances: Instances,
27}
28
29impl InstancedMesh {
30    ///
31    /// Creates a new instanced 3D mesh from the given [CpuMesh].
32    /// All data in the [CpuMesh] is transfered to the GPU, so make sure to remove all unnecessary data from the [CpuMesh] before calling this method.
33    /// The model is rendered in as many instances as there are attributes in [Instances] given as input.
34    ///
35    pub fn new(context: &Context, instances: &Instances, cpu_mesh: &CpuMesh) -> Self {
36        #[cfg(debug_assertions)]
37        instances.validate().expect("invalid instances");
38
39        let aabb = cpu_mesh.compute_aabb();
40        let instanced_mesh = Self {
41            context: context.clone(),
42            base_mesh: BaseMesh::new(context, cpu_mesh),
43            transform: RwLock::new((
44                InstanceBuffer::<Vec4>::new(context),
45                InstanceBuffer::<Vec4>::new(context),
46                InstanceBuffer::<Vec4>::new(context),
47            )),
48            tex_transform: RwLock::new(None),
49            instance_color: RwLock::new(None),
50            last_camera_position: RwLock::new(None),
51            indices: RwLock::new((0..instances.transformations.len()).collect::<Vec<usize>>()),
52            aabb,
53            transformation: Mat4::identity(),
54            current_transformation: Mat4::identity(),
55            animation: None,
56            instances: instances.clone(),
57        };
58        instanced_mesh.update_instance_buffers();
59        instanced_mesh
60    }
61
62    ///
63    /// Returns the local to world transformation applied to all instances.
64    ///
65    pub fn transformation(&self) -> Mat4 {
66        self.transformation
67    }
68
69    ///
70    /// Set the local to world transformation applied to all instances.
71    /// This is applied before the transform for each instance.
72    ///
73    pub fn set_transformation(&mut self, transformation: Mat4) {
74        self.transformation = transformation;
75        self.current_transformation = transformation;
76        *self.last_camera_position.write().unwrap() = None;
77    }
78
79    ///
80    /// Specifies a function which takes a time parameter as input and returns a transformation that should be applied to this mesh at the given time.
81    /// To actually animate this instanced mesh, call [Geometry::animate] at each frame which in turn evaluates the animation function defined by this method.
82    /// This transformation is applied first, then the local to world transformation defined by [Self::set_transformation].
83    ///
84    pub fn set_animation(&mut self, animation: impl Fn(f32) -> Mat4 + Send + Sync + 'static) {
85        self.animation = Some(Box::new(animation));
86    }
87
88    /// Returns the number of instances that is rendered.
89    pub fn instance_count(&self) -> u32 {
90        self.instances.count()
91    }
92
93    ///
94    /// Update the instances.
95    ///
96    pub fn set_instances(&mut self, instances: &Instances) {
97        #[cfg(debug_assertions)]
98        instances.validate().expect("invalid instances");
99        self.instances = instances.clone();
100        *self.indices.write().unwrap() =
101            (0..instances.transformations.len()).collect::<Vec<usize>>();
102        *self.last_camera_position.write().unwrap() = None;
103
104        self.update_instance_buffers();
105    }
106
107    ///
108    /// This function updates the instance buffers, so the instances are rendered in the order given by the indices
109    ///
110    fn update_instance_buffers(&self) {
111        let indices = self.indices.read().unwrap();
112        let mut row1 = Vec::new();
113        let mut row2 = Vec::new();
114        let mut row3 = Vec::new();
115        for transformation in indices.iter().map(|i| self.instances.transformations[*i]) {
116            row1.push(transformation.row(0));
117            row2.push(transformation.row(1));
118            row3.push(transformation.row(2));
119        }
120
121        *self.transform.write().unwrap() = (
122            InstanceBuffer::new_with_data(&self.context, &row1),
123            InstanceBuffer::new_with_data(&self.context, &row2),
124            InstanceBuffer::new_with_data(&self.context, &row3),
125        );
126
127        *self.tex_transform.write().unwrap() =
128            self.instances
129                .texture_transformations
130                .as_ref()
131                .map(|texture_transforms| {
132                    let mut instance_tex_transform1 = Vec::new();
133                    let mut instance_tex_transform2 = Vec::new();
134                    for texture_transform in indices.iter().map(|i| texture_transforms[*i]) {
135                        instance_tex_transform1.push(vec3(
136                            texture_transform.x.x,
137                            texture_transform.y.x,
138                            texture_transform.z.x,
139                        ));
140                        instance_tex_transform2.push(vec3(
141                            texture_transform.x.y,
142                            texture_transform.y.y,
143                            texture_transform.z.y,
144                        ));
145                    }
146                    (
147                        InstanceBuffer::new_with_data(&self.context, &instance_tex_transform1),
148                        InstanceBuffer::new_with_data(&self.context, &instance_tex_transform2),
149                    )
150                });
151        *self.instance_color.write().unwrap() =
152            self.instances.colors.as_ref().map(|instance_colors| {
153                let ordered_instance_colors = indices
154                    .iter()
155                    .map(|i| instance_colors[*i].to_linear_srgb())
156                    .collect::<Vec<_>>();
157                InstanceBuffer::new_with_data(&self.context, &ordered_instance_colors)
158            });
159    }
160}
161
162impl<'a> IntoIterator for &'a InstancedMesh {
163    type Item = &'a dyn Geometry;
164    type IntoIter = std::iter::Once<&'a dyn Geometry>;
165
166    fn into_iter(self) -> Self::IntoIter {
167        std::iter::once(self)
168    }
169}
170
171impl Geometry for InstancedMesh {
172    fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
173        // Check if we need a reorder the instance draw order. This only applies to transparent materials.
174        if render_states.blend != Blend::Disabled
175            && self
176                .last_camera_position
177                .read()
178                .unwrap()
179                .map(|p| p.distance2(viewer.position()) > 0.001)
180                .unwrap_or(true)
181        {
182            *self.last_camera_position.write().unwrap() = Some(viewer.position());
183            let distances = self
184                .instances
185                .transformations
186                .iter()
187                .map(|m| {
188                    (m * self.current_transformation)
189                        .w
190                        .truncate()
191                        .distance2(viewer.position())
192                })
193                .collect::<Vec<_>>();
194            self.indices.write().unwrap().sort_by(|a, b| {
195                distances[*b]
196                    .partial_cmp(&distances[*a])
197                    .unwrap_or(std::cmp::Ordering::Equal)
198            });
199            self.update_instance_buffers();
200        }
201
202        program.use_uniform("viewProjection", viewer.projection() * viewer.view());
203        program.use_uniform("modelMatrix", self.current_transformation);
204
205        let (row1, row2, row3) = &*self.transform.read().unwrap();
206        program.use_instance_attribute("row1", row1);
207        program.use_instance_attribute("row2", row2);
208        program.use_instance_attribute("row3", row3);
209
210        if program.requires_attribute("tex_transform_row1") {
211            if let Some((row1, row2)) = &*self.tex_transform.read().unwrap() {
212                program.use_instance_attribute("tex_transform_row1", row1);
213                program.use_instance_attribute("tex_transform_row2", row2);
214            }
215        }
216
217        if program.requires_attribute("instance_color") {
218            if let Some(color) = &*self.instance_color.read().unwrap() {
219                program.use_instance_attribute("instance_color", color);
220            }
221        }
222
223        self.base_mesh
224            .draw_instanced(program, render_states, viewer, self.instance_count());
225    }
226
227    fn vertex_shader_source(&self) -> String {
228        format!(
229            "#define USE_INSTANCE_TRANSFORMS\n{}{}{}",
230            if self.instance_color.read().unwrap().is_some() {
231                "#define USE_INSTANCE_COLORS\n"
232            } else {
233                ""
234            },
235            if self.tex_transform.read().unwrap().is_some() {
236                "#define USE_INSTANCE_TEXTURE_TRANSFORMATION\n"
237            } else {
238                ""
239            },
240            self.base_mesh.vertex_shader_source()
241        )
242    }
243
244    fn id(&self) -> GeometryId {
245        GeometryId::InstancedMesh(
246            self.base_mesh.normals.is_some(),
247            self.base_mesh.tangents.is_some(),
248            self.base_mesh.uvs.is_some(),
249            self.base_mesh.colors.is_some(),
250            self.instance_color.read().unwrap().is_some(),
251            self.tex_transform.read().unwrap().is_some(),
252        )
253    }
254
255    fn aabb(&self) -> AxisAlignedBoundingBox {
256        let mut aabb = AxisAlignedBoundingBox::EMPTY;
257        let local_aabb = self.aabb.transformed(self.current_transformation);
258        for transformation in &self.instances.transformations {
259            aabb.expand_with_aabb(local_aabb.transformed(*transformation));
260        }
261        aabb
262    }
263
264    fn animate(&mut self, time: f32) {
265        if let Some(animation) = &self.animation {
266            self.current_transformation = self.transformation * animation(time);
267            *self.last_camera_position.write().unwrap() = None;
268        }
269    }
270
271    fn render_with_material(
272        &self,
273        material: &dyn Material,
274        viewer: &dyn Viewer,
275        lights: &[&dyn Light],
276    ) {
277        render_with_material(&self.context, viewer, self, material, lights)
278    }
279
280    fn render_with_effect(
281        &self,
282        material: &dyn Effect,
283        viewer: &dyn Viewer,
284        lights: &[&dyn Light],
285        color_texture: Option<ColorTexture>,
286        depth_texture: Option<DepthTexture>,
287    ) {
288        render_with_effect(
289            &self.context,
290            viewer,
291            self,
292            material,
293            lights,
294            color_texture,
295            depth_texture,
296        )
297    }
298}
299
300///
301/// Defines the attributes for the instances of the model defined in [InstancedMesh] or [InstancedModel].
302///
303/// Each list of attributes must contain the same number of elements as the number of instances.
304/// The attributes are applied to each instance before they are rendered.
305/// The [Instances::transformations] are applied after the transformation applied to all instances (see [InstancedMesh::set_transformation]).
306///
307#[derive(Clone, Debug, Default)]
308pub struct Instances {
309    /// The transformations applied to each instance.
310    pub transformations: Vec<Mat4>,
311    /// The texture transform applied to the uv coordinates of each instance.
312    pub texture_transformations: Option<Vec<Mat3>>,
313    /// Colors multiplied onto the base color of each instance.
314    pub colors: Option<Vec<Srgba>>,
315}
316
317impl Instances {
318    ///
319    /// Returns an error if the instances is not valid.
320    ///
321    pub fn validate(&self) -> Result<(), RendererError> {
322        let instance_count = self.count();
323        let buffer_check = |length: Option<usize>, name: &str| -> Result<(), RendererError> {
324            if let Some(length) = length {
325                if length < instance_count as usize {
326                    Err(RendererError::InvalidBufferLength(
327                        name.to_string(),
328                        instance_count as usize,
329                        length,
330                    ))?;
331                }
332            }
333            Ok(())
334        };
335
336        buffer_check(
337            self.texture_transformations.as_ref().map(|b| b.len()),
338            "texture transformations",
339        )?;
340        buffer_check(Some(self.transformations.len()), "transformations")?;
341        buffer_check(self.colors.as_ref().map(|b| b.len()), "colors")?;
342
343        Ok(())
344    }
345
346    /// Returns the number of instances.
347    pub fn count(&self) -> u32 {
348        self.transformations.len() as u32
349    }
350}
351
352impl From<PointCloud> for Instances {
353    fn from(points: PointCloud) -> Self {
354        Self {
355            transformations: points
356                .positions
357                .to_f32()
358                .into_iter()
359                .map(Mat4::from_translation)
360                .collect(),
361            colors: points.colors,
362            ..Default::default()
363        }
364    }
365}