(
data: &[u8],
renderer: &mut crate::renderer::Renderer,
base_dir: Option<&std::path::Path>,
)
| 1018 | } |
| 1019 | |
| 1020 | fn load_gltf_with_textures( |
| 1021 | data: &[u8], |
| 1022 | renderer: &mut crate::renderer::Renderer, |
| 1023 | base_dir: Option<&std::path::Path>, |
| 1024 | ) -> Option<ModelData> { |
| 1025 | let gltf = gltf::Gltf::from_slice(data).ok()?; |
| 1026 | |
| 1027 | // Get buffer data |
| 1028 | let mut buffer_data: Vec<Vec<u8>> = Vec::new(); |
| 1029 | for buffer in gltf.buffers() { |
| 1030 | match buffer.source() { |
| 1031 | gltf::buffer::Source::Bin => { |
| 1032 | if let Some(blob) = gltf.blob.as_ref() { buffer_data.push(blob.clone()); } |
| 1033 | } |
| 1034 | gltf::buffer::Source::Uri(uri) => { |
| 1035 | if let Some(encoded) = uri.strip_prefix("data:application/octet-stream;base64,") { |
| 1036 | let mut decoded = Vec::new(); |
| 1037 | let _ = base64_decode(encoded, &mut decoded); |
| 1038 | buffer_data.push(decoded); |
| 1039 | } else if let Some(dir) = base_dir { |
| 1040 | // External .bin file alongside the .gltf. |
| 1041 | let path = dir.join(uri); |
| 1042 | match std::fs::read(&path) { |
| 1043 | Ok(bytes) => buffer_data.push(bytes), |
| 1044 | Err(_) => buffer_data.push(Vec::new()), |
| 1045 | } |
| 1046 | } else { |
| 1047 | buffer_data.push(Vec::new()); |
| 1048 | } |
| 1049 | } |
| 1050 | } |
| 1051 | } |
| 1052 | |
| 1053 | // Pre-walk materials to identify which image indices are normal |
| 1054 | // maps. They need LEADR-style vector-space mip generation and per- |
| 1055 | // mip variance baked into alpha; see register_texture_kind. |
| 1056 | let mut normal_image_set: std::collections::HashSet<usize> = Default::default(); |
| 1057 | for mat in gltf.materials() { |
| 1058 | if let Some(nt) = mat.normal_texture() { |
| 1059 | normal_image_set.insert(nt.texture().source().index()); |
| 1060 | } |
| 1061 | } |
| 1062 | |
| 1063 | // Extract and register textures |
| 1064 | let mut texture_indices: Vec<u32> = Vec::new(); // maps glTF image index -> renderer texture index |
| 1065 | for (image_idx, image) in gltf.images().enumerate() { |
| 1066 | let is_normal = normal_image_set.contains(&image_idx); |
| 1067 | match image.source() { |
| 1068 | gltf::image::Source::View { view, .. } => { |
| 1069 | let buf_idx = view.buffer().index(); |
| 1070 | if buf_idx < buffer_data.len() { |
| 1071 | let offset = view.offset(); |
| 1072 | let length = view.length(); |
| 1073 | if offset + length <= buffer_data[buf_idx].len() { |
| 1074 | let img_data = &buffer_data[buf_idx][offset..offset + length]; |
| 1075 | // Decode image (PNG/JPEG) |
| 1076 | if let Ok(img) = image::load_from_memory(img_data) { |
| 1077 | let rgba = img.to_rgba8(); |
no test coverage detected