extern crate serde; extern crate serde_json; #[macro_use] extern crate serde_derive; #[macro_use] extern crate log; extern crate stderrlog; extern crate clap; extern crate glium; extern crate model_converter; use std::fs::File; use std::io::Write; use std::process::exit; use std::time::{Instant, Duration}; use std::thread::sleep; use clap::{App, Arg}; use glium::Display; use glium::glutin; use glium::glutin::{EventsLoop, WindowBuilder}; use glium::glutin::{ Event, WindowEvent, VirtualKeyCode, ElementState, }; use model_converter::scene::Scene; use model_converter::math::bounding_box::BoundingBox3; use model_converter::math::vector::Vector3; use model_converter::parser::parse_file; use model_converter::renderer::Renderer; use model_converter::controls::{OrbitControls, 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 } #[derive(Serialize, Deserialize)] struct Vector { x: f64, y: f64, z: f64, } impl From> for Vector { fn from(v: Vector3) -> Vector { Vector { x: v[0], y: v[1], z: v[2], } } } #[derive(Serialize, Deserialize)] struct CameraEvent { position: Vector, target: Vector, } fn main() { 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") .help("Uses first person controls instead of orbit controls")) .arg(Arg::with_name("verbose") .short("v") .long("verbose") .multiple(true) .help("Shows logs during the parsing of the model")) .get_matches(); // Set verbose flag stderrlog::new() .module(module_path!()) .verbosity(matches.occurrences_of("verbose") as usize) .init() .expect("Couldn't initialize logger"); let mut capture_count = 0; let mut path_count = 0; let mut path = vec![]; 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() { info!("Parsing model {}", input); match parse_file(&input) { Ok(model) => { if model.vertices.len() > 0 { bbox = bbox.union(&model.bounding_box()); } models.push((input.to_owned(), model)) }, Err(e) => { error!("Error while parsing file: {}", e); exit(1); }, } } 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 scene = Scene::new(); let mut before; let mut duration; for (name, mut model) in models { info!("Scaling model {}...", name); model.center_and_scale_from_box(&bbox); info!("Building textures for model {}...", name); before = Instant::now(); model.build_textures(&renderer); duration = Instant::now().duration_since(before); info!("Done in {}ms.", as_millis(duration)); info!("Building vertex buffers for model {}...", name); before = Instant::now(); model.build_vertex_buffers(&renderer); duration = Instant::now().duration_since(before); scene.emplace(model); info!("Done in {}ms.", as_millis(duration)); info!("Finished"); } let center = (bbox.min() + bbox.max()) / 2.0; let size = (bbox.max() - bbox.min()).norm(); let center_f64 = Vector3::new(center.x() as f64, center.y() as f64, center.z() as f64); let size_f64 = size as f64; let mut closed = false; 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, 1.0, 0.0), ); camera.z_near = 0.0001; let mut controls: Box = if matches.is_present("first person") { 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::controls::Controls; let mut recording = false; let mut before = Instant::now(); while !closed { let mut should_screenshot = false; events_loop.poll_events(|ev| { controls.manage_event(&ev, &mut camera, &renderer); match ev { // Close window Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => closed = true, // Escape key Event::WindowEvent { event: WindowEvent::KeyboardInput { input: glutin::KeyboardInput { virtual_keycode: Some(VirtualKeyCode::Escape), state: ElementState::Pressed, .. }, .. }, .. } => closed = true, // R key Event::WindowEvent { event: WindowEvent::KeyboardInput { input: glutin::KeyboardInput { virtual_keycode: Some(VirtualKeyCode::R), state: ElementState::Pressed, .. }, .. }, .. } => { if ! recording { path.clear(); recording = true; } else { recording = false; let string = serde_json::to_string(&path).unwrap(); let mut file = File::create(format!("path-{}.json", path_count)).unwrap(); file.write_all(string.as_bytes()).unwrap(); path_count += 1; } }, // Enter key Event::WindowEvent { event: WindowEvent::KeyboardInput { input: glutin::KeyboardInput { virtual_keycode: Some(VirtualKeyCode::Return), state: ElementState::Pressed, .. }, .. }, .. } => { trace!("Camera:"); let world_position = camera.position * size_f64 + center_f64; let world_target = camera.target * size_f64 + center_f64; trace!("\tPosition: ({}, {}, {})", world_position.x(), world_position.y(), world_position.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::KeyboardInput { input: glutin::KeyboardInput { virtual_keycode: Some(VirtualKeyCode::C), state: ElementState::Pressed, .. }, .. }, .. } => should_screenshot = true, _ => (), } }); controls.update(&mut camera, &renderer); renderer.render(&scene, &camera); if recording { let position = camera.position * size_f64 + center_f64; let target = camera.target * size_f64 + center_f64; path.push(CameraEvent { position: position.into(), target: target.into(), }); } if should_screenshot { // Make a screenshot let cap = renderer.capture(); cap.save(format!("capture-{}.png", capture_count)).unwrap(); capture_count += 1; } let elapsed = as_millis(Instant::now().duration_since(before)); if elapsed < 20 { sleep(Duration::from_millis(20 - elapsed)); } before = Instant::now(); } }