mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +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"
|
||||
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]]
|
||||
name = "motion_blur"
|
||||
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::<GltfMeshExtras>()
|
||||
.register_type::<GltfMaterialExtras>()
|
||||
.register_type::<GltfMaterialName>()
|
||||
.init_asset::<Gltf>()
|
||||
.init_asset::<GltfNode>()
|
||||
.init_asset::<GltfPrimitive>()
|
||||
|
@ -460,6 +461,13 @@ pub struct GltfMaterialExtras {
|
|||
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
|
||||
///
|
||||
/// You can use [`GltfAssetLabel::from_asset`] to add it to an asset path
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
vertex_attributes::convert_attribute, Gltf, GltfAssetLabel, GltfExtras, GltfMaterialExtras,
|
||||
GltfMeshExtras, GltfNode, GltfSceneExtras, GltfSkin,
|
||||
GltfMaterialName, GltfMeshExtras, GltfNode, GltfSceneExtras, GltfSkin,
|
||||
};
|
||||
|
||||
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)));
|
||||
// Mark for adding skinned mesh
|
||||
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
|
||||
[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
|
||||
[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
|
||||
[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
|
||||
|
|
Loading…
Reference in a new issue