From 58d90e9cd7ac956038361ec8ba0dab1bee76a70c Mon Sep 17 00:00:00 2001 From: yggdrasil75 Date: Fri, 30 Jan 2026 19:41:55 -0500 Subject: [PATCH] fixes and lod --- tests/g3etest.cpp | 13 ++- util/grid/grid3eigen.hpp | 170 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 170 insertions(+), 13 deletions(-) diff --git a/tests/g3etest.cpp b/tests/g3etest.cpp index a659a5b..cbcf023 100644 --- a/tests/g3etest.cpp +++ b/tests/g3etest.cpp @@ -25,7 +25,7 @@ struct defaults { int outWidth = 512; int outHeight = 512; - int gridSizecube = 512; + int gridSizecube = 10000; PNoise2 noise = PNoise2(42); }; @@ -401,6 +401,7 @@ void livePreview(Octree& grid, defaults& config, const Camera& cam) { auto renderStart = std::chrono::high_resolution_clock::now(); frame currentPreviewFrame = grid.fastRenderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB); + //frame currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB, 3, 2, true); auto renderEnd = std::chrono::high_resolution_clock::now(); renderFrameTime = std::chrono::duration(renderEnd - renderStart).count(); @@ -699,7 +700,7 @@ int main() { statsNeedUpdate = true; resetView(cam, config.gridSizecube); - + grid.generateLODs(); livePreview(grid, config, cam); ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight)); @@ -710,7 +711,12 @@ int main() { { ImGui::Begin("Planet Preview"); - if (ImGui::Checkbox("update Preview", &worldPreview)) if (gridInitialized) livePreview(grid, config, cam); + ImGui::Checkbox("update Preview", &worldPreview); + if (worldPreview) { + if (gridInitialized) { + livePreview(grid, config, cam); + } + } if (gridInitialized && textureInitialized) { ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight)); @@ -743,7 +749,6 @@ int main() { } ImGui::Separator(); - ImGui::Text("Performance: %.1f FPS (%.1f ms)", renderFPS, avgRenderFrameTime * 1000.0); if (gridInitialized) { auto now = std::chrono::steady_clock::now(); diff --git a/util/grid/grid3eigen.hpp b/util/grid/grid3eigen.hpp index 52d5da8..172a399 100644 --- a/util/grid/grid3eigen.hpp +++ b/util/grid/grid3eigen.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef SSE #include @@ -75,8 +76,11 @@ public: std::array, 8> children; PointType center; bool isLeaf; + + mutable std::shared_ptr lodData; + mutable std::mutex lodMutex; - OctreeNode(const PointType& min, const PointType& max) : bounds(min,max), isLeaf(true) { + OctreeNode(const PointType& min, const PointType& max) : bounds(min,max), isLeaf(true), lodData(nullptr) { for (std::unique_ptr& child : children) { child = nullptr; } @@ -106,6 +110,9 @@ private: Eigen::Vector3f skylight_ = {0.1f, 0.1f, 0.1f}; Eigen::Vector3f backgroundColor_ = {0.53f, 0.81f, 0.92f}; + float lodFalloffRate_ = 0.1f; // Lower = better, higher = worse. 0-1 + float lodMinDistance_ = 100.0f; + uint8_t getOctant(const PointType& point, const PointType& center) const { int octant = 0; for (int i = 0; i < Dim; ++i) { @@ -148,6 +155,11 @@ private: } bool insertRecursive(OctreeNode* node, const std::shared_ptr& pointData, int depth) { + { + std::lock_guard lock(node->lodMutex); + node->lodData = nullptr; + } + if (!node->contains(pointData->position)) return false; if (node->isLeaf) { @@ -164,6 +176,74 @@ private: } return false; } + + void ensureLOD(const OctreeNode* node) const { + std::lock_guard lock(node->lodMutex); + if (node->lodData != nullptr) return; + + PointType avgPos = PointType::Zero(); + Eigen::Vector3f avgColor = Eigen::Vector3f::Zero(); + float avgEmittance = 0.0f; + float avgRefl = 0.0f; + float avgRefr = 0.0f; + bool anyLight = false; + int count = 0; + + if (node->isLeaf) { + if (node->points.size() == 1) { + node->lodData = node->points[0]; + return; + } else if (node->points.size() == 0) { + return; + } + } + + + auto accumulate = [&](const std::shared_ptr& item) { + if (!item || !item->active || !item->visible) return; + avgPos += item->position; + avgColor += item->color; + avgEmittance += item->emittance; + avgRefl += item->reflection; + avgRefr += item->refraction; + if (item->light) anyLight = true; + count++; + }; + + for (const auto& child : node->children) { + if (child) { + ensureLOD(child.get()); + if (child->lodData) { + accumulate(child->lodData); + } else if (child->isLeaf && !child->points.empty()) { + for(const auto& pt : child->points) accumulate(pt); + } + } + } + + if (count > 0) { + float invCount = 1.0f / count; + + auto lod = std::make_shared(); + + lod->position = avgPos * invCount; + lod->color = avgColor * invCount; + + PointType nodeDims = node->bounds.second - node->bounds.first; + lod->size = nodeDims.maxCoeff(); + + lod->emittance = avgEmittance * invCount; + lod->reflection = avgRefl * invCount; + lod->refraction = avgRefr * invCount; + lod->light = anyLight; + lod->active = true; + lod->visible = true; + lod->shape = Shape::CUBE; + lod->objectId = -1; + + node->lodData = lod; + } + } template void writeVal(std::ofstream& out, const V& val) const { @@ -232,6 +312,7 @@ private: bool isLeaf; readVal(in, isLeaf); node->isLeaf = isLeaf; + node->lodData = nullptr; if (isLeaf) { size_t pointCount; @@ -446,6 +527,14 @@ public: return backgroundColor_; } + void setLODFalloff(float rate) { lodFalloffRate_ = rate; } + void setLODMinDistance(float dist) { lodMinDistance_ = dist; } + + void generateLODs() { + if (!root_) return; + ensureLOD(root_.get()); + } + bool set(const T& data, const PointType& pos, bool visible, Eigen::Vector3f color, float size, bool active, int objectId = -1, bool light = false, float emittance = 0.0f, float refraction = 0.0f, float reflection = 0.0f, Shape shape = Shape::SPHERE) { @@ -543,6 +632,11 @@ public: bool remove(const PointType& pos, float tolerance = 0.0001f) { bool found = false; std::function removeNode = [&](OctreeNode* node) -> bool { + { + std::lock_guard lock(node->lodMutex); + node->lodData = nullptr; + } + if (!node->contains(pos)) return false; if (node->isLeaf) { @@ -584,9 +678,6 @@ public: std::function searchNode = [&](OctreeNode* node) { // Check if node's bounding box intersects with search sphere - PointType boxCenter = (node->bounds.first + node->bounds.second) * 0.5f; - PointType boxHalfSize = (node->bounds.second - node->bounds.first) * 0.5f; - PointType closestPoint; for (int i = 0; i < Dim; ++i) { closestPoint[i] = std::max(node->bounds.first[i], @@ -752,7 +843,7 @@ public: } std::vector> voxelTraverse(const PointType& origin, const PointType& direction, - float maxDist, bool stopAtFirstHit) const { + float maxDist, bool stopAtFirstHit, bool enableLOD = false) const { std::vector> hits; if (empty()) return hits; @@ -760,6 +851,34 @@ public: std::function traverseNode = [&](OctreeNode* node, const PointType& origin, const PointType& dir, float tMin, float tMax) { if (!node || tMin > tMax) return; + + if (enableLOD && !node->isLeaf) { + float dist = (node->center - origin).norm(); + float nodeSize = (node->bounds.second - node->bounds.first).norm(); + + if (dist > lodMinDistance_) { + float ratio = dist / (nodeSize + 0.0001f); + + if (ratio > (1.0f / lodFalloffRate_)) { + ensureLOD(node); + if (node->lodData) { + if (node->lodData->shape == Shape::SPHERE) { + float t; + if (raySphereIntersect(origin, dir, node->lodData->position, node->lodData->size, t)) { + if (t >= 0 && t <= maxDist) hits.emplace_back(node->lodData); + } + } else { + float t; PointType n, h; + if (rayCubeIntersect(origin, dir, node->lodData.get(), t, n, h)) { + if (t >= 0 && t <= maxDist) hits.emplace_back(node->lodData); + } + } + return; + } + } + } + } + if (node->isLeaf) { for (const auto& pointData : node->points) { if (!pointData->active) continue; @@ -856,7 +975,7 @@ public: if (bounces > maxBounces) return globalIllumination ? skylight_ : Eigen::Vector3f::Zero(); - auto hits = voxelTraverse(rayOrig, rayDir, std::numeric_limits::max(), true); + auto hits = voxelTraverse(rayOrig, rayDir, std::numeric_limits::max(), true, true); if (hits.empty()) { if (bounces == 0) { return backgroundColor_; @@ -1003,7 +1122,7 @@ public: } std::shared_ptr fastVoxelTraverse(const PointType& origin, const PointType& direction, - float maxDist) const { + float maxDist, bool enableLOD = false) const { std::shared_ptr hit; if (empty()) return hit; @@ -1011,6 +1130,32 @@ public: std::function traverseNode = [&](OctreeNode* node, const PointType& origin, const PointType& dir, float tMin, float tMax) { if (!node || tMin > tMax) return; + + // LOD Check for fast traverse + if (enableLOD && !node->isLeaf) { + float dist = (node->center - origin).norm(); + float nodeSize = (node->bounds.second - node->bounds.first).norm(); + if (dist > lodMinDistance_) { + float ratio = dist / (nodeSize + 0.0001f); + if (ratio > (1.0f / lodFalloffRate_)) { + ensureLOD(node); + if (node->lodData) { + // Project LOD + PointType toPoint = node->lodData->position - origin; + float projection = toPoint.dot(dir); + if (projection >= 0 && projection <= maxDist) { + PointType closestPoint = origin + dir * projection; + float distSq = (node->lodData->position - closestPoint).squaredNorm(); + if (distSq < node->lodData->size * node->lodData->size) { + hit = node->lodData; + return; + } + } + } + } + } + } + if (node->isLeaf) { for (const auto& pointData : node->points) { if (!pointData->active) continue; @@ -1103,7 +1248,7 @@ public: Eigen::Vector3f color = backgroundColor_; // Start with background color - auto hit = fastVoxelTraverse(origin, rayDir, std::numeric_limits::max()); + auto hit = fastVoxelTraverse(origin, rayDir, std::numeric_limits::max(), true); if (hit != nullptr) { auto obj = hit; @@ -1166,7 +1311,8 @@ public: size_t maxTreeDepth = 0; size_t maxPointsInLeaf = 0; size_t minPointsInLeaf = std::numeric_limits::max(); - + size_t lodGeneratedNodes = 0; + std::function traverse = [&](const OctreeNode* node, size_t depth) { if (!node) return; @@ -1174,6 +1320,8 @@ public: totalNodes++; maxTreeDepth = std::max(maxTreeDepth, depth); + if (node->lodData) lodGeneratedNodes++; + if (node->isLeaf) { leafNodes++; size_t pts = node->points.size(); @@ -1201,10 +1349,13 @@ public: os << "Config:\n"; os << " Max Depth Allowed : " << maxDepth << "\n"; os << " Max Pts Per Node : " << maxPointsPerNode << "\n"; + os << " LOD Falloff Rate : " << lodFalloffRate_ << "\n"; + os << " LOD Min Distance : " << lodMinDistance_ << "\n"; os << "Structure:\n"; os << " Total Nodes : " << totalNodes << "\n"; os << " Leaf Nodes : " << leafNodes << "\n"; os << " Non-Leaf Nodes : " << (totalNodes - leafNodes) << "\n"; + os << " LODs Generated : " << lodGeneratedNodes << "\n"; os << " Tree Height : " << maxTreeDepth << "\n"; os << "Data:\n"; os << " Total Points : " << size << " (Tracked) / " << actualPoints << " (Counted)\n"; @@ -1230,6 +1381,7 @@ public: node->points.clear(); node->points.shrink_to_fit(); + node->lodData = nullptr; for (int i = 0; i < 8; ++i) { if (node->children[i]) {