Globally cascading sunlight x3

master
Nicole Collings 2020-06-16 22:36:12 -07:00
parent 9dfa361833
commit 41bb678eec
7 changed files with 126 additions and 101 deletions

View File

@ -11,7 +11,7 @@
#include "../util/net/PacketView.h"
Server::Server(unsigned short port, const std::string& subgame) :
seed(27),
seed(69),
port(port),
config(defs),
defs(subgame, seed),

View File

@ -59,32 +59,33 @@ void ServerWorld::update(double delta) {
auto finished = genStream->update();
generatedChunks = static_cast<int>(finished->size());
std::unordered_set<glm::ivec3, Vec::ivec3> changed {};
for (auto& mb : *finished) {
for (const auto& chunk : mb.chunks) dimension.setChunk(chunk);
dimension.calculateEdgeLight(mb.pos);
dimension.getMapBlock(mb.pos)->generated = true;
unsigned long long mapBlockIntegrity = dimension.getMapBlockIntegrity(mb.pos);
for (auto& client : clientList.clients) {
if (client->hasPlayer) {
auto playerMapBlock = Space::MapBlock::world::fromBlock(client->getPos());
std::pair<glm::ivec3, glm::ivec3> bounds = {
{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(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);
}
}
}
for (const auto& chunk : mb.chunks) {
changed.insert(chunk->pos);
dimension.setChunk(chunk);
}
auto resend = dimension.calculateEdgeLight(mb.pos);
changed.insert(resend.begin(), resend.end());
dimension.getMapBlock(mb.pos)->generated = true;
}
for (auto& chunk : changed) {
for (auto& client : clientList.clients) {
if (!client->hasPlayer) continue;
auto myChunk = Space::Chunk::world::fromBlock(client->getPos());
std::pair<glm::ivec3, glm::ivec3> bounds = {
{myChunk.x - CHUNK_SEND_H, myChunk.y - CHUNK_SEND_V, myChunk.z - CHUNK_SEND_H},
{myChunk.x + CHUNK_SEND_H, myChunk.y + CHUNK_SEND_V, myChunk.z + CHUNK_SEND_H}};
if (isInBounds(chunk, bounds)) sendChunk(dimension.getChunk(chunk), *client);
}
}
// Send the # of generated chunks to the client (debug),
// and trigger new chunks to be generated if a player has changed MapBlocks.
@ -135,7 +136,7 @@ void ServerWorld::changedChunks(ServerClient& client) {
unsigned int mapBlocksGenerating = 0;
for (const auto &c : generateOrder) {
glm::vec3 mapBlockPos = mapBlock + c;
glm::ivec3 mapBlockPos = mapBlock + c;
if (!isInBounds(mapBlockPos, oldBounds)) {
auto existing = dimension.getMapBlock(mapBlockPos);
if (existing != nullptr && existing->generated) {

View File

@ -13,8 +13,8 @@
class ServerWorld : public World {
public:
const static int MB_GEN_H = 2;
const static int MB_GEN_V = 2;
const static int MB_GEN_H = 2, MB_GEN_V = 2;
const static int CHUNK_SEND_H = 8, CHUNK_SEND_V = 8;
explicit ServerWorld(unsigned int seed, ServerGame& game, ClientList& clients);

View File

@ -16,64 +16,39 @@ bool Dimension::setBlock(glm::ivec3 pos, unsigned int block) {
if (oldLight.x + oldLight.y + oldLight.z != 0) removeBlockLight(pos);
if (newLight.x + newLight.y + newLight.z != 0) addBlockLight(pos, newLight);
if (def.lightPropagates) reflowLightThroughTransparent(pos);
if (!def.lightPropagates && getLight(pos, chunk.get()).w != 0) reflowSunlightAroundSolid(pos);
if (def.lightPropagates) reflowLight(pos);
if (!def.lightPropagates && getLight(pos, chunk.get()).w != 0) removeSunlight(pos);
propogateRemoveNodes();
return true;
}
void Dimension::calculateEdgeLight(glm::ivec3 mbPos) {
// for (auto i = 0; i < 64; i++) {
// glm::ivec3 l = Space::Chunk::fromIndex(i);
// glm::ivec3 chunkPos = mbPos * 4 + l;
//
// auto edges = std::array<bool, 6> { l.x == 0, l.y == 0, l.z == 0,
// l.x == Space::MAPBLOCK_SIZE - 1, l.y == Space::MAPBLOCK_SIZE - 1, l.z == Space::MAPBLOCK_SIZE - 1};
//
// // Pull light from above
// if (edges[4]) {
// if (!getChunk(chunkPos + glm::ivec3 {0, 1, 0})) continue;
// auto chunk = getChunk(chunkPos).get();
//
// for (unsigned int j = 0; j < 16; j++) {
// for (unsigned int k = 0; k < 16; k++) {
// glm::ivec3 belowPos = chunkPos * 16 + glm::ivec3 {j, 15, k};
// glm::ivec3 abovePos = belowPos + glm::ivec3 {0, 1, 0};
//
// unsigned int lightAbove = getLight(abovePos).w;
// unsigned int lightBelow = getLight(belowPos).w;
// if (lightBelow > lightAbove) {
// unsigned int ind = Space::Block::index(belowPos);
// lightRemoveQueue[SUNLIGHT_CHANNEL].emplace(ind, lightBelow, chunk);
// chunk->setSunlight(ind, 0);
// }
// }
// }
// }
//
// // Push light below
// if (edges[1]) {
// auto belowChunk = getChunk(chunkPos + glm::ivec3 {0, -1, 0}).get();
// if (!belowChunk) continue;
//
// for (unsigned int j = 0; j < 16; j++) {
// for (unsigned int k = 0; k < 16; k++) {
// glm::ivec3 abovePos = chunkPos * 16 + glm::ivec3 {j, 0, k};
// glm::ivec3 belowPos = abovePos + glm::ivec3 {0, -1, 0};
//
// unsigned int lightAbove = getLight(abovePos).w;
// unsigned int lightBelow = getLight(belowPos).w;
// if (lightBelow > lightAbove) {
// unsigned int ind = Space::Block::index(belowPos);
// lightRemoveQueue[SUNLIGHT_CHANNEL].emplace(ind, lightBelow, belowChunk);
// belowChunk->setSunlight(ind, 0);
// }
// }
// }
// }
// }
std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::calculateEdgeLight(glm::ivec3 mbPos) {
bool ypos = mapBlockGenerated(mbPos + glm::ivec3 {0, 1, 0});
bool yneg = mapBlockGenerated(mbPos + glm::ivec3 {0, -1, 0});
bool xpos = mapBlockGenerated(mbPos + glm::ivec3 {1, 0, 0});
bool xneg = mapBlockGenerated(mbPos + glm::ivec3 {-1, 0, 0});
bool zpos = mapBlockGenerated(mbPos + glm::ivec3 {0, 0, 1});
bool zneg = mapBlockGenerated(mbPos + glm::ivec3 {0, 0, -1});
propogateRemoveNodes();
for (unsigned int i = 0; i < 64; i++) {
glm::ivec3 l = Space::Chunk::fromIndex(i);
glm::ivec3 chunkPos = mbPos * 4 + l;
auto self = getChunk(chunkPos);
if (yneg && l.y == 0) calculateVerticalEdge(self, getChunk(chunkPos + glm::ivec3 {0, -1, 0}));
else if (ypos && l.y == 3) calculateVerticalEdge(getChunk(chunkPos + glm::ivec3 {0, 1, 0}), self);
if (xpos && l.x == 3) calculateHorizontalEdge(self, getChunk(chunkPos + glm::ivec3 {1, 0, 0}));
if (xneg && l.x == 0) calculateHorizontalEdge(self, getChunk(chunkPos + glm::ivec3 {-1, 0, 0}));
if (zpos && l.z == 3) calculateHorizontalEdge(self, getChunk(chunkPos + glm::ivec3 {0, 0, 1}));
if (zneg && l.z == 0) calculateHorizontalEdge(self, getChunk(chunkPos + glm::ivec3 {0, 0, -1}));
}
return propogateRemoveNodes();
}
std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateAddNodes() {
@ -152,7 +127,9 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
}
}
propogateAddNodes(); //TODO: Merge returned chunks with our list
auto otherChunksUpdated = propogateAddNodes();
chunksUpdated.insert(otherChunksUpdated.begin(), otherChunksUpdated.end());
return chunksUpdated;
}
@ -166,6 +143,37 @@ glm::ivec4 Dimension::getLight(glm::ivec3 worldPos, BlockChunk *chunk) {
return (oChunk ? oChunk->getLight(Space::Block::index(worldPos)) : glm::ivec4 {});
}
void Dimension::calculateHorizontalEdge(std::shared_ptr<BlockChunk> a, std::shared_ptr<BlockChunk> b) {
for (unsigned int j = 0; j < 256; j++) {
glm::ivec3 diff = a->pos - b->pos;
glm::ivec3 aPos = {
(diff.x == 0 ? j % 16 : diff.x == 1 ? 0 : 15), j / 16,
(diff.z == 0 ? j % 16 : diff.z == 1 ? 0 : 15) };
glm::ivec3 bPos = {
(diff.x == 0 ? j % 16 : diff.x == 1 ? 15 : 0), j / 16,
(diff.z == 0 ? j % 16 : diff.z == 1 ? 15 : 0) };
auto lightA = a->getSunlight(Space::Block::index(aPos));
auto lightB = b->getSunlight(Space::Block::index(bPos));
if (lightA > lightB + 1) setSunlight(b->pos * 16 + bPos, lightA - 1);
else if (lightB > lightA + 1) setSunlight(a->pos * 16 + aPos, lightB - 1);
}
}
void Dimension::calculateVerticalEdge(std::shared_ptr<BlockChunk> above, std::shared_ptr<BlockChunk> below) {
for (unsigned int j = 0; j < 256; j++) {
unsigned int xx = j / 16;
unsigned int zz = j % 16;
auto lightAbove = above->getSunlight(Space::Block::index({xx, 0, zz}));
auto lightBelow = below->getSunlight(Space::Block::index({xx, 15, zz}));
if (lightBelow > lightAbove) removeSunlight(below->pos * 16 + glm::ivec3{xx, 15, zz});
}
}
void Dimension::addBlockLight(glm::ivec3 pos, glm::ivec3 light) {
auto startChunk = getChunk(Space::Chunk::world::fromBlock(pos));
auto ind = Space::Block::index(pos);
@ -174,7 +182,6 @@ void Dimension::addBlockLight(glm::ivec3 pos, glm::ivec3 light) {
lightAddQueue[0].emplace(ind, startChunk.get());
lightAddQueue[1].emplace(ind, startChunk.get());
lightAddQueue[2].emplace(ind, startChunk.get());
propogateAddNodes();
}
void Dimension::removeBlockLight(glm::ivec3 pos) {
@ -187,10 +194,9 @@ void Dimension::removeBlockLight(glm::ivec3 pos) {
lightRemoveQueue[0].emplace(ind, val.x, startChunk.get());
lightRemoveQueue[1].emplace(ind, val.y, startChunk.get());
lightRemoveQueue[2].emplace(ind, val.z, startChunk.get());
propogateRemoveNodes();
}
void Dimension::reflowLightThroughTransparent(glm::ivec3 pos) {
void Dimension::reflowLight(glm::ivec3 pos) {
glm::ivec4 placeLight {};
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} };
@ -214,15 +220,21 @@ void Dimension::reflowLightThroughTransparent(glm::ivec3 pos) {
lightAddQueue[1].emplace(ind, chunk.get());
lightAddQueue[2].emplace(ind, chunk.get());
lightAddQueue[3].emplace(ind, chunk.get());
propogateAddNodes();
}
void Dimension::reflowSunlightAroundSolid(glm::ivec3 pos) {
auto startChunk = getChunk(Space::Chunk::world::fromBlock(pos));
auto ind = Space::Block::index(pos);
unsigned int light = startChunk->getSunlight(ind);
startChunk->setSunlight(ind, 0);
lightRemoveQueue[SUNLIGHT_CHANNEL].emplace(ind, light, startChunk.get());
propogateRemoveNodes();
void Dimension::removeSunlight(glm::ivec3 pos) {
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
unsigned int ind = Space::Block::index(pos);
unsigned int light = chunk->getSunlight(ind);
chunk->setSunlight(ind, 0);
lightRemoveQueue[SUNLIGHT_CHANNEL].emplace(ind, light, chunk.get());
}
void Dimension::setSunlight(glm::ivec3 pos, unsigned char level) {
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
unsigned int ind = Space::Block::index(pos);
chunk->setSunlight(ind, level);
lightAddQueue[SUNLIGHT_CHANNEL].emplace(ind, chunk.get());
}

View File

@ -14,6 +14,8 @@
class Dimension : public DimensionBase {
public:
typedef std::unordered_set<glm::ivec3, Vec::ivec3> relitChunks;
Dimension(DefinitionAtlas& defs) : DimensionBase(defs) {}
// Override setBlock to update lighting.
@ -21,24 +23,27 @@ public:
// Calculate light propogation around MapBlock edges,
// Called after a new mapblock is inserted into the dimension.
void calculateEdgeLight(glm::ivec3 mbPos);
relitChunks calculateEdgeLight(glm::ivec3 mbPos);
protected:
virtual std::unordered_set<glm::ivec3, Vec::ivec3> propogateAddNodes();
virtual std::unordered_set<glm::ivec3, Vec::ivec3> propogateRemoveNodes();
virtual relitChunks propogateAddNodes();
virtual relitChunks propogateRemoveNodes();
private:
// Helper methods to speed up light propagation.
// Lighting functions
static inline bool containsWorldPos(BlockChunk* chunk, glm::ivec3 pos);
inline glm::ivec4 getLight(glm::ivec3 worldPos, BlockChunk* chunk = nullptr);
// Add and remove block light sources.
void calculateHorizontalEdge(std::shared_ptr<BlockChunk> a, std::shared_ptr<BlockChunk> b);
void calculateVerticalEdge(std::shared_ptr<BlockChunk> above, std::shared_ptr<BlockChunk> below);
inline void addBlockLight(glm::ivec3 pos, glm::ivec3 light);
inline void removeBlockLight(glm::ivec3 pos);
// Special methods to recalculate lights after world manipulation.
inline void reflowLightThroughTransparent(glm::ivec3 pos);
inline void reflowSunlightAroundSolid(glm::ivec3 pos);
inline void reflowLight(glm::ivec3 pos);
inline void removeSunlight(glm::ivec3 pos);
inline void setSunlight(glm::ivec3 pos, unsigned char level);
struct LightAddNode {
LightAddNode(unsigned short index, BlockChunk* chunk) : index(index), chunk(chunk) {};

View File

@ -29,6 +29,11 @@ void DimensionBase::removeMapBlock(glm::ivec3 pos) {
if (region->count == 0) removeRegion(Space::Region::world::fromMapBlock(pos));
}
bool DimensionBase::mapBlockGenerated(glm::ivec3 mapBlockPosition) {
auto mb = getMapBlock(mapBlockPosition);
return mb && mb->generated;
}
std::shared_ptr<BlockChunk> DimensionBase::getChunk(glm::ivec3 chunkPosition) {
auto mapBlock = getMapBlock(Space::MapBlock::world::fromChunk(chunkPosition));
if (!mapBlock) return nullptr;
@ -77,4 +82,4 @@ std::shared_ptr<MapBlock> DimensionBase::getOrCreateMapBlock(glm::ivec3 mapBlock
if ((*region)[index] != nullptr) return (*region)[index];
(*region).set(index, std::make_shared<MapBlock>(mapBlockPosition));
return (*region)[index];
}
}

View File

@ -21,6 +21,8 @@ public:
std::shared_ptr<MapBlock> getMapBlock(glm::ivec3 mapBlockPosition);
virtual void removeMapBlock(glm::ivec3 pos);
bool mapBlockGenerated(glm::ivec3 mapBlockPosition);
std::shared_ptr<BlockChunk> getChunk(glm::ivec3 chunkPosition);
virtual void setChunk(std::shared_ptr<BlockChunk> chunk);
virtual void removeChunk(glm::ivec3 pos);