Compare commits

..

1 Commits

Author SHA1 Message Date
ebb55da52c
Nice 2019-10-03 16:25:28 +02:00
7 changed files with 369 additions and 370 deletions

View File

@ -7,8 +7,8 @@ authors = ["Thomas Forgione <thomas@tforgione.fr>"]
log = "0.4" log = "0.4"
stderrlog = "0.4.1" stderrlog = "0.4.1"
num = "0.1.42" num = "0.1.42"
glium = "0.32.1" glium = "0.22.0"
image = "0.23" image = "0.21.2"
byteorder = "1.2.3" byteorder = "1.2.3"
clap = "2.31.2" clap = "2.31.2"
# nalgebra = "0.16.13" # nalgebra = "0.16.13"

View File

@ -1,31 +1,75 @@
#version 140 #version 140
#extension GL_OES_standard_derivatives : enable
uniform sampler2D tex; uniform sampler2D tex;
uniform vec3 diffuse; uniform vec3 diffuse;
in vec3 v_normal; in vec3 v_normal;
in vec2 v_tex_coords; in vec2 v_tex_coords;
in vec3 v_barycentric;
in vec4 v_position;
out vec4 color; out vec4 color;
vec4 wireframe_color;
vec4 render_color;
vec3 ambientLight = vec3(0.3,0.3,0.3); vec3 ambientLight = vec3(0.3,0.3,0.3);
vec3 directionnalLight = normalize(vec3(10,5,7)); vec3 directionnalLight = normalize(vec3(10,5,7));
vec3 directionnalLightFactor = vec3(0.6,0.6,0.6); vec3 directionnalLightFactor = vec3(0.6,0.6,0.6);
// float edgeFactor(vec3 a){
// vec3 d = fwidth(v_barycentric);
// vec3 a3 = smoothstep(vec3(0.0), d*1.5, v_barycentric);
// return min(min(a3.x, a3.y), a3.z);
// }
void main() { void main() {
vec3 d = fwidth(v_barycentric);
vec3 a3 = smoothstep(vec3(0.0), 0.8 * d, v_barycentric);
float scale = (1 - min(min(a3.x, a3.y), a3.z)) / 2 + 0.5;
wireframe_color = vec4(scale, scale, scale, 1.0);
// float threshold = 0.1;
// vec3 d = fwidth(v_barycentric);
// if (d.x < threshold || d.y < threshold || d.z < threshold) {
// wireframe_color = vec4(1.0, 1.0, 1.0, 1.0);
// } else {
// wireframe_color = vec4(0.5, 0.5, 0.5, 1.0);
// }
vec3 lambertComponent = dot(directionnalLight, v_normal) * directionnalLightFactor; vec3 lambertComponent = dot(directionnalLight, v_normal) * directionnalLightFactor;
lambertComponent = max(vec3(0.0, 0.0, 0.0), lambertComponent); lambertComponent = max(vec3(0.0, 0.0, 0.0), lambertComponent);
vec4 factor = vec4(ambientLight + lambertComponent, 1.0); vec4 factor = vec4(ambientLight + lambertComponent, 1.0);
color = factor * vec4(diffuse, 1.0) * texture(tex, v_tex_coords); render_color = factor * vec4(diffuse, 1.0) * texture(tex, v_tex_coords);
if (color.a < 0.05) { if (render_color.a < 0.05) {
discard; discard;
} else { } else {
color.a = 1.0; render_color.a = 1.0;
} }
float z_min = 0.15;
float z_max = 0.25;
float lambda = (v_position.z - z_min) / (z_max - z_min);
if (lambda < 0) {
lambda = 0;
}
if (lambda > 1) {
lambda = 1;
}
color = lambda * wireframe_color + (1 - lambda) * render_color;
} }

View File

@ -6,14 +6,19 @@ uniform vec3 texture_size;
in vec3 vertex; in vec3 vertex;
in vec2 tex_coords; in vec2 tex_coords;
in vec3 barycentric;
in vec3 normal; in vec3 normal;
out vec2 v_tex_coords; out vec2 v_tex_coords;
out vec3 v_normal; out vec3 v_normal;
out vec3 v_barycentric;
out vec4 v_position;
void main() { void main() {
v_normal = normal; v_normal = normal;
v_barycentric = barycentric;
v_tex_coords = vec2(tex_coords.x * texture_size.x, tex_coords.y * texture_size.y); v_tex_coords = vec2(tex_coords.x * texture_size.x, tex_coords.y * texture_size.y);
gl_Position = perspective * view * vec4(vertex, 1.0); gl_Position = perspective * view * vec4(vertex, 1.0);
v_position = gl_Position;
} }

View File

