+void Chunk::SetNeighbor(Chunk &other) noexcept {
+ if (other.position == position + Pos(-1, 0, 0)) {
+ if (neighbor[Block::FACE_LEFT] != &other) {
+ neighbor[Block::FACE_LEFT] = &other;
+ other.neighbor[Block::FACE_RIGHT] = this;
+ for (int z = 0; z < Depth(); ++z) {
+ for (int y = 0; y < Height(); ++y) {
+ Pos my_pos(0, y, z);
+ Pos other_pos(Width() - 1, y, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(1, 0, 0)) {
+ if (neighbor[Block::FACE_RIGHT] != &other) {
+ neighbor[Block::FACE_RIGHT] = &other;
+ other.neighbor[Block::FACE_LEFT] = this;
+ for (int z = 0; z < Depth(); ++z) {
+ for (int y = 0; y < Height(); ++y) {
+ Pos my_pos(Width() - 1, y, z);
+ Pos other_pos(0, y, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, -1, 0)) {
+ if (neighbor[Block::FACE_DOWN] != &other) {
+ neighbor[Block::FACE_DOWN] = &other;
+ other.neighbor[Block::FACE_UP] = this;
+ for (int z = 0; z < Depth(); ++z) {
+ for (int x = 0; x < Width(); ++x) {
+ Pos my_pos(x, 0, z);
+ Pos other_pos(x, Height() - 1, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, 1, 0)) {
+ if (neighbor[Block::FACE_UP] != &other) {
+ neighbor[Block::FACE_UP] = &other;
+ other.neighbor[Block::FACE_DOWN] = this;
+ for (int z = 0; z < Depth(); ++z) {
+ for (int x = 0; x < Width(); ++x) {
+ Pos my_pos(x, Height() - 1, z);
+ Pos other_pos(x, 0, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, 0, -1)) {
+ if (neighbor[Block::FACE_BACK] != &other) {
+ neighbor[Block::FACE_BACK] = &other;
+ other.neighbor[Block::FACE_FRONT] = this;
+ for (int y = 0; y < Height(); ++y) {
+ for (int x = 0; x < Width(); ++x) {
+ Pos my_pos(x, y, 0);
+ Pos other_pos(x, y, Depth() - 1);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, 0, 1)) {
+ if (neighbor[Block::FACE_FRONT] != &other) {
+ neighbor[Block::FACE_FRONT] = &other;
+ other.neighbor[Block::FACE_BACK] = this;
+ for (int y = 0; y < Height(); ++y) {
+ for (int x = 0; x < Width(); ++x) {
+ Pos my_pos(x, y, Depth() - 1);
+ Pos other_pos(x, y, 0);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ }
+}
+
+void Chunk::ClearNeighbors() noexcept {
+ for (int i = 0; i < Block::FACE_COUNT; ++i) {
+ neighbor[i] = nullptr;
+ }
+}
+
+void Chunk::Unlink() noexcept {
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ if (neighbor[face]) {
+ neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = nullptr;
+ }
+ }
+}
+
+void Chunk::Relink() noexcept {
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ if (neighbor[face]) {
+ neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = this;
+ }
+ }
+}
+
+
+void Chunk::SetLight(int index, int level) noexcept {
+ if (light[index] != level) {
+ light[index] = level;
+ Invalidate();
+ }
+}
+
+int Chunk::GetLight(int index) const noexcept {
+ return light[index];
+}
+
+float Chunk::GetVertexLight(int index, const BlockModel::Position &vtx, const Model::Normal &norm) const noexcept {
+ float light = GetLight(index);
+ Chunk::Pos pos(ToPos(index));
+
+ Block::Face direct_face(Block::NormalFace(norm));
+ // tis okay
+ BlockLookup direct(const_cast<Chunk *>(this), pos, Block::NormalFace(norm));
+ if (direct) {
+ float direct_light = direct.GetLight();
+ if (direct_light > light) {
+ light = direct_light;
+ }
+ } else {
+ return light;
+ }
+
+ if (Type(BlockAt(index)).luminosity > 0 || direct.GetType().block_light) {
+ return light;
+ }
+
+ Block::Face edge[2];
+ switch (Block::Axis(direct_face)) {
+ case 0: // X
+ edge[0] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
+ edge[1] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
+ break;
+ case 1: // Y
+ edge[0] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
+ edge[1] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
+ break;
+ case 2: // Z
+ edge[0] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
+ edge[1] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
+ break;
+ }
+
+ int num = 1;
+ int occlusion = 0;
+
+ BlockLookup next[2] = {
+ direct.Next(edge[0]),
+ direct.Next(edge[1]),
+ };
+
+ if (next[0]) {
+ if (next[0].GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += next[0].GetLight();
+ ++num;
+ }
+ }
+ if (next[1]) {
+ if (next[1].GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += next[1].GetLight();
+ ++num;
+ }
+ }
+ if (occlusion < 2) {
+ if (next[0]) {
+ BlockLookup corner = next[0].Next(edge[1]);
+ if (corner) {
+ if (corner.GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += corner.GetLight();
+ ++num;
+ }
+ }
+ } else if (next[1]) {
+ BlockLookup corner = next[1].Next(edge[0]);
+ if (corner) {
+ if (corner.GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += corner.GetLight();
+ ++num;
+ }
+ }
+ }
+ } else {
+ ++occlusion;
+ }
+
+ return (light / num) - (occlusion * 0.8f);
+}
+
+
+bool Chunk::IsSurface(const Pos &pos) const noexcept {
+ const Block &block = BlockAt(pos);
+ if (!Type(block).visible) {
+ return false;
+ }
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ BlockLookup next = BlockLookup(const_cast<Chunk *>(this), pos, Block::Face(face));
+ if (!next || !next.GetType().visible) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Chunk::Draw() noexcept {