
Reduce dependencies on `bevy_render` by preferring `bevy_mesh` imports over `bevy_render` re-exports. ```diff - use bevy_render::mesh::Mesh; + use bevy_mesh::Mesh; ``` This is intended to help with #18423 (render crate restructure). Affects `bevy_gltf`, `bevy_animation` and `bevy_picking`. As part of #18423, I'm assuming there'll be a push to make crates less dependent on the big render crates. This PR seemed like a small and safe step along that path - it only changes imports and makes the `bevy_mesh` crate dependency explicit in `Cargo.toml`. Any remaining dependencies on `bevy_render` are true dependencies. ``` cargo run --example testbed_3d cargo run --example mesh_picking ```
166 lines
5.6 KiB
Rust
166 lines
5.6 KiB
Rust
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
#![forbid(unsafe_code)]
|
|
#![doc(
|
|
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
|
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
|
)]
|
|
|
|
//! Plugin providing an [`AssetLoader`](bevy_asset::AssetLoader) and type definitions
|
|
//! for loading glTF 2.0 (a standard 3D scene definition format) files in Bevy.
|
|
//!
|
|
//! The [glTF 2.0 specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html) defines the format of the glTF files.
|
|
//!
|
|
//! # Quick Start
|
|
//!
|
|
//! Here's how to spawn a simple glTF scene
|
|
//!
|
|
//! ```
|
|
//! # use bevy_ecs::prelude::*;
|
|
//! # use bevy_asset::prelude::*;
|
|
//! # use bevy_scene::prelude::*;
|
|
//! # use bevy_transform::prelude::*;
|
|
//! # use bevy_gltf::prelude::*;
|
|
//!
|
|
//! fn spawn_gltf(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
//! commands.spawn((
|
|
//! // This is equivalent to "models/FlightHelmet/FlightHelmet.gltf#Scene0"
|
|
//! // The `#Scene0` label here is very important because it tells bevy to load the first scene in the glTF file.
|
|
//! // If this isn't specified bevy doesn't know which part of the glTF file to load.
|
|
//! SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"))),
|
|
//! // You can use the transform to give it a position
|
|
//! Transform::from_xyz(2.0, 0.0, -5.0),
|
|
//! ));
|
|
//! }
|
|
//! ```
|
|
//! # Loading parts of a glTF asset
|
|
//!
|
|
//! ## Using `Gltf`
|
|
//!
|
|
//! If you want to access part of the asset, you can load the entire `Gltf` using the `AssetServer`.
|
|
//! Once the `Handle<Gltf>` is loaded you can then use it to access named parts of it.
|
|
//!
|
|
//! ```
|
|
//! # use bevy_ecs::prelude::*;
|
|
//! # use bevy_asset::prelude::*;
|
|
//! # use bevy_scene::prelude::*;
|
|
//! # use bevy_transform::prelude::*;
|
|
//! # use bevy_gltf::Gltf;
|
|
//!
|
|
//! // Holds the scene handle
|
|
//! #[derive(Resource)]
|
|
//! struct HelmetScene(Handle<Gltf>);
|
|
//!
|
|
//! fn load_gltf(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
//! let gltf = asset_server.load("models/FlightHelmet/FlightHelmet.gltf");
|
|
//! commands.insert_resource(HelmetScene(gltf));
|
|
//! }
|
|
//!
|
|
//! fn spawn_gltf_objects(
|
|
//! mut commands: Commands,
|
|
//! helmet_scene: Res<HelmetScene>,
|
|
//! gltf_assets: Res<Assets<Gltf>>,
|
|
//! mut loaded: Local<bool>,
|
|
//! ) {
|
|
//! // Only do this once
|
|
//! if *loaded {
|
|
//! return;
|
|
//! }
|
|
//! // Wait until the scene is loaded
|
|
//! let Some(gltf) = gltf_assets.get(&helmet_scene.0) else {
|
|
//! return;
|
|
//! };
|
|
//! *loaded = true;
|
|
//!
|
|
//! // Spawns the first scene in the file
|
|
//! commands.spawn(SceneRoot(gltf.scenes[0].clone()));
|
|
//!
|
|
//! // Spawns the scene named "Lenses_low"
|
|
//! commands.spawn((
|
|
//! SceneRoot(gltf.named_scenes["Lenses_low"].clone()),
|
|
//! Transform::from_xyz(1.0, 2.0, 3.0),
|
|
//! ));
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! ## Asset Labels
|
|
//!
|
|
//! The glTF loader let's you specify labels that let you target specific parts of the glTF.
|
|
//!
|
|
//! Be careful when using this feature, if you misspell a label it will simply ignore it without warning.
|
|
//!
|
|
//! You can use [`GltfAssetLabel`] to ensure you are using the correct label.
|
|
|
|
mod assets;
|
|
mod label;
|
|
mod loader;
|
|
mod vertex_attributes;
|
|
|
|
extern crate alloc;
|
|
|
|
use bevy_platform_support::collections::HashMap;
|
|
|
|
use bevy_app::prelude::*;
|
|
use bevy_asset::AssetApp;
|
|
use bevy_image::CompressedImageFormats;
|
|
use bevy_mesh::MeshVertexAttribute;
|
|
use bevy_render::renderer::RenderDevice;
|
|
|
|
/// The glTF prelude.
|
|
///
|
|
/// This includes the most common types in this crate, re-exported for your convenience.
|
|
pub mod prelude {
|
|
#[doc(hidden)]
|
|
pub use crate::{assets::Gltf, assets::GltfExtras, label::GltfAssetLabel};
|
|
}
|
|
|
|
pub use {assets::*, label::GltfAssetLabel, loader::*};
|
|
|
|
/// Adds support for glTF file loading to the app.
|
|
#[derive(Default)]
|
|
pub struct GltfPlugin {
|
|
custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
|
|
}
|
|
|
|
impl GltfPlugin {
|
|
/// Register a custom vertex attribute so that it is recognized when loading a glTF file with the [`GltfLoader`].
|
|
///
|
|
/// `name` must be the attribute name as found in the glTF data, which must start with an underscore.
|
|
/// See [this section of the glTF specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview)
|
|
/// for additional details on custom attributes.
|
|
pub fn add_custom_vertex_attribute(
|
|
mut self,
|
|
name: &str,
|
|
attribute: MeshVertexAttribute,
|
|
) -> Self {
|
|
self.custom_vertex_attributes.insert(name.into(), attribute);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Plugin for GltfPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.register_type::<GltfExtras>()
|
|
.register_type::<GltfSceneExtras>()
|
|
.register_type::<GltfMeshExtras>()
|
|
.register_type::<GltfMaterialExtras>()
|
|
.register_type::<GltfMaterialName>()
|
|
.init_asset::<Gltf>()
|
|
.init_asset::<GltfNode>()
|
|
.init_asset::<GltfPrimitive>()
|
|
.init_asset::<GltfMesh>()
|
|
.init_asset::<GltfSkin>()
|
|
.preregister_asset_loader::<GltfLoader>(&["gltf", "glb"]);
|
|
}
|
|
|
|
fn finish(&self, app: &mut App) {
|
|
let supported_compressed_formats = match app.world().get_resource::<RenderDevice>() {
|
|
Some(render_device) => CompressedImageFormats::from_features(render_device.features()),
|
|
None => CompressedImageFormats::NONE,
|
|
};
|
|
app.register_asset_loader(GltfLoader {
|
|
supported_compressed_formats,
|
|
custom_vertex_attributes: self.custom_vertex_attributes.clone(),
|
|
});
|
|
}
|
|
}
|