Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gltf/src/loader/extensions/mod.rs
9374 views
1
//! glTF extensions defined by the Khronos Group and other vendors
2
3
mod khr_materials_anisotropy;
4
mod khr_materials_clearcoat;
5
mod khr_materials_specular;
6
7
use alloc::sync::Arc;
8
use async_lock::RwLock;
9
10
use bevy_asset::{Handle, LoadContext};
11
use bevy_ecs::{
12
entity::Entity,
13
resource::Resource,
14
world::{EntityWorldMut, World},
15
};
16
use gltf::Node;
17
18
#[cfg(feature = "bevy_animation")]
19
use {
20
bevy_animation::AnimationClip,
21
bevy_platform::collections::{HashMap, HashSet},
22
};
23
24
use crate::{GltfMaterial, GltfMesh};
25
26
pub(crate) use self::{
27
khr_materials_anisotropy::AnisotropyExtension, khr_materials_clearcoat::ClearcoatExtension,
28
khr_materials_specular::SpecularExtension,
29
};
30
31
/// Stores the `GltfExtensionHandler` implementations so that they
32
/// can be added by users and also passed to the glTF loader
33
#[derive(Resource, Default)]
34
pub struct GltfExtensionHandlers(pub Arc<RwLock<Vec<Box<dyn GltfExtensionHandler>>>>);
35
36
/// glTF Extensions can attach data to any objects in a glTF file.
37
/// This is done by inserting data in the `extensions` sub-object, and
38
/// data in the extensions sub-object is keyed by the id of the extension.
39
/// For example: `KHR_materials_variants`, `EXT_meshopt_compression`, or `BEVY_my_tool`
40
///
41
/// A list of publicly known extensions and their ids can be found
42
/// in the [KhronosGroup/glTF](https://github.com/KhronosGroup/glTF/blob/main/extensions/README.md)
43
/// git repo. Vendors reserve prefixes, such as the `BEVY` prefix,
44
/// which is also listed in the [KhronosGroup repo](https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md).
45
///
46
/// The `GltfExtensionHandler` trait should be implemented to participate in
47
/// processing glTF files as they load, and exposes glTF extension data via
48
/// a series of hook callbacks.
49
///
50
/// The type a `GltfExtensionHandler` is implemented for can define data
51
/// which will be cloned for each new glTF load. This enables stateful
52
/// handling of glTF extension data during a single load.
53
///
54
/// When loading a glTF file, a glTF object that could contain extension
55
/// data will cause the relevant hook to execute once per object.
56
/// Each invocation will receive all extension data, which is required because
57
/// many extensions require accessing data defined by other extensions.
58
///
59
/// The hooks are always called once, even if there is no extension data
60
/// This is useful for scenarios where additional extension data isn't
61
/// required, but processing should still happen.
62
pub trait GltfExtensionHandler: Send + Sync {
63
/// Required for dyn cloning
64
fn dyn_clone(&self) -> Box<dyn GltfExtensionHandler>;
65
66
/// Called when the "global" data for an extension
67
/// at the root of a glTF file is encountered.
68
#[expect(
69
unused,
70
reason = "default trait implementations do not use the arguments because they are no-ops"
71
)]
72
fn on_root(&mut self, load_context: &mut LoadContext<'_>, gltf: &gltf::Gltf) {}
73
74
#[cfg(feature = "bevy_animation")]
75
#[expect(
76
unused,
77
reason = "default trait implementations do not use the arguments because they are no-ops"
78
)]
79
/// Called when an individual animation is processed
80
fn on_animation(&mut self, gltf_animation: &gltf::Animation, handle: Handle<AnimationClip>) {}
81
82
#[cfg(feature = "bevy_animation")]
83
#[expect(
84
unused,
85
reason = "default trait implementations do not use the arguments because they are no-ops"
86
)]
87
/// Called when all animations have been collected.
88
/// `animations` is the glTF ordered list of `Handle<AnimationClip>`s
89
/// `named_animations` is a `HashMap` from animation name to `Handle<AnimationClip>`
90
/// `animation_roots` is the glTF index of the animation root object
91
fn on_animations_collected(
92
&mut self,
93
load_context: &mut LoadContext<'_>,
94
animations: &[Handle<AnimationClip>],
95
named_animations: &HashMap<Box<str>, Handle<AnimationClip>>,
96
animation_roots: &HashSet<usize>,
97
) {
98
}
99
100
/// Called when an individual texture is processed
101
#[expect(
102
unused,
103
reason = "default trait implementations do not use the arguments because they are no-ops"
104
)]
105
fn on_texture(&mut self, gltf_texture: &gltf::Texture, texture: Handle<bevy_image::Image>) {}
106
107
/// Called when an individual material is processed
108
#[expect(
109
unused,
110
reason = "default trait implementations do not use the arguments because they are no-ops"
111
)]
112
fn on_material(
113
&mut self,
114
load_context: &mut LoadContext<'_>,
115
gltf_material: &gltf::Material,
116
material: Handle<GltfMaterial>,
117
material_asset: &GltfMaterial,
118
material_label: &str,
119
) {
120
}
121
122
/// Called when an individual glTF Mesh is processed
123
#[expect(
124
unused,
125
reason = "default trait implementations do not use the arguments because they are no-ops"
126
)]
127
fn on_gltf_mesh(
128
&mut self,
129
load_context: &mut LoadContext<'_>,
130
gltf_mesh: &gltf::Mesh,
131
mesh: Handle<GltfMesh>,
132
) {
133
}
134
135
/// mesh and material are spawned as a single Entity,
136
/// which means an extension would have to decide for
137
/// itself how to merge the extension data.
138
#[expect(
139
unused,
140
reason = "default trait implementations do not use the arguments because they are no-ops"
141
)]
142
fn on_spawn_mesh_and_material(
143
&mut self,
144
load_context: &mut LoadContext<'_>,
145
primitive: &gltf::Primitive,
146
mesh: &gltf::Mesh,
147
material: &gltf::Material,
148
entity: &mut EntityWorldMut,
149
material_label: &str,
150
) {
151
}
152
153
/// Called when an individual Scene is done processing
154
#[expect(
155
unused,
156
reason = "default trait implementations do not use the arguments because they are no-ops"
157
)]
158
fn on_scene_completed(
159
&mut self,
160
load_context: &mut LoadContext<'_>,
161
scene: &gltf::Scene,
162
world_root_id: Entity,
163
scene_world: &mut World,
164
) {
165
}
166
167
/// Called when a node is processed
168
#[expect(
169
unused,
170
reason = "default trait implementations do not use the arguments because they are no-ops"
171
)]
172
fn on_gltf_node(
173
&mut self,
174
load_context: &mut LoadContext<'_>,
175
gltf_node: &Node,
176
entity: &mut EntityWorldMut,
177
) {
178
}
179
180
/// Called with a `DirectionalLight` node is spawned
181
/// which is typically created as a result of
182
/// `KHR_lights_punctual`
183
#[expect(
184
unused,
185
reason = "default trait implementations do not use the arguments because they are no-ops"
186
)]
187
fn on_spawn_light_directional(
188
&mut self,
189
load_context: &mut LoadContext<'_>,
190
gltf_node: &Node,
191
entity: &mut EntityWorldMut,
192
) {
193
}
194
/// Called with a `PointLight` node is spawned
195
/// which is typically created as a result of
196
/// `KHR_lights_punctual`
197
#[expect(
198
unused,
199
reason = "default trait implementations do not use the arguments because they are no-ops"
200
)]
201
fn on_spawn_light_point(
202
&mut self,
203
load_context: &mut LoadContext<'_>,
204
gltf_node: &Node,
205
entity: &mut EntityWorldMut,
206
) {
207
}
208
/// Called with a `SpotLight` node is spawned
209
/// which is typically created as a result of
210
/// `KHR_lights_punctual`
211
#[expect(
212
unused,
213
reason = "default trait implementations do not use the arguments because they are no-ops"
214
)]
215
fn on_spawn_light_spot(
216
&mut self,
217
load_context: &mut LoadContext<'_>,
218
gltf_node: &Node,
219
entity: &mut EntityWorldMut,
220
) {
221
}
222
}
223
224
impl Clone for Box<dyn GltfExtensionHandler> {
225
fn clone(&self) -> Self {
226
self.dyn_clone()
227
}
228
}
229
230