From 0bfe2693083a15fdd77189a3c9ee5a8216681223 Mon Sep 17 00:00:00 2001 From: Nicole Collings <100Toby1@gmail.com> Date: Tue, 26 May 2020 17:16:56 -0700 Subject: [PATCH] Split lighting functionality and block functionality --- src/CMakeLists.txt | 8 +- src/world/Dimension.cpp | 218 +++++++++++++---------------------- src/world/Dimension.h | 60 ++++------ src/world/DimensionBase.cpp | 80 +++++++++++++ src/world/DimensionBase.h | 39 +++++++ src/world/LocalDimension.cpp | 23 ---- 6 files changed, 226 insertions(+), 202 deletions(-) create mode 100644 src/world/DimensionBase.cpp create mode 100644 src/world/DimensionBase.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1dee06d..2faa2532 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/src/world/Dimension.cpp b/src/world/Dimension.cpp index e1f5f1a4..ddb1b69d 100644 --- a/src/world/Dimension.cpp +++ b/src/world/Dimension.cpp @@ -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 Dimension::getRegion(glm::ivec3 regionPosition) { - if (!regions.count(regionPosition)) return nullptr; - return regions[regionPosition]; + auto edges = std::array { 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 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 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 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 Dimension::getOrCreateRegion(glm::ivec3 pos) { - if (regions[pos]) return regions[pos]; - regions[pos] = std::make_shared(pos); - return regions[pos]; -} - -std::shared_ptr 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(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 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 Dimension::propogateAddNodes() { std::unordered_set chunksUpdated {}; @@ -249,7 +171,25 @@ std::unordered_set 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; } diff --git a/src/world/Dimension.h b/src/world/Dimension.h index 8628e529..9dd71b93 100644 --- a/src/world/Dimension.h +++ b/src/world/Dimension.h @@ -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 -#include -#include #include +#include -#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 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 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 getChunk(glm::ivec3 chunkPosition); - virtual void setChunk(std::shared_ptr 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, Vec::ivec3> block_region_map; - block_region_map regions; - DefinitionAtlas& defs; - virtual std::unordered_set propogateAddNodes(); virtual std::unordered_set propogateRemoveNodes(); -private: - inline std::shared_ptr getOrCreateRegion(glm::ivec3 pos); - inline std::shared_ptr 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; diff --git a/src/world/DimensionBase.cpp b/src/world/DimensionBase.cpp new file mode 100644 index 00000000..fe329c53 --- /dev/null +++ b/src/world/DimensionBase.cpp @@ -0,0 +1,80 @@ +// +// Created by aurailus on 2019-11-20. +// + +#include "DimensionBase.h" + +DimensionBase::DimensionBase(DefinitionAtlas &defs) : defs(defs) {} + +std::shared_ptr 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 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 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 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 DimensionBase::getOrCreateRegion(glm::ivec3 pos) { + if (regions[pos]) return regions[pos]; + regions[pos] = std::make_shared(pos); + return regions[pos]; +} + +std::shared_ptr 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(mapBlockPosition)); + return (*region)[index]; +} \ No newline at end of file diff --git a/src/world/DimensionBase.h b/src/world/DimensionBase.h new file mode 100644 index 00000000..87c5f5bf --- /dev/null +++ b/src/world/DimensionBase.h @@ -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 + +#include "../util/Vec.h" +#include "region/Region.h" + +class DimensionBase { +public: + DimensionBase(DefinitionAtlas& defs); + + std::shared_ptr getRegion(glm::ivec3 regionPosition); + void removeRegion(glm::ivec3 pos); + + std::shared_ptr getMapBlock(glm::ivec3 mapBlockPosition); + virtual void removeMapBlock(glm::ivec3 pos); + + std::shared_ptr getChunk(glm::ivec3 chunkPosition); + virtual void setChunk(std::shared_ptr 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, Vec::ivec3> block_region_map; + block_region_map regions; + DefinitionAtlas& defs; +private: + + inline std::shared_ptr getOrCreateRegion(glm::ivec3 pos); + inline std::shared_ptr getOrCreateMapBlock(glm::ivec3 mapBlockPosition); +}; diff --git a/src/world/LocalDimension.cpp b/src/world/LocalDimension.cpp index 6d0302ee..9883b098 100644 --- a/src/world/LocalDimension.cpp +++ b/src/world/LocalDimension.cpp @@ -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() {