17#include <unordered_map>
22enum class VoxelClass : uint8_t {
30inline constexpr uint8_t k_normal_direction_count = 26;
32inline constexpr uint8_t k_normal_undefined = 26;
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;
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);
44inline auto normalDirection(uint8_t index)
noexcept -> glm::ivec3 {
45 if (index >= k_normal_direction_count) {
48 const int raw = index < 13 ? index : index + 1;
49 return {(raw % 3) - 1, ((raw / 3) % 3) - 1, (raw / 9) - 1};
55 return static_cast<uint8_t
>(
static_cast<uint8_t
>(type) | (normal << 3));
59constexpr auto classOf(uint8_t classification)
noexcept -> VoxelClass {
60 return static_cast<VoxelClass
>(classification & 7u);
64constexpr auto normalOf(uint8_t classification)
noexcept -> uint8_t {
65 return static_cast<uint8_t
>(classification >> 3);
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);
80 const auto axis = [](
bool solid_pos,
bool solid_neg) ->
int {
84 return solid_neg ? 0 : -1;
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);
89 VoxelClass type = VoxelClass::corner;
90 if (normal != k_normal_undefined) {
92 type = VoxelClass::face;
93 }
else if (exposed == 2) {
94 type = VoxelClass::edge;
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)));
106inline constexpr uint8_t k_all_faces_exposed = 0x3F;
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)
122 uint8_t classification = 0;
123 uint8_t exposure = 0;
126 constexpr auto operator==(
const SurfaceVoxel&)
const noexcept ->
bool =
default;
129static_assert(
sizeof(
SurfaceVoxel) == 4,
"a surface list entry must be four bytes");
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);
140 const BrickOccupancy shell = brick & ~(neg_x & pos_x & neg_y & pos_y & neg_z & pos_z);
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));
147 const uint64_t mask = 1ull << bit;
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;
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);
167 std::vector<SurfaceVoxel> out;
168 out.reserve(popCount(brick));
169 buildBrickSurfaceInto(brick, neighbours, out);
176 void rebuild(
const Volume& volume);
178 void rebuildBrick(
const Volume& volume, glm::ivec3 brick);
180 void repairAround(
const Volume& volume, glm::ivec3 voxel);
182 void repairBrickRegion(
const Volume& volume, glm::ivec3 brick);
184 void repairBricks(
const Volume& volume, std::span<const glm::ivec3> dirty);
187 auto brickSurface(glm::ivec3 brick)
const -> std::span<const SurfaceVoxel>;
190 auto surfaceVoxelCount()
const -> size_t;
193 auto populatedBrickCount()
const noexcept ->
size_t {
194 return m_bricks.size();
198 auto brickDims()
const noexcept -> glm::uvec3 {
202 template<
typename Callback>
203 void forEachPopulatedBrick(Callback&& callback)
const {
204 for (
const auto& [slot, voxels] : m_bricks) {
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)),
217 auto contains(glm::ivec3 brick)
const noexcept -> bool;
220 auto slotOf(glm::ivec3 brick)
const noexcept -> uint32_t;
222 glm::uvec3 m_brick_dims {0};
224 std::unordered_map<uint32_t, std::vector<SurfaceVoxel>> m_bricks;
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
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