retrofire_core/render/ctx.rs
1//! Rendering context and parameters.
2
3use core::{cell::RefCell, cmp::Ordering};
4
5use crate::math::{Color4, rgba};
6
7use super::Stats;
8
9/// Context and parameters used by the renderer.
10#[derive(Clone, Debug)]
11pub struct Context {
12 /// The color with which to fill the color buffer to clear it, if any.
13 ///
14 /// If rendered geometry always fills the entire frame, `color_clear`
15 /// can be set to `None` to avoid redundant work.
16 pub color_clear: Option<Color4>,
17
18 /// The value with which to fill the depth buffer to clear it, if any.
19 pub depth_clear: Option<f32>,
20
21 /// Whether to cull (discard) faces pointing either away from or towards
22 /// the camera.
23 ///
24 /// If all geometry drawn is "solid" meshes without holes, backfaces can
25 /// usually be culled because they are always occluded by front faces and
26 /// drawing them would be redundant.
27 pub face_cull: Option<FaceCull>,
28
29 /// Whether to sort visible faces by their depth.
30 ///
31 /// If z-buffering or other hidden surface determination method is not used,
32 /// back-to-front depth sorting can be used to ensure correct rendering
33 /// unless there is intersecting or non-orderable geometry (this is the
34 /// so-called "painter's algorithm").
35 ///
36 /// Overlapping transparent surfaces have to be drawn back-to-front to get
37 /// correct results. Rendering nontransparent geometry in front-to-back
38 /// order can improve performance by reducing overdraw.
39 pub depth_sort: Option<DepthSort>,
40
41 /// Whether to do depth testing and which predicate to use.
42 ///
43 /// If set to `Some(Ordering::Less)`, a fragment passes the depth test
44 /// *iff* `new_z < old_z` (the default). If set to `None`, depth test
45 /// is not performed. This setting has no effect if the render target
46 /// does not support z-buffering.
47 pub depth_test: Option<Ordering>,
48
49 /// Whether to write color values.
50 ///
51 /// If `false`, other fragment processing is done but there is no color
52 /// output. This setting has no effect if the render target does not
53 /// support color writes.
54 pub color_write: bool,
55
56 /// Whether to write depth values.
57 ///
58 /// If `false`, other fragment processing is done but there is no depth
59 /// output. This setting has no effect if the render target does not
60 /// support depth writes.
61 pub depth_write: bool,
62
63 /// Collecting rendering statistics.
64 pub stats: RefCell<Stats>,
65}
66
67/// Whether to sort faces front to back or back to front.
68#[derive(Copy, Clone, Debug, Eq, PartialEq)]
69pub enum DepthSort {
70 FrontToBack,
71 BackToFront,
72}
73
74/// Whether to cull front faces or backfaces.
75#[derive(Copy, Clone, Debug, Eq, PartialEq)]
76pub enum FaceCull {
77 Front,
78 Back,
79}
80
81impl Context {
82 /// Compares the reciprocal depth value `new` to `curr` and returns
83 /// whether `new` passes the depth test specified by `self.depth_test`.
84 /// If `self.depth_test` is `None`, always returns `true`.
85 #[inline]
86 pub fn depth_test(&self, new: f32, curr: f32) -> bool {
87 // Reverse comparison because we're comparing reciprocals
88 self.depth_test.is_none() || self.depth_test == curr.partial_cmp(&new)
89 }
90
91 /// Returns whether a primitive should be culled based on the current face
92 /// culling setting.
93 // TODO this could also be in Render
94 #[inline]
95 pub fn face_cull(&self, is_backface: bool) -> bool {
96 match self.face_cull {
97 Some(FaceCull::Back) if is_backface => true,
98 Some(FaceCull::Front) if !is_backface => true,
99 _ => false,
100 }
101 }
102}
103
104impl Default for Context {
105 /// Creates a rendering context with default settings.
106 ///
107 /// The default values are:
108 /// * Color clear: Opaque black
109 /// * Depth clear: Positive infinity
110 /// * Face culling: Backfaces
111 /// * Depth sorting: Disabled
112 /// * Color writes: Enabled
113 /// * Depth testing: Pass if closer
114 /// * Depth writes: Enabled
115 fn default() -> Self {
116 Self {
117 color_clear: Some(rgba(0, 0, 0, 0xFF)),
118 depth_clear: Some(f32::INFINITY),
119 face_cull: Some(FaceCull::Back),
120 depth_sort: None,
121 color_write: true,
122 depth_test: Some(Ordering::Less),
123 depth_write: true,
124 stats: Default::default(),
125 }
126 }
127}