Add GltfLoaderSettings (#10804)

# Objective

when loading gltfs we may want to filter the results. in particular, i
need to be able to exclude cameras.

i can do this already by modifying the gltf after load and before
spawning, but it seems like a useful general option.

## Solution

add `GltfLoaderSettings` struct with bool members:
- `load_cameras` : checked before processing camera nodes.
- `load_lights` : checked before processing light nodes
- `load_meshes` : checked before loading meshes, materials and morph
weights

Existing code will work as before. Now you also have the option to
restrict what parts of the gltf are loaded. For example, to load a gltf
but exclude the cameras, replace a call to
`asset_server.load("my.gltf")` with:
```rust
asset_server.load_with_settings(
    "my.gltf",
    |s: &mut GltfLoaderSettings| {
        s.load_cameras = false;
    }
);
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
robtfm 2023-11-30 00:34:45 +00:00 committed by GitHub
parent 0a588dbfd9
commit 74ead1eb80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -39,7 +39,7 @@ use gltf::{
texture::{MagFilter, MinFilter, WrappingMode}, texture::{MagFilter, MinFilter, WrappingMode},
Material, Node, Primitive, Semantic, Material, Node, Primitive, Semantic,
}; };
use serde::Deserialize; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -105,20 +105,57 @@ pub struct GltfLoader {
pub custom_vertex_attributes: HashMap<String, MeshVertexAttribute>, pub custom_vertex_attributes: HashMap<String, MeshVertexAttribute>,
} }
/// Specifies optional settings for processing gltfs at load time. By default, all recognized contents of
/// the gltf will be loaded.
///
/// # Example
///
/// To load a gltf but exclude the cameras, replace a call to `asset_server.load("my.gltf")` with
/// ```no_run
/// # use bevy_asset::{AssetServer, Handle};
/// # use bevy_gltf::*;
/// # let asset_server: AssetServer = panic!();
/// let gltf_handle: Handle<Gltf> = asset_server.load_with_settings(
/// "my.gltf",
/// |s: &mut GltfLoaderSettings| {
/// s.load_cameras = false;
/// }
/// );
/// ```
#[derive(Serialize, Deserialize)]
pub struct GltfLoaderSettings {
/// If true, the loader will load mesh nodes and the associated materials.
pub load_meshes: bool,
/// If true, the loader will spawn cameras for gltf camera nodes.
pub load_cameras: bool,
/// If true, the loader will spawn lights for gltf light nodes.
pub load_lights: bool,
}
impl Default for GltfLoaderSettings {
fn default() -> Self {
Self {
load_meshes: true,
load_cameras: true,
load_lights: true,
}
}
}
impl AssetLoader for GltfLoader { impl AssetLoader for GltfLoader {
type Asset = Gltf; type Asset = Gltf;
type Settings = (); type Settings = GltfLoaderSettings;
type Error = GltfError; type Error = GltfError;
fn load<'a>( fn load<'a>(
&'a self, &'a self,
reader: &'a mut Reader, reader: &'a mut Reader,
_settings: &'a (), settings: &'a GltfLoaderSettings,
load_context: &'a mut LoadContext, load_context: &'a mut LoadContext,
) -> bevy_utils::BoxedFuture<'a, Result<Gltf, Self::Error>> { ) -> bevy_utils::BoxedFuture<'a, Result<Gltf, Self::Error>> {
Box::pin(async move { Box::pin(async move {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?; reader.read_to_end(&mut bytes).await?;
load_gltf(self, &bytes, load_context).await load_gltf(self, &bytes, load_context, settings).await
}) })
} }
@ -132,6 +169,7 @@ async fn load_gltf<'a, 'b, 'c>(
loader: &GltfLoader, loader: &GltfLoader,
bytes: &'a [u8], bytes: &'a [u8],
load_context: &'b mut LoadContext<'c>, load_context: &'b mut LoadContext<'c>,
settings: &'b GltfLoaderSettings,
) -> Result<Gltf, GltfError> { ) -> Result<Gltf, GltfError> {
let gltf = gltf::Gltf::from_slice(bytes)?; let gltf = gltf::Gltf::from_slice(bytes)?;
let buffer_data = load_buffers(&gltf, load_context).await?; let buffer_data = load_buffers(&gltf, load_context).await?;
@ -555,6 +593,7 @@ async fn load_gltf<'a, 'b, 'c>(
parent, parent,
load_context, load_context,
&mut scene_load_context, &mut scene_load_context,
settings,
&mut node_index_to_entity_map, &mut node_index_to_entity_map,
&mut entity_to_skin_index_map, &mut entity_to_skin_index_map,
&mut active_camera_found, &mut active_camera_found,
@ -860,6 +899,7 @@ fn load_node(
world_builder: &mut WorldChildBuilder, world_builder: &mut WorldChildBuilder,
root_load_context: &LoadContext, root_load_context: &LoadContext,
load_context: &mut LoadContext, load_context: &mut LoadContext,
settings: &GltfLoaderSettings,
node_index_to_entity_map: &mut HashMap<usize, Entity>, node_index_to_entity_map: &mut HashMap<usize, Entity>,
entity_to_skin_index_map: &mut HashMap<Entity, usize>, entity_to_skin_index_map: &mut HashMap<Entity, usize>,
active_camera_found: &mut bool, active_camera_found: &mut bool,
@ -887,6 +927,7 @@ fn load_node(
} }
// create camera node // create camera node
if settings.load_cameras {
if let Some(camera) = gltf_node.camera() { if let Some(camera) = gltf_node.camera() {
let projection = match camera.projection() { let projection = match camera.projection() {
gltf::camera::Projection::Orthographic(orthographic) => { gltf::camera::Projection::Orthographic(orthographic) => {
@ -928,6 +969,7 @@ fn load_node(
*active_camera_found = true; *active_camera_found = true;
} }
}
// Map node index to entity // Map node index to entity
node_index_to_entity_map.insert(gltf_node.index(), node.id()); node_index_to_entity_map.insert(gltf_node.index(), node.id());
@ -935,6 +977,7 @@ fn load_node(
let mut morph_weights = None; let mut morph_weights = None;
node.with_children(|parent| { node.with_children(|parent| {
if settings.load_meshes {
if let Some(mesh) = gltf_node.mesh() { if let Some(mesh) = gltf_node.mesh() {
// append primitives // append primitives
for primitive in mesh.primitives() { for primitive in mesh.primitives() {
@ -997,7 +1040,9 @@ fn load_node(
} }
} }
} }
}
if settings.load_lights {
if let Some(light) = gltf_node.light() { if let Some(light) = gltf_node.light() {
match light.kind() { match light.kind() {
gltf::khr_lights_punctual::Kind::Directional => { gltf::khr_lights_punctual::Kind::Directional => {
@ -1073,6 +1118,7 @@ fn load_node(
} }
} }
} }
}
// append other nodes // append other nodes
for child in gltf_node.children() { for child in gltf_node.children() {
@ -1081,6 +1127,7 @@ fn load_node(
parent, parent,
root_load_context, root_load_context,
load_context, load_context,
settings,
node_index_to_entity_map, node_index_to_entity_map,
entity_to_skin_index_map, entity_to_skin_index_map,
active_camera_found, active_camera_found,
@ -1092,11 +1139,13 @@ fn load_node(
} }
}); });
if settings.load_meshes {
if let (Some(mesh), Some(weights)) = (gltf_node.mesh(), morph_weights) { if let (Some(mesh), Some(weights)) = (gltf_node.mesh(), morph_weights) {
let primitive_label = mesh.primitives().next().map(|p| primitive_label(&mesh, &p)); let primitive_label = mesh.primitives().next().map(|p| primitive_label(&mesh, &p));
let first_mesh = primitive_label.map(|label| load_context.get_label_handle(label)); let first_mesh = primitive_label.map(|label| load_context.get_label_handle(label));
node.insert(MorphWeights::new(weights, first_mesh)?); node.insert(MorphWeights::new(weights, first_mesh)?);
} }
}
if let Some(err) = gltf_error { if let Some(err) = gltf_error {
Err(err) Err(err)