Add sunlight generation - wip implementation, does not pass MapBlocks.

* Added light_propagates block definition parameter.
* Improved BlockLight propagation and rendering.
* Change index order to improve caching.
master
Nicole Collings 2020-05-22 13:21:08 -07:00
parent b50732ec5e
commit 571453843e
24 changed files with 537 additions and 326 deletions

View File

@ -95,7 +95,7 @@ void main() {
float sunlightIntensity = 1;
// float sunlightIntensity = aLight.w * clamp(sin(time / 2.5) + 0.25, 0, 1) / MAX_SUNLIGHT;
vec3 blockLightColor = (aLight.xyz / MAX_BLOCKLIGHT) * vec3(1 + sunlightIntensity / 4);
vec3 sunlightColor = clamp(sunlightIntensity * 1.25 * vec3(1, 0.9, 0.75), 0, 1);
vec3 sunlightColor = clamp(sunlightIntensity * 1.25 * vec3(1, 0.9, 0.75) * (aLight.w / 16.0), 0, 1);
vec3 resultantLight = vec3(max(sunlightColor.x, blockLightColor.x), max(sunlightColor.y, blockLightColor.y), max(sunlightColor.z, blockLightColor.z));
vec4 worldPos = model * pos;

View File

@ -7,13 +7,13 @@
LocalDefinitionAtlas::LocalDefinitionAtlas(TextureAtlas& atlas) {
//Invalid Node
BlockModel invalidModel = BlockModel::createCube({atlas["_missing"]}, {}, {});
BlockDef* invalid = new BlockDef("invalid", "Invalid (you broke the game!)", 64, invalidModel, invalidModel, true, {}, {{}}, {{}});
BlockDef* invalid = new BlockDef("invalid", "Invalid (you broke the game!)", 64, invalidModel, invalidModel, true, {}, false, {{}}, {{}});
defs.push_back(invalid);
defTable.insert({"invalid", 0});
//Air Node
BlockModel nullModel {};
BlockDef* air = new BlockDef("air", "Air", 64, nullModel, nullModel, false, {}, {}, {}, 1);
BlockDef* air = new BlockDef("air", "Air", 64, nullModel, nullModel, false, {}, true, {}, {}, 1);
defs.push_back(air);
defTable.insert({"air", 1});
}

View File

@ -8,12 +8,12 @@ ServerDefinitionAtlas::ServerDefinitionAtlas() {
//Invalid Node
BlockModel invalidModel = BlockModel::createCube({}, {}, {});
BlockDef* invalid = new BlockDef("invalid", "Invalid (you broke the game!)", 1, invalidModel, invalidModel, true, {}, {{}}, {{}}, INVALID);
BlockDef* invalid = new BlockDef("invalid", "Invalid (you broke the game!)", 1, invalidModel, invalidModel, true, {}, false, {{}}, {{}}, INVALID);
registerDef(invalid);
//Air Node
BlockModel nullModel {};
BlockDef* air = new BlockDef("air", "Air (you broke the game!)", 1, nullModel, nullModel, false, {}, {}, {}, AIR);
BlockDef* air = new BlockDef("air", "Air (you broke the game!)", 1, nullModel, nullModel, false, {}, true, {}, {}, AIR);
registerDef(air);
}

View File

@ -2,12 +2,13 @@
// Created by aurailus on 28/01/19.
//
#include "MapGen.h"
#include "NoiseSample.h"
#include "../../game/scene/world/Schematic.h"
#include <random>
#include <cmath>
#include "MapGen.h"
#include "NoiseSample.h"
MapGen::MapGen(unsigned int seed, DefinitionAtlas& defs, BiomeAtlas& biomes, std::shared_ptr<MapGenProps> props) :
seed(seed),
defs(defs),
@ -26,6 +27,8 @@ MapGen::chunk_partials_map MapGen::generateMapBlock(glm::ivec3 mbPos) {
}
}
generateSunlight(chunks, mbPos);
for (auto& chunk : chunks) {
// Delete MapGenJobs
delete chunk.second.first;
@ -122,9 +125,8 @@ void MapGen::buildElevationMap(chunk_partials_map& chunks, chunk_partial& chunk)
}
void MapGen::generateBlocks(chunk_partial& chunk) {
glm::ivec3 lp {};
auto dupe = std::make_unique<BlockChunk>(*chunk.second);
std::unique_ptr<BlockChunk> dupe = nullptr;
if (chunk.second->partial) dupe = std::make_unique<BlockChunk>(*chunk.second);
chunk.second->blocks = {};
chunk.second->biomes = {};
@ -133,7 +135,7 @@ void MapGen::generateBlocks(chunk_partial& chunk) {
for (unsigned short x = 0; x < 16; x++) {
biomeArray[x] = {};
for (unsigned short z = 0; z < 16; z++) {
lp = {x, 0, z};
glm::ivec3 lp = {x, 0, z};
biomeArray[x][z] = biomes.getBiomeAt(
chunk.first->temperature.get(lp),
chunk.first->humidity.get(lp),
@ -142,7 +144,7 @@ void MapGen::generateBlocks(chunk_partial& chunk) {
}
for (unsigned short m = 0; m < 4096; m++) {
Vec::indAssignVec(m, lp);
glm::ivec3 lp = Space::Block::fromIndex(m);
auto& biome = biomes.biomeFromId(biomeArray[lp.x][lp.z]);
unsigned int storedBlock = (chunk.second->blocks.size() <= 0 ? -1 : chunk.second->blocks[chunk.second->blocks.size() - 1]);
@ -166,7 +168,7 @@ void MapGen::generateBlocks(chunk_partial& chunk) {
}
}
if (dupe->partial) for (unsigned short i = 0; i < 4096; i++) {
if (dupe) for (unsigned short i = 0; i < 4096; i++) {
unsigned int b = dupe->getBlock(i);
if (b != DefinitionAtlas::INVALID) chunk.second->setBlock(i, b);
}
@ -176,17 +178,6 @@ void MapGen::generateStructures(chunk_partials_map& chunks, chunk_partial& chunk
std::default_random_engine generator(chunk.second->pos.x + chunk.second->pos.y * 30 + chunk.second->pos.z * 3.5);
std::uniform_real_distribution<float> distribution(0, 1);
// unsigned int cWood = defs.blockFromStr("zeus:default:wood").index;
// unsigned int cLeaves = defs.blockFromStr("zeus:default:leaves").index;
// unsigned int cAir = DefinitionAtlas::INVALID;
// Schematic c {};
// c.dimensions = {3, 3, 3};
// c.origin = {1, 0, 1};
// c.blocks = { cAir, cAir, cAir, cAir, cLeaves, cAir, cAir, cAir, cAir,
// cAir, cWood, cAir, cLeaves, cWood, cLeaves, cAir, cLeaves, cAir,
// cAir, cAir, cAir, cAir, cLeaves, cAir, cAir, cAir, cAir };
glm::ivec3 wp = chunk.second->pos;
glm::ivec3 lp;
@ -220,6 +211,89 @@ void MapGen::generateStructures(chunk_partials_map& chunks, chunk_partial& chunk
}
}
void MapGen::generateSunlight(MapGen::chunk_partials_map &chunks, glm::ivec3 mbPos) {
std::queue<SunlightNode> sunlightQueue;
glm::ivec3 c {};
for (c.x = 0; c.x < 4; c.x++) {
for (c.z = 0; c.z < 4; c.z++) {
c.y = 3;
BlockChunk* chunk = chunks[mbPos * 4 + c].second;
glm::ivec3 b {};
for (b.x = 0; b.x < 16; b.x++) {
for (b.z = 0; b.z < 16; b.z++) {
b.y = 15;
while (true) {
unsigned int ind = Space::Block::index(b);
if (defs.blockFromId(chunk->getBlock(ind)).lightPropagates) {
chunk->setSunlight(ind, 15);
// const static std::array<glm::ivec3, 4> checks {
// glm::ivec3 {-1, 0, 0}, glm::ivec3 {1, 0, 0}, glm::ivec3 {0, 0, -1}, glm::ivec3 {0, 0, 1}};
sunlightQueue.emplace(ind, chunk);
}
else {
c.y = 3;
chunk = chunks[mbPos * 4 + c].second;
break;
}
b.y--;
if (b.y < 0) {
b.y = 15;
c.y = c.y ? c.y - 1 : 3;
if (c.y == 3) break;
chunk = chunks[mbPos * 4 + c].second;
}
}
}
}
}
}
propogateSunlightNodes(chunks, sunlightQueue);
}
bool MapGen::containsWorldPos(BlockChunk *chunk, glm::ivec3 pos) {
return chunk && Space::Chunk::world::fromBlock(pos) == chunk->pos;
}
void MapGen::propogateSunlightNodes(MapGen::chunk_partials_map &chunks, std::queue<SunlightNode> &queue) {
while (!queue.empty()) {
SunlightNode& node = queue.front();
unsigned char lightLevel = node.chunk->getSunlight(node.index);
glm::ivec3 worldPos = node.chunk->pos * 16 + Space::Block::fromIndex(node.index);
const static std::array<glm::ivec3, 6> checks = { glm::ivec3 {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1} };
for (const auto& i : checks) {
glm::ivec3 check = worldPos + i;
BlockChunk* chunk;
if (containsWorldPos(node.chunk, check)) chunk = node.chunk;
else {
glm::ivec3 worldPos = Space::Chunk::world::fromBlock(check);
if (!chunks.count(worldPos)) continue;
chunk = chunks[worldPos].second;
if (!chunk) continue;
}
auto ind = Space::Block::index(check);
if (defs.blockFromId(chunk->getBlock(ind)).lightPropagates && chunk->getSunlight(ind) + 2 <= lightLevel) {
int subtract = 1;
if (lightLevel == 15 && i == checks[2]) subtract = 0;
chunk->setSunlight(ind, lightLevel - subtract);
queue.emplace(ind, chunk);
}
}
queue.pop();
}
}
void MapGen::setBlock(glm::ivec3 worldPos, unsigned int block, MapGen::chunk_partials_map &chunks) {
if (block == DefinitionAtlas::INVALID) return;

View File

@ -4,6 +4,8 @@
#pragma once
#include <queue>
#include "MapGenJob.h"
#include "MapGenProps.h"
#include "../../util/Vec.h"
@ -21,6 +23,12 @@ public:
// If both are partials `b` takes preference, if one is a fully generated chunk the partial takes preference.
static std::shared_ptr<BlockChunk> combinePartials(std::shared_ptr<BlockChunk> a, std::shared_ptr<BlockChunk> b);
private:
struct SunlightNode {
SunlightNode(unsigned short index, BlockChunk* chunk) : index(index), chunk(chunk) {};
unsigned short index;
BlockChunk* chunk;
};
// Generate a chunk at `worldPos`, and place it and any partials in `chunks`.
void generateChunk(chunk_partials_map& chunks, glm::ivec3 worldPos);
@ -34,6 +42,11 @@ private:
void generateBlocks(chunk_partial& chunk);
void generateStructures(chunk_partials_map& chunks, chunk_partial& chunk);
// Generate sunlight on the mapgen threads to speed up perf
void generateSunlight(chunk_partials_map& chunks, glm::ivec3 mbPos);
bool containsWorldPos(BlockChunk *chunk, glm::ivec3 pos);
void propogateSunlightNodes(chunk_partials_map& chunks, std::queue<SunlightNode>& queue);
// Place block in the `chunks` array, creates a partial if necessary.
static void setBlock(glm::ivec3 worldPos, unsigned int block, chunk_partials_map& chunks);

View File

@ -12,6 +12,7 @@ BlockDef::BlockDef(
const BlockModel& farModel,
bool solid,
glm::ivec3 lightSource,
bool lightPropagates,
const std::vector<SelectionBox>& sBoxes,
const std::vector<SelectionBox>& cBoxes,
unsigned int index) :
@ -23,6 +24,7 @@ BlockDef::BlockDef(
culls(model.culls),
solid(solid),
lightSource(lightSource),
lightPropagates(lightPropagates),
sBoxes(sBoxes),
cBoxes(cBoxes) {}

View File

@ -23,6 +23,7 @@ public:
const BlockModel& farModel,
bool solid,
glm::ivec3 lightSource,
bool lightPropagates,
const std::vector<SelectionBox>& sBoxes,
const std::vector<SelectionBox>& cBoxes,
unsigned int index = 0
@ -35,6 +36,7 @@ public:
bool culls = false;
bool solid = false;
bool lightPropagates = false;
glm::ivec3 lightSource;

View File

@ -99,16 +99,16 @@ BlockDef& ChunkMeshGenerator::getBlockAt(const glm::ivec3& pos) {
}
glm::vec4 ChunkMeshGenerator::getLightAt(const glm::ivec3& pos) {
if (pos.x == 16) return adjacent[0]->getLightVec(Space::Block::index(pos - glm::ivec3 {16, 0, 0}));
if (pos.x == -1) return adjacent[1]->getLightVec(Space::Block::index(pos + glm::ivec3 {16, 0, 0}));
if (pos.x == 16) return adjacent[0]->getLight(Space::Block::index(pos - glm::ivec3 {16, 0, 0}));
if (pos.x == -1) return adjacent[1]->getLight(Space::Block::index(pos + glm::ivec3 {16, 0, 0}));
if (pos.y == 16) return adjacent[2]->getLightVec(Space::Block::index(pos - glm::ivec3 {0, 16, 0}));
if (pos.y == -1) return adjacent[3]->getLightVec(Space::Block::index(pos + glm::ivec3 {0, 16, 0}));
if (pos.y == 16) return adjacent[2]->getLight(Space::Block::index(pos - glm::ivec3 {0, 16, 0}));
if (pos.y == -1) return adjacent[3]->getLight(Space::Block::index(pos + glm::ivec3 {0, 16, 0}));
if (pos.z == 16) return adjacent[4]->getLightVec(Space::Block::index(pos - glm::ivec3 {0, 0, 16}));
if (pos.z == -1) return adjacent[5]->getLightVec(Space::Block::index(pos + glm::ivec3 {0, 0, 16}));
if (pos.z == 16) return adjacent[4]->getLight(Space::Block::index(pos - glm::ivec3 {0, 0, 16}));
if (pos.z == -1) return adjacent[5]->getLight(Space::Block::index(pos + glm::ivec3 {0, 0, 16}));
return chunk->getLightVec(Space::Block::index(pos));
return chunk->getLight(Space::Block::index(pos));
}
void ChunkMeshGenerator::addFaces(const glm::vec3 &offset, const std::vector<MeshPart> &meshParts, const glm::vec3& tint, glm::vec4 light) {
@ -134,7 +134,7 @@ void ChunkMeshGenerator::addFaces(const glm::vec3 &offset, const std::vector<Mes
mp.blendInd ? tint : glm::vec3 {1, 1, 1},
mp.blendInd ? vertex.blendMask : glm::vec2 {-1, -1},
Util::packFloat(vertex.nml),
light,
glm::vec4(light),
static_cast<float>(mp.shaderMod),
modData
});

View File

@ -263,6 +263,7 @@ namespace RegisterBlocks {
if (!nameOpt) throw identifier + " is missing name property!";
bool solid = blockTable.get_or("solid", true);
bool lightPropagates = blockTable.get_or("light_propagates", false);
auto maxStack = blockTable.get_or("stack", 64);
glm::vec3 lightSource {};
@ -299,6 +300,7 @@ namespace RegisterBlocks {
models.first, models.second,
solid,
lightSource,
lightPropagates,
std::move(selectionBoxes),
std::move(collisionBoxes),
defs.size() // Index

View File

@ -23,16 +23,16 @@ bool ServerGenStream::queue(glm::vec3 pos) {
return false;
}
std::unique_ptr<std::vector<std::shared_ptr<BlockChunk>>> ServerGenStream::update() {
auto finishedChunks = std::make_unique<std::vector<std::shared_ptr<BlockChunk>>>();
std::unique_ptr<std::vector<ServerGenStream::FinishedBlockJob>> ServerGenStream::update() {
auto finishedChunks = std::make_unique<std::vector<FinishedBlockJob>>();
for (auto& t : threads) {
for (auto& u : t.tasks) {
if (u.locked) continue;
if (!u.chunks.empty()) {
for (auto chunk : u.chunks)
finishedChunks->push_back(std::shared_ptr<BlockChunk>(chunk.second.second));
finishedChunks->push_back({u.pos, {}});
for (auto chunk : u.chunks) finishedChunks->back().chunks.push_back(std::shared_ptr<BlockChunk>(chunk.second.second));
u.chunks.clear();
}

View File

@ -18,6 +18,11 @@ public:
static const int THREADS = 4;
static const int THREAD_QUEUE_SIZE = 6;
struct FinishedBlockJob {
glm::ivec3 pos;
std::vector<std::shared_ptr<BlockChunk>> chunks;
};
explicit ServerGenStream(unsigned int seed, ServerGame& game);
~ServerGenStream();
@ -25,8 +30,7 @@ public:
bool queue(glm::vec3 pos);
// Returns a vector of BlockChunks that have finished generating,
// and gives the threads new data to work with.
std::unique_ptr<std::vector<std::shared_ptr<BlockChunk>>> update();
std::unique_ptr<std::vector<FinishedBlockJob>> update();
private:
struct Job {
bool locked = false;

View File

@ -9,6 +9,7 @@
#include "../conn/ClientList.h"
#include "../conn/ServerClient.h"
#include "../../util/Timer.h"
ServerWorld::ServerWorld(unsigned int seed, ServerGame& game, ClientList& clients) :
clientList(clients),
@ -58,13 +59,19 @@ void ServerWorld::update(double delta) {
auto finished = genStream->update();
generatedChunks = static_cast<int>(finished->size());
for (const auto& chunk : *finished) {
dimension.setChunk(chunk);
for (auto& mb : *finished) {
Timer t("finishing mapblock");
glm::ivec3 mapBlockPos = Space::MapBlock::world::fromChunk(chunk->pos);
if (chunk->generated) dimension.getMapBlock(mapBlockPos)->generated = true;
unsigned long long mapBlockIntegrity = dimension.getMapBlockIntegrity(mapBlockPos);
for (const auto& chunk : mb.chunks) {
dimension.setChunk(chunk);
// dimension.createSunlight(chunk->pos);
}
// dimension.propogateLight();
dimension.getMapBlock(mb.pos)->generated = true;
t.printElapsedMs();
unsigned long long mapBlockIntegrity = dimension.getMapBlockIntegrity(mb.pos);
for (auto& client : clientList.clients) {
if (client->hasPlayer) {
auto playerMapBlock = Space::MapBlock::world::fromBlock(client->getPos());
@ -73,14 +80,19 @@ void ServerWorld::update(double delta) {
{playerMapBlock.x - MB_GEN_H, playerMapBlock.y - MB_GEN_V, playerMapBlock.z - MB_GEN_H},
{playerMapBlock.x + MB_GEN_H, playerMapBlock.y + MB_GEN_V, playerMapBlock.z + MB_GEN_H}};
if (isInBounds(mapBlockPos, bounds) && client->getMapBlockIntegrity(mapBlockPos) < mapBlockIntegrity) {
client->setMapBlockIntegrity(mapBlockPos, mapBlockIntegrity);
sendChunk(chunk->pos, *client);
if (isInBounds(mb.pos, bounds) && client->getMapBlockIntegrity(mb.pos) < mapBlockIntegrity) {
client->setMapBlockIntegrity(mb.pos, mapBlockIntegrity);
//TODO: Replace with sendMapBlock
for (int i = 0; i < 64; i++) {
sendChunk(dimension.getMapBlock(mb.pos)->operator[](i)->pos, *client);
}
}
}
}
}
// Send the # of generated chunks to the client (debug),
// and trigger new chunks to be generated if a player has changed MapBlocks.
Packet r(PacketType::SERVER_INFO);

View File

@ -111,6 +111,18 @@ namespace Space {
glm::ivec3 local = Chunk::relative::toMapBlock(vec);
return static_cast<unsigned int>(local.x + MAPBLOCK_SIZE * (local.y + MAPBLOCK_SIZE * local.z));
}
// Return a local vector of an chunk within its mapblock.
static inline glm::ivec3 fromIndex(unsigned int ind) {
glm::ivec3 vec {};
vec.y = ind / (MAPBLOCK_SIZE * MAPBLOCK_SIZE);
ind -= (static_cast<int>(vec.y) * MAPBLOCK_SIZE * MAPBLOCK_SIZE);
vec.z = ind / MAPBLOCK_SIZE;
vec.x = ind % MAPBLOCK_SIZE;
return vec;
}
}
namespace Block {
@ -134,16 +146,16 @@ namespace Space {
// Get the index of a Block within its Chunk from its local or world position.
static inline unsigned int index(const glm::ivec3& vec) {
glm::ivec3 local = Block::relative::toChunk(vec);
return static_cast<unsigned int>(local.x + CHUNK_SIZE * (local.y + CHUNK_SIZE * local.z));
return static_cast<unsigned int>(local.x + CHUNK_SIZE * (local.z + CHUNK_SIZE * local.y));
}
// Return a local vector of an index within it's chunk.
// Return a local vector of an block within its chunk.
static inline glm::ivec3 fromIndex(unsigned int ind) {
glm::ivec3 vec {};
vec.z = ind / (CHUNK_SIZE * CHUNK_SIZE);
ind -= (static_cast<int>(vec.z) * CHUNK_SIZE * CHUNK_SIZE);
vec.y = ind / CHUNK_SIZE;
vec.y = ind / (CHUNK_SIZE * CHUNK_SIZE);
ind -= (static_cast<int>(vec.y) * CHUNK_SIZE * CHUNK_SIZE);
vec.z = ind / CHUNK_SIZE;
vec.x = ind % CHUNK_SIZE;
return vec;

View File

@ -30,9 +30,9 @@ namespace Vec {
};
static inline void indAssignVec(int ind, glm::ivec3& vec, unsigned int wid = 16) {
vec.z = ind / (wid * wid);
ind -= ((int)vec.z * wid * wid);
vec.y = ind / wid;
vec.y = ind / (wid * wid);
ind -= ((int)vec.y * wid * wid);
vec.z = ind / wid;
vec.x = ind % wid;
}
};

View File

@ -60,9 +60,9 @@ bool Dimension::setBlock(glm::ivec3 pos, unsigned int block) {
chunk->setBlock(Space::Block::relative::toChunk(pos), block);
glm::ivec3 oldLight = chunk->getBlockLight(Space::Block::index(pos));
glm::ivec4 oldLight = chunk->getLight(Space::Block::index(pos));
glm::ivec3 newLight = defs.blockFromId(block).lightSource;
if (oldLight != newLight) {
if (oldLight.x != newLight.x || oldLight.y != newLight.y || oldLight.z != newLight.z) {
if (abs(oldLight.x) + abs(oldLight.y) + abs(oldLight.z) != 0) removeLight(pos);
if (abs(newLight.x) + abs(newLight.y) + abs(newLight.z) != 0) addLight(pos, newLight);
}
@ -84,6 +84,42 @@ std::shared_ptr<MapBlock> Dimension::getOrCreateMapBlock(glm::ivec3 mapBlockPosi
return (*region)[index];
}
//
// Light related functions.
//
void Dimension::createSunlight(glm::ivec3 pos) {
// auto chunk = getChunk(pos);
// auto top = getChunk(pos + glm::ivec3 {0, 1, 0});
// if (top) {
// for (unsigned int i = 0; i < 256; i++) {
// unsigned int ind = Space::Block::index(glm::ivec3 {i / 16, 0, i % 16});
// auto light = top->getSunlight(ind);
// if (light != 0) {
// lightAddQueue[SUNLIGHT_CHANNEL].emplace(ind, top.get());
// if (light == 15) for (int j = 15; j >= 0; j--) {
// unsigned int ind = Space::Block::index({i / 16, j, i % 16});
// if (!defs.blockFromId(chunk->getBlock(ind)).solid) {
// chunk->setSunlight(ind, 15);
// lightAddQueue[SUNLIGHT_CHANNEL].emplace(ind, chunk.get());
// }
// }
// }
// }
// }
// else {
// for (unsigned int i = 0; i < 256; i++) {
// for (unsigned int j = 15; j >= 0; j--) {
// auto index = Space::Block::index(glm::ivec3{i / 16, j, i % 16});
// if (defs.blockFromId(chunk->getBlock(index)).solid) continue;
// chunk->setSunlight(index, 15);
// lightAddQueue[SUNLIGHT_CHANNEL].emplace(index, chunk.get());
// }
// }
// }
}
// Returns true if the provided pos references a block within chunk, otherwise returns false.
bool Dimension::containsWorldPos(BlockChunk *chunk, glm::ivec3 pos) {
return chunk && Space::Chunk::world::fromBlock(pos) == chunk->pos;
@ -91,10 +127,10 @@ bool Dimension::containsWorldPos(BlockChunk *chunk, glm::ivec3 pos) {
// Get the BlockLight of a block. This function can be accelerated
// by providing a chunk that might contain the world position.
glm::ivec3 Dimension::getBlockLight(glm::ivec3 worldPos, BlockChunk *chunk) {
if (containsWorldPos(chunk, worldPos)) return chunk->getBlockLight(Space::Block::index(worldPos));
glm::ivec4 Dimension::getLight(glm::ivec3 worldPos, BlockChunk *chunk) {
if (containsWorldPos(chunk, worldPos)) return chunk->getLight(Space::Block::index(worldPos));
auto oChunk = getChunk(Space::Chunk::world::fromBlock(worldPos)).get();
return (oChunk ? oChunk->getBlockLight(Space::Block::index(worldPos)) : glm::ivec3 {});
return (oChunk ? oChunk->getLight(Space::Block::index(worldPos)) : glm::ivec4 {});
}
void Dimension::addLight(glm::ivec3 pos, glm::ivec3 light) {
@ -110,7 +146,7 @@ void Dimension::addLight(glm::ivec3 pos, glm::ivec3 light) {
void Dimension::removeLight(glm::ivec3 pos) {
auto startChunk = getChunk(Space::Chunk::world::fromBlock(pos));
glm::ivec3 val = startChunk->getBlockLight(Space::Block::index(pos));
glm::ivec4 val = startChunk->getLight(Space::Block::index(pos));
startChunk->setBlockLight(Space::Block::index(pos), {});
lightRemoveQueue[0].emplace(Space::Block::index(pos), val.x, startChunk.get());
@ -122,17 +158,17 @@ void Dimension::removeLight(glm::ivec3 pos) {
std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateAddNodes() {
std::unordered_set<glm::ivec3, Vec::ivec3> chunksUpdated {};
for (unsigned int channel = 0; channel < 3; channel++) {
for (unsigned int channel = 0; channel < SUNLIGHT_CHANNEL; channel++) {
while (!lightAddQueue[channel].empty()) {
LightAddNode& node = lightAddQueue[channel].front();
unsigned char lightLevel = node.chunk->getBlockLight(node.index, channel);
unsigned char lightLevel = node.chunk->getLight(node.index, channel);
glm::ivec3 worldPos = node.chunk->pos * 16 + Space::Block::fromIndex(node.index);
const static std::array<glm::ivec3, 6> checks = { glm::ivec3 {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1} };
for (const auto& i : checks) {
glm::ivec3 check = worldPos + i;
if (!defs.blockFromId(getBlock(check)).solid && getBlockLight(check, node.chunk)[channel] + 2 <= lightLevel) {
if (defs.blockFromId(getBlock(check)).lightPropagates && getLight(check, node.chunk)[channel] + 2 <= lightLevel) {
BlockChunk* chunk;
if (containsWorldPos(node.chunk, check)) chunk = node.chunk;
else {
@ -141,7 +177,8 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateAddNodes() {
chunksUpdated.insert(chunk->pos);
chunk->dirty = true;
}
chunk->setBlockLight(Space::Block::index(check), channel, lightLevel - 1);
chunk->setLight(Space::Block::index(check), channel, lightLevel - 1);
lightAddQueue[channel].emplace(Space::Block::index(check), chunk);
}
}
@ -149,13 +186,41 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateAddNodes() {
}
}
while (!lightAddQueue[SUNLIGHT_CHANNEL].empty()) {
LightAddNode& node = lightAddQueue[SUNLIGHT_CHANNEL].front();
unsigned char lightLevel = node.chunk->getSunlight(node.index);
glm::ivec3 worldPos = node.chunk->pos * 16 + Space::Block::fromIndex(node.index);
const static std::array<glm::ivec3, 6> checks = { glm::ivec3 {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1} };
for (const auto& i : checks) {
glm::ivec3 check = worldPos + i;
if (defs.blockFromId(getBlock(check)).lightPropagates && getLight(check, node.chunk)[SUNLIGHT_CHANNEL] + 2 <= lightLevel) {
BlockChunk* chunk;
if (containsWorldPos(node.chunk, check)) chunk = node.chunk;
else {
chunk = getChunk(Space::Chunk::world::fromBlock(check)).get();
if (!chunk) continue;
chunksUpdated.insert(chunk->pos);
chunk->dirty = true;
}
int subtract = 1;
if (lightLevel == 15 && i == checks[2]) subtract = 0;
chunk->setLight(Space::Block::index(check), SUNLIGHT_CHANNEL, lightLevel - subtract);
lightAddQueue[SUNLIGHT_CHANNEL].emplace(Space::Block::index(check), chunk);
}
}
lightAddQueue[SUNLIGHT_CHANNEL].pop();
}
return chunksUpdated;
}
std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
std::unordered_set<glm::ivec3, Vec::ivec3> chunksUpdated {};
for (unsigned int channel = 0; channel < 3; channel++) {
for (unsigned int channel = 0; channel < SUNLIGHT_CHANNEL; channel++) {
while (!lightRemoveQueue[channel].empty()) {
LightRemoveNode& node = lightRemoveQueue[channel].front();
@ -164,7 +229,7 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
const static std::array<glm::ivec3, 6> checks = { glm::ivec3 {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1} };
for (const auto& i : checks) {
glm::ivec3 check = worldPos + i;
unsigned char checkLight = getBlockLight(check, node.chunk)[channel];
unsigned char checkLight = getLight(check, node.chunk)[channel];
if (checkLight != 0 && checkLight < node.value) {
BlockChunk* chunk;
@ -177,7 +242,7 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
}
auto blockLight = defs.blockFromId(chunk->getBlock(Space::Block::index(check))).lightSource[channel];
chunk->setBlockLight(Space::Block::index(check), channel, blockLight);
chunk->setLight(Space::Block::index(check), channel, blockLight);
if (blockLight) lightAddQueue[channel].emplace(Space::Block::index(check), chunk);
lightRemoveQueue[channel].emplace(Space::Block::index(check), checkLight, chunk);
}
@ -194,3 +259,8 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
propogateAddNodes();
return chunksUpdated;
}
void Dimension::propogateLight() {
propogateRemoveNodes();
propogateAddNodes();
}

View File

@ -29,6 +29,8 @@ public:
unsigned int getBlock(glm::ivec3 pos);
virtual bool setBlock(glm::ivec3 pos, unsigned int block);
void createSunlight(glm::ivec3 pos);
void propogateLight();
protected:
typedef std::unordered_map<glm::ivec3, std::shared_ptr<Region>, Vec::ivec3> block_region_map;
block_region_map regions;
@ -41,8 +43,8 @@ private:
inline std::shared_ptr<MapBlock> getOrCreateMapBlock(glm::ivec3 mapBlockPosition);
static inline bool containsWorldPos(BlockChunk* chunk, glm::ivec3 pos);
inline glm::ivec4 getLight(glm::ivec3 worldPos, BlockChunk* chunk = nullptr);
inline glm::ivec3 getBlockLight(glm::ivec3 worldPos, BlockChunk* chunk = nullptr);
inline void addLight(glm::ivec3 pos, glm::ivec3 light);
inline void removeLight(glm::ivec3 pos);
@ -59,6 +61,7 @@ private:
BlockChunk* chunk;
};
std::array<std::queue<LightAddNode>, 3> lightAddQueue;
std::array<std::queue<LightRemoveNode>, 3> lightRemoveQueue;
static constexpr unsigned char SUNLIGHT_CHANNEL = 3;
std::array<std::queue<LightAddNode>, 4> lightAddQueue;
std::array<std::queue<LightRemoveNode>, 4> lightRemoveQueue;
};

View File

@ -19,7 +19,7 @@ BlockChunk::BlockChunk(const std::vector<unsigned int>& blocks, const std::vecto
biomes(std::move(biomes)),
pos(pos),
generated(true) {
memset(light.data(), 0, sizeof(light));
memset(blocklight.data(), 0, sizeof(blocklight));
calcNonAirBlocks();
}
@ -61,11 +61,12 @@ Packet BlockChunk::serialize() {
s.append<std::string>(gzip::compress(temp.data(), temp.size()));
std::vector<unsigned char> lights {};
lights.resize(4096 * 3);
lights.resize(4096 * 4);
for (unsigned short i = 0; i < 4096; i++) {
lights[i * 3] = this->light[i].r;
lights[i * 3 + 1] = this->light[i].g;
lights[i * 3 + 2] = this->light[i].b;
lights[i * 4] = blocklight[i].r;
lights[i * 4 + 1] = blocklight[i].g;
lights[i * 4 + 2] = blocklight[i].b;
lights[i * 4 + 3] = getSunlight(i);
}
temp = Serializer().append(lights).data;
s.append<std::string>(gzip::compress(temp.data(), temp.size()));
@ -96,9 +97,10 @@ void BlockChunk::deserialize(PacketView& packet) {
auto lightsVec = Deserializer(gzip).read<std::vector<unsigned char>>();
for (unsigned int i = 0; i < 4096; i++) {
light[i].r = lightsVec[i * 3];
light[i].g = lightsVec[i * 3 + 1];
light[i].b = lightsVec[i * 3 + 2];
blocklight[i].r = lightsVec[i * 4];
blocklight[i].g = lightsVec[i * 4 + 1];
blocklight[i].b = lightsVec[i * 4 + 2];
setSunlight(i, lightsVec[i * 4 + 3]);
}
}

View File

@ -22,51 +22,62 @@ public:
void initializeEmpty();
bool setBlock(unsigned int ind, unsigned int blk);
inline bool setBlock(const glm::ivec3& pos, unsigned int blk);
// Blocks
inline unsigned int getBlock(unsigned int ind) const;
inline unsigned int getBlock(const glm::ivec3& pos) const;
inline bool setBiome(unsigned int ind, unsigned short bio);
inline bool setBiome(const glm::ivec3& pos, unsigned short bio);
bool setBlock(unsigned int ind, unsigned int blk);
inline bool setBlock(const glm::ivec3& pos, unsigned int blk);
const std::vector<unsigned int>& cGetBlocks() const;
// Biomes
inline unsigned short getBiome(unsigned int ind) const;
inline unsigned short getBiome(const glm::ivec3& pos) const;
inline void setSunlight(unsigned int ind, unsigned char val);
inline bool setBiome(unsigned int ind, unsigned short bio);
inline bool setBiome(const glm::ivec3& pos, unsigned short bio);
const std::vector<unsigned short>& cGetBiomes() const;
// Light
inline glm::ivec4 getLight(unsigned int ind);
inline unsigned char getLight(unsigned int ind, unsigned char channel);
inline void setLight(unsigned int ind, unsigned char channel, unsigned char light);
inline void setBlockLight(unsigned int ind, glm::ivec3 light);
inline void setBlockLight(unsigned int ind, unsigned char channel, unsigned char light);
inline unsigned char getSunlight(unsigned int ind);
inline glm::ivec3 getBlockLight(unsigned int ind);
inline unsigned char getBlockLight(unsigned int ind, unsigned char channel);
inline void setSunlight(unsigned int ind, unsigned char val);
inline glm::vec4 getLightVec(unsigned int ind);
const std::vector<unsigned int>& cGetBlocks() const;
const std::vector<unsigned short>& cGetBiomes() const;
// Serialization
Packet serialize();
void deserialize(PacketView& packet);
bool generated = false;
bool shouldHaveMesh = true;
bool dirty = true;
bool generated = false;
glm::ivec3 pos;
private:
struct light_bits {
struct blocklight_bits {
// 16 bits - 1 short
unsigned char r: 5;
unsigned char g: 5;
unsigned char b: 5, :1;
};
struct sunlight_bits {
// 8 bits for two values - 1 char
unsigned char a: 4;
unsigned char b: 4;
};
std::vector<unsigned int> blocks {};
std::vector<unsigned short> biomes {};
std::array <light_bits, 4096> light {};
std::array <blocklight_bits, 4096> blocklight {};
std::array <sunlight_bits, 2048> sunlight {};
bool empty = true;
bool partial = false;
@ -110,35 +121,36 @@ inline unsigned short BlockChunk::getBiome(const glm::ivec3& pos) const {
return getBiome(Space::Block::index(pos));
}
inline void BlockChunk::setSunlight(unsigned int ind, unsigned char val) {
inline glm::ivec4 BlockChunk::getLight(unsigned int ind) {
return { blocklight[ind].r, blocklight[ind].g, blocklight[ind].b, getSunlight(ind) };
}
inline unsigned char BlockChunk::getLight(unsigned int ind, unsigned char channel) {
return channel == 0 ? blocklight[ind].r :
channel == 1 ? blocklight[ind].g :
channel == 2 ? blocklight[ind].b :
getSunlight(ind);
}
inline void BlockChunk::setLight(unsigned int ind, unsigned char channel, unsigned char l) {
channel == 0 ? blocklight[ind].r = l:
channel == 1 ? blocklight[ind].g = l:
channel == 2 ? blocklight[ind].b = l:
(setSunlight(ind, l), 0);
}
inline void BlockChunk::setBlockLight(unsigned int ind, glm::ivec3 l) {
light[ind].r = l.x;
light[ind].g = l.y;
light[ind].b = l.z;
}
inline void BlockChunk::setBlockLight(unsigned int ind, unsigned char channel, unsigned char l){
channel == 0 ? light[ind].r = l:
channel == 1 ? light[ind].g = l:
light[ind].b = l;
blocklight[ind].r = l.x;
blocklight[ind].g = l.y;
blocklight[ind].b = l.z;
}
inline unsigned char BlockChunk::getSunlight(unsigned int ind) {
return 15;
if (ind % 2 == 0) return sunlight[ind / 2].a;
else return sunlight[ind / 2].b;
}
inline glm::ivec3 BlockChunk::getBlockLight(unsigned int ind) {
return { light[ind].r, light[ind].g, light[ind].b };
}
inline unsigned char BlockChunk::getBlockLight(unsigned int ind, unsigned char channel) {
return channel == 0 ? light[ind].r:
channel == 1 ? light[ind].g:
light[ind].b;
}
inline glm::vec4 BlockChunk::getLightVec(unsigned int ind) {
return glm::vec4 { light[ind].r, light[ind].g, light[ind].b, getSunlight(ind) };
inline void BlockChunk::setSunlight(unsigned int ind, unsigned char val) {
if (ind % 2 == 0) sunlight[ind / 2].a = val;
else sunlight[ind / 2].b = val;
}

View File

@ -7,6 +7,6 @@ zepha.register_block("zeus:default:dirt", {
shovel = 1,
pick = 2
},
light_source = { 0, 0, 31 },
light_source = { 0, 16, 31 },
yields = "zeus:default:dirt"
})

View File

@ -1,6 +1,7 @@
zepha.register_block("zeus:default:leaves", {
visible = true,
culls = false,
light_propagates = true,
name = "Leaves",
model = "base:leaf_like",
textures = {

View File

@ -1,127 +1,127 @@
local noise = {
heightmap = {
module = "add",
sources = {{
## Elevation
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.002,
octaves = 8
},
scale = 250,
bias = -32
}, {
## Features
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.2,
octaves = 3,
},
scale = 6,
bias = 6
}}
}
}
local woo = "zeus:default:wood"
local lea = "zeus:default:leaves"
local inv = "invalid"
local trunk_layer_0 = {
{ inv, inv, inv, inv, inv },
{ inv, woo, woo, woo, inv },
{ inv, woo, woo, woo, inv },
{ inv, woo, woo, woo, inv },
{ inv, inv, inv, inv, inv }
}
local trunk_layer_1 = {
{ inv, inv, inv, inv, inv },
{ inv, inv, woo, inv, inv },
{ inv, woo, woo, woo, inv },
{ inv, inv, woo, inv, inv },
{ inv, inv, inv, inv, inv }
}
local trunk_layer_2 = {
{ inv, inv, inv, inv, inv },
{ inv, inv, inv, inv, inv },
{ inv, inv, woo, inv, inv },
{ inv, inv, inv, inv, inv },
{ inv, inv, inv, inv, inv }
}
local leaf_layer_1 = {
{ inv, lea, lea, lea, inv },
{ lea, lea, lea, lea, lea },
{ lea, lea, woo, lea, lea },
{ lea, lea, lea, lea, lea },
{ inv, lea, lea, lea, inv }
}
local leaf_layer_2 = {
{ inv, inv, inv, inv, inv },
{ inv, lea, lea, lea, inv },
{ inv, lea, woo, lea, inv },
{ inv, lea, lea, lea, inv },
{ inv, inv, inv, inv, inv }
}
local leaf_layer_3 = {
{ inv, inv, inv, inv, inv },
{ inv, lea, lea, inv, inv },
{ inv, lea, lea, lea, inv },
{ inv, inv, lea, lea, inv },
{ inv, inv, inv, inv, inv }
}
local tree = zepha.create_structure({
origin = V(2, 2, 2),
schematic = {
trunk_layer_0,
trunk_layer_0,
trunk_layer_0,
trunk_layer_0,
trunk_layer_1,
trunk_layer_1,
trunk_layer_1,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
leaf_layer_2,
leaf_layer_1,
leaf_layer_1,
leaf_layer_1,
leaf_layer_1,
leaf_layer_2,
leaf_layer_3
}
})
zepha.register_biome("zeus:mapgen:forest", {
environment = {
temperature = 15/100,
humidity = 80/100,
roughness = 20/100,
},
blocks = {
top = "zeus:default:grass",
soil = "zeus:default:dirt",
rock = "zeus:default:stone"
},
biome_tint = "#7beb26",
noise = noise,
structures = {
tree
}
})
##local noise = {
## heightmap = {
## module = "add",
## sources = {{
## ## Elevation
## module = "scale_bias",
## source = {
## module = "perlin",
## frequency = 0.002,
## octaves = 8
## },
## scale = 250,
## bias = -32
## }, {
## ## Features
## module = "scale_bias",
## source = {
## module = "perlin",
## frequency = 0.2,
## octaves = 3,
## },
## scale = 6,
## bias = 6
## }}
## }
##}
##
##local woo = "zeus:default:wood"
##local lea = "zeus:default:leaves"
##local inv = "invalid"
##
##local trunk_layer_0 = {
## { inv, inv, inv, inv, inv },
## { inv, woo, woo, woo, inv },
## { inv, woo, woo, woo, inv },
## { inv, woo, woo, woo, inv },
## { inv, inv, inv, inv, inv }
##}
##
##local trunk_layer_1 = {
## { inv, inv, inv, inv, inv },
## { inv, inv, woo, inv, inv },
## { inv, woo, woo, woo, inv },
## { inv, inv, woo, inv, inv },
## { inv, inv, inv, inv, inv }
##}
##
##local trunk_layer_2 = {
## { inv, inv, inv, inv, inv },
## { inv, inv, inv, inv, inv },
## { inv, inv, woo, inv, inv },
## { inv, inv, inv, inv, inv },
## { inv, inv, inv, inv, inv }
##}
##
##local leaf_layer_1 = {
## { inv, lea, lea, lea, inv },
## { lea, lea, lea, lea, lea },
## { lea, lea, woo, lea, lea },
## { lea, lea, lea, lea, lea },
## { inv, lea, lea, lea, inv }
##}
##
##local leaf_layer_2 = {
## { inv, inv, inv, inv, inv },
## { inv, lea, lea, lea, inv },
## { inv, lea, woo, lea, inv },
## { inv, lea, lea, lea, inv },
## { inv, inv, inv, inv, inv }
##}
##
##local leaf_layer_3 = {
## { inv, inv, inv, inv, inv },
## { inv, lea, lea, inv, inv },
## { inv, lea, lea, lea, inv },
## { inv, inv, lea, lea, inv },
## { inv, inv, inv, inv, inv }
##}
##
##local tree = zepha.create_structure({
## origin = V(2, 2, 2),
## schematic = {
## trunk_layer_0,
## trunk_layer_0,
## trunk_layer_0,
## trunk_layer_0,
## trunk_layer_1,
## trunk_layer_1,
## trunk_layer_1,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## trunk_layer_2,
## leaf_layer_2,
## leaf_layer_1,
## leaf_layer_1,
## leaf_layer_1,
## leaf_layer_1,
## leaf_layer_2,
## leaf_layer_3
## }
##})
##
##zepha.register_biome("zeus:mapgen:forest", {
## environment = {
## temperature = 15/100,
## humidity = 80/100,
## roughness = 20/100,
## },
## blocks = {
## top = "zeus:default:grass",
## soil = "zeus:default:dirt",
## rock = "zeus:default:stone"
## },
## biome_tint = "#7beb26",
## noise = noise,
## structures = {
## tree
## }
##})

View File

@ -19,7 +19,7 @@ local noise = {
frequency = 0.2,
octaves = 3,
},
scale = 6,
scale = 12,
bias = 6
}}
}

View File

@ -1,44 +1,46 @@
local noise = {
heightmap = {
module = "add",
sources = {{
## Elevation
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.002,
octaves = 8
},
scale = 250,
bias = -32
}, {
## Features
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.2,
octaves = 3,
},
scale = 6,
bias = 6
}}
}
}
zepha.register_biome("zeus:mapgen:plains", {
environment = {
temperature = 15/100,
humidity = 60/100,
roughness = 20/100,
},
blocks = {
top = "zeus:default:grass",
soil = "zeus:default:dirt",
rock = "zeus:default:stone"
},
biome_tint = "#aaed45",
noise = noise,
structures = {
tree
}
})
##local noise = {
## heightmap = {
## module = "const",
## value = -12
#### module = "add",
#### sources = {{
#### ## Elevation
#### module = "scale_bias",
#### source = {
#### module = "perlin",
#### frequency = 0.002,
#### octaves = 8
#### },
#### scale = 250,
#### bias = -32
#### }, {
#### ## Features
#### module = "scale_bias",
#### source = {
#### module = "perlin",
#### frequency = 0.2,
#### octaves = 3,
#### },
#### scale = 6,
#### bias = 6
#### }}
## }
##}
##
##zepha.register_biome("zeus:mapgen:plains", {
## environment = {
## temperature = 15/100,
## humidity = 60/100,
## roughness = 20/100,
## },
## blocks = {
## top = "zeus:default:grass",
## soil = "zeus:default:dirt",
## rock = "zeus:default:stone"
## },
## biome_tint = "#aaed45",
## noise = noise,
## structures = {
## tree
## }
##})

View File

@ -10,41 +10,41 @@
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
TEST_CASE("BlockChunk", "[engine]") {
SECTION("Lighting") {
BlockChunk b;
b.setSunlight(1, 4);
b.setSunlight(2, 1);
b.setSunlight(3, 11);
b.setSunlight(4, 5);
b.setSunlight(100, 15);
b.setSunlight(3000, 0);
b.setBlocklight(1, 4);
b.setBlocklight(2, 1);
b.setBlocklight(3, 11);
b.setBlocklight(4, 5);
b.setBlocklight(100, 15);
b.setBlocklight(3000, 0);
SECTION("Sunlight") {
REQUIRE(b.getSunlight(1) == 4);
REQUIRE(b.getSunlight(2) == 1);
REQUIRE(b.getSunlight(3) == 11);
REQUIRE(b.getSunlight(4) == 5);
REQUIRE(b.getSunlight(100) == 15);
REQUIRE(b.getSunlight(3000) == 0);
}
SECTION("Blocklight") {
REQUIRE(b.getBlocklight(1) == 4);
REQUIRE(b.getBlocklight(2) == 1);
REQUIRE(b.getBlocklight(3) == 11);
REQUIRE(b.getBlocklight(4) == 5);
REQUIRE(b.getBlocklight(100) == 15);
REQUIRE(b.getBlocklight(3000) == 0);
}
}
// SECTION("Lighting") {
// BlockChunk b;
//
// b.setSunlight(1, 4);
// b.setSunlight(2, 1);
// b.setSunlight(3, 11);
// b.setSunlight(4, 5);
// b.setSunlight(100, 15);
// b.setSunlight(3000, 0);
//
// b.setBlocklight(1, 4);
// b.setBlocklight(2, 1);
// b.setBlocklight(3, 11);
// b.setBlocklight(4, 5);
// b.setBlocklight(100, 15);
// b.setBlocklight(3000, 0);
//
// SECTION("Sunlight") {
// REQUIRE(b.getSunlight(1) == 4);
// REQUIRE(b.getSunlight(2) == 1);
// REQUIRE(b.getSunlight(3) == 11);
// REQUIRE(b.getSunlight(4) == 5);
// REQUIRE(b.getSunlight(100) == 15);
// REQUIRE(b.getSunlight(3000) == 0);
// }
//
// SECTION("Blocklight") {
// REQUIRE(b.getBlocklight(1) == 4);
// REQUIRE(b.getBlocklight(2) == 1);
// REQUIRE(b.getBlocklight(3) == 11);
// REQUIRE(b.getBlocklight(4) == 5);
// REQUIRE(b.getBlocklight(100) == 15);
// REQUIRE(b.getBlocklight(3000) == 0);
// }
// }
SECTION("Blocks") {