Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental viewports #1389

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ce59eff
Add Viewport component to the camera bundles
rmsc Feb 6, 2021
8686fd1
Account for viewport scale factor in the render pass.
rmsc Feb 7, 2021
cef5e9f
Automatically resize viewports in relation to their parent surface
rmsc Feb 8, 2021
e8661e5
Rename viewports system, it does more than handle resizes.
rmsc Feb 8, 2021
1cfed6a
Make the location of the Viewport sides linearly independent.
rmsc Feb 8, 2021
321a334
Fix the 'multiple_windows' example after the changes to the Camera sy…
rmsc Feb 8, 2021
c0da856
Fix viewport size clamping, add support for a few more std::ops
rmsc Feb 10, 2021
65017aa
Make the viewport system iterate over viewports rather than windows.
rmsc Feb 11, 2021
2c4b1e4
Add viewport example.
rmsc Feb 11, 2021
510e4a0
Move surface and viewport related code to its own 'surface' module.
rmsc Feb 11, 2021
c21c606
Rename ViewportSideLocation to just SideLocation, fix viewport clamping
rmsc Feb 11, 2021
18d86ab
Make Viewport fields 'origin' and 'size' private
rmsc Feb 11, 2021
319c7c1
Show how to swap viewports in the 'viewports' example
rmsc Feb 11, 2021
54a1032
Update the examples README.md
rmsc Feb 12, 2021
89d90b0
Use HashSet instead of HashMap for storing changed window ids.
rmsc Feb 14, 2021
7fa410f
Chain camera-related systems
rmsc Feb 16, 2021
c0cc3ca
Use const strings as node/camera names.
rmsc Feb 17, 2021
e2ad8f7
Add support for viewport depth ranges.
rmsc Feb 17, 2021
d3f971c
Use RangeInclusive uniformly in the viewport depth range API
rmsc Feb 17, 2021
ef79790
Fix cargo fmt
rmsc Feb 17, 2021
afe0165
Panic when attempting to render with a Camera without Viewport.
rmsc Feb 17, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,10 @@ path = "examples/window/multiple_windows.rs"
name = "scale_factor_override"
path = "examples/window/scale_factor_override.rs"

[[example]]
name = "viewports"
path = "examples/window/viewports.rs"

[[example]]
name = "window_settings"
path = "examples/window/window_settings.rs"
Expand Down
58 changes: 11 additions & 47 deletions crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use super::CameraProjection;
use bevy_app::prelude::EventReader;
use bevy_ecs::{Added, Component, Entity, Query, QuerySet, Res};
use crate::surface::Viewport;
use bevy_ecs::{Changed, Component, Query};
use bevy_math::{Mat4, Vec2, Vec3};
use bevy_reflect::{Reflect, ReflectComponent, ReflectDeserialize};
use bevy_transform::components::GlobalTransform;
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
use serde::{Deserialize, Serialize};

#[derive(Default, Debug, Reflect)]
Expand All @@ -13,8 +12,6 @@ pub struct Camera {
pub projection_matrix: Mat4,
pub name: Option<String>,
#[reflect(ignore)]
pub window: WindowId,
#[reflect(ignore)]
pub depth_calculation: DepthCalculation,
}

Expand All @@ -37,12 +34,10 @@ impl Camera {
/// Given a position in world space, use the camera to compute the screen space coordinates.
pub fn world_to_screen(
&self,
windows: &Windows,
viewport: &Viewport,
camera_transform: &GlobalTransform,
world_position: Vec3,
) -> Option<Vec2> {
let window = windows.get(self.window)?;
let window_size = Vec2::new(window.width(), window.height());
// Build a transform to convert from world to NDC using camera data
let world_to_ndc: Mat4 =
self.projection_matrix * camera_transform.compute_matrix().inverse();
Expand All @@ -52,50 +47,19 @@ impl Camera {
return None;
}
// Once in NDC space, we can discard the z element and rescale x/y to fit the screen
let screen_space_coords = (ndc_space_coords.truncate() + Vec2::one()) / 2.0 * window_size;
let screen_space_coords =
viewport.origin() + (ndc_space_coords.truncate() + Vec2::one()) / 2.0 * viewport.size();
Some(screen_space_coords)
}
}