@ -2,20 +2,30 @@
const EPSILON: f64 = 0.001; const EPSILON: f64 = 0.001;
use glium::glutin::dpi::{PhysicalPosition, PhysicalSize}; use glium::glutin::{
use glium::glutin::event::{ Event,
ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent, WindowEvent,
ElementState,
MouseButton,
MouseScrollDelta,
KeyboardInput,
VirtualKeyCode,
};
use glium::glutin::dpi::{
LogicalSize,
LogicalPosition,
}; };
use camera::Camera;
use math::vector::{Vector2, Vector3}; use math::vector::{Vector2, Vector3};
use model::Model; use camera::Camera;
use renderer::Renderer; use renderer::Renderer;
use model::Model;
/// The trait that all controls should implement. /// The trait that all controls should implement.
pub trait Controls { pub trait Controls {
/// Modifies the camera depending on the event. /// Modifies the camera depending on the event.
fn manage_event(&mut self, event: &Event<()>, camera: &mut Camera, renderer: &Renderer); fn manage_event(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer);
/// Updates the camera depending on time. /// Updates the camera depending on time.
fn update(&mut self, camera: &mut Camera, renderer: &Renderer); fn update(&mut self, camera: &mut Camera, renderer: &Renderer);
@ -48,6 +58,7 @@ pub struct OrbitControls {
} }
impl OrbitControls { impl OrbitControls {
/// Creates a new orbit controls, and initializes the camera. /// Creates a new orbit controls, and initializes the camera.
pub fn new(center: Vector3<f64>, distance: f64, camera: &mut Camera) -> OrbitControls { pub fn new(center: Vector3<f64>, distance: f64, camera: &mut Camera) -> OrbitControls {
let controls = OrbitControls { let controls = OrbitControls {
@ -79,40 +90,35 @@ impl OrbitControls {
OrbitControls::new( OrbitControls::new(
Vector3::new(center.x() as f64, center.y() as f64, center.z() as f64), Vector3::new(center.x() as f64, center.y() as f64, center.z() as f64),
distance as f64, distance as f64,
camera, camera
) )
} }
} }
impl Controls for OrbitControls { impl Controls for OrbitControls {
fn manage_event(&mut self, event: &Event<()>, camera: &mut Camera, _: &Renderer) { fn manage_event(&mut self, event: &Event, camera: &mut Camera, _: &Renderer) {
match *event { match *event {
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::MouseInput {
WindowEvent::MouseInput {
button: MouseButton::Left, button: MouseButton::Left,
state, state, ..
.. }, ..
},
..
} => { } => {
self.pressed = state == ElementState::Pressed; self.pressed = state == ElementState::Pressed;
} },
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::Resized(PhysicalSize { width, height }), event: WindowEvent::Resized(LogicalSize { width, height }), ..
..
} => { } => {
camera.aspect_ratio = width as f64 / height as f64; camera.aspect_ratio = width as f64 / height as f64;
} },
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::MouseWheel {
WindowEvent::MouseWheel { delta: MouseScrollDelta::LineDelta(_, y), ..
delta: MouseScrollDelta::LineDelta(_, y), }, ..
..
},
..
} => { } => {
self.distance -= y as f64 / self.sensitivity; self.distance -= y as f64 / self.sensitivity;
@ -122,15 +128,12 @@ impl Controls for OrbitControls {
camera.position += self.center; camera.position += self.center;
camera.target = self.center; camera.target = self.center;
} },
Event::WindowEvent{ Event::WindowEvent{
event: event: WindowEvent::CursorMoved {
WindowEvent::CursorMoved { position: LogicalPosition { x, y }, ..
position: PhysicalPosition { x, y }, }, ..
..
},
..
} => { } => {
let current_position = Vector2::new(x as f64, y as f64); let current_position = Vector2::new(x as f64, y as f64);
@ -150,21 +153,25 @@ impl Controls for OrbitControls {
camera.position += self.center; camera.position += self.center;
camera.target = self.center; camera.target = self.center;
} }
// Record new position // Record new position
self.mouse_position = current_position; self.mouse_position = current_position;
} },
_ => (), _ => (),
} }
} }
fn update(&mut self, _: &mut Camera, _: &Renderer) {} fn update(&mut self, _: &mut Camera, _: &Renderer) {
}
} }
/// First person controls, just like in video games. /// First person controls, just like in video games.
pub struct FirstPersonControls { pub struct FirstPersonControls {
/// Theta angle of the spheric coordinates of the direction of the camera. /// Theta angle of the spheric coordinates of the direction of the camera.
theta: f64, theta: f64,
@ -229,111 +236,80 @@ impl FirstPersonControls {
} }
impl Controls for FirstPersonControls { impl Controls for FirstPersonControls {
fn manage_event(&mut self, event: &Event<()>, camera: &mut Camera, renderer: &Renderer) {
fn manage_event(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer) {
match *event { match *event {
// On resize window // On resize window
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::Resized(PhysicalSize { width, height }), event: WindowEvent::Resized(LogicalSize { width, height } ), ..
..
} => { } => {
camera.aspect_ratio = width as f64 / height as f64; camera.aspect_ratio = width as f64 / height as f64;
} },
// On Z pressed // On Z pressed
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: KeyboardInput {
input: virtual_keycode: Some(VirtualKeyCode::Z), state, ..
KeyboardInput { }, ..
virtual_keycode: Some(VirtualKeyCode::Z), }, ..
state,
..
},
..
},
..
} => { } => {
self.forward_pressed = state == ElementState::Pressed; self.forward_pressed = state == ElementState::Pressed;
} },
// On S pressed // On S pressed
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: KeyboardInput {
input: virtual_keycode: Some(VirtualKeyCode::S), state, ..
KeyboardInput { }, ..
virtual_keycode: Some(VirtualKeyCode::S), }, ..
state,
..
},
..
},
..
} => { } => {
self.backward_pressed = state == ElementState::Pressed; self.backward_pressed = state == ElementState::Pressed;
} },
// On Q pressed // On Q pressed
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: KeyboardInput {
input: virtual_keycode: Some(VirtualKeyCode::Q), state, ..
KeyboardInput { }, ..
virtual_keycode: Some(VirtualKeyCode::Q), }, ..
state,
..
},
..
},
..
} => { } => {
self.left_pressed = state == ElementState::Pressed; self.left_pressed = state == ElementState::Pressed;
} },
// On D pressed // On D pressed
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: KeyboardInput {
input: virtual_keycode: Some(VirtualKeyCode::D), state, ..
KeyboardInput { }, ..
virtual_keycode: Some(VirtualKeyCode::D), }, ..
state,
..
},
..
},
..
} => { } => {
self.right_pressed = state == ElementState::Pressed; self.right_pressed = state == ElementState::Pressed;
} },
// On Space pressed // On Space pressed
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: KeyboardInput {
input: virtual_keycode: Some(VirtualKeyCode::Space), state, ..
KeyboardInput { }, ..
virtual_keycode: Some(VirtualKeyCode::Space), }, ..
state,
..
},
..
},
..
} => { } => {
self.boost = state == ElementState::Pressed; self.boost = state == ElementState::Pressed;
} },
// On mouse move // On mouse move
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::CursorMoved {
WindowEvent::CursorMoved { position: LogicalPosition { x, y }, ..
position: PhysicalPosition { x, y }, }, ..
..
},
..
} => { } => {
let size = renderer.gl_window().window().inner_size();
let size = renderer.gl_window().window().get_inner_size().unwrap();
let center = Vector2::new(size.width as f64 / 2.0, size.height as f64 / 2.0); let center = Vector2::new(size.width as f64 / 2.0, size.height as f64 / 2.0);
let current_position = Vector2::new(x as f64, y as f64); let current_position = Vector2::new(x as f64, y as f64);
let difference = (current_position - center) / self.sensitivity; let difference = (current_position - center) / self.sensitivity;
@ -351,29 +327,29 @@ impl Controls for FirstPersonControls {
self.phi.cos() * self.theta.sin(), self.phi.cos() * self.theta.sin(),
); );
self.left = Vector3::new(0.0, 1.0, 0.0) self.left = Vector3::new(0.0, 1.0, 0.0).cross_product(self.forward).normalized();
.cross_product(self.forward)
.normalized();
// Move the cursor back to the center // Move the cursor back to the center
renderer renderer
.gl_window() .gl_window()
.window() .window()
.set_cursor_position(PhysicalPosition::new( .set_cursor_position(LogicalPosition::new(
size.width as f64 / 2.0, size.width / 2.0, size.height / 2.0))
size.height as f64 / 2.0,
))
.unwrap(); .unwrap();
}
},
_ => (), _ => (),
} }
self.update_camera(camera); self.update_camera(camera);
} }
fn update(&mut self, camera: &mut Camera, renderer: &Renderer) { fn update(&mut self, camera: &mut Camera, renderer: &Renderer) {
renderer.gl_window().window().set_cursor_visible(false);
renderer.gl_window().hide_cursor(true);
let mut speed = Vector3::new(0.0, 0.0, 0.0); let mut speed = Vector3::new(0.0, 0.0, 0.0);

View File

@ -26,10 +26,11 @@ pub struct Vertex {
vertex: [f64; 3], vertex: [f64; 3],
tex_coords: [f64; 2], tex_coords: [f64; 2],
normal: [f64; 3], normal: [f64; 3],
barycentric: [f64; 3],
face_color: [f64; 3], face_color: [f64; 3],
} }
implement_vertex!(Vertex, vertex, tex_coords, normal, face_color); implement_vertex!(Vertex, vertex, tex_coords, normal, barycentric, face_color);
/// A part of a 3D model. /// A part of a 3D model.
/// ///
@ -534,7 +535,12 @@ impl Model {
let mut vertex_buffer = vec![]; let mut vertex_buffer = vec![];
for face in part.faces() { for face in part.faces() {
for &&v in &[&face.a, &face.b, &face.c] { let v0 = vertices[face.a.vertex];
let v1 = vertices[face.b.vertex];
let v2 = vertices[face.c.vertex];
let barycenter = (v0 + v1 + v2) / 3.0;
for (index, &&v) in [&face.a, &face.b, &face.c].iter().enumerate() {
let vertex = vertices[v.vertex].into(); let vertex = vertices[v.vertex].into();
let tex_coord = if let Some(tex_index) = v.texture_coordinate { let tex_coord = if let Some(tex_index) = v.texture_coordinate {
texture_coordinates[tex_index].into() texture_coordinates[tex_index].into()
@ -542,6 +548,14 @@ impl Model {
[0.0, 0.0] [0.0, 0.0]
}; };
let barycentric = match index {
0 => [1.0, 0.0, 0.0],
1 => [0.0, 1.0, 0.0],
2 => [0.0, 0.0, 1.0],
_ => unreachable!(),
};
let normal = if let Some(normal_index) = v.normal { let normal = if let Some(normal_index) = v.normal {
normals[normal_index].into() normals[normal_index].into()
} else { } else {
@ -567,6 +581,7 @@ impl Model {
vertex: vertex, vertex: vertex,
tex_coords: tex_coord, tex_coords: tex_coord,
normal: normal, normal: normal,
barycentric: barycentric,
face_color: [r, g, b], face_color: [r, g, b],
}); });
} }

View File

@ -6,33 +6,37 @@ extern crate serde_derive;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate stderrlog;
extern crate clap; extern crate clap;
extern crate glium; extern crate glium;
extern crate model_converter; extern crate model_converter;
extern crate stderrlog;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::process::exit; use std::process::exit;
use std::time::{Instant, Duration};
use std::thread::sleep; use std::thread::sleep;
use std::time::{Duration, Instant};
use clap::{App, Arg}; use clap::{App, Arg};
use glium::glutin;
use glium::glutin::event_loop::EventLoop;
use glium::glutin::window::WindowBuilder;
use glium::Display; use glium::Display;
use glium::glutin;
use glium::glutin::{EventsLoop, WindowBuilder};
use glium::glutin::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use glium::glutin::{
Event,
WindowEvent,
VirtualKeyCode,
ElementState,
};
use model_converter::camera::Camera; use model_converter::scene::Scene;
use model_converter::controls::{FirstPersonControls, OrbitControls};
use model_converter::math::bounding_box::BoundingBox3; use model_converter::math::bounding_box::BoundingBox3;
use model_converter::math::vector::Vector3; use model_converter::math::vector::Vector3;
use model_converter::parser::parse_file; use model_converter::parser::parse_file;
use model_converter::renderer::Renderer; use model_converter::renderer::Renderer;
use model_converter::scene::Scene; use model_converter::controls::{OrbitControls, FirstPersonControls};
use model_converter::camera::Camera;
fn as_millis(duration: Duration) -> u64 { fn as_millis(duration: Duration) -> u64 {
duration.as_secs() * 1_000 + (duration.subsec_nanos() as u64) / 1_000_000 duration.as_secs() * 1_000 + (duration.subsec_nanos() as u64) / 1_000_000
@ -62,31 +66,26 @@ struct CameraEvent {
} }
fn main() { fn main() {
let matches = App::new("3D Viewer") let matches = App::new("3D Viewer")
.version("1.0") .version("1.0")
.arg( .arg(Arg::with_name("input")
Arg::with_name("input")
.short("i") .short("i")
.long("input") .long("input")
.value_name("FILES") .value_name("FILES")
.takes_value(true) .takes_value(true)
.multiple(true) .multiple(true)
.help("Input model files") .help("Input model files")
.required(true), .required(true))
) .arg(Arg::with_name("first person")
.arg(
Arg::with_name("first person")
.short("f") .short("f")
.long("first-person") .long("first-person")
.help("Uses first person controls instead of orbit controls"), .help("Uses first person controls instead of orbit controls"))
) .arg(Arg::with_name("verbose")
.arg(
Arg::with_name("verbose")
.short("v") .short("v")
.long("verbose") .long("verbose")
.multiple(true) .multiple(true)
.help("Shows logs during the parsing of the model"), .help("Shows logs during the parsing of the model"))
)
.get_matches(); .get_matches();
// Set verbose flag // Set verbose flag
@ -100,12 +99,16 @@ fn main() {
let mut path_count = 0; let mut path_count = 0;
let mut path = vec![]; let mut path = vec![];
use std::f64::{MAX, MIN}; use std::f64::{MIN, MAX};
let mut bbox = BoundingBox3::new(Vector3::new(MAX, MAX, MAX), Vector3::new(MIN, MIN, MIN)); let mut bbox = BoundingBox3::new(
Vector3::new(MAX, MAX, MAX),
Vector3::new(MIN, MIN, MIN),
);
let mut models = vec![]; let mut models = vec![];
for input in matches.values_of("input").unwrap() { for input in matches.values_of("input").unwrap() {
info!("Parsing model {}", input); info!("Parsing model {}", input);
match parse_file(&input) { match parse_file(&input) {
@ -114,20 +117,21 @@ fn main() {
bbox = bbox.union(&model.bounding_box()); bbox = bbox.union(&model.bounding_box());
} }
models.push((input.to_owned(), model)) models.push((input.to_owned(), model))
} },
Err(e) => { Err(e) => {
error!("Error while parsing file: {}", e); error!("Error while parsing file: {}", e);
exit(1); exit(1);
} },
} }
} }
let event_loop = EventLoop::new(); let mut events_loop = EventsLoop::new();
let window = WindowBuilder::new().with_visible(false); let window = WindowBuilder::new().with_visibility(false);
let context = glutin::ContextBuilder::new().with_depth_buffer(24); let context = glutin::ContextBuilder::new().with_depth_buffer(24);
let display = Display::new(window, context, &event_loop).unwrap(); let display = Display::new(window, context, &events_loop).unwrap();
let mut renderer = Renderer::new(display); let mut renderer = Renderer::new(display);
renderer.set_clear_color(1.0, 1.0, 1.0, 1.0);
let mut scene = Scene::new(); let mut scene = Scene::new();
let mut before; let mut before;
@ -160,6 +164,8 @@ fn main() {
let center_f64 = Vector3::new(center.x() as f64, center.y() as f64, center.z() as f64); let center_f64 = Vector3::new(center.x() as f64, center.y() as f64, center.z() as f64);
let size_f64 = size as f64; let size_f64 = size as f64;
let mut closed = false;
let mut camera = Camera::new( let mut camera = Camera::new(
Vector3::new( 0.0, 0.0, 0.0), Vector3::new( 0.0, 0.0, 0.0),
Vector3::new( 0.0, 0.0, 0.0), Vector3::new( 0.0, 0.0, 0.0),
@ -168,13 +174,13 @@ fn main() {
camera.z_near = 0.0001; camera.z_near = 0.0001;
let mut controls: Box<dyn Controls> = if matches.is_present("first person") { let mut controls: Box<Controls> = if matches.is_present("first person") {
Box::new(FirstPersonControls::new()) Box::new(FirstPersonControls::new())
} else { } else {
Box::new(OrbitControls::new( Box::new(OrbitControls::new(
Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 0.0),
1.0, 1.0,
&mut camera, &mut camera
)) ))
}; };
@ -185,52 +191,38 @@ fn main() {
let mut recording = false; let mut recording = false;
let mut before = Instant::now(); let mut before = Instant::now();
event_loop.run(move |ev, _, control_flow| { while !closed {
let mut should_screenshot = false; let mut should_screenshot = false;
events_loop.poll_events(|ev| {
controls.manage_event(&ev, &mut camera, &renderer); controls.manage_event(&ev, &mut camera, &renderer);
match ev { match ev {
// No idea what this is
Event::NewEvents(cause) => match cause {
glium::glutin::event::StartCause::ResumeTimeReached { .. } => (),
glium::glutin::event::StartCause::Init => (),
_ => return,
},
// Close window // Close window
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested, ..
.. } => closed = true,
} => *control_flow = glutin::event_loop::ControlFlow::Exit,
// Escape key // Escape key
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: glutin::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
state: ElementState::Pressed, state: ElementState::Pressed, ..
.. }, ..
}, }, ..
.. } => closed = true,
},
..
} => *control_flow = glutin::event_loop::ControlFlow::Exit,
// R key // R key
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: glutin::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::R), virtual_keycode: Some(VirtualKeyCode::R),
state: ElementState::Pressed, state: ElementState::Pressed, ..
.. }, ..
}, }, ..
..
},
..
} => { } => {
if ! recording { if ! recording {
path.clear(); path.clear();
@ -242,63 +234,43 @@ fn main() {
file.write_all(string.as_bytes()).unwrap(); file.write_all(string.as_bytes()).unwrap();
path_count += 1; path_count += 1;
} }
} },
// Enter key // Enter key
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: glutin::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Return), virtual_keycode: Some(VirtualKeyCode::Return),
state: ElementState::Pressed, state: ElementState::Pressed, ..
.. }, ..
}, }, ..
..
},
..
} => { } => {
trace!("Camera:"); trace!("Camera:");
let world_position = camera.position * size_f64 + center_f64; let world_position = camera.position * size_f64 + center_f64;
let world_target = camera.target * size_f64 + center_f64; let world_target = camera.target * size_f64 + center_f64;
trace!( trace!("\tPosition: ({}, {}, {})",
"\tPosition: ({}, {}, {})", world_position.x(), world_position.y(), world_position.z());
world_position.x(), trace!("\tTarget: ({}, {}, {})",
world_position.y(), world_target.x(), world_target.y(), world_target.z());
world_position.z() trace!("\tUp: ({}, {}, {})",
); camera.up.x(), camera.up.y(), camera.up.z());
trace!( },
"\tTarget: ({}, {}, {})",
world_target.x(),
world_target.y(),
world_target.z()
);
trace!(
"\tUp: ({}, {}, {})",
camera.up.x(),
camera.up.y(),
camera.up.z()
);
}
Event::WindowEvent { Event::WindowEvent {
event: event: WindowEvent::KeyboardInput {
WindowEvent::KeyboardInput { input: glutin::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::C), virtual_keycode: Some(VirtualKeyCode::C),
state: ElementState::Pressed, state: ElementState::Pressed, ..
.. }, ..
}, }, ..
..
},
..
} => should_screenshot = true, } => should_screenshot = true,
_ => (), _ => (),
} }
});
controls.update(&mut camera, &renderer); controls.update(&mut camera, &renderer);
renderer.render(&scene, &camera); renderer.render(&scene, &camera);
@ -327,5 +299,5 @@ fn main() {
} }
before = Instant::now(); before = Instant::now();
}); }
} }

