Toast Engine
Loading...
Searching...
No Matches
surface.hpp
Go to the documentation of this file.
1
6
7#pragma once
8#include "brick.hpp"
9#include "voxel_constants.hpp"
10#include "voxel_volume.hpp"
11
12#include <bit>
13#include <cstdint>
14#include <glm/glm.hpp>
15#include <span>
16#include <toast/export.hpp>
17#include <unordered_map>
18#include <vector>
19
20namespace voxel {
21
22enum class VoxelClass : uint8_t {
23 empty = 0,
24 inside = 1,
25 face = 2,
26 edge = 3,
27 corner = 4,
28};
29
30inline constexpr uint8_t k_normal_direction_count = 26;
31
32inline constexpr uint8_t k_normal_undefined = 26;
33
34[[nodiscard]]
35inline auto normalIndexOf(glm::ivec3 direction) noexcept -> uint8_t {
36 if (direction.x == 0 && direction.y == 0 && direction.z == 0) {
37 return k_normal_undefined;
38 }
39 const int raw = (direction.x + 1) + ((direction.y + 1) * 3) + ((direction.z + 1) * 9);
40 return static_cast<uint8_t>(raw < 13 ? raw : raw - 1);
41}
42
43[[nodiscard]]
44inline auto normalDirection(uint8_t index) noexcept -> glm::ivec3 {
45 if (index >= k_normal_direction_count) {
46 return glm::ivec3(0);
47 }
48 const int raw = index < 13 ? index : index + 1;
49 return {(raw % 3) - 1, ((raw / 3) % 3) - 1, (raw / 9) - 1};
50}
51
53[[nodiscard]]
54constexpr auto packClassification(VoxelClass type, uint8_t normal) noexcept -> uint8_t {
55 return static_cast<uint8_t>(static_cast<uint8_t>(type) | (normal << 3));
56}
57
58[[nodiscard]]
59constexpr auto classOf(uint8_t classification) noexcept -> VoxelClass {
60 return static_cast<VoxelClass>(classification & 7u);
61}
62
63[[nodiscard]]
64constexpr auto normalOf(uint8_t classification) noexcept -> uint8_t {
65 return static_cast<uint8_t>(classification >> 3);
66}
67
69[[nodiscard]]
71 bool solid_neg_x, bool solid_pos_x, bool solid_neg_y, bool solid_pos_y, bool solid_neg_z, bool solid_pos_z
72) noexcept -> uint8_t {
73 const int exposed = static_cast<int>(!solid_neg_x) + static_cast<int>(!solid_pos_x) + static_cast<int>(!solid_neg_y) +
74 static_cast<int>(!solid_pos_y) + static_cast<int>(!solid_neg_z) + static_cast<int>(!solid_pos_z);
75 if (exposed == 0) {
76 return packClassification(VoxelClass::inside, k_normal_undefined);
77 }
78
79 // subtracting flags cancels to (0,0,0) for an isolated voxel which reads as undefined and gets dropped
80 const auto axis = [](bool solid_pos, bool solid_neg) -> int {
81 if (!solid_pos) {
82 return 1;
83 }
84 return solid_neg ? 0 : -1;
85 };
86 const glm::ivec3 direction(axis(solid_pos_x, solid_neg_x), axis(solid_pos_y, solid_neg_y), axis(solid_pos_z, solid_neg_z));
87 const uint8_t normal = normalIndexOf(direction);
88
89 VoxelClass type = VoxelClass::corner;
90 if (normal != k_normal_undefined) {
91 if (exposed == 1) {
92 type = VoxelClass::face;
93 } else if (exposed == 2) {
94 type = VoxelClass::edge;
95 }
96 }
97 return packClassification(type, normal);
98}
99
101[[nodiscard]]
102constexpr auto faceBit(int axis, bool positive) noexcept -> uint8_t {
103 return static_cast<uint8_t>(1u << ((static_cast<unsigned>(axis) * 2u) + (positive ? 1u : 0u)));
104}
105
106inline constexpr uint8_t k_all_faces_exposed = 0x3F;
107
109[[nodiscard]]
110constexpr auto exposureMask(
111 bool solid_neg_x, bool solid_pos_x, bool solid_neg_y, bool solid_pos_y, bool solid_neg_z, bool solid_pos_z
112) noexcept -> uint8_t {
113 return static_cast<uint8_t>(
114 (solid_neg_x ? 0u : 1u) | (solid_pos_x ? 0u : 2u) | (solid_neg_y ? 0u : 4u) | (solid_pos_y ? 0u : 8u) |
115 (solid_neg_z ? 0u : 16u) | (solid_pos_z ? 0u : 32u)
116 );
117}
118
121 uint16_t local_index = 0;
122 uint8_t classification = 0;
123 uint8_t exposure = 0;
124
125 [[nodiscard]]
126 constexpr auto operator==(const SurfaceVoxel&) const noexcept -> bool = default;
127};
128
129static_assert(sizeof(SurfaceVoxel) == 4, "a surface list entry must be four bytes");
130
131inline void
132 buildBrickSurfaceInto(const BrickOccupancy& brick, const BrickNeighbourhood& neighbours, std::vector<SurfaceVoxel>& out) {
133 const BrickOccupancy neg_x = neighboursNegX(brick, neighbours.neg_x);
134 const BrickOccupancy pos_x = neighboursPosX(brick, neighbours.pos_x);
135 const BrickOccupancy neg_y = neighboursNegY(brick, neighbours.neg_y);
136 const BrickOccupancy pos_y = neighboursPosY(brick, neighbours.pos_y);
137 const BrickOccupancy neg_z = neighboursNegZ(brick, neighbours.neg_z);
138 const BrickOccupancy pos_z = neighboursPosZ(brick, neighbours.pos_z);
139
140 const BrickOccupancy shell = brick & ~(neg_x & pos_x & neg_y & pos_y & neg_z & pos_z);
141
142 for (uint32_t z = 0; z < k_brick_dim; ++z) {
143 uint64_t word = shell[z];
144 while (word != 0ull) {
145 const auto bit = static_cast<uint32_t>(std::countr_zero(word));
146 word &= word - 1ull;
147 const uint64_t mask = 1ull << bit;
148
149 const bool solid_neg_x = (neg_x[z] & mask) != 0ull;
150 const bool solid_pos_x = (pos_x[z] & mask) != 0ull;
151 const bool solid_neg_y = (neg_y[z] & mask) != 0ull;
152 const bool solid_pos_y = (pos_y[z] & mask) != 0ull;
153 const bool solid_neg_z = (neg_z[z] & mask) != 0ull;
154 const bool solid_pos_z = (pos_z[z] & mask) != 0ull;
155
156 SurfaceVoxel entry;
157 entry.local_index = static_cast<uint16_t>((z * 64u) + bit);
158 entry.classification = classifyFromNeighbours(solid_neg_x, solid_pos_x, solid_neg_y, solid_pos_y, solid_neg_z, solid_pos_z);
159 entry.exposure = exposureMask(solid_neg_x, solid_pos_x, solid_neg_y, solid_pos_y, solid_neg_z, solid_pos_z);
160 out.push_back(entry);
161 }
162 }
163}
164
165[[nodiscard]]
166inline auto buildBrickSurface(const BrickOccupancy& brick, const BrickNeighbourhood& neighbours) -> std::vector<SurfaceVoxel> {
167 std::vector<SurfaceVoxel> out;
168 out.reserve(popCount(brick));
169 buildBrickSurfaceInto(brick, neighbours, out);
170 return out;
171}
172
174class TOAST_API VolumeSurface {
175public:
176 void rebuild(const Volume& volume);
177
178 void rebuildBrick(const Volume& volume, glm::ivec3 brick);
179
180 void repairAround(const Volume& volume, glm::ivec3 voxel);
181
182 void repairBrickRegion(const Volume& volume, glm::ivec3 brick);
183
184 void repairBricks(const Volume& volume, std::span<const glm::ivec3> dirty);
185
186 [[nodiscard]]
187 auto brickSurface(glm::ivec3 brick) const -> std::span<const SurfaceVoxel>;
188
189 [[nodiscard]]
190 auto surfaceVoxelCount() const -> size_t;
191
192 [[nodiscard]]
193 auto populatedBrickCount() const noexcept -> size_t {
194 return m_bricks.size();
195 }
196
197 [[nodiscard]]
198 auto brickDims() const noexcept -> glm::uvec3 {
199 return m_brick_dims;
200 }
201
202 template<typename Callback>
203 void forEachPopulatedBrick(Callback&& callback) const {
204 for (const auto& [slot, voxels] : m_bricks) {
205 (void)voxels;
206 const glm::ivec3 brick {
207 static_cast<int32_t>(slot % m_brick_dims.x),
208 static_cast<int32_t>((slot / m_brick_dims.x) % m_brick_dims.y),
209 static_cast<int32_t>(slot / (m_brick_dims.x * m_brick_dims.y)),
210 };
211 callback(brick);
212 }
213 }
214
215private:
216 [[nodiscard]]
217 auto contains(glm::ivec3 brick) const noexcept -> bool;
218
219 [[nodiscard]]
220 auto slotOf(glm::ivec3 brick) const noexcept -> uint32_t;
221
222 glm::uvec3 m_brick_dims {0};
223
224 std::unordered_map<uint32_t, std::vector<SurfaceVoxel>> m_bricks;
225};
226
227}
Keyed by brick slot not pool id since uniform bricks have no pool id and shared bricks differ per ins...
Definition surface.hpp:174
Definition voxel_volume.hpp:21
Definition brick.hpp:266
One 64-bit word per z-slice with bit y * 8 + x.
Definition brick.hpp:18
Definition surface.hpp:119
uint16_t local_index
localIndex which is also the occupancy bit index
Definition surface.hpp:121
constexpr auto exposureMask(bool solid_neg_x, bool solid_pos_x, bool solid_neg_y, bool solid_pos_y, bool solid_neg_z, bool solid_pos_z) noexcept -> uint8_t
A set bit is a face with an empty neighbour behind it.
Definition surface.hpp:110
constexpr auto faceBit(int axis, bool positive) noexcept -> uint8_t
One bit per face in the order neg x pos x neg y pos y neg z pos z.
Definition surface.hpp:102
constexpr auto packClassification(VoxelClass type, uint8_t normal) noexcept -> uint8_t
Class in the low three bits and normal index in the high five.
Definition surface.hpp:54
auto classifyFromNeighbours(bool solid_neg_x, bool solid_pos_x, bool solid_neg_y, bool solid_pos_y, bool solid_neg_z, bool solid_pos_z) noexcept -> uint8_t
Exposed faces that cancel (one voxel thick walls) promote to corner.
Definition surface.hpp:70