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(),
);
}
}