ranim_render/primitives/
viewport.rs

1use glam::{Mat4, Vec2};
2use ranim_core::prelude::CameraFrame;
3
4use crate::{
5    primitives::{Primitive, RenderResource},
6    utils::{WgpuBuffer, WgpuContext},
7};
8
9/// Uniforms for the camera
10#[repr(C, align(16))]
11#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
12pub struct ViewportUniform {
13    proj_mat: Mat4,
14    view_mat: Mat4,
15    half_frame_size: Vec2,
16    _padding: [u32; 2],
17}
18impl Primitive for ViewportUniform {
19    type RenderPacket = ViewportGpuPacket;
20}
21
22impl ViewportUniform {
23    pub fn from_camera_frame(camera_frame: &CameraFrame, width: u32, height: u32) -> Self {
24        let ratio = width as f64 / height as f64;
25        Self {
26            proj_mat: camera_frame.projection_matrix(ratio).as_mat4(),
27            view_mat: camera_frame.view_matrix().as_mat4(),
28            half_frame_size: Vec2::new(
29                (camera_frame.frame_height * ratio) as f32 / 2.0,
30                camera_frame.frame_height as f32 / 2.0,
31            ),
32            _padding: [0; 2],
33        }
34    }
35    pub(crate) fn as_bind_group_layout_entry(binding: u32) -> wgpu::BindGroupLayoutEntry {
36        wgpu::BindGroupLayoutEntry {
37            binding,
38            visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::VERTEX_FRAGMENT,
39            ty: wgpu::BindingType::Buffer {
40                ty: wgpu::BufferBindingType::Uniform,
41                has_dynamic_offset: false,
42                min_binding_size: None,
43            },
44            count: None,
45        }
46    }
47}
48
49pub struct ViewportBindGroup {
50    pub bind_group: wgpu::BindGroup,
51}
52
53impl AsRef<wgpu::BindGroup> for ViewportBindGroup {
54    fn as_ref(&self) -> &wgpu::BindGroup {
55        &self.bind_group
56    }
57}
58
59impl ViewportBindGroup {
60    pub(crate) fn bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
61        ctx.device
62            .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
63                label: Some("Viewport Bind Group Layout"),
64                entries: &[ViewportUniform::as_bind_group_layout_entry(0)],
65            })
66    }
67
68    pub(crate) fn new(ctx: &WgpuContext, uniforms_buffer: &WgpuBuffer<ViewportUniform>) -> Self {
69        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
70            label: Some("Camera Uniforms"),
71            layout: &Self::bind_group_layout(ctx),
72            entries: &[wgpu::BindGroupEntry {
73                binding: 0,
74                resource: wgpu::BindingResource::Buffer(
75                    uniforms_buffer.as_ref().as_entire_buffer_binding(),
76                ),
77            }],
78        });
79        Self { bind_group }
80    }
81}
82
83pub struct ViewportGpuPacket {
84    pub(crate) uniforms_buffer: WgpuBuffer<ViewportUniform>,
85    pub(crate) uniforms_bind_group: ViewportBindGroup,
86}
87
88impl RenderResource for ViewportGpuPacket {
89    type Data = ViewportUniform;
90
91    fn init(ctx: &WgpuContext, data: &Self::Data) -> Self {
92        let uniforms_buffer = WgpuBuffer::new_init(
93            ctx,
94            Some("Uniforms Buffer"),
95            wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
96            *data,
97        );
98        let uniforms_bind_group = ViewportBindGroup::new(ctx, &uniforms_buffer);
99
100        Self {
101            uniforms_buffer,
102            uniforms_bind_group,
103        }
104    }
105
106    fn update(&mut self, ctx: &WgpuContext, data: &Self::Data) {
107        self.uniforms_buffer.set(ctx, *data);
108    }
109}