
# Objective Create a minimal example of how to modify the material from a `Gltf`. This is frequently asked about on the help channel of the discord. ## Solution Create the example. ## Showcase 
94 lines
3.2 KiB
Rust
94 lines
3.2 KiB
Rust
//! Showcases how to change the material of a `Scene` spawned from a Gltf
|
|
|
|
use bevy::{
|
|
app::{App, PluginGroup, Startup},
|
|
asset::{AssetServer, Assets},
|
|
audio::AudioPlugin,
|
|
color::{palettes, Color},
|
|
gltf::GltfAssetLabel,
|
|
math::{Dir3, Vec3},
|
|
pbr::{DirectionalLight, MeshMaterial3d, StandardMaterial},
|
|
prelude::{Camera3d, Children, Commands, Component, Query, Res, ResMut, Transform, Trigger},
|
|
scene::{SceneInstanceReady, SceneRoot},
|
|
DefaultPlugins,
|
|
};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins.build().disable::<AudioPlugin>())
|
|
.add_systems(Startup, setup_scene)
|
|
.add_observer(change_material)
|
|
.run();
|
|
}
|
|
|
|
/// This is added to a [`SceneRoot`] and will cause the [`StandardMaterial::base_color`]
|
|
/// of all materials to be overwritten
|
|
#[derive(Component)]
|
|
struct ColorOverride(Color);
|
|
|
|
fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
commands.spawn((
|
|
Camera3d::default(),
|
|
Transform::from_xyz(0., 1., 2.5).looking_at(Vec3::new(0., 0.25, 0.), Dir3::Y),
|
|
));
|
|
|
|
commands.spawn((
|
|
DirectionalLight::default(),
|
|
Transform::from_xyz(0., 1., 0.25).looking_at(Vec3::ZERO, Dir3::Y),
|
|
));
|
|
|
|
// FlightHelmet handle
|
|
let flight_helmet = asset_server
|
|
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
|
|
// This model will keep its original materials
|
|
commands.spawn(SceneRoot(flight_helmet.clone()));
|
|
// This model will be tinted red
|
|
commands.spawn((
|
|
SceneRoot(flight_helmet.clone()),
|
|
Transform::from_xyz(-1.25, 0., 0.),
|
|
ColorOverride(palettes::tailwind::RED_300.into()),
|
|
));
|
|
// This model will be tinted green
|
|
commands.spawn((
|
|
SceneRoot(flight_helmet),
|
|
Transform::from_xyz(1.25, 0., 0.),
|
|
ColorOverride(palettes::tailwind::GREEN_300.into()),
|
|
));
|
|
}
|
|
|
|
fn change_material(
|
|
trigger: Trigger<SceneInstanceReady>,
|
|
mut commands: Commands,
|
|
children: Query<&Children>,
|
|
color_override: Query<&ColorOverride>,
|
|
mesh_materials: Query<&MeshMaterial3d<StandardMaterial>>,
|
|
mut asset_materials: ResMut<Assets<StandardMaterial>>,
|
|
) {
|
|
// Get the `ColorOverride` of the entity, if it does not have a color override, skip
|
|
let Ok(color_override) = color_override.get(trigger.target()) else {
|
|
return;
|
|
};
|
|
|
|
// Iterate over all children recursively
|
|
for descendants in children.iter_descendants(trigger.target()) {
|
|
// Get the material of the descendant
|
|
if let Some(material) = mesh_materials
|
|
.get(descendants)
|
|
.ok()
|
|
.and_then(|id| asset_materials.get_mut(id.id()))
|
|
{
|
|
// Create a copy of the material and override base color
|
|
// If you intend on creating multiple models with the same tint, it
|
|
// is best to cache the handle somewhere, as having multiple materials
|
|
// that are identical is expensive
|
|
let mut new_material = material.clone();
|
|
new_material.base_color = color_override.0;
|
|
|
|
// Override `MeshMaterial3d` with new material
|
|
commands
|
|
.entity(descendants)
|
|
.insert(MeshMaterial3d(asset_materials.add(new_material)));
|
|
}
|
|
}
|
|
}
|