From 210f651807847c290ab7ba14c64f9eff1c47284e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 24 Jul 2018 20:00:16 +0200 Subject: World: factor out Section handling to a generic interface --- src/CMakeLists.txt | 1 + src/World/Block.cpp | 2 - src/World/Block.hpp | 17 +++---- src/World/Chunk.cpp | 130 ++++++++++++++++++++------------------------------ src/World/Chunk.hpp | 71 ++++++++------------------- src/World/Section.cpp | 56 ++++++++++++++++++++++ src/World/Section.hpp | 110 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 246 insertions(+), 141 deletions(-) create mode 100644 src/World/Section.cpp create mode 100644 src/World/Section.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e4d23f..2b57fa9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable(MinedMap World/ChunkData.cpp World/Level.cpp World/Region.cpp + World/Section.cpp ) set_target_properties(MinedMap PROPERTIES COMPILE_FLAGS "-std=c++11 -Wall") target_link_libraries(MinedMap ${ZLIB_LIBRARIES} ${PNG_LIBRARIES}) diff --git a/src/World/Block.cpp b/src/World/Block.cpp index 06017fd..b4a464b 100644 --- a/src/World/Block.cpp +++ b/src/World/Block.cpp @@ -26,14 +26,12 @@ #include "Block.hpp" #include "../Resource/Biome.hpp" -#include "../Resource/BlockType.hpp" namespace MinedMap { namespace World { uint32_t Block::getColor() const { - const Resource::BlockType *type = Resource::LEGACY_BLOCK_TYPES.types[id][data]; if (!type || !type->opaque) return 0; diff --git a/src/World/Block.hpp b/src/World/Block.hpp index 4975faa..e2dcfde 100644 --- a/src/World/Block.hpp +++ b/src/World/Block.hpp @@ -26,28 +26,25 @@ #pragma once -#include +#include "../NBT/CompoundTag.hpp" +#include "../Resource/BlockType.hpp" namespace MinedMap { namespace World { struct Block { - uint8_t id; - uint8_t data; - + const Resource::BlockType *type; unsigned height; - uint8_t blockLight; - uint8_t skyLight; - uint8_t biome; - Block() : id(0), data(0), height(0), blockLight(0), skyLight(0), biome(0) {} - Block(uint8_t id0, uint8_t data0, unsigned height0, uint8_t blockLight0, uint8_t skyLight0, uint8_t biome0) - : id(id0), data(data0), height(height0), blockLight(blockLight0), skyLight(skyLight0), biome(biome0) {} uint32_t getColor() const; + + operator bool() const { + return type; + } }; } diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index d4c6029..50ac81e 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -25,14 +25,11 @@ #include "Chunk.hpp" -#include "../Resource/BlockType.hpp" +#include "../NBT/ListTag.hpp" +#include "../NBT/StringTag.hpp" -#include -#include -#include #include - -#include +#include namespace MinedMap { @@ -41,100 +38,77 @@ namespace World { Chunk::Chunk(const ChunkData *data) { level = assertValue(data->getRoot().get("Level")); - std::shared_ptr lightPopulatedTag = level->get("LightPopulated"); - bool lightPopulated = lightPopulatedTag && lightPopulatedTag->getValue(); + std::shared_ptr sectionsTag = level->get("Sections"); + if (!sectionsTag) + return; - sections = assertValue(level->get("Sections")); - const NBT::CompoundTag *lastSection = assertValue(dynamic_cast(sections->back().get())); - maxY = (assertValue(lastSection->get("Y"))->getValue() + 1) * SIZE; + biomeBytes = level->get("Biomes"); + biomeInts = level->get("Biomes"); + assertValue(biomeBytes || biomeInts); - - std::shared_ptr biomeTag = assertValue(level->get("Biomes")); - if (biomeTag->getLength() != SIZE*SIZE) + if (biomeBytes && biomeBytes->getLength() != SIZE*SIZE) + throw std::invalid_argument("corrupt biome data"); + else if (biomeInts && biomeInts->getLength() != SIZE*SIZE) throw std::invalid_argument("corrupt biome data"); - biomes = biomeTag->getValue(); - - blockIDs.reset(new uint8_t[maxY * SIZE * SIZE]); - blockData.reset(new uint8_t[maxY * SIZE * SIZE / 2]); - blockSkyLight.reset(new uint8_t[maxY * SIZE * SIZE / 2]); - blockBlockLight.reset(new uint8_t[maxY * SIZE * SIZE / 2]); - - std::memset(blockIDs.get(), 0, maxY * SIZE * SIZE); - std::memset(blockData.get(), 0, maxY * SIZE * SIZE / 2); - std::memset(blockSkyLight.get(), 0xff, maxY * SIZE * SIZE / 2); - std::memset(blockBlockLight.get(), 0, maxY * SIZE * SIZE / 2); - - - for (auto §ionTag : *sections) { - const NBT::CompoundTag *section = assertValue(dynamic_cast(sectionTag.get())); - std::shared_ptr blocks = assertValue(section->get("Blocks")); - std::shared_ptr data = assertValue(section->get("Data")); - size_t Y = assertValue(section->get("Y"))->getValue(); - - if (blocks->getLength() != SIZE*SIZE*SIZE || data->getLength() != SIZE*SIZE*SIZE/2) - throw std::invalid_argument("corrupt chunk data"); - - if (lightPopulated) { - std::shared_ptr blockLight = assertValue(section->get("BlockLight")); - std::shared_ptr skyLight = assertValue(section->get("SkyLight")); + for (auto &sTag : *sectionsTag) { + auto s = std::dynamic_pointer_cast(sTag); + std::unique_ptr
section = Section::makeSection(s); + size_t Y = section->getY(); + sections.resize(Y); + sections.push_back(std::move(section)); + } +} - if (blockLight->getLength() != SIZE*SIZE*SIZE/2 || skyLight->getLength() != SIZE*SIZE*SIZE/2) - throw std::invalid_argument("corrupt chunk data"); +bool Chunk::getBlock(Block *block, const Section *section, size_t x, size_t y, size_t z, uint8_t prev_light) const { + if (block->height > 0) + return false; - std::memcpy(blockBlockLight.get() + Y*SIZE*SIZE*SIZE/2, blockLight->getValue(), SIZE*SIZE*SIZE/2); - std::memcpy(blockSkyLight.get() + Y*SIZE*SIZE*SIZE/2, skyLight->getValue(), SIZE*SIZE*SIZE/2); - } + const Resource::BlockType *type = section->getBlockStateAt(x, y, z); + if (!type || !type->opaque) + return false; - std::memcpy(blockIDs.get() + Y*SIZE*SIZE*SIZE, blocks->getValue(), SIZE*SIZE*SIZE); - std::memcpy(blockData.get() + Y*SIZE*SIZE*SIZE/2, data->getValue(), SIZE*SIZE*SIZE/2); + if (!block->type) { + block->type = type; + block->blockLight = prev_light; + block->biome = getBiomeAt(x, z); } -} -Block Chunk::getBlock(size_t x, size_t y, size_t z) const { - size_t y2 = y; - if (y2 < maxY-1) - y2++; + if (type->blue) + return false; - unsigned h; - for (h = y; h > 0; h--) { - uint8_t id2 = getBlockAt(x, h, z); - if (id2 != 8 && id2 != 9) - break; - } + block->height = SIZE*section->getY() + y; - return Block( - getBlockAt(x, y, z), - getDataAt(x, y, z), - h, - getBlockLightAt(x, y2, z), - getSkyLightAt(x, y2, z), - getBiomeAt(x, z) - ); + return true; } Chunk::Blocks Chunk::getTopLayer() const { size_t done = 0; - Blocks ret; + Blocks ret = {}; + uint8_t prev_light[SIZE][SIZE] = {}; - for (ssize_t y = maxY-1; y >= 0; y--) { + for (ssize_t Y = sections.size() - 1; Y >= 0; Y--) { if (done == SIZE*SIZE) break; - for (size_t z = 0; z < SIZE; z++) { - for (size_t x = 0; x < SIZE; x++) { - if (ret.blocks[x][z].id) - continue; + if (!sections[Y]) { + std::memset(prev_light, 0, sizeof(prev_light)); + continue; + } + + const Section *section = sections[Y].get(); - uint8_t id = getBlockAt(x, y, z); - uint8_t data = getDataAt(x, y, z); + for (ssize_t y = SIZE-1; y >= 0; y--) { + if (done == SIZE*SIZE) + break; - const Resource::BlockType *type = Resource::LEGACY_BLOCK_TYPES.types[id][data]; - if (!type || !type->opaque) - continue; + for (size_t z = 0; z < SIZE; z++) { + for (size_t x = 0; x < SIZE; x++) { + if (getBlock(&ret.blocks[x][z], section, x, y, z, prev_light[x][z])) + done++; - ret.blocks[x][z] = getBlock(x, y, z); - done++; + prev_light[x][z] = section->getBlockLightAt(x, y, z); + } } } } diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index 4bd301d..8451cbe 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -29,13 +29,13 @@ #include "Block.hpp" #include "ChunkData.hpp" -#include "../Util.hpp" -#include "../NBT/ListTag.hpp" -#include "../NBT/ByteTag.hpp" +#include "Section.hpp" #include "../NBT/ByteArrayTag.hpp" +#include "../NBT/IntArrayTag.hpp" +#include "../Resource/BlockType.hpp" +#include "../Util.hpp" #include -#include namespace MinedMap { @@ -43,62 +43,27 @@ namespace World { class Chunk { public: - static const size_t SIZE = 16; + static const size_t SIZE = Section::SIZE; struct Blocks { Block blocks[SIZE][SIZE]; }; - private: std::shared_ptr level; - std::shared_ptr sections; - - - unsigned maxY; - - std::unique_ptr blockIDs; - std::unique_ptr blockData; - std::unique_ptr blockSkyLight; - std::unique_ptr blockBlockLight; - const uint8_t *biomes; + std::vector> sections; + std::shared_ptr biomeBytes; + std::shared_ptr biomeInts; - size_t getIndex(size_t x, size_t y, size_t z) const { - if (x >= SIZE || y >= maxY || z >= SIZE) - throw std::range_error("Chunk::getIndex(): bad coordinates"); - - return SIZE*SIZE*y + SIZE*z + x; - } - - uint8_t getHalf(const uint8_t *v, size_t x, size_t y, size_t z) const { - size_t i = getIndex(x, y, z); - - if (i % 2) - return (v[i/2] >> 4); + uint8_t getBiomeAt(size_t x, size_t z) const { + if (biomeBytes) + return biomeBytes->getValue()[z*SIZE + x]; else - return (v[i/2] & 0xf); + return biomeInts->getValue(z*SIZE + x); } - uint8_t getBlockAt(size_t x, size_t y, size_t z) const { - return blockIDs[getIndex(x, y, z)]; - } - - uint8_t getDataAt(size_t x, size_t y, size_t z) const { - return getHalf(blockData.get(), x, y, z); - } - - uint8_t getBlockLightAt(size_t x, size_t y, size_t z) const { - return getHalf(blockBlockLight.get(), x, y, z); - } - - uint8_t getSkyLightAt(size_t x, size_t y, size_t z) const { - return getHalf(blockSkyLight.get(), x, y, z); - } - - uint8_t getBiomeAt(size_t x, size_t z) const { - return biomes[z*SIZE + x]; - } + bool getBlock(Block *block, const Section *section, size_t x, size_t y, size_t z, uint8_t prev_light) const; public: Chunk(const ChunkData *data); @@ -107,11 +72,15 @@ public: return *level; } - const NBT::ListTag & getSections() const { - return *sections; + const Resource::BlockType * getBlockStateAt(size_t x, size_t y, size_t z) const { + size_t Y = y / SIZE; + + if (Y >= sections.size() || !sections[Y]) + return nullptr; + + return sections[Y]->getBlockStateAt(x, y % SIZE, z); } - Block getBlock(size_t x, size_t y, size_t z) const; Blocks getTopLayer() const; }; diff --git a/src/World/Section.cpp b/src/World/Section.cpp new file mode 100644 index 0000000..6f06435 --- /dev/null +++ b/src/World/Section.cpp @@ -0,0 +1,56 @@ +/* + Copyright (c) 2015-2018, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "Section.hpp" +#include "../NBT/ByteTag.hpp" +#include "../Util.hpp" + + +namespace MinedMap { +namespace World { + +Section::Section(const std::shared_ptr §ion) { + Y = assertValue(section->get("Y"))->getValue(); + blockLight = section->get("BlockLight"); +} + + +std::unique_ptr
Section::makeSection(const std::shared_ptr §ion) { + std::shared_ptr blocks = assertValue(section->get("Blocks")); + std::shared_ptr data = assertValue(section->get("Data")); + + return std::unique_ptr
(new LegacySection(section, std::move(blocks), std::move(data))); +} + +const Resource::BlockType * LegacySection::getBlockStateAt(size_t x, size_t y, size_t z) const { + uint8_t type = getBlockAt(x, y, z); + uint8_t data = getDataAt(x, y, z); + + return Resource::LEGACY_BLOCK_TYPES.types[type][data]; +} + +} +} diff --git a/src/World/Section.hpp b/src/World/Section.hpp new file mode 100644 index 0000000..a4686e3 --- /dev/null +++ b/src/World/Section.hpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2015-2018, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#pragma once + + +#include "../NBT/ByteArrayTag.hpp" +#include "../NBT/CompoundTag.hpp" +#include "../Resource/BlockType.hpp" + +#include +#include + + +namespace MinedMap { +namespace World { + +class Section { +public: + static const size_t SIZE = 16; + +private: + size_t Y; + std::shared_ptr blockLight; + +protected: + static size_t getIndex(size_t x, size_t y, size_t z) { + if (x >= SIZE || y >= SIZE || z >= SIZE) + throw std::range_error("Chunk::getIndex(): bad coordinates"); + + return SIZE*SIZE*y + SIZE*z + x; + } + + static uint8_t getHalf(const uint8_t *v, size_t x, size_t y, size_t z) { + size_t i = getIndex(x, y, z); + + if (i % 2) + return (v[i/2] >> 4); + else + return (v[i/2] & 0xf); + } + + Section(const std::shared_ptr §ion); + +public: + virtual ~Section() {} + + size_t getY() const { return Y; }; + + virtual const Resource::BlockType * getBlockStateAt(size_t x, size_t y, size_t z) const = 0; + + uint8_t getBlockLightAt(size_t x, size_t y, size_t z) const { + if (!blockLight) + return 0; + + return getHalf(blockLight->getValue(), x, y, z); + } + + static std::unique_ptr
makeSection(const std::shared_ptr §ion); +}; + +class LegacySection : public Section { +private: + std::shared_ptr blocks; + std::shared_ptr data; + + + uint8_t getBlockAt(size_t x, size_t y, size_t z) const { + return blocks->getValue()[getIndex(x, y, z)]; + } + + uint8_t getDataAt(size_t x, size_t y, size_t z) const { + return getHalf(data->getValue(), x, y, z); + } + +public: + LegacySection( + const std::shared_ptr §ion, + std::shared_ptr &&blocks0, + std::shared_ptr &&data0 + ) : Section(section), blocks(blocks0), data(data0) {} + + virtual const Resource::BlockType * getBlockStateAt(size_t x, size_t y, size_t z) const; +}; + +} +} -- cgit v1.2.3