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