retrofire_core/render/
batch.rs

1//! Builder for setting up geometry for rendering.
2
3use alloc::vec::Vec;
4use core::borrow::Borrow;
5
6use crate::{
7    geom::{Mesh, Tri, Vertex3},
8    math::{Mat4x4, Vary},
9};
10
11use super::{Clip, Context, NdcToScreen, Render, Shader, Target};
12
13/// A builder for rendering a chunk of geometry as a batch.
14///
15/// Several values must be assigned before the [`render`][Batch::render]
16/// method can be called:
17/// * [primitives][Batch::primitives]: A list of primitives, each a tuple
18///       of indices into the list of vertices (TODO: handling oob)
19/// * [vertices][Batch::vertices]: A list of vertices
20/// * [shader][Batch::shader]: The combined vertex and fragment shader used
21/// * [target][Batch::target]: The render target to render into
22/// * [context][Batch::context]: The rendering context and settings used.
23///       (TODO: optional?)
24///
25/// Additionally, setting the following values is optional:
26/// * [uniform][Batch::uniform]: The uniform value passed to the vertex shader
27/// * [viewport][Batch::viewport]: The matrix used for the NDC-to-screen transform.
28// TODO Not correct right now due to method call ordering constraints
29// A batch can be freely reused, for example to render several chunks of geometry
30// using the same configuration, or several [instances] of the same geometry.
31// [instances]: https://en.wikipedia.org/wiki/Geometry_instancing
32#[derive(Clone, Debug, Default)]
33pub struct Batch<Prim, Vtx, Uni, Shd, Tgt, Ctx> {
34    prims: Vec<Prim>,
35    verts: Vec<Vtx>,
36    uniform: Uni,
37    shader: Shd,
38    viewport: Mat4x4<NdcToScreen>,
39    target: Tgt,
40    ctx: Ctx,
41}
42
43macro_rules! update {
44    ($($upd:ident)+ ; $self:ident $($rest:ident)+) => {{
45        let Self { $($upd: _, )+ $($rest, )+ } = $self;
46        Batch { $($upd, )+ $($rest, )+ }
47    }};
48}
49
50impl Batch<(), (), (), (), (), Context> {
51    pub fn new() -> Self {
52        Self::default()
53    }
54}
55
56impl<Prim, Vtx, Uni, Shd, Tgt, Ctx> Batch<Prim, Vtx, Uni, Shd, Tgt, Ctx> {
57    /// Sets the primitives to be rendered.
58    ///
59    /// The primitives are copied into the batch.
60    pub fn primitives<P: Clone>(
61        self,
62        prims: impl AsRef<[P]>,
63    ) -> Batch<P, Vtx, Uni, Shd, Tgt, Ctx> {
64        let prims = prims.as_ref().to_vec();
65        update!(prims; self verts uniform shader viewport target ctx)
66    }
67
68    /// Sets the vertices to be rendered.
69    ///
70    /// The vertices are cloned into the batch.
71    // TODO: Allow taking by reference to make cloning Batch cheap
72    pub fn vertices<V: Clone>(
73        self,
74        verts: impl AsRef<[V]>,
75    ) -> Batch<Prim, V, Uni, Shd, Tgt, Ctx> {
76        let verts = verts.as_ref().to_vec();
77        update!(verts; self prims uniform shader viewport target ctx)
78    }
79
80    /// Clones faces and vertices from a mesh to this batch.
81    pub fn mesh<A: Clone>(
82        self,
83        mesh: &Mesh<A>,
84    ) -> Batch<Tri<usize>, Vertex3<A>, Uni, Shd, Tgt, Ctx> {
85        let prims = mesh.faces.clone();
86        let verts = mesh.verts.clone();
87        update!(verts prims; self uniform shader viewport target ctx)
88    }
89
90    /// Sets the uniform data to be passed to the vertex shaders.
91    pub fn uniform<U: Copy>(
92        self,
93        uniform: U,
94    ) -> Batch<Prim, Vtx, U, Shd, Tgt, Ctx> {
95        update!(uniform; self verts prims shader viewport target ctx)
96    }
97
98    /// Sets the combined vertex and fragment shader.
99    pub fn shader<V: Vary, U, S: Shader<Vtx, V, U>>(
100        self,
101        shader: S,
102    ) -> Batch<Prim, Vtx, Uni, S, Tgt, Ctx> {
103        update!(shader; self verts prims uniform viewport target ctx)
104    }
105
106    /// Sets the viewport matrix.
107    pub fn viewport(self, viewport: Mat4x4<NdcToScreen>) -> Self {
108        update!(viewport; self verts prims uniform shader target ctx)
109    }
110
111    /// Sets the render target.
112    // TODO what bound for T?
113    pub fn target<T>(self, target: T) -> Batch<Prim, Vtx, Uni, Shd, T, Ctx> {
114        update!(target; self verts prims uniform shader viewport ctx)
115    }
116
117    /// Sets the rendering context.
118    pub fn context(
119        self,
120        ctx: &Context,
121    ) -> Batch<Prim, Vtx, Uni, Shd, Tgt, &Context> {
122        update!(ctx; self verts prims uniform shader viewport target)
123    }
124}
125
126impl<Prim, Vtx, Uni, Shd, Tgt, Ctx> Batch<Prim, Vtx, Uni, Shd, &mut Tgt, Ctx> {
127    /// Renders this batch of geometry.
128    #[rustfmt::skip]
129    pub fn render<Var>(&mut self)
130    where
131        Var: Vary,
132        Prim: Render<Var> + Clone,
133        Vtx: Clone,
134        Uni: Copy,
135        [<Prim>::Clip]: Clip<Item= Prim::Clip>,
136        Shd: Shader<Vtx, Var, Uni>,
137        Tgt: Target,
138        Ctx: Borrow<Context>
139    {
140        let Self {
141            prims, verts, shader, uniform, viewport, target, ctx,
142        } = self;
143
144        super::render(
145            prims, verts, shader, *uniform, *viewport, *target,
146            (*ctx).borrow(),
147        );
148    }
149}