1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use cgmath::{EuclideanSpace, SquareMatrix, Zero};
use pill_engine::internal::{
    TransformComponent,
    CameraComponent
};

use anyhow::{ Result };
use wgpu::util::DeviceExt;
use std::f32::consts::FRAC_PI_2;

#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 0.5, 0.0,
    0.0, 0.0, 0.5, 1.0,
);

// --- Camera Uniform ---

#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub(crate) struct CameraUniform {
    pub(crate) position: [f32; 4], // Camera position
    pub(crate) view_projection_matrix: [[f32; 4]; 4], // Perspective manipulation
}

impl CameraUniform {
    pub fn new() -> Self {
        Self {
            position: cgmath::Vector4::zero().into(),
            view_projection_matrix: cgmath::Matrix4::identity().into(),
        }
    }

    pub fn update_data(&mut self, camera_component: &CameraComponent, transform_component: &TransformComponent) {
        // Update position
        self.position = cgmath::Vector4::<f32> { 
            x: transform_component.position.x, 
            y: transform_component.position.y, 
            z: transform_component.position.z, 
            w: 0.0
        }.into();

        // Update view-projection
        self.view_projection_matrix = (CameraUniform::calculate_projection_matrix(camera_component) * CameraUniform::calculate_view_matrix(transform_component)).into();
    }

    fn calculate_view_matrix(transform_component: &TransformComponent) -> cgmath::Matrix4::<f32> {
        let position = cgmath::Point3::from_vec(transform_component.position);

        let roll_matrix  = cgmath::Matrix3::from_angle_z(cgmath::Deg(transform_component.rotation.z));
        let yaw_matrix  = cgmath::Matrix3::from_angle_y(cgmath::Deg(transform_component.rotation.y));
        let pitch_matrix  = cgmath::Matrix3::from_angle_x(cgmath::Deg(transform_component.rotation.x));
        let rotation_matrix = yaw_matrix * pitch_matrix * roll_matrix;
        let direction  = rotation_matrix * cgmath::Vector3::<f32>::unit_z();

        cgmath::Matrix4::look_to_rh(
            position,
            direction,
            cgmath::Vector3::unit_y()
        )
    }

    fn calculate_projection_matrix(camera_component: &CameraComponent) -> cgmath::Matrix4::<f32> {
        OPENGL_TO_WGPU_MATRIX * cgmath::perspective(
            cgmath::Deg(camera_component.fov), 
            camera_component.aspect.get_value(), 
            camera_component.range.start,
            camera_component.range.end
        )
    }
}

// --- Camera ---

#[derive(Debug)]
pub struct RendererCamera {
    pub(crate) uniform: CameraUniform,
    buffer: wgpu::Buffer,
    pub bind_group: wgpu::BindGroup,
}

impl RendererCamera {
    pub fn new(device: &wgpu::Device, camera_bind_group_layout: &wgpu::BindGroupLayout) -> Result<Self> {

        let uniform = CameraUniform::new();

        let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
            label: Some("camera_buffer"),
            contents: bytemuck::cast_slice(&[uniform]),
            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
        });

        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            layout: &camera_bind_group_layout,
            entries: &[wgpu::BindGroupEntry {
                binding: 0,
                resource: buffer.as_entire_binding(),
            }],
            label: Some("camera_bind_group"),
        });

        let camera = Self {
            uniform,
            buffer,
            bind_group,
        };

        Ok(camera)
    }

    pub fn update(&mut self, queue: &wgpu::Queue, camera_component: &CameraComponent, transform_component: &TransformComponent) {
        self.uniform.update_data(camera_component, transform_component);
        queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.uniform]));
    }
}