Skip to main content

ranim_render/primitives/
viewport.rs

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