edit_material_on_gltf/
edit_material_on_gltf.rs

1//! Showcases how to change the material of a `Scene` spawned from a Gltf
2
3use bevy::{
4    audio::AudioPlugin, color::palettes, gltf::GltfMaterialName, prelude::*,
5    scene::SceneInstanceReady,
6};
7
8fn main() {
9    App::new()
10        .add_plugins(DefaultPlugins.build().disable::<AudioPlugin>())
11        .add_systems(Startup, setup_scene)
12        .add_observer(change_material)
13        .run();
14}
15
16/// This is added to a [`SceneRoot`] and will cause the [`StandardMaterial::base_color`]
17/// of all materials to be overwritten
18#[derive(Component)]
19struct ColorOverride(Color);
20
21fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
22    commands.spawn((
23        Camera3d::default(),
24        Transform::from_xyz(0., 1., 2.5).looking_at(Vec3::new(0., 0.25, 0.), Dir3::Y),
25    ));
26
27    commands.spawn((
28        DirectionalLight::default(),
29        Transform::from_xyz(0., 1., 0.25).looking_at(Vec3::ZERO, Dir3::Y),
30    ));
31
32    // FlightHelmet handle
33    let flight_helmet = asset_server
34        .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
35    // This model will keep its original materials
36    commands.spawn(SceneRoot(flight_helmet.clone()));
37    // This model will be tinted red
38    commands.spawn((
39        SceneRoot(flight_helmet.clone()),
40        Transform::from_xyz(-1.25, 0., 0.),
41        ColorOverride(palettes::tailwind::RED_300.into()),
42    ));
43    // This model will be tinted green
44    commands.spawn((
45        SceneRoot(flight_helmet),
46        Transform::from_xyz(1.25, 0., 0.),
47        ColorOverride(palettes::tailwind::GREEN_300.into()),
48    ));
49}
50
51fn change_material(
52    scene_ready: On<SceneInstanceReady>,
53    mut commands: Commands,
54    children: Query<&Children>,
55    color_override: Query<&ColorOverride>,
56    mesh_materials: Query<(&MeshMaterial3d<StandardMaterial>, &GltfMaterialName)>,
57    mut asset_materials: ResMut<Assets<StandardMaterial>>,
58) {
59    info!("processing Scene Entity: {}", scene_ready.entity);
60    // Iterate over all children recursively
61    for descendant in children.iter_descendants(scene_ready.entity) {
62        // Get the material id and name which were created from the glTF file information
63        let Ok((id, material_name)) = mesh_materials.get(descendant) else {
64            continue;
65        };
66        // Get the material of the descendant
67        let Some(material) = asset_materials.get_mut(id.id()) else {
68            continue;
69        };
70
71        // match on the material name, modifying the materials as necessary
72        match material_name.0.as_str() {
73            "LeatherPartsMat" => {
74                info!("editing LeatherPartsMat to use ColorOverride tint");
75                // Get the `ColorOverride` of the entity, if it does not have a color override, skip
76                let Ok(color_override) = color_override.get(scene_ready.entity) else {
77                    continue;
78                };
79                // Create a copy of the material and override base color
80                // If you intend on creating multiple models with the same tint, it
81                // is best to cache the handle somewhere, as having multiple materials
82                // that are identical is expensive
83                let mut new_material = material.clone();
84                new_material.base_color = color_override.0;
85
86                // Override `MeshMaterial3d` with new material
87                commands
88                    .entity(descendant)
89                    .insert(MeshMaterial3d(asset_materials.add(new_material)));
90            }
91            name => {
92                info!("not replacing: {name}");
93            }
94        }
95    }
96}