Skip to content

Commit

Permalink
Merge branch 'multiple-windows' into multiple-windows-support
Browse files Browse the repository at this point in the history
  • Loading branch information
vladbat00 committed Apr 10, 2021
2 parents 58e7c17 + 174662b commit 54b4924
Show file tree
Hide file tree
Showing 8 changed files with 580 additions and 173 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ fn main() {
}

fn ui_example(mut egui_context: ResMut<EguiContext>) {
let ctx = &mut egui_context.ctx;
egui::Window::new("Hello").show(ctx, |ui| {
egui::Window::new("Hello").show(egui_context.ctx(), |ui| {
ui.label("world");
});
}
Expand Down
5 changes: 2 additions & 3 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ fn main() {
.run();
}

fn ui_example(mut egui_context: ResMut<EguiContext>) {
let ctx = &mut egui_context.ctx;
egui::Window::new("Hello").show(ctx, |ui| {
fn ui_example(egui_context: Res<EguiContext>) {
egui::Window::new("Hello").show(egui_context.ctx(), |ui| {
ui.label("world");
});
}
270 changes: 270 additions & 0 deletions examples/two_windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
use bevy::{
prelude::*,
render::{
camera::{ActiveCameras, Camera},
pass::*,
render_graph::{
base::MainPass, CameraNode, PassNode, RenderGraph, WindowSwapChainNode,
WindowTextureNode,
},
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
},
window::{CreateWindow, WindowDescriptor, WindowId},
};
use bevy_egui::{egui, EguiContext, EguiPlugin};

/// This example creates a second window and draws a mesh from two different cameras.
fn main() {
App::build()
.insert_resource(Msaa { samples: 4 })
.add_state(AppState::CreateWindow)
.add_plugins(DefaultPlugins)
.add_plugin(EguiPlugin)
.add_system_set(
SystemSet::on_update(AppState::CreateWindow).with_system(setup_window.system()),
)
.add_system_set(SystemSet::on_update(AppState::Setup).with_system(setup.system()))
.add_system_set(SystemSet::on_update(AppState::Done).with_system(ui_second_window.system()))
.add_system(ui_first_window.system())
.run();
}

struct SecondWindow {
id: WindowId,
}

// NOTE: this "state based" approach to multiple windows is a short term workaround.
// Future Bevy releases shouldn't require such a strict order of operations.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
enum AppState {
CreateWindow,
Setup,
Done,
}

fn setup_window(
mut app_state: ResMut<State<AppState>>,
mut create_window_events: EventWriter<CreateWindow>,
) {
let window_id = WindowId::new();

// sends out a "CreateWindow" event, which will be received by the windowing backend
create_window_events.send(CreateWindow {
id: window_id,
descriptor: WindowDescriptor {
width: 800.,
height: 600.,
vsync: false,
title: "second window".to_string(),
..Default::default()
},
});

app_state.set(AppState::Setup).unwrap();
}

mod second_window {
pub const SWAP_CHAIN: &str = "second_window_swap_chain";
pub const DEPTH_TEXTURE: &str = "second_window_depth_texture";
pub const CAMERA_NODE: &str = "secondary_camera";
pub const CAMERA_NAME: &str = "Secondary";
pub const SAMPLED_COLOR_ATTACHMENT: &str = "second_multi_sampled_color_attachment";
pub const PASS: &str = "second_window_pass";
}

fn setup_pipeline(
render_graph: &mut RenderGraph,
active_cameras: &mut ActiveCameras,
msaa: &Msaa,
window_id: WindowId,
) {
// here we setup our render graph to draw our second camera to the new window's swap chain

// add a swapchain node for our new window
render_graph.add_node(
second_window::SWAP_CHAIN,
WindowSwapChainNode::new(window_id),
);

// add a new depth texture node for our new window
render_graph.add_node(
second_window::DEPTH_TEXTURE,
WindowTextureNode::new(
window_id,
TextureDescriptor {
format: TextureFormat::Depth32Float,
usage: TextureUsage::OUTPUT_ATTACHMENT,
sample_count: msaa.samples,
..Default::default()
},
),
);

// add a new camera node for our new window
render_graph.add_system_node(
second_window::CAMERA_NODE,
CameraNode::new(second_window::CAMERA_NAME),
);

// add a new render pass for our new window / camera
let mut second_window_pass = PassNode::<&MainPass>::new(PassDescriptor {
color_attachments: vec![msaa.color_attachment_descriptor(
TextureAttachment::Input("color_attachment".to_string()),
TextureAttachment::Input("color_resolve_target".to_string()),
Operations {
load: LoadOp::Clear(Color::rgb(0.5, 0.5, 0.8)),
store: true,
},
)],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor {
attachment: TextureAttachment::Input("depth".to_string()),
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
sample_count: msaa.samples,
});

second_window_pass.add_camera(second_window::CAMERA_NAME);
active_cameras.add(second_window::CAMERA_NAME);

render_graph.add_node(second_window::PASS, second_window_pass);

render_graph
.add_slot_edge(
second_window::SWAP_CHAIN,
WindowSwapChainNode::OUT_TEXTURE,
second_window::PASS,
if msaa.samples > 1 {
"color_resolve_target"
} else {
"color_attachment"
},
)
.unwrap();

render_graph
.add_slot_edge(
second_window::DEPTH_TEXTURE,
WindowTextureNode::OUT_TEXTURE,
second_window::PASS,
"depth",
)
.unwrap();

render_graph
.add_node_edge(second_window::CAMERA_NODE, second_window::PASS)
.unwrap();

if msaa.samples > 1 {
render_graph.add_node(
second_window::SAMPLED_COLOR_ATTACHMENT,
WindowTextureNode::new(
window_id,
TextureDescriptor {
size: Extent3d {
depth: 1,
width: 1,
height: 1,
},
mip_level_count: 1,
sample_count: msaa.samples,
dimension: TextureDimension::D2,
format: TextureFormat::default(),
usage: TextureUsage::OUTPUT_ATTACHMENT,
},
),
);

render_graph
.add_slot_edge(
second_window::SAMPLED_COLOR_ATTACHMENT,
WindowSwapChainNode::OUT_TEXTURE,
second_window::PASS,
"color_attachment",
)
.unwrap();
}

bevy_egui::setup_pipeline(
render_graph,
msaa,
bevy_egui::RenderGraphConfig {
window_id,
egui_pass: "egui_pass2",
main_pass: second_window::PASS,
swap_chain_node: second_window::SWAP_CHAIN,
depth_texture: second_window::DEPTH_TEXTURE,
sampled_color_attachment: second_window::SAMPLED_COLOR_ATTACHMENT,
transform_node: "egui_transform2",
},
);
}

fn setup(
mut commands: Commands,
mut app_state: ResMut<State<AppState>>,
windows: Res<Windows>,
mut active_cameras: ResMut<ActiveCameras>,
mut render_graph: ResMut<RenderGraph>,
mut meshes: ResMut<Assets<Mesh>>,
msaa: Res<Msaa>,
) {
// get the non-default window id
let window_id = match windows
.iter()
.find(|w| w.id() != WindowId::default())
.map(|w| w.id())
{
Some(x) => x,
None => return,
};

setup_pipeline(&mut render_graph, &mut active_cameras, &msaa, window_id);

// SETUP SCENE

// add entities to the world
commands.spawn_bundle(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
..Default::default()
});
// light
commands.spawn_bundle(LightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0),
..Default::default()
});
// main camera
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(0.0, 0.0, 6.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
// second window camera
commands.spawn_bundle(PerspectiveCameraBundle {
camera: Camera {
name: Some("Secondary".to_string()),
window: window_id,
..Default::default()
},
transform: Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});

commands.insert_resource(SecondWindow { id: window_id });

app_state.set(AppState::Done).unwrap();
}

fn ui_first_window(egui_context: Res<EguiContext>) {
egui::Window::new("First Window").show(egui_context.ctx(), |ui| {
ui.label("Some UI");
});
}

fn ui_second_window(egui_context: Res<EguiContext>, second_window: Res<SecondWindow>) {
egui::Window::new("Second Window").show(egui_context.ctx_for_window(second_window.id), |ui| {
ui.label("Some more UI");
});
}
22 changes: 11 additions & 11 deletions examples/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@ fn ui_example(
mut ui_state: ResMut<UiState>,
assets: Res<AssetServer>,
) {
let ctx = &mut egui_ctx.ctx;

let mut load = false;
let mut remove = false;
let mut invert = false;

egui::SidePanel::left("side_panel", 200.0).show(ctx, |ui| {
egui::SidePanel::left("side_panel", 200.0).show(egui_ctx.ctx(), |ui| {
ui.heading("Side Panel");

ui.horizontal(|ui| {
Expand Down Expand Up @@ -76,7 +74,7 @@ fn ui_example(
});
});

egui::TopPanel::top("top_panel").show(ctx, |ui| {
egui::TopPanel::top("top_panel").show(egui_ctx.ctx(), |ui| {
// The top panel is often a good place for a menu bar:
egui::menu::bar(ui, |ui| {
egui::menu::menu(ui, "File", |ui| {
Expand All @@ -87,7 +85,7 @@ fn ui_example(
});
});

egui::CentralPanel::default().show(ctx, |ui| {
egui::CentralPanel::default().show(egui_ctx.ctx(), |ui| {
ui.heading("Egui Template");
ui.hyperlink("https://github.com/emilk/egui_template");
ui.add(egui::github_link_file_line!(
Expand All @@ -109,12 +107,14 @@ fn ui_example(
});
});

egui::Window::new("Window").scroll(true).show(ctx, |ui| {
ui.label("Windows can be moved by dragging them.");
ui.label("They are automatically sized based on contents.");
ui.label("You can turn on resizing and scrolling if you like.");
ui.label("You would normally chose either panels OR windows.");
});
egui::Window::new("Window")
.scroll(true)
.show(egui_ctx.ctx(), |ui| {
ui.label("Windows can be moved by dragging them.");
ui.label("They are automatically sized based on contents.");
ui.label("You can turn on resizing and scrolling if you like.");
ui.label("You would normally chose either panels OR windows.");
});

if invert {
ui_state.inverted = !ui_state.inverted;
Expand Down
Loading

0 comments on commit 54b4924

Please sign in to comment.