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_reflect::prelude::*;
use bevy_render::{
camera::{Camera, CameraProjection, Projection},
camera::{Camera, Projection},
extract_component::ExtractComponent,
extract_resource::ExtractResource,
mesh::Mesh3d,

View File

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

View File

@ -1,9 +1,9 @@
use core::fmt::Debug;
use core::ops::{Deref, DerefMut};
use crate::{primitives::Frustum, view::VisibilitySystems};
use bevy_app::{App, Plugin, PostStartup, PostUpdate};
use bevy_asset::AssetEventSystems;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*;
use bevy_math::{ops, AspectRatio, Mat4, Rect, Vec2, Vec3A, Vec4};
use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize};
@ -131,11 +131,10 @@ mod sealed {
/// custom projection.
///
/// 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)]
pub struct CustomProjection {
#[reflect(ignore)]
#[deref]
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.
///
/// 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
// 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
// `CameraProjection`.
P: CameraProjection + Debug + Send + Sync + Clone + 'static,
@ -251,44 +264,24 @@ impl Projection {
}
}
impl CameraProjection for Projection {
fn get_clip_from_view(&self) -> Mat4 {
match self {
Projection::Perspective(projection) => projection.get_clip_from_view(),
Projection::Orthographic(projection) => projection.get_clip_from_view(),
Projection::Custom(projection) => projection.get_clip_from_view(),
}
}
impl Deref for Projection {
type Target = dyn CameraProjection;
fn get_clip_from_view_for_sub(&self, sub_view: &super::SubCameraView) -> Mat4 {
fn deref(&self) -> &Self::Target {
match self {
Projection::Perspective(projection) => projection.get_clip_from_view_for_sub(sub_view),
Projection::Orthographic(projection) => projection.get_clip_from_view_for_sub(sub_view),
Projection::Custom(projection) => projection.get_clip_from_view_for_sub(sub_view),
Projection::Perspective(projection) => projection,
Projection::Orthographic(projection) => projection,
Projection::Custom(projection) => projection.deref(),
}
}
}
fn update(&mut self, width: f32, height: f32) {
impl DerefMut for Projection {
fn deref_mut(&mut self) -> &mut Self::Target {
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),
Projection::Perspective(projection) => projection,
Projection::Orthographic(projection) => projection,
Projection::Custom(projection) => projection.deref_mut(),
}
}
}

View File

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