25inline constexpr float k_no_floor = -1.0e30f;
28inline auto segmentBoxDistanceSquared(
const glm::vec3& from,
const glm::vec3& to,
const glm::vec3& center,
float half_extent)
30 const auto squared = [&](
float t) {
31 const glm::vec3 gap = glm::max(glm::abs(glm::mix(from, to, t) - center) - glm::vec3(half_extent), glm::vec3(0.0f));
32 return glm::dot(gap, gap);
37 for (
int i = 0; i < 32; ++i) {
38 const float first = low + ((high - low) / 3.0f);
39 const float second = high - ((high - low) / 3.0f);
40 if (squared(first) < squared(second)) {
46 return squared(0.5f * (low + high));
50 glm::vec3 axis_start {};
51 glm::vec3 axis_end {};
54 glm::vec3 up {0.0f, 0.0f, 1.0f};
55 float floor = k_no_floor;
56 bool touching =
false;
59 auto poseCount()
const ->
int {
60 if (touching || not(reach > 0.0f)) {
63 return std::max(1,
static_cast<int>(std::ceil(glm::length(sweep) / (0.25f * reach))));
67 auto contains(
const glm::vec3& point,
int poses)
const ->
bool {
68 if (glm::dot(point, up) < floor) {
72 return segmentBoxDistanceSquared(axis_start, axis_start + sweep, point, 0.5f * voxel::k_voxel_size) <=
73 (reach * reach) + 1.0e-8f;
76 const glm::vec3 axis = axis_end - axis_start;
77 const float axis_squared = glm::dot(axis, axis);
78 const float reach_squared = reach * reach;
79 for (
int pose = 0; pose <= poses; ++pose) {
80 const glm::vec3 offset = point - (axis_start + (sweep * (
static_cast<float>(pose) /
static_cast<float>(poses))));
81 const float along = axis_squared > 0.0f ? glm::clamp(glm::dot(offset, axis) / axis_squared, 0.0f, 1.0f) : 0.0f;
82 const glm::vec3 from_axis = offset - (axis * along);
83 if (glm::dot(from_axis, from_axis) <= reach_squared) {
91 auto bounds()
const -> std::pair<glm::vec3, glm::vec3> {
92 const glm::vec3 low = glm::min(axis_start, axis_end);
93 const glm::vec3 high = glm::max(axis_start, axis_end);
94 return {glm::min(low, low + sweep) - glm::vec3(reach), glm::max(high, high + sweep) + glm::vec3(reach)};
99 std::vector<DetachedComponent> pieces;
101 glm::vec3 index_sum {0.0f};
104template<
typename Breaks>
106auto collectBrokenPieces(
108 const glm::vec3& high, Breaks&& breaks
111 if (materials.materials.empty()) {
115 const glm::ivec3 voxel_dims = glm::ivec3(volume.voxelDims());
116 glm::ivec3 first = glm::ivec3(glm::floor(low / voxel::k_voxel_size));
117 glm::ivec3 last = glm::ivec3(glm::ceil(high / voxel::k_voxel_size)) - 1;
118 first = glm::clamp(first, glm::ivec3(0), voxel_dims - 1);
119 last = glm::clamp(last, glm::ivec3(0), voxel_dims - 1);
120 if (first.x > last.x || first.y > last.y || first.z > last.z) {
124 const auto brick_dim =
static_cast<int32_t
>(voxel::k_brick_dim);
125 const glm::ivec3 first_brick = first / brick_dim;
126 const glm::ivec3 last_brick = last / brick_dim;
128 for (int32_t bz = first_brick.z; bz <= last_brick.z; ++bz) {
129 for (int32_t by = first_brick.y; by <= last_brick.y; ++by) {
130 for (int32_t bx = first_brick.x; bx <= last_brick.x; ++bx) {
131 const glm::ivec3 brick {bx, by, bz};
132 const voxel::BrickOccupancy* occupancy = volume.occupancyPointer(brick);
133 if (occupancy ==
nullptr) {
137 voxel::BrickOccupancy mask {};
138 const glm::ivec3 brick_first = glm::max(first, brick * brick_dim);
139 const glm::ivec3 brick_last = glm::min(last, (brick * brick_dim) + (brick_dim - 1));
140 for (int32_t z = brick_first.z; z <= brick_last.z; ++z) {
141 for (int32_t y = brick_first.y; y <= brick_last.y; ++y) {
142 for (int32_t x = brick_first.x; x <= brick_last.x; ++x) {
143 const glm::ivec3 voxel {x, y, z};
144 const glm::ivec3 local = voxel - (brick * brick_dim);
145 if (not voxel::isSolid(
146 *occupancy,
static_cast<uint32_t
>(local.x),
static_cast<uint32_t
>(local.y),
static_cast<uint32_t
>(local.z)
151 const glm::vec3 center = (glm::vec3 {voxel} + 0.5f) * voxel::k_voxel_size;
152 const uint8_t palette_index = volume.materialAt(voxel);
153 const voxel::PhysicalMaterial& material =
155 if (material.isIndestructible() || not breaks(center, material)) {
160 mask,
static_cast<uint32_t
>(local.x),
static_cast<uint32_t
>(local.y),
static_cast<uint32_t
>(local.z),
true
162 out.index_sum += glm::vec3(voxel);
168 if (voxel::isEmpty(mask)) {
171 for (
const voxel::BrickOccupancy& region : voxel::brickComponents(mask)) {
173 piece.component =
static_cast<uint32_t
>(out.pieces.size());
174 piece.voxel_count = voxel::popCount(region);
175 piece.pieces.push_back(voxel::BrickPiece {.brick = brick, .voxels = region, .component = piece.component});
176 out.pieces.push_back(std::move(piece));
186inline auto collectSmashPieces(
187 const voxel::Volume& volume,
const voxel::Palette& palette,
const voxel::MaterialLibrary& materials,
const SmashVolume& smash,
188 float energy_density, Hardness max_hardness = k_hardest
190 const auto [low, high] = smash.bounds();
191 const int poses = smash.poseCount();
192 return collectBrokenPieces(
193 volume, palette, materials, low, high, [&](
const glm::vec3& center,
const voxel::PhysicalMaterial& material) {
194 return material.breakableBy(max_hardness) && material.
toughness <= energy_density && smash.contains(center, poses);
Definition voxel_volume.hpp:21
TODO: Brief description of the file's purpose.
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
Definition component_classification.hpp:26
Definition voxel_smash.hpp:98
Definition voxel_smash.hpp:49
Definition palette.hpp:143
Entry 0 is the empty voxel and must stay all zero.
Definition palette.hpp:59
float toughness
kJ/m³ it takes to break per m3
Definition palette.hpp:106