2018-04-17 10:45:44 +02:00
|
|
|
//! This module contains the rendering structs.
|
|
|
|
|
|
|
|
use std::cell::Ref;
|
2018-04-17 14:21:35 +02:00
|
|
|
use std::borrow::Cow;
|
2018-04-17 10:45:44 +02:00
|
|
|
|
|
|
|
use image;
|
2018-06-12 17:22:16 +02:00
|
|
|
use image::{ImageBuffer, Rgba};
|
|
|
|
|
2018-04-17 14:21:35 +02:00
|
|
|
use glium::texture::{RawImage2d, SrgbTexture2d, Texture2dDataSink};
|
2018-04-17 10:45:44 +02:00
|
|
|
use glium::{Frame, Display, Surface, Program, DrawParameters, Depth, VertexBuffer};
|
2018-04-17 14:21:35 +02:00
|
|
|
use glium::{Rect, BlitTarget};
|
|
|
|
use glium::framebuffer::SimpleFrameBuffer;
|
|
|
|
use glium::uniforms::MagnifySamplerFilter;
|
2018-04-17 10:45:44 +02:00
|
|
|
use glium::draw_parameters::{DepthTest, Blend};
|
|
|
|
use glium::index::{NoIndices, PrimitiveType};
|
|
|
|
use glium::glutin::GlWindow;
|
|
|
|
|
|
|
|
use scene::Scene;
|
|
|
|
use camera::RenderCamera;
|
|
|
|
|
|
|
|
use model::{Vertex, Part, Model};
|
2018-06-15 17:36:51 +02:00
|
|
|
use math::vector::Vector3;
|
2018-04-17 10:45:44 +02:00
|
|
|
|
2018-04-17 14:21:35 +02:00
|
|
|
/// Image data stored as RGBA.
|
|
|
|
pub struct RgbaImageData {
|
|
|
|
/// The array of colors.
|
|
|
|
pub data: Vec<(u8, u8, u8, u8)>,
|
|
|
|
|
|
|
|
/// The width of the image.
|
|
|
|
pub width: u32,
|
|
|
|
|
|
|
|
/// The height of the image.
|
|
|
|
pub height: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Texture2dDataSink<(u8, u8, u8, u8)> for RgbaImageData {
|
|
|
|
fn from_raw(data: Cow<[(u8, u8, u8, u8)]>, width: u32, height: u32) -> Self {
|
|
|
|
RgbaImageData {
|
|
|
|
data: data.into_owned(),
|
|
|
|
width: width,
|
|
|
|
height: height,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-17 10:45:44 +02:00
|
|
|
/// A renderer. It contains a display, shaders, and some rendering parameters.
|
|
|
|
pub struct Renderer {
|
|
|
|
/// The display on which the rendering will be done.
|
|
|
|
display: Display,
|
|
|
|
|
|
|
|
/// The shading program.
|
|
|
|
program: Program,
|
|
|
|
|
|
|
|
/// The background color.
|
|
|
|
clear_color: (f32, f32, f32, f32),
|
2018-04-24 11:57:25 +02:00
|
|
|
|
|
|
|
/// The texture that will be applied to non textured models.
|
|
|
|
default_texture: SrgbTexture2d,
|
2018-04-17 10:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Renderer {
|
|
|
|
/// Creates a new renderer from a display.
|
|
|
|
///
|
|
|
|
/// Is uses the default shaders and creates an empty vec of models.
|
|
|
|
pub fn new(display: Display) -> Renderer {
|
|
|
|
|
|
|
|
let program = Program::from_source(
|
|
|
|
&display,
|
|
|
|
include_str!("../assets/shaders/shader.vert"),
|
|
|
|
include_str!("../assets/shaders/shader.frag"),
|
|
|
|
None
|
|
|
|
).unwrap();
|
|
|
|
|
2018-04-24 11:57:25 +02:00
|
|
|
let image = RawImage2d::from_raw_rgba(vec![1.0, 1.0, 1.0, 1.0], (1, 1));
|
|
|
|
let texture = SrgbTexture2d::new(&display, image).ok().unwrap();
|
|
|
|
|
2018-04-17 10:45:44 +02:00
|
|
|
Renderer {
|
|
|
|
display: display,
|
|
|
|
program: program,
|
|
|
|
clear_color: (0.0, 0.0, 0.0, 1.0),
|
2018-04-24 11:57:25 +02:00
|
|
|
default_texture: texture,
|
2018-04-17 10:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the inner GlWindows.
|
|
|
|
pub fn gl_window(&self) -> Ref<GlWindow> {
|
|
|
|
self.display.gl_window()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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();
|
2018-06-12 17:22:16 +02:00
|
|
|
self.make_texture_from_buffer(image)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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()
|
2018-04-17 10:45:44 +02:00
|
|
|
}
|
|
|
|
|
2018-04-17 14:21:35 +02:00
|
|
|
/// Creates a 1x1 SrgbTexture with the color passed as parameter.
|
2018-04-24 11:08:32 +02:00
|
|
|
pub fn make_texture_from_color_channels(&self, r: f32, g: f32, b: f32, a: f32) -> SrgbTexture2d {
|
2018-04-17 14:21:35 +02:00
|
|
|
let image = RawImage2d::from_raw_rgba(vec![r, g, b, a], (1, 1));
|
|
|
|
SrgbTexture2d::new(&self.display, image).ok().unwrap()
|
|
|
|
}
|
|
|
|
|
2018-04-24 11:08:32 +02:00
|
|
|
/// Creates a 1x1 SrgbTexture with the color passed as parameter.
|
|
|
|
pub fn make_texture_from_color(&self, c: (f32, f32, f32, f32)) -> SrgbTexture2d {
|
|
|
|
let image = RawImage2d::from_raw_rgba(vec![c.0, c.1, c.2, c.3], (1, 1));
|
|
|
|
SrgbTexture2d::new(&self.display, image).ok().unwrap()
|
|
|
|
}
|
|
|
|
|
2018-04-17 10:45:44 +02:00
|
|
|
/// Creates a frame from the display.
|
|
|
|
pub fn draw(&self) -> Frame {
|
|
|
|
self.display.draw()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Renders on the display.
|
|
|
|
pub fn render<C: RenderCamera>(&self, scene: &Scene, camera: &C) {
|
|
|
|
|
|
|
|
let mut target = self.draw();
|
2018-04-24 11:57:25 +02:00
|
|
|
target.clear_color_srgb_and_depth(self.clear_color, 1.0);
|
2018-04-17 10:45:44 +02:00
|
|
|
|
|
|
|
let perspective = camera.get_perspective_matrix();
|
|
|
|
let view = camera.get_view_matrix();
|
|
|
|
|
|
|
|
let params = DrawParameters {
|
|
|
|
depth: Depth {
|
|
|
|
test: DepthTest::IfLess,
|
|
|
|
write: true,
|
|
|
|
.. Default::default()
|
|
|
|
},
|
|
|
|
blend: Blend::alpha_blending(),
|
|
|
|
.. Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
for model in scene.iter() {
|
|
|
|
let model = &*model.borrow();
|
|
|
|
for part in &model.parts {
|
|
|
|
|
|
|
|
if let &Some(ref buffer) = part.vertex_buffer() {
|
|
|
|
|
2018-06-15 17:36:51 +02:00
|
|
|
let diffuse = if let Some(ref name) = part.material_name {
|
|
|
|
model.materials.get(name).unwrap().diffuse
|
|
|
|
} else {
|
|
|
|
Vector3::new(1.0, 1.0, 1.0)
|
|
|
|
};
|
|
|
|
|
2018-04-17 10:45:44 +02:00
|
|
|
let texture = self.get_texture_of_part(&model, part);
|
|
|
|
|
2018-06-20 17:24:33 +02:00
|
|
|
let (texture, size) = if let Some((texture, size)) = texture {
|
|
|
|
(texture, size)
|
2018-04-24 11:57:25 +02:00
|
|
|
} else {
|
2018-06-20 17:24:33 +02:00
|
|
|
(&self.default_texture, Vector3::new(1.0, 1.0, 1.0))
|
2018-04-24 11:57:25 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
target.draw(
|
|
|
|
buffer,
|
|
|
|
NoIndices(PrimitiveType::TrianglesList),
|
|
|
|
&self.program,
|
|
|
|
&uniform!(
|
2018-06-15 17:36:51 +02:00
|
|
|
diffuse: Into::<[f32; 3]>::into(diffuse),
|
2018-04-24 11:57:25 +02:00
|
|
|
tex: texture,
|
2018-06-14 22:03:43 +02:00
|
|
|
perspective: Into::<[[f32; 4]; 4]>::into(perspective),
|
|
|
|
view: Into::<[[f32; 4]; 4]>::into(view),
|
2018-06-20 17:24:33 +02:00
|
|
|
texture_size: Into::<[f32; 3]>::into(size),
|
2018-06-14 22:03:43 +02:00
|
|
|
),
|
2018-04-24 11:08:32 +02:00
|
|
|
¶ms,
|
|
|
|
).unwrap();
|
2018-04-17 10:45:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
target.finish().unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Renders a part of a model.
|
2018-06-20 17:24:33 +02:00
|
|
|
fn get_texture_of_part<'a>(&self, model: &'a Model, part: &Part) -> Option<(&'a SrgbTexture2d, Vector3<f32>)> {
|
2018-04-17 10:45:44 +02:00
|
|
|
if let Some(ref material_name) = part.material_name {
|
|
|
|
if let Some(ref material) = model.materials.get(material_name) {
|
2018-06-21 14:13:34 +02:00
|
|
|
if let Some((texture, size)) = material.textures.get("map_Kd") {
|
|
|
|
if let Some(texture) = model.get_texture_by_name(texture) {
|
2018-06-20 17:24:33 +02:00
|
|
|
Some((texture, *size))
|
2018-04-18 11:49:27 +02:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2018-04-17 10:45:44 +02:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Builds a vertex buffer from a vector of vertex.
|
|
|
|
pub fn build_vertex_buffer(&self, vertices: &Vec<Vertex>) -> VertexBuffer<Vertex> {
|
|
|
|
VertexBuffer::new(&self.display, vertices).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Changes the clear color of the renderer.
|
|
|
|
pub fn set_clear_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
|
|
|
|
self.clear_color = (r, g, b, a);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Shows the window if hidden.
|
|
|
|
pub fn show(&mut self) {
|
|
|
|
self.gl_window().show();
|
|
|
|
}
|
2018-04-17 14:21:35 +02:00
|
|
|
|
|
|
|
/// Returns a DynamicImage of the corresponding frame.
|
|
|
|
pub fn capture(&self, dimensions: (u32, u32)) -> image::DynamicImage {
|
|
|
|
rgba_image_data_to_image(self.capture_rgba_image_data(dimensions))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a RgbaImageData of the corresponding frame.
|
|
|
|
pub fn capture_rgba_image_data(&self, dimensions: (u32, u32)) -> RgbaImageData {
|
|
|
|
|
|
|
|
let rect = Rect {
|
|
|
|
left: 0,
|
|
|
|
bottom: 0,
|
|
|
|
width: dimensions.0,
|
|
|
|
height: dimensions.1,
|
|
|
|
};
|
|
|
|
|
|
|
|
let blit_target = BlitTarget {
|
|
|
|
left: 0,
|
|
|
|
bottom: 0,
|
|
|
|
width: dimensions.0 as i32,
|
|
|
|
height: dimensions.1 as i32,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Create temporary texture and blit the front buffer to it
|
|
|
|
let texture = SrgbTexture2d::empty(&self.display, dimensions.0, dimensions.1)
|
|
|
|
.unwrap();
|
|
|
|
let framebuffer = SimpleFrameBuffer::new(&self.display, &texture)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
use glium::Surface;
|
|
|
|
framebuffer.blit_from_frame(
|
|
|
|
&rect,
|
|
|
|
&blit_target,
|
|
|
|
MagnifySamplerFilter::Nearest
|
|
|
|
);
|
|
|
|
|
|
|
|
// Read the texture into new pixel buffer
|
|
|
|
let pixel_buffer = texture.read_to_pixel_buffer();
|
|
|
|
pixel_buffer.read_as_texture_2d().unwrap()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Converts a RgbaImamgeData to a DynamicImage.
|
|
|
|
pub fn rgba_image_data_to_image(image_data: RgbaImageData) -> image::DynamicImage {
|
|
|
|
|
|
|
|
let pixels = {
|
|
|
|
let mut v = Vec::with_capacity(image_data.data.len() * 4);
|
|
|
|
for (a, b, c, d) in image_data.data {
|
|
|
|
v.push(a);
|
|
|
|
v.push(b);
|
|
|
|
v.push(c);
|
|
|
|
v.push(d);
|
|
|
|
}
|
|
|
|
v
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Create ImageBuffer
|
|
|
|
let image_buffer =
|
|
|
|
image::ImageBuffer::from_raw(image_data.width, image_data.height, pixels)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Save the screenshot to file
|
|
|
|
let image = image::DynamicImage::ImageRgba8(image_buffer).flipv();
|
|
|
|
|
|
|
|
image
|
|
|
|
|
2018-04-17 10:45:44 +02:00
|
|
|
}
|