use geometric_algebra::{
ppga3d::{Rotor, Translator},
GeometricProduct, One, Signum, Transformation,
};
use splatter::{
renderer::{Configuration, DepthSorting, Renderer},
scene::Scene,
};
use std::{collections::HashSet, env, fs::File};
mod application_framework;
const LOAD_CHUNK_SIZE: usize = 1024 * 32;
fn rotate_around_axis(angle: f32, axis: &[f32; 3]) -> Rotor {
let sinus = (angle * 0.5).sin();
Rotor::new((angle * 0.5).cos(), axis[0] * sinus, axis[1] * sinus, axis[2] * sinus)
}
struct Application {
renderer: Renderer,
scene: Scene,
file: File,
file_header_size: u16,
chunks_left_to_load: usize,
depth_stencil_texture_view: Option<wgpu::TextureView>,
viewport_size: wgpu::Extent3d,
camera_rotation: Rotor,
camera_translation: Translator,
pressed_keys: HashSet<winit::event::VirtualKeyCode>,
}
impl application_framework::Application for Application {
fn new(device: &wgpu::Device, _queue: &mut wgpu::Queue, surface_configuration: &wgpu::SurfaceConfiguration) -> Self {
let file = File::open(env::args().nth(1).unwrap()).unwrap();
let renderer = Renderer::new(
device,
Configuration {
surface_configuration: surface_configuration.clone(),
depth_sorting: DepthSorting::Gpu,
use_unaligned_rectangles: true,
spherical_harmonics_order: 2,
max_splat_count: 1024 * 1024 * 4,
radix_bits_per_digit: 8,
frustum_culling_tolerance: 1.1,
ellipse_margin: 2.0,
splat_scale: 1.0,
},
);
let (file_header_size, splat_count, file) = Scene::parse_file_header(file);
let scene = Scene::new(device, &renderer, splat_count);
let chunks_left_to_load = (scene.splat_count + LOAD_CHUNK_SIZE - 1) / LOAD_CHUNK_SIZE;
Self {
renderer,
scene,
file,
file_header_size,
chunks_left_to_load,
depth_stencil_texture_view: None,
viewport_size: wgpu::Extent3d::default(),
camera_rotation: Rotor::one(),
camera_translation: Translator::one(),
pressed_keys: HashSet::new(),
}
}
fn resize(&mut self, device: &wgpu::Device, _queue: &mut wgpu::Queue, surface_configuration: &wgpu::SurfaceConfiguration) {
self.viewport_size = wgpu::Extent3d {
width: surface_configuration.width,
height: surface_configuration.height,
depth_or_array_layers: 1,
};
let depth_stencil_texture_descriptor = wgpu::TextureDescriptor {
size: self.viewport_size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth24PlusStencil8,
view_formats: &[wgpu::TextureFormat::Depth24PlusStencil8],
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
label: None,
};
let depth_stencil_texture = device.create_texture(&depth_stencil_texture_descriptor);
self.depth_stencil_texture_view = Some(depth_stencil_texture.create_view(&wgpu::TextureViewDescriptor {
dimension: Some(wgpu::TextureViewDimension::D2),
..wgpu::TextureViewDescriptor::default()
}));
}
fn render(&mut self, device: &wgpu::Device, queue: &mut wgpu::Queue, frame: &wgpu::SurfaceTexture, frame_time: f32) {
if self.chunks_left_to_load > 0 {
self.chunks_left_to_load -= 1;
let load_range = self.chunks_left_to_load * LOAD_CHUNK_SIZE..(self.chunks_left_to_load + 1) * LOAD_CHUNK_SIZE;
self.scene.load_chunk(queue, &mut self.file, self.file_header_size, load_range);
queue.submit([]);
}
for keycode in &self.pressed_keys {
let speed = frame_time * 2.0;
match keycode {
winit::event::VirtualKeyCode::A => {
self.camera_translation += self.camera_rotation.transformation(Translator::new(0.0, speed, 0.0, 0.0));
}
winit::event::VirtualKeyCode::D => {
self.camera_translation += self.camera_rotation.transformation(Translator::new(0.0, -speed, 0.0, 0.0));
}
winit::event::VirtualKeyCode::W => {
self.camera_translation += self.camera_rotation.transformation(Translator::new(0.0, 0.0, 0.0, -speed));
}
winit::event::VirtualKeyCode::S => {
self.camera_translation += self.camera_rotation.transformation(Translator::new(0.0, 0.0, 0.0, speed));
}
winit::event::VirtualKeyCode::Q => {
self.camera_translation += self.camera_rotation.transformation(Translator::new(0.0, 0.0, -speed, 0.0));
}
winit::event::VirtualKeyCode::E => {
self.camera_translation += self.camera_rotation.transformation(Translator::new(0.0, 0.0, speed, 0.0));
}
winit::event::VirtualKeyCode::Z => {
self.camera_rotation = self
.camera_rotation
.geometric_product(rotate_around_axis(-0.5 * speed, &[0.0, 0.0, 1.0]))
.signum();
}
winit::event::VirtualKeyCode::X => {
self.camera_rotation = self
.camera_rotation
.geometric_product(rotate_around_axis(0.5 * speed, &[0.0, 0.0, 1.0]))
.signum();
}
_ => {}
}
}
let camera_motor = self.camera_translation.geometric_product(self.camera_rotation);
let frame_view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
self.renderer
.render_frame(device, queue, &frame_view, self.viewport_size, camera_motor, &self.scene);
}
fn mouse_motion(&mut self, delta: (f64, f64)) {
let position = [
-1.0 * std::f32::consts::PI * (delta.0 as f32 / self.viewport_size.width as f32),
-1.0 * std::f32::consts::PI * (delta.1 as f32 / self.viewport_size.height as f32),
];
self.camera_rotation = self
.camera_rotation
.geometric_product(rotate_around_axis(position[1], &[1.0, 0.0, 0.0]))
.geometric_product(rotate_around_axis(position[0], &[0.0, 1.0, 0.0]))
.signum();
}
fn keyboard_input(&mut self, input: winit::event::KeyboardInput) {
let keycode = if let Some(keycode) = input.virtual_keycode {
keycode
} else {
return;
};
match input.state {
winit::event::ElementState::Pressed => {
self.pressed_keys.insert(keycode);
}
winit::event::ElementState::Released => {
self.pressed_keys.remove(&keycode);
}
}
}
}
fn main() {
application_framework::ApplicationManager::run::<Application>("Splatter Renderer");
}