mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 14:08:32 +00:00
feat(gltf): add name component to gltf mesh primitive (#13912)
# Objective - fixes https://github.com/bevyengine/bevy/issues/13473 ## Solution - When a single mesh is assigned multiple materials, it is divided into several primitive nodes, with each primitive assigned a unique material. Presently, these primitives are named using the format Mesh.index, which complicates querying. To improve this, we can assign a specific name to each primitive based on the material’s name, since each primitive corresponds to one material exclusively. ## Testing - I have included a simple example which shows how to query a material and mesh part based on the new name component. ## Changelog - adds `GltfMaterialName` component to the mesh entity of the gltf primitive node. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
5289e18e0b
commit
78a3aae81b
6 changed files with 121 additions and 1 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -910,6 +910,17 @@ description = "Loads and renders a glTF file as a scene, including the gltf extr
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "query_gltf_primitives"
|
||||||
|
path = "examples/3d/query_gltf_primitives.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.query_gltf_primitives]
|
||||||
|
name = "Query glTF primitives"
|
||||||
|
description = "Query primitives in a glTF scene"
|
||||||
|
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/GltfPrimitives/gltf_primitives.glb
Normal file
BIN
assets/models/GltfPrimitives/gltf_primitives.glb
Normal file
Binary file not shown.
|
@ -153,6 +153,7 @@ impl Plugin for GltfPlugin {
|
||||||
.register_type::<GltfSceneExtras>()
|
.register_type::<GltfSceneExtras>()
|
||||||
.register_type::<GltfMeshExtras>()
|
.register_type::<GltfMeshExtras>()
|
||||||
.register_type::<GltfMaterialExtras>()
|
.register_type::<GltfMaterialExtras>()
|
||||||
|
.register_type::<GltfMaterialName>()
|
||||||
.init_asset::<Gltf>()
|
.init_asset::<Gltf>()
|
||||||
.init_asset::<GltfNode>()
|
.init_asset::<GltfNode>()
|
||||||
.init_asset::<GltfPrimitive>()
|
.init_asset::<GltfPrimitive>()
|
||||||
|
@ -460,6 +461,13 @@ pub struct GltfMaterialExtras {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The material name of a glTF primitive.
|
||||||
|
///
|
||||||
|
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-material).
|
||||||
|
#[derive(Clone, Debug, Reflect, Default, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct GltfMaterialName(pub 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,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
vertex_attributes::convert_attribute, Gltf, GltfAssetLabel, GltfExtras, GltfMaterialExtras,
|
vertex_attributes::convert_attribute, Gltf, GltfAssetLabel, GltfExtras, GltfMaterialExtras,
|
||||||
GltfMeshExtras, GltfNode, GltfSceneExtras, GltfSkin,
|
GltfMaterialName, GltfMeshExtras, GltfNode, GltfSceneExtras, GltfSkin,
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloc::collections::VecDeque;
|
use alloc::collections::VecDeque;
|
||||||
|
@ -1380,6 +1380,10 @@ fn load_node(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(name) = material.name() {
|
||||||
|
mesh_entity.insert(GltfMaterialName(String::from(name)));
|
||||||
|
}
|
||||||
|
|
||||||
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() {
|
||||||
|
|
96
examples/3d/query_gltf_primitives.rs
Normal file
96
examples/3d/query_gltf_primitives.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
//! This example demonstrates how to query a [`StandardMaterial`] within a glTF scene.
|
||||||
|
//! It is particularly useful for glTF scenes with a mesh that consists of multiple primitives.
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
gltf::GltfMaterialName,
|
||||||
|
pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap},
|
||||||
|
prelude::*,
|
||||||
|
render::mesh::VertexAttributeValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.insert_resource(DirectionalLightShadowMap { size: 4096 })
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, find_top_material_and_mesh)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_top_material_and_mesh(
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
time: Res<Time>,
|
||||||
|
mat_query: Query<(&Handle<StandardMaterial>, &Handle<Mesh>, &GltfMaterialName)>,
|
||||||
|
) {
|
||||||
|
for (mat_handle, mesh_handle, name) in mat_query.iter() {
|
||||||
|
// locate a material by material name
|
||||||
|
if name.0 == "Top" {
|
||||||
|
if let Some(material) = materials.get_mut(mat_handle) {
|
||||||
|
if let Color::Hsla(ref mut hsla) = material.base_color {
|
||||||
|
*hsla = hsla.rotate_hue(time.delta_seconds() * 100.0);
|
||||||
|
} else {
|
||||||
|
material.base_color = Color::from(Hsla::hsl(0.0, 0.8, 0.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mesh) = meshes.get_mut(mesh_handle) {
|
||||||
|
if let Some(VertexAttributeValues::Float32x3(positions)) =
|
||||||
|
mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)
|
||||||
|
{
|
||||||
|
positions[0] = (
|
||||||
|
ops::sin(2.0 * PI * time.elapsed_seconds()),
|
||||||
|
positions[0][1],
|
||||||
|
positions[0][2],
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
commands.spawn((
|
||||||
|
Camera3dBundle {
|
||||||
|
transform: Transform::from_xyz(0.6, 1.6, 11.3)
|
||||||
|
.looking_at(Vec3::new(0.0, 0.0, 3.0), Vec3::Y),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
EnvironmentMapLight {
|
||||||
|
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||||
|
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||||
|
intensity: 500.0,
|
||||||
|
rotation: Quat::IDENTITY,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.spawn(DirectionalLightBundle {
|
||||||
|
directional_light: DirectionalLight {
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
// This is a relatively small scene, so use tighter shadow
|
||||||
|
// cascade bounds than the default for better quality.
|
||||||
|
// We also adjusted the shadow map to be larger since we're
|
||||||
|
// only using a single cascade.
|
||||||
|
cascade_shadow_config: CascadeShadowConfigBuilder {
|
||||||
|
num_cascades: 1,
|
||||||
|
maximum_distance: 1.6,
|
||||||
|
..default()
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
commands.spawn(SceneBundle {
|
||||||
|
scene: asset_server
|
||||||
|
.load(GltfAssetLabel::Scene(0).from_asset("models/GltfPrimitives/gltf_primitives.glb")),
|
||||||
|
transform: Transform {
|
||||||
|
rotation: Quat::from_rotation_y(-90.0 / 180.0 * PI),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
}
|
|
@ -159,6 +159,7 @@ Example | Description
|
||||||
[Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations
|
[Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations
|
||||||
[Percentage-closer soft shadows](../examples/3d/pcss.rs) | Demonstrates percentage-closer soft shadows (PCSS)
|
[Percentage-closer soft shadows](../examples/3d/pcss.rs) | Demonstrates percentage-closer soft shadows (PCSS)
|
||||||
[Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
|
[Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
|
||||||
|
[Query glTF primitives](../examples/3d/query_gltf_primitives.rs) | Query primitives in a glTF scene
|
||||||
[Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes
|
[Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes
|
||||||
[Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images
|
[Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images
|
||||||
[Rotate Environment Map](../examples/3d/rotate_environment_map.rs) | Demonstrates how to rotate the skybox and the environment map simultaneously
|
[Rotate Environment Map](../examples/3d/rotate_environment_map.rs) | Demonstrates how to rotate the skybox and the environment map simultaneously
|
||||||
|
|
Loading…
Add table
Reference in a new issue