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/// Total normalized-depth span reserved for scene-order biasing.
7///
8/// Fragments whose true depth differs by less than this span may be reordered
9/// by scene insertion order (later items on top); anything farther apart keeps
10/// true depth ordering. The span is split evenly across all items of a frame
11/// (`epsilon = DEPTH_ORDER_SPAN / item_count`) so the total offset does not
12/// grow with scene size.
13pub const DEPTH_ORDER_SPAN: f32 = 1e-4;
14
15/// Uniforms for the camera
16#[repr(C, align(16))]
17#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
18pub struct ViewportUniform {
19    proj_mat: Mat4,
20    view_mat: Mat4,
21    half_frame_size: Vec2,
22    /// Per-order depth bias epsilon, see [`DEPTH_ORDER_SPAN`].
23    bias_epsilon: f32,
24    _padding: u32,
25}
26impl ViewportUniform {
27    pub fn from_camera_frame(
28        camera_frame: &CameraFrame,
29        width: u32,
30        height: u32,
31        bias_epsilon: f32,
32    ) -> Self {
33        let ratio = width as f64 / height as f64;
34        Self {
35            proj_mat: camera_frame.projection_matrix(ratio).as_mat4(),
36            view_mat: camera_frame.view_matrix().as_mat4(),
37            half_frame_size: Vec2::new(
38                (camera_frame.frame_height * ratio) as f32 / 2.0,
39                camera_frame.frame_height as f32 / 2.0,
40            ),
41            bias_epsilon,
42            _padding: 0,
43        }
44    }
45    pub(crate) fn as_bind_group_layout_entry(binding: u32) -> wgpu::BindGroupLayoutEntry {
46        wgpu::BindGroupLayoutEntry {
47            binding,
48            visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::VERTEX_FRAGMENT,
49            ty: wgpu::BindingType::Buffer {
50                ty: wgpu::BufferBindingType::Uniform,
51                has_dynamic_offset: false,
52                min_binding_size: None,
53            },
54            count: None,
55        }
56    }
57}
58
59pub struct ViewportBindGroup {
60    pub bind_group: wgpu::BindGroup,
61}
62
63impl AsRef<wgpu::BindGroup> for ViewportBindGroup {
64    fn as_ref(&self) -> &wgpu::BindGroup {
65        &self.bind_group
66    }
67}
68
69impl ViewportBindGroup {
70    pub(crate) fn bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
71        ctx.device
72            .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
73                label: Some("Viewport Bind Group Layout"),
74                entries: &[ViewportUniform::as_bind_group_layout_entry(0)],
75            })
76    }
77
78    pub(crate) fn new(ctx: &WgpuContext, uniforms_buffer: &WgpuBuffer<ViewportUniform>) -> Self {
79        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
80            label: Some("Camera Uniforms"),
81            layout: &Self::bind_group_layout(ctx),
82            entries: &[wgpu::BindGroupEntry {
83                binding: 0,
84                resource: wgpu::BindingResource::Buffer(
85                    uniforms_buffer.as_ref().as_entire_buffer_binding(),
86                ),
87            }],
88        });
89        Self { bind_group }
90    }
91}
92
93#[derive(bevy_ecs::prelude::Resource)]
94pub struct ViewportGpuPacket {
95    pub(crate) uniforms_buffer: WgpuBuffer<ViewportUniform>,
96    pub(crate) uniforms_bind_group: ViewportBindGroup,
97}
98
99impl ViewportGpuPacket {
100    pub(crate) fn new(ctx: &WgpuContext, data: &ViewportUniform) -> Self {
101        let uniforms_buffer = WgpuBuffer::new_init(
102            ctx,
103            Some("Uniforms Buffer"),
104            wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
105            *data,
106        );
107        let uniforms_bind_group = ViewportBindGroup::new(ctx, &uniforms_buffer);
108
109        Self {
110            uniforms_buffer,
111            uniforms_bind_group,
112        }
113    }
114
115    pub(crate) fn update(&mut self, ctx: &WgpuContext, data: &ViewportUniform) {
116        self.uniforms_buffer.set(ctx, *data);
117    }
118}