diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/MinedMap.cpp | 8 | ||||
-rw-r--r-- | src/NBT/ByteArrayTag.hpp | 9 | ||||
-rw-r--r-- | src/World/Block.cpp | 57 | ||||
-rw-r--r-- | src/World/Block.hpp | 9 | ||||
-rw-r--r-- | src/World/Chunk.cpp | 103 | ||||
-rw-r--r-- | src/World/Chunk.hpp | 89 |
7 files changed, 212 insertions, 64 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd10223..5146071 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ link_directories(${ZLIB_LIBRARY_DIRS} ${LIBPNG_LIBRARY_DIRS}) add_executable(MinedMap MinedMap.cpp NBT/Tag.cpp + World/Block.cpp World/BlockType.cpp World/Chunk.cpp World/Region.cpp diff --git a/src/MinedMap.cpp b/src/MinedMap.cpp index a95b6d6..594268c 100644 --- a/src/MinedMap.cpp +++ b/src/MinedMap.cpp @@ -96,12 +96,8 @@ int main(int argc, char *argv[]) { World::Chunk::Blocks layer = chunk->getTopLayer(); for (size_t x = 0; x < World::Chunk::SIZE; x++) { - for (size_t z = 0; z < World::Chunk::SIZE; z++) { - const World::BlockType &t = World::BLOCK_TYPES[layer.blocks[x][z].id]; - - if (t.opaque) - image[Z*World::Chunk::SIZE+z][X*World::Chunk::SIZE+x] = htonl((t.color << 8) | 0xff); - } + for (size_t z = 0; z < World::Chunk::SIZE; z++) + image[Z*World::Chunk::SIZE+z][X*World::Chunk::SIZE+x] = htonl(layer.blocks[x][z].getColor()); } } } diff --git a/src/NBT/ByteArrayTag.hpp b/src/NBT/ByteArrayTag.hpp index 2081759..4f2d331 100644 --- a/src/NBT/ByteArrayTag.hpp +++ b/src/NBT/ByteArrayTag.hpp @@ -55,9 +55,16 @@ public: return len; } - const uint8_t & operator[](size_t i) const { + const uint8_t & get(size_t i) const { return value[i]; } + + uint8_t getHalf(size_t i) const { + if (i % 2) + return (value[i/2] >> 4); + else + return (value[i/2] & 0xf); + } }; } diff --git a/src/World/Block.cpp b/src/World/Block.cpp new file mode 100644 index 0000000..3aa4af3 --- /dev/null +++ b/src/World/Block.cpp @@ -0,0 +1,57 @@ +/* + Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net> + 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 "Block.hpp" +#include "BlockType.hpp" + + +namespace MinedMap { +namespace World { + +uint32_t Block::getColor() const { + const World::BlockType &t = World::BLOCK_TYPES[id]; + + if (!t.opaque) + return 0; + + uint8_t r = t.color >> 16; + uint8_t g = t.color >> 8; + uint8_t b = t.color; + + uint8_t light = (blockLight > skyLight) ? blockLight : skyLight; + + float lightCoef = light/30.0f + 0.5f; + float heightCoef = height/255.0f + 0.5f; + + r *= lightCoef * heightCoef; + g *= lightCoef * heightCoef; + b *= lightCoef * heightCoef; + + return (r << 24) | (g << 16) | (b << 8) | 0xff; +} + +} +} diff --git a/src/World/Block.hpp b/src/World/Block.hpp index 4bf0a0d..f2d481a 100644 --- a/src/World/Block.hpp +++ b/src/World/Block.hpp @@ -35,6 +35,15 @@ namespace World { struct Block { uint8_t id; uint8_t data; + + unsigned height; + + uint8_t blockLight; + uint8_t skyLight; + + Block() : id(0), data(0), height(0), blockLight(0), skyLight(0) {} + + uint32_t getColor() const; }; } diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index 99d40a5..c3c15f8 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -26,9 +26,6 @@ #include "Chunk.hpp" #include "BlockType.hpp" -#include "../Util.hpp" -#include "../NBT/ByteTag.hpp" -#include "../NBT/ByteArrayTag.hpp" #include <iostream> #include <stdexcept> @@ -92,69 +89,89 @@ void Chunk::inflateChunk(Buffer buffer) { void Chunk::parseChunk() { Buffer nbt(data.get(), len); std::pair<std::string, std::shared_ptr<const NBT::Tag>> tag = NBT::Tag::readNamedTag(&nbt); - - std::shared_ptr<const NBT::CompoundTag>::operator=(std::dynamic_pointer_cast<const NBT::CompoundTag>(tag.second)); - - if (!(*this) || tag.first != "") + if (tag.first != "") throw std::invalid_argument("invalid root tag"); - level = assertValue((*this)->get<NBT::CompoundTag>("Level")); - sections = assertValue(level->get<NBT::ListTag<NBT::CompoundTag>>("Sections")); + root = assertValue(std::dynamic_pointer_cast<const NBT::CompoundTag>(tag.second)); + level = assertValue(root->get<NBT::CompoundTag>("Level")); } void Chunk::analyzeChunk() { - maxY = (assertValue(sections->back()->get<NBT::ByteTag>("Y"))->getValue() + 1) * SIZE; + std::shared_ptr<const NBT::ByteTag> lightPopulatedTag = level->get<NBT::ByteTag>("LightPopulated"); + if (!lightPopulatedTag && lightPopulatedTag->getValue()) + throw std::invalid_argument("light data missing"); - for (auto §ion : *sections) { - if (assertValue(section->get<NBT::ByteArrayTag>("Blocks"))->getLength() != SIZE*SIZE*SIZE - || assertValue(section->get<NBT::ByteArrayTag>("Data"))->getLength() != SIZE*SIZE*SIZE/2) - throw std::invalid_argument("corrupt chunk data"); - } -} + for (auto §ion : *assertValue(level->get<NBT::ListTag<NBT::CompoundTag>>("Sections"))) + sections.emplace_back(*section); -uint8_t Chunk::getBlockAt(const std::shared_ptr<const NBT::CompoundTag> §ion, size_t x, size_t y, size_t z) const { - return (*section->get<NBT::ByteArrayTag>("Blocks"))[getIndex(x, y, z)]; -} + maxY = sections.back().getY() + SIZE; + blocks.reset(new Block[maxY * SIZE * SIZE]); -uint8_t Chunk::getDataAt(const std::shared_ptr<const NBT::CompoundTag> §ion, size_t x, size_t y, size_t z) const { - size_t i = getIndex(x, y, z); - uint8_t v = (*section->get<NBT::ByteArrayTag>("Data"))[i / 2]; + for (auto §ion : sections) { + unsigned Y = section.getY(); - if (i % 2) - return (v >> 4); - else - return (v & 0xf); + for (size_t y = 0; y < SIZE; y++) { + for (size_t z = 0; z < SIZE; z++) { + for (size_t x = 0; x < SIZE; x++) { + Block &block = getBlock(x, Y+y, z); + + block.id = section.getBlockAt(x, y, z); + block.data = section.getDataAt(x, y, z); + block.blockLight = section.getBlockLightAt(x, y, z); + block.skyLight = section.getSkyLightAt(x, y, z); + } + } + } + } } Chunk::Blocks Chunk::getTopLayer() const { size_t done = 0; - Blocks blocks = {}; + Blocks ret; - for (auto it = sections->rbegin(); it != sections->rend(); ++it) { + for (ssize_t y = maxY-1; y >= 0; y--) { if (done == SIZE*SIZE) break; - for (ssize_t y = 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++) { - for (size_t z = 0; z < SIZE; z++) { - if (blocks.blocks[x][z].id) - continue; - - uint8_t block = getBlockAt(*it, x, y, z); - if (BLOCK_TYPES[block].opaque) { - blocks.blocks[x][z].id = block; - blocks.blocks[x][z].data = getDataAt(*it, x, y, z); - done++; - } + if (ret.blocks[x][z].id) + continue; + + const Block &block = getBlock(x, y, z); + if (!BLOCK_TYPES[block.id].opaque) + continue; + + Block &b = ret.blocks[x][z]; + + b.id = block.id; + b.data = block.data; + + const Block *lightBlock; + if (y < maxY-1) + lightBlock = &getBlock(x, y+1, z); + else + lightBlock = █ + + b.blockLight = lightBlock->blockLight; + b.skyLight = lightBlock->skyLight; + + + unsigned h; + for (h = y; h > 0; h--) { + const Block &block2 = getBlock(x, h, z); + if (block2.id != 8 && block2.id != 9) + break; } + + b.height = h; + + done++; } } } - return blocks; + return ret; } } diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index 22054db..333d5e5 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -30,8 +30,11 @@ #include "Block.hpp" #include "../Buffer.hpp" #include "../UniqueCPtr.hpp" +#include "../Util.hpp" #include "../NBT/CompoundTag.hpp" #include "../NBT/ListTag.hpp" +#include "../NBT/ByteTag.hpp" +#include "../NBT/ByteArrayTag.hpp" #include <cstdint> #include <tuple> @@ -40,42 +43,100 @@ namespace MinedMap { namespace World { -class Chunk : public std::shared_ptr<const NBT::CompoundTag> { +class Chunk { public: static const size_t SIZE = 16; + class Section { + private: + 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; + } + + std::shared_ptr<const NBT::ByteArrayTag> blocks; + std::shared_ptr<const NBT::ByteArrayTag> data; + + std::shared_ptr<const NBT::ByteArrayTag> blockLight; + std::shared_ptr<const NBT::ByteArrayTag> skyLight; + + unsigned Y; + + public: + Section(const NBT::CompoundTag &s) { + blocks = assertValue(s.get<NBT::ByteArrayTag>("Blocks")); + data = assertValue(s.get<NBT::ByteArrayTag>("Data")); + blockLight = assertValue(s.get<NBT::ByteArrayTag>("BlockLight")); + skyLight = assertValue(s.get<NBT::ByteArrayTag>("SkyLight")); + + if (blocks->getLength() != SIZE*SIZE*SIZE || data->getLength() != SIZE*SIZE*SIZE/2 + || blockLight->getLength() != SIZE*SIZE*SIZE/2 || skyLight->getLength() != SIZE*SIZE*SIZE/2) + throw std::invalid_argument("corrupt chunk data"); + + Y = assertValue(s.get<NBT::ByteTag>("Y"))->getValue() * SIZE; + } + + uint8_t getBlockAt(size_t x, size_t y, size_t z) const { + return blocks->get(getIndex(x, y, z)); + } + + uint8_t getDataAt(size_t x, size_t y, size_t z) const { + return data->getHalf(getIndex(x, y, z)); + } + + uint8_t getBlockLightAt(size_t x, size_t y, size_t z) const { + return blockLight->getHalf(getIndex(x, y, z)); + } + + uint8_t getSkyLightAt(size_t x, size_t y, size_t z) const { + return skyLight->getHalf(getIndex(x, y, z)); + } + + unsigned getY() const { + return Y; + } + }; + struct Blocks { Block blocks[SIZE][SIZE]; }; private: - 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 256*y + 16*z + x; - } - size_t len; UniqueCPtr<uint8_t[]> data; + std::shared_ptr<const NBT::CompoundTag> root; std::shared_ptr<const NBT::CompoundTag> level; - std::shared_ptr<const NBT::ListTag<NBT::CompoundTag>> sections; + + std::vector<Section> sections; unsigned maxY; + std::unique_ptr<Block[]> blocks; + + Block & getBlock(size_t x, size_t y, size_t z) { + return blocks[SIZE*SIZE*y + SIZE*z + x]; + } + + const Block & getBlock(size_t x, size_t y, size_t z) const { + return blocks[SIZE*SIZE*y + SIZE*z + x]; + } + void inflateChunk(Buffer buffer); void parseChunk(); void analyzeChunk(); - uint8_t getBlockAt(const std::shared_ptr<const NBT::CompoundTag> §ion, size_t x, size_t y, size_t z) const; - uint8_t getDataAt(const std::shared_ptr<const NBT::CompoundTag> §ion, size_t x, size_t y, size_t z) const; - public: Chunk(Buffer buffer); - const NBT::ListTag<NBT::CompoundTag> & getSections() const { - return *sections; + const NBT::CompoundTag & getLevel() const { + return *level; + } + + const std::vector<Section> & getSections() const { + return sections; } Blocks getTopLayer() const; |