diff --git a/core/include/vf/LightEngine.h b/core/include/vf/LightEngine.h new file mode 100644 index 0000000..f328bd5 --- /dev/null +++ b/core/include/vf/LightEngine.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include "World.h" +#include "Block.h" + +namespace vf { + +constexpr int maxLightLevel = 15; + +// Incremental sky + block light over a fixed world box. Sky light is seeded from +// the open top of the box at full strength and travels straight down without loss +// while unobstructed, attenuating one level per step in every other direction. +// Block light radiates from emitters, attenuating one level per step. Editing a +// block re-lights only the affected neighbourhood (flood-fill add plus removal +// darkening) and yields the same field a full rebuild would produce. The box is +// chunk-agnostic: it indexes by world coordinate, so light crosses chunk borders +// seamlessly. +class LightEngine { +public: + LightEngine(World& world, const Vec3i& origin, const Vec3i& dims); + + void rebuild(); + + // Sets a world block and incrementally updates both light channels. + void setBlock(int gx, int gy, int gz, Block block); + + uint8_t sunAt(int gx, int gy, int gz) const; + uint8_t blockAt(int gx, int gy, int gz) const; + float combinedAt(int gx, int gy, int gz, float dayFactor = 1.0f) const; + + const Vec3i& regionOrigin() const { return origin; } + const Vec3i& regionDims() const { return dims; } + const std::vector& sunField() const { return sun; } + const std::vector& blockField() const { return block; } + void loadRaw(const std::vector& sunData, const std::vector& blockData) { + if (sunData.size() == sun.size()) sun = sunData; + if (blockData.size() == block.size()) block = blockData; + } + +private: + struct Node { + int lx, ly, lz; + uint8_t level; + }; + + int index(int lx, int ly, int lz) const { return (ly * dims.z + lz) * dims.x + lx; } + bool inBox(int lx, int ly, int lz) const; + bool occludesLocal(int lx, int ly, int lz) const; + + // isSun selects the straight-down full-strength rule for the channel. + void floodAdd(std::vector& field, std::vector& queue, bool isSun); + void floodRemove(std::vector& field, std::vector& removals, + std::vector& reAdd, bool isSun); + void updateSun(int lx, int ly, int lz); + void updateBlock(int lx, int ly, int lz, Block newBlock); + + World& world; + Vec3i origin; + Vec3i dims; + std::vector sun; + std::vector block; +}; + +} // namespace vf