summaryrefslogtreecommitdiffstats
path: root/src/tacmap/camera.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tacmap/camera.rs')
-rw-r--r--src/tacmap/camera.rs272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/tacmap/camera.rs b/src/tacmap/camera.rs
new file mode 100644
index 0000000..1a2787b
--- /dev/null
+++ b/src/tacmap/camera.rs
@@ -0,0 +1,272 @@
+use std::time::Duration;
+
+use cgmath::{InnerSpace, Point3, Rad, Vector2, Vector3, Vector4, Zero, perspective};
+use winit::{event::ElementState, keyboard::KeyCode};
+
+use crate::{solar_system::BodyId, wgpuctx::WgpuCtx};
+
+pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::from_cols(
+ Vector4::new(1.0, 0.0, 0.0, 0.0),
+ Vector4::new(0.0, 1.0, 0.0, 0.0),
+ Vector4::new(0.0, 0.0, 0.5, 0.0),
+ Vector4::new(0.0, 0.0, 0.5, 1.0)
+);
+
+pub struct Camera
+{
+ position: Point3<f32>,
+ pitch: Rad<f32>,
+ yaw: Rad<f32>,
+ scale: f32,
+
+ target: Option<BodyId>,
+
+ buffer: wgpu::Buffer,
+ staging_buffer: wgpu::Buffer,
+ bindgroup: wgpu::BindGroup
+}
+
+pub struct CameraController
+{
+ position_pos_delta: Vector3<f32>,
+ position_neg_delta: Vector3<f32>,
+ rotation_pos_delta: Vector2<f32>,
+ rotation_neg_delta: Vector2<f32>,
+ scale_delta: Vector2<f32>,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
+pub struct CameraUniform
+{
+ view: [[f32;4];4],
+ proj: [[f32;4];4],
+ scale: f32
+}
+
+pub struct Projection
+{
+ aspect: f32,
+ fovy: Rad<f32>,
+ clip: (f32, f32)
+}
+
+impl Camera
+{
+ pub fn new<
+ V: Into<Point3<f32>>,
+ Y: Into<Rad<f32>>,
+ P: Into<Rad<f32>>
+ >(
+ wgpuctx: &WgpuCtx,
+ position: V,
+ yaw: Y,
+ pitch: P)
+ -> Self {
+ use wgpu::BufferUsages;
+ let buffer = wgpuctx.create_buffer(
+ &wgpu::BufferDescriptor {
+ label: Some("Camera buffer"),
+ size: 144,
+ usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
+ mapped_at_creation: false
+ }
+ );
+
+ let staging_buffer = wgpuctx.create_buffer(
+ &wgpu::wgt::BufferDescriptor {
+ label: Some("Camera staging buffer"),
+ size: 144,
+ usage: BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
+ mapped_at_creation: false
+ }
+ );
+
+ let bind_group_layout = Camera::bindgroup_layout(wgpuctx);
+ let bind_group = wgpuctx.device().create_bind_group(
+ &wgpu::BindGroupDescriptor {
+ label: Some("Camera bind group"),
+ layout: &bind_group_layout,
+ entries: &[
+ wgpu::BindGroupEntry {
+ binding: 0,
+ resource: buffer.as_entire_binding()
+ }
+ ]
+ }
+ );
+ Self {
+ position: position.into(),
+ yaw: yaw.into(),
+ pitch: pitch.into(),
+ scale: 1.0,
+ target: None,
+ buffer: buffer,
+ staging_buffer: staging_buffer,
+ bindgroup: bind_group
+ }
+ }
+
+ pub fn get_scale(&self) -> f32
+ { self.scale }
+
+ pub fn bindgroup_layout(wgpuctx: &WgpuCtx)
+ -> wgpu::BindGroupLayout
+ {
+ wgpuctx.device().create_bind_group_layout(
+ &wgpu::BindGroupLayoutDescriptor {
+ label: Some("Camera bind group layout"),
+ entries: &[
+ wgpu::BindGroupLayoutEntry {
+ binding: 0,
+ visibility: wgpu::ShaderStages::VERTEX,
+ ty: wgpu::BindingType::Buffer {
+ ty: wgpu::BufferBindingType::Uniform,
+ has_dynamic_offset: false,
+ min_binding_size: None
+ },
+ count: None
+ }
+ ]
+ }
+ )
+ }
+
+ pub fn bindgroup(&self)
+ -> &wgpu::BindGroup
+ { &self.bindgroup }
+
+ pub fn stage_changes(
+ &self,
+ encoder: &mut wgpu::CommandEncoder)
+ {
+ let buffer_size = size_of::<CameraUniform>();
+ encoder.copy_buffer_to_buffer(&self.staging_buffer, 0, &self.buffer, 0, Some(buffer_size as u64));
+ }
+
+ pub fn update_buffer(
+ &self,
+ wgpuctx: &WgpuCtx,
+ projection: &Projection)
+ {
+ wgpuctx.queue().write_buffer(&self.staging_buffer, 0, bytemuck::cast_slice(&[self.uniform(projection)]));
+ }
+
+ pub fn view_matrix(
+ &self)
+ -> cgmath::Matrix4<f32> {
+
+ let (yaw_sin, yaw_cos) = self.yaw.0.sin_cos();
+ let (pitch_sin, pitch_cos) = self.pitch.0.sin_cos();
+
+ cgmath::Matrix4::look_to_rh(
+ self.position,
+ Vector3::new(
+ pitch_cos * yaw_cos,
+ pitch_sin,
+ pitch_cos * yaw_sin
+ ).normalize(),
+ Vector3::unit_y()
+ )
+ }
+
+ pub fn uniform(
+ &self,
+ projection: &Projection)
+ -> CameraUniform
+ {
+ CameraUniform {
+ view: (self.view_matrix()).into(),
+ proj: (OPENGL_TO_WGPU_MATRIX * projection.projection_matrix()).into(),
+ scale: self.scale
+ }
+ }
+} //impl Camera
+
+impl CameraController
+{
+ pub fn new() -> Self {
+ Self {
+ position_pos_delta: Vector3::new(0.0, 0.0, 0.0),
+ position_neg_delta: Vector3::new(0.0, 0.0, 0.0),
+ rotation_pos_delta: Vector2::new(0.0, 0.0),
+ rotation_neg_delta: Vector2::new(0.0, 0.0),
+ scale_delta: Vector2::new(0.0, 0.0),
+ }
+ }
+
+ pub fn keyboard_input(
+ &mut self,
+ key_code: KeyCode,
+ key_state: ElementState)
+ {
+ let press_q = if key_state == ElementState::Pressed { 1.0 } else { 0.0 };
+ match key_code {
+ KeyCode::KeyW => { self.position_pos_delta.z = press_q; },
+ KeyCode::KeyS => { self.position_neg_delta.z = press_q; },
+ KeyCode::KeyA => { self.position_neg_delta.x = press_q; },
+ KeyCode::KeyD => { self.position_pos_delta.x = press_q; },
+ KeyCode::Space => { self.position_pos_delta.y = press_q; },
+ KeyCode::ShiftLeft => { self.position_neg_delta.y = press_q; },
+
+ KeyCode::KeyQ => { self.scale_delta.x = press_q; },
+ KeyCode::KeyE => { self.scale_delta.y = press_q; },
+ _ => {}
+ }
+ }
+
+ pub fn update(
+ &mut self,
+ camera: &mut Camera,
+ dt: Duration)
+ {
+ let dt = dt.as_secs_f32();
+ let speed = 1.0;
+
+ camera.pitch.0 += (self.rotation_pos_delta.x - self.rotation_neg_delta.x) * speed * dt;
+ camera.yaw.0 += (self.rotation_pos_delta.y - self.rotation_neg_delta.y) * speed * dt;
+
+ let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
+ let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
+ let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
+ let up = Vector3::new(0.0, 1.0, 0.0);
+
+ camera.position += forward * (self.position_pos_delta.z - self.position_neg_delta.z) * speed * dt;
+ camera.position += right * (self.position_pos_delta.x - self.position_neg_delta.x) * speed * dt;
+ camera.position += up * (self.position_pos_delta.y - self.position_neg_delta.y) * speed * dt;
+
+ camera.scale *= 1.0 + ((self.scale_delta.y - self.scale_delta.x) * 0.1);
+ camera.scale = f32::max(1e-16, f32::min(1.0, camera.scale));
+ }
+}
+
+impl Projection {
+ pub fn new<
+ F: Into<Rad<f32>>
+ >(
+ width: u32,
+ height: u32,
+ fovy: F,
+ clip: (f32, f32))
+ -> Self {
+ Self {
+ aspect: width as f32 / height as f32,
+ fovy: fovy.into(),
+ clip: clip
+ }
+ }
+
+ pub fn resize(
+ &mut self,
+ width: u32,
+ height: u32)
+ {
+ self.aspect = width as f32 / height as f32;
+ }
+
+ pub fn projection_matrix(
+ &self)
+ -> cgmath::Matrix4<f32> {
+ perspective(self.fovy, self.aspect, self.clip.0, self.clip.1)
+ }
+}