feat: extend Schematic
This commit is contained in:
1 parent
1a5d637423
commit
0f1f623a69
1 file changed
+163
@@ -158,3 +158,166 @@ ClipboardBuffer ClipboardBuffer::rotatedY(Rotation rotation) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int anchorX = 0, anchorY = 0, anchorZ = 0;
|
||||||
|
mapCell(handle.x, handle.y, handle.z, anchorX, anchorY, anchorZ);
|
||||||
|
ClipboardBuffer turned(newDims, Vec3i{anchorX, anchorY, anchorZ});
|
||||||
|
|
||||||
|
for (int y = 0; y < size.y; ++y)
|
||||||
|
for (int z = 0; z < size.z; ++z)
|
||||||
|
for (int x = 0; x < size.x; ++x) {
|
||||||
|
int tx = 0, ty = 0, tz = 0;
|
||||||
|
mapCell(x, y, z, tx, ty, tz);
|
||||||
|
turned.put(tx, ty, tz, at(x, y, z), filledAt(x, y, z));
|
||||||
|
}
|
||||||
|
return turned;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipboardBuffer ClipboardBuffer::mirrored(MirrorAxis axis) const {
|
||||||
|
if (empty()) return *this;
|
||||||
|
auto mapCell = [&](int x, int y, int z, int& outX, int& outY, int& outZ) {
|
||||||
|
outX = axis == MirrorAxis::X ? size.x - 1 - x : x;
|
||||||
|
outY = axis == MirrorAxis::Y ? size.y - 1 - y : y;
|
||||||
|
outZ = axis == MirrorAxis::Z ? size.z - 1 - z : z;
|
||||||
|
};
|
||||||
|
|
||||||
|
int anchorX = 0, anchorY = 0, anchorZ = 0;
|
||||||
|
mapCell(handle.x, handle.y, handle.z, anchorX, anchorY, anchorZ);
|
||||||
|
ClipboardBuffer flipped(size, Vec3i{anchorX, anchorY, anchorZ});
|
||||||
|
|
||||||
|
for (int y = 0; y < size.y; ++y)
|
||||||
|
for (int z = 0; z < size.z; ++z)
|
||||||
|
for (int x = 0; x < size.x; ++x) {
|
||||||
|
int tx = 0, ty = 0, tz = 0;
|
||||||
|
mapCell(x, y, z, tx, ty, tz);
|
||||||
|
flipped.put(tx, ty, tz, at(x, y, z), filledAt(x, y, z));
|
||||||
|
}
|
||||||
|
return flipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipboardBuffer ClipboardBuffer::trimmed() const {
|
||||||
|
Vec3i lo{size.x, size.y, size.z};
|
||||||
|
Vec3i hi{-1, -1, -1};
|
||||||
|
for (int y = 0; y < size.y; ++y)
|
||||||
|
for (int z = 0; z < size.z; ++z)
|
||||||
|
for (int x = 0; x < size.x; ++x) {
|
||||||
|
if (!filledAt(x, y, z) || isAir(at(x, y, z))) continue;
|
||||||
|
lo = {std::min(lo.x, x), std::min(lo.y, y), std::min(lo.z, z)};
|
||||||
|
hi = {std::max(hi.x, x), std::max(hi.y, y), std::max(hi.z, z)};
|
||||||
|
}
|
||||||
|
if (hi.x < lo.x) return ClipboardBuffer{};
|
||||||
|
|
||||||
|
ClipboardBuffer tight({hi.x - lo.x + 1, hi.y - lo.y + 1, hi.z - lo.z + 1},
|
||||||
|
{handle.x - lo.x, handle.y - lo.y, handle.z - lo.z});
|
||||||
|
for (int y = lo.y; y <= hi.y; ++y)
|
||||||
|
for (int z = lo.z; z <= hi.z; ++z)
|
||||||
|
for (int x = lo.x; x <= hi.x; ++x)
|
||||||
|
tight.put(x - lo.x, y - lo.y, z - lo.z, at(x, y, z), filledAt(x, y, z));
|
||||||
|
return tight;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClipboardBuffer::sameContent(const ClipboardBuffer& other) const {
|
||||||
|
if (!(size == other.size)) return false;
|
||||||
|
for (int y = 0; y < size.y; ++y)
|
||||||
|
for (int z = 0; z < size.z; ++z)
|
||||||
|
for (int x = 0; x < size.x; ++x) {
|
||||||
|
if (filledAt(x, y, z) != other.filledAt(x, y, z)) return false;
|
||||||
|
if (!filledAt(x, y, z)) continue;
|
||||||
|
if (at(x, y, z) != other.at(x, y, z)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Schematic::bitsForPalette(std::size_t paletteSize) {
|
||||||
|
if (paletteSize <= 2u) return 1u;
|
||||||
|
if (paletteSize <= 4u) return 2u;
|
||||||
|
if (paletteSize <= 16u) return 4u;
|
||||||
|
return 8u;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Schematic::errorText(SchematicError error) {
|
||||||
|
switch (error) {
|
||||||
|
case SchematicError::None: return "ok";
|
||||||
|
case SchematicError::BadMagic: return "not a schematic";
|
||||||
|
case SchematicError::UnsupportedVersion: return "unsupported schematic version";
|
||||||
|
case SchematicError::Truncated: return "truncated schematic";
|
||||||
|
case SchematicError::BadChecksum: return "checksum mismatch";
|
||||||
|
case SchematicError::DimensionMismatch: return "dimensions do not match payload";
|
||||||
|
case SchematicError::BadPalette: return "invalid block palette";
|
||||||
|
}
|
||||||
|
return "unknown schematic error";
|
||||||
|
}
|
||||||
|
|
||||||
|
Schematic Schematic::capture(const World& world, const Selection& selection,
|
||||||
|
const std::string& name, const std::string& author, uint64_t tick) {
|
||||||
|
return fromBuffer(ClipboardBuffer::copyFromWorld(world, selection), name, author, tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
Schematic Schematic::fromBuffer(const ClipboardBuffer& buffer, const std::string& name,
|
||||||
|
const std::string& author, uint64_t tick) {
|
||||||
|
Schematic out;
|
||||||
|
out.cells = buffer;
|
||||||
|
out.meta.version = SchematicVersion::V2;
|
||||||
|
out.meta.name = name;
|
||||||
|
out.meta.author = author;
|
||||||
|
out.meta.tick = tick;
|
||||||
|
out.refreshHeaderFromBuffer();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Schematic::refreshHeaderFromBuffer() {
|
||||||
|
std::vector<Block> palette;
|
||||||
|
std::vector<uint16_t> slotOfBlock;
|
||||||
|
buildPalette(cells, palette, slotOfBlock);
|
||||||
|
meta.dims = cells.dims();
|
||||||
|
meta.anchor = cells.anchor();
|
||||||
|
meta.paletteSize = static_cast<uint16_t>(palette.size());
|
||||||
|
meta.indexBits = bitsForPalette(palette.size());
|
||||||
|
meta.hasVoidMask = cells.filledCount() != cells.volume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Schematic::setAnchor(const Vec3i& anchor) {
|
||||||
|
cells.setAnchor(anchor);
|
||||||
|
meta.anchor = anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Schematic::rotate(Rotation rotation) {
|
||||||
|
cells = cells.rotatedY(rotation);
|
||||||
|
refreshHeaderFromBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Schematic::mirror(MirrorAxis axis) {
|
||||||
|
cells = cells.mirrored(axis);
|
||||||
|
refreshHeaderFromBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Schematic::trim() {
|
||||||
|
cells = cells.trimmed();
|
||||||
|
refreshHeaderFromBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer Schematic::encode() const { return encode(SchematicVersion::V2); }
|
||||||
|
|
||||||
|
ByteBuffer Schematic::encode(SchematicVersion version) const {
|
||||||
|
std::vector<Block> palette;
|
||||||
|
std::vector<uint16_t> slotOfBlock;
|
||||||
|
buildPalette(cells, palette, slotOfBlock);
|
||||||
|
uint8_t bits = bitsForPalette(palette.size());
|
||||||
|
|
||||||
|
const Vec3i& dims = cells.dims();
|
||||||
|
std::size_t cellCount = cells.volume();
|
||||||
|
bool writeVoidMask =
|
||||||
|
version == SchematicVersion::V2 && cells.filledCount() != cellCount;
|
||||||
|
|
||||||
|
std::vector<uint16_t> indices;
|
||||||
|
indices.reserve(cellCount);
|
||||||
|
std::vector<uint16_t> presence;
|
||||||
|
presence.reserve(writeVoidMask ? cellCount : 0u);
|
||||||
|
for (int y = 0; y < dims.y; ++y)
|
||||||
|
for (int z = 0; z < dims.z; ++z)
|
||||||
|
for (int x = 0; x < dims.x; ++x) {
|
||||||
|
bool filled = cells.filledAt(x, y, z);
|
||||||
|
Block block = filled ? cells.at(x, y, z) : Block::Air;
|
||||||
|
indices.push_back(slotOfBlock[static_cast<std::size_t>(block)]);
|
||||||
|
if (writeVoidMask) presence.push_back(filled ? 1u : 0u);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user