Skip to main content

flow_ngin/resources/
mod.rs

1use std::{
2    collections::HashMap,
3    convert::identity,
4    io::{BufReader, Cursor},
5};
6
7use crate::{
8    data_structures::{
9        model::{self},
10        scene_graph::{AnimationClip, ContainerNode, SceneNode, to_scene_node},
11        texture::Texture,
12    }, pick::PickId, resources::{
13        animation::Keyframes,
14        texture::{diffuse_normal_layout, load_binary, load_texture},
15    }
16};
17
18/**
19 * This module contains all logic for loading mesh/textures/etc. from external files.
20 */
21pub mod animation;
22pub mod mesh;
23pub mod pick;
24pub mod texture;
25
26pub async fn load_model_obj(
27    file_name: &str,
28    device: &wgpu::Device,
29    queue: &wgpu::Queue,
30) -> anyhow::Result<model::Model> {
31    let bind_group_layout = diffuse_normal_layout(device);
32
33    let (materials, models) =
34        texture::load_textures(file_name, queue, device, &bind_group_layout).await?;
35    let meshes = mesh::load_meshes(&models, file_name, device);
36    let meshes = meshes.into_iter().enumerate().filter_map(|(idx, result)| {
37        match result {
38            Ok(mesh) => Some(mesh),
39            Err(_) => {
40                log::warn!("Mesh at index {} in file {} could not be loaded due to overflows. Make sure you use the right scale in your .obj export settings.", idx, file_name);
41                None
42            },
43        }
44    }).collect();
45
46    let model = model::Model { meshes, materials };
47    Ok(model)
48}
49
50/// Loads a gltf model incl. aninmations into a `SceneNode`.
51///
52/// `id` is a unique identifyer to identify click events on this resource.
53pub async fn load_model_gltf(
54    id: impl Into<PickId>,
55    file_name: &str,
56    device: &wgpu::Device,
57    queue: &wgpu::Queue,
58) -> anyhow::Result<Box<dyn SceneNode + Send>> {
59    let gltf_text = load_binary(file_name).await?;
60    let gltf_cursor = Cursor::new(gltf_text);
61    let gltf_reader = BufReader::new(gltf_cursor);
62    let gltf = gltf::Gltf::from_reader(gltf_reader)?;
63
64    // Load buffers
65    let mut buffer_data = Vec::new();
66    for buffer in gltf.buffers() {
67        match buffer.source() {
68            gltf::buffer::Source::Bin => {
69                if let Some(blob) = gltf.blob.as_deref() {
70                    buffer_data.push(blob.into());
71                };
72            }
73            gltf::buffer::Source::Uri(uri) => {
74                let bin = load_binary(uri).await?;
75                buffer_data.push(bin);
76            }
77        }
78    }
79    // Load animations
80    let mut animations: HashMap<usize, Vec<AnimationClip>> = HashMap::new();
81    for animation in gltf.animations() {
82        for channel in animation.channels() {
83            let reader = channel.reader(|buffer| Some(&buffer_data[buffer.index()]));
84            let timestamps = if let Some(inputs) = reader.read_inputs() {
85                match inputs {
86                    gltf::accessor::Iter::Standard(times) => {
87                        let times: Vec<f32> = times.collect();
88                        times
89                    }
90                    gltf::accessor::Iter::Sparse(_) => {
91                        let times: Vec<f32> = Vec::new();
92                        times
93                    }
94                }
95            } else {
96                log::warn!("No animation found in channel {}", channel.index());
97                let times: Vec<f32> = Vec::new();
98                times
99            };
100            let keyframes = if let Some(outputs) = reader.read_outputs() {
101                match outputs {
102                    gltf::animation::util::ReadOutputs::Translations(translation) => {
103                        let translation_vec = translation
104                            .map(|tr| {
105                                let vector = tr.into();
106                                vector
107                            })
108                            .collect();
109                        Keyframes::Translation(translation_vec)
110                    }
111                    gltf::animation::util::ReadOutputs::Rotations(rotation) => {
112                        let quaternions: Vec<cgmath::Quaternion<f32>> = rotation
113                            .into_f32()
114                            .map(|quat| {
115                                let quat = quat.into();
116                                quat
117                            })
118                            .collect();
119                        Keyframes::Rotation(quaternions)
120                    }
121                    gltf::animation::util::ReadOutputs::Scales(scales) => {
122                        let quaternion = scales
123                            .map(|sc| {
124                                let sc = sc.into();
125                                sc
126                            })
127                            .collect();
128                        Keyframes::Scale(quaternion)
129                    }
130                    // TODO: implement morphing
131                    gltf::animation::util::ReadOutputs::MorphTargetWeights(_) => Keyframes::Other,
132                }
133            } else {
134                log::warn!("No Keyframes found in channel {}", channel.index());
135                Keyframes::Other
136            };
137            let name = animation.name().unwrap_or("Default").to_string();
138            let animation = AnimationClip {
139                name,
140                keyframes,
141                timestamps,
142            };
143            animations
144                .entry(channel.target().node().index())
145                .and_modify(|v| v.push(animation.clone()))
146                .or_insert(vec![animation]);
147        }
148    }
149    // Load materials
150    let mut materials = Vec::new();
151    for material in gltf.materials() {
152        let pbr = material.pbr_metallic_roughness();
153        let texture_source = &pbr
154            .base_color_texture()
155            .map(|tex| tex.texture().source().source())
156            .expect("texture");
157        let diffuse_texture = match texture_source {
158            gltf::image::Source::View { view, mime_type } => {
159                let diffuse_texture = Texture::from_bytes(
160                    device,
161                    queue,
162                    &buffer_data[view.buffer().index()],
163                    file_name,
164                    mime_type.split('/').last(),
165                    false,
166                )
167                .expect("Couldn't load diffuse");
168                diffuse_texture
169            }
170            gltf::image::Source::Uri { uri, mime_type } => {
171                let diffuse_texture = load_texture(
172                    uri,
173                    false,
174                    device,
175                    queue,
176                    mime_type.map(|mt| mt.split('/').last().map_or("jpg", identity)),
177                )
178                .await?;
179                diffuse_texture
180            }
181        };
182        let normal_texture = if let Some(texture) = material.normal_texture() {
183            // TODO: add this as param for Textures
184            // let sampler = texture.texture().sampler().mag_filter().unwrap();
185            // println!("tex: {:?}", sampler);
186            // let sampler = texture.texture().sampler().min_filter().unwrap();
187            // println!("tex: {:?}", sampler);
188            // let sampler = texture.texture().sampler().wrap_s();
189            // println!("tex: {:?}", sampler);
190            // let sampler = texture.texture().sampler().wrap_t();
191            // println!("tex: {:?}", sampler);
192            // let sampler = texture.texture().sampler().index().unwrap();
193            // println!("tex: {:?}", sampler);
194            match &texture.texture().source().source() {
195                gltf::image::Source::View { view, mime_type: _ } => {
196                    let texture = Texture::from_bytes(
197                        device,
198                        queue,
199                        &buffer_data[view.buffer().index()],
200                        file_name,
201                        None,
202                        true,
203                    )
204                    .expect("Couldn't load normal");
205                    texture
206                }
207                // TODO: parse and pass the mime_type so that the img lib does't have to guess
208                gltf::image::Source::Uri { uri, mime_type: _ } => {
209                    let texture = load_texture(uri, true, device, queue, None).await?;
210                    texture
211                }
212            }
213        } else {
214            Texture::create_default_normal_map(2, 2, device, queue)
215        };
216        let name = format!("{}.gltf", file_name);
217        let name = name.as_str();
218        let layout = &diffuse_normal_layout(device);
219        if let Ok(material) =
220            model::Material::new(device, name, diffuse_texture, normal_texture, layout)
221        {
222            materials.push(material);
223        } else {
224            log::warn!("Failed to create material for gltf ({})", file_name);
225        }
226    }
227
228    let mut models = Vec::new();
229
230    let id = id.into();
231    for scene in gltf.scenes() {
232        for node in scene.nodes() {
233            let model = to_scene_node(id, node, &buffer_data, device, &materials, &animations);
234            models.push(model);
235        }
236    }
237
238    let mut root_node = if models.len() == 1 {
239        models.into_iter().next().unwrap()
240    } else {
241        let mut root_node = ContainerNode::new(1, Vec::new());
242        root_node.children = models;
243        Box::new(root_node)
244    };
245    root_node.update_world_transform_all();
246
247    Ok(root_node)
248}