diff --git a/Cargo.toml b/Cargo.toml index 7d6c4f76f3..fa0cbd5349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,6 +121,10 @@ path = "examples/3d/load_gltf.rs" name = "msaa" path = "examples/3d/msaa.rs" +[[example]] +name = "orthographic" +path = "examples/3d/orthographic.rs" + [[example]] name = "parenting" path = "examples/3d/parenting.rs" diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 05b2dbc116..bebbb39c87 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -2,9 +2,10 @@ use super::CameraProjection; use bevy_app::prelude::EventReader; use bevy_ecs::{Added, Component, Entity, Query, QuerySet, Res}; use bevy_math::{Mat4, Vec2, Vec3}; -use bevy_reflect::{Reflect, ReflectComponent}; +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)] #[reflect(Component)] @@ -17,9 +18,12 @@ pub struct Camera { pub depth_calculation: DepthCalculation, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy, Reflect, Serialize, Deserialize)] +#[reflect_value(Serialize, Deserialize)] pub enum DepthCalculation { + /// Pythagorean distance; works everywhere, more expensive to compute. Distance, + /// Optimization for 2D; assuming the camera points towards -Z. ZDifference, } diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index 9f36e73ec2..51c5c3e535 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -77,6 +77,7 @@ pub struct OrthographicProjection { pub window_origin: WindowOrigin, pub scaling_mode: ScalingMode, pub scale: f32, + pub depth_calculation: DepthCalculation, } impl CameraProjection for OrthographicProjection { @@ -140,7 +141,7 @@ impl CameraProjection for OrthographicProjection { } fn depth_calculation(&self) -> DepthCalculation { - DepthCalculation::ZDifference + self.depth_calculation } } @@ -156,6 +157,7 @@ impl Default for OrthographicProjection { window_origin: WindowOrigin::Center, scaling_mode: ScalingMode::WindowSize, scale: 1.0, + depth_calculation: DepthCalculation::Distance, } } } diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 206c9e763c..fcbc6919d9 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -231,7 +231,7 @@ pub fn visible_entities_system( // smaller distances are sorted to lower indices by using the distance from the camera FloatOrd(match camera.depth_calculation { DepthCalculation::ZDifference => camera_position.z - position.z, - DepthCalculation::Distance => (camera_position - position).length(), + DepthCalculation::Distance => (camera_position - position).length_squared(), }) } else { let order = FloatOrd(no_transform_order); diff --git a/crates/bevy_render/src/entity.rs b/crates/bevy_render/src/entity.rs index 72ebe7d4d1..28344fd4bd 100644 --- a/crates/bevy_render/src/entity.rs +++ b/crates/bevy_render/src/entity.rs @@ -1,5 +1,8 @@ use crate::{ - camera::{Camera, OrthographicProjection, PerspectiveProjection, VisibleEntities}, + camera::{ + Camera, DepthCalculation, OrthographicProjection, PerspectiveProjection, ScalingMode, + VisibleEntities, + }, pipeline::RenderPipelines, prelude::Visible, render_graph::base, @@ -92,6 +95,7 @@ impl OrthographicCameraBundle { }, orthographic_projection: OrthographicProjection { far, + depth_calculation: DepthCalculation::ZDifference, ..Default::default() }, visible_entities: Default::default(), @@ -106,7 +110,11 @@ impl OrthographicCameraBundle { name: Some(base::camera::CAMERA_3D.to_string()), ..Default::default() }, - orthographic_projection: Default::default(), + orthographic_projection: OrthographicProjection { + scaling_mode: ScalingMode::FixedVertical, + depth_calculation: DepthCalculation::Distance, + ..Default::default() + }, visible_entities: Default::default(), transform: Default::default(), global_transform: Default::default(), diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index 28f0faf21f..7e31c3e2b6 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -7,7 +7,7 @@ use crate::{ use bevy_asset::Handle; use bevy_ecs::Bundle; use bevy_render::{ - camera::{Camera, OrthographicProjection, VisibleEntities, WindowOrigin}, + camera::{Camera, DepthCalculation, OrthographicProjection, VisibleEntities, WindowOrigin}, draw::Draw, mesh::Mesh, pipeline::{RenderPipeline, RenderPipelines}, @@ -185,6 +185,7 @@ impl Default for UiCameraBundle { orthographic_projection: OrthographicProjection { far, window_origin: WindowOrigin::BottomLeft, + depth_calculation: DepthCalculation::ZDifference, ..Default::default() }, visible_entities: Default::default(), diff --git a/examples/3d/orthographic.rs b/examples/3d/orthographic.rs new file mode 100644 index 0000000000..82c3ee09d3 --- /dev/null +++ b/examples/3d/orthographic.rs @@ -0,0 +1,62 @@ +use bevy::prelude::*; + +fn main() { + App::build() + .insert_resource(Msaa { samples: 4 }) + .add_plugins(DefaultPlugins) + .add_startup_system(setup.system()) + .run(); +} + +/// set up a simple 3D scene +fn setup( + commands: &mut Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // set up the camera + let mut camera = OrthographicCameraBundle::new_3d(); + camera.orthographic_projection.scale = 3.0; + camera.transform = Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::zero(), Vec3::unit_y()); + + // add entities to the world + commands + // plane + .spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + ..Default::default() + }) + // cubes + .spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(1.5, 0.5, 1.5), + ..Default::default() + }) + .spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(1.5, 0.5, -1.5), + ..Default::default() + }) + .spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(-1.5, 0.5, 1.5), + ..Default::default() + }) + .spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(-1.5, 0.5, -1.5), + ..Default::default() + }) + // light + .spawn(LightBundle { + transform: Transform::from_xyz(3.0, 8.0, 5.0), + ..Default::default() + }) + // camera + .spawn(camera); +} diff --git a/examples/README.md b/examples/README.md index ff3a47d28c..9ed43edc1b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -71,6 +71,7 @@ Example | File | Description `3d_scene` | [`3d/3d_scene.rs`](./3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting `load_gltf` | [`3d/load_gltf.rs`](./3d/load_gltf.rs) | Loads and renders a gltf file as a scene `msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges +`orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications) `parenting` | [`3d/parenting.rs`](./3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations `spawner` | [`3d/spawner.rs`](./3d/spawner.rs) | Renders a large number of cubes with changing position and material `texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials