#pragma once #include "VISIRenderer.hpp" #include "zeus/CAABox.hpp" #include "xxhash/xxhash.h" #include "athena/MemoryWriter.hpp" #include #ifdef _WIN32 using ProcessType = HANDLE; #else using ProcessType = pid_t; #endif namespace std { template <> struct hash { size_t operator()(const zeus::CVector3f& val) const noexcept { zeus::simd_floats f(val.mSimd); return XXH64(&f[0], 12, 0); } }; } // namespace std struct VISIBuilder { struct Leaf { std::vector bits; void setBit(size_t bit) { size_t byte = bit / 8; if (byte >= bits.size()) bits.resize(byte + 1); bits[byte] |= 1 << (bit & 0x7); } void setLightEnum(size_t bit, EPVSVisSetState state) { size_t byte0 = bit / 8; size_t byte1 = (bit + 1) / 8; if (byte1 >= bits.size()) bits.resize(byte1 + 1); if (byte0 == byte1) { bits[byte0] |= int(state) << (bit & 0x7); } else { bits[byte0] |= (int(state) << 7) & 0x1; bits[byte1] |= (int(state) >> 1) & 0x1; } } bool operator==(const Leaf& other) const { if (bits.size() != other.bits.size()) return false; if (memcmp(bits.data(), other.bits.data(), bits.size())) return false; return true; } Leaf& operator|=(const Leaf& other) { if (bits.size() < other.bits.size()) bits.resize(other.bits.size()); for (size_t i = 0; i < other.bits.size(); ++i) bits[i] |= other.bits[i]; return *this; } operator bool() const { return bits.size() != 0; } void write(athena::io::MemoryWriter& w, size_t leafBytes) const { for (size_t i = 0; i < leafBytes; ++i) { if (i < bits.size()) w.writeUByte(bits[i]); else w.writeUByte(0); } } }; class PVSRenderCache { friend struct VISIBuilder; VISIRenderer& m_renderer; std::unordered_map> m_cache; size_t m_lightMetaBit; public: PVSRenderCache(VISIRenderer& renderer); const Leaf& GetLeaf(const zeus::CVector3f& vec); } renderCache; class Progress { float m_prog = 0.f; FPercent m_updatePercent; public: void report(int divisions); Progress(FPercent updatePercent) : m_updatePercent(updatePercent) {} }; struct Node { std::vector childNodes; size_t childRelOffs[8] = {}; Leaf leaf; uint8_t flags = 0; void buildChildren(int level, int divisions, const zeus::CAABox& curAabb, PVSRenderCache& rc, Progress& prog, const std::function& terminate); void calculateSizesAndOffs(size_t& cur, size_t leafSz); void writeNodes(athena::io::MemoryWriter& w, size_t leafBytes) const; bool operator==(const Node& other) const { if ((flags & 0x7) || (other.flags & 0x7)) return false; return leaf == other.leaf; } } rootNode; std::vector build(const zeus::CAABox& fullAabb, size_t modelCount, const std::vector& entities, const std::vector& lights, size_t layer2LightCount, FPercent updatePercent, ProcessType parentPid); VISIBuilder(VISIRenderer& renderer) : renderCache(renderer) {} };