Path: blob/main/crates/bevy_gltf/src/loader/extensions/mod.rs
9374 views
//! glTF extensions defined by the Khronos Group and other vendors12mod khr_materials_anisotropy;3mod khr_materials_clearcoat;4mod khr_materials_specular;56use alloc::sync::Arc;7use async_lock::RwLock;89use bevy_asset::{Handle, LoadContext};10use bevy_ecs::{11entity::Entity,12resource::Resource,13world::{EntityWorldMut, World},14};15use gltf::Node;1617#[cfg(feature = "bevy_animation")]18use {19bevy_animation::AnimationClip,20bevy_platform::collections::{HashMap, HashSet},21};2223use crate::{GltfMaterial, GltfMesh};2425pub(crate) use self::{26khr_materials_anisotropy::AnisotropyExtension, khr_materials_clearcoat::ClearcoatExtension,27khr_materials_specular::SpecularExtension,28};2930/// Stores the `GltfExtensionHandler` implementations so that they31/// can be added by users and also passed to the glTF loader32#[derive(Resource, Default)]33pub struct GltfExtensionHandlers(pub Arc<RwLock<Vec<Box<dyn GltfExtensionHandler>>>>);3435/// glTF Extensions can attach data to any objects in a glTF file.36/// This is done by inserting data in the `extensions` sub-object, and37/// data in the extensions sub-object is keyed by the id of the extension.38/// For example: `KHR_materials_variants`, `EXT_meshopt_compression`, or `BEVY_my_tool`39///40/// A list of publicly known extensions and their ids can be found41/// in the [KhronosGroup/glTF](https://github.com/KhronosGroup/glTF/blob/main/extensions/README.md)42/// git repo. Vendors reserve prefixes, such as the `BEVY` prefix,43/// which is also listed in the [KhronosGroup repo](https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md).44///45/// The `GltfExtensionHandler` trait should be implemented to participate in46/// processing glTF files as they load, and exposes glTF extension data via47/// a series of hook callbacks.48///49/// The type a `GltfExtensionHandler` is implemented for can define data50/// which will be cloned for each new glTF load. This enables stateful51/// handling of glTF extension data during a single load.52///53/// When loading a glTF file, a glTF object that could contain extension54/// data will cause the relevant hook to execute once per object.55/// Each invocation will receive all extension data, which is required because56/// many extensions require accessing data defined by other extensions.57///58/// The hooks are always called once, even if there is no extension data59/// This is useful for scenarios where additional extension data isn't60/// required, but processing should still happen.61pub trait GltfExtensionHandler: Send + Sync {62/// Required for dyn cloning63fn dyn_clone(&self) -> Box<dyn GltfExtensionHandler>;6465/// Called when the "global" data for an extension66/// at the root of a glTF file is encountered.67#[expect(68unused,69reason = "default trait implementations do not use the arguments because they are no-ops"70)]71fn on_root(&mut self, load_context: &mut LoadContext<'_>, gltf: &gltf::Gltf) {}7273#[cfg(feature = "bevy_animation")]74#[expect(75unused,76reason = "default trait implementations do not use the arguments because they are no-ops"77)]78/// Called when an individual animation is processed79fn on_animation(&mut self, gltf_animation: &gltf::Animation, handle: Handle<AnimationClip>) {}8081#[cfg(feature = "bevy_animation")]82#[expect(83unused,84reason = "default trait implementations do not use the arguments because they are no-ops"85)]86/// Called when all animations have been collected.87/// `animations` is the glTF ordered list of `Handle<AnimationClip>`s88/// `named_animations` is a `HashMap` from animation name to `Handle<AnimationClip>`89/// `animation_roots` is the glTF index of the animation root object90fn on_animations_collected(91&mut self,92load_context: &mut LoadContext<'_>,93animations: &[Handle<AnimationClip>],94named_animations: &HashMap<Box<str>, Handle<AnimationClip>>,95animation_roots: &HashSet<usize>,96) {97}9899/// Called when an individual texture is processed100#[expect(101unused,102reason = "default trait implementations do not use the arguments because they are no-ops"103)]104fn on_texture(&mut self, gltf_texture: &gltf::Texture, texture: Handle<bevy_image::Image>) {}105106/// Called when an individual material is processed107#[expect(108unused,109reason = "default trait implementations do not use the arguments because they are no-ops"110)]111fn on_material(112&mut self,113load_context: &mut LoadContext<'_>,114gltf_material: &gltf::Material,115material: Handle<GltfMaterial>,116material_asset: &GltfMaterial,117material_label: &str,118) {119}120121/// Called when an individual glTF Mesh is processed122#[expect(123unused,124reason = "default trait implementations do not use the arguments because they are no-ops"125)]126fn on_gltf_mesh(127&mut self,128load_context: &mut LoadContext<'_>,129gltf_mesh: &gltf::Mesh,130mesh: Handle<GltfMesh>,131) {132}133134/// mesh and material are spawned as a single Entity,135/// which means an extension would have to decide for136/// itself how to merge the extension data.137#[expect(138unused,139reason = "default trait implementations do not use the arguments because they are no-ops"140)]141fn on_spawn_mesh_and_material(142&mut self,143load_context: &mut LoadContext<'_>,144primitive: &gltf::Primitive,145mesh: &gltf::Mesh,146material: &gltf::Material,147entity: &mut EntityWorldMut,148material_label: &str,149) {150}151152/// Called when an individual Scene is done processing153#[expect(154unused,155reason = "default trait implementations do not use the arguments because they are no-ops"156)]157fn on_scene_completed(158&mut self,159load_context: &mut LoadContext<'_>,160scene: &gltf::Scene,161world_root_id: Entity,162scene_world: &mut World,163) {164}165166/// Called when a node is processed167#[expect(168unused,169reason = "default trait implementations do not use the arguments because they are no-ops"170)]171fn on_gltf_node(172&mut self,173load_context: &mut LoadContext<'_>,174gltf_node: &Node,175entity: &mut EntityWorldMut,176) {177}178179/// Called with a `DirectionalLight` node is spawned180/// which is typically created as a result of181/// `KHR_lights_punctual`182#[expect(183unused,184reason = "default trait implementations do not use the arguments because they are no-ops"185)]186fn on_spawn_light_directional(187&mut self,188load_context: &mut LoadContext<'_>,189gltf_node: &Node,190entity: &mut EntityWorldMut,191) {192}193/// Called with a `PointLight` node is spawned194/// which is typically created as a result of195/// `KHR_lights_punctual`196#[expect(197unused,198reason = "default trait implementations do not use the arguments because they are no-ops"199)]200fn on_spawn_light_point(201&mut self,202load_context: &mut LoadContext<'_>,203gltf_node: &Node,204entity: &mut EntityWorldMut,205) {206}207/// Called with a `SpotLight` node is spawned208/// which is typically created as a result of209/// `KHR_lights_punctual`210#[expect(211unused,212reason = "default trait implementations do not use the arguments because they are no-ops"213)]214fn on_spawn_light_spot(215&mut self,216load_context: &mut LoadContext<'_>,217gltf_node: &Node,218entity: &mut EntityWorldMut,219) {220}221}222223impl Clone for Box<dyn GltfExtensionHandler> {224fn clone(&self) -> Self {225self.dyn_clone()226}227}228229230