Split lighting functionality and block functionality

master
Nicole Collings 2020-05-26 17:16:56 -07:00
parent f8da40b229
commit 0bfe269308
6 changed files with 226 additions and 202 deletions

View File

@ -227,9 +227,9 @@ set(ZEPHA_SRC
def/gen/ServerBiomeAtlas.cpp
def/gen/ServerBiomeAtlas.h
def/gen/LocalBiomeAtlas.cpp
def/gen/LocalBiomeAtlas.h
world/Dimension.cpp
world/Dimension.h
def/gen/LocalBiomeAtlas.h
world/DimensionBase.cpp
world/DimensionBase.h
def/texture/RawTexData.h
game/hud/components/compound/GuiImageButton.cpp
game/hud/components/compound/GuiImageButton.h
@ -333,6 +333,6 @@ set(ZEPHA_SRC
game/scene/world/graph/FarMeshGenerator.h
game/scene/world/FarMapMeshDetails.h
lua/api/class/LuaGuiElement.cpp
lua/api/class/LuaGuiElement.h)
lua/api/class/LuaGuiElement.h world/Dimension.cpp world/Dimension.h)
add_library (Zepha_Core ${ZEPHA_SRC})

View File

@ -1,133 +1,28 @@
//
// Created by aurailus on 2019-11-20.
// Created by aurailus on 2020-05-26.
//
#include "Dimension.h"
Dimension::Dimension(DefinitionAtlas &defs) : defs(defs) {}
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;
std::shared_ptr<Region> Dimension::getRegion(glm::ivec3 regionPosition) {
if (!regions.count(regionPosition)) return nullptr;
return regions[regionPosition];
auto edges = std::array<bool, 6> { l.x == 0, l.y == 0, l.z == 0,
l.x == Space::MAPBLOCK_SIZE, l.y == Space::MAPBLOCK_SIZE, l.z == Space::MAPBLOCK_SIZE};
if (edges[1]) {
for (unsigned int j = 0; j < 64; j++) {
for (unsigned int k = 0; k < 64; k++) {
}
}
}
}
}
void Dimension::removeRegion(glm::ivec3 pos) {
regions.erase(pos);
}
std::shared_ptr<MapBlock> Dimension::getMapBlock(glm::ivec3 mapBlockPosition) {
auto region = getRegion(Space::Region::world::fromMapBlock(mapBlockPosition));
if (!region) return nullptr;
return (*region)[Space::MapBlock::index(mapBlockPosition)];
}
void Dimension::removeMapBlock(glm::ivec3 pos) {
auto region = getRegion(Space::Region::world::fromMapBlock(pos));
if (!region) return;
auto ind = Space::MapBlock::index(pos);
region->remove(ind);
if (region->count == 0) removeRegion(Space::Region::world::fromMapBlock(pos));
}
std::shared_ptr<BlockChunk> Dimension::getChunk(glm::ivec3 chunkPosition) {
auto mapBlock = getMapBlock(Space::MapBlock::world::fromChunk(chunkPosition));
if (!mapBlock) return nullptr;
return (*mapBlock)[Space::Chunk::index(chunkPosition)];
}
void Dimension::setChunk(std::shared_ptr<BlockChunk> chunk) {
auto mapBlock = getOrCreateMapBlock(Space::MapBlock::world::fromChunk(chunk->pos));
(*mapBlock).set(Space::Chunk::index(chunk->pos), chunk);
}
void Dimension::removeChunk(glm::ivec3 pos){
auto mapBlock = getMapBlock(Space::MapBlock::world::fromChunk(pos));
if (!mapBlock) return;
auto ind = Space::Chunk::index(pos);
mapBlock->remove(ind);
if (mapBlock->count == 0) removeMapBlock(Space::MapBlock::world::fromChunk(pos));
}
unsigned int Dimension::getBlock(glm::ivec3 pos) {
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
if (chunk) return chunk->getBlock(Space::Block::relative::toChunk(pos));
return 0;
}
bool Dimension::setBlock(glm::ivec3 pos, unsigned int block) {
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
if (!chunk) return false;
chunk->setBlock(Space::Block::relative::toChunk(pos), block);
// Remove light when placing solid blocks.
glm::ivec4 oldLight = chunk->getLight(Space::Block::index(pos));
if (!def.lightPropagates && oldLight.x + oldLight.y + oldLight.z != 0) removeLight(pos);
// Add light when placing light emitting blocks.
glm::ivec3 newLight = def.lightSource;
if (newLight.x > oldLight.x || newLight.y > oldLight.y || newLight.z > oldLight.z) addLight(pos, newLight);
// Reflow light when a transparent block is placed.
if (def.lightPropagates) reflowLight(pos);
// Block sunlight when a solid block is placed.
if (!def.lightPropagates && getLight(pos, chunk.get()).w != 0) blockSunlight(pos);
return true;
}
std::shared_ptr<Region> Dimension::getOrCreateRegion(glm::ivec3 pos) {
if (regions[pos]) return regions[pos];
regions[pos] = std::make_shared<Region>(pos);
return regions[pos];
}
std::shared_ptr<MapBlock> Dimension::getOrCreateMapBlock(glm::ivec3 mapBlockPosition) {
auto region = getOrCreateRegion(Space::Region::world::fromMapBlock(mapBlockPosition));
unsigned int index = Space::MapBlock::index(mapBlockPosition);
if ((*region)[index] != nullptr) return (*region)[index];
(*region).set(index, std::make_shared<MapBlock>(mapBlockPosition));
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());
// }
// }
// }
}
void Dimension::blockSunlight(glm::ivec3 pos) {
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);
@ -136,41 +31,68 @@ void Dimension::blockSunlight(glm::ivec3 pos) {
propogateRemoveNodes();
}
// 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;
}
// Get the BlockLight of a block. This function can be accelerated
// by providing a chunk that might contain the world position.
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->getLight(Space::Block::index(worldPos)) : glm::ivec4 {});
}
void Dimension::addLight(glm::ivec3 pos, glm::ivec3 light) {
void Dimension::addBlockLight(glm::ivec3 pos, glm::ivec3 light) {
auto startChunk = getChunk(Space::Chunk::world::fromBlock(pos));
auto index = Space::Block::index(pos);
auto ind = Space::Block::index(pos);
startChunk->setBlockLight(index, light);
lightAddQueue[0].emplace(index, startChunk.get());
lightAddQueue[1].emplace(index, startChunk.get());
lightAddQueue[2].emplace(index, startChunk.get());
startChunk->setBlockLight(ind, light);
lightAddQueue[0].emplace(ind, startChunk.get());
lightAddQueue[1].emplace(ind, startChunk.get());
lightAddQueue[2].emplace(ind, startChunk.get());
propogateAddNodes();
}
void Dimension::removeLight(glm::ivec3 pos) {
void Dimension::removeBlockLight(glm::ivec3 pos) {
auto startChunk = getChunk(Space::Chunk::world::fromBlock(pos));
glm::ivec4 val = startChunk->getLight(Space::Block::index(pos));
auto ind = Space::Block::index(pos);
glm::ivec4 val = startChunk->getLight(ind);
startChunk->setBlockLight(Space::Block::index(pos), {});
lightRemoveQueue[0].emplace(Space::Block::index(pos), val.x, startChunk.get());
lightRemoveQueue[1].emplace(Space::Block::index(pos), val.y, startChunk.get());
lightRemoveQueue[2].emplace(Space::Block::index(pos), val.z, startChunk.get());
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::reflowLightAroundTransparent(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} };
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
auto ind = Space::Block::index(pos);
if (!chunk) return;
for (const auto& i : checks) {
glm::ivec3 check = pos + i;
auto adjLight = getLight(check, chunk.get());
placeLight.x = fmax(placeLight.x, adjLight.x - 1);
placeLight.y = fmax(placeLight.y, adjLight.y - 1);
placeLight.z = fmax(placeLight.z, adjLight.z - 1);
placeLight.w = fmax(placeLight.w, adjLight.w - (i.y == 1 ? 0 : 1));
}
chunk->setBlockLight(ind, {placeLight.x, placeLight.y, placeLight.z});
chunk->setSunlight(ind, placeLight.w);
lightAddQueue[0].emplace(ind, chunk.get());
lightAddQueue[1].emplace(ind, chunk.get());
lightAddQueue[2].emplace(ind, chunk.get());
lightAddQueue[3].emplace(ind, chunk.get());
propogateAddNodes();
}
std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateAddNodes() {
std::unordered_set<glm::ivec3, Vec::ivec3> chunksUpdated {};
@ -249,7 +171,25 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
return chunksUpdated;
}
void Dimension::propogateLight() {
propogateRemoveNodes();
propogateAddNodes();
bool Dimension::setBlock(glm::ivec3 pos, unsigned int block) {
if (!DimensionBase::setBlock(pos, block)) return false;
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
auto &def = defs.blockFromId(block);
// Remove light when placing solid blocks.
glm::ivec4 oldLight = chunk->getLight(Space::Block::index(pos));
if (!def.lightPropagates && oldLight.x + oldLight.y + oldLight.z != 0) removeBlockLight(pos);
// Add light when placing light emitting blocks.
glm::ivec3 newLight = def.lightSource;
if (newLight.x > oldLight.x || newLight.y > oldLight.y || newLight.z > oldLight.z) addBlockLight(pos, newLight);
// Reflow light when a transparent block is placed.
if (def.lightPropagates) reflowLightAroundTransparent(pos);
// Block sunlight when a solid block is placed.
if (!def.lightPropagates && getLight(pos, chunk.get()).w != 0) reflowSunlightAroundSolid(pos);
return true;
}

View File

@ -1,64 +1,52 @@
//
// Created by aurailus on 2019-11-20.
// DimensionBase subclass that handles block light and sunlight.
// This class was split from DimensionBase to help differentiate
// lighting functions from basic world manipulation.
// Created by aurailus on 2020-05-26.
//
#pragma once
#include <queue>
#include <array>
#include <memory>
#include <unordered_set>
#include <queue>
#include "../util/Vec.h"
#include "region/Region.h"
#include "DimensionBase.h"
class Dimension {
class Dimension : public DimensionBase {
public:
Dimension(DefinitionAtlas& defs);
Dimension(DefinitionAtlas& defs) : DimensionBase(defs) {}
std::shared_ptr<Region> getRegion(glm::ivec3 regionPosition);
void removeRegion(glm::ivec3 pos);
// Override setBlock to update lighting.
bool setBlock(glm::ivec3 pos, unsigned int block) override;
std::shared_ptr<MapBlock> getMapBlock(glm::ivec3 mapBlockPosition);
virtual void removeMapBlock(glm::ivec3 pos);
// Calculate light propogation around MapBlock edges,
// Called after a new mapblock is inserted into the dimension.
void calculateEdgeLight(glm::ivec3 mbPos);
std::shared_ptr<BlockChunk> getChunk(glm::ivec3 chunkPosition);
virtual void setChunk(std::shared_ptr<BlockChunk> chunk);
virtual void removeChunk(glm::ivec3 pos);
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;
DefinitionAtlas& defs;
virtual std::unordered_set<glm::ivec3, Vec::ivec3> propogateAddNodes();
virtual std::unordered_set<glm::ivec3, Vec::ivec3> propogateRemoveNodes();
private:
inline std::shared_ptr<Region> getOrCreateRegion(glm::ivec3 pos);
inline std::shared_ptr<MapBlock> getOrCreateMapBlock(glm::ivec3 mapBlockPosition);
private:
// Helper methods to speed up light propagation.
static inline bool containsWorldPos(BlockChunk* chunk, glm::ivec3 pos);
inline glm::ivec4 getLight(glm::ivec3 worldPos, BlockChunk* chunk = nullptr);
inline void addLight(glm::ivec3 pos, glm::ivec3 light);
inline void removeLight(glm::ivec3 pos);
// Add and remove block light sources.
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 reflowLightAroundTransparent(glm::ivec3 pos);
inline void reflowSunlightAroundSolid(glm::ivec3 pos);
struct LightAddNode {
LightAddNode(unsigned short index, BlockChunk* chunk) : index(index), chunk(chunk) {};
unsigned short index;
BlockChunk* chunk;
unsigned short index; BlockChunk* chunk;
};
struct LightRemoveNode {
LightRemoveNode(unsigned short index, unsigned short value, BlockChunk* chunk) : index(index), value(value), chunk(chunk) {};
unsigned short index;
unsigned short value;
BlockChunk* chunk;
unsigned short index, value; BlockChunk* chunk;
};
static constexpr unsigned char SUNLIGHT_CHANNEL = 3;

View File

@ -0,0 +1,80 @@
//
// Created by aurailus on 2019-11-20.
//
#include "DimensionBase.h"
DimensionBase::DimensionBase(DefinitionAtlas &defs) : defs(defs) {}
std::shared_ptr<Region> DimensionBase::getRegion(glm::ivec3 regionPosition) {
if (!regions.count(regionPosition)) return nullptr;
return regions[regionPosition];
}
void DimensionBase::removeRegion(glm::ivec3 pos) {
regions.erase(pos);
}
std::shared_ptr<MapBlock> DimensionBase::getMapBlock(glm::ivec3 mapBlockPosition) {
auto region = getRegion(Space::Region::world::fromMapBlock(mapBlockPosition));
if (!region) return nullptr;
return (*region)[Space::MapBlock::index(mapBlockPosition)];
}
void DimensionBase::removeMapBlock(glm::ivec3 pos) {
auto region = getRegion(Space::Region::world::fromMapBlock(pos));
if (!region) return;
auto ind = Space::MapBlock::index(pos);
region->remove(ind);
if (region->count == 0) removeRegion(Space::Region::world::fromMapBlock(pos));
}
std::shared_ptr<BlockChunk> DimensionBase::getChunk(glm::ivec3 chunkPosition) {
auto mapBlock = getMapBlock(Space::MapBlock::world::fromChunk(chunkPosition));
if (!mapBlock) return nullptr;
return (*mapBlock)[Space::Chunk::index(chunkPosition)];
}
void DimensionBase::setChunk(std::shared_ptr<BlockChunk> chunk) {
auto mapBlock = getOrCreateMapBlock(Space::MapBlock::world::fromChunk(chunk->pos));
(*mapBlock).set(Space::Chunk::index(chunk->pos), chunk);
}
void DimensionBase::removeChunk(glm::ivec3 pos){
auto mapBlock = getMapBlock(Space::MapBlock::world::fromChunk(pos));
if (!mapBlock) return;
auto ind = Space::Chunk::index(pos);
mapBlock->remove(ind);
if (mapBlock->count == 0) removeMapBlock(Space::MapBlock::world::fromChunk(pos));
}
unsigned int DimensionBase::getBlock(glm::ivec3 pos) {
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
if (chunk) return chunk->getBlock(Space::Block::relative::toChunk(pos));
return 0;
}
bool DimensionBase::setBlock(glm::ivec3 pos, unsigned int block) {
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));
if (!chunk) return false;
auto &def = defs.blockFromId(block);
chunk->setBlock(Space::Block::relative::toChunk(pos), block);
return true;
}
std::shared_ptr<Region> DimensionBase::getOrCreateRegion(glm::ivec3 pos) {
if (regions[pos]) return regions[pos];
regions[pos] = std::make_shared<Region>(pos);
return regions[pos];
}
std::shared_ptr<MapBlock> DimensionBase::getOrCreateMapBlock(glm::ivec3 mapBlockPosition) {
auto region = getOrCreateRegion(Space::Region::world::fromMapBlock(mapBlockPosition));
unsigned int index = Space::MapBlock::index(mapBlockPosition);
if ((*region)[index] != nullptr) return (*region)[index];
(*region).set(index, std::make_shared<MapBlock>(mapBlockPosition));
return (*region)[index];
}

39
src/world/DimensionBase.h Normal file
View File

@ -0,0 +1,39 @@
//
// The base superclass for both dimensions. Handles chunk storage and manipulation.
// Does not include lighting mechanics, which are contained in subclass Dimension.
// Created by aurailus on 2019-11-20.
//
#pragma once
#include <memory>
#include "../util/Vec.h"
#include "region/Region.h"
class DimensionBase {
public:
DimensionBase(DefinitionAtlas& defs);
std::shared_ptr<Region> getRegion(glm::ivec3 regionPosition);
void removeRegion(glm::ivec3 pos);
std::shared_ptr<MapBlock> getMapBlock(glm::ivec3 mapBlockPosition);
virtual void removeMapBlock(glm::ivec3 pos);
std::shared_ptr<BlockChunk> getChunk(glm::ivec3 chunkPosition);
virtual void setChunk(std::shared_ptr<BlockChunk> chunk);
virtual void removeChunk(glm::ivec3 pos);
unsigned int getBlock(glm::ivec3 pos);
virtual bool setBlock(glm::ivec3 pos, unsigned int block);
protected:
typedef std::unordered_map<glm::ivec3, std::shared_ptr<Region>, Vec::ivec3> block_region_map;
block_region_map regions;
DefinitionAtlas& defs;
private:
inline std::shared_ptr<Region> getOrCreateRegion(glm::ivec3 pos);
inline std::shared_ptr<MapBlock> getOrCreateMapBlock(glm::ivec3 mapBlockPosition);
};

View File

@ -20,7 +20,6 @@ void LocalDimension::update(double delta, glm::vec3 playerPos) {
auto clientMapBlock = Space::MapBlock::world::fromBlock(playerPos);
for (auto it = regions.cbegin(); it != regions.cend();) {
bool remove = false;
for (unsigned short m = 0; m < 64; m++) {
@ -34,7 +33,6 @@ void LocalDimension::update(double delta, glm::vec3 playerPos) {
for (unsigned short c = 0; c < 64; c++) {
auto chunk = mapBlock->operator[](c);
if (!chunk) continue;
removeMeshChunk(chunk->pos);
}
@ -48,27 +46,6 @@ void LocalDimension::update(double delta, glm::vec3 playerPos) {
}
if (!remove) it++;
}
// for (const auto& region : regions) {
// for (unsigned short i = 0; i < 64; i++) {
// auto mapBlock = region.second->operator[](i);
// if (mapBlock == nullptr) continue;
//
// if (abs(clientMapBlock.x - mapBlock->pos.x) > LocalDimension::MB_STORE_H + 1
// || abs(clientMapBlock.y - mapBlock->pos.y) > LocalDimension::MB_STORE_V + 1
// || abs(clientMapBlock.z - mapBlock->pos.z) > LocalDimension::MB_STORE_H + 1) {
//
// for (unsigned short j = 0; j < 64; j++) {
// auto chunk = mapBlock->operator[](j);
// if (chunk == nullptr) continue;
// removeMeshChunk(chunk->pos);
// }
//
// removeMapBlock(mapBlock->pos);
// if (region.second->count == 0) break;
// }
// }
// }
}
void LocalDimension::finishMeshes() {