Pool/src/render/eye.rs
2026-09-14 08:15:56 +01:00

236 lines
8.5 KiB
Rust

use crate::render::{Texture};
use bytemuck::{Pod, Zeroable};
use glam::camera::lh::proj::directx::perspective;
use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4};
use wgpu::util::DeviceExt;
use wgpu::{Device, Queue};
use crate::render::instance::material::MaterialProperties;
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) camera_buffer: wgpu::Buffer,
pub(crate) layout: wgpu::BindGroupLayout,
pub(crate) group: wgpu::BindGroup,
pub sky_pipeline: wgpu::RenderPipeline,
}
#[repr(C)]
#[derive(Pod, Copy, Clone, Zeroable)]
pub struct Environment {
ambient: Vec4,
light: Vec4,
dir: Vec4,
}
impl Eye {
pub(crate) fn write(&mut self, queue: &Queue) {
let camera = Mat4::from_mat3_translation(
self.frame.matrix3.into(),
Vec3::from(self.frame.translation),
);
let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far);
queue.write_buffer(
&self.camera_buffer,
0,
bytemuck::cast_slice(&[
projection,
camera * projection.inverse(),
camera.inverse(),
camera,
]),
);
queue.write_buffer(
&self.environment_buffer,
0,
bytemuck::cast_slice(&[self.environment]),
);
}
pub(crate) fn new(device: &Device, config: &wgpu::SurfaceConfiguration, width: u32, height: u32, skybox: Texture) -> Eye {
let camera_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,
Mat4::IDENTITY,
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.1, 0.1, 0.1, 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,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
],
label: Some("eye_bind_group_layout"),
});
let shader = device.create_shader_module(wgpu::include_wgsl!("sky.wgsl"));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[Some(&layout)],
immediate_size: 0,
});
//todo: use a pipeline cache!
let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Sky Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_sky"),
compilation_options: Default::default(),
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_sky"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
// 4.
format: config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
front_face: wgpu::FrontFace::Cw,
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: Some(false),
depth_compare: Some(wgpu::CompareFunction::LessEqual),
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
let group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox);
Eye {
aspect_ratio: width as f32 / height as f32,
frame: Affine3A::IDENTITY,
fov_y: 90.0,
z_near: 0.1,
z_far: 1000.0,
camera_buffer,
group,
layout,
environment,
environment_buffer,
sky_pipeline,
}
}
fn bind_group(
layout: &wgpu::BindGroupLayout,
device: &wgpu::Device,
camera: &wgpu::Buffer,
environment: &wgpu::Buffer,
skybox: Texture,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: camera.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: environment.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(
&MaterialProperties::default().sampler(device),
),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::TextureView(&skybox.view),
},
],
label: Some("eye_bind_group"),
})
}
pub fn skybox(&mut self, device: &wgpu::Device, texture: Texture) {
self.group = Eye::bind_group(
&self.layout,
device,
&self.camera_buffer,
&self.environment_buffer,
texture,
)
}
pub(crate) fn resize(&mut self, queue: &Queue, width: u32, height: u32) {
self.aspect_ratio = width as f32 / height as f32;
self.write(queue);
}
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);
}
}