Skip to main content

bevy_aabb_instancing/
material.rs

1use bevy::prelude::*;
2use bevy::render::render_resource::{DynamicUniformBuffer, ShaderType};
3
4/// Bare enum for toggling shader behavior for [`Color`].
5///
6/// One of:
7/// - [`COLOR_MODE_RGB`]
8/// - [`COLOR_MODE_SCALAR_HUE`]
9pub type ColorMode = u32;
10
11/// "Manual" coloring based on RGB-valued `cuboid.color`.
12///
13/// Encode with `Color::as_rgba_u32`.
14pub const COLOR_MODE_RGB: ColorMode = 0;
15
16/// "Automatic" coloring based on scalar-valued `cuboid.color`. See [`ScalarHueOptions`].
17///
18/// Encode with `u32::from_le_bytes(f32::to_le_bytes(x))`.
19pub const COLOR_MODE_SCALAR_HUE: ColorMode = 1;
20
21/// Denotes which [`CuboidMaterial`] to use when rendering
22/// [`Cuboids`](crate::Cuboids).
23///
24/// When a material is modified, _all_ entities with the corresponding
25/// [`CuboidMaterialId`] will be affected.
26#[derive(Clone, Component, Copy, Eq, Hash, PartialEq)]
27pub struct CuboidMaterialId(pub usize);
28
29/// Shading options, constant for each draw call.
30#[derive(Clone, Debug, ShaderType)]
31pub struct CuboidMaterial {
32    pub color_mode: ColorMode,
33    /// Nonzero values imply that _only_ cuboid edges will be shaded.
34    /// [`VertexPullingRenderPlugin::edges`](crate::VertexPullingRenderPlugin)
35    /// must be `true` for this to take effect.
36    pub wireframe: u32,
37    #[align(16)]
38    pub scalar_hue: ScalarHueOptions,
39
40    /// An extra factor that multiplies a cuboid's color when the "emissive" bit
41    /// on [`MetaBits`](crate::cuboids::MetaBits) is set.
42    pub emissive_gain: Vec3,
43}
44
45impl Default for CuboidMaterial {
46    fn default() -> Self {
47        Self {
48            color_mode: COLOR_MODE_RGB,
49            wireframe: default(),
50            scalar_hue: default(),
51            emissive_gain: Vec3::splat(30.0),
52        }
53    }
54}
55
56/// Dynamic controls for coloring and visibility of scalar values encoded in
57/// `cuboid.color`.
58///
59/// HSL hue is determined as:
60/// ```
61/// use bevy_aabb_instancing::ScalarHueOptions;
62///
63/// fn clamp<T: PartialOrd>(value: T, min: T, max: T) -> T {
64///   if value < min {
65///     min
66///   } else if value > max {
67///     max
68///   } else {
69///     value
70///   }
71/// }
72///
73/// let hue_options = ScalarHueOptions::default();
74/// // Normalize scalar value.
75/// let scalar = 12.2;
76/// let s = (clamp(scalar, hue_options.clamp_min, hue_options.clamp_max) - hue_options.clamp_min) / (hue_options.clamp_max - hue_options.clamp_min);
77/// // Choose hue linearly.
78/// let hue = (360.0 + hue_options.hue_zero + s * hue_options.hue_slope) % 360.0;
79/// ```
80///
81/// These options are only available in [`COLOR_MODE_SCALAR_HUE`].
82#[derive(Clone, Debug, ShaderType)]
83pub struct ScalarHueOptions {
84    /// Cuboids with `cuboid.color < min_visible` will be clipped.
85    pub min_visible: f32,
86    /// Cuboids with `cuboid.color > max_visible` will be clipped.
87    pub max_visible: f32,
88
89    pub clamp_min: f32,
90    pub clamp_max: f32,
91    pub hue_zero: f32,
92    pub hue_slope: f32,
93
94    pub lightness: f32,
95    pub saturation: f32,
96}
97
98impl Default for ScalarHueOptions {
99    fn default() -> Self {
100        Self {
101            min_visible: 0.0,
102            max_visible: 1000.0,
103            clamp_min: 0.0,
104            clamp_max: 1000.0,
105            hue_zero: 240.0,
106            hue_slope: -300.0,
107            lightness: 0.5,
108            saturation: 1.0,
109        }
110    }
111}
112
113/// Resource used to create and modify a set of [`CuboidMaterial`] that are
114/// automatically synced to shader uniforms.
115#[derive(Clone, Debug, Resource)]
116pub struct CuboidMaterialMap {
117    // Consumed every frame during GPU buffering.
118    materials: Vec<CuboidMaterial>,
119}
120
121impl Default for CuboidMaterialMap {
122    fn default() -> Self {
123        Self {
124            materials: vec![default()],
125        }
126    }
127}
128
129impl CuboidMaterialMap {
130    pub fn is_empty(&self) -> bool {
131        self.materials.is_empty()
132    }
133
134    pub fn clear(&mut self) {
135        self.materials.clear();
136    }
137
138    pub fn get(&self, id: CuboidMaterialId) -> &CuboidMaterial {
139        &self.materials[id.0]
140    }
141
142    pub fn get_mut(&mut self, id: CuboidMaterialId) -> &mut CuboidMaterial {
143        &mut self.materials[id.0]
144    }
145
146    pub fn push(&mut self, material: CuboidMaterial) -> CuboidMaterialId {
147        let id = CuboidMaterialId(self.materials.len());
148        self.materials.push(material);
149        id
150    }
151
152    pub(crate) fn write_uniforms(
153        &self,
154        uniforms: &mut DynamicUniformBuffer<CuboidMaterial>,
155    ) -> Vec<CuboidMaterialUniformIndex> {
156        uniforms.clear();
157        let mut indices = Vec::new();
158        for material in self.materials.iter() {
159            indices.push(CuboidMaterialUniformIndex(uniforms.push(material.clone())));
160        }
161        indices
162    }
163}
164
165#[derive(Clone, Copy, Debug, Component)]
166pub(crate) struct CuboidMaterialUniformIndex(pub u32);