View File

@ -1,24 +1,23 @@
//! This module contains the rendering structs. //! This module contains the rendering structs.
use std::borrow::Cow;
use std::cell::Ref; use std::cell::Ref;
use std::ops::Deref; use std::borrow::Cow;
use image; use image;
use image::{DynamicImage, ImageBuffer, Rgba}; use image::{ImageBuffer, Rgba, DynamicImage};
use glium::draw_parameters::{Blend, DepthTest};
use glium::glutin::PossiblyCurrent as Pc;
use glium::index::{NoIndices, PrimitiveType};
use glium::program::ProgramCreationInput;
use glium::texture::{RawImage2d, SrgbTexture2d, Texture2dDataSink}; use glium::texture::{RawImage2d, SrgbTexture2d, Texture2dDataSink};
use glium::{Depth, Display, DrawParameters, Frame, Program, Surface, VertexBuffer}; 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 glium::program::ProgramCreationInput;
use camera::{mat_to_f32, RenderCamera};
use scene::Scene; use scene::Scene;
use camera::{RenderCamera, mat_to_f32};
use model::{Vertex, Part, Model};
use math::vector::Vector3; use math::vector::Vector3;
use model::{Model, Part, Vertex};
/// Image data stored as RGBA. /// Image data stored as RGBA.
pub struct RgbaImageData { pub struct RgbaImageData {
@ -58,11 +57,10 @@ pub struct Renderer {
} }
impl Renderer { impl Renderer {
/// Creates the program with the default shader. /// Creates the program with the default shader.
pub fn default_shader(display: &Display) -> Program { pub fn default_shader(display: &Display) -> Program {
Program::new( Program::new(display, ProgramCreationInput::SourceCode {
display,
ProgramCreationInput::SourceCode {
vertex_shader: include_str!("../assets/shaders/default.vert"), vertex_shader: include_str!("../assets/shaders/default.vert"),
fragment_shader: include_str!("../assets/shaders/default.frag"), fragment_shader: include_str!("../assets/shaders/default.frag"),
geometry_shader: None, geometry_shader: None,
@ -71,16 +69,12 @@ impl Renderer {
transform_feedback_varyings: None, transform_feedback_varyings: None,
outputs_srgb: false, outputs_srgb: false,
uses_point_size: false, uses_point_size: false,
}, }).unwrap()
)
.unwrap()
} }
/// Creates the shader with one color per face. /// Creates the shader with one color per face.
pub fn color_shader(display: &Display) -> Program { pub fn color_shader(display: &Display) -> Program {
Program::new( Program::new(display, ProgramCreationInput::SourceCode {
display,
ProgramCreationInput::SourceCode {
vertex_shader: include_str!("../assets/shaders/color.vert"), vertex_shader: include_str!("../assets/shaders/color.vert"),
fragment_shader: include_str!("../assets/shaders/color.frag"), fragment_shader: include_str!("../assets/shaders/color.frag"),
geometry_shader: None, geometry_shader: None,
@ -89,31 +83,35 @@ impl Renderer {
transform_feedback_varyings: None, transform_feedback_varyings: None,
outputs_srgb: true, outputs_srgb: true,
uses_point_size: false, uses_point_size: false,
}, }).unwrap()
)
.unwrap()
} }
/// Creates a new renderer from a display. /// Creates a new renderer from a display.
/// ///
/// Is uses the default shaders and creates an empty vec of models. /// Is uses the default shaders and creates an empty vec of models.
pub fn new(display: Display) -> Renderer { pub fn new(display: Display) -> Renderer {
let program = Renderer::default_shader(&display); let program = Renderer::default_shader(&display);
Renderer::from_display_and_program(display, program) Renderer::from_display_and_program(display, program)
} }
/// Creates a new colored renderer from a display. /// Creates a new colored renderer from a display.
/// ///
/// Is uses the face colors shaders and creates an empty vec of models. /// Is uses the face colors shaders and creates an empty vec of models.
pub fn color(display: Display) -> Renderer { pub fn color(display: Display) -> Renderer {
let program = Renderer::color_shader(&display); let program = Renderer::color_shader(&display);
Renderer::from_display_and_program(display, program) Renderer::from_display_and_program(display, program)
} }
/// Creates a new renderer from a program. /// Creates a new renderer from a program.
/// ///
/// It allows you to use a custom shader. /// It allows you to use a custom shader.
pub fn from_display_and_program(display: Display, program: Program) -> Renderer { pub fn from_display_and_program(display: Display, program: Program) -> Renderer {
let image = RawImage2d::from_raw_rgba(vec![1.0, 1.0, 1.0, 1.0], (1, 1)); 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(); let texture = SrgbTexture2d::new(&display, image).ok().unwrap();
@ -128,17 +126,18 @@ impl Renderer {
renderer.capture(); renderer.capture();
renderer renderer
} }
/// Returns the inner GlWindows. /// Returns the inner GlWindows.
pub fn gl_window(&self) -> Ref<'_, impl Deref<Target = glium::glutin::WindowedContext<Pc>>> { pub fn gl_window(&self) -> Ref<GlWindow> {
self.display.gl_window() self.display.gl_window()
} }
/// 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 = match image::open(path) { let image = match image::open(path) {
Ok(r) => r.to_rgba8(), Ok(r) => r.to_rgba(),
Err(e) => panic!("Error while opening file {}: {}", path, e), Err(e) => panic!("Error while opening file {}: {}", path, e),
}; };
@ -146,23 +145,14 @@ impl Renderer {
} }
/// Creates a SrgbTexture from an image buffer. /// Creates a SrgbTexture from an image buffer.
pub fn make_texture_from_buffer( pub fn make_texture_from_buffer(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>) -> SrgbTexture2d {
&self,
buffer: ImageBuffer<Rgba<u8>, Vec<u8>>,
) -> SrgbTexture2d {
let dimensions = buffer.dimensions(); let dimensions = buffer.dimensions();
let buffer = RawImage2d::from_raw_rgba_reversed(&buffer.into_raw(), dimensions); let buffer = RawImage2d::from_raw_rgba_reversed(&buffer.into_raw(), dimensions);
SrgbTexture2d::new(&self.display, buffer).ok().unwrap() 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.
pub fn make_texture_from_color_channels( pub fn make_texture_from_color_channels(&self, r: f32, g: f32, b: f32, a: f32) -> SrgbTexture2d {
&self,
r: f32,
g: f32,
b: f32,
a: f32,
) -> SrgbTexture2d {
let image = RawImage2d::from_raw_rgba(vec![r, g, b, a], (1, 1)); let image = RawImage2d::from_raw_rgba(vec![r, g, b, a], (1, 1));
SrgbTexture2d::new(&self.display, image).ok().unwrap() SrgbTexture2d::new(&self.display, image).ok().unwrap()
} }
@ -180,6 +170,7 @@ impl Renderer {
/// Renders on the display. /// Renders on the display.
pub fn render<C: RenderCamera>(&self, scene: &Scene, camera: &C) { pub fn render<C: RenderCamera>(&self, scene: &Scene, camera: &C) {
let mut target = self.draw(); let mut target = self.draw();
target.clear_color_srgb_and_depth(self.clear_color, 1.0); target.clear_color_srgb_and_depth(self.clear_color, 1.0);
@ -199,7 +190,9 @@ impl Renderer {
for model in scene.iter() { for model in scene.iter() {
let model = &*model.borrow(); let model = &*model.borrow();
for part in &model.parts { for part in &model.parts {
if let &Some(ref buffer) = part.vertex_buffer() { if let &Some(ref buffer) = part.vertex_buffer() {
let diffuse = if let Some(ref name) = part.material_name { let diffuse = if let Some(ref name) = part.material_name {
if let None = model.materials.get(name) { if let None = model.materials.get(name) {
panic!("Material {} not found", name); panic!("Material {} not found", name);
@ -213,16 +206,12 @@ impl Renderer {
let texture = self.get_texture_of_part(&model, part); let texture = self.get_texture_of_part(&model, part);
let (texture, size) = if let Some((texture, size)) = texture { let (texture, size) = if let Some((texture, size)) = texture {
( (texture, Vector3::new(size[0] as f32, size[1] as f32, size[2] as f32))
texture,
Vector3::new(size[0] as f32, size[1] as f32, size[2] as f32),
)
} else { } else {
(&self.default_texture, Vector3::new(1.0, 1.0, 1.0)) (&self.default_texture, Vector3::new(1.0, 1.0, 1.0))
}; };
target target.draw(
.draw(
buffer, buffer,
NoIndices(PrimitiveType::TrianglesList), NoIndices(PrimitiveType::TrianglesList),
&self.program, &self.program,
@ -234,8 +223,7 @@ impl Renderer {
texture_size: Into::<[f32; 3]>::into(size), texture_size: Into::<[f32; 3]>::into(size),
), ),
&params, &params,
) ).unwrap();
.unwrap();
} }
} }
} }
@ -244,11 +232,7 @@ impl Renderer {
} }
/// Renders a part of a model. /// Renders a part of a model.
fn get_texture_of_part<'a>( fn get_texture_of_part<'a>(&self, model: &'a Model, part: &Part) -> Option<(&'a SrgbTexture2d, Vector3<f64>)> {
&self,
model: &'a Model,
part: &Part,
) -> Option<(&'a SrgbTexture2d, Vector3<f64>)> {
if let Some(ref material_name) = part.material_name { if let Some(ref material_name) = part.material_name {
if let Some(ref material) = model.materials.get(material_name) { if let Some(ref material) = model.materials.get(material_name) {
if let Some((texture, size)) = material.textures.get("map_Kd") { if let Some((texture, size)) = material.textures.get("map_Kd") {
@ -280,21 +264,21 @@ impl Renderer {
/// Shows the window if hidden. /// Shows the window if hidden.
pub fn show(&mut self) { pub fn show(&mut self) {
self.gl_window().window().set_visible(true); self.gl_window().show();
} }
/// Returns a DynamicImage of the corresponding frame. /// Returns a DynamicImage of the corresponding frame.
pub fn capture(&self) -> DynamicImage { pub fn capture(&self) -> DynamicImage {
// Create temporary texture and blit the front buffer to it // Create temporary texture and blit the front buffer to it
let image: RawImage2d<u8> = self.display.read_front_buffer().unwrap(); let image: RawImage2d<u8> = self.display.read_front_buffer();
let image = let image = ImageBuffer::from_raw(image.width, image.height, image.data.into_owned()).unwrap();
ImageBuffer::from_raw(image.width, image.height, image.data.into_owned()).unwrap();
DynamicImage::ImageRgba8(image).flipv() DynamicImage::ImageRgba8(image).flipv()
} }
} }
/// Converts a RgbaImamgeData to a DynamicImage. /// Converts a RgbaImamgeData to a DynamicImage.
pub fn rgba_image_data_to_image(image_data: RgbaImageData) -> image::DynamicImage { pub fn rgba_image_data_to_image(image_data: RgbaImageData) -> image::DynamicImage {
let pixels = { let pixels = {
let mut v = Vec::with_capacity(image_data.data.len() * 4); let mut v = Vec::with_capacity(image_data.data.len() * 4);
for (a, b, c, d) in image_data.data { for (a, b, c, d) in image_data.data {
@ -306,12 +290,15 @@ pub fn rgba_image_data_to_image(image_data: RgbaImageData) -> image::DynamicImag
v v
}; };
// Create ImageBuffer // Create ImageBuffer
let image_buffer = let image_buffer =
image::ImageBuffer::from_raw(image_data.width, image_data.height, pixels).unwrap(); image::ImageBuffer::from_raw(image_data.width, image_data.height, pixels)
.unwrap();
// Save the screenshot to file // Save the screenshot to file
let image = image::DynamicImage::ImageRgba8(image_buffer).flipv(); let image = image::DynamicImage::ImageRgba8(image_buffer).flipv();
image image
} }