use glam::{Mat4, Vec2, Vec3};
use super::bind_group::AsBindGroup;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
pub struct View {
pub clip_from_world: Mat4,
_padding: [Mat4; 3],
}
impl AsBindGroup for View {
fn bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout
where
Self: Sized,
{
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("view_bind_group_layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: None,
},
count: None,
}],
})
}
fn as_bind_group(&self, render_state: &crate::RenderState) -> wgpu::BindGroup {
render_state
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("view_bind_group"),
layout: &Self::bind_group_layout(&render_state.device),
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: render_state.buffers.get::<View>().unwrap(),
offset: 0,
size: Some((size_of::<View>() as u64).try_into().unwrap()),
}),
}],
})
}
}
#[derive(Debug, Clone)]
pub struct Camera {
pub position: Vec3,
pub projection: Projection,
pub scale: f32,
pub scaling_mode: ScalingMode,
}
impl Default for Camera {
fn default() -> Self {
Camera::default_2d()
}
}
impl Camera {
pub fn default_2d() -> Self {
Camera {
position: Vec3::ZERO,
projection: Projection::Orthographic,
scale: 1.0,
scaling_mode: ScalingMode::WindowSize,
}
}
pub fn view(&self, surface_size: Vec2) -> View {
let render_size = self.scaling_mode.render_size(surface_size);
let scale = 2.0 / render_size * self.scale;
let translation = -self.position;
let clip_from_world =
Mat4::from_scale((scale, 1.0).into()) * Mat4::from_translation(translation);
View {
clip_from_world,
..Default::default()
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Projection {
Orthographic,
}
#[derive(Debug, Clone, Copy)]
pub enum ScalingMode {
WindowSize,
Fixed(Vec2),
FixedHorizontal(f32),
FixedVertical(f32),
AutoMin(Vec2),
AutoMax(Vec2),
}
impl ScalingMode {
pub fn render_size(self, surface_size: Vec2) -> Vec2 {
match self {
ScalingMode::WindowSize => surface_size,
ScalingMode::Fixed(size) => size,
ScalingMode::FixedHorizontal(width) => surface_size * width / surface_size.x,
ScalingMode::FixedVertical(height) => surface_size * height / surface_size.y,
ScalingMode::AutoMin(size) => surface_size * (size / surface_size).max_element(),
ScalingMode::AutoMax(size) => surface_size * (size / surface_size).min_element(),
}
}
}