From d63a7a647ac985e449fbfd321181030da73a6237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tero=20Laxstr=C3=B6m?= Date: Thu, 13 Mar 2025 11:08:44 +0200 Subject: [PATCH] Basics for the multi_window_camera_ui example --- Cargo.toml | 11 ++ examples/window/multi_window_camera_ui.rs | 217 ++++++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 examples/window/multi_window_camera_ui.rs diff --git a/Cargo.toml b/Cargo.toml index 4efd0f5d87..8cfa6fba93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3540,6 +3540,17 @@ description = "Demonstrates creating multiple windows, and rendering to them" category = "Window" wasm = false +[[example]] +name = "multi_window_camera_ui" +path = "examples/window/multi_window_camera_ui.rs" +doc-scrape-examples = true + +[package.metadata.example.multi_window_camera_ui] +name = "Multi-Window Camera UI" +description = "Demonstrates creating multiple windows with multiple cameras with different UIs" +category = "Window" +wasm = false + [[example]] name = "scale_factor_override" path = "examples/window/scale_factor_override.rs" diff --git a/examples/window/multi_window_camera_ui.rs b/examples/window/multi_window_camera_ui.rs new file mode 100644 index 0000000000..6a5dfadaf3 --- /dev/null +++ b/examples/window/multi_window_camera_ui.rs @@ -0,0 +1,217 @@ +//! Uses two windows to visualize a 3D model from different angles. + +use bevy::color::palettes::css::{DEEP_SKY_BLUE, LIGHT_SKY_BLUE, YELLOW}; +use bevy::{prelude::*, render::camera::RenderTarget, window::WindowRef}; + +fn main() { + App::new() + // By default, a primary window gets spawned by `WindowPlugin`, contained in `DefaultPlugins` + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup_scene) + .run(); +} + +fn setup_scene(mut commands: Commands, asset_server: Res) { + // add entities to the world + commands.spawn(SceneRoot( + asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/torus/torus.gltf")), + )); + // light + commands.spawn(( + DirectionalLight::default(), + Transform::from_xyz(3.0, 3.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y), + )); + + let first_window_camera = commands + .spawn(( + Camera3d::default(), + Transform::from_xyz(0.0, 0.0, 6.0).looking_at(Vec3::ZERO, Vec3::Y), + )) + .id(); + + // Spawn a second window + let second_window = commands + .spawn(Window { + title: "Second window".to_owned(), + ..default() + }) + .id(); + + let second_window_camera = commands + .spawn(( + Camera3d::default(), + Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), + Camera { + target: RenderTarget::Window(WindowRef::Entity(second_window)), + ..default() + }, + )) + .id(); + + let example_nodes = [ + (1., BorderRadius::all(Val::Px(20.))), + (2., BorderRadius::MAX), + (3., BorderRadius::all(Val::Px(20.))), + (4., BorderRadius::MAX), + (5., BorderRadius::all(Val::Px(20.))), + ]; + + commands + .spawn(( + Node { + flex_direction: FlexDirection::RowReverse, + align_items: AlignItems::Center, + justify_content: JustifyContent::SpaceEvenly, + width: Val::Percent(100.), + height: Val::Percent(11.), + ..default() + }, + BackgroundColor(Color::srgb(0.25, 0.25, 0.25)), + // Since we are using multiple cameras, we need to specify which camera UI should be rendered to + UiTargetCamera(first_window_camera), + )) + .with_children(|commands| { + for (blur, border_radius) in example_nodes { + commands.spawn(box_shadow_node_bundle(blur, border_radius)); + } + }); + + let first_font_handle = asset_server.load("fonts/FiraSans-Bold.ttf"); + commands + .spawn(( + Node { + position_type: PositionType::Absolute, + bottom: Val::ZERO, + left: Val::Px(200.), + width: Val::Px(100.), + ..default() + }, + BackgroundColor(Color::from(DEEP_SKY_BLUE)), + UiTargetCamera(first_window_camera), + )) + .with_children(|command| { + command.spawn(( + Text::new("First window"), + TextFont { + font: first_font_handle.clone(), + font_size: 50.0, + ..default() + }, + TextColor(YELLOW.into()), + )); + }); + commands + .spawn(( + Node { + position_type: PositionType::Absolute, + bottom: Val::ZERO, + left: Val::Px(500.), + width: Val::Px(100.), + overflow: Overflow::hidden(), + ..default() + }, + BackgroundColor(Color::from(DEEP_SKY_BLUE)), + UiTargetCamera(first_window_camera), + )) + .with_children(|command| { + command.spawn(( + Text::new("xxxxxxx"), + TextFont { + font: first_font_handle.clone(), + font_size: 50.0, + ..default() + }, + TextColor(YELLOW.into()), + )); + }); + + commands + .spawn(( + Node { + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + justify_content: JustifyContent::SpaceEvenly, + width: Val::Percent(7.), + height: Val::Percent(100.), + ..default() + }, + BackgroundColor(Color::srgb(0.25, 0.25, 0.25)), + // Since we are using multiple cameras, we need to specify which camera UI should be rendered to + UiTargetCamera(second_window_camera), + )) + .with_children(|commands| { + for (blur, border_radius) in example_nodes { + commands.spawn(box_shadow_node_bundle(blur, border_radius)); + } + }); + + let second_font_handle = asset_server.load("fonts/FiraMono-Medium.ttf"); + commands + .spawn(( + Node { + position_type: PositionType::Absolute, + bottom: Val::ZERO, + left: Val::Px(200.), + width: Val::Px(100.), + ..default() + }, + BackgroundColor(Color::from(DEEP_SKY_BLUE)), + UiTargetCamera(second_window_camera), + )) + .with_children(|command| { + command.spawn(( + Text::new("Second window"), + TextFont { + font: second_font_handle.clone(), + font_size: 50.0, + ..default() + }, + TextColor(YELLOW.into()), + )); + }); + commands + .spawn(( + Node { + position_type: PositionType::Absolute, + bottom: Val::ZERO, + left: Val::Px(500.), + width: Val::Px(100.), + overflow: Overflow::hidden(), + ..default() + }, + BackgroundColor(Color::from(DEEP_SKY_BLUE)), + UiTargetCamera(second_window_camera), + )) + .with_children(|command| { + command.spawn(( + Text::new("xxxxxxx"), + TextFont { + font: second_font_handle.clone(), + font_size: 50.0, + ..default() + }, + TextColor(YELLOW.into()), + )); + }); +} + +fn box_shadow_node_bundle(blur: f32, border_radius: BorderRadius) -> impl Bundle { + ( + Node { + width: Val::Px(50.), + height: Val::Px(50.), + border: UiRect::all(Val::Px(4.)), + ..default() + }, + BorderColor(LIGHT_SKY_BLUE.into()), + border_radius, + BackgroundColor(DEEP_SKY_BLUE.into()), + BoxShadow::new( + Color::BLACK.with_alpha(0.8), + Val::Percent(10.), + Val::Percent(10.), + Val::Percent(10.), + Val::Px(blur), + ), + ) +}