bevy/examples/window/multiple_windows.rs
Rob Parrett 1c573de4f9 Fix panic in multiple_windows example (#1737)
Fixes #1736

There seemed to be an obvious transcription error where `setup_pipeline` was being called during `on_update` of `AppState::CreateWindow` instead of `AppState::Setup`.

However, the example still panicked after fixing that.

I'm not sure if there's some intended or unintended change in event processing during state transitions or something, but this PR make the example work again.

Cleaned up an unused `Stage` label while I was in there.

Please feel free to close this in favor of another approach.
2021-03-24 00:39:54 +00:00

212 lines
6.4 KiB
Rust

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},
};
/// 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_system_set(
SystemSet::on_update(AppState::CreateWindow).with_system(setup_window.system()),
)
.add_system_set(SystemSet::on_update(AppState::Setup).with_system(setup_pipeline.system()))
.run();
}
// 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)]
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_next(AppState::Setup).unwrap();
}
fn setup_pipeline(
mut commands: Commands,
windows: Res<Windows>,
mut active_cameras: ResMut<ActiveCameras>,
mut render_graph: ResMut<RenderGraph>,
asset_server: Res<AssetServer>,
msaa: Res<Msaa>,
mut app_state: ResMut<State<AppState>>,
) {
// get the non-default window id
let window_id = windows
.iter()
.find(|w| w.id() != WindowId::default())
.map(|w| w.id());
let window_id = match window_id {
Some(window_id) => window_id,
None => return,
};
// 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("secondary_camera", CameraNode::new("Secondary"));
// 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("Secondary");
active_cameras.add("Secondary");
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("secondary_camera", "second_window_pass")
.unwrap();
if msaa.samples > 1 {
render_graph.add_node(
"second_multi_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_multi_sampled_color_attachment",
WindowSwapChainNode::OUT_TEXTURE,
"second_window_pass",
"color_attachment",
)
.unwrap();
}
// SETUP SCENE
// add entities to the world
commands.spawn_scene(asset_server.load("models/monkey/Monkey.gltf#Scene0"));
// 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()
});
app_state.set_next(AppState::Done).unwrap();
}