use std::io::Read;
use truck_platform::*;
use truck_polymesh::prelude::*;
use truck_rendimpl::*;
use wgpu::*;
use winit::{dpi::*, event::*, event_loop::ControlFlow};
mod app;
use app::*;
struct MyApp {
scene: Scene,
rotate_flag: bool,
prev_cursor: Vector2,
path: Option<std::path::PathBuf>,
light_changed: Option<std::time::Instant>,
camera_changed: Option<std::time::Instant>,
}
impl MyApp {
fn create_camera() -> Camera {
let matrix = Matrix4::look_at_rh(
Point3::new(1.5, 1.5, 1.5),
Point3::origin(),
Vector3::unit_y(),
);
Camera::perspective_camera(
matrix.invert().unwrap(),
Rad(std::f64::consts::PI / 4.0),
0.1,
40.0,
)
}
fn load_shape<R: Read>(&mut self, reader: R) {
let scene = &mut self.scene;
scene.clear_objects();
let solid = Solid::extract(serde_json::from_reader(reader).unwrap()).unwrap();
let mut bdd_box = BoundingBox::new();
solid.boundaries().iter().flatten().for_each(|face| {
let surface = face.oriented_surface();
bdd_box += surface.roughly_bounding_box();
});
let (size, center) = (bdd_box.size(), bdd_box.center());
let mat = Matrix4::from_translation(center.to_vec()) * Matrix4::from_scale(size);
let inst_desc = ShapeInstanceDescriptor {
instance_state: InstanceState {
matrix: mat.invert().unwrap(),
material: Material {
albedo: Vector4::new(1.0, 1.0, 1.0, 1.0),
reflectance: 0.5,
roughness: 0.1,
ambient_ratio: 0.02,
},
..Default::default()
},
mesh_precision: 0.005,
};
let mut mesh = scene
.instance_creator()
.create_shape_instance(&solid, &inst_desc);
scene.add_object(&mut mesh);
}
}
impl App for MyApp {
fn init(handler: &DeviceHandler, info: AdapterInfo) -> MyApp {
let sample_count = match info.backend {
Backend::Vulkan => 2,
Backend::Dx12 => 2,
_ => 1,
};
let scene_desc = SceneDescriptor {
background: Color::BLACK,
camera: MyApp::create_camera(),
lights: vec![Light {
position: Point3::new(1.0, 1.0, 1.0),
color: Vector3::new(1.0, 1.0, 1.0),
light_type: LightType::Point,
}],
sample_count,
};
let mut app = MyApp {
scene: Scene::new(handler.clone(), &scene_desc),
rotate_flag: false,
prev_cursor: Vector2::zero(),
path: None,
camera_changed: None,
light_changed: None,
};
app.load_shape(include_bytes!("cube.json").as_ref());
app
}
fn app_title<'a>() -> Option<&'a str> { Some("simple shape viewer") }
fn dropped_file(&mut self, path: std::path::PathBuf) -> ControlFlow {
self.path = Some(path);
Self::default_control_flow()
}
fn mouse_input(&mut self, state: ElementState, button: MouseButton) -> ControlFlow {
match button {
MouseButton::Left => {
self.rotate_flag = state == ElementState::Pressed;
}
MouseButton::Right => {
let (light, camera) = {
let desc = self.scene.descriptor_mut();
(&mut desc.lights[0], &desc.camera)
};
match light.light_type {
LightType::Point => {
light.position = camera.position();
}
LightType::Uniform => {
light.position = camera.position();
let strength = light.position.to_vec().magnitude();
light.position /= strength;
}
}
}
_ => {}
}
Self::default_control_flow()
}
fn mouse_wheel(&mut self, delta: MouseScrollDelta, _: TouchPhase) -> ControlFlow {
match delta {
MouseScrollDelta::LineDelta(_, y) => {
let camera = &mut self.scene.descriptor_mut().camera;
let trans_vec = camera.eye_direction() * 0.2 * y as f64;
camera.matrix = Matrix4::from_translation(trans_vec) * camera.matrix;
}
MouseScrollDelta::PixelDelta(_) => {}
};
Self::default_control_flow()
}
fn cursor_moved(&mut self, position: PhysicalPosition<f64>) -> ControlFlow {
let position = Vector2::new(position.x, position.y);
if self.rotate_flag {
let matrix = &mut self.scene.descriptor_mut().camera.matrix;
let dir2d = &position - self.prev_cursor;
if dir2d.so_small() {
return Self::default_control_flow();
}
let mut axis = dir2d[1] * matrix[0].truncate();
axis += dir2d[0] * &matrix[1].truncate();
axis /= axis.magnitude();
let angle = dir2d.magnitude() * 0.01;
let mat = Matrix4::from_axis_angle(axis, Rad(angle));
*matrix = mat.invert().unwrap() * *matrix;
}
self.prev_cursor = position;
Self::default_control_flow()
}
fn keyboard_input(&mut self, input: KeyboardInput, _: bool) -> ControlFlow {
let keycode = match input.virtual_keycode {
Some(keycode) => keycode,
None => return Self::default_control_flow(),
};
match keycode {
VirtualKeyCode::P => {
if let Some(ref instant) = self.camera_changed {
let time = instant.elapsed().as_secs_f64();
if time < 0.2 {
return Self::default_control_flow();
}
}
let camera = &mut self.scene.descriptor_mut().camera;
self.camera_changed = Some(std::time::Instant::now());
*camera = match camera.projection_type() {
ProjectionType::Parallel => Camera::perspective_camera(
camera.matrix,
Rad(std::f64::consts::PI / 4.0),
0.1,
40.0,
),
ProjectionType::Perspective => {
Camera::parallel_camera(camera.matrix, 1.0, 0.1, 40.0)
}
};
}
VirtualKeyCode::L => {
if let Some(ref instant) = self.light_changed {
let time = instant.elapsed().as_secs_f64();
if time < 0.2 {
return Self::default_control_flow();
}
}
let (light, camera) = {
let desc = self.scene.descriptor_mut();
(&mut desc.lights[0], &desc.camera)
};
self.light_changed = Some(std::time::Instant::now());
*light = match light.light_type {
LightType::Point => {
let mut vec = camera.position();
vec /= vec.to_vec().magnitude();
Light {
position: vec,
color: Vector3::new(1.0, 1.0, 1.0),
light_type: LightType::Uniform,
}
}
LightType::Uniform => {
let position = camera.position();
Light {
position,
color: Vector3::new(1.0, 1.0, 1.0),
light_type: LightType::Point,
}
}
};
}
_ => {}
}
Self::default_control_flow()
}
fn update(&mut self, _: &DeviceHandler) {
if let Some(path) = self.path.take() {
let file = std::fs::File::open(path).unwrap();
self.load_shape(file);
}
}
fn render(&mut self, frame: &SwapChainFrame) { self.scene.render_scene(&frame.output.view); }
}
fn main() { MyApp::run(); }