pub fn camera_system<T: CameraProjection + Component>(
mut window_resized_events: EventReader<WindowResized>,
mut window_created_events: EventReader<WindowCreated>,
windows: Res<Windows>,
mut queries: QuerySet<(
Query<(Entity, &mut Camera, &mut T)>,
Query<Entity, Added<Camera>>,
)>,
mut query: Query<(&mut Camera, &mut T, &Viewport), Changed<Viewport>>,
) {
let mut changed_window_ids = Vec::new();
// handle resize events. latest events are handled first because we only want to resize each window once
for event in window_resized_events.iter().rev() {
if changed_window_ids.contains(&event.id) {
continue;
}

changed_window_ids.push(event.id);
}

// handle resize events. latest events are handled first because we only want to resize each window once
for event in window_created_events.iter().rev() {
if changed_window_ids.contains(&event.id) {
continue;
}

changed_window_ids.push(event.id);
}

let mut added_cameras = vec![];
for entity in &mut queries.q1().iter() {
added_cameras.push(entity);
}
for (entity, mut camera, mut camera_projection) in queries.q0_mut().iter_mut() {
if let Some(window) = windows.get(camera.window) {
if changed_window_ids.contains(&window.id()) || added_cameras.contains(&entity) {
camera_projection.update(window.width(), window.height());
camera.projection_matrix = camera_projection.get_projection_matrix();
camera.depth_calculation = camera_projection.depth_calculation();
}
}
for (mut camera, mut camera_projection, viewport) in query.iter_mut() {
let size = viewport.size();
camera_projection.update(size.x, size.y);
camera.projection_matrix = camera_projection.get_projection_matrix();
camera.depth_calculation = camera_projection.depth_calculation();
}
}
8 changes: 8 additions & 0 deletions crates/bevy_render/src/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
pipeline::RenderPipelines,
prelude::Visible,
render_graph::base,
surface::Viewport,
Draw, Mesh,
};
use base::MainPass;
Expand All @@ -32,6 +33,7 @@ pub struct MeshBundle {
pub struct PerspectiveCameraBundle {
pub camera: Camera,
pub perspective_projection: PerspectiveProjection,
pub viewport: Viewport,
pub visible_entities: VisibleEntities,
pub transform: Transform,
pub global_transform: GlobalTransform,
Expand All @@ -49,6 +51,7 @@ impl PerspectiveCameraBundle {
..Default::default()
},
perspective_projection: Default::default(),
viewport: Default::default(),
visible_entities: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
Expand All @@ -64,6 +67,7 @@ impl Default for PerspectiveCameraBundle {
..Default::default()
},
perspective_projection: Default::default(),
viewport: Default::default(),
visible_entities: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
Expand All @@ -78,6 +82,7 @@ impl Default for PerspectiveCameraBundle {
pub struct OrthographicCameraBundle {
pub camera: Camera,
pub orthographic_projection: OrthographicProjection,
pub viewport: Viewport,
pub visible_entities: VisibleEntities,
pub transform: Transform,
pub global_transform: GlobalTransform,
Expand All @@ -98,6 +103,7 @@ impl OrthographicCameraBundle {
depth_calculation: DepthCalculation::ZDifference,
..Default::default()
},
viewport: Default::default(),
visible_entities: Default::default(),
transform: Transform::from_xyz(0.0, 0.0, far - 0.1),
global_transform: Default::default(),
Expand All @@ -115,6 +121,7 @@ impl OrthographicCameraBundle {
depth_calculation: DepthCalculation::Distance,
..Default::default()
},
viewport: Default::default(),
visible_entities: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
Expand All @@ -128,6 +135,7 @@ impl OrthographicCameraBundle {
..Default::default()
},
orthographic_projection: Default::default(),
viewport: Default::default(),
visible_entities: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
Expand Down
24 changes: 9 additions & 15 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ pub mod pipeline;
pub mod render_graph;
pub mod renderer;
pub mod shader;
pub mod surface;
pub mod texture;
pub mod wireframe;

use bevy_ecs::{IntoExclusiveSystem, IntoSystem, SystemStage};
use bevy_ecs::{IntoChainSystem, IntoExclusiveSystem, IntoSystem, SystemStage};
use bevy_reflect::RegisterTypeBuilder;
use draw::Visible;
pub use once_cell;
Expand Down Expand Up @@ -149,20 +150,13 @@ impl Plugin for RenderPlugin {
.add_system_to_stage(CoreStage::PreUpdate, draw::clear_draw_system.system())
.add_system_to_stage(
CoreStage::PostUpdate,
camera::active_cameras_system.system(),
)
.add_system_to_stage(
CoreStage::PostUpdate,
camera::camera_system::<OrthographicProjection>.system(),
)
.add_system_to_stage(
CoreStage::PostUpdate,
camera::camera_system::<PerspectiveProjection>.system(),
)
// registration order matters here. this must come after all camera_system::<T> systems
.add_system_to_stage(
CoreStage::PostUpdate,
camera::visible_entities_system.system(),
surface::viewport_system
.system()
.chain(camera::active_cameras_system.system())
.chain(camera::camera_system::<OrthographicProjection>.system())
.chain(camera::camera_system::<PerspectiveProjection>.system())
// registration order matters here. this must come after all camera_system::<T> systems
.chain(camera::visible_entities_system.system()),
)
.add_system_to_stage(
RenderStage::RenderResource,
Expand Down
17 changes: 14 additions & 3 deletions crates/bevy_render/src/render_graph/nodes/pass_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
renderer::{
BindGroup, BindGroupId, BufferId, RenderContext, RenderResourceBindings, RenderResourceType,
},
surface::Viewport,
};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{ReadOnlyFetch, Resources, World, WorldQuery};
Expand Down Expand Up @@ -216,13 +217,23 @@ where
continue;
};

// get an ordered list of entities visible to the camera
let visible_entities = if let Some(camera_entity) = active_cameras.get(&camera_info.name) {
world.get::<VisibleEntities>(camera_entity).unwrap()
let camera_entity = if let Some(camera_entity) = active_cameras.get(&camera_info.name) {
camera_entity
} else {
continue;
};

// get camera viewport and apply it
let viewport = world.get::<Viewport>(camera_entity)
.expect("A camera requires a Viewport component.");
let origin = viewport.physical_origin();
let size = viewport.physical_size();
let (min_depth, max_depth) = viewport.depth_range().into_inner();
render_pass.set_viewport(origin.x, origin.y, size.x, size.y, min_depth, max_depth);

// get an ordered list of entities visible to the camera
let visible_entities = world.get::<VisibleEntities>(camera_entity).unwrap();

// attempt to draw each visible entity
let mut draw_state = DrawState::default();
for visible_entity in visible_entities.iter() {
Expand Down
48 changes: 48 additions & 0 deletions crates/bevy_render/src/surface/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
mod viewport;

pub use viewport::*;

use crate::renderer::TextureId;
use bevy_window::WindowId;

#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum SurfaceId {
Window(WindowId),
Texture(TextureId),
Copy link
Contributor Author

@rmsc rmsc Feb 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm implementing an offscreeen rendering plugin on top of this, and found the Texture variant a bit awkward to use.

Perhaps I should be using a Handle<Texture> here instead?

}

impl SurfaceId {
pub fn get_window(&self) -> Option<WindowId> {
if let SurfaceId::Window(id) = self {
Some(*id)
} else {
None
}
}

pub fn get_texture(&self) -> Option<TextureId> {
if let SurfaceId::Texture(id) = self {
Some(*id)
} else {
None
}
}
}

impl Default for SurfaceId {
fn default() -> Self {
WindowId::primary().into()
}
}

impl From<WindowId> for SurfaceId {
fn from(value: WindowId) -> Self {
SurfaceId::Window(value)
}
}

impl From<TextureId> for SurfaceId {
fn from(value: TextureId) -> Self {
SurfaceId::Texture(value)
}
}
Loading