use crate::ecs::world::{Entity, Mat4, Vec3, Vec4, World};
#[derive(Clone)]
pub struct CameraMatrices {
pub camera_position: Vec3,
pub projection: Mat4,
pub view: Mat4,
}
pub fn query_active_camera_matrices(world: &World) -> Option<CameraMatrices> {
#[cfg(feature = "openxr")]
if let Some(xr_override) = &world.resources.xr.camera_override {
return Some(xr_override.clone());
}
let active_camera = world.resources.active_camera?;
query_camera_matrices(world, active_camera)
}
pub fn query_camera_matrices(world: &World, entity: Entity) -> Option<CameraMatrices> {
let camera = world.core.get_camera(entity)?;
let global_transform = world.core.get_global_transform(entity)?;
let camera_position = global_transform.translation();
let forward = global_transform.forward_vector();
let up = global_transform.up_vector();
let target = camera_position + forward;
let projection = match &camera.projection {
crate::ecs::world::components::Projection::Perspective(persp) => {
let aspect_ratio = persp
.aspect_ratio
.or_else(|| query_window_aspect_ratio(world))
.unwrap_or(16.0 / 9.0);
persp.matrix_with_aspect(aspect_ratio)
}
crate::ecs::world::components::Projection::Orthographic(_) => camera.projection.matrix(),
};
let view = nalgebra_glm::look_at(&camera_position, &target, &up);
Some(CameraMatrices {
camera_position,
projection,
view,
})
}
pub fn query_window_aspect_ratio(world: &World) -> Option<f32> {
let (width, height) = world.resources.window.cached_viewport_size?;
let aspect_ratio = width as f32 / height.max(1) as f32;
Some(aspect_ratio)
}
#[derive(Clone)]
pub struct CameraFrustumCorners {
pub near_top_left: Vec3,
pub near_top_right: Vec3,
pub near_bottom_left: Vec3,
pub near_bottom_right: Vec3,
pub far_top_left: Vec3,
pub far_top_right: Vec3,
pub far_bottom_left: Vec3,
pub far_bottom_right: Vec3,
}
pub fn query_camera_frustum(world: &World, entity: Entity) -> Option<CameraFrustumCorners> {
let matrices = query_camera_matrices(world, entity)?;
let view_proj = matrices.projection * matrices.view;
let inv_view_proj = view_proj.try_inverse()?;
let near_z = 1.0_f32;
let far_z = 0.0_f32;
Some(CameraFrustumCorners {
near_top_left: unproject_ndc(&inv_view_proj, Vec3::new(-1.0, 1.0, near_z)),
near_top_right: unproject_ndc(&inv_view_proj, Vec3::new(1.0, 1.0, near_z)),
near_bottom_left: unproject_ndc(&inv_view_proj, Vec3::new(-1.0, -1.0, near_z)),
near_bottom_right: unproject_ndc(&inv_view_proj, Vec3::new(1.0, -1.0, near_z)),
far_top_left: unproject_ndc(&inv_view_proj, Vec3::new(-1.0, 1.0, far_z)),
far_top_right: unproject_ndc(&inv_view_proj, Vec3::new(1.0, 1.0, far_z)),
far_bottom_left: unproject_ndc(&inv_view_proj, Vec3::new(-1.0, -1.0, far_z)),
far_bottom_right: unproject_ndc(&inv_view_proj, Vec3::new(1.0, -1.0, far_z)),
})
}
fn unproject_ndc(inv_view_proj: &Mat4, ndc: Vec3) -> Vec3 {
let clip = inv_view_proj * Vec4::new(ndc.x, ndc.y, ndc.z, 1.0);
Vec3::new(clip.x / clip.w, clip.y / clip.w, clip.z / clip.w)
}