Projection Improvements (#18458)

# Objective

- Remove a component impl footgun
- Make projection code slightly nicer, and remove the need to import the
projection trait when using the methods on `Projection`.

## Solution

- Do the things.
This commit is contained in:
Aevyrie 2025-06-23 20:26:38 -07:00 committed by GitHub
parent 82d62e606c
commit 3cefe82aff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 38 additions and 45 deletions

View File

@ -7,7 +7,7 @@ use bevy_ecs::{
use bevy_math::{ops, Mat4, Vec3A, Vec4}; use bevy_math::{ops, Mat4, Vec3A, Vec4};
use bevy_reflect::prelude::*; use bevy_reflect::prelude::*;
use bevy_render::{ use bevy_render::{
camera::{Camera, CameraProjection, Projection}, camera::{Camera, Projection},
extract_component::ExtractComponent, extract_component::ExtractComponent,
extract_resource::ExtractResource, extract_resource::ExtractResource,
mesh::Mesh3d, mesh::Mesh3d,

View File

@ -5,7 +5,7 @@
use super::{ClearColorConfig, Projection}; use super::{ClearColorConfig, Projection};
use crate::{ use crate::{
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
camera::{CameraProjection, ManualTextureViewHandle, ManualTextureViews}, camera::{ManualTextureViewHandle, ManualTextureViews},
primitives::Frustum, primitives::Frustum,
render_asset::RenderAssets, render_asset::RenderAssets,
render_graph::{InternedRenderSubGraph, RenderSubGraph}, render_graph::{InternedRenderSubGraph, RenderSubGraph},
@ -311,8 +311,8 @@ pub enum ViewportConversionError {
#[error("computed coordinate beyond `Camera`'s far plane")] #[error("computed coordinate beyond `Camera`'s far plane")]
PastFarPlane, PastFarPlane,
/// The Normalized Device Coordinates could not be computed because the `camera_transform`, the /// The Normalized Device Coordinates could not be computed because the `camera_transform`, the
/// `world_position`, or the projection matrix defined by [`CameraProjection`] contained `NAN` /// `world_position`, or the projection matrix defined by [`Projection`] contained `NAN` (see
/// (see [`world_to_ndc`][Camera::world_to_ndc] and [`ndc_to_world`][Camera::ndc_to_world]). /// [`world_to_ndc`][Camera::world_to_ndc] and [`ndc_to_world`][Camera::ndc_to_world]).
#[error("found NaN while computing NDC")] #[error("found NaN while computing NDC")]
InvalidData, InvalidData,
} }
@ -490,7 +490,7 @@ impl Camera {
.map(|t: &RenderTargetInfo| t.scale_factor) .map(|t: &RenderTargetInfo| t.scale_factor)
} }
/// The projection matrix computed using this camera's [`CameraProjection`]. /// The projection matrix computed using this camera's [`Projection`].
#[inline] #[inline]
pub fn clip_from_view(&self) -> Mat4 { pub fn clip_from_view(&self) -> Mat4 {
self.computed.clip_from_view self.computed.clip_from_view
@ -655,7 +655,7 @@ impl Camera {
/// To get the coordinates in the render target's viewport dimensions, you should use /// To get the coordinates in the render target's viewport dimensions, you should use
/// [`world_to_viewport`](Self::world_to_viewport). /// [`world_to_viewport`](Self::world_to_viewport).
/// ///
/// Returns `None` if the `camera_transform`, the `world_position`, or the projection matrix defined by [`CameraProjection`] contain `NAN`. /// Returns `None` if the `camera_transform`, the `world_position`, or the projection matrix defined by [`Projection`] contain `NAN`.
/// ///
/// # Panics /// # Panics
/// ///
@ -681,7 +681,7 @@ impl Camera {
/// To get the world space coordinates with the viewport position, you should use /// To get the world space coordinates with the viewport position, you should use
/// [`world_to_viewport`](Self::world_to_viewport). /// [`world_to_viewport`](Self::world_to_viewport).
/// ///
/// Returns `None` if the `camera_transform`, the `world_position`, or the projection matrix defined by [`CameraProjection`] contain `NAN`. /// Returns `None` if the `camera_transform`, the `world_position`, or the projection matrix defined by [`Projection`] contain `NAN`.
/// ///
/// # Panics /// # Panics
/// ///

View File

@ -1,9 +1,9 @@
use core::fmt::Debug; use core::fmt::Debug;
use core::ops::{Deref, DerefMut};
use crate::{primitives::Frustum, view::VisibilitySystems}; use crate::{primitives::Frustum, view::VisibilitySystems};
use bevy_app::{App, Plugin, PostStartup, PostUpdate}; use bevy_app::{App, Plugin, PostStartup, PostUpdate};
use bevy_asset::AssetEventSystems; use bevy_asset::AssetEventSystems;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::{ops, AspectRatio, Mat4, Rect, Vec2, Vec3A, Vec4}; use bevy_math::{ops, AspectRatio, Mat4, Rect, Vec2, Vec3A, Vec4};
use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize}; use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize};
@ -131,11 +131,10 @@ mod sealed {
/// custom projection. /// custom projection.
/// ///
/// The contained dynamic object can be downcast into a static type using [`CustomProjection::get`]. /// The contained dynamic object can be downcast into a static type using [`CustomProjection::get`].
#[derive(Component, Debug, Reflect, Deref, DerefMut)] #[derive(Debug, Reflect)]
#[reflect(Default, Clone)] #[reflect(Default, Clone)]
pub struct CustomProjection { pub struct CustomProjection {
#[reflect(ignore)] #[reflect(ignore)]
#[deref]
dyn_projection: Box<dyn sealed::DynCameraProjection>, dyn_projection: Box<dyn sealed::DynCameraProjection>,
} }
@ -204,6 +203,20 @@ impl CustomProjection {
} }
} }
impl Deref for CustomProjection {
type Target = dyn CameraProjection;
fn deref(&self) -> &Self::Target {
self.dyn_projection.as_ref()
}
}
impl DerefMut for CustomProjection {
fn deref_mut(&mut self) -> &mut Self::Target {
self.dyn_projection.as_mut()
}
}
/// Component that defines how to compute a [`Camera`]'s projection matrix. /// Component that defines how to compute a [`Camera`]'s projection matrix.
/// ///
/// Common projections, like perspective and orthographic, are provided out of the box to handle the /// Common projections, like perspective and orthographic, are provided out of the box to handle the
@ -240,7 +253,7 @@ impl Projection {
// that, say, the `Debug` implementation is missing. Wrapping these traits behind a super // that, say, the `Debug` implementation is missing. Wrapping these traits behind a super
// trait or some other indirection will make the errors harder to understand. // trait or some other indirection will make the errors harder to understand.
// //
// For example, we don't use the `DynCameraProjection`` trait bound, because it is not the // For example, we don't use the `DynCameraProjection` trait bound, because it is not the
// trait the user should be implementing - they only need to worry about implementing // trait the user should be implementing - they only need to worry about implementing
// `CameraProjection`. // `CameraProjection`.
P: CameraProjection + Debug + Send + Sync + Clone + 'static, P: CameraProjection + Debug + Send + Sync + Clone + 'static,
@ -251,44 +264,24 @@ impl Projection {
} }
} }
impl CameraProjection for Projection { impl Deref for Projection {
fn get_clip_from_view(&self) -> Mat4 { type Target = dyn CameraProjection;
fn deref(&self) -> &Self::Target {
match self { match self {
Projection::Perspective(projection) => projection.get_clip_from_view(), Projection::Perspective(projection) => projection,
Projection::Orthographic(projection) => projection.get_clip_from_view(), Projection::Orthographic(projection) => projection,
Projection::Custom(projection) => projection.get_clip_from_view(), Projection::Custom(projection) => projection.deref(),
} }
} }
}
fn get_clip_from_view_for_sub(&self, sub_view: &super::SubCameraView) -> Mat4 { impl DerefMut for Projection {
fn deref_mut(&mut self) -> &mut Self::Target {
match self { match self {
Projection::Perspective(projection) => projection.get_clip_from_view_for_sub(sub_view), Projection::Perspective(projection) => projection,
Projection::Orthographic(projection) => projection.get_clip_from_view_for_sub(sub_view), Projection::Orthographic(projection) => projection,
Projection::Custom(projection) => projection.get_clip_from_view_for_sub(sub_view), Projection::Custom(projection) => projection.deref_mut(),
}
}
fn update(&mut self, width: f32, height: f32) {
match self {
Projection::Perspective(projection) => projection.update(width, height),
Projection::Orthographic(projection) => projection.update(width, height),
Projection::Custom(projection) => projection.update(width, height),
}
}
fn far(&self) -> f32 {
match self {
Projection::Perspective(projection) => projection.far(),
Projection::Orthographic(projection) => projection.far(),
Projection::Custom(projection) => projection.far(),
}
}
fn get_frustum_corners(&self, z_near: f32, z_far: f32) -> [Vec3A; 8] {
match self {
Projection::Perspective(projection) => projection.get_frustum_corners(z_near, z_far),
Projection::Orthographic(projection) => projection.get_frustum_corners(z_near, z_far),
Projection::Custom(projection) => projection.get_frustum_corners(z_near, z_far),
} }
} }
} }

View File

@ -20,7 +20,7 @@ use smallvec::SmallVec;
use super::NoCpuCulling; use super::NoCpuCulling;
use crate::{ use crate::{
camera::{Camera, CameraProjection, Projection}, camera::{Camera, Projection},
mesh::{Mesh, Mesh3d, MeshAabb}, mesh::{Mesh, Mesh3d, MeshAabb},
primitives::{Aabb, Frustum, Sphere}, primitives::{Aabb, Frustum, Sphere},
sync_world::MainEntity, sync_world::MainEntity,