1
0
Fork 0
Univerxel/src/core/world/iterators.hpp

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;
};
}