custom_gltf_vertex_attribute/
custom_gltf_vertex_attribute.rs

1//! Renders a glTF mesh in 2D with a custom vertex attribute.
2
3use bevy::{
4    gltf::GltfPlugin,
5    prelude::*,
6    reflect::TypePath,
7    render::{
8        mesh::{MeshVertexAttribute, MeshVertexBufferLayoutRef},
9        render_resource::*,
10    },
11    sprite::{Material2d, Material2dKey, Material2dPlugin},
12};
13
14/// This example uses a shader source file from the assets subdirectory
15const SHADER_ASSET_PATH: &str = "shaders/custom_gltf_2d.wgsl";
16
17/// This vertex attribute supplies barycentric coordinates for each triangle.
18///
19/// Each component of the vector corresponds to one corner of a triangle. It's
20/// equal to 1.0 in that corner and 0.0 in the other two. Hence, its value in
21/// the fragment shader indicates proximity to a corner or the opposite edge.
22const ATTRIBUTE_BARYCENTRIC: MeshVertexAttribute =
23    MeshVertexAttribute::new("Barycentric", 2137464976, VertexFormat::Float32x3);
24
25fn main() {
26    App::new()
27        .insert_resource(AmbientLight {
28            color: Color::WHITE,
29            brightness: 1.0 / 5.0f32,
30            ..default()
31        })
32        .add_plugins((
33            DefaultPlugins.set(
34                GltfPlugin::default()
35                    // Map a custom glTF attribute name to a `MeshVertexAttribute`.
36                    // The glTF file used here has an attribute name with *two*
37                    // underscores: __BARYCENTRIC
38                    // One is stripped to do the comparison here.
39                    .add_custom_vertex_attribute("_BARYCENTRIC", ATTRIBUTE_BARYCENTRIC),
40            ),
41            Material2dPlugin::<CustomMaterial>::default(),
42        ))
43        .add_systems(Startup, setup)
44        .run();
45}
46
47fn setup(
48    mut commands: Commands,
49    asset_server: Res<AssetServer>,
50    mut materials: ResMut<Assets<CustomMaterial>>,
51) {
52    // Add a mesh loaded from a glTF file. This mesh has data for `ATTRIBUTE_BARYCENTRIC`.
53    let mesh = asset_server.load(
54        GltfAssetLabel::Primitive {
55            mesh: 0,
56            primitive: 0,
57        }
58        .from_asset("models/barycentric/barycentric.gltf"),
59    );
60    commands.spawn((
61        Mesh2d(mesh),
62        MeshMaterial2d(materials.add(CustomMaterial {})),
63        Transform::from_scale(150.0 * Vec3::ONE),
64    ));
65
66    commands.spawn(Camera2d);
67}
68
69/// This custom material uses barycentric coordinates from
70/// `ATTRIBUTE_BARYCENTRIC` to shade a white border around each triangle. The
71/// thickness of the border is animated using the global time shader uniform.
72#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
73struct CustomMaterial {}
74
75impl Material2d for CustomMaterial {
76    fn vertex_shader() -> ShaderRef {
77        SHADER_ASSET_PATH.into()
78    }
79    fn fragment_shader() -> ShaderRef {
80        SHADER_ASSET_PATH.into()
81    }
82
83    fn specialize(
84        descriptor: &mut RenderPipelineDescriptor,
85        layout: &MeshVertexBufferLayoutRef,
86        _key: Material2dKey<Self>,
87    ) -> Result<(), SpecializedMeshPipelineError> {
88        let vertex_layout = layout.0.get_layout(&[
89            Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
90            Mesh::ATTRIBUTE_COLOR.at_shader_location(1),
91            ATTRIBUTE_BARYCENTRIC.at_shader_location(2),
92        ])?;
93        descriptor.vertex.buffers = vec![vertex_layout];
94        Ok(())
95    }
96}