From 2504538a3a164f27a96f413f5b389f8dad6b2cac Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 28 Aug 2019 08:29:02 +0200 Subject: Added a basic PalettedBlockArea implementation (#4377) --- src/BlockTypePalette.cpp | 80 ++++++++++++++ src/BlockTypePalette.h | 44 ++++++++ src/CMakeLists.txt | 4 + src/Cuboid.h | 22 ++++ src/PalettedBlockArea.cpp | 261 ++++++++++++++++++++++++++++++++++++++++++++++ src/PalettedBlockArea.h | 114 ++++++++++++++++++++ 6 files changed, 525 insertions(+) create mode 100644 src/BlockTypePalette.cpp create mode 100644 src/BlockTypePalette.h create mode 100644 src/PalettedBlockArea.cpp create mode 100644 src/PalettedBlockArea.h (limited to 'src') diff --git a/src/BlockTypePalette.cpp b/src/BlockTypePalette.cpp new file mode 100644 index 000000000..fabf5698e --- /dev/null +++ b/src/BlockTypePalette.cpp @@ -0,0 +1,80 @@ +#include "Globals.h" +#include "BlockTypePalette.h" + + + + +BlockTypePalette::BlockTypePalette() +{ + // Nothing needed yet +} + + + + + +UInt32 BlockTypePalette::index(const AString & aBlockTypeName, const BlockState & aBlockState) +{ + auto idx = maybeIndex(aBlockTypeName, aBlockState); + if (idx.second) + { + return idx.first; + } + + // Not found, append: + mPalette.push_back(std::make_pair(aBlockTypeName, aBlockState)); + return static_cast(mPalette.size() - 1); +} + + + + + +std::pair BlockTypePalette::maybeIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const +{ + auto count = mPalette.size(); + for (size_t idx = 0; idx < count; ++idx) + { + const auto & entry = mPalette[idx]; + if ((entry.first == aBlockTypeName) && (entry.second == aBlockState)) + { + return std::make_pair(static_cast(idx), true); + } + } + return std::make_pair(0, false); +} + + + + + +UInt32 BlockTypePalette::count() const +{ + return static_cast(mPalette.size()); +} + + + + + +const std::pair & BlockTypePalette::entry(UInt32 aIndex) const +{ + ASSERT(aIndex < mPalette.size()); + return mPalette[aIndex]; +} + + + + + +std::map BlockTypePalette::createTransformMap(const BlockTypePalette & aOther) +{ + std::map res; + auto numIndices = aOther.count(); + for (UInt32 idx = 0; idx < numIndices; ++idx) + { + const auto & e = aOther.mPalette[idx]; + res[idx] = index(e.first, e.second); + } + return res; +} diff --git a/src/BlockTypePalette.h b/src/BlockTypePalette.h new file mode 100644 index 000000000..47318f171 --- /dev/null +++ b/src/BlockTypePalette.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include "BlockState.h" + + + + + +/** Holds a palette that maps block type + state into numbers. +Used primarily by PalettedBlockArea to translate between numeric and stringular block representation. +The object itself provides no thread safety, users of this class need to handle locking, if required. */ +class BlockTypePalette +{ +public: + + /** Create a new empty instance. */ + BlockTypePalette(); + + /** Returns the index of the specified block type name and state. + If the combination is not found, it is added to the palette and the new index is returned. */ + UInt32 index(const AString & aBlockTypeName, const BlockState & aBlockState); + + /** Returns the of the specified block type name and state, if it exists. + If the combination is not found, returns . */ + std::pair maybeIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const; + + /** Returns the total number of entries in the palette. */ + UInt32 count() const; + + /** Returns the blockspec represented by the specified palette index. + The index must be valid (ASSERTed). */ + const std::pair & entry(UInt32 aIndex) const; + + /** Adds missing entries from aOther to this, and returns an index-transform map from aOther to this. + Used when pasting two areas, to transform the src palette to dst palette. */ + std::map createTransformMap(const BlockTypePalette & aOther); + + +protected: + + /** The palette. Each item in the vector represents a single entry in the palette, the vector index is the palette index. */ + std::vector> mPalette; +}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd92a969c..072eb6c97 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ SET (SRCS BlockID.cpp BlockInfo.cpp BlockState.cpp + BlockTypePalette.cpp BlockTypeRegistry.cpp BrewingRecipes.cpp Broadcaster.cpp @@ -59,6 +60,7 @@ SET (SRCS MonsterConfig.cpp NetherPortalScanner.cpp OverridesSettingsRepository.cpp + PalettedBlockArea.cpp ProbabDistrib.cpp RankManager.cpp RCONServer.cpp @@ -87,6 +89,7 @@ SET (HDRS BlockInfo.h BlockState.h BlockTracer.h + BlockTypePalette.h BlockTypeRegistry.h BrewingRecipes.h BoundingBox.h @@ -140,6 +143,7 @@ SET (HDRS NetherPortalScanner.h OpaqueWorld.h OverridesSettingsRepository.h + PalettedBlockArea.h ProbabDistrib.h RankManager.h RCONServer.h diff --git a/src/Cuboid.h b/src/Cuboid.h index ed36a2298..4fefb8f57 100644 --- a/src/Cuboid.h +++ b/src/Cuboid.h @@ -117,6 +117,28 @@ public: /** If needed, expands the cuboid so that it contains the specified point. Assumes sorted. Doesn't contract. */ void Engulf(Vector3i a_Point); + // tolua_end + + /** Checks the two cuboids for equality. */ + bool operator == (const cCuboid & aOther) const + { + return ( + (p1.x == aOther.p1.x) && + (p1.y == aOther.p1.y) && + (p1.z == aOther.p1.z) && + (p2.x == aOther.p2.x) && + (p2.y == aOther.p2.y) && + (p2.z == aOther.p2.z) + ); + } + + bool operator != (const cCuboid & aOther) const + { + return !operator ==(aOther); + } + + + // tolua_begin private: diff --git a/src/PalettedBlockArea.cpp b/src/PalettedBlockArea.cpp new file mode 100644 index 000000000..e703adec0 --- /dev/null +++ b/src/PalettedBlockArea.cpp @@ -0,0 +1,261 @@ +#include "Globals.h" +#include "PalettedBlockArea.h" + + + + + +PalettedBlockArea::PalettedBlockArea() +{ + // Nothing needed yet +} + + + + + +PalettedBlockArea PalettedBlockArea::createFilled(Vector3i aSize, const AString & aBlockTypeName, const BlockState & aBlockState) +{ + ASSERT(aSize.x > 0); + ASSERT(aSize.y > 0); + ASSERT(aSize.z > 0); + + PalettedBlockArea res; + auto numBlocks = static_cast(aSize.x) * static_cast(aSize.y) * static_cast(aSize.z); + if (numBlocks >= std::numeric_limits::max()) + { + // We use 32-bit indices in some functions (for ARM speed), so we need the entire area to fit into UInt32 + throw std::runtime_error("Size is too large"); + } + res.mSize = aSize; + res.mBlocks.resize(static_cast(numBlocks)); + res.fill(aBlockTypeName, aBlockState); + return res; +} + + + + + +cCuboid PalettedBlockArea::whole() const +{ + return cCuboid(Vector3i(), mSize); +} + + + + + +void PalettedBlockArea::setBlock(Vector3i aPos, const AString & aBlockTypeName, const BlockState & aBlockState) +{ + setBlock(aPos, paletteIndex(aBlockTypeName, aBlockState)); +} + + + + + +void PalettedBlockArea::setBlock(Vector3i aPos, UInt32 aPaletteIndex) +{ + ASSERT(isPositionValid(aPos)); + ASSERT(aPaletteIndex < mPalette.count()); + + auto idx = positionToIndex(aPos); + mBlocks[idx] = aPaletteIndex; +} + + + + + +UInt32 PalettedBlockArea::paletteIndex(const AString & aBlockTypeName, const BlockState & aBlockState) +{ + return mPalette.index(aBlockTypeName, aBlockState); +} + + + + + +std::pair PalettedBlockArea::maybePaletteIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const +{ + return mPalette.maybeIndex(aBlockTypeName, aBlockState); +} + + + + + +UInt32 PalettedBlockArea::blockPaletteIndex(Vector3i aPos) const +{ + auto idx = positionToIndex(aPos); + return mBlocks[idx]; +} + + + + + +const std::pair & PalettedBlockArea::block(Vector3i aPos) const +{ + return paletteEntry(blockPaletteIndex(aPos)); +} + + + + + +const std::pair & PalettedBlockArea::paletteEntry(UInt32 aPaletteIndex) const +{ + return mPalette.entry(aPaletteIndex); +} + + + + + +bool PalettedBlockArea::isPositionValid(Vector3i aPos) const +{ + return ( + (aPos.x >= 0) && (aPos.y >= 0) && (aPos.z >= 0) && // Non-negative coords + (aPos.x < mSize.x) && (aPos.y < mSize.y) && (aPos.z < mSize.z) // Fit into size + ); +} + + + + + +void PalettedBlockArea::fill(const AString & aBlockTypeName, const BlockState & aBlockState) +{ + BlockTypePalette btp; + auto idx = btp.index(aBlockTypeName, aBlockState); + std::swap(mPalette, btp); + std::fill(mBlocks.begin(), mBlocks.end(), idx); +} + + + + + +void PalettedBlockArea::paste(const PalettedBlockArea & aSrc, const cCuboid & aSrcCuboid, Vector3i aDstOrigin) +{ + // Clamp the src cuboid, first by src itself, then by this PBA's coord range: + cCuboid srcCuboid(aSrcCuboid); + srcCuboid.Sort(); + srcCuboid.Clamp(aSrc.whole()); + Vector3i maxSize = mSize - aDstOrigin; + srcCuboid.ClampSize(maxSize); + Vector3i dstOrigin(aDstOrigin); + + // If any aDstOrigin coord is lower than 0, adjust the coord and src cuboid size: + if (dstOrigin.x < 0) + { + srcCuboid.p1.x -= dstOrigin.x; + if (srcCuboid.p1.x >= srcCuboid.p2.x) + { + return; + } + dstOrigin.x = 0; + } + if (dstOrigin.y < 0) + { + srcCuboid.p1.y -= dstOrigin.y; + if (srcCuboid.p1.y >= srcCuboid.p2.y) + { + return; + } + dstOrigin.y = 0; + } + if (dstOrigin.z < 0) + { + srcCuboid.p1.z -= dstOrigin.z; + if (srcCuboid.p1.z >= srcCuboid.p2.z) + { + return; + } + dstOrigin.z = 0; + } + + // Create a transform map from aSrc's palette to our palette: + auto paletteTransform = mPalette.createTransformMap(aSrc.mPalette); + + // Copy the data: + UInt32 srcStrideY = static_cast(aSrc.size().x * aSrc.size().z); + UInt32 srcStrideZ = static_cast(aSrc.size().x); + UInt32 dstStrideY = static_cast(mSize.x * mSize.z); + UInt32 dstStrideZ = static_cast(mSize.x); + UInt32 minX = static_cast(srcCuboid.p1.x); + UInt32 maxX = static_cast(srcCuboid.p2.x); + UInt32 minY = static_cast(srcCuboid.p1.y); + UInt32 maxY = static_cast(srcCuboid.p2.y); + UInt32 minZ = static_cast(srcCuboid.p1.z); + UInt32 maxZ = static_cast(srcCuboid.p2.z); + UInt32 dstX = static_cast(dstOrigin.x); + UInt32 dstY = static_cast(dstOrigin.y); + UInt32 dstZ = static_cast(dstOrigin.z); + for (UInt32 y = minY; y < maxY; ++y) + { + UInt32 srcOfsY = y * srcStrideY; + UInt32 dstOfsY = (y - minY + dstY) * dstStrideY; + for (UInt32 z = minZ; z < maxZ; ++z) + { + UInt32 srcOfs = srcOfsY + z * srcStrideZ + minX; + UInt32 dstOfs = dstOfsY + (z - minZ + dstZ) * dstStrideZ + dstX; + for (UInt32 x = minX; x < maxX; ++x) + { + mBlocks[dstOfs] = paletteTransform[aSrc.mBlocks[srcOfs]]; + srcOfs += 1; + dstOfs += 1; + } + } + } +} + + + + + +void PalettedBlockArea::crop(const cCuboid & aArea) +{ + cCuboid area(aArea); + area.Clamp(whole()); + + // Copy the data: + UInt32 srcStrideY = static_cast(size().x * size().z); + UInt32 srcStrideZ = static_cast(size().x); + UInt32 dstStrideY = static_cast(area.DifX() * area.DifZ()); + UInt32 dstStrideZ = static_cast(area.DifZ()); + UInt32 minX = static_cast(area.p1.x); + UInt32 maxX = static_cast(area.p2.x); + UInt32 minY = static_cast(area.p1.y); + UInt32 maxY = static_cast(area.p2.y); + UInt32 minZ = static_cast(area.p1.z); + UInt32 maxZ = static_cast(area.p2.z); + for (UInt32 y = minY; y < maxY; ++y) + { + UInt32 srcOfsY = (y - minY) * srcStrideY; + UInt32 dstOfsY = y * dstStrideY; + for (UInt32 z = minZ; z < maxZ; ++z) + { + UInt32 srcOfs = srcOfsY + (z - minZ) * srcStrideZ + minX; + UInt32 dstOfs = dstOfsY + z * dstStrideZ; + for (UInt32 x = minX; x < maxX; ++x) + { + mBlocks[dstOfs] = mBlocks[srcOfs]; + srcOfs += 1; + dstOfs += 1; + } + } + } +} + + + + + +UInt32 PalettedBlockArea::positionToIndex(Vector3i aPos) const +{ + ASSERT(isPositionValid(aPos)); + return static_cast(aPos.x + aPos.z * mSize.x + aPos.y * mSize.x * mSize.z); +} diff --git a/src/PalettedBlockArea.h b/src/PalettedBlockArea.h new file mode 100644 index 000000000..3866d405b --- /dev/null +++ b/src/PalettedBlockArea.h @@ -0,0 +1,114 @@ +#pragma once + + + + + +#include + +#include "BlockTypePalette.h" +#include "Cuboid.h" + + + + + +/** Represents an area of blocks that are represented using a palette. +The object itself provides no thread safety, users of this class need to handle locking, if required. +The PalettedBlockArea always contains Blocks and their associated BlockEntities, it may optionally contain Entities. +There's no way to instantiate this class directly, you need to use either createFilled(), or read from cWorld. */ +class PalettedBlockArea +{ +public: + + /** Creates a new PBA of the specified size filled with the specified block. + Throws if there is an error (memory allocation etc.) */ + static PalettedBlockArea createFilled(Vector3i aSize, const AString & aBlockTypeName, const BlockState & aBlockState); + + /** Returns the actual size of the area in all 3 axes. */ + const Vector3i & size() const { return mSize; } + + /** Returns a cCuboid that encompasses the entire PBA. + Technically, {0, 0, 0} to mSize. */ + cCuboid whole() const; + + /** Sets a single block using its full blockspec. + The position must be valid (ASSERTed). + If the block is not already in palette, it is added. */ + void setBlock(Vector3i aPos, const AString & aBlockTypeName, const BlockState & aBlockState); + + /** Sets a single block using an index to the palette (retrieved earlier by paletteIndex()). + The position must be valid (ASSERTed). + The palette index must be valid (ASSERTed). */ + void setBlock(Vector3i aPos, UInt32 aPaletteIndex); + + /** Returns the index into the palette that is used by the specified full blockspec. + Adds the blockspec to palette if not already there. */ + UInt32 paletteIndex(const AString & aBlockTypeName, const BlockState & aBlockState); + + /** Returns the into the palette that is used by the specified full blockspec. + Returns if blockspec not in palette. */ + std::pair maybePaletteIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const; + + /** Returns the index into the palette for the block at the specified pos. + The position must be valid (ASSERTed). */ + UInt32 blockPaletteIndex(Vector3i aPos) const; + + /** Returns the full blockspec of the block at the specified position. + The position must be valid (ASSERTed). */ + const std::pair & block(Vector3i aPos) const; + + /** Returns the blockspec represented by the specified palette index. + The index must be valid (ASSERTed). */ + const std::pair & paletteEntry(UInt32 aPaletteIndex) const; + + /** Returns true if the specified position is within the size bounds of the area. */ + bool isPositionValid(Vector3i aPos) const; + + /** Fills the entire PBA with a single block of the specified type. + The palette is reset to one containing only the single block. */ + void fill(const AString & aBlockTypeName, const BlockState & aBlockState); + + /** Pastes (copies verbatim) a cCuboid out of the src PBA into this PBA. + aSrcCuboid is the coord range in aSrc that will be copied (min-coord is inclusive, max-coord is exclusive). + aDstOrigin is the coord relative to this PBA where the lowest coords of the copied area will be put. + aDstOrigin can be outside of this PBA's coord range (only part of the src is copied). + Automatically crops aSrcCuboid so that the copied part is entirely inside this PBA's coord range. */ + void paste(const PalettedBlockArea & aSrc, const cCuboid & aSrcCuboid, Vector3i aDstOrigin = Vector3i()); + + /** Pastes (copies verbatim) the entire src PBA into this PBA. + aDstOrigin is the coord relative to this PBA where the lowest coords of the copied area will be put. + aDstOrigin can be outside of this PBA's coord range (only part of the src is copied). + Gracefully handles situations where the copied src PBA goes outside of this PBA's coord range. */ + inline void paste(const PalettedBlockArea & aSrc, Vector3i aDstOrigin = Vector3i()) + { + paste(aSrc, aSrc.whole(), aDstOrigin); + } + + /** Crops this PBA by the specified coords. + aArea is first cropped to the size of this PBA (so it's only possible to shrink a PBA, not enlarge). */ + void crop(const cCuboid & aArea); + + /** Returns the (reqd-only) palette used internally by this object. */ + const BlockTypePalette & palette() { return mPalette; } + + +protected: + + /** The palette used in the area. */ + BlockTypePalette mPalette; + + /** The blocks contained in the area, stored as indices into mPalette. */ + std::vector mBlocks; + + /** The size (dimensions) of the area. */ + Vector3i mSize; + + + /** Creates a new uninitialized instance (all sizes zero). */ + PalettedBlockArea(); + + /** Converts the position to index in mBlocks. + This may be removed later on when optimizing the RAM usage of this class by compressing low-palette-count PBAs. */ + UInt32 positionToIndex(Vector3i aPos) const; +}; -- cgit v1.2.3