, color(col)
, outline_color(-1, -1, -1)
, id(0)
+, luminosity(0)
, visible(v)
+, block_light(false)
, fill({ false, false, false, false, false, false }) {
}
Turn GetTurn() const { return Turn(orient % 4); }
void SetTurn(Turn turn) { orient = GetFace() * TURN_COUNT + turn; }
+ static glm::tvec3<int> 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 };
+ }
+ }
+
};
Block::Type id;
+ int luminosity;
+
bool visible;
+ bool block_light;
struct Faces {
bool face[Block::FACE_COUNT];
#include "generator.hpp"
#include <limits>
+#include <queue>
#include <glm/gtx/transform.hpp>
-namespace {
-
-blank::Model::Buffer buf;
-
-}
-
namespace blank {
Chunk::Chunk(const BlockTypeRegistry &types)
}
+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<SetNode> light_queue;
+std::queue<UnsetNode> 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);
}
}
+namespace {
+
+Model::Buffer buf;
+
+}
+
void Chunk::CheckUpdate() {
if (dirty) {
Update();
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) {
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()))
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,
const BlockTypeRegistry *types;
Chunk *neighbor[Block::FACE_COUNT];
std::vector<Block> blocks;
+ std::vector<unsigned char> light;
Model model;
Pos position;
bool dirty;
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));
}
}
}
PrintBlockInfo();
}
break;
+ case SDLK_l:
+ if (event.state == SDL_PRESSED) {
+ PrintLightInfo();
+ }
+ break;
case SDLK_p:
if (event.state == SDL_PRESSED) {
PrintSelectionInfo();
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);
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();
}
void RemoveBlock();
void PrintBlockInfo();
+ void PrintLightInfo();
void PrintSelectionInfo();
void Print(const Block &);
{ // 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 });
}
+Chunk &World::PlayerChunk() {
+ return chunks.ForceLoad(player->ChunkCoords());
+}
+
Chunk &World::Next(const Chunk &to, const glm::tvec3<int> &dir) {
const Chunk::Pos tgt_pos = to.Position() + dir;
return chunks.ForceLoad(tgt_pos);
Entity &Player() { return *player; }
Entity &AddEntity() { entities.emplace_back(); return entities.back(); }
+ Chunk &PlayerChunk();
Chunk &Next(const Chunk &to, const glm::tvec3<int> &dir);
void Update(int dt);