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:
parent
061bee7e3c
commit
d26900a9ea
11
Cargo.toml
11
Cargo.toml
@ -802,6 +802,17 @@ description = "Loads and renders a glTF file as a scene"
|
|||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
wasm = true
|
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]]
|
[[example]]
|
||||||
name = "motion_blur"
|
name = "motion_blur"
|
||||||
path = "examples/3d/motion_blur.rs"
|
path = "examples/3d/motion_blur.rs"
|
||||||
|
BIN
assets/models/extras/gltf_extras.glb
Normal file
BIN
assets/models/extras/gltf_extras.glb
Normal file
Binary file not shown.
@ -146,6 +146,9 @@ impl GltfPlugin {
|
|||||||
impl Plugin for GltfPlugin {
|
impl Plugin for GltfPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.register_type::<GltfExtras>()
|
app.register_type::<GltfExtras>()
|
||||||
|
.register_type::<GltfSceneExtras>()
|
||||||
|
.register_type::<GltfMeshExtras>()
|
||||||
|
.register_type::<GltfMaterialExtras>()
|
||||||
.init_asset::<Gltf>()
|
.init_asset::<Gltf>()
|
||||||
.init_asset::<GltfNode>()
|
.init_asset::<GltfNode>()
|
||||||
.init_asset::<GltfPrimitive>()
|
.init_asset::<GltfPrimitive>()
|
||||||
@ -239,7 +242,7 @@ pub struct GltfPrimitive {
|
|||||||
pub material_extras: Option<GltfExtras>,
|
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).
|
/// 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)]
|
#[derive(Clone, Debug, Reflect, Default, Component)]
|
||||||
@ -249,6 +252,36 @@ pub struct GltfExtras {
|
|||||||
pub value: String,
|
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
|
/// Labels that can be used to load part of a glTF
|
||||||
///
|
///
|
||||||
/// You can use [`GltfAssetLabel::from_asset`] to add it to an asset path
|
/// You can use [`GltfAssetLabel::from_asset`] to add it to an asset path
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::GltfAssetLabel;
|
use crate::{
|
||||||
use crate::{vertex_attributes::convert_attribute, Gltf, GltfExtras, GltfNode};
|
vertex_attributes::convert_attribute, Gltf, GltfAssetLabel, GltfExtras, GltfMaterialExtras,
|
||||||
|
GltfMeshExtras, GltfNode, GltfSceneExtras,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "bevy_animation")]
|
#[cfg(feature = "bevy_animation")]
|
||||||
use bevy_animation::{AnimationTarget, AnimationTargetId};
|
use bevy_animation::{AnimationTarget, AnimationTargetId};
|
||||||
use bevy_asset::{
|
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 node_index_to_entity_map = HashMap::new();
|
||||||
let mut entity_to_skin_index_map = EntityHashMap::default();
|
let mut entity_to_skin_index_map = EntityHashMap::default();
|
||||||
let mut scene_load_context = load_context.begin_labeled_asset();
|
let mut scene_load_context = load_context.begin_labeled_asset();
|
||||||
world
|
|
||||||
|
let world_root_id = world
|
||||||
.spawn(SpatialBundle::INHERITED_IDENTITY)
|
.spawn(SpatialBundle::INHERITED_IDENTITY)
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
for node in scene.nodes() {
|
for node in scene.nodes() {
|
||||||
@ -659,7 +663,15 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
return;
|
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 {
|
if let Some(Err(err)) = err {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
@ -1270,6 +1282,7 @@ fn load_node(
|
|||||||
material: load_context.get_label_handle(&material_label),
|
material: load_context.get_label_handle(&material_label),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let target_count = primitive.morph_targets().len();
|
let target_count = primitive.morph_targets().len();
|
||||||
if target_count != 0 {
|
if target_count != 0 {
|
||||||
let weights = match mesh.weights() {
|
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)));
|
mesh_entity.insert(Name::new(primitive_name(&mesh, &primitive)));
|
||||||
// Mark for adding skinned mesh
|
// Mark for adding skinned mesh
|
||||||
if let Some(skin) = gltf_node.skin() {
|
if let Some(skin) = gltf_node.skin() {
|
||||||
|
97
examples/3d/load_gltf_extras.rs
Normal file
97
examples/3d/load_gltf_extras.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
@ -143,6 +143,7 @@ Example | Description
|
|||||||
[Lightmaps](../examples/3d/lightmaps.rs) | Rendering a scene with baked lightmaps
|
[Lightmaps](../examples/3d/lightmaps.rs) | Rendering a scene with baked lightmaps
|
||||||
[Lines](../examples/3d/lines.rs) | Create a custom material to draw 3d lines
|
[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](../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)
|
[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
|
[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)
|
[Orthographic View](../examples/3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look in games or CAD applications)
|
||||||
|
Loading…
Reference in New Issue
Block a user