From 14b4d06495aeb206e448af1431795964c0f6f6e4 Mon Sep 17 00:00:00 2001 From: Yggdrasil75 Date: Mon, 16 Feb 2026 10:21:18 -0500 Subject: [PATCH] some crud and such --- util/grid/grid3eigen.hpp | 224 ++++++++++++++++++++++++++++++++++----- util/grid/mesh.hpp | 62 ++++++++--- 2 files changed, 247 insertions(+), 39 deletions(-) diff --git a/util/grid/grid3eigen.hpp b/util/grid/grid3eigen.hpp index 676c6b7..d114e2e 100644 --- a/util/grid/grid3eigen.hpp +++ b/util/grid/grid3eigen.hpp @@ -35,6 +35,7 @@ public: T data; PointType position; int objectId; + int subId; bool active; bool visible; float size; @@ -45,13 +46,13 @@ public: float reflection; NodeData(const T& data, const PointType& pos, bool visible, Eigen::Vector3f color, float size = 0.01f, - bool active = true, int objectId = -1, bool light = false, float emittance = 0.0f, + bool active = true, int objectId = -1, int subId = 0, bool light = false, float emittance = 0.0f, float refraction = 0.0f, float reflection = 0.0f) - : data(data), position(pos), objectId(objectId), active(active), visible(visible), + : data(data), position(pos), objectId(objectId), subId(subId), active(active), visible(visible), color(color), size(size), light(light), emittance(emittance), refraction(refraction), reflection(reflection) {} - NodeData() : objectId(-1), active(false), visible(false), size(0.0f), light(false), + NodeData() : objectId(-1), subId(0), active(false), visible(false), size(0.0f), light(false), emittance(0.0f), refraction(0.0f), reflection(0.0f) {} // Helper method to get half-size for cube @@ -104,6 +105,30 @@ private: Eigen::Vector3f skylight_ = {0.1f, 0.1f, 0.1f}; Eigen::Vector3f backgroundColor_ = {0.53f, 0.81f, 0.92f}; + + std::map, std::shared_ptr> meshCache_; + std::set> dirtyMeshes_; + int nextSubIdGenerator_ = 1; + + void invalidateMesh(int objectId, int subId) { + if (objectId < 0) return; + dirtyMeshes_.insert({objectId, subId}); + } + + void collectNodesBySubId(OctreeNode* node, int objId, int subId, std::vector>& results) const { + if (!node) return; + if (node->isLeaf) { + for (const auto& pt : node->points) { + if (pt->active && pt->objectId == objId && pt->subId == subId) { + results.push_back(pt); + } + } + } else { + for (const auto& child : node->children) { + if (child) collectNodesBySubId(child.get(), objId, subId, results); + } + } + } float lodFalloffRate_ = 0.1f; // Lower = better, higher = worse. 0-1 float lodMinDistance_ = 100.0f; @@ -855,34 +880,45 @@ public: auto pointData = find(oldPos, tolerance); if (!pointData) return false; + + int oldSubId = pointData->subId; + int targetObjId = (newObjectId != -2) ? newObjectId : pointData->objectId; - T dataCopy = pointData->data; - bool activeCopy = pointData->active; - bool visibleCopy = pointData->visible; - Eigen::Vector3f colorCopy = pointData->color; - float sizeCopy = pointData->size; - int objectIdCopy = pointData->objectId; - bool lightCopy = pointData->light; - float emittanceCopy = pointData->emittance; - float refractionCopy = pointData->refraction; - float reflectionCopy = pointData->reflection; + int finalSubId = oldSubId; - // Remove the old point - if (!remove(oldPos, tolerance)) { - return false; + if (oldSubId == 0 && targetObjId == pointData->objectId) { + finalSubId = nextSubIdGenerator_++; + + auto neighbors = findInRadius(oldPos, newSize * 3.0f, targetObjId); + for(auto& n : neighbors) { + if(n->subId == 0) { + n->subId = finalSubId; + invalidateMesh(targetObjId, 0); + } + } } - // Insert at new position with updated properties - return set(newData, newPos, - newVisible ? newVisible : visibleCopy, - newColor != Eigen::Vector3f(1.0f, 1.0f, 1.0f) ? newColor : colorCopy, - newSize > 0 ? newSize : sizeCopy, - newActive ? newActive : activeCopy, - newObjectId != -2 ? newObjectId : objectIdCopy, - newLight ? newLight : lightCopy, - newEmittance > 0 ? newEmittance : emittanceCopy, - newRefraction >= 0 ? newRefraction : refractionCopy, - newReflection >= 0 ? newReflection : reflectionCopy); + if (!remove(oldPos, tolerance)) return false; + + bool res = set(newData, newPos, + newVisible, + newColor != Eigen::Vector3f(1.0f, 1.0f, 1.0f) ? newColor : pointData->color, + newSize > 0 ? newSize : pointData->size, + newActive, + targetObjId, + finalSubId, + newLight, + newEmittance > 0 ? newEmittance : pointData->emittance, + newRefraction >= 0 ? newRefraction : pointData->refraction, + newReflection >= 0 ? newReflection : pointData->reflection); + + if(res) { + // Mark relevant meshes dirty + invalidateMesh(targetObjId, finalSubId); + if(finalSubId != oldSubId) invalidateMesh(targetObjId, oldSubId); + } + + return res; } bool move(const PointType& pos, const PointType& newPos) { @@ -1409,6 +1445,140 @@ public: return std::make_shared(objectId, vertices, polys, vertexColors); } + std::shared_ptr generateSubMesh(int objectId, int subId, float isolevel = 0.5f, int resolution = 32) { + if (subId == -999) return nullptr; + + std::vector> nodes; + collectNodesBySubId(root_.get(), objectId, subId, nodes); + if (nodes.empty()) return nullptr; + + PointType minB = nodes[0]->position; + PointType maxB = nodes[0]->position; + float maxRadius = 0.0f; + + for(auto& n : nodes) { + minB = minB.cwiseMin(n->position); + maxB = maxB.cwiseMax(n->position); + maxRadius = std::max(maxRadius, n->size); + } + + float padding = maxRadius * 2.5f; + minB -= PointType::Constant(padding); + maxB += PointType::Constant(padding); + + PointType size = maxB - minB; + PointType step = size / static_cast(resolution); + + std::vector vertices; + std::vector vertexColors; + std::vector triangles; + + // Marching Cubes Loop + for(int z = 0; z < resolution; ++z) { + for(int y = 0; y < resolution; ++y) { + for(int x = 0; x < resolution; ++x) { + PointType currPos(minB.x() + x * step.x(), minB.y() + y * step.y(), minB.z() + z * step.z()); + + GridCell cell; + PointType offsets[8] = {{0,0,0}, {step.x(),0,0}, {step.x(),step.y(),0}, {0,step.y(),0}, + {0,0,step.z()}, {step.x(),0,step.z()}, {step.x(),step.y(),step.z()}, {0,step.y(),step.z()}}; + + bool activeCell = false; + for(int i=0; i<8; ++i) { + cell.p[i] = currPos + offsets[i]; + auto [density, color] = getScalarFieldValue(cell.p[i], objectId, maxRadius * 2.0f); + cell.val[i] = density; + cell.color[i] = color; + if(cell.val[i] >= isolevel) activeCell = true; + } + + if(activeCell) { + auto owner = find(currPos + step*0.5f, maxRadius * 2.0f); + if (owner && owner->objectId == objectId && owner->subId == subId) { + polygonise(cell, isolevel, vertices, vertexColors, triangles); + } + } + } + } + } + + if(vertices.empty()) return nullptr; + + std::vector> polys; + for(auto& t : triangles) polys.push_back({t[0], t[1], t[2]}); + + return std::make_shared(objectId, vertices, polys, vertexColors, subId); + } + + std::vector> getMeshes(int objectId) { + std::vector> result; + + std::set relevantSubIds; + for(auto const& [key, val] : meshCache_) if(key.first == objectId) relevantSubIds.insert(key.second); + for(auto const& key : dirtyMeshes_) if(key.first == objectId) relevantSubIds.insert(key.second); + + for(int subId : relevantSubIds) { + if(dirtyMeshes_.count({objectId, subId})) { + auto newMesh = generateSubMesh(objectId, subId); + if(newMesh) meshCache_[{objectId, subId}] = newMesh; + else meshCache_.erase({objectId, subId}); + dirtyMeshes_.erase({objectId, subId}); + } + } + + for(auto const& [key, mesh] : meshCache_) { + if(key.first == objectId && mesh) result.push_back(mesh); + } + return result; + } + + void isolateInternalNodes(int objectId) { + std::vector> nodes; + collectNodesByObjectId(root_.get(), objectId, nodes); + + if(nodes.empty()) return; + float checkRad = nodes[0]->size * 1.5f; + + for(auto& node : nodes) { + int hiddenSides = 0; + PointType dirs[6] = {{1,0,0}, {-1,0,0}, {0,1,0}, {0,-1,0}, {0,0,1}, {0,0,-1}}; + for(int i=0; i<6; ++i) { + auto neighbor = find(node->position + dirs[i] * node->size, checkRad); + if(neighbor && neighbor->objectId == objectId && neighbor->active && neighbor->refraction < 0.01f) { + hiddenSides++; + } + } + + if(hiddenSides == 6) { + if(node->subId != -999) { + invalidateMesh(objectId, node->subId); + node->subId = -999; + } + } + } + } + + bool moveObject(int objectId, const PointType& offset) { + if (!root_) return false; + + std::vector> nodes; + collectNodesByObjectId(root_.get(), objectId, nodes); + if(nodes.empty()) return false; + + for(auto& n : nodes) remove(n->position); + for(auto& n : nodes) { + n->position += offset; + insertRecursive(root_.get(), n, 0); + } + + for (auto& [key, mesh] : meshCache_) { + if (key.first == objectId && mesh) { + mesh->translate(offset); + } + } + return true; + } + void printStats(std::ostream& os = std::cout) const { if (!root_) { os << "[Octree Stats] Tree is null/empty." << std::endl; diff --git a/util/grid/mesh.hpp b/util/grid/mesh.hpp index 43a9327..8f992a2 100644 --- a/util/grid/mesh.hpp +++ b/util/grid/mesh.hpp @@ -35,6 +35,7 @@ struct Triangle2D { class Mesh { private: int id; + int _subId; std::vector _vertices; std::vector> _polys; std::vector _colors; @@ -46,30 +47,61 @@ private: inline static float edgeFunction(const Vector2f& a, const Vector2f& b, const Vector2f& c) { return (c.x() - a.x()) * (b.y() - a.y()) - (c.y() - a.y()) * (b.x() - a.x()); } + public: - Mesh(int id, const std::vector& verts, const std::vector>& polys, const std::vector& colors) - : id(id), _vertices(verts), _polys(polys), _colors(colors) { + Mesh(int id, const std::vector& verts, const std::vector>& polys, const std::vector& colors, int subId = 0) + : id(id), _subId(subId), _vertices(verts), _polys(polys), _colors(colors) { _needs_triangulation = true; _needs_norm_calc = true; } + int getId() const { + return id; + } + + int getSubId() const { + return _subId; + } + + void setSubId(int s) { + _subId = s; + } + std::vector vertices() { return _vertices; } - bool vertices(std::vector verts) { - if (verts.size() != _colors.size()) { - if (_colors.size() == 1) { - _vertices = verts; - _needs_norm_calc = true; - return true; - } - return false; - } else { - _vertices = verts; + bool updateVertex(size_t index, const Vector3f& newPos) { + if (index < _vertices.size()) { + _vertices[index] = newPos; _needs_norm_calc = true; return true; } + return false; + } + + void translate(const Vector3f& offset) { + for(auto& v : _vertices) { + v += offset; + } + _needs_norm_calc = false; + } + + void replace(const std::vector& verts, const std::vector>& polys, const std::vector& colors) { + _vertices = verts; + _polys = polys; + _colors = colors; + _needs_triangulation = true; + _needs_norm_calc = true; + } + + bool vertices(std::vector verts) { + if (verts.size() != _colors.size() && _colors.size() != 1) { + return false; + } + _vertices = verts; + _needs_norm_calc = true; + return true; } std::vector> polys() { @@ -319,6 +351,11 @@ public: _meshes.push_back(mesh); updateStats(); } + + void addMeshes(const std::vector>& meshes) { + _meshes.insert(_meshes.end(), meshes.begin(), meshes.end()); + updateStats(); + } void clear() { _meshes.clear(); @@ -400,6 +437,7 @@ public: os << "========================================\n"; os << " Scene STATS \n"; os << "========================================\n"; + os << "Total Meshes: " << _meshes.size() << "\n"; for (auto m : _meshes) { m->printStats(os); }