From 3072e2cd49ad1614100d1a1c73afe6a4888fb875 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Wed, 18 Mar 2015 00:22:16 +0100 Subject: [PATCH] begun block lighting implementation --- src/block.cpp | 2 + src/block.hpp | 22 ++++++ src/chunk.cpp | 167 ++++++++++++++++++++++++++++++++++++++++++++-- src/chunk.hpp | 30 ++++++--- src/generator.cpp | 4 +- src/interface.cpp | 16 ++++- src/interface.hpp | 1 + src/world.cpp | 24 +++++++ src/world.hpp | 1 + 9 files changed, 248 insertions(+), 19 deletions(-) diff --git a/src/block.cpp b/src/block.cpp index 50ade39..5fc3cc0 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -51,7 +51,9 @@ BlockType::BlockType(bool v, const glm::vec3 &col, const Shape *s) , color(col) , outline_color(-1, -1, -1) , id(0) +, luminosity(0) , visible(v) +, block_light(false) , fill({ false, false, false, false, false, false }) { } diff --git a/src/block.hpp b/src/block.hpp index 72954ce..0d7d75a 100644 --- a/src/block.hpp +++ b/src/block.hpp @@ -47,6 +47,25 @@ struct Block { Turn GetTurn() const { return Turn(orient % 4); } void SetTurn(Turn turn) { orient = GetFace() * TURN_COUNT + turn; } + static glm::tvec3 FaceNormal(Face face) { + switch (face) { + case FACE_UP: + return { 0, 1, 0 }; + case FACE_DOWN: + return { 0, -1, 0 }; + case FACE_RIGHT: + return { 1, 0, 0 }; + case FACE_LEFT: + return { -1, 0, 0 }; + case FACE_FRONT: + return { 0, 0, 1 }; + case FACE_BACK: + return { 0, 0, -1 }; + default: + return { 0, 0, 0 }; + } + } + }; @@ -59,7 +78,10 @@ struct BlockType { Block::Type id; + int luminosity; + bool visible; + bool block_light; struct Faces { bool face[Block::FACE_COUNT]; diff --git a/src/chunk.cpp b/src/chunk.cpp index ab89ef5..d298fb2 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -3,15 +3,10 @@ #include "generator.hpp" #include +#include #include -namespace { - -blank::Model::Buffer buf; - -} - namespace blank { Chunk::Chunk(const BlockTypeRegistry &types) @@ -117,8 +112,160 @@ void Chunk::Relink() { } +namespace { + +struct SetNode { + + Chunk *chunk; + Chunk::Pos pos; + + SetNode(Chunk *chunk, Chunk::Pos pos) + : chunk(chunk), pos(pos) { } + + int Get() const { return chunk->GetLight(pos); } + void Set(int level) { chunk->SetLight(pos, level); } + + bool HasNext(Block::Face face) { + const Block *next = chunk->FindNext(pos, face); + return next && !chunk->Type(*next).block_light; + } + SetNode GetNext(Block::Face face) { + Chunk::Pos next_pos(pos + Block::FaceNormal(face)); + if (Chunk::InBounds(next_pos)) { + return SetNode(chunk, next_pos); + } else { + return SetNode(&chunk->GetNeighbor(face), next_pos - (Block::FaceNormal(face) * Chunk::Extent())); + } + } + +}; + +struct UnsetNode +: public SetNode { + + int level; + + UnsetNode(Chunk *chunk, Chunk::Pos pos) + : SetNode(chunk, pos), level(Get()) { } + + UnsetNode(const SetNode &set) + : SetNode(set), level(Get()) { } + + UnsetNode GetNext(Block::Face face) { return UnsetNode(SetNode::GetNext(face)); } + +}; + +std::queue light_queue; +std::queue dark_queue; + +void work_light() { + while (!light_queue.empty()) { + SetNode node = light_queue.front(); + light_queue.pop(); + + int level = node.Get() - 1; + for (int face = 0; face < Block::FACE_COUNT; ++face) { + if (node.HasNext(Block::Face(face))) { + SetNode other = node.GetNext(Block::Face(face)); + if (other.Get() < level) { + other.Set(level); + light_queue.emplace(other); + } + } + } + } +} + +void work_dark() { + while (!dark_queue.empty()) { + UnsetNode node = dark_queue.front(); + dark_queue.pop(); + + for (int face = 0; face < Block::FACE_COUNT; ++face) { + if (node.HasNext(Block::Face(face))) { + UnsetNode other = node.GetNext(Block::Face(face)); + // TODO: if there a light source here with the same level this will err + if (other.Get() != 0 && other.Get() < node.level) { + other.Set(0); + dark_queue.emplace(other); + } else { + light_queue.emplace(other); + } + } + } + } + work_light(); +} + +} + +void Chunk::SetBlock(int index, const Block &block) { + const BlockType &old_type = Type(blocks[index]); + const BlockType &new_type = Type(block); + + blocks[index] = block; + + if (&old_type == &new_type) return; + + if (new_type.luminosity > 0) { + if (GetLight(index) < new_type.luminosity) { + SetLight(index, new_type.luminosity); + light_queue.emplace(this, ToPos(index)); + work_light(); + } + } else if (new_type.block_light && GetLight(index) != 0) { + SetLight(index, 0); + dark_queue.emplace(this, ToPos(index)); + work_dark(); + } else if (old_type.block_light && !new_type.block_light) { + int level = 0; + for (int face = 0; face < Block::FACE_COUNT; ++face) { + Pos next_pos(ToPos(index) + Block::FaceNormal(Block::Face(face))); + int next_level = 0; + if (InBounds(next_pos)) { + next_level = GetLight(next_pos); + } else { + if (HasNeighbor(Block::Face(face))) { + next_pos -= (Block::FaceNormal(Block::Face(face)) * Chunk::Extent()); + next_level = GetNeighbor(Block::Face(face)).GetLight(next_pos); + } + } + if (level < next_level) { + level = next_level; + } + } + if (level > 1) { + SetLight(index, level - 1); + light_queue.emplace(this, ToPos(index)); + work_light(); + } + } +} + +const Block *Chunk::FindNext(const Pos &pos, Block::Face face) const { + Pos next_pos(pos + Block::FaceNormal(face)); + if (InBounds(next_pos)) { + return &BlockAt(pos + Block::FaceNormal(face)); + } else if (HasNeighbor(face)) { + return &GetNeighbor(face).BlockAt(next_pos - (Block::FaceNormal(face) * Extent())); + } else { + return nullptr; + } +} + + +void Chunk::SetLight(int index, int level) { + light[index] = level; +} + +int Chunk::GetLight(int index) const { + return light[index]; +} + + void Chunk::Allocate() { - blocks.resize(Size()); + blocks.resize(Size(), Block(0)); + light.resize(Size(), 0); } @@ -177,6 +324,12 @@ glm::mat4 Chunk::Transform(const Pos &offset) const { } +namespace { + +Model::Buffer buf; + +} + void Chunk::CheckUpdate() { if (dirty) { Update(); diff --git a/src/chunk.hpp b/src/chunk.hpp index 6c9eeff..7e720bd 100644 --- a/src/chunk.hpp +++ b/src/chunk.hpp @@ -38,13 +38,13 @@ public: pos.y >= 0 && pos.y < Height() && pos.z >= 0 && pos.z < Depth(); } - static constexpr bool InBounds(const Chunk::Pos &pos) { + static constexpr bool InBounds(const Pos &pos) { return pos.x >= 0 && pos.x < Width() && pos.y >= 0 && pos.y < Height() && pos.z >= 0 && pos.z < Depth(); } - static constexpr int ToIndex(const Chunk::Pos &pos) { + static constexpr int ToIndex(const Pos &pos) { return pos.x + pos.y * Width() + pos.z * Width() * Height(); } static constexpr bool InBounds(int idx) { @@ -57,8 +57,8 @@ public: 0.5f + (idx / (Width() * Height())) ); } - static Chunk::Pos ToPos(int idx) { - return Chunk::Pos( + static Pos ToPos(int idx) { + return Pos( (idx % Width()), ((idx / Width()) % Height()), (idx / (Width() * Height())) @@ -90,15 +90,28 @@ public: void Allocate(); void Invalidate() { dirty = true; } - Block &BlockAt(int index) { return blocks[index]; } + void SetBlock(int index, const Block &); + void SetBlock(const Block::Pos &pos, const Block &block) { SetBlock(ToIndex(pos), block); } + void SetBlock(const Pos &pos, const Block &block) { SetBlock(ToIndex(pos), block); } + const Block &BlockAt(int index) const { return blocks[index]; } - Block &BlockAt(const Block::Pos &pos) { return BlockAt(ToIndex(pos)); } const Block &BlockAt(const Block::Pos &pos) const { return BlockAt(ToIndex(pos)); } - Block &BlockAt(const Chunk::Pos &pos) { return BlockAt(ToIndex(pos)); } - const Block &BlockAt(const Chunk::Pos &pos) const { return BlockAt(ToIndex(pos)); } + const Block &BlockAt(const Pos &pos) const { return BlockAt(ToIndex(pos)); } + + const Block *FindNext(const Pos &pos, Block::Face face) const; + const Block *FindNext(const Block::Pos &pos, Block::Face face) const { return FindNext(Pos(pos), face); } + const Block *FindNext(int index, Block::Face face) const { return FindNext(ToPos(index), face); } const BlockType &Type(const Block &b) const { return *types->Get(b.type); } + void SetLight(int index, int level); + void SetLight(const Pos &pos, int level) { SetLight(ToIndex(pos), level); } + void SetLight(const Block::Pos &pos, int level) { SetLight(ToIndex(pos), level); } + + int GetLight(int index) const; + int GetLight(const Pos &pos) const { return GetLight(ToIndex(pos)); } + int GetLight(const Block::Pos &pos) const { return GetLight(ToIndex(pos)); } + bool Intersection( const Ray &ray, const glm::mat4 &M, @@ -128,6 +141,7 @@ private: const BlockTypeRegistry *types; Chunk *neighbor[Block::FACE_COUNT]; std::vector blocks; + std::vector light; Model model; Pos position; bool dirty; diff --git a/src/generator.cpp b/src/generator.cpp index 15a15e0..f701230 100644 --- a/src/generator.cpp +++ b/src/generator.cpp @@ -28,9 +28,9 @@ void Generator::operator ()(Chunk &chunk) const { float val = solidNoise(gen_pos); if (val > solid_threshold) { int type_val = int((typeNoise(gen_pos) + 1.0f) * solids.size()) % solids.size(); - chunk.BlockAt(block_pos) = Block(solids[type_val]); + chunk.SetBlock(block_pos, Block(solids[type_val])); } else { - chunk.BlockAt(block_pos) = Block(space); + chunk.SetBlock(block_pos, Block(space)); } } } diff --git a/src/interface.cpp b/src/interface.cpp index 03c059f..f4b13b0 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -73,6 +73,11 @@ void Interface::Handle(const SDL_KeyboardEvent &event) { PrintBlockInfo(); } break; + case SDLK_l: + if (event.state == SDL_PRESSED) { + PrintLightInfo(); + } + break; case SDLK_p: if (event.state == SDL_PRESSED) { PrintSelectionInfo(); @@ -104,6 +109,13 @@ void Interface::PrintBlockInfo() { Print(aim_chunk->BlockAt(aim_block)); } +void Interface::PrintLightInfo() { + std::cout + << "light level " << world.PlayerChunk().GetLight(world.Player().Position()) + << " at position " << world.Player().Position() + << std::endl; +} + void Interface::PrintSelectionInfo() { std::cout << std::endl; Print(selection); @@ -148,13 +160,13 @@ void Interface::PlaceBlock() { mod_chunk = &world.Next(*aim_chunk, aim_normal); next_pos -= aim_normal * glm::vec3(Chunk::Extent()); } - mod_chunk->BlockAt(next_pos) = selection; + mod_chunk->SetBlock(next_pos, selection); mod_chunk->Invalidate(); } void Interface::RemoveBlock() { if (!aim_chunk) return; - aim_chunk->BlockAt(aim_block) = remove; + aim_chunk->SetBlock(aim_block, remove); aim_chunk->Invalidate(); } diff --git a/src/interface.hpp b/src/interface.hpp index 6817305..d3d58e8 100644 --- a/src/interface.hpp +++ b/src/interface.hpp @@ -34,6 +34,7 @@ public: void RemoveBlock(); void PrintBlockInfo(); + void PrintLightInfo(); void PrintSelectionInfo(); void Print(const Block &); diff --git a/src/world.cpp b/src/world.cpp index 27daecd..7ab7fe4 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -20,68 +20,88 @@ World::World() { // white block BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape); + type.block_light = true; type.fill = block_fill; blockType.Add(type); } { // white slab BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape); + type.block_light = true; type.fill = slab_fill; blockType.Add(type); } { // white stair BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape); + type.block_light = true; type.fill = stair_fill; blockType.Add(type); } { // red block BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape); + type.block_light = true; type.fill = block_fill; blockType.Add(type); } { // red slab BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape); + type.block_light = true; type.fill = slab_fill; blockType.Add(type); } { // red stair BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape); + type.block_light = true; type.fill = stair_fill; blockType.Add(type); } { // green block BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape); + type.block_light = true; type.fill = block_fill; blockType.Add(type); } { // green slab BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape); + type.block_light = true; type.fill = slab_fill; blockType.Add(type); } { // green stair BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape); + type.block_light = true; type.fill = stair_fill; blockType.Add(type); } { // blue block BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape); + type.block_light = true; type.fill = block_fill; blockType.Add(type); } { // blue slab BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape); + type.block_light = true; type.fill = slab_fill; blockType.Add(type); } { // blue stair BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape); + type.block_light = true; type.fill = stair_fill; blockType.Add(type); } + { // glowing yellow block + BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape); + type.luminosity = 10; + type.block_light = true; + type.fill = block_fill; + blockType.Add(type); + } + generate.Space(0); generate.Solids({ 1, 4, 7, 10 }); @@ -157,6 +177,10 @@ bool World::Intersection( } +Chunk &World::PlayerChunk() { + return chunks.ForceLoad(player->ChunkCoords()); +} + Chunk &World::Next(const Chunk &to, const glm::tvec3 &dir) { const Chunk::Pos tgt_pos = to.Position() + dir; return chunks.ForceLoad(tgt_pos); diff --git a/src/world.hpp b/src/world.hpp index 8783540..b5c4a29 100644 --- a/src/world.hpp +++ b/src/world.hpp @@ -32,6 +32,7 @@ public: Entity &Player() { return *player; } Entity &AddEntity() { entities.emplace_back(); return entities.back(); } + Chunk &PlayerChunk(); Chunk &Next(const Chunk &to, const glm::tvec3 &dir); void Update(int dt); -- 2.39.2