three_d/renderer/object/
instanced_model.rs

1use crate::renderer::*;
2
3///
4/// Part of an [InstancedModel] consisting of a [InstancedMesh], some type of [material] and a set of possible animations.
5///
6pub struct InstancedModelPart<M: Material> {
7    gm: Gm<InstancedMesh, M>,
8    animations: Vec<KeyFrameAnimation>,
9}
10
11impl<M: Material> InstancedModelPart<M> {
12    ///
13    /// Returns a list of unique names for the animations for this model part. Use these names as input to [Self::choose_animation].
14    ///
15    pub fn animations(&self) -> Vec<Option<String>> {
16        self.animations
17            .iter()
18            .map(|animation| animation.name.clone())
19            .collect()
20    }
21
22    ///
23    /// Specifies the animation to use when [Geometry::animate] is called. Use the [Self::animations] method to get a list of possible animations.
24    ///
25    pub fn choose_animation(&mut self, animation_name: Option<&str>) {
26        if let Some(animation) = self
27            .animations
28            .iter()
29            .find(|a| animation_name == a.name.as_deref())
30            .cloned()
31        {
32            self.set_animation(move |time| animation.transformation(time));
33        }
34    }
35}
36
37impl<'a, M: Material> IntoIterator for &'a InstancedModelPart<M> {
38    type Item = &'a dyn Object;
39    type IntoIter = std::iter::Once<&'a dyn Object>;
40
41    fn into_iter(self) -> Self::IntoIter {
42        self.gm.into_iter()
43    }
44}
45
46use std::ops::Deref;
47impl<M: Material> Deref for InstancedModelPart<M> {
48    type Target = Gm<InstancedMesh, M>;
49    fn deref(&self) -> &Self::Target {
50        &self.gm
51    }
52}
53
54impl<M: Material> std::ops::DerefMut for InstancedModelPart<M> {
55    fn deref_mut(&mut self) -> &mut Self::Target {
56        &mut self.gm
57    }
58}
59
60impl<M: Material> Geometry for InstancedModelPart<M> {
61    impl_geometry_body!(deref);
62
63    fn animate(&mut self, time: f32) {
64        self.gm.animate(time)
65    }
66}
67
68impl<M: Material> Object for InstancedModelPart<M> {
69    impl_object_body!(deref);
70}
71
72///
73/// Similar to [Model], except it is possible to render many instances of the same model efficiently.
74///
75pub struct InstancedModel<M: Material>(Vec<InstancedModelPart<M>>);
76
77impl<'a, M: Material> IntoIterator for &'a InstancedModel<M> {
78    type Item = &'a dyn Object;
79    type IntoIter = std::vec::IntoIter<&'a dyn Object>;
80
81    fn into_iter(self) -> Self::IntoIter {
82        self.iter()
83            .map(|m| m as &dyn Object)
84            .collect::<Vec<_>>()
85            .into_iter()
86    }
87}
88
89impl<M: Material + FromCpuMaterial + Clone + Default> InstancedModel<M> {
90    ///
91    /// Constructs an [InstancedModel] from a [CpuModel] and the given [Instances] attributes, ie. constructs a list of [Gm]s with a [InstancedMesh] as geometry (constructed from the [CpuMesh]es in the [CpuModel]) and
92    /// a [material] type specified by the generic parameter which implement [FromCpuMaterial] (constructed from the [CpuMaterial]s in the [CpuModel]).
93    ///
94    pub fn new(
95        context: &Context,
96        instances: &Instances,
97        cpu_model: &CpuModel,
98    ) -> Result<Self, RendererError> {
99        let materials = cpu_model
100            .materials
101            .iter()
102            .map(|m| M::from_cpu_material(context, m))
103            .collect::<Vec<_>>();
104        let mut gms = Vec::new();
105        for primitive in cpu_model.geometries.iter() {
106            if let CpuGeometry::Triangles(geometry) = &primitive.geometry {
107                let material = if let Some(material_index) = primitive.material_index {
108                    materials
109                        .get(material_index)
110                        .ok_or_else(|| {
111                            RendererError::MissingMaterial(
112                                material_index.to_string(),
113                                primitive.name.clone(),
114                            )
115                        })?
116                        .clone()
117                } else {
118                    M::default()
119                };
120                let mut gm = Gm {
121                    geometry: InstancedMesh::new(context, instances, geometry),
122                    material,
123                };
124                gm.set_transformation(primitive.transformation);
125                gms.push(InstancedModelPart {
126                    gm,
127                    animations: primitive.animations.clone(),
128                });
129            }
130        }
131        let mut model = Self(gms);
132        if let Some(animation_name) = model.animations().first().cloned() {
133            model.choose_animation(animation_name.as_deref());
134        }
135        Ok(model)
136    }
137
138    ///
139    /// Returns a list of unique names for the animations in this model. Use these names as input to [Self::choose_animation].
140    ///
141    pub fn animations(&self) -> Vec<Option<String>> {
142        let mut set = std::collections::HashSet::new();
143        for model_part in self.0.iter() {
144            set.extend(model_part.animations());
145        }
146        set.into_iter().collect()
147    }
148
149    ///
150    /// Specifies the animation to use when [Geometry::animate] is called. Use the [Self::animations] method to get a list of possible animations.
151    ///
152    pub fn choose_animation(&mut self, animation_name: Option<&str>) {
153        for part in self.0.iter_mut() {
154            part.choose_animation(animation_name);
155        }
156    }
157
158    ///
159    /// For updating the animation. The time parameter should be some continious time, for example the time since start.
160    ///
161    pub fn animate(&mut self, time: f32) {
162        self.iter_mut().for_each(|m| m.animate(time));
163    }
164}
165
166impl<M: Material> std::ops::Deref for InstancedModel<M> {
167    type Target = Vec<InstancedModelPart<M>>;
168    fn deref(&self) -> &Self::Target {
169        &self.0
170    }
171}
172
173impl<M: Material> std::ops::DerefMut for InstancedModel<M> {
174    fn deref_mut(&mut self) -> &mut Self::Target {
175        &mut self.0
176    }
177}