Skip to main content

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