2018-04-10 15:25:01 +02:00
|
|
|
//! This module contains everything related to rendering.
|
|
|
|
|
2018-02-27 11:42:35 +01:00
|
|
|
pub mod camera;
|
2018-04-10 15:13:35 +02:00
|
|
|
pub mod controls;
|
2018-02-27 11:42:35 +01:00
|
|
|
|
2018-02-26 14:59:32 +01:00
|
|
|
use glium::draw_parameters::DepthTest;
|
|
|
|
use glium::texture::{
|
|
|
|
RawImage2d,
|
2018-02-27 11:42:35 +01:00
|
|
|
SrgbTexture2d,
|
2018-02-26 14:59:32 +01:00
|
|
|
};
|
2018-02-26 11:45:53 +01:00
|
|
|
use glium::index::{
|
|
|
|
NoIndices,
|
|
|
|
PrimitiveType
|
|
|
|
};
|
|
|
|
use glium::{
|
|
|
|
Program,
|
|
|
|
Display,
|
|
|
|
VertexBuffer,
|
|
|
|
Frame,
|
2018-02-26 14:59:32 +01:00
|
|
|
DrawParameters,
|
|
|
|
Depth,
|
2018-02-26 11:45:53 +01:00
|
|
|
};
|
2018-04-11 14:13:49 +02:00
|
|
|
|
|
|
|
use glium::backend::Facade;
|
|
|
|
use glium::backend::glutin::headless::Headless;
|
|
|
|
|
2018-02-26 14:59:32 +01:00
|
|
|
use image;
|
2018-02-26 11:45:53 +01:00
|
|
|
|
|
|
|
use model::{Model, Vertex};
|
2018-04-10 15:13:35 +02:00
|
|
|
use renderer::camera::RenderCamera;
|
2018-02-26 14:59:32 +01:00
|
|
|
|
2018-04-11 14:13:49 +02:00
|
|
|
/// Anything on which you can call draw to get a frame.
|
|
|
|
pub trait Drawer {
|
|
|
|
/// Get a frame from the drawer.
|
|
|
|
fn draw(&self) -> Frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drawer for Display {
|
|
|
|
fn draw(&self) -> Frame {
|
|
|
|
Display::draw(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drawer for Headless {
|
|
|
|
fn draw(&self) -> Frame {
|
|
|
|
Headless::draw(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-10 15:25:01 +02:00
|
|
|
/// A material that can be bound to OpenGL.
|
|
|
|
///
|
|
|
|
/// Only textures are supported for now.
|
2018-02-26 14:59:32 +01:00
|
|
|
pub struct RenderMaterial {
|
2018-02-27 11:42:35 +01:00
|
|
|
texture: Option<SrgbTexture2d>,
|
2018-02-26 14:59:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderMaterial {
|
|
|
|
|
2018-04-10 15:25:01 +02:00
|
|
|
/// Creates a new material with no texture.
|
2018-02-26 14:59:32 +01:00
|
|
|
pub fn new() -> RenderMaterial {
|
|
|
|
RenderMaterial {
|
|
|
|
texture: None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-10 15:25:01 +02:00
|
|
|
/// Creates a material from a path to an image file.
|
2018-04-11 14:13:49 +02:00
|
|
|
pub fn from_texture_path<D: Drawer + Facade + Sized>(path: &str, drawer: &D) -> RenderMaterial {
|
2018-02-26 14:59:32 +01:00
|
|
|
let image = image::open(path);
|
|
|
|
|
|
|
|
if let Ok(image) = image {
|
|
|
|
let image = image.to_rgba();
|
|
|
|
let dim = image.dimensions();
|
|
|
|
let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dim);
|
|
|
|
RenderMaterial {
|
2018-04-11 14:13:49 +02:00
|
|
|
texture: SrgbTexture2d::new(drawer, image).ok()
|
2018-02-26 14:59:32 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RenderMaterial {
|
|
|
|
texture: None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-10 15:25:01 +02:00
|
|
|
/// A renderer. It contains a display, shaders, and a vector of models it can render.
|
2018-04-11 14:13:49 +02:00
|
|
|
pub struct Renderer<'a, D: Drawer + Facade> {
|
|
|
|
drawer: D,
|
2018-02-26 11:45:53 +01:00
|
|
|
program: Program,
|
2018-02-26 14:59:32 +01:00
|
|
|
models: Vec<(&'a Model, Vec<(RenderMaterial, VertexBuffer<Vertex>)>)>,
|
2018-02-26 11:45:53 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 14:13:49 +02:00
|
|
|
impl<'a, D: Drawer + Facade + Sized> Renderer<'a, D> {
|
2018-04-10 15:25:01 +02:00
|
|
|
/// Creates a new renderer from a display.
|
|
|
|
///
|
|
|
|
/// Is uses the default shaders and creates an empty vec of models.
|
2018-04-11 14:13:49 +02:00
|
|
|
pub fn new(drawer: D) -> Renderer<'a, D> {
|
2018-02-26 11:45:53 +01:00
|
|
|
|
|
|
|
let program = Program::from_source(
|
2018-04-11 14:13:49 +02:00
|
|
|
&drawer,
|
2018-02-27 11:42:35 +01:00
|
|
|
include_str!("../../assets/shaders/shader.vert"),
|
|
|
|
include_str!("../../assets/shaders/shader.frag"),
|
2018-02-26 11:45:53 +01:00
|
|
|
None
|
2018-04-11 14:13:49 +02:00
|
|
|
);
|
2018-02-26 11:45:53 +01:00
|
|
|
|
2018-04-11 14:13:49 +02:00
|
|
|
if let Ok(program) = program {
|
|
|
|
Renderer {
|
|
|
|
drawer: drawer,
|
|
|
|
program: program,
|
|
|
|
models: vec![],
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
println!("{:?}", program.err().unwrap());
|
|
|
|
panic!()
|
2018-02-26 11:45:53 +01:00
|
|
|
}
|
2018-04-11 14:13:49 +02:00
|
|
|
|
|
|
|
|
2018-02-26 11:45:53 +01:00
|
|
|
}
|
|
|
|
|
2018-04-10 15:25:01 +02:00
|
|
|
/// Adds a model to the renderer, and compute the corresponding buffers for rendering.
|
2018-02-26 11:45:53 +01:00
|
|
|
pub fn add_model(&mut self, model: &'a Model) {
|
|
|
|
let mut buffers = vec![];
|
|
|
|
|
|
|
|
for part in &model.parts {
|
2018-02-26 14:59:32 +01:00
|
|
|
|
|
|
|
let material = if let Some(ref material_name) = part.material_name {
|
|
|
|
if let Some(material) = model.materials.get(material_name) {
|
|
|
|
if let Some(path) = material.textures.get("map_Kd") {
|
2018-04-11 14:13:49 +02:00
|
|
|
RenderMaterial::from_texture_path(path, &self.drawer)
|
2018-02-26 14:59:32 +01:00
|
|
|
} else {
|
|
|
|
RenderMaterial::new()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RenderMaterial::new()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RenderMaterial::new()
|
|
|
|
};
|
|
|
|
|
2018-02-26 11:45:53 +01:00
|
|
|
let shape = part.build_shape(&model.vertices, &model.texture_coordinates, &model.normals);
|
2018-04-11 14:13:49 +02:00
|
|
|
buffers.push((material, VertexBuffer::new(&self.drawer, &shape).unwrap()));
|
2018-02-26 11:45:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.models.push((model, buffers));
|
|
|
|
}
|
|
|
|
|
2018-04-11 14:13:49 +02:00
|
|
|
/// Returns the reference to the drawer used for this renderer.
|
|
|
|
pub fn facade(&self) -> &D {
|
|
|
|
&self.drawer
|
|
|
|
}
|
|
|
|
|
2018-04-10 15:25:01 +02:00
|
|
|
/// Creates a frame from the display.
|
2018-02-26 11:45:53 +01:00
|
|
|
pub fn draw(&self) -> Frame {
|
2018-04-11 14:13:49 +02:00
|
|
|
self.drawer.draw()
|
2018-02-26 11:45:53 +01:00
|
|
|
}
|
|
|
|
|
2018-04-10 15:25:01 +02:00
|
|
|
/// Renders the result of the models from the camera and paint it on the target.
|
2018-04-10 15:13:35 +02:00
|
|
|
pub fn render<C: RenderCamera>(&self, camera: &C, target: &mut Frame) {
|
2018-02-26 11:45:53 +01:00
|
|
|
use glium::Surface;
|
2018-02-27 11:42:35 +01:00
|
|
|
target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
|
2018-02-26 14:59:32 +01:00
|
|
|
|
|
|
|
let params = DrawParameters {
|
|
|
|
depth: Depth {
|
|
|
|
test: DepthTest::IfLess,
|
|
|
|
write: true,
|
|
|
|
.. Default::default()
|
|
|
|
},
|
|
|
|
.. Default::default()
|
|
|
|
};
|
2018-02-26 11:45:53 +01:00
|
|
|
|
|
|
|
for &(_, ref buffers) in &self.models {
|
2018-04-10 15:13:35 +02:00
|
|
|
|
2018-02-26 14:59:32 +01:00
|
|
|
for &(ref material, ref buffer) in buffers {
|
|
|
|
|
2018-04-11 17:03:32 +02:00
|
|
|
let perspective = camera.get_perspective_matrix();
|
2018-02-26 14:59:32 +01:00
|
|
|
let view = camera.get_view_matrix();
|
|
|
|
|
|
|
|
if let &Some(ref texture) = &material.texture {
|
|
|
|
target.draw(
|
|
|
|
buffer,
|
|
|
|
NoIndices(PrimitiveType::TrianglesList),
|
|
|
|
&self.program,
|
|
|
|
&uniform!(
|
|
|
|
tex: texture,
|
|
|
|
perspective: perspective,
|
|
|
|
view: view,
|
|
|
|
),
|
|
|
|
¶ms,
|
|
|
|
).unwrap();
|
|
|
|
} else {
|
|
|
|
target.draw(
|
|
|
|
buffer,
|
|
|
|
NoIndices(PrimitiveType::TrianglesList),
|
|
|
|
&self.program,
|
|
|
|
&uniform!(
|
|
|
|
perspective: perspective,
|
|
|
|
view: view,
|
|
|
|
),
|
|
|
|
¶ms,
|
|
|
|
).unwrap();
|
|
|
|
};
|
2018-02-26 11:45:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-04-11 14:13:49 +02:00
|
|
|
|
2018-02-26 11:45:53 +01:00
|
|
|
}
|
|
|
|
}
|