retrofire_core/render/
batch.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Builder for setting up geometry for rendering.

use alloc::vec::Vec;
use core::borrow::Borrow;

use crate::geom::{mesh, Mesh, Tri};
use crate::math::{mat::Mat4x4, vary::Vary};

use super::{ctx::Context, target::Target, NdcToScreen, Shader};

/// A builder for rendering a chunk of geometry as a batch.
///
/// Several values must be assigned before the [`render`][Batch::render]
/// method can be called:
/// * [faces][Batch::faces]: A list of triangles, each a triplet of indices
///   into the list of vertices (TODO: handling oob)
/// * [vertices][Batch::vertices]: A list of vertices
/// * [shader][Batch::shader]: The combined vertex and fragment shader used
/// * [target][Batch::target]: The render target to render into
/// * [context][Batch::context]: The rendering context and settings used. (TODO: optional?)
///
/// Additionally, setting the following values is optional:
/// * [uniform][Batch::uniform]: The uniform value passed to the vertex shader
/// * [viewport][Batch::viewport]: The matrix used for the NDC-to-screen transform.
// TODO Not correct right now due to method call ordering constraints
// A batch can be freely reused, for example to render several chunks of geometry
// using the same configuration, or several [instances] of the same geometry.
// [instances]: https://en.wikipedia.org/wiki/Geometry_instancing
#[derive(Clone, Debug, Default)]
pub struct Batch<Vtx, Uni, Shd, Tgt, Ctx> {
    faces: Vec<Tri<usize>>,
    verts: Vec<Vtx>,
    uniform: Uni,
    shader: Shd,
    viewport: Mat4x4<NdcToScreen>,
    target: Tgt,
    ctx: Ctx,
}

macro_rules! update {
    ($($upd:ident)+ ; $self:ident $($rest:ident)+) => {{
        let Self { $($upd: _, )+ $($rest, )+ } = $self;
        Batch { $($upd, )+ $($rest, )+ }
    }};
}

impl Batch<(), (), (), (), Context> {
    pub fn new() -> Self {
        Self::default()
    }
}

impl<Vtx, Uni, Shd, Tgt, Ctx> Batch<Vtx, Uni, Shd, Tgt, Ctx> {
    /// Sets the faces to be rendered.
    ///
    /// The faces are copied into the batch.
    pub fn faces(self, faces: impl AsRef<[Tri<usize>]>) -> Self {
        Self {
            faces: faces.as_ref().to_vec(),
            ..self
        }
    }

    /// Sets the vertices to be rendered.
    ///
    /// The vertices are cloned into the batch.
    // TODO: Allow taking by reference to make cloning Batch cheap
    pub fn vertices<V: Clone>(
        self,
        verts: impl AsRef<[V]>,
    ) -> Batch<V, Uni, Shd, Tgt, Ctx> {
        let verts = verts.as_ref().to_vec();
        update!(verts; self faces uniform shader viewport target ctx)
    }

    /// Clones faces and vertices from a mesh to this batch.
    pub fn mesh<A: Clone>(
        self,
        mesh: &Mesh<A>,
    ) -> Batch<mesh::Vertex<A>, Uni, Shd, Tgt, Ctx> {
        let faces = mesh.faces.clone();
        let verts = mesh.verts.clone();
        update!(verts faces; self uniform shader viewport target ctx)
    }

    /// Sets the uniform data to be passed to the vertex shaders.
    pub fn uniform<U: Copy>(self, uniform: U) -> Batch<Vtx, U, Shd, Tgt, Ctx> {
        update!(uniform; self verts faces shader viewport target ctx)
    }

    /// Sets the combined vertex and fragment shader.
    pub fn shader<V: Vary, S: Shader<Vtx, V, Uni>>(
        self,
        shader: S,
    ) -> Batch<Vtx, Uni, S, Tgt, Ctx> {
        update!(shader; self verts faces uniform viewport target ctx)
    }

    /// Sets the viewport matrix.
    pub fn viewport(self, viewport: Mat4x4<NdcToScreen>) -> Self {
        update!(viewport; self verts faces uniform shader target ctx)
    }

    /// Sets the render target.
    pub fn target<T>(self, target: T) -> Batch<Vtx, Uni, Shd, T, Ctx> {
        update!(target; self verts faces uniform shader viewport ctx)
    }

    /// Sets the rendering context.
    pub fn context(self, ctx: &Context) -> Batch<Vtx, Uni, Shd, Tgt, &Context> {
        update!(ctx; self verts faces uniform shader viewport target)
    }
}

impl<Vtx: Clone, Uni: Copy, Shd, Tgt: Target, Ctx>
    Batch<Vtx, Uni, Shd, &mut Tgt, Ctx>
where
    Ctx: Borrow<Context>,
{
    /// Renders this batch of geometry.
    pub fn render<V: Vary>(&mut self)
    where
        Shd: Shader<Vtx, V, Uni>,
    {
        #[rustfmt::skip]
        let Self {
            faces, verts, shader, uniform, viewport, target, ctx,
        } = self;
        super::render(
            faces,
            verts,
            shader,
            *uniform,
            *viewport,
            *target,
            (*ctx).borrow(),
        );
    }
}