Some quite aggressive commit... 😢

This commit is contained in:
Thomas Forgione 2018-04-17 10:45:44 +02:00
parent c5b04aef9d
commit 25ee1fefce
No known key found for this signature in database
GPG Key ID: C75CD416BD1FFCE1
18 changed files with 563 additions and 507 deletions

View File

@ -7,8 +7,9 @@ authors = ["Thomas Forgione <thomas@tforgione.fr>"]
num = "*"
glium = "*"
image = "*"
verbose-log = { git = "https://gitea.tforgione.fr/dash-3d/verbose-log" }
byteorder = "*"
clap = "*"
verbose-log = { git = "https://gitea.tforgione.fr/dash-3d/verbose-log" }
[[bin]]
name = "3d-viewer"

View File

@ -21,6 +21,8 @@ void main() {
if (color.a < 0.5) {
discard;
} else {
color.a = 1.0;
}
}

View File

@ -2,7 +2,6 @@
const EPSILON: f32 = 0.001;
use glium::backend::glutin::Display;
use glium::glutin::{
Event,
WindowEvent,
@ -15,7 +14,7 @@ use glium::glutin::{
};
use math::vector::{Vector2, Vector3};
use renderer::camera::Camera;
use camera::Camera;
use renderer::Renderer;
use model::Model;
@ -23,10 +22,10 @@ use model::Model;
pub trait Controls {
/// Modifies the camera depending on the event.
fn manage_event<'a>(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer<'a, Display>);
fn manage_event(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer);
/// Updates the camera depending on time.
fn update<'a>(&mut self, camera: &mut Camera, renderer: &Renderer<'a, Display>);
fn update(&mut self, camera: &mut Camera, renderer: &Renderer);
}
/// An orbit controls allowing to orbit around an object.
@ -95,7 +94,7 @@ impl OrbitControls {
}
impl Controls for OrbitControls {
fn manage_event<'a>(&mut self, event: &Event, camera: &mut Camera, _: &Renderer<'a, Display>) {
fn manage_event(&mut self, event: &Event, camera: &mut Camera, _: &Renderer) {
match *event {
Event::WindowEvent {
@ -162,7 +161,7 @@ impl Controls for OrbitControls {
}
}
fn update<'a>(&mut self, _: &mut Camera, _: &Renderer<'a, Display>) {
fn update(&mut self, _: &mut Camera, _: &Renderer) {
}
}
@ -235,7 +234,7 @@ impl FirstPersonControls {
impl Controls for FirstPersonControls {
fn manage_event<'a>(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer<'a, Display>) {
fn manage_event(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer) {
match *event {
// On resize window
@ -344,7 +343,7 @@ impl Controls for FirstPersonControls {
self.update_camera(camera);
}
fn update<'a>(&mut self, camera: &mut Camera, renderer: &Renderer<'a, Display>) {
fn update(&mut self, camera: &mut Camera, renderer: &Renderer) {
renderer.gl_window().window().set_cursor(MouseCursor::NoneCursor);

View File

@ -2,14 +2,14 @@
pub mod obj;
use parser::Element;
use model::Model;
use std::io;
use std::io::Write;
use std::fmt;
use parser::Element;
use model::Model;
/// An error occured during export.
pub enum ExportError {
/// An IO error occured when creating files or directories.

View File

@ -24,7 +24,7 @@ impl ObjElementSerializer {
}
/// Writes a face to a writable output.
pub fn face_to_string(&self, f: model::Face) -> String {
pub fn face_to_string(&self, f: model::face::Face) -> String {
if f.a.texture_coordinate.is_none() && f.a.normal.is_none() {
// Only vertices

View File

@ -13,3 +13,6 @@ pub mod model;
pub mod renderer;
pub mod parser;
pub mod exporter;
pub mod scene;
pub mod camera;
pub mod controls;

View File

@ -80,6 +80,20 @@ macro_rules! make_bounding_box {
}
ret
}
/// Returns the bounding box of the union of two bounding boxes.
pub fn union(&self, other: &$name<T>) -> $name<T> {
let mut ret = self.clone();
for i in 0..$size {
if ret.min()[i] > other.min()[i] {
ret.min_mut()[i] = other.min()[i];
}
if ret.max()[i] < other.max()[i] {
ret.max_mut()[i] = other.max()[i];
}
}
ret
}
}
}

88
src/model/face.rs Normal file
View File

@ -0,0 +1,88 @@
//! This module contains the structs needed to build faces.
use std::fmt;
#[derive(Copy, Clone, PartialEq)]
/// The indices needed for each vertex of a face.
pub struct FaceVertex {
/// The index of the vertex.
pub vertex: usize,
/// The index of the texture coordinate, None if there is no texture coordinate.
pub texture_coordinate: Option<usize>,
/// The index of the normal, None if there is no normal.
pub normal: Option<usize>,
}
impl FaceVertex {
/// Creates a new face vertex from its attributes.
pub fn new(vertex: usize, texture_coordinate: Option<usize>, normal: Option<usize>) -> FaceVertex {
FaceVertex {
vertex: vertex,
texture_coordinate: texture_coordinate,
normal: normal,
}
}
}
impl fmt::Debug for FaceVertex {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let texture_coordinate = if let Some(tc) = self.texture_coordinate {
tc.to_string()
} else {
"".to_owned()
};
let normal = if let Some(n) = self.normal {
n.to_string()
} else {
"".to_owned()
};
write!(formatter, "{}/{}/{}", self.vertex, texture_coordinate, normal)
}
}
#[derive(Clone, PartialEq, Debug)]
/// A 3D Face.
///
/// It contains exactly 3 face vertices, and the name of the corresponding material.
pub struct Face {
/// The first vertex of the face.
pub a: FaceVertex,
/// The second vertex of the face.
pub b: FaceVertex,
/// The third vertex of the face.
pub c: FaceVertex,
/// The name of the material, None if no material is specified.
pub material_name: Option<String>,
/// Id of the face, number of the face in the original file.
pub id: Option<usize>,
}
impl Face {
/// Creates a new face from its attributes.
///
/// Material name defaults to None.
pub fn new(a: FaceVertex, b: FaceVertex, c: FaceVertex) -> Face {
Face {
a: a,
b: b,
c: c,
material_name: None,
id: None,
}
}
}

49
src/model/face_vertex.rs Normal file
View File

@ -0,0 +1,49 @@
//! This module contains the FaceVertex struct.
use std::fmt;
#[derive(Copy, Clone, PartialEq)]
/// The indices needed for each vertex of a face.
pub struct FaceVertex {
/// The index of the vertex.
pub vertex: usize,
/// The index of the texture coordinate, None if there is no texture coordinate.
pub texture_coordinate: Option<usize>,
/// The index of the normal, None if there is no normal.
pub normal: Option<usize>,
}
impl FaceVertex {
/// Creates a new face vertex from its attributes.
pub fn new(vertex: usize, texture_coordinate: Option<usize>, normal: Option<usize>) -> FaceVertex {
FaceVertex {
vertex: vertex,
texture_coordinate: texture_coordinate,
normal: normal,
}
}
}
impl fmt::Debug for FaceVertex {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let texture_coordinate = if let Some(tc) = self.texture_coordinate {
tc.to_string()
} else {
"".to_owned()
};
let normal = if let Some(n) = self.normal {
n.to_string()
} else {
"".to_owned()
};
write!(formatter, "{}/{}/{}", self.vertex, texture_coordinate, normal)
}
}

41
src/model/material.rs Normal file
View File

@ -0,0 +1,41 @@
//! This module contains everything related to materials.
use std::collections::HashMap;
use glium::texture::SrgbTexture2d;
/// A 3D material.
pub struct Material {
/// The name of the material.
pub name: String,
/// Map linking each texture map to its file path.
pub textures: HashMap<String, String>,
/// Instructions that are unknown.
///
/// They might be necessary for the export though.
pub unknown_instructions: Vec<String>,
/// The texture that will be used for rendering.
pub rendering_texture: Option<SrgbTexture2d>,
}
impl Material {
/// Creates a material from its name, without texture.
pub fn new(name: &str) -> Material {
Material {
name: name.to_owned(),
textures: HashMap::new(),
unknown_instructions: vec![],
rendering_texture: None,
}
}
/// Adds a unknown instruction into the material.
pub fn add_unknown_instruction(&mut self, instruction: String) {
self.unknown_instructions.push(instruction);
}
}

View File

@ -1,13 +1,22 @@
//! Module containg the struct for storing 3D model
pub mod face;
pub mod material;
use std::collections::HashMap;
use std::fmt;
use glium::VertexBuffer;
use parser::{parse, ParserError};
use model::face::{FaceVertex, Face};
use model::material::Material;
use math::vector::{Vector2, Vector3};
use math::bounding_box::BoundingBox3;
use renderer::Renderer;
#[derive(Copy, Clone, Debug, PartialEq)]
/// A raw vertex, with its 3D coordinates, texture coordinates and normals.
pub struct Vertex {
@ -18,122 +27,6 @@ pub struct Vertex {
implement_vertex!(Vertex, vertex, tex_coords, normal);
#[derive(Copy, Clone, PartialEq)]
/// The indices needed for each vertex of a face.
pub struct FaceVertex {
/// The index of the vertex.
pub vertex: usize,
/// The index of the texture coordinate, None if there is no texture coordinate.
pub texture_coordinate: Option<usize>,
/// The index of the normal, None if there is no normal.
pub normal: Option<usize>,
}
impl FaceVertex {
/// Creates a new face vertex from its attributes.
pub fn new(vertex: usize, texture_coordinate: Option<usize>, normal: Option<usize>) -> FaceVertex {
FaceVertex {
vertex: vertex,
texture_coordinate: texture_coordinate,
normal: normal,
}
}
}
impl fmt::Debug for FaceVertex {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let texture_coordinate = if let Some(tc) = self.texture_coordinate {
tc.to_string()
} else {
"".to_owned()
};
let normal = if let Some(n) = self.normal {
n.to_string()
} else {
"".to_owned()
};
write!(formatter, "{}/{}/{}", self.vertex, texture_coordinate, normal)
}
}
#[derive(Clone, PartialEq, Debug)]
/// A 3D Face.
///
/// It contains exactly 3 face vertices, and the name of the corresponding material.
pub struct Face {
/// The first vertex of the face.
pub a: FaceVertex,
/// The second vertex of the face.
pub b: FaceVertex,
/// The third vertex of the face.
pub c: FaceVertex,
/// The name of the material, None if no material is specified.
pub material_name: Option<String>,
/// Id of the face, number of the face in the original file.
pub id: Option<usize>,
}
impl Face {
/// Creates a new face from its attributes.
///
/// Material name defaults to None.
pub fn new(a: FaceVertex, b: FaceVertex, c: FaceVertex) -> Face {
Face {
a: a,
b: b,
c: c,
material_name: None,
id: None,
}
}
}
/// A 3D material.
pub struct Material {
/// The name of the material.
pub name: String,
/// Map linking each texture map to its file path.
pub textures: HashMap<String, String>,
/// Instructions that are unknown.
///
/// They might be necessary for the export though.
pub unknown_instructions: Vec<String>,
}
impl Material {
/// Creates a material from its name, without texture.
pub fn new(name: &str) -> Material {
Material {
name: name.to_owned(),
textures: HashMap::new(),
unknown_instructions: vec![],
}
}
/// Adds a unknown instruction into the material.
pub fn add_unknown_instruction(&mut self, instruction: String) {
self.unknown_instructions.push(instruction);
}
}
/// A part of a 3D model.
///
/// It contains a list of faces that correspond to the same material.
@ -144,6 +37,10 @@ pub struct Part {
/// List of all the faces in the part.
pub faces: Vec<Face>,
/// VertexBuffer for rendering.
vertex_buffer: Option<VertexBuffer<Vertex>>,
}
impl Part {
@ -153,6 +50,7 @@ impl Part {
Part {
material_name: material_name,
faces: vec![],
vertex_buffer: None,
}
}
@ -165,36 +63,12 @@ impl Part {
self.faces.push(f);
}
/// Creates a shape that can be rendered.
pub fn build_shape(&self, vertices: &Vec<Vector3<f64>>, tex_coords: &Vec<Vector2<f64>>, normals: &Vec<Vector3<f64>>) -> Vec<Vertex> {
let mut shape = vec![];
for face in &self.faces {
for &&v in &[&face.a, &face.b, &face.c] {
let vertex = vertices[v.vertex].into();
let tex_coord = if let Some(tex_index) = v.texture_coordinate {
tex_coords[tex_index].into()
} else {
[0.0, 0.0]
};
let normal = if let Some(normal_index) = v.normal {
normals[normal_index].into()
} else {
[0.0, 0.0, 0.0]
};
shape.push(Vertex {
vertex: vertex,
tex_coords: tex_coord,
normal: normal,
});
}
}
shape
/// Returns a reference to the vertex buffer.
pub fn vertex_buffer(&self) -> &Option<VertexBuffer<Vertex>> {
&self.vertex_buffer
}
}
/// A 3D model.
@ -407,9 +281,9 @@ impl Model {
}
/// Centers and scales the model.
pub fn center_and_scale(&mut self) {
let bbox = self.bounding_box();
/// Centers and scales the model from a bounding box.
pub fn center_and_scale_from_box(&mut self, bbox: &BoundingBox3<f64>) {
// let bbox = self.bounding_box();
let center = (bbox.min() + bbox.max()) / 2.0;
let size = (bbox.max() - bbox.min()).norm();
@ -418,4 +292,71 @@ impl Model {
*vertex /= size;
}
}
/// Centers and scales the model from its own bounding box.
pub fn center_and_scale(&mut self) {
let bbox = self.bounding_box();
self.center_and_scale_from_box(&bbox);
}
/// Creates vertex buffers for each part of the model.
pub fn build_vertex_buffers(&mut self, renderer: &Renderer) {
for part in &mut self.parts {
Model::build_vertex_buffer_for_part(
&self.vertices,
&self.texture_coordinates,
&self.normals,
part,
renderer);
}
}
/// Creates a vertex buffer that can be rendered for a part of the model.
pub fn build_vertex_buffer_for_part(
vertices: &Vec<Vector3<f64>>,
texture_coordinates: &Vec<Vector2<f64>>,
normals: &Vec<Vector3<f64>>,
part: &mut Part,
renderer: &Renderer) {
let mut vertex_buffer = vec![];
for face in &part.faces {
for &&v in &[&face.a, &face.b, &face.c] {
let vertex = vertices[v.vertex].into();
let tex_coord = if let Some(tex_index) = v.texture_coordinate {
texture_coordinates[tex_index].into()
} else {
[0.0, 0.0]
};
let normal = if let Some(normal_index) = v.normal {
normals[normal_index].into()
} else {
[0.0, 0.0, 0.0]
};
vertex_buffer.push(Vertex {
vertex: vertex,
tex_coords: tex_coord,
normal: normal,
});
}
}
part.vertex_buffer = Some(renderer.build_vertex_buffer(&vertex_buffer));
}
/// Builds the textures of all materials.
pub fn build_textures(&mut self, renderer: &Renderer) {
for (_, ref mut material) in &mut self.materials {
Model::build_texture_for_material(material, renderer);
}
}
/// Builds the SrgbTextures for rendering.
pub fn build_texture_for_material(material: &mut Material, renderer: &Renderer) {
if let Some(path) = material.textures.get("map_Kd") {
material.rendering_texture = Some(renderer.make_texture(path));
};
}
}

View File

@ -8,7 +8,9 @@ use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::{BufReader, Error};
use math::vector::{Vector2, Vector3};
use model::{Face, Model, Material, ModelError};
use model::face::Face;
use model::material::Material;
use model::{Model, ModelError};
/// A 3D element.
///

View File

@ -2,7 +2,7 @@
use parser::{Element, ParserError, LineParser};
use math::vector::{Vector2, Vector3};
use model::{FaceVertex, Face};
use model::face::{FaceVertex, Face};
/// The wavefront OBJ format parser.
pub struct ObjParser {

View File

@ -1,8 +1,15 @@
extern crate clap;
extern crate glium;
#[macro_use]
extern crate verbose_log;
extern crate model_converter;
use std::env;
use std::rc::Rc;
use std::cell::RefCell;
use std::process::exit;
use std::time::{Instant, Duration};
use clap::{App, Arg};
use glium::Display;
use glium::glutin;
@ -16,59 +23,99 @@ use glium::glutin::WindowEvent;
use glium::glutin::VirtualKeyCode;
use model_converter::math::bounding_box::BoundingBox3;
use model_converter::math::vector::Vector3;
use model_converter::parser::parse_into_model;
use model_converter::parser::parse;
use model_converter::renderer::Renderer;
use model_converter::renderer::controls::OrbitControls;
use model_converter::renderer::controls::FirstPersonControls;
use model_converter::renderer::camera::Camera;
use model_converter::model::Model;
use model_converter::controls::OrbitControls;
use model_converter::controls::FirstPersonControls;
use model_converter::camera::Camera;
fn as_millis(duration: Duration) -> u64 {
duration.as_secs() * 1_000 + (duration.subsec_nanos() as u64) / 1_000_000
}
fn main() {
let mut model = Model::new();
let mut inputs = vec![];
let mut iterator = env::args();
let matches = App::new("3D Viewer")
.version("1.0")
.arg(Arg::with_name("input")
.short("i")
.long("input")
.value_name("FILES")
.takes_value(true)
.multiple(true)
.help("Input model files")
.required(true))
.arg(Arg::with_name("first person")
.short("f")
.long("first-person")
.takes_value(false)
.help("Uses first person controls instead of orbit controls"))
.arg(Arg::with_name("verbose")
.short("v")
.long("verbose")
.help("Shows logs during the parsing of the model"))
.get_matches();
// Skip the name of the program
iterator.next();
// Set verbose flag
verbose_log::log::VERBOSE.with(|verbose| {
*verbose.borrow_mut() = matches.occurrences_of("verbose") > 0;
});
while let Some(argument) = iterator.next() {
match argument.as_ref() {
"-i" | "--input" => {
if let Some(path) = iterator.next() {
inputs.push(path);
} else {
eprintln!("Expecting path after {}", argument);
exit(-1);
}
use std::f64::{MIN, MAX};
let mut bbox = BoundingBox3::new(
Vector3::new(MAX, MAX, MAX),
Vector3::new(MIN, MIN, MIN),
);
let mut models = vec![];
for input in matches.values_of("input").unwrap() {
match parse(&input) {
Ok(model) => {
bbox = bbox.union(&model.bounding_box());
models.push((input.to_owned(), model))
},
arg => {
eprintln!("Argument unkown: {}", arg);
exit(-1);
Err(e) => {
eprintln!("Error while parsing file: {}", e);
exit(1);
},
}
}
for input in inputs {
if let Err(e) = parse_into_model(&input, &mut model) {
eprintln!("Error while parsing file: {}", e);
exit(1);
}
}
model.center_and_scale();
let mut events_loop = EventsLoop::new();
let window = WindowBuilder::new().with_visibility(false);
let context = glutin::ContextBuilder::new().with_depth_buffer(24);
let display = Display::new(window, context, &events_loop).unwrap();
let mut renderer = Renderer::new(display);
let mut closed = false;
let mut scene = vec![];
let mut before;
let mut duration;
renderer.add_model(&model);
for (name, mut model) in models {
log!("Scaling model {}...", name);
model.center_and_scale_from_box(&bbox);
log!("\nBuilding textures for model {}...", name);
before = Instant::now();
model.build_textures(&renderer);
duration = Instant::now().duration_since(before);
log!(" done in {}ms.\nBuilding vertex buffers for model {}...",
as_millis(duration) ,name);
before = Instant::now();
model.build_vertex_buffers(&renderer);
duration = Instant::now().duration_since(before);
scene.push(Rc::new(RefCell::new(model)));
logln!(" done in {}ms.\nFinished", as_millis(duration));
}
let mut closed = false;
let mut camera = Camera::new(
Vector3::new( 0.0, 0.0, 0.0),
@ -76,13 +123,21 @@ fn main() {
Vector3::new( 0.0, 1.0, 0.0),
);
camera.z_near = 0.00001;
camera.z_near = 0.0001;
let mut controls = FirstPersonControls::new();
let mut controls: Box<Controls> = if matches.value_of("first person").is_some() {
Box::new(FirstPersonControls::new())
} else {
Box::new(OrbitControls::new(
Vector3::new(0.0, 0.0, 0.0),
1.0,
&mut camera
))
};
renderer.show();
use model_converter::renderer::controls::Controls;
use model_converter::controls::Controls;
while !closed {
events_loop.poll_events(|ev| {
@ -109,10 +164,7 @@ fn main() {
});
controls.update(&mut camera, &renderer);
let mut target = renderer.draw();
renderer.render(&camera, &mut target);
target.finish().unwrap();
renderer.render(&scene, &camera);
}
}

158
src/renderer.rs Normal file
View File

@ -0,0 +1,158 @@
//! This module contains the rendering structs.
use std::cell::Ref;
use image;
use glium::texture::{RawImage2d, SrgbTexture2d};
use glium::{Frame, Display, Surface, Program, DrawParameters, Depth, VertexBuffer};
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};
/// 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),
}
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();
Renderer {
display: display,
program: program,
clear_color: (0.0, 0.0, 0.0, 1.0),
}
}
/// 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();
let dimensions = image.dimensions();
let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dimensions);
SrgbTexture2d::new(&self.display, image).ok().unwrap()
}
/// 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();
target.clear_color_and_depth(self.clear_color, 1.0);
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() {
let texture = self.get_texture_of_part(&model, part);
if let Some(texture) = texture {
target.draw(
buffer,
NoIndices(PrimitiveType::TrianglesList),
&self.program,
&uniform!(
tex: texture,
perspective: perspective,
view: view,
),
&params,
).unwrap();
} else {
target.draw(
buffer,
NoIndices(PrimitiveType::TrianglesList),
&self.program,
&uniform!(
perspective: perspective,
view: view,
),
&params,
).unwrap();
}
}
}
}
target.finish().unwrap();
}
/// Renders a part of a model.
fn get_texture_of_part<'a>(&self, model: &'a Model, part: &Part) -> Option<&'a SrgbTexture2d> {
if let Some(ref material_name) = part.material_name {
if let Some(ref material) = model.materials.get(material_name) {
if let Some(ref texture) = material.rendering_texture {
Some(texture)
} 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();
}
}

View File

@ -1,302 +0,0 @@
//! This module contains everything related to rendering.
pub mod camera;
pub mod controls;
use std::cell::Ref;
use std::borrow::Cow;
use glium;
use glium::glutin::GlWindow;
use glium::draw_parameters::{DepthTest, Blend};
use glium::texture::{
RawImage2d,
SrgbTexture2d,
};
use glium::index::{
NoIndices,
PrimitiveType
};
use glium::{
Program,
Display,
VertexBuffer,
Frame,
DrawParameters,
Depth,
};
use glium::backend::Facade;
use glium::backend::glutin::headless::Headless;
use image;
use model::{Model, Vertex};
use renderer::camera::RenderCamera;
struct RGBAImageData {
pub data: Vec<(u8, u8, u8, u8)>,
pub width: u32,
pub height: u32,
}
impl glium::texture::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,
}
}
}
/// 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)
}
}
/// A material that can be bound to OpenGL.
///
/// Only textures are supported for now.
pub struct RenderMaterial {
texture: Option<SrgbTexture2d>,
}
impl RenderMaterial {
/// Creates a new material with no texture.
pub fn new() -> RenderMaterial {
RenderMaterial {
texture: None
}
}
/// Creates a material from a path to an image file.
pub fn from_texture_path<D: Drawer + Facade + Sized>(path: &str, drawer: &D) -> RenderMaterial {
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 {
texture: SrgbTexture2d::new(drawer, image).ok()
}
} else {
RenderMaterial {
texture: None
}
}
}
}
/// A renderer. It contains a display, shaders, and a vector of models it can render.
pub struct Renderer<'a, D: Drawer + Facade> {
drawer: D,
program: Program,
models: Vec<(&'a Model, Vec<(RenderMaterial, VertexBuffer<Vertex>)>)>,
}
impl<'a, D: Drawer + Facade + Sized> Renderer<'a, D> {
/// Creates a new renderer from a display.
///
/// Is uses the default shaders and creates an empty vec of models.
pub fn new(drawer: D) -> Renderer<'a, D> {
let program = Program::from_source(
&drawer,
include_str!("../../assets/shaders/shader.vert"),
include_str!("../../assets/shaders/shader.frag"),
None
);
if let Ok(program) = program {
Renderer {
drawer: drawer,
program: program,
models: vec![],
}
} else {
println!("{:?}", program.err().unwrap());
panic!()
}
}
/// Adds a model to the renderer, and compute the corresponding buffers for rendering.
pub fn add_model(&mut self, model: &'a Model) {
let mut buffers = vec![];
for part in &model.parts {
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") {
RenderMaterial::from_texture_path(path, &self.drawer)
} else {
RenderMaterial::new()
}
} else {
RenderMaterial::new()
}
} else {
RenderMaterial::new()
};
let shape = part.build_shape(&model.vertices, &model.texture_coordinates, &model.normals);
buffers.push((material, VertexBuffer::new(&self.drawer, &shape).unwrap()));
}
self.models.push((model, buffers));
}
/// Returns the reference to the drawer used for this renderer.
pub fn facade(&self) -> &D {
&self.drawer
}
/// Creates a frame from the display.
pub fn draw(&self) -> Frame {
self.drawer.draw()
}
/// Renders the result of the models from the camera and paint it on the target.
pub fn render<C: RenderCamera>(&self, camera: &C, target: &mut Frame) {
use glium::Surface;
target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
let params = DrawParameters {
depth: Depth {
test: DepthTest::IfLess,
write: true,
.. Default::default()
},
blend: Blend::alpha_blending(),
.. Default::default()
};
for &(_, ref buffers) in &self.models {
for &(ref material, ref buffer) in buffers {
let perspective = camera.get_perspective_matrix();
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,
),
&params,
).unwrap();
} else {
target.draw(
buffer,
NoIndices(PrimitiveType::TrianglesList),
&self.program,
&uniform!(
perspective: perspective,
view: view,
),
&params,
).unwrap();
};
}
}
}
/// Returns an image of the corresponding frame.
pub fn capture(&self, dimensions: (u32, u32)) -> image::DynamicImage {
let rect = glium::Rect {
left: 0,
bottom: 0,
width: dimensions.0,
height: dimensions.1,
};
let blit_target = glium::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 = glium::texture::Texture2d::empty(self.facade(), dimensions.0, dimensions.1)
.unwrap();
let framebuffer = glium::framebuffer::SimpleFrameBuffer::new(self.facade(), &texture)
.unwrap();
use glium::Surface;
framebuffer.blit_from_frame(
&rect,
&blit_target,
glium::uniforms::MagnifySamplerFilter::Nearest
);
// Read the texture into new pixel buffer
let pixel_buffer = texture.read_to_pixel_buffer();
let image_data: RGBAImageData = pixel_buffer.read_as_texture_2d().unwrap();
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
}
}
impl<'a> Renderer<'a, Display> {
/// Shows the window if it was hidden.
pub fn show(&mut self) {
self.drawer.gl_window().show();
}
/// Hides the window if it was visible.
pub fn hide(&mut self) {
self.drawer.gl_window().hide();
}
/// Returns a reference to the gl window.
pub fn gl_window(&self) -> Ref<GlWindow> {
self.drawer.gl_window()
}
}

8
src/scene.rs Normal file
View File

@ -0,0 +1,8 @@
//! This module contains everything related to scenes.
use std::rc::Rc;
use std::cell::RefCell;
use model::Model;
/// Represents a 3D scene with models.
pub type Scene = Vec<Rc<RefCell<Model>>>;