World: factor out Section handling to a generic interface

This commit is contained in:
Matthias Schiffer 2018-07-24 20:00:16 +02:00
parent dd432af298
commit 210f651807
Signed by: neocturne
GPG key ID: 16EF3F64CB201D9C
7 changed files with 248 additions and 143 deletions

View file

@ -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})

View file

@ -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;

View file

@ -26,28 +26,25 @@
#pragma once
#include <cstdint>
#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;
}
};
}

View file

@ -25,14 +25,11 @@
#include "Chunk.hpp"
#include "../Resource/BlockType.hpp"
#include "../NBT/ListTag.hpp"
#include "../NBT/StringTag.hpp"
#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <cstring>
#include <zlib.h>
#include <stdexcept>
namespace MinedMap {
@ -41,100 +38,77 @@ namespace World {
Chunk::Chunk(const ChunkData *data) {
level = assertValue(data->getRoot().get<NBT::CompoundTag>("Level"));
std::shared_ptr<const NBT::ByteTag> lightPopulatedTag = level->get<NBT::ByteTag>("LightPopulated");
bool lightPopulated = lightPopulatedTag && lightPopulatedTag->getValue();
std::shared_ptr<const NBT::ListTag> sectionsTag = level->get<NBT::ListTag>("Sections");
if (!sectionsTag)
return;
sections = assertValue(level->get<NBT::ListTag>("Sections"));
const NBT::CompoundTag *lastSection = assertValue(dynamic_cast<const NBT::CompoundTag *>(sections->back().get()));
maxY = (assertValue(lastSection->get<NBT::ByteTag>("Y"))->getValue() + 1) * SIZE;
biomeBytes = level->get<NBT::ByteArrayTag>("Biomes");
biomeInts = level->get<NBT::IntArrayTag>("Biomes");
assertValue(biomeBytes || biomeInts);
std::shared_ptr<const NBT::ByteArrayTag> biomeTag = assertValue(level->get<NBT::ByteArrayTag>("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 &sectionTag : *sections) {
const NBT::CompoundTag *section = assertValue(dynamic_cast<const NBT::CompoundTag *>(sectionTag.get()));
std::shared_ptr<const NBT::ByteArrayTag> blocks = assertValue(section->get<NBT::ByteArrayTag>("Blocks"));
std::shared_ptr<const NBT::ByteArrayTag> data = assertValue(section->get<NBT::ByteArrayTag>("Data"));
size_t Y = assertValue(section->get<NBT::ByteTag>("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<const NBT::ByteArrayTag> blockLight = assertValue(section->get<NBT::ByteArrayTag>("BlockLight"));
std::shared_ptr<const NBT::ByteArrayTag> skyLight = assertValue(section->get<NBT::ByteArrayTag>("SkyLight"));
if (blockLight->getLength() != SIZE*SIZE*SIZE/2 || skyLight->getLength() != SIZE*SIZE*SIZE/2)
throw std::invalid_argument("corrupt chunk data");
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);
}
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);
for (auto &sTag : *sectionsTag) {
auto s = std::dynamic_pointer_cast<const NBT::CompoundTag>(sTag);
std::unique_ptr<Section> section = Section::makeSection(s);
size_t Y = section->getY();
sections.resize(Y);
sections.push_back(std::move(section));
}
}
Block Chunk::getBlock(size_t x, size_t y, size_t z) const {
size_t y2 = y;
if (y2 < maxY-1)
y2++;
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;
unsigned h;
for (h = y; h > 0; h--) {
uint8_t id2 = getBlockAt(x, h, z);
if (id2 != 8 && id2 != 9)
break;
const Resource::BlockType *type = section->getBlockStateAt(x, y, z);
if (!type || !type->opaque)
return false;
if (!block->type) {
block->type = type;
block->blockLight = prev_light;
block->biome = getBiomeAt(x, z);
}
return Block(
getBlockAt(x, y, z),
getDataAt(x, y, z),
h,
getBlockLightAt(x, y2, z),
getSkyLightAt(x, y2, z),
getBiomeAt(x, z)
);
if (type->blue)
return false;
block->height = SIZE*section->getY() + y;
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;
}
uint8_t id = getBlockAt(x, y, z);
uint8_t data = getDataAt(x, y, z);
const Section *section = sections[Y].get();
const Resource::BlockType *type = Resource::LEGACY_BLOCK_TYPES.types[id][data];
if (!type || !type->opaque)
continue;
for (ssize_t y = SIZE-1; y >= 0; y--) {
if (done == SIZE*SIZE)
break;
ret.blocks[x][z] = getBlock(x, y, z);
done++;
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++;
prev_light[x][z] = section->getBlockLightAt(x, y, z);
}
}
}
}

View file

@ -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 <cstdint>
#include <tuple>
namespace MinedMap {
@ -43,63 +43,28 @@ 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<const NBT::CompoundTag> level;
std::shared_ptr<const NBT::ListTag> sections;
std::vector<std::unique_ptr<Section>> sections;
unsigned maxY;
std::unique_ptr<uint8_t[]> blockIDs;
std::unique_ptr<uint8_t[]> blockData;
std::unique_ptr<uint8_t[]> blockSkyLight;
std::unique_ptr<uint8_t[]> blockBlockLight;
const uint8_t *biomes;
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);
else
return (v[i/2] & 0xf);
}
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);
}
std::shared_ptr<const NBT::ByteArrayTag> biomeBytes;
std::shared_ptr<const NBT::IntArrayTag> biomeInts;
uint8_t getBiomeAt(size_t x, size_t z) const {
return biomes[z*SIZE + x];
if (biomeBytes)
return biomeBytes->getValue()[z*SIZE + x];
else
return biomeInts->getValue(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;
};

56
src/World/Section.cpp Normal file
View file

@ -0,0 +1,56 @@
/*
Copyright (c) 2015-2018, 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 "Section.hpp"
#include "../NBT/ByteTag.hpp"
#include "../Util.hpp"
namespace MinedMap {
namespace World {
Section::Section(const std::shared_ptr<const NBT::CompoundTag> &section) {
Y = assertValue(section->get<NBT::ByteTag>("Y"))->getValue();
blockLight = section->get<NBT::ByteArrayTag>("BlockLight");
}
std::unique_ptr<Section> Section::makeSection(const std::shared_ptr<const NBT::CompoundTag> &section) {
std::shared_ptr<const NBT::ByteArrayTag> blocks = assertValue(section->get<NBT::ByteArrayTag>("Blocks"));
std::shared_ptr<const NBT::ByteArrayTag> data = assertValue(section->get<NBT::ByteArrayTag>("Data"));
return std::unique_ptr<Section>(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];
}
}
}

110
src/World/Section.hpp Normal file
View file

@ -0,0 +1,110 @@
/*
Copyright (c) 2015-2018, 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.
*/
#pragma once
#include "../NBT/ByteArrayTag.hpp"
#include "../NBT/CompoundTag.hpp"
#include "../Resource/BlockType.hpp"
#include <cstdint>
#include <stdexcept>
namespace MinedMap {
namespace World {
class Section {
public:
static const size_t SIZE = 16;
private:
size_t Y;
std::shared_ptr<const NBT::ByteArrayTag> 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<const NBT::CompoundTag> &section);
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<Section> makeSection(const std::shared_ptr<const NBT::CompoundTag> &section);
};
class LegacySection : public Section {
private:
std::shared_ptr<const NBT::ByteArrayTag> blocks;
std::shared_ptr<const NBT::ByteArrayTag> 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<const NBT::CompoundTag> &section,
std::shared_ptr<const NBT::ByteArrayTag> &&blocks0,
std::shared_ptr<const NBT::ByteArrayTag> &&data0
) : Section(section), blocks(blocks0), data(data0) {}
virtual const Resource::BlockType * getBlockStateAt(size_t x, size_t y, size_t z) const;
};
}
}