three_d/renderer/object/
model.rs

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