#![cfg_attr(docsrs, feature(doc_auto_cfg))]1#![forbid(unsafe_code)]2#![doc(3html_logo_url = "https://bevy.org/assets/icon.png",4html_favicon_url = "https://bevy.org/assets/icon.png"5)]67//! Plugin providing an [`AssetLoader`](bevy_asset::AssetLoader) and type definitions8//! for loading glTF 2.0 (a standard 3D scene definition format) files in Bevy.9//!10//! The [glTF 2.0 specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html) defines the format of the glTF files.11//!12//! # Quick Start13//!14//! Here's how to spawn a simple glTF scene15//!16//! ```17//! # use bevy_ecs::prelude::*;18//! # use bevy_asset::prelude::*;19//! # use bevy_scene::prelude::*;20//! # use bevy_transform::prelude::*;21//! # use bevy_gltf::prelude::*;22//!23//! fn spawn_gltf(mut commands: Commands, asset_server: Res<AssetServer>) {24//! commands.spawn((25//! // This is equivalent to "models/FlightHelmet/FlightHelmet.gltf#Scene0"26//! // The `#Scene0` label here is very important because it tells bevy to load the first scene in the glTF file.27//! // If this isn't specified bevy doesn't know which part of the glTF file to load.28//! SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"))),29//! // You can use the transform to give it a position30//! Transform::from_xyz(2.0, 0.0, -5.0),31//! ));32//! }33//! ```34//! # Loading parts of a glTF asset35//!36//! ## Using `Gltf`37//!38//! If you want to access part of the asset, you can load the entire `Gltf` using the `AssetServer`.39//! Once the `Handle<Gltf>` is loaded you can then use it to access named parts of it.40//!41//! ```42//! # use bevy_ecs::prelude::*;43//! # use bevy_asset::prelude::*;44//! # use bevy_scene::prelude::*;45//! # use bevy_transform::prelude::*;46//! # use bevy_gltf::Gltf;47//!48//! // Holds the scene handle49//! #[derive(Resource)]50//! struct HelmetScene(Handle<Gltf>);51//!52//! fn load_gltf(mut commands: Commands, asset_server: Res<AssetServer>) {53//! let gltf = asset_server.load("models/FlightHelmet/FlightHelmet.gltf");54//! commands.insert_resource(HelmetScene(gltf));55//! }56//!57//! fn spawn_gltf_objects(58//! mut commands: Commands,59//! helmet_scene: Res<HelmetScene>,60//! gltf_assets: Res<Assets<Gltf>>,61//! mut loaded: Local<bool>,62//! ) {63//! // Only do this once64//! if *loaded {65//! return;66//! }67//! // Wait until the scene is loaded68//! let Some(gltf) = gltf_assets.get(&helmet_scene.0) else {69//! return;70//! };71//! *loaded = true;72//!73//! // Spawns the first scene in the file74//! commands.spawn(SceneRoot(gltf.scenes[0].clone()));75//!76//! // Spawns the scene named "Lenses_low"77//! commands.spawn((78//! SceneRoot(gltf.named_scenes["Lenses_low"].clone()),79//! Transform::from_xyz(1.0, 2.0, 3.0),80//! ));81//! }82//! ```83//!84//! ## Asset Labels85//!86//! The glTF loader let's you specify labels that let you target specific parts of the glTF.87//!88//! Be careful when using this feature, if you misspell a label it will simply ignore it without warning.89//!90//! You can use [`GltfAssetLabel`] to ensure you are using the correct label.9192mod assets;93mod convert_coordinates;94mod label;95mod loader;96mod vertex_attributes;9798extern crate alloc;99100use alloc::sync::Arc;101use std::sync::Mutex;102use tracing::warn;103104use bevy_platform::collections::HashMap;105106use bevy_app::prelude::*;107use bevy_asset::AssetApp;108use bevy_ecs::prelude::Resource;109use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats, ImageSamplerDescriptor};110use bevy_mesh::MeshVertexAttribute;111112/// The glTF prelude.113///114/// This includes the most common types in this crate, re-exported for your convenience.115pub mod prelude {116#[doc(hidden)]117pub use crate::{assets::Gltf, assets::GltfExtras, label::GltfAssetLabel};118}119120pub use {assets::*, label::GltfAssetLabel, loader::*};121122// Has to store an Arc<Mutex<...>> as there is no other way to mutate fields of asset loaders.123/// Stores default [`ImageSamplerDescriptor`] in main world.124#[derive(Resource)]125pub struct DefaultGltfImageSampler(Arc<Mutex<ImageSamplerDescriptor>>);126127impl DefaultGltfImageSampler {128/// Creates a new [`DefaultGltfImageSampler`].129pub fn new(descriptor: &ImageSamplerDescriptor) -> Self {130Self(Arc::new(Mutex::new(descriptor.clone())))131}132133/// Returns the current default [`ImageSamplerDescriptor`].134pub fn get(&self) -> ImageSamplerDescriptor {135self.0.lock().unwrap().clone()136}137138/// Makes a clone of internal [`Arc`] pointer.139///140/// Intended only to be used by code with no access to ECS.141pub fn get_internal(&self) -> Arc<Mutex<ImageSamplerDescriptor>> {142self.0.clone()143}144145/// Replaces default [`ImageSamplerDescriptor`].146///147/// Doesn't apply to samplers already built on top of it, i.e. `GltfLoader`'s output.148/// Assets need to manually be reloaded.149pub fn set(&self, descriptor: &ImageSamplerDescriptor) {150*self.0.lock().unwrap() = descriptor.clone();151}152}153154/// Adds support for glTF file loading to the app.155pub struct GltfPlugin {156/// The default image sampler to lay glTF sampler data on top of.157///158/// Can be modified with the [`DefaultGltfImageSampler`] resource.159pub default_sampler: ImageSamplerDescriptor,160161/// _CAUTION: This is an experimental feature with [known issues](https://github.com/bevyengine/bevy/issues/20621). Behavior may change in future versions._162///163/// How to convert glTF coordinates on import. Assuming glTF cameras, glTF lights, and glTF meshes had global identity transforms,164/// their Bevy [`Transform::forward`](bevy_transform::components::Transform::forward) will be pointing in the following global directions:165/// - When set to `false`166/// - glTF cameras and glTF lights: global -Z,167/// - glTF models: global +Z.168/// - When set to `true`169/// - glTF cameras and glTF lights: global +Z,170/// - glTF models: global -Z.171///172/// The default is `false`.173pub use_model_forward_direction: bool,174175/// Registry for custom vertex attributes.176///177/// To specify, use [`GltfPlugin::add_custom_vertex_attribute`].178pub custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,179}180181impl Default for GltfPlugin {182fn default() -> Self {183GltfPlugin {184default_sampler: ImageSamplerDescriptor::linear(),185custom_vertex_attributes: HashMap::default(),186use_model_forward_direction: false,187}188}189}190191impl GltfPlugin {192/// Register a custom vertex attribute so that it is recognized when loading a glTF file with the [`GltfLoader`].193///194/// `name` must be the attribute name as found in the glTF data, which must start with an underscore.195/// See [this section of the glTF specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview)196/// for additional details on custom attributes.197pub fn add_custom_vertex_attribute(198mut self,199name: &str,200attribute: MeshVertexAttribute,201) -> Self {202self.custom_vertex_attributes.insert(name.into(), attribute);203self204}205}206207impl Plugin for GltfPlugin {208fn build(&self, app: &mut App) {209app.init_asset::<Gltf>()210.init_asset::<GltfNode>()211.init_asset::<GltfPrimitive>()212.init_asset::<GltfMesh>()213.init_asset::<GltfSkin>()214.preregister_asset_loader::<GltfLoader>(&["gltf", "glb"]);215}216217fn finish(&self, app: &mut App) {218let supported_compressed_formats = if let Some(resource) =219app.world().get_resource::<CompressedImageFormatSupport>()220{221resource.0222} else {223warn!("CompressedImageFormatSupport resource not found. It should either be initialized in finish() of \224RenderPlugin, or manually if not using the RenderPlugin or the WGPU backend.");225CompressedImageFormats::NONE226};227228let default_sampler_resource = DefaultGltfImageSampler::new(&self.default_sampler);229let default_sampler = default_sampler_resource.get_internal();230app.insert_resource(default_sampler_resource);231232app.register_asset_loader(GltfLoader {233supported_compressed_formats,234custom_vertex_attributes: self.custom_vertex_attributes.clone(),235default_sampler,236default_use_model_forward_direction: self.use_model_forward_direction,237});238}239}240241242