Starting to work on frustums

This commit is contained in:
Thomas Forgione 2018-06-12 17:22:16 +02:00
parent 66001b2f71
commit 4544acad88
No known key found for this signature in database
GPG Key ID: 203DAEA747F48F41
7 changed files with 182 additions and 15 deletions

View File

@ -1,6 +1,22 @@
//! This module contains everything to deal with cameras. //! This module contains everything to deal with cameras.
use math::vector::Vector3; 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. /// The trait that a render camera should implement.
/// ///
@ -11,7 +27,17 @@ pub trait RenderCamera {
fn get_view_matrix(&self) -> [[f32; 4]; 4]; fn get_view_matrix(&self) -> [[f32; 4]; 4];
/// Returns the perspective matrix of the camera. /// 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())
}
} }

View File

@ -226,7 +226,7 @@ impl FirstPersonControls {
} }
/// Updates the camera according to the state of the controls. /// 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.position = self.position;
camera.target = self.position + self.forward; camera.target = self.position + self.forward;
} }

67
src/math/frustum.rs Normal file
View File

@ -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<f32>) -> bool {
use num::Zero;
let mut p1 = Vector3::<f32>::zero();
let mut p2 = Vector3::<f32>::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
}
}

View File

@ -2,6 +2,8 @@
pub mod vector; pub mod vector;
pub mod bounding_box; pub mod bounding_box;
pub mod plane;
pub mod frustum;
use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign}; use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign};
use num::{Zero, One}; use num::{Zero, One};

65
src/math/plane.rs Normal file
View File

@ -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<f32>,
/// 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<f32>, 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<f32>, point: Vector3<f32>) -> Plane {
Plane::from_normal_and_constant(normal, - point.dot(normal))
}
/// Creates a new plane from three points.
pub fn from_points(p1: Vector3<f32>, p2: Vector3<f32>, p3: Vector3<f32>) -> 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<f32> {
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>) -> f32 {
self.normal.dot(point) + self.constant
}
}

View File

@ -4,6 +4,8 @@ use std::cell::Ref;
use std::borrow::Cow; use std::borrow::Cow;
use image; use image;
use image::{ImageBuffer, Rgba};
use glium::texture::{RawImage2d, SrgbTexture2d, Texture2dDataSink}; use glium::texture::{RawImage2d, SrgbTexture2d, Texture2dDataSink};
use glium::{Frame, Display, Surface, Program, DrawParameters, Depth, VertexBuffer}; use glium::{Frame, Display, Surface, Program, DrawParameters, Depth, VertexBuffer};
use glium::{Rect, BlitTarget}; use glium::{Rect, BlitTarget};
@ -88,9 +90,14 @@ impl Renderer {
/// Creates a SrgbTexture from a path to an image. /// Creates a SrgbTexture from a path to an image.
pub fn make_texture(&self, path: &str) -> SrgbTexture2d { pub fn make_texture(&self, path: &str) -> SrgbTexture2d {
let image = image::open(path).unwrap().to_rgba(); let image = image::open(path).unwrap().to_rgba();
let dimensions = image.dimensions(); self.make_texture_from_buffer(image)
let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dimensions); }
SrgbTexture2d::new(&self.display, image).ok().unwrap()
/// Creates a SrgbTexture from an image buffer.
pub fn make_texture_from_buffer(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>) -> 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. /// Creates a 1x1 SrgbTexture with the color passed as parameter.

View File

@ -2,13 +2,13 @@
use std::slice::{Iter, IterMut}; use std::slice::{Iter, IterMut};
use std::vec::IntoIter; use std::vec::IntoIter;
use std::rc::Rc; use std::sync::Arc;
use std::cell::RefCell; use std::cell::RefCell;
use model::Model; use model::Model;
/// Represents a 3D scene with models. /// Represents a 3D scene with models.
pub struct Scene { pub struct Scene {
models: Vec<Rc<RefCell<Model>>>, models: Vec<Arc<RefCell<Model>>>,
} }
impl Scene { impl Scene {
@ -20,29 +20,29 @@ impl Scene {
} }
/// Adds a model to the scene. /// Adds a model to the scene.
pub fn push(&mut self, model: Rc<RefCell<Model>>) { pub fn push(&mut self, model: Arc<RefCell<Model>>) {
self.models.push(model); self.models.push(model);
} }
/// Converts the model to a Rc<RefCell<Model>> and adds it to the scene. /// Converts the model to a Arc<RefCell<Model>> and adds it to the scene.
pub fn emplace(&mut self, model: Model) { 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. /// Returns an iterator to the models of the scene.
pub fn iter(&self) -> Iter<Rc<RefCell<Model>>> { pub fn iter(&self) -> Iter<Arc<RefCell<Model>>> {
self.models.iter() self.models.iter()
} }
/// Returns a mutable iterator to the model of the scene. /// Returns a mutable iterator to the model of the scene.
pub fn iter_mut(&mut self) -> IterMut<Rc<RefCell<Model>>> { pub fn iter_mut(&mut self) -> IterMut<Arc<RefCell<Model>>> {
self.models.iter_mut() self.models.iter_mut()
} }
} }
impl IntoIterator for Scene { impl IntoIterator for Scene {
type IntoIter = IntoIter<Rc<RefCell<Model>>>; type IntoIter = IntoIter<Arc<RefCell<Model>>>;
type Item = Rc<RefCell<Model>>; type Item = Arc<RefCell<Model>>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.models.into_iter() self.models.into_iter()
@ -52,7 +52,7 @@ impl IntoIterator for Scene {
impl From<Vec<Model>> for Scene { impl From<Vec<Model>> for Scene {
fn from(input: Vec<Model>) -> Scene { fn from(input: Vec<Model>) -> Scene {
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(),
} }
} }
} }