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