add handling of all missing gltf extras: scene, mesh & materials (#13453)

# Objective

- fixes #4823 

## Solution

As outlined in the discussion in the linked issue as the best current
solution, this PR adds specific GltfExtras for
 - scenes 
 - meshes
 - materials

- As it is , it is not a breaking change, I hesitated to rename the
current "GltfExtras" component to "PrimitiveGltfExtras", but that would
result in a breaking change and might be a bit confusing as to what
"primitive" that refers to.
 

## Testing

- I included a bare-bones example & asset (exported gltf file from
Blender) with gltf extras at all the relevant levels : scene, mesh,
material

---

## Changelog
- adds "SceneGltfExtras" injected at the scene level if any
- adds "MeshGltfExtras", injected at the mesh level if any
- adds "MaterialGltfExtras", injected at the mesh level if any: ie if a
mesh has a material that has gltf extras, the component will be injected
there.
This commit is contained in:
Mark Moissette 2024-06-03 15:16:38 +02:00 committed by GitHub
parent 061bee7e3c
commit d26900a9ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 171 additions and 4 deletions

View file

@ -802,6 +802,17 @@ description = "Loads and renders a glTF file as a scene"
category = "3D Rendering"
wasm = true
[[example]]
name = "load_gltf_extras"
path = "examples/3d/load_gltf_extras.rs"
doc-scrape-examples = true
[package.metadata.example.load_gltf_extras]
name = "Load glTF extras"
description = "Loads and renders a glTF file as a scene, including the gltf extras"
category = "3D Rendering"
wasm = true
[[example]]
name = "motion_blur"
path = "examples/3d/motion_blur.rs"

Binary file not shown.

View file

@ -146,6 +146,9 @@ impl GltfPlugin {
impl Plugin for GltfPlugin {
fn build(&self, app: &mut App) {
app.register_type::<GltfExtras>()
.register_type::<GltfSceneExtras>()
.register_type::<GltfMeshExtras>()
.register_type::<GltfMaterialExtras>()
.init_asset::<Gltf>()
.init_asset::<GltfNode>()
.init_asset::<GltfPrimitive>()
@ -239,7 +242,7 @@ pub struct GltfPrimitive {
pub material_extras: Option<GltfExtras>,
}
/// Additional untyped data that can be present on most glTF types.
/// Additional untyped data that can be present on most glTF types at the primitive level.
///
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-extras).
#[derive(Clone, Debug, Reflect, Default, Component)]
@ -249,6 +252,36 @@ pub struct GltfExtras {
pub value: String,
}
/// Additional untyped data that can be present on most glTF types at the scene level.
///
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-extras).
#[derive(Clone, Debug, Reflect, Default, Component)]
#[reflect(Component)]
pub struct GltfSceneExtras {
/// Content of the extra data.
pub value: String,
}
/// Additional untyped data that can be present on most glTF types at the mesh level.
///
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-extras).
#[derive(Clone, Debug, Reflect, Default, Component)]
#[reflect(Component)]
pub struct GltfMeshExtras {
/// Content of the extra data.
pub value: String,
}
/// Additional untyped data that can be present on most glTF types at the material level.
///
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-extras).
#[derive(Clone, Debug, Reflect, Default, Component)]
#[reflect(Component)]
pub struct GltfMaterialExtras {
/// Content of the extra data.
pub value: String,
}
/// Labels that can be used to load part of a glTF
///
/// You can use [`GltfAssetLabel::from_asset`] to add it to an asset path

View file

