diff --git a/Cargo.toml b/Cargo.toml index 8c2543542b..715d1e683a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -920,6 +920,17 @@ description = "Showcases different blend modes" category = "3D Rendering" wasm = true +[[example]] +name = "edit_material_on_gltf" +path = "examples/3d/edit_material_on_gltf.rs" +doc-scrape-examples = true + +[package.metadata.example.edit_material_on_gltf] +name = "Edit Gltf Material" +description = "Showcases changing materials of a Gltf after Scene spawn" +category = "3D Rendering" +wasm = true + [[example]] name = "lighting" path = "examples/3d/lighting.rs" diff --git a/examples/3d/edit_material_on_gltf.rs b/examples/3d/edit_material_on_gltf.rs new file mode 100644 index 0000000000..f9de5842a9 --- /dev/null +++ b/examples/3d/edit_material_on_gltf.rs @@ -0,0 +1,93 @@ +//! 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::()) + .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) { + 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, + mut commands: Commands, + children: Query<&Children>, + color_override: Query<&ColorOverride>, + mesh_materials: Query<&MeshMaterial3d>, + mut asset_materials: ResMut>, +) { + // 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))); + } + } +} diff --git a/examples/README.md b/examples/README.md index 9ff424a1d2..dc562b2bf9 100644 --- a/examples/README.md +++ b/examples/README.md @@ -150,6 +150,7 @@ Example | Description [Decal](../examples/3d/decal.rs) | Decal rendering [Deferred Rendering](../examples/3d/deferred_rendering.rs) | Renders meshes with both forward and deferred pipelines [Depth of field](../examples/3d/depth_of_field.rs) | Demonstrates depth of field +[Edit Gltf Material](../examples/3d/edit_material_on_gltf.rs) | Showcases changing materials of a Gltf after Scene spawn [Fog](../examples/3d/fog.rs) | A scene showcasing the distance fog effect [Fog volumes](../examples/3d/fog_volumes.rs) | Demonstrates fog volumes [Generate Custom Mesh](../examples/3d/generate_custom_mesh.rs) | Simple showcase of how to generate a custom mesh with a custom texture