feat: add packIndices and unpackIndices
This commit is contained in:
1 parent
e4208f3a97
commit
1a5d637423
1 file changed
+160
@@ -0,0 +1,160 @@
|
||||
#include "vf/Schematic.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace vf {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t schematicMagic = 0x56585343u; // "VXSC"
|
||||
constexpr std::size_t maxSchematicCells = 1u << 26;
|
||||
constexpr uint8_t voidMaskFlag = 0x01;
|
||||
|
||||
std::size_t packedByteCount(std::size_t cells, uint8_t bits) {
|
||||
return (cells * static_cast<std::size_t>(bits) + 7u) / 8u;
|
||||
}
|
||||
|
||||
// Indices are packed low bits first. Every supported width divides eight, so a
|
||||
// value never straddles two bytes and the reader can address it directly.
|
||||
void packIndices(const std::vector<uint16_t>& indices, uint8_t bits, ByteBuffer& out) {
|
||||
std::size_t base = out.size();
|
||||
out.resize(base + packedByteCount(indices.size(), bits));
|
||||
uint32_t mask = (1u << bits) - 1u;
|
||||
for (std::size_t i = 0; i < indices.size(); ++i) {
|
||||
std::size_t bitPosition = i * bits;
|
||||
std::size_t byteIndex = base + (bitPosition >> 3);
|
||||
unsigned shift = static_cast<unsigned>(bitPosition & 7u);
|
||||
out[byteIndex] |= static_cast<uint8_t>((indices[i] & mask) << shift);
|
||||
}
|
||||
}
|
||||
|
||||
void unpackIndices(const uint8_t* data, std::size_t cells, uint8_t bits,
|
||||
std::vector<uint16_t>& out) {
|
||||
out.assign(cells, 0u);
|
||||
uint32_t mask = (1u << bits) - 1u;
|
||||
for (std::size_t i = 0; i < cells; ++i) {
|
||||
std::size_t bitPosition = i * bits;
|
||||
unsigned shift = static_cast<unsigned>(bitPosition & 7u);
|
||||
out[i] = static_cast<uint16_t>((data[bitPosition >> 3] >> shift) & mask);
|
||||
}
|
||||
}
|
||||
|
||||
void buildPalette(const ClipboardBuffer& buffer, std::vector<Block>& palette,
|
||||
std::vector<uint16_t>& slotOfBlock) {
|
||||
palette.clear();
|
||||
slotOfBlock.assign(static_cast<std::size_t>(Block::Count), 0u);
|
||||
std::vector<uint8_t> used(static_cast<std::size_t>(Block::Count), 0u);
|
||||
used[static_cast<std::size_t>(Block::Air)] = 1u;
|
||||
|
||||
const Vec3i& dims = buffer.dims();
|
||||
for (int y = 0; y < dims.y; ++y)
|
||||
for (int z = 0; z < dims.z; ++z)
|
||||
for (int x = 0; x < dims.x; ++x) {
|
||||
if (!buffer.filledAt(x, y, z)) continue;
|
||||
used[static_cast<std::size_t>(buffer.at(x, y, z))] = 1u;
|
||||
}
|
||||
|
||||
palette.push_back(Block::Air);
|
||||
for (std::size_t id = 0; id < used.size(); ++id) {
|
||||
if (used[id] == 0u || static_cast<Block>(id) == Block::Air) continue;
|
||||
slotOfBlock[id] = static_cast<uint16_t>(palette.size());
|
||||
palette.push_back(static_cast<Block>(id));
|
||||
}
|
||||
}
|
||||
|
||||
void putI32(ByteWriter& writer, int value) { writer.putU32(static_cast<uint32_t>(value)); }
|
||||
|
||||
bool getI32(ByteReader& reader, int& out) {
|
||||
uint32_t raw = 0;
|
||||
if (!reader.getU32(raw)) return false;
|
||||
out = static_cast<int>(static_cast<int32_t>(raw));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ClipboardBuffer::ClipboardBuffer(const Vec3i& dims, const Vec3i& anchor) {
|
||||
if (dims.x <= 0 || dims.y <= 0 || dims.z <= 0) return;
|
||||
size = dims;
|
||||
handle = anchor;
|
||||
blocks.assign(volume(), Block::Air);
|
||||
present = SelectionMask(Vec3i{0, 0, 0}, dims);
|
||||
}
|
||||
|
||||
std::size_t ClipboardBuffer::volume() const {
|
||||
return static_cast<std::size_t>(size.x) * static_cast<std::size_t>(size.y) *
|
||||
static_cast<std::size_t>(size.z);
|
||||
}
|
||||
|
||||
bool ClipboardBuffer::inBounds(int x, int y, int z) const {
|
||||
return x >= 0 && x < size.x && y >= 0 && y < size.y && z >= 0 && z < size.z;
|
||||
}
|
||||
|
||||
std::size_t ClipboardBuffer::indexOf(int x, int y, int z) const {
|
||||
return (static_cast<std::size_t>(y) * static_cast<std::size_t>(size.z) +
|
||||
static_cast<std::size_t>(z)) *
|
||||
static_cast<std::size_t>(size.x) +
|
||||
static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
Block ClipboardBuffer::at(int x, int y, int z) const {
|
||||
if (!inBounds(x, y, z)) return Block::Air;
|
||||
return blocks[indexOf(x, y, z)];
|
||||
}
|
||||
|
||||
bool ClipboardBuffer::filledAt(int x, int y, int z) const {
|
||||
if (!inBounds(x, y, z)) return false;
|
||||
return present.contains(Vec3i{x, y, z});
|
||||
}
|
||||
|
||||
void ClipboardBuffer::put(int x, int y, int z, Block block, bool filled) {
|
||||
if (!inBounds(x, y, z)) return;
|
||||
blocks[indexOf(x, y, z)] = block;
|
||||
present.setCell(Vec3i{x, y, z}, filled);
|
||||
}
|
||||
|
||||
void ClipboardBuffer::clearCell(int x, int y, int z) {
|
||||
if (!inBounds(x, y, z)) return;
|
||||
blocks[indexOf(x, y, z)] = Block::Air;
|
||||
present.remove(Vec3i{x, y, z});
|
||||
}
|
||||
|
||||
ClipboardBuffer ClipboardBuffer::copyFromWorld(const World& world, const Selection& selection) {
|
||||
if (selection.empty()) return ClipboardBuffer{};
|
||||
Vec3i origin = selection.min();
|
||||
ClipboardBuffer copy(selection.dims(), Vec3i{0, 0, 0});
|
||||
selection.forEach([&](const Vec3i& cell) {
|
||||
copy.put(cell.x - origin.x, cell.y - origin.y, cell.z - origin.z,
|
||||
world.blockAt(cell.x, cell.y, cell.z), true);
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
ClipboardBuffer ClipboardBuffer::rotatedY(Rotation rotation) const {
|
||||
if (rotation == Rotation::None || empty()) return *this;
|
||||
|
||||
bool swapsAxes = rotation == Rotation::Cw90 || rotation == Rotation::Cw270;
|
||||
Vec3i newDims = swapsAxes ? Vec3i{size.z, size.y, size.x} : size;
|
||||
|
||||
auto mapCell = [&](int x, int y, int z, int& outX, int& outY, int& outZ) {
|
||||
outY = y;
|
||||
switch (rotation) {
|
||||
case Rotation::Cw90:
|
||||
outX = size.z - 1 - z;
|
||||
outZ = x;
|
||||
break;
|
||||
case Rotation::Cw180:
|
||||
outX = size.x - 1 - x;
|
||||
outZ = size.z - 1 - z;
|
||||
break;
|
||||
case Rotation::Cw270:
|
||||
outX = z;
|
||||
outZ = size.x - 1 - x;
|
||||
break;
|
||||
case Rotation::None:
|
||||
outX = x;
|
||||
outZ = z;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user