279 lines
9.1 KiB
Rust
279 lines
9.1 KiB
Rust
#![warn(clippy::pedantic)]
|
|
#![allow(clippy::too_many_lines)]
|
|
// we use lots of casts, so explicitly allowing
|
|
#![allow(clippy::cast_possible_truncation)]
|
|
#![allow(clippy::cast_sign_loss)]
|
|
#![allow(clippy::cast_precision_loss)]
|
|
|
|
use d3::quaternion::Quaternion;
|
|
use d3::rgb::Rgb;
|
|
use d3::vector2::Vec2;
|
|
use d3::vector3::{Direction3, Vec3};
|
|
use softbuffer::GraphicsContext;
|
|
use std::collections::HashMap;
|
|
use std::f32::consts::PI;
|
|
use std::thread;
|
|
use std::time::{Duration, Instant};
|
|
use winit::dpi::PhysicalPosition;
|
|
use winit::event::{
|
|
ElementState, Event, KeyboardInput, ModifiersState, MouseButton, VirtualKeyCode, WindowEvent,
|
|
};
|
|
use winit::event_loop::{ControlFlow, EventLoop};
|
|
use winit::window::WindowBuilder;
|
|
|
|
use crate::buffer::GraphicsBuffer;
|
|
|
|
mod buffer;
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
struct Transform {
|
|
pos: Vec3,
|
|
rot: Quaternion,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Camera {
|
|
transform: Transform,
|
|
near_clip: f32,
|
|
far_clip: f32,
|
|
vertical_fov: f32,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Triangle {
|
|
points: [Vec3; 3],
|
|
color: Rgb,
|
|
}
|
|
|
|
struct InputManager {
|
|
keys: HashMap<VirtualKeyCode, bool>,
|
|
buttons: HashMap<MouseButton, bool>,
|
|
modifier_keys: ModifiersState,
|
|
mouse_pos: Vec2,
|
|
mouse_delta: Vec2,
|
|
}
|
|
|
|
impl InputManager {
|
|
fn new() -> Self {
|
|
Self {
|
|
keys: HashMap::new(),
|
|
buttons: HashMap::new(),
|
|
modifier_keys: ModifiersState::empty(),
|
|
mouse_pos: (0, 0).into(),
|
|
mouse_delta: (0, 0).into(),
|
|
}
|
|
}
|
|
|
|
fn handle_keyboard_input(&mut self, ev: KeyboardInput) {
|
|
if let Some(keycode) = ev.virtual_keycode {
|
|
self.keys.insert(
|
|
keycode,
|
|
match ev.state {
|
|
ElementState::Pressed => true,
|
|
ElementState::Released => false,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
fn handle_mouse_movement(&mut self, ev: PhysicalPosition<f64>) {
|
|
let ev = ev.cast::<f32>();
|
|
let new_pos = Vec2::from((ev.x, ev.y));
|
|
|
|
self.mouse_delta += new_pos - self.mouse_pos;
|
|
self.mouse_pos = new_pos;
|
|
}
|
|
|
|
fn handle_modifiers_changed(&mut self, ev: ModifiersState) {
|
|
self.modifier_keys = ev;
|
|
}
|
|
|
|
fn handle_mouse_button(&mut self, ev: (MouseButton, ElementState)) {
|
|
self.buttons.insert(
|
|
ev.0,
|
|
match ev.1 {
|
|
ElementState::Pressed => true,
|
|
ElementState::Released => false,
|
|
},
|
|
);
|
|
}
|
|
|
|
fn key_pressed(&self, key: VirtualKeyCode) -> bool {
|
|
self.keys.get(&key).copied().unwrap_or_default()
|
|
}
|
|
|
|
fn button_pressed(&self, button: MouseButton) -> bool {
|
|
self.buttons.get(&button).copied().unwrap_or_default()
|
|
}
|
|
|
|
fn end_of_frame(&mut self) {
|
|
self.mouse_delta = (0, 0).into();
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let mut camera = Camera {
|
|
transform: Transform {
|
|
pos: Vec3::default(),
|
|
rot: Quaternion::one(),
|
|
},
|
|
near_clip: 0.1,
|
|
far_clip: 10.0,
|
|
vertical_fov: 70.0,
|
|
};
|
|
|
|
macro_rules! tri {
|
|
($a:expr, $b:expr, $c:expr; $col:expr) => {
|
|
Triangle {
|
|
points: [$a.into(), $b.into(), $c.into()],
|
|
color: $col.into(),
|
|
}
|
|
};
|
|
}
|
|
|
|
let tris = vec![
|
|
tri!((1, 1, 5), (1, -1, 5), (-1, -1, 5); Rgb::RED),
|
|
tri!((1, 1, 5), (-1, -1, 5), (-1, 1, 5); Rgb::GREEN),
|
|
tri!((-1, 1, 7), (-1, -1, 7), (1, -1, 7); Rgb::BLUE),
|
|
tri!((-1, 1, 7), (1, -1, 7), (1, 1, 7); Rgb::YELLOW),
|
|
];
|
|
|
|
let event_loop = EventLoop::new();
|
|
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
|
let mut graphics_context = unsafe { GraphicsContext::new(window) }.unwrap();
|
|
let mut graphics_buffer = GraphicsBuffer::new();
|
|
let mut input_manager = InputManager::new();
|
|
|
|
let mut last_frame_start_time = Instant::now();
|
|
|
|
event_loop.run(move |event, _, control_flow| {
|
|
*control_flow = ControlFlow::Poll;
|
|
|
|
match event {
|
|
Event::MainEventsCleared => {
|
|
thread::sleep(
|
|
Duration::from_secs_f32(1.0 / 30.0)
|
|
.saturating_sub(Instant::now() - last_frame_start_time),
|
|
);
|
|
|
|
let start_time = Instant::now();
|
|
let delta_time = (start_time - last_frame_start_time).as_secs_f32();
|
|
last_frame_start_time = start_time;
|
|
|
|
let (width, height) = {
|
|
let size = graphics_context.window().inner_size();
|
|
(size.width as u16, size.height as u16)
|
|
};
|
|
|
|
graphics_buffer
|
|
.resize_and_clear((width.into(), height.into()).into(), 0xbb_bb_bb.into());
|
|
|
|
if input_manager.key_pressed(VirtualKeyCode::A) {
|
|
camera.transform.pos +=
|
|
(Direction3::Left * 2.0 * delta_time).rotate_by(camera.transform.rot);
|
|
}
|
|
if input_manager.key_pressed(VirtualKeyCode::D) {
|
|
camera.transform.pos +=
|
|
(Direction3::Right * 2.0 * delta_time).rotate_by(camera.transform.rot);
|
|
}
|
|
if input_manager.key_pressed(VirtualKeyCode::W) {
|
|
camera.transform.pos +=
|
|
(Direction3::Forward * 2.0 * delta_time).rotate_by(camera.transform.rot);
|
|
}
|
|
if input_manager.key_pressed(VirtualKeyCode::S) {
|
|
camera.transform.pos +=
|
|
(Direction3::Backward * 2.0 * delta_time).rotate_by(camera.transform.rot);
|
|
}
|
|
if input_manager.modifier_keys.shift() {
|
|
camera.transform.pos +=
|
|
(Direction3::Down * 2.0 * delta_time).rotate_by(camera.transform.rot);
|
|
}
|
|
if input_manager.key_pressed(VirtualKeyCode::Space) {
|
|
camera.transform.pos +=
|
|
(Direction3::Up * 2.0 * delta_time).rotate_by(camera.transform.rot);
|
|
}
|
|
if input_manager.key_pressed(VirtualKeyCode::Q) {
|
|
*control_flow = ControlFlow::Exit;
|
|
}
|
|
|
|
if input_manager.button_pressed(MouseButton::Right) {
|
|
camera
|
|
.transform
|
|
.rot
|
|
.compose_in_place(Quaternion::rotation_around(
|
|
Direction3::Up.into(),
|
|
input_manager.mouse_delta.x / 1000.0,
|
|
));
|
|
|
|
camera
|
|
.transform
|
|
.rot
|
|
.compose_in_place(Quaternion::rotation_around(
|
|
Vec3::from(Direction3::Right).rotate_by(camera.transform.rot),
|
|
input_manager.mouse_delta.y / 1000.0,
|
|
));
|
|
}
|
|
|
|
for tri in &tris {
|
|
let points = tri.points.map(|v| transform_point(camera, v));
|
|
|
|
graphics_buffer.draw_line(points[0], points[1], tri.color);
|
|
graphics_buffer.draw_line(points[1], points[2], tri.color);
|
|
graphics_buffer.draw_line(points[2], points[0], tri.color);
|
|
}
|
|
|
|
graphics_context.set_buffer(&graphics_buffer, width, height);
|
|
|
|
// graphics_context
|
|
// .window()
|
|
// .set_cursor_position(LogicalPosition::new(400, 300))
|
|
// .unwrap();
|
|
// graphics_context.window().set_cursor_visible(false);
|
|
|
|
input_manager.end_of_frame();
|
|
}
|
|
Event::WindowEvent { window_id, event }
|
|
if window_id == graphics_context.window().id() =>
|
|
{
|
|
match event {
|
|
WindowEvent::CloseRequested => {
|
|
*control_flow = ControlFlow::Exit;
|
|
}
|
|
WindowEvent::KeyboardInput { input, .. } => {
|
|
input_manager.handle_keyboard_input(input);
|
|
}
|
|
WindowEvent::CursorMoved { position, .. } => {
|
|
input_manager.handle_mouse_movement(position);
|
|
}
|
|
WindowEvent::ModifiersChanged(state) => {
|
|
input_manager.handle_modifiers_changed(state);
|
|
}
|
|
WindowEvent::MouseInput { state, button, .. } => {
|
|
input_manager.handle_mouse_button((button, state));
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn transform_point(camera: Camera, mut point: Vec3) -> Vec3 {
|
|
point -= camera.transform.pos;
|
|
let mut point = point.rotate_by(camera.transform.rot.inv());
|
|
let z_scale = point.z.abs();
|
|
if z_scale < (1.0 / 10000.0) {
|
|
point.x = 0.0;
|
|
point.y = 0.0;
|
|
} else {
|
|
let fov_scale = camera.vertical_fov.to_radians().tan();
|
|
let scale = fov_scale * z_scale;
|
|
point.x /= scale;
|
|
point.y /= scale;
|
|
}
|
|
point.z -= camera.near_clip;
|
|
point.z /= camera.far_clip - camera.near_clip;
|
|
point
|
|
}
|