Implemented 3D support and camera

This commit is contained in:
Verox001 2025-05-05 20:07:20 +02:00
parent 03c061839c
commit 08e49e1b22
6 changed files with 76 additions and 13 deletions

View File

@ -36,7 +36,7 @@ pub async fn run() {
sim.step(dt * timewarp as f64); sim.step(dt * timewarp as f64);
} }
thread::sleep(Duration::from_millis(1)); thread::sleep(Duration::from_nanos(1));
} }
}); });

View File

@ -0,0 +1,57 @@
use cgmath::{Matrix4, Point3, Vector3, Deg, perspective, InnerSpace, Rotation, Quaternion, Rotation3, Rad};
#[derive(Debug)]
pub struct Camera {
pub eye: Point3<f32>,
pub target: Point3<f32>,
pub up: Vector3<f32>,
pub fov_y: Deg<f32>,
pub aspect: f32,
pub znear: f32,
pub zfar: f32,
}
impl Camera {
pub fn new(aspect: f32) -> Self {
Self {
eye: Point3::new(0.0, 0.0, 5.0),
target: Point3::new(0.0, 0.0, 0.0),
up: Vector3::unit_y(),
fov_y: Deg(45.0),
aspect,
znear: 0.1,
zfar: 100.0,
}
}
pub fn build_view_projection_matrix(&self) -> Matrix4<f32> {
let view = Matrix4::look_at_rh(self.eye, self.target, self.up);
let proj = perspective(self.fov_y, self.aspect, self.znear, self.zfar);
proj * view
}
pub fn rotate_yaw_pitch(&mut self, yaw: f32, pitch: f32) {
let dir = (self.target - self.eye).normalize();
let horizontal = Vector3::unit_y().cross(dir).normalize();
let vertical = horizontal.cross(dir).normalize();
let pitch_q = Quaternion::from_axis_angle(vertical, Rad(pitch.to_radians()));
let yaw_q = Quaternion::from_axis_angle(Vector3::unit_y(), Rad(yaw.to_radians()));
let rotation = yaw_q * pitch_q;
let rotated = rotation.rotate_vector(dir).normalize();
let distance = (self.target - self.eye).magnitude();
self.eye = self.target - rotated * distance;
}
pub fn zoom(&mut self, amount: f32) {
let dir = (self.target - self.eye).normalize();
self.eye += dir * amount;
}
pub fn set_aspect(&mut self, aspect: f32) {
self.aspect = aspect;
}
}

View File

@ -4,6 +4,7 @@ mod state;
mod render; mod render;
mod application; mod application;
mod input; mod input;
mod camera;
pub use body::Body; pub use body::Body;

View File

@ -1,7 +1,7 @@
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Globals { pub struct Globals {
pub(crate) aspect_ratio: f32, pub view_proj: [[f32; 4]; 4],
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]

View File

@ -17,7 +17,7 @@ struct VSOutput {
}; };
struct Globals { struct Globals {
aspect_ratio: f32, view_proj: mat4x4<f32>,
} }
@group(0) @binding(0) @group(0) @binding(0)
@ -32,14 +32,7 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput {
instance.model_row2, instance.model_row2,
instance.model_row3 instance.model_row3
); );
out.position = globals.view_proj * model * vec4<f32>(vertex.position, 1.0);
let projection = mat4x4<f32>(
vec4<f32>(1.0 / globals.aspect_ratio, 0.0, 0.0, 0.0),
vec4<f32>(0.0, 1.0, 0.0, 0.0),
vec4<f32>(0.0, 0.0, 1.0, 0.0),
vec4<f32>(0.0, 0.0, 0.0, 1.0),
);
out.position = projection * model * vec4<f32>(vertex.position, 1.0);
out.color = instance.color; out.color = instance.color;
return out; return out;
} }

View File

@ -1,12 +1,14 @@
use std::cmp::max; use std::cmp::max;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc}; use std::sync::{Arc};
use cgmath::{perspective, Deg, Matrix4, Point3, Vector3};
use log::info; use log::info;
use pollster::FutureExt; use pollster::FutureExt;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use wgpu::{Adapter, Device, Instance, PresentMode, Queue, Surface, SurfaceCapabilities, SurfaceConfiguration}; use wgpu::{Adapter, Device, Instance, PresentMode, Queue, Surface, SurfaceCapabilities, SurfaceConfiguration};
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
use winit::window::{Window}; use winit::window::{Window};
use crate::camera::Camera;
use crate::render::{create_circle_vertices, Geometry, Globals, InstanceRaw, RenderInstance, SampleCount, Shape, Vertex}; use crate::render::{create_circle_vertices, Geometry, Globals, InstanceRaw, RenderInstance, SampleCount, Shape, Vertex};
pub struct State<'a> { pub struct State<'a> {
@ -27,6 +29,8 @@ pub struct State<'a> {
geometries: HashMap<Shape, Geometry>, geometries: HashMap<Shape, Geometry>,
global_bind_group: wgpu::BindGroup, global_bind_group: wgpu::BindGroup,
global_buffer: wgpu::Buffer, global_buffer: wgpu::Buffer,
camera: Camera,
} }
impl<'a> State<'a> { impl<'a> State<'a> {
@ -44,8 +48,12 @@ impl<'a> State<'a> {
let sample_count = SampleCount(Self::probe_msaa_support(&device, &config)); let sample_count = SampleCount(Self::probe_msaa_support(&device, &config));
info!("MSAA sample count: {}", sample_count.0); info!("MSAA sample count: {}", sample_count.0);
let aspect = config.width as f32 / config.height as f32;
let camera = Camera::new(aspect);
let view_proj = camera.build_view_projection_matrix();
let globals = Globals { let globals = Globals {
aspect_ratio: config.width as f32 / config.height as f32, view_proj: view_proj.into(),
}; };
let global_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let global_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Global Uniform Buffer"), label: Some("Global Uniform Buffer"),
@ -99,6 +107,7 @@ impl<'a> State<'a> {
global_buffer, global_buffer,
instances, instances,
instance_buffer, instance_buffer,
camera,
} }
} }
@ -288,8 +297,11 @@ impl<'a> State<'a> {
self.surface.configure(&self.device, &self.config); self.surface.configure(&self.device, &self.config);
self.camera.set_aspect(self.config.width as f32 / self.config.height as f32);
let view_proj = self.camera.build_view_projection_matrix();
let new_globals = Globals { let new_globals = Globals {
aspect_ratio: self.config.width as f32 / self.config.height as f32, view_proj: view_proj.into(),
}; };
self.queue.write_buffer(&self.global_buffer, 0, bytemuck::cast_slice(&[new_globals])); self.queue.write_buffer(&self.global_buffer, 0, bytemuck::cast_slice(&[new_globals]));