use crate::core::algebra::{Matrix4, Vector2, Vector3, Vector4};
use crate::core::pool::Handle;
use crate::resource::texture::{TextureKind, TexturePixelKind};
use crate::scene::graph::Graph;
use crate::{
core::{
math::{ray::Ray, Rect},
visitor::{Visit, VisitResult, Visitor},
},
resource::texture::Texture,
scene::{
base::{Base, BaseBuilder},
node::Node,
VisibilityCache,
},
};
use rapier3d::na::Point3;
use std::ops::{Deref, DerefMut};
#[derive(Debug)]
pub struct Camera {
base: Base,
fov: f32,
z_near: f32,
z_far: f32,
viewport: Rect<f32>,
view_matrix: Matrix4<f32>,
projection_matrix: Matrix4<f32>,
enabled: bool,
skybox: Option<Box<SkyBox>>,
environment: Option<Texture>,
pub visibility_cache: VisibilityCache,
}
impl Deref for Camera {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for Camera {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl Default for Camera {
fn default() -> Self {
CameraBuilder::new(BaseBuilder::new()).build_camera()
}
}
impl Visit for Camera {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.fov.visit("Fov", visitor)?;
self.z_near.visit("ZNear", visitor)?;
self.z_far.visit("ZFar", visitor)?;
self.viewport.visit("Viewport", visitor)?;
self.base.visit("Base", visitor)?;
self.enabled.visit("Enabled", visitor)?;
self.skybox.visit("SkyBox", visitor)?;
self.environment.visit("Environment", visitor)?;
visitor.leave_region()
}
}
impl Camera {
#[inline]
pub fn calculate_matrices(&mut self, frame_size: Vector2<f32>) {
let pos = self.base.global_position();
let look = self.base.look_vector();
let up = self.base.up_vector();
self.view_matrix = Matrix4::look_at_rh(&Point3::from(pos), &Point3::from(pos + look), &up);
let viewport = self.viewport_pixels(frame_size);
let aspect = viewport.w() as f32 / viewport.h() as f32;
self.projection_matrix =
Matrix4::new_perspective(aspect, self.fov, self.z_near, self.z_far);
}
pub fn set_viewport(&mut self, viewport: Rect<f32>) -> &mut Self {
self.viewport = viewport;
self
}
#[inline]
pub fn viewport_pixels(&self, frame_size: Vector2<f32>) -> Rect<i32> {
Rect::new(
(self.viewport.x() * frame_size.x) as i32,
(self.viewport.y() * frame_size.y) as i32,
(self.viewport.w() * frame_size.x) as i32,
(self.viewport.h() * frame_size.y) as i32,
)
}
#[inline]
pub fn view_projection_matrix(&self) -> Matrix4<f32> {
self.projection_matrix * self.view_matrix
}
#[inline]
pub fn projection_matrix(&self) -> Matrix4<f32> {
self.projection_matrix
}
#[inline]
pub fn view_matrix(&self) -> Matrix4<f32> {
self.view_matrix
}
#[inline]
pub fn inv_view_matrix(&self) -> Option<Matrix4<f32>> {
self.view_matrix.try_inverse()
}
#[inline]
pub fn set_z_far(&mut self, z_far: f32) -> &mut Self {
self.z_far = z_far;
self
}
#[inline]
pub fn z_far(&self) -> f32 {
self.z_far
}
#[inline]
pub fn set_z_near(&mut self, z_near: f32) -> &mut Self {
self.z_near = z_near;
self
}
#[inline]
pub fn z_near(&self) -> f32 {
self.z_near
}
#[inline]
pub fn set_fov(&mut self, fov: f32) -> &mut Self {
self.fov = fov;
self
}
#[inline]
pub fn fov(&self) -> f32 {
self.fov
}
#[inline]
pub fn is_enabled(&self) -> bool {
self.enabled
}
#[inline]
pub fn set_enabled(&mut self, enabled: bool) -> &mut Self {
self.enabled = enabled;
self
}
pub fn set_skybox(&mut self, skybox: Option<SkyBox>) -> &mut Self {
self.skybox = skybox.map(Box::new);
self
}
pub fn skybox_mut(&mut self) -> Option<&mut SkyBox> {
self.skybox.as_deref_mut()
}
pub fn skybox_ref(&self) -> Option<&SkyBox> {
self.skybox.as_deref()
}
pub fn set_environment(&mut self, environment: Option<Texture>) -> &mut Self {
self.environment = environment;
self
}
pub fn environment_mut(&mut self) -> Option<&mut Texture> {
self.environment.as_mut()
}
pub fn environment_ref(&self) -> Option<&Texture> {
self.environment.as_ref()
}
pub fn environment_map(&self) -> Option<Texture> {
self.environment.clone()
}
pub fn make_ray(&self, screen_coord: Vector2<f32>, screen_size: Vector2<f32>) -> Ray {
let viewport = self.viewport_pixels(screen_size);
let nx = screen_coord.x / (viewport.w() as f32) * 2.0 - 1.0;
let ny = (viewport.h() as f32 - screen_coord.y) / (viewport.h() as f32) * 2.0 - 1.0;
let inv_view_proj = self
.view_projection_matrix()
.try_inverse()
.unwrap_or_default();
let near = inv_view_proj * Vector4::new(nx, ny, -1.0, 1.0);
let far = inv_view_proj * Vector4::new(nx, ny, 1.0, 1.0);
let begin = near.xyz().scale(1.0 / near.w);
let end = far.xyz().scale(1.0 / far.w);
Ray::from_two_points(begin, end)
}
pub fn project(
&self,
world_pos: Vector3<f32>,
screen_size: Vector2<f32>,
) -> Option<Vector2<f32>> {
let viewport = self.viewport_pixels(screen_size);
let proj = self.view_projection_matrix()
* Vector4::new(world_pos.x, world_pos.y, world_pos.z, 1.0);
if proj.w != 0.0 && proj.z >= 0.0 {
let k = (1.0 / proj.w) * 0.5;
Some(Vector2::new(
viewport.x() as f32 + viewport.w() as f32 * (proj.x * k + 0.5),
viewport.h() as f32
- (viewport.y() as f32 + viewport.h() as f32 * (proj.y * k + 0.5)),
))
} else {
None
}
}
pub fn raw_copy(&self) -> Self {
Self {
base: self.base.raw_copy(),
fov: self.fov,
z_near: self.z_near,
z_far: self.z_far,
viewport: self.viewport,
view_matrix: self.view_matrix,
projection_matrix: self.projection_matrix,
enabled: self.enabled,
skybox: self.skybox.clone(),
environment: self.environment.clone(),
visibility_cache: Default::default(),
}
}
}
pub struct CameraBuilder {
base_builder: BaseBuilder,
fov: f32,
z_near: f32,
z_far: f32,
viewport: Rect<f32>,
enabled: bool,
skybox: Option<SkyBox>,
environment: Option<Texture>,
}
impl CameraBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
enabled: true,
base_builder,
fov: 75.0f32.to_radians(),
z_near: 0.025,
z_far: 2048.0,
viewport: Rect::new(0.0, 0.0, 1.0, 1.0),
skybox: None,
environment: None,
}
}
pub fn with_fov(mut self, fov: f32) -> Self {
self.fov = fov;
self
}
pub fn with_z_near(mut self, z_near: f32) -> Self {
self.z_near = z_near;
self
}
pub fn with_z_far(mut self, z_far: f32) -> Self {
self.z_far = z_far;
self
}
pub fn with_viewport(mut self, viewport: Rect<f32>) -> Self {
self.viewport = viewport;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_skybox(mut self, skybox: SkyBox) -> Self {
self.skybox = Some(skybox);
self
}
pub fn with_environment(mut self, environment: Texture) -> Self {
self.environment = Some(environment);
self
}
pub fn build_camera(self) -> Camera {
Camera {
enabled: self.enabled,
base: self.base_builder.build_base(),
fov: self.fov,
z_near: self.z_near,
z_far: self.z_far,
viewport: self.viewport,
view_matrix: Matrix4::identity(),
projection_matrix: Matrix4::identity(),
visibility_cache: Default::default(),
skybox: self.skybox.map(Box::new),
environment: self.environment,
}
}
pub fn build_node(self) -> Node {
Node::Camera(self.build_camera())
}
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}
pub struct SkyBoxBuilder {
pub front: Option<Texture>,
pub back: Option<Texture>,
pub left: Option<Texture>,
pub right: Option<Texture>,
pub top: Option<Texture>,
pub bottom: Option<Texture>,
}
impl SkyBoxBuilder {
pub fn with_front(mut self, texture: Texture) -> Self {
self.front = Some(texture);
self
}
pub fn with_back(mut self, texture: Texture) -> Self {
self.back = Some(texture);
self
}
pub fn with_left(mut self, texture: Texture) -> Self {
self.left = Some(texture);
self
}
pub fn with_right(mut self, texture: Texture) -> Self {
self.right = Some(texture);
self
}
pub fn with_top(mut self, texture: Texture) -> Self {
self.top = Some(texture);
self
}
pub fn with_bottom(mut self, texture: Texture) -> Self {
self.bottom = Some(texture);
self
}
pub fn build(self) -> Result<SkyBox, SkyBoxError> {
let mut skybox = SkyBox {
left: self.left,
right: self.right,
top: self.top,
bottom: self.bottom,
front: self.front,
back: self.back,
cubemap: None,
};
skybox.create_cubemap()?;
Ok(skybox)
}
}
#[derive(Debug, Clone, Default)]
pub struct SkyBox {
pub(in crate) front: Option<Texture>,
pub(in crate) back: Option<Texture>,
pub(in crate) left: Option<Texture>,
pub(in crate) right: Option<Texture>,
pub(in crate) top: Option<Texture>,
pub(in crate) bottom: Option<Texture>,
pub(in crate) cubemap: Option<Texture>,
}
#[derive(Debug)]
pub enum SkyBoxError {
UnsupportedTextureKind(TextureKind),
}
impl SkyBox {
pub fn cubemap(&self) -> Option<Texture> {
self.cubemap.clone()
}
pub fn create_cubemap(&mut self) -> Result<(), SkyBoxError> {
let (kind, pixel_kind, bytes_per_face) =
self.textures().iter().find(|face| face.is_some()).map_or(
(
TextureKind::Rectangle {
width: 1,
height: 1,
},
TexturePixelKind::R8,
1,
),
|face| {
let face = face.clone().unwrap();
let data = face.data_ref();
(data.kind(), data.pixel_kind(), data.data().len())
},
);
let (width, height) = match kind {
TextureKind::Rectangle { width, height } => (width, height),
_ => return Err(SkyBoxError::UnsupportedTextureKind(kind)),
};
let mut data = Vec::<u8>::with_capacity(bytes_per_face * 6);
for face in self.textures().iter() {
if let Some(f) = face.clone() {
data.extend(f.data_ref().data());
} else {
let black_face_data = vec![0; bytes_per_face];
data.extend(black_face_data);
}
}
let cubemap =
Texture::from_bytes(TextureKind::Cube { width, height }, pixel_kind, data, false);
self.cubemap = cubemap;
Ok(())
}
pub(in crate) fn textures(&self) -> [Option<Texture>; 6] {
[
self.left.clone(),
self.right.clone(),
self.top.clone(),
self.bottom.clone(),
self.front.clone(),
self.back.clone(),
]
}
}
impl Visit for SkyBox {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.left.visit("Left", visitor)?;
self.right.visit("Right", visitor)?;
self.top.visit("Top", visitor)?;
self.bottom.visit("Bottom", visitor)?;
self.front.visit("Front", visitor)?;
self.back.visit("Back", visitor)?;
visitor.leave_region()
}
}