Toast Engine
Loading...
Searching...
No Matches
stamp.hpp
Go to the documentation of this file.
1
6
7#pragma once
8#include "palette.hpp"
9#include "voxel_constants.hpp"
10#include "voxel_volume.hpp"
11
12#include <array>
13#include <cmath>
14#include <cstdint>
15#include <glm/glm.hpp>
16#include <optional>
17#include <toast/export.hpp>
18#include <vector>
19
20namespace voxel {
21
24 std::array<uint8_t, 3> source {0, 1, 2};
25 std::array<bool, 3> flip {false, false, false};
26
27 [[nodiscard]]
28 constexpr auto operator==(const LatticeOrientation&) const noexcept -> bool = default;
29
30 [[nodiscard]]
31 constexpr auto isMirror() const noexcept -> bool {
32 // Permutation parity times one sign flip per negated axis
33 int inversions = 0;
34 for (int i = 0; i < 3; ++i) {
35 for (int j = i + 1; j < 3; ++j) {
36 inversions += source[i] > source[j] ? 1 : 0;
37 }
38 }
39 const int negated = static_cast<int>(flip[0]) + static_cast<int>(flip[1]) + static_cast<int>(flip[2]);
40 return (inversions + negated) % 2 != 0;
41 }
42};
43
44[[nodiscard]]
45inline auto allLatticeOrientations() -> std::array<LatticeOrientation, 48> {
46 constexpr std::array<std::array<uint8_t, 3>, 6> permutations {
47 {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}
48 };
49
50 std::array<LatticeOrientation, 48> out {};
51 size_t next = 0;
52 for (const std::array<uint8_t, 3>& permutation : permutations) {
53 for (uint32_t signs = 0; signs < 8; ++signs) {
54 LatticeOrientation orientation;
55 orientation.source = permutation;
56 orientation.flip = {(signs & 1u) != 0, (signs & 2u) != 0, (signs & 4u) != 0};
57 out[next++] = orientation;
58 }
59 }
60 return out;
61}
62
63[[nodiscard]]
64inline auto toMatrix(const LatticeOrientation& orientation) -> glm::mat3 {
65 glm::mat3 matrix(0.0f);
66 for (int row = 0; row < 3; ++row) {
67 matrix[orientation.source[row]][row] = orientation.flip[row] ? -1.0f : 1.0f; // glm indexes [column][row]
68 }
69 return matrix;
70}
71
73 LatticeOrientation orientation;
74
75 glm::ivec3 offset {0};
76
77 [[nodiscard]]
78 auto operator==(const LatticePlacement&) const noexcept -> bool = default;
79};
80
82[[nodiscard]]
83inline auto placeVoxel(const LatticePlacement& placement, glm::ivec3 voxel) noexcept -> glm::ivec3 {
84 glm::ivec3 out;
85 for (int axis = 0; axis < 3; ++axis) {
86 const int32_t along = voxel[placement.orientation.source[axis]];
87 out[axis] = placement.offset[axis] + (placement.orientation.flip[axis] ? -along - 1 : along);
88 }
89 return out;
90}
91
93[[nodiscard]]
94inline auto placementFromTransform(const glm::mat4& transform, float voxel_size = k_voxel_size, float tolerance = 1e-4f)
95 -> std::optional<LatticePlacement> {
97 std::array<bool, 3> used {false, false, false};
98
99 for (int row = 0; row < 3; ++row) {
100 int found = -1;
101 for (int column = 0; column < 3; ++column) {
102 const float value = transform[column][row];
103 if (std::abs(std::abs(value) - 1.0f) <= tolerance) {
104 if (found != -1) {
105 return std::nullopt;
106 }
107 found = column;
108 out.orientation.flip[row] = value < 0.0f;
109 } else if (std::abs(value) > tolerance) {
110 return std::nullopt;
111 }
112 }
113 if (found == -1 || used[found]) {
114 return std::nullopt;
115 }
116 used[found] = true;
117 out.orientation.source[row] = static_cast<uint8_t>(found);
118 }
119
120 if (std::abs(transform[0][3]) > tolerance || std::abs(transform[1][3]) > tolerance || std::abs(transform[2][3]) > tolerance ||
121 std::abs(transform[3][3] - 1.0f) > tolerance) {
122 return std::nullopt;
123 }
124
125 const float slack = 0.001f / voxel_size;
126 for (int axis = 0; axis < 3; ++axis) {
127 const float voxels = transform[3][axis] / voxel_size;
128 const float nearest = std::round(voxels);
129 if (std::abs(voxels - nearest) > slack) {
130 return std::nullopt;
131 }
132 out.offset[axis] = static_cast<int32_t>(nearest);
133 }
134 return out;
135}
136
138using PaletteRemapTable = std::array<uint8_t, k_palette_size>;
139
140[[nodiscard]]
141inline auto identityRemap() -> PaletteRemapTable {
142 PaletteRemapTable table {};
143 for (uint32_t i = 0; i < k_palette_size; ++i) {
144 table[i] = static_cast<uint8_t>(i);
145 }
146 return table;
147}
148
150 PaletteRemapTable table {};
151
152 std::vector<uint8_t> unmatched;
153};
154
155[[nodiscard]]
156inline auto buildRemap(const Palette& piece, const Palette& master) -> PaletteRemap {
157 PaletteRemap out;
158 for (uint32_t i = 1; i < k_palette_size; ++i) {
159 const PaletteEntry& entry = piece.entries[i];
160 if (entry == PaletteEntry {}) {
161 continue;
162 }
163
164 bool matched = false;
165 if (entry.emissive == 0 || piece.max_emissive == master.max_emissive) {
166 for (uint32_t j = 1; j < k_palette_size; ++j) {
167 if (master.entries[j] == entry) {
168 out.table[i] = static_cast<uint8_t>(j);
169 matched = true;
170 break;
171 }
172 }
173 }
174 if (!matched) {
175 out.unmatched.push_back(static_cast<uint8_t>(i));
176 }
177 }
178 return out;
179}
180
182 uint32_t voxels_written = 0;
183
184 uint32_t voxels_clipped = 0;
185
187 uint32_t voxels_unmapped = 0;
188
189 bool pool_exhausted = false;
190};
191
193[[nodiscard]]
194TOAST_API auto stamp(Volume& target, const Volume& piece, const LatticePlacement& placement, const PaletteRemapTable& remap)
195 -> StampResult;
196
197}
Definition voxel_volume.hpp:21
auto placementFromTransform(const glm::mat4 &transform, float voxel_size=k_voxel_size, float tolerance=1e-4f) -> std::optional< LatticePlacement >
Matrix entries within tolerance of 0 or +/-1 and translation within a millimetre of a voxel boundary.
Definition stamp.hpp:94
auto placeVoxel(const LatticePlacement &placement, glm::ivec3 voxel) noexcept -> glm::ivec3
A flipped axis lands cell [v, v + 1] on [-v - 1, -v] hence the -1.
Definition stamp.hpp:83
std::array< uint8_t, k_palette_size > PaletteRemapTable
Index 0 always maps to 0.
Definition stamp.hpp:138
Output axis i reads input axis source[i] negated when flip[i].
Definition stamp.hpp:23
Definition stamp.hpp:72
16 bytes and the offsets are a shader contract
Definition palette.hpp:26
uint8_t emissive
Scaled by Palette::max_emissive.
Definition palette.hpp:39
Definition stamp.hpp:149
Entry 0 is the empty voxel and must stay all zero.
Definition palette.hpp:59
Definition stamp.hpp:181
uint32_t voxels_unmapped
Skipped since stamping them as empty would erase the target.
Definition stamp.hpp:187