@ -1,5 +1,8 @@
use crate::GltfAssetLabel;
use crate::{vertex_attributes::convert_attribute, Gltf, GltfExtras, GltfNode};
use crate::{
vertex_attributes::convert_attribute, Gltf, GltfAssetLabel, GltfExtras, GltfMaterialExtras,
GltfMeshExtras, GltfNode, GltfSceneExtras,
};
#[cfg(feature = "bevy_animation")]
use bevy_animation::{AnimationTarget, AnimationTargetId};
use bevy_asset::{
@ -634,7 +637,8 @@ async fn load_gltf<'a, 'b, 'c>(
let mut node_index_to_entity_map = HashMap::new();
let mut entity_to_skin_index_map = EntityHashMap::default();
let mut scene_load_context = load_context.begin_labeled_asset();
world
let world_root_id = world
.spawn(SpatialBundle::INHERITED_IDENTITY)
.with_children(|parent| {
for node in scene.nodes() {
@ -659,7 +663,15 @@ async fn load_gltf<'a, 'b, 'c>(
return;
}
}
})
.id();
if let Some(extras) = scene.extras().as_ref() {
world.entity_mut(world_root_id).insert(GltfSceneExtras {
value: extras.get().to_string(),
});
}
if let Some(Err(err)) = err {
return Err(err);
}
@ -1270,6 +1282,7 @@ fn load_node(
material: load_context.get_label_handle(&material_label),
..Default::default()
});
let target_count = primitive.morph_targets().len();
if target_count != 0 {
let weights = match mesh.weights() {
@ -1300,6 +1313,18 @@ fn load_node(
});
}
if let Some(extras) = mesh.extras() {
mesh_entity.insert(GltfMeshExtras {
value: extras.get().to_string(),
});
}
if let Some(extras) = material.extras() {
mesh_entity.insert(GltfMaterialExtras {
value: extras.get().to_string(),
});
}
mesh_entity.insert(Name::new(primitive_name(&mesh, &primitive)));
// Mark for adding skinned mesh
if let Some(skin) = gltf_node.skin() {

View file

@ -0,0 +1,97 @@
//! Loads and renders a glTF file as a scene, and list all the different `gltf_extras`.
use bevy::{
gltf::{GltfExtras, GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras},
prelude::*,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, check_for_gltf_extras)
.run();
}
#[derive(Component)]
struct ExampleDisplay;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(2.0, 2.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
shadows_enabled: true,
..default()
},
..default()
});
// a barebones scene containing one of each gltf_extra type
commands.spawn(SceneBundle {
scene: asset_server.load("models/extras/gltf_extras.glb#Scene0"),
..default()
});
// a place to display the extras on screen
commands.spawn((
TextBundle::from_section(
"",
TextStyle {
font_size: 18.,
..default()
},
)
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
}),
ExampleDisplay,
));
}
fn check_for_gltf_extras(
gltf_extras_per_entity: Query<(
Entity,
Option<&Name>,
Option<&GltfSceneExtras>,
Option<&GltfExtras>,
Option<&GltfMeshExtras>,
Option<&GltfMaterialExtras>,
)>,
mut display: Query<&mut Text, With<ExampleDisplay>>,
) {
let mut gltf_extra_infos_lines: Vec<String> = vec![];
for (id, name, scene_extras, extras, mesh_extras, material_extras) in
gltf_extras_per_entity.iter()
{
if scene_extras.is_some()
|| extras.is_some()
|| mesh_extras.is_some()
|| material_extras.is_some()
{
let formatted_extras = format!(
"Extras per entity {} ('Name: {}'):
- scene extras: {:?}
- primitive extras: {:?}
- mesh extras: {:?}
- material extras: {:?}
",
id,
name.unwrap_or(&Name::default()),
scene_extras,
extras,
mesh_extras,
material_extras
);
gltf_extra_infos_lines.push(formatted_extras);
}
let mut display = display.single_mut();
display.sections[0].value = gltf_extra_infos_lines.join("\n");
}
}

View file

@ -143,6 +143,7 @@ Example | Description
[Lightmaps](../examples/3d/lightmaps.rs) | Rendering a scene with baked lightmaps
[Lines](../examples/3d/lines.rs) | Create a custom material to draw 3d lines
[Load glTF](../examples/3d/load_gltf.rs) | Loads and renders a glTF file as a scene
[Load glTF extras](../examples/3d/load_gltf_extras.rs) | Loads and renders a glTF file as a scene, including the gltf extras
[Meshlet](../examples/3d/meshlet.rs) | Meshlet rendering for dense high-poly scenes (experimental)
[Motion Blur](../examples/3d/motion_blur.rs) | Demonstrates per-pixel motion blur
[Orthographic View](../examples/3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look in games or CAD applications)