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);