Commit f75afb5e authored by Shu's avatar Shu 🍠
Browse files

Voxel bitpacking

parent 7d125b81
......@@ -117,7 +117,7 @@ namespace contouring {
for (int x = 0; x < SIZE; x++) {
const auto &chunk = surrounding[(z >= CHUNK_LENGTH) + (y >= CHUNK_LENGTH)*2 + (x >= CHUNK_LENGTH)*4];
const auto &voxel = chunk->getAt(chunk_voxel_pos(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH));
grid.emplace_back(voxel.Density * 1.f / UCHAR_MAX, voxel.Material);
grid.emplace_back(voxel.density() * 1.f / world::Voxel::DENSITY_MAX, voxel.material());
}}}
}
{
......
......@@ -104,22 +104,22 @@ namespace contouring {
}
bool FlatSurroundingBox::isTransparent(const surrounding::faces &surrounding, const std::pair<ushort, ushort> &idx) {
return surrounding[idx.first]->get(idx.second).Density < UCHAR_MAX; // MAYBE: materials::transparent
return surrounding[idx.first]->get(idx.second).density() < world::Voxel::DENSITY_MAX; // MAYBE: materials::transparent
}
void FlatSurroundingBox::render(const surrounding::faces &surrounding, std::vector<buffer::VertexData> &vertices) {
const auto center = surrounding[surrounding::CENTER];
vertices.clear();
for (ushort i = 0; i < CHUNK_SIZE; i++) {
if (center->get(i).Density > 0) {
Faces faces = center->get(i).Density < UCHAR_MAX ? Faces::All :
if (center->get(i).density() > 0) {
Faces faces = center->get(i).density() < world::Voxel::DENSITY_MAX ? Faces::All :
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Right)) & Faces::Right) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Left)) & Faces::Left) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Up)) & Faces::Up) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Down)) & Faces::Down) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Forward)) & Faces::Forward) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Backward)) & Faces::Backward);
box::addCube(vertices, world::Chunk::getPosition(i), center->get(i).Material, faces, glm::vec3(center->get(i).Density * 1.f / UCHAR_MAX));
box::addCube(vertices, world::Chunk::getPosition(i), center->get(i).material(), faces, glm::vec3(center->get(i).density() * 1.f / world::Voxel::DENSITY_MAX));
}
}
}
......
......@@ -4,7 +4,7 @@
#include "../render/window.hpp"
Camera::Camera(GLFWwindow *window, const InputMap& inputs, const Camera::options& opt): window(window), inputs(inputs),
Position(voxel_pos(1ll << 24), 1), o(opt){
Position(voxel_pos(0), 1), o(opt){
updateProjection();
}
Camera::~Camera() { }
......
......@@ -100,9 +100,9 @@ int main(int /*unused*/, char */*unused*/[]){
state.look_at = world.raycast(camera.getRay() * options.voxel_density);
if (state.capture_mouse && state.look_at.has_value()) {
if (inputs.isPressing(Mouse::Left))
world.setCube(state.look_at.value().first, world::Voxel{0, 0}, options.tool.radius);
world.setCube(state.look_at.value().first, world::Voxel(0), options.tool.radius);
else if (inputs.isPressing(Mouse::Right))
world.setCube(state.look_at.value().first, world::Voxel{UCHAR_MAX, options.tool.material}, options.tool.radius);
world.setCube(state.look_at.value().first, world::Voxel(options.tool.material, world::Voxel::DENSITY_MAX), options.tool.radius);
}
world.update((state.position * options.voxel_density).as_voxel(), reports.world);
inputs.saveKeys();
......
......@@ -166,7 +166,7 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
if (options.editor_show) {
ImGui::Begin("Editor", &options.editor_show, ImGuiWindowFlags_AlwaysAutoResize);
if (state.look_at.has_value()) {
ImGui::Text("Look at: (%lld, %lld, %lld) (%s, %.1f)", state.look_at.value().first.x, state.look_at.value().first.y, state.look_at.value().first.z, world::materials::textures[state.look_at.value().second.Material].c_str(), state.look_at.value().second.Density * 1. / UCHAR_MAX);
ImGui::Text("Look at: (%lld, %lld, %lld) (%s, %.1f)", state.look_at.value().first.x, state.look_at.value().first.y, state.look_at.value().first.z, world::materials::textures[state.look_at.value().second.material()].c_str(), state.look_at.value().second.density() * 1. / world::Voxel::DENSITY_MAX);
ImGui::Text("(%.3f, %.3f, %.3f)", state.look_at.value().first.x * 1. / options.voxel_density, state.look_at.value().first.y * 1. / options.voxel_density, state.look_at.value().first.z * 1. / options.voxel_density);
} else {
ImGui::Text("Look at: none");
......
......@@ -7,71 +7,67 @@ using namespace world;
constexpr auto DENSITY = 0.f;
constexpr auto GRANULARITY = 30.f;
constexpr auto RLE = true;
Chunk::Chunk(const chunk_pos& pos, Generator& rnd) {
const auto [densitySet, materialSet] = rnd.getChunk(pos, CHUNK_LENGTH);
for (size_t i = 0; i < CHUNK_SIZE; i++) {
voxels[i].Density = std::clamp((densitySet[i] + DENSITY) * GRANULARITY, 0.f, 1.f) * UCHAR_MAX;
voxels[i].Material = voxels[i].Density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 2))),
const auto density = std::clamp((densitySet[i] + DENSITY) * GRANULARITY, 0.f, 1.f) * Voxel::DENSITY_MAX;
const auto material = density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 2))),
0, materials::count - 2) : 0; //NOTE: map (approx -1, 1) to (1, mat_max)
voxels[i] = Voxel(material, density);
}
rnd.free(densitySet);
rnd.free(materialSet);
}
Chunk::Chunk(std::istream& str) {
if constexpr (RLE) {
ushort i = 0;
while(!str.eof()) {
ushort count;
Voxel voxel;
str.read(reinterpret_cast<char *>(&count), sizeof(count));
str.read(reinterpret_cast<char *>(&voxel.Density), sizeof(Voxel::Density));
str.read(reinterpret_cast<char *>(&voxel.Material), sizeof(Voxel::Material));
str.peek();
for (; count > 0; count--) {
voxels[i] = voxel;
i++;
Chunk::Chunk(std::istream& str, bool rle) {
if(rle) {
ushort i = 0;
while(!str.eof()) {
ushort count;
Voxel voxel;
str.read(reinterpret_cast<char *>(&count), sizeof(count));
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
str.peek();
for (; count > 0; count--) {
voxels[i] = voxel;
i++;
}
}
assert(("Mismatch data length", i == CHUNK_SIZE-1));
} else {
for(auto& voxel: voxels) {
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
}
}
assert(("Mismatch data length", i == CHUNK_SIZE-1));
} else {
for(auto& voxel: voxels) {
str.read(reinterpret_cast<char *>(&voxel.Density), sizeof(Voxel::Density));
str.read(reinterpret_cast<char *>(&voxel.Material), sizeof(Voxel::Material));
}
}
}
Chunk::~Chunk() { }
void Chunk::write(std::ostream& str) const {
if constexpr (RLE) {
const auto *it = voxels.begin();
ushort counter = 1;
Voxel current = *it;
while(true) {
it++;
const auto end = (it == voxels.end());
if(end || current.Density != it->Density || current.Material != it->Material) {
str.write(reinterpret_cast<char *>(&counter), sizeof(counter));
str.write(reinterpret_cast<char *>(&current.Density), sizeof(current.Density));
str.write(reinterpret_cast<char *>(&current.Material), sizeof(current.Material));
if(end)
break;
current = *it;
counter = 1;
} else {
counter++;
void Chunk::write(std::ostream& str, bool rle) const {
if (rle) {
const auto *it = voxels.begin();
ushort counter = 1;
Voxel current = *it;
while(true) {
it++;
const auto end = (it == voxels.end());
if(end || current.value != it->value) {
str.write(reinterpret_cast<char *>(&counter), sizeof(counter));
str.write(reinterpret_cast<char *>(&current), sizeof(current));
if(end)
break;
current = *it;
counter = 1;
} else {
counter++;
}
}
} else {
for(auto current: voxels) {
str.write(reinterpret_cast<char *>(&current), sizeof(current));
}
}
} else {
for(auto current: voxels) {
str.write(reinterpret_cast<char *>(&current.Density), sizeof(current.Density));
str.write(reinterpret_cast<char *>(&current.Material), sizeof(current.Material));
}
}
}
std::optional<Faces> Chunk::update() {
......
......@@ -10,12 +10,13 @@
#define CHUNK_SIZE (CHUNK_LENGTH2 * CHUNK_LENGTH)
namespace world {
constexpr auto RLE = true; //NOTE: only 2.7% gain after zstd
using namespace geometry;
/// World part as linear 3d voxel array
class Chunk {
public:
Chunk(const chunk_pos& pos, Generator& rnd);
Chunk(std::istream& str);
Chunk(std::istream& str, bool rle = RLE);
~Chunk();
/// Update voxels
......@@ -47,13 +48,12 @@ namespace world {
const auto idx = getIdx(pos);
const auto res = voxels[idx];
set(idx, val);
return Item{res.Density, res.Material};
return Item{res.density(), res.material()};
}
// Is player modified
inline bool isModified() const { return modified; }
// Write to file.
// Using RLE
void write(std::ostream& str) const;
void write(std::ostream& str, bool rle = RLE) const;
static inline chunk_voxel_pos getPosition(ushort idx) {
return chunk_voxel_pos(idx / CHUNK_LENGTH2, (idx / CHUNK_LENGTH) % CHUNK_LENGTH, idx % CHUNK_LENGTH);
......
......@@ -210,7 +210,7 @@ std::optional<std::pair<voxel_pos, Voxel>> Universe::raycast(const Ray &ray) con
}
if(chunk != NULL) {
const auto voxel = chunk->getAt(glm::modulo(point, glm::uvec3(CHUNK_LENGTH)));
if(voxel.Density > 0)
if(voxel.density() > 0)
return {{point, voxel}};
}
}
......
......@@ -5,13 +5,35 @@
namespace world {
/// Universe unit
struct Voxel {
/// Packed value
/// swap(1) + material(12) + density(3)
uint16_t value;
using material_t = uint_fast16_t;
using density_t = uint_fast8_t;
constexpr static const density_t DENSITY_MAX = 0b0111;
Voxel(uint16_t value = 0): value(value) { }
Voxel(material_t material, density_t density, bool swap = false) {
assert(density <= DENSITY_MAX);
assert(material < (1 << 12));
value = (swap & 0b1000'0000'0000'0000) |
((material << 3) & 0b0111'1111'1111'1000) |
(density & DENSITY_MAX);
}
/// Quantity of material
/// FIXME: low density area are cheatty
/// @note v < iso * UCHAR_MAX are useless
unsigned char Density;
constexpr inline density_t density() const {
return value & DENSITY_MAX;
}
/// Material type
/// @see world::materials
unsigned short Material;
constexpr inline material_t material() const {
return (value & 0b0111'1111'1111'1000) >> 3;
}
/// Swap value
/// Use external metadata table
constexpr inline bool swap() const {
return value & 0b1000'0000'0000'0000;
}
};
/// Stock of material
struct Item {
......@@ -19,7 +41,7 @@ namespace world {
unsigned long long Count;
/// Material type
/// @see world::materials
unsigned short Material;
Voxel::material_t Material;
};
/// List of materials
struct ItemList: std::map<unsigned short, unsigned long long> {
......
......@@ -6,6 +6,7 @@
namespace world::materials {
/// Materials count
static const auto count = 9;
static_assert(count < (USHRT_MAX >> 4)); //NOTE: for byte packing see Voxel
/// Materials textures
static const std::array<std::string, count> textures = {{"Air", "Sand", "Dirt", "Stone_path", "Mapl", "Seaside_rock", "Stone_wall", "Rough_rock", "Alien"}};
/// Materials roughness.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment