Some quite aggressive commit... 😢
This commit is contained in:
parent
c5b04aef9d
commit
25ee1fefce
|
@ -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"
|
||||
|
|
|
@ -21,6 +21,8 @@ void main() {
|
|||
|
||||
if (color.a < 0.5) {
|
||||
discard;
|
||||
} else {
|
||||
color.a = 1.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
¶ms,
|
||||
).unwrap();
|
||||
} else {
|
||||
target.draw(
|
||||
buffer,
|
||||
NoIndices(PrimitiveType::TrianglesList),
|
||||
&self.program,
|
||||
&uniform!(
|
||||
perspective: perspective,
|
||||
view: view,
|
||||
),
|
||||
¶ms,
|
||||
).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();
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
¶ms,
|
||||
).unwrap();
|
||||
} else {
|
||||
target.draw(
|
||||
buffer,
|
||||
NoIndices(PrimitiveType::TrianglesList),
|
||||
&self.program,
|
||||
&uniform!(
|
||||
perspective: perspective,
|
||||
view: view,
|
||||
),
|
||||
¶ms,
|
||||
).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()
|
||||
}
|
||||
|
||||
}
|
|
@ -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>>>;
|
Loading…
Reference in New Issue