+ static Block::Pos ToCoords(const Pos &pos) noexcept {
+ return Block::Pos(pos) + 0.5f;
+ }
+ static Pos ToPos(int idx) noexcept {
+ return Pos(
+ (idx % width),
+ ((idx / width) % height),
+ (idx / (width * height))
+ );
+ }
+ glm::mat4 ToTransform(const Pos &pos, int idx) const noexcept;
+
+ static constexpr bool IsBorder(int idx) noexcept {
+ return
+ idx < width * height || // low Z plane
+ idx % width == 0 || // low X plane
+ (idx / (width * height)) == depth - 1 || // high Z plane
+ idx % width == width - 1 || // high X plane
+ (idx / width) % height == 0 || // low Y plane
+ (idx / width) % height == height - 1; // high Y plane
+ }
+
+ bool IsSurface(int index) const noexcept { return IsSurface(ToPos(index)); }
+ bool IsSurface(const Block::Pos &pos) const noexcept { return IsSurface(Pos(pos)); }
+ bool IsSurface(const Pos &pos) const noexcept;
+
+ void SetNeighbor(Chunk &) noexcept;
+ bool HasNeighbor(Block::Face f) const noexcept { return neighbor[f]; }
+ Chunk &GetNeighbor(Block::Face f) noexcept { return *neighbor[f]; }
+ const Chunk &GetNeighbor(Block::Face f) const noexcept { return *neighbor[f]; }
+ void ClearNeighbors() noexcept;
+ void Unlink() noexcept;
+ void Relink() noexcept;
+
+ // check which faces of a block at given index are obstructed (and therefore invisible)
+ Block::FaceSet Obstructed(const Pos &) const noexcept;
+
+ void Invalidate() noexcept { dirty = true; }
+
+ void SetBlock(int index, const Block &) noexcept;
+ void SetBlock(const Block::Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
+ void SetBlock(const Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
+
+ const Block &BlockAt(int index) const noexcept { return blocks[index]; }
+ const Block &BlockAt(const Block::Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }
+ const Block &BlockAt(const Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }