use bytemuck::{Pod, Zeroable}; use env_logger::Env; use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4}; use glam::camera::lh::proj::directx::perspective; use wgpu::{Device, Queue}; use wgpu::util::DeviceExt; pub(crate) struct Eye { pub(crate) frame: Affine3A, pub(crate) environment: Environment, aspect_ratio: f32, z_near: f32, z_far: f32, fov_y: f32, pub(crate) environment_buffer: wgpu::Buffer, pub(crate) buffer: wgpu::Buffer, pub(crate) layout: wgpu::BindGroupLayout, pub(crate) group: wgpu::BindGroup, } #[repr(C)] #[derive(Pod, Copy, Clone, Zeroable)] struct Environment { ambient: Vec4, light: Vec4, dir: Vec4, } impl Eye { pub(crate) fn view(&self) -> Mat4 { let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far); projection * self.frame.inverse() } pub(crate) fn write(&mut self, queue: &Queue) { queue.write_buffer(&self.buffer,0,bytemuck::cast_slice(&[ self.view(), Mat4::from_mat3_translation(self.frame.matrix3.into(), Vec3::from(self.frame.translation)) ])); queue.write_buffer(&self.environment_buffer,0,bytemuck::cast_slice(&[self.environment])); } pub(crate) fn new(device: &Device, width: u32, height: u32) -> Eye { let buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Camera Buffer"), contents: bytemuck::cast_slice(&[Mat4::from_translation(Vec3::new(0.0,2.0,-8.0)),Mat4::IDENTITY]), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, } ); let dir = Vec3::new(1.0,0.5,1.0).normalize(); let environment = Environment { ambient: Vec4::new(0.15,0.15,0.15, 0.0), light: Vec4::new(1.0,1.0,1.0, 0.0), dir: Vec4::new(dir.x,dir.y,dir.z,0.0), }; let environment_buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some(""), contents: bytemuck::cast_slice(&[environment]), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, } ); let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None, }, count: None, }, wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None, }, count: None, } ], label: Some("eye_bind_group_layout"), }); let group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &layout, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: buffer.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, resource: environment_buffer.as_entire_binding(), } ], label: Some("eye_bind_group"), }); Eye { aspect_ratio: width as f32 / height as f32, frame: Affine3A::IDENTITY, fov_y: 90.0, z_near: 0.1, z_far: 1000.0, buffer, group, layout, environment, environment_buffer, } } pub(crate) fn resize(&mut self, width: u32, height: u32) { self.aspect_ratio = width as f32 / height as f32 } pub(crate) fn control(&mut self, delta: Vec3) { self.frame *= Affine3A::from_translation(delta); } pub(crate) fn rotate(&mut self, yaw: f32, pitch: f32) { let (mut y,mut x,z) = self.frame.matrix3.to_euler(EulerRot::YXZ); x = (x + pitch * 0.005).clamp(-1.4,1.4); y += yaw * 0.005; self.frame.matrix3 = Mat3A::from_euler(EulerRot::YXZ,y,x,z); } }