1use std::collections::HashMap;
14
15use serde::{Deserialize, Serialize};
16use wde_logger::prelude::*;
17
18use bevy::{
19 asset::{AssetLoader, LoadContext, io::Reader},
20 prelude::*
21};
22use wde_pbr::prelude::*;
23use wde_renderer::prelude::*;
24
25#[doc(hidden)]
26pub mod prelude {
27 pub use crate::GltfAsset;
28 pub use crate::GltfError;
29 pub use crate::GltfLoader;
30 pub use crate::GltfLoaderSettings;
31 pub use crate::GltfSpawnQueue;
32}
33
34mod accessor;
35mod error;
36mod loader;
37mod material;
38mod model;
39mod parser;
40
41pub use error::GltfError;
42
43pub struct GltfPlugin;
44impl Plugin for GltfPlugin {
45 fn build(&self, app: &mut App) {
46 app.init_asset::<GltfAsset>()
47 .init_asset_loader::<GltfAssetLoader>()
48 .init_resource::<GltfSpawnQueue>()
49 .add_systems(Update, process_gltf_spawn_queue);
50 }
51}
52
53#[derive(Resource, Default)]
55pub struct GltfSpawnQueue {
56 pending: Vec<(Entity, Handle<GltfAsset>)>
57}
58
59#[derive(Asset, TypePath, Clone)]
63pub struct GltfAsset {
64 path: String,
65
66 pub models: Vec<(Handle<Mesh>, Handle<PbrMaterial>)>,
69 pub bbox: MeshBbox,
71 pub properties: HashMap<String, (Vec3, Vec3)>
74}
75
76#[derive(Serialize, Deserialize, Default)]
78pub struct GltfLoaderSettings {
79 pub stencil_value: Option<u32>
81}
82
83#[derive(Default, TypePath)]
84pub(crate) struct GltfAssetLoader;
85impl AssetLoader for GltfAssetLoader {
86 type Asset = GltfAsset;
87 type Settings = GltfLoaderSettings;
88 type Error = GltfError;
89
90 async fn load(
91 &self,
92 reader: &mut dyn Reader,
93 _settings: &Self::Settings,
94 load_context: &mut LoadContext<'_>
95 ) -> Result<Self::Asset, Self::Error> {
96 let path = load_context.path().clone();
97 debug!("Loading glTF file {}.", &path);
98
99 let mut bytes = Vec::new();
101 reader.read_to_end(&mut bytes).await?;
102 let model = parser::parse_gltf(bytes, &path.to_string())?;
103
104 let (raw_materials, raw_meshes, bounding_boxes) = loader::form_models(&model)?;
106
107 let materials_handles: Vec<Handle<PbrMaterial>> = raw_materials
109 .iter()
110 .map(|material| material.to_pbr(load_context))
111 .collect();
112
113 let mut models = Vec::new();
115 let mut bbox_min = Vec3::splat(f32::INFINITY);
116 let mut bbox_max = Vec3::splat(f32::NEG_INFINITY);
117 for (i, (indices_data, vertices, material_id)) in raw_meshes.iter().enumerate() {
118 let label = format!("gltf_mesh_{}", i);
119 let (bb_min, bb_max) = bounding_boxes[i];
120 let mesh_asset = Mesh {
121 label: label.clone(),
122 vertices: vertices.clone(),
123 indices: indices_data.clone(),
124 bbox: MeshBbox {
125 min: bb_min,
126 max: bb_max
127 },
128 use_ssbo: true
129 };
130
131 models.push((
132 load_context.add_labeled_asset(label.clone(), mesh_asset),
133 materials_handles[*material_id].clone()
134 ));
135
136 for j in 0..3 {
138 if bb_min[j] < bbox_min[j] {
139 bbox_min[j] = bb_min[j];
140 }
141 if bb_max[j] > bbox_max[j] {
142 bbox_max[j] = bb_max[j];
143 }
144 }
145 }
146
147 Ok(GltfAsset {
148 path: path.to_string(),
149 models,
150 bbox: MeshBbox {
151 min: bbox_min,
152 max: bbox_max
153 },
154 properties: model.properties
155 })
156 }
157
158 fn extensions(&self) -> &[&str] {
159 &["gltf", "glb"]
160 }
161}
162
163pub struct GltfLoader;
166impl GltfLoader {
167 pub fn spawn(queue: &mut GltfSpawnQueue, gltf_asset: Handle<GltfAsset>, parent: Entity) {
169 queue.pending.push((parent, gltf_asset));
170 }
171
172 pub fn try_spawn(
175 commands: &mut Commands,
176 gltf_asset: &Handle<GltfAsset>,
177 gltf_assets: &Assets<GltfAsset>,
178 parent: Entity
179 ) -> Option<()> {
180 let gltf_asset = gltf_assets.get(gltf_asset)?;
181 for (i, (mesh_handle, material_handle)) in gltf_asset.models.iter().enumerate() {
182 commands.spawn((
183 Name::new(format!(
184 "Mesh Entity {} for GLTF Model {}",
185 i, gltf_asset.path
186 )),
187 Transform::default(),
188 Mesh3d(mesh_handle.clone()),
189 PbrMaterial3d(material_handle.clone()),
190 ChildOf(parent)
191 ));
192 }
193 Some(())
194 }
195}
196
197fn process_gltf_spawn_queue(
198 mut commands: Commands,
199 mut queue: ResMut<GltfSpawnQueue>,
200 gltf_assets: Res<Assets<GltfAsset>>
201) {
202 let mut i = 0;
203 while i < queue.pending.len() {
204 let (parent, gltf_asset) = &queue.pending[i];
205 if GltfLoader::try_spawn(&mut commands, gltf_asset, &gltf_assets, *parent).is_some() {
206 queue.pending.swap_remove(i);
207 } else {
208 i += 1;
209 }
210 }
211}