Toast Engine
Loading...
Searching...
No Matches
palette.hpp
Go to the documentation of this file.
1
6
7#pragma once
8#include "voxel_constants.hpp"
9
10#include <array>
11#include <cstdint>
15#include <vector>
16
17namespace voxel {
18
19inline constexpr uint32_t k_max_physical_materials = 256;
20
21inline constexpr uint8_t k_default_material = 0;
22
23inline constexpr uint8_t k_entry_transparent = 1u << 0;
24
27 uint8_t albedo_r = 0;
28 uint8_t albedo_g = 0;
29 uint8_t albedo_b = 0;
30
32 uint8_t roughness = 0;
33 uint8_t metallic = 0;
34
36 uint8_t reflectivity = 0;
37
39 uint8_t emissive = 0;
40
41 uint8_t material = k_default_material;
42
43 uint8_t flags = 0;
44
46 uint8_t transforms_to = 0;
47
48 uint8_t alpha = 255;
49
50 std::array<uint8_t, 5> reserved {};
51
52 [[nodiscard]]
53 constexpr auto operator==(const PaletteEntry&) const noexcept -> bool = default;
54};
55
56static_assert(sizeof(PaletteEntry) == 16, "a palette entry must be sixteen bytes - the shader indexes it that way");
57
59struct Palette {
60 std::array<PaletteEntry, k_palette_size> entries {};
61
62 float max_emissive = 1.0f;
63
64 uint32_t revision = 0;
65};
66
67[[nodiscard]]
68inline auto defaultPalette() -> const Palette& {
69 static const Palette palette = [] {
70 Palette out;
71 for (uint32_t i = 1; i < k_palette_size; ++i) {
72 out.entries[i].albedo_r = 160;
73 out.entries[i].albedo_g = 160;
74 out.entries[i].albedo_b = 160;
75 out.entries[i].roughness = 200;
76 }
77 return out;
78 }();
79 return palette;
80}
81
82[[nodiscard]]
83inline auto emissiveIntensity(const Palette& palette, uint8_t index) noexcept -> float {
84 return static_cast<float>(palette.entries[index].emissive) / 255.0f * palette.max_emissive;
85}
86
87inline constexpr uint8_t k_material_indestructible = 1u << 0;
88
90inline constexpr uint8_t k_material_passable = 1u << 1;
91
92inline constexpr uint8_t k_material_flammable = 1u << 2;
93
96 uint16_t density = static_cast<uint16_t>(physics::material_defaults::k_density);
97
98 uint8_t flags = 0;
99
101 physics::Hardness hardness = physics::material_defaults::k_hardness;
102 physics::FracturePattern fracture_pattern = physics::material_defaults::k_fracture_pattern;
103 physics::PackedFractureProfile fracture_profile = physics::material_defaults::k_fracture_profile;
104
106 float toughness = physics::material_defaults::k_toughness;
107
108 float structural_strength = physics::material_defaults::k_structural_strength;
109
110 float static_friction = physics::material_defaults::k_static_friction;
111 float dynamic_friction = physics::material_defaults::k_dynamic_friction;
112 float restitution = physics::material_defaults::k_restitution;
113 float rolling_friction = physics::material_defaults::k_rolling_friction;
114 float spin_friction = physics::material_defaults::k_spin_friction;
115
116 float ignition_energy = 0.0f;
117 float burn_rate = 0.0f;
118 float fuel = 0.0f;
119
120 [[nodiscard]]
121 constexpr auto isIndestructible() const noexcept -> bool {
122 return (flags & k_material_indestructible) != 0;
123 }
124
125 [[nodiscard]]
126 constexpr auto breakableBy(physics::Hardness max_hardness) const noexcept -> bool {
127 return not isIndestructible() && physics::canBreak(max_hardness, hardness);
128 }
129
130 [[nodiscard]]
131 constexpr auto collides() const noexcept -> bool {
132 return (flags & k_material_passable) == 0;
133 }
134
135 [[nodiscard]]
136 constexpr auto isFlammable() const noexcept -> bool {
137 return (flags & k_material_flammable) != 0;
138 }
139};
140
141static_assert(sizeof(PhysicalMaterial) <= 64, "a physical material must fit in one cache line");
142
144 std::vector<PhysicalMaterial> materials;
145};
146
148[[nodiscard]]
149inline auto resolveMaterialIndex(const Palette& palette, const MaterialLibrary& library, uint8_t palette_index) noexcept
150 -> uint32_t {
151 const uint8_t material = palette.entries[palette_index].material;
152 return material < library.materials.size() ? material : k_default_material;
153}
154
155[[nodiscard]]
156inline auto densityTable(const Palette& palette, const MaterialLibrary& library) -> std::array<uint32_t, k_palette_size> {
157 std::array<uint32_t, k_palette_size> out {};
158 for (uint32_t i = 0; i < k_palette_size; ++i) {
159 out[i] = library.materials[resolveMaterialIndex(palette, library, static_cast<uint8_t>(i))].density;
160 }
161 return out;
162}
163
164[[nodiscard]]
165inline auto massPerVoxel(const PhysicalMaterial& material, float voxel_size = k_voxel_size) noexcept -> float {
166 return static_cast<float>(material.density) * voxel_size * voxel_size * voxel_size;
167}
168
169enum class TableIssue : uint8_t {
170 library_empty,
173 zero_density,
174 out_of_range,
175 reserved_slot_used,
176 unknown_material,
177};
178
181 TableIssue issue = TableIssue::library_empty;
182 uint32_t index = 0;
183
184 [[nodiscard]]
185 constexpr auto operator==(const TableProblem&) const noexcept -> bool = default;
186};
187
188[[nodiscard]]
189inline auto validateLibrary(const MaterialLibrary& library) -> std::vector<TableProblem> {
190 std::vector<TableProblem> out;
191 if (library.materials.empty()) {
192 out.push_back({TableIssue::library_empty, 0});
193 return out;
194 }
195 if (library.materials.size() > k_max_physical_materials) {
196 out.push_back({TableIssue::library_too_large, static_cast<uint32_t>(library.materials.size())});
197 }
198
199 // !(x >= 0) also catches NaN
200 const auto negative = [](float value) { return !(value >= 0.0f); };
201
202 for (uint32_t i = 0; i < library.materials.size(); ++i) {
203 const PhysicalMaterial& m = library.materials[i];
204 if (m.density == 0) {
205 out.push_back({TableIssue::zero_density, i});
206 }
207 if (negative(m.toughness) || negative(m.structural_strength) || negative(m.static_friction) || negative(m.dynamic_friction) ||
208 negative(m.rolling_friction) || negative(m.spin_friction) || negative(m.restitution) || m.restitution > 1.0f ||
209 negative(m.ignition_energy) || negative(m.burn_rate) || negative(m.fuel)) {
210 out.push_back({TableIssue::out_of_range, i});
211 }
212 }
213 return out;
214}
215
216[[nodiscard]]
217inline auto validatePalette(const Palette& palette, const MaterialLibrary& library) -> std::vector<TableProblem> {
218 std::vector<TableProblem> out;
219 if (!(palette.entries[k_empty_palette_index] == PaletteEntry {})) {
220 out.push_back({TableIssue::reserved_slot_used, k_empty_palette_index});
221 }
222 if (library.materials.empty()) {
223 out.push_back({TableIssue::library_empty, 0});
224 return out;
225 }
226 for (uint32_t i = 1; i < k_palette_size; ++i) {
227 if (palette.entries[i].material >= library.materials.size()) {
228 out.push_back({TableIssue::unknown_material, i});
229 }
230 }
231 return out;
232}
233
234}
How a material breaks: how the energy of a hit is spent and how coarse its cracks are.
Tiers of how hard a material is to break.
The values a material has when nothing sets them.
constexpr float k_structural_strength
weak bricks
Definition material_defaults.hpp:32
constexpr float k_density
between wood at 600 and stone at 2600
Definition material_defaults.hpp:26
constexpr float k_rolling_friction
A ball rolling on a hard floor loses a few percent of its speed per metre.
Definition material_defaults.hpp:22
auto resolveMaterialIndex(const Palette &palette, const MaterialLibrary &library, uint8_t palette_index) noexcept -> uint32_t
The library must not be empty.
Definition palette.hpp:149
constexpr uint8_t k_material_passable
"Passable" so the zero default is solid
Definition palette.hpp:90
TableIssue
Definition palette.hpp:169
@ library_too_large
index is the count
Definition palette.hpp:172
Definition fracture_profile.hpp:49
Definition palette.hpp:143
16 bytes and the offsets are a shader contract
Definition palette.hpp:26
uint8_t reflectivity
Separate from metallic since a wet non metal reflects and a dirty metal barely does.
Definition palette.hpp:36
uint8_t emissive
Scaled by Palette::max_emissive.
Definition palette.hpp:39
uint8_t transforms_to
0 burns away and only read for flammable materials
Definition palette.hpp:46
uint8_t roughness
0-255 for 0-1
Definition palette.hpp:32
Entry 0 is the empty voxel and must stay all zero.
Definition palette.hpp:59
Definition palette.hpp:94
physics::Hardness hardness
Damage that breaks only softer tiers never breaks this material.
Definition palette.hpp:101
uint16_t density
kg/m³ never zero and integer so MassMoments stays exact
Definition palette.hpp:96
float toughness
kJ/m³ it takes to break per m3
Definition palette.hpp:106
index is a material index a palette index or a count
Definition palette.hpp:180