201 lines
6.3 KiB
C++
201 lines
6.3 KiB
C++
#pragma once
|
|
|
|
#include "position.h"
|
|
#include "actions.hpp"
|
|
#include <queue>
|
|
|
|
namespace world::iterator {
|
|
|
|
using pair = std::pair<glm::ivec3, float>;
|
|
|
|
class AbstractFill {
|
|
public:
|
|
virtual bool next(pair&) = 0;
|
|
|
|
protected:
|
|
static constexpr uint32_t Diam(uint16_t radius) { return radius * 2 + 1; }
|
|
};
|
|
class AbstractBorder {
|
|
public:
|
|
virtual bool next(glm::ivec3&) = 0;
|
|
};
|
|
|
|
/// From -radius to radius
|
|
std::unique_ptr<AbstractFill> Get(action::Shape, uint16_t radius);
|
|
/// Border sampling from -radius to radius
|
|
std::unique_ptr<AbstractBorder> GetBorder(action::Shape, uint16_t radius);
|
|
|
|
template<typename Chunk, typename Area>
|
|
inline bool GetChunk(Area& chunks, const std::pair<chunk_pos, chunk_voxel_idx>& split, Voxel& out,
|
|
typename std::shared_ptr<Chunk>& ck = nullptr, chunk_pos& ck_pos = chunk_pos(INT32_MAX)
|
|
) {
|
|
if (split.first != ck_pos && chunks.inRange(split.first)) {
|
|
if(auto it = chunks.find(split.first); it != chunks.end()) {
|
|
ck = std::dynamic_pointer_cast<Chunk>(it->second);
|
|
ck_pos = split.first;
|
|
}
|
|
}
|
|
if (split.first == ck_pos) {
|
|
out = ck->get(split.second);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Apply shape on areas
|
|
template<typename Chunk, typename area_map, typename CB>
|
|
inline void Apply(area_map &areas, action::FillShape fill, const CB& callback) {
|
|
const auto it = areas.find(fill.pos.first);
|
|
if (it == areas.end())
|
|
return;
|
|
|
|
auto &chunks = it->second->setChunks();
|
|
auto iterator = Get(fill.shape, fill.radius);
|
|
pair point;
|
|
typename std::shared_ptr<Chunk> ck = nullptr;
|
|
chunk_pos ck_pos = chunk_pos(INT32_MAX);
|
|
while (iterator->next(point)) {
|
|
const voxel_pos offset = point.first;
|
|
const auto split = glm::splitIdx(fill.pos.second + offset);
|
|
if (Voxel prev; GetChunk<Chunk>(chunks, split, prev, ck, ck_pos)) {
|
|
const auto next = prev.filled(fill.val, point.second);
|
|
if (prev.value != next.value) {
|
|
callback(ck, ck_pos, split.second, prev, next, glm::length2(offset) / fill.radius * .05f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// Find parts using flood fill
|
|
template<typename Chunk, typename area_map, typename CB>
|
|
inline bool Split(area_map &areas, action::FillShape fill, size_t floodFillLimit, const CB& callback) {
|
|
if (floodFillLimit == 0 || fill.val.is_solid())
|
|
return false;
|
|
|
|
const auto it = areas.find(fill.pos.first);
|
|
if (it == areas.end())
|
|
return false;
|
|
|
|
// Check for subpart break
|
|
robin_hood::unordered_set<voxel_pos> joints;
|
|
auto &chunks = it->second->setChunks();
|
|
auto iterator = GetBorder(fill.shape, fill.radius+1);
|
|
glm::ivec3 point;
|
|
typename std::shared_ptr<Chunk> ck = nullptr;
|
|
chunk_pos ck_pos = chunk_pos(INT32_MAX);
|
|
const auto cleanVoxel = Voxel(world::materials::AIR, fill.val.density());
|
|
while (iterator->next(point)) {
|
|
const auto full = fill.pos.second + voxel_pos(point);
|
|
if (Voxel v; GetChunk<Chunk>(chunks, glm::splitIdx(full), v, ck, ck_pos) && v.is_solid())
|
|
joints.insert(full);
|
|
}
|
|
bool added = false;
|
|
while (!joints.empty()) {
|
|
std::queue<voxel_pos> todo;
|
|
todo.push(*joints.begin());
|
|
robin_hood::unordered_set<voxel_pos> part;
|
|
part.insert(todo.front());
|
|
size_t i = 0;
|
|
for (; !todo.empty() && i < floodFillLimit; i++) {
|
|
const auto full = todo.front();
|
|
todo.pop();
|
|
joints.erase(full);
|
|
if (Voxel v; GetChunk<Chunk>(chunks, glm::splitIdx(full), v, ck, ck_pos) && v.is_solid()) {
|
|
constexpr std::array<voxel_pos, 6> DIRS = {
|
|
voxel_pos(1, 0, 0), voxel_pos(-1, 0, 0), voxel_pos(0, 1, 0),
|
|
voxel_pos(0, -1, 0), voxel_pos(0, 0, 1), voxel_pos(0, 0, -1)};
|
|
//MAYBE: diag
|
|
for (auto dir : DIRS) {
|
|
const auto off = full + dir;
|
|
if (part.insert(off).second)
|
|
todo.push(off);
|
|
}
|
|
}
|
|
}
|
|
if (todo.empty()) {
|
|
added = true;
|
|
callback(part, cleanVoxel, chunks, ck, ck_pos, it->second);
|
|
}
|
|
}
|
|
return added;
|
|
}
|
|
|
|
/// Call Apply then Split
|
|
template<typename Chunk, typename area_map, typename CB>
|
|
inline void ApplySplit(area_map &areas, action::FillShape fill, size_t floodFillLimit, const CB& callback) {
|
|
Apply<Chunk>(areas, fill, callback);
|
|
Split<Chunk>(areas, fill, floodFillLimit, [&](const robin_hood::unordered_set<voxel_pos>& part, Voxel next,
|
|
world::ChunkContainer& chunks, std::shared_ptr<Chunk>& ck, chunk_pos& ck_pos, std::shared_ptr<Area>&) {
|
|
for(auto full: part) {
|
|
const auto split = glm::splitIdx(full);
|
|
if (Voxel prev; world::iterator::GetChunk<Chunk>(chunks, split, prev, ck, ck_pos) && prev.is_solid()) {
|
|
if (prev.value != next.value) {
|
|
callback(ck, ck_pos, split.second, prev, next, fill.radius * .05f);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
class Cube final: public AbstractFill {
|
|
public:
|
|
bool next(pair&) override;
|
|
|
|
Cube(uint16_t radius): radius(radius), pos(-radius, -radius, -radius) { }
|
|
|
|
private:
|
|
const uint16_t radius;
|
|
glm::ivec3 pos;
|
|
};
|
|
class CubeBorder final: public AbstractBorder {
|
|
public:
|
|
bool next(glm::ivec3&) override;
|
|
|
|
CubeBorder(uint16_t radius): radius(radius), pos(-radius, -radius, -radius), face(0) { }
|
|
|
|
private:
|
|
const uint16_t radius;
|
|
glm::ivec3 pos;
|
|
int face;
|
|
};
|
|
|
|
/// Interger sphere
|
|
class RawSphere final: public AbstractFill {
|
|
public:
|
|
bool next(pair&) override;
|
|
|
|
RawSphere(uint16_t radius): radius(radius), pos(-radius-1, 0, 0), dy(0), dz(0) { }
|
|
|
|
private:
|
|
const uint16_t radius;
|
|
glm::ivec3 pos;
|
|
int dy;
|
|
int dz;
|
|
};
|
|
class SphereBorder final: public AbstractBorder {
|
|
public:
|
|
bool next(glm::ivec3&) override;
|
|
|
|
SphereBorder(uint16_t radius): radius(radius), max_i(radius*3), max_j(radius*4), i(0), j(0) { }
|
|
|
|
private:
|
|
const uint16_t radius;
|
|
const uint32_t max_i;
|
|
const uint32_t max_j;
|
|
uint32_t i;
|
|
uint32_t j;
|
|
};
|
|
|
|
/// Anti-aliased sphere
|
|
class SmoothSphere final: public AbstractFill {
|
|
public:
|
|
bool next(pair&) override;
|
|
|
|
SmoothSphere(uint16_t radius): radius(radius), pos(-radius-1, 0, 0), dy(0), dz(0) { }
|
|
|
|
private:
|
|
const uint16_t radius;
|
|
glm::ivec3 pos;
|
|
double dy;
|
|
double dz;
|
|
};
|
|
} |