diff --git a/src/camera.rs b/src/camera.rs index 8591497..44c79f7 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,6 +1,22 @@ //! This module contains everything to deal with cameras. use math::vector::Vector3; +use math::frustum::Frustum; + +/// Multiplies two matrices 4x4 +fn multiply_matrices(a: [[f32; 4]; 4], b: [[f32; 4]; 4]) -> [[f32; 4]; 4] { + let mut res = [[0.0; 4]; 4]; + + for i in 0..4 { + for j in 0..4 { + for k in 0..4 { + res[i][j] += a[i][k] + b[k][j]; + } + } + } + + res +} /// The trait that a render camera should implement. /// @@ -11,7 +27,17 @@ pub trait RenderCamera { fn get_view_matrix(&self) -> [[f32; 4]; 4]; /// Returns the perspective matrix of the camera. - fn get_perspective_matrix(&self) -> [[f32; 4]; 4] ; + fn get_perspective_matrix(&self) -> [[f32; 4]; 4]; + + /// Returns the product of the perspective matrix and the view matrix. + fn get_model_view_matrix(&self) -> [[f32; 4]; 4] { + multiply_matrices(self.get_perspective_matrix(), self.get_view_matrix()) + } + + /// Returns the frustum of the camera. + fn frustum(&self) -> Frustum { + Frustum::from_matrix(&self.get_model_view_matrix()) + } } diff --git a/src/controls.rs b/src/controls.rs index 5b6d000..4a8f67e 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -226,7 +226,7 @@ impl FirstPersonControls { } /// Updates the camera according to the state of the controls. - fn update_camera(&self, camera: &mut Camera) { + pub fn update_camera(&self, camera: &mut Camera) { camera.position = self.position; camera.target = self.position + self.forward; } diff --git a/src/math/frustum.rs b/src/math/frustum.rs new file mode 100644 index 0000000..baafc42 --- /dev/null +++ b/src/math/frustum.rs @@ -0,0 +1,67 @@ +//! Module containing the frustum struct. +//! +//! The frustum is the field of view of a camera. It allows you to make computation to know what is +//! visible or not in an efficient manner, though it doesn't take occlusions into account. + +use math::vector::Vector3; +use math::plane::Plane; +use math::bounding_box::BoundingBox3; + +/// Struct containing the 6 planes of a 3D camera. +#[derive(Copy, Clone)] +pub struct Frustum { + /// The planes of the frustum. + planes: [Plane; 6], +} + +impl Frustum { + /// Creates a frustum from the matrix of a camera. + /// + /// This is *ahem...* slightly inspired from THREE.js Frustum + pub fn from_matrix(m: &[[f32; 4]; 4]) -> Frustum { + + let m0 = m[0][0]; let m1 = m[0][1]; let m2 = m[0][2]; let m3 = m[0][3]; + let m4 = m[1][0]; let m5 = m[1][1]; let m6 = m[1][2]; let m7 = m[1][3]; + let m8 = m[2][0]; let m9 = m[2][1]; let m10 = m[2][2]; let m11 = m[2][3]; + let m12 = m[3][0]; let m13 = m[3][1]; let m14 = m[3][2]; let m15 = m[3][3]; + + Frustum { + planes: [ + Plane::from_coordinates(m3 - m0, m7 - m4, m11 - m8, m15 - m12), + Plane::from_coordinates(m3 + m0, m7 + m4, m11 + m8, m15 + m12), + Plane::from_coordinates(m3 + m1, m7 + m5, m11 + m9, m15 + m13), + Plane::from_coordinates(m3 - m1, m7 - m5, m11 - m9, m15 - m13), + Plane::from_coordinates(m3 - m2, m7 - m6, m11 - m10, m15 - m14), + Plane::from_coordinates(m3 + m2, m7 + m6, m11 + m10, m15 + m14), + ], + } + } + + /// Returns true if the intersection of the frustum and the bounding box is not empty. + pub fn intersects_box(&self, bbox: BoundingBox3) -> bool { + + use num::Zero; + + let mut p1 = Vector3::::zero(); + let mut p2 = Vector3::::zero(); + + for plane in &self.planes { + + p1[0] = if plane.normal().x() > 0.0 { bbox.min().x() } else { bbox.max().x() }; + p2[0] = if plane.normal().x() > 0.0 { bbox.max().x() } else { bbox.min().x() }; + p1[1] = if plane.normal().y() > 0.0 { bbox.min().y() } else { bbox.max().y() }; + p2[1] = if plane.normal().y() > 0.0 { bbox.max().y() } else { bbox.min().y() }; + p1[2] = if plane.normal().z() > 0.0 { bbox.min().z() } else { bbox.max().z() }; + p2[2] = if plane.normal().z() > 0.0 { bbox.max().z() } else { bbox.min().z() }; + + let d1 = plane.distance_to_point(p1); + let d2 = plane.distance_to_point(p2); + + if d1 < 0.0 && d2 < 2.0 { + return false; + } + } + + true + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs index f9057f8..4e21f6a 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -2,6 +2,8 @@ pub mod vector; pub mod bounding_box; +pub mod plane; +pub mod frustum; use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign}; use num::{Zero, One}; diff --git a/src/math/plane.rs b/src/math/plane.rs new file mode 100644 index 0000000..a0ae38c --- /dev/null +++ b/src/math/plane.rs @@ -0,0 +1,65 @@ +//! Module containing the plane struct. + +use math::vector::Vector3; + +/// A 3D plane. +#[derive(Copy, Clone)] +pub struct Plane { + /// The normal of the plane. + normal: Vector3, + + /// The constant, offset of the plane from the origin. + constant: f32, +} + +impl Plane { + /// Creates a new plane from its normal and its constant. + pub fn from_coordinates(a: f32, b: f32, c: f32, w: f32) -> Plane { + let mut p = Plane { + normal: Vector3::new(a, b, c), + constant: w, + }; + p.normalize(); + p + } + + /// Creates a new plane from its normal and its constant. + pub fn from_normal_and_constant(normal: Vector3, constant: f32) -> Plane { + Plane::from_coordinates(normal.x(), normal.y(), normal.z(), constant) + } + + /// Creates a new plane from its normal and a point of the plane. + pub fn from_normal_and_point(normal: Vector3, point: Vector3) -> Plane { + Plane::from_normal_and_constant(normal, - point.dot(normal)) + } + + + /// Creates a new plane from three points. + pub fn from_points(p1: Vector3, p2: Vector3, p3: Vector3) -> Plane { + let p1p2 = p2 - p1; + let p1p3 = p3 - p1; + Plane::from_normal_and_point(p1p2.cross_product(p1p3), p1) + } + + /// Normalizes the plane. + pub fn normalize(&mut self) { + let normal_inverse = 1.0 / self.normal.norm(); + self.normal *= normal_inverse; + self.constant *= normal_inverse; + } + + /// Returns the normal of the plane. + pub fn normal(&self) -> Vector3 { + self.normal + } + + /// Returns the constant of the plane. + pub fn constant(&self) -> f32 { + self.constant + } + + /// Returns the distance between the plane and the point. + pub fn distance_to_point(&self, point: Vector3) -> f32 { + self.normal.dot(point) + self.constant + } +} diff --git a/src/renderer.rs b/src/renderer.rs index b7fb9e4..c02128c 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -4,6 +4,8 @@ use std::cell::Ref; use std::borrow::Cow; use image; +use image::{ImageBuffer, Rgba}; + use glium::texture::{RawImage2d, SrgbTexture2d, Texture2dDataSink}; use glium::{Frame, Display, Surface, Program, DrawParameters, Depth, VertexBuffer}; use glium::{Rect, BlitTarget}; @@ -88,9 +90,14 @@ impl Renderer { /// Creates a SrgbTexture from a path to an image. pub fn make_texture(&self, path: &str) -> SrgbTexture2d { let image = image::open(path).unwrap().to_rgba(); - let dimensions = image.dimensions(); - let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dimensions); - SrgbTexture2d::new(&self.display, image).ok().unwrap() + self.make_texture_from_buffer(image) + } + + /// Creates a SrgbTexture from an image buffer. + pub fn make_texture_from_buffer(&self, buffer: ImageBuffer, Vec>) -> SrgbTexture2d { + let dimensions = buffer.dimensions(); + let buffer = RawImage2d::from_raw_rgba_reversed(&buffer.into_raw(), dimensions); + SrgbTexture2d::new(&self.display, buffer).ok().unwrap() } /// Creates a 1x1 SrgbTexture with the color passed as parameter. diff --git a/src/scene.rs b/src/scene.rs index abd1d65..2972ffe 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -2,13 +2,13 @@ use std::slice::{Iter, IterMut}; use std::vec::IntoIter; -use std::rc::Rc; +use std::sync::Arc; use std::cell::RefCell; use model::Model; /// Represents a 3D scene with models. pub struct Scene { - models: Vec>>, + models: Vec>>, } impl Scene { @@ -20,29 +20,29 @@ impl Scene { } /// Adds a model to the scene. - pub fn push(&mut self, model: Rc>) { + pub fn push(&mut self, model: Arc>) { self.models.push(model); } - /// Converts the model to a Rc> and adds it to the scene. + /// Converts the model to a Arc> and adds it to the scene. pub fn emplace(&mut self, model: Model) { - self.models.push(Rc::new(RefCell::new(model))); + self.models.push(Arc::new(RefCell::new(model))); } /// Returns an iterator to the models of the scene. - pub fn iter(&self) -> Iter>> { + pub fn iter(&self) -> Iter>> { self.models.iter() } /// Returns a mutable iterator to the model of the scene. - pub fn iter_mut(&mut self) -> IterMut>> { + pub fn iter_mut(&mut self) -> IterMut>> { self.models.iter_mut() } } impl IntoIterator for Scene { - type IntoIter = IntoIter>>; - type Item = Rc>; + type IntoIter = IntoIter>>; + type Item = Arc>; fn into_iter(self) -> Self::IntoIter { self.models.into_iter() @@ -52,7 +52,7 @@ impl IntoIterator for Scene { impl From> for Scene { fn from(input: Vec) -> Scene { Scene { - models: input.into_iter().map(|x| Rc::new(RefCell::new(x))).collect(), + models: input.into_iter().map(|x| Arc::new(RefCell::new(x))).collect(), } } }