From 6b9362219e1dbe1b2f2919fa363e71c221365ccd Mon Sep 17 00:00:00 2001 From: Yggdrasil75 Date: Fri, 13 Feb 2026 13:07:51 -0500 Subject: [PATCH] meshify is working decently --- tests/g3etest.cpp | 23 +++-- util/grid/grid3eigen.hpp | 215 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+), 8 deletions(-) diff --git a/tests/g3etest.cpp b/tests/g3etest.cpp index 9a240a2..c5dedb7 100644 --- a/tests/g3etest.cpp +++ b/tests/g3etest.cpp @@ -10,7 +10,6 @@ #include #include "../util/grid/grid3eigen.hpp" -#include "../util/grid/gridmesh.hpp" #include "../util/output/bmpwriter.hpp" #include "../util/output/frame.hpp" #include "../util/timing_decorator.cpp" @@ -38,7 +37,7 @@ struct defaults { float lodDropoff = 0.1; PNoise2 noise = PNoise2(42); - float meshResolution = 0.5f; + int meshResolution = 32; float meshIsoLevel = 0.4f; }; @@ -89,7 +88,7 @@ const std::chrono::seconds STATS_UPDATE_INTERVAL(60); std::string cachedStats; bool statsNeedUpdate = true; -CachedGridMesh planetMesh(1); +Scene scene; bool meshNeedsUpdate = false; void createSphere(const defaults& config, const spheredefaults& sconfig, Octree& grid) { @@ -200,11 +199,19 @@ void updateStatsCache(Octree& grid) { void livePreview(Octree& grid, defaults& config, const Camera& cam) { std::lock_guard lock(PreviewMutex); updatePreview = true; + if (meshNeedsUpdate) { - planetMesh.setResolution(config.meshResolution); - planetMesh.setIsoLevel(config.meshIsoLevel); - planetMesh.update(grid); + scene.clear(); + std::shared_ptr planetMesh = grid.generateMesh(1, config.meshIsoLevel, config.meshResolution); + std::shared_ptr starMesh = grid.generateMesh(2, config.meshIsoLevel, config.meshResolution); + + scene.addMesh(planetMesh); + scene.addMesh(starMesh); + + // planetMesh.setResolution(config.meshResolution); + // planetMesh.setIsoLevel(config.meshIsoLevel); + // planetMesh.update(grid); meshNeedsUpdate = false; statsNeedUpdate = true; } @@ -212,7 +219,7 @@ void livePreview(Octree& grid, defaults& config, const Camera& cam) { auto renderStart = std::chrono::high_resolution_clock::now(); frame currentPreviewFrame; - currentPreviewFrame = planetMesh.render(cam, config.outWidth, config.outHeight, 0.1f, 10000.0f, frame::colormap::RGB); + currentPreviewFrame = scene.render(cam, config.outWidth, config.outHeight, 0.1f, 10000.0f, frame::colormap::RGB); // grid.setLODMinDistance(config.lodDist); // grid.setLODFalloff(config.lodDropoff); @@ -520,7 +527,7 @@ int main() { ImGui::Separator(); ImGui::Text("Marching Cubes Config"); - if (ImGui::SliderFloat("Mesh Resolution", &config.meshResolution, 0.1f, 2.0f)) { + if (ImGui::SliderInt("Mesh Resolution", &config.meshResolution, 16, 128)) { meshNeedsUpdate = true; } if (ImGui::SliderFloat("Iso Level", &config.meshIsoLevel, 0.01f, 1.0f)) { diff --git a/util/grid/grid3eigen.hpp b/util/grid/grid3eigen.hpp index 6ac3866..676c6b7 100644 --- a/util/grid/grid3eigen.hpp +++ b/util/grid/grid3eigen.hpp @@ -5,6 +5,7 @@ #include "../timing_decorator.hpp" #include "../output/frame.hpp" #include "camera.hpp" +#include "mesh.hpp" #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #ifdef SSE #include @@ -497,6 +499,144 @@ private: } } + struct GridCell { + std::array p; + std::array val; + std::array color; + }; + + struct Vector3fCompare { + bool operator()(const Eigen::Vector3f& a, const Eigen::Vector3f& b) const { + return std::tie(a.x(), a.y(), a.z()) < std::tie(b.x(), b.y(), b.z()); + } + }; + + std::pair vertexInterpolate(float isolevel, const PointType& p1, const PointType& p2, float val1, float val2, const PointType& c1, const PointType& c2) { + if (std::abs(isolevel - val1) < 1e-5) return {p1, c1}; + if (std::abs(isolevel - val2) < 1e-5) return {p2, c2}; + if (std::abs(val1 - val2) < 1e-5) return {p1, c1}; + float mu = (isolevel - val1) / (val2 - val1); + PointType pos = p1 + mu * (p2 - p1); + PointType color = c1 + mu * (c2 - c1); + return {pos, color}; + } + + void polygonise(GridCell& grid, float isolevel, std::vector& vertices, std::vector& colors, std::vector& triangles) { + int cubeindex = 0; + if (grid.val[0] < isolevel) cubeindex |= 1; + if (grid.val[1] < isolevel) cubeindex |= 2; + if (grid.val[2] < isolevel) cubeindex |= 4; + if (grid.val[3] < isolevel) cubeindex |= 8; + if (grid.val[4] < isolevel) cubeindex |= 16; + if (grid.val[5] < isolevel) cubeindex |= 32; + if (grid.val[6] < isolevel) cubeindex |= 64; + if (grid.val[7] < isolevel) cubeindex |= 128; + + if (edgeTable[cubeindex] == 0) return; + + std::array vertlist_pos; + std::array vertlist_color; + + auto interpolate = [&](int edge_idx, int p_idx1, int p_idx2) { + auto [pos, col] = vertexInterpolate(isolevel, grid.p[p_idx1], grid.p[p_idx2], grid.val[p_idx1], grid.val[p_idx2], grid.color[p_idx1], grid.color[p_idx2]); + vertlist_pos[edge_idx] = pos; + vertlist_color[edge_idx] = col; + }; + + if (edgeTable[cubeindex] & 1) interpolate(0, 0, 1); + if (edgeTable[cubeindex] & 2) interpolate(1, 1, 2); + if (edgeTable[cubeindex] & 4) interpolate(2, 2, 3); + if (edgeTable[cubeindex] & 8) interpolate(3, 3, 0); + if (edgeTable[cubeindex] & 16) interpolate(4, 4, 5); + if (edgeTable[cubeindex] & 32) interpolate(5, 5, 6); + if (edgeTable[cubeindex] & 64) interpolate(6, 6, 7); + if (edgeTable[cubeindex] & 128) interpolate(7, 7, 4); + if (edgeTable[cubeindex] & 256) interpolate(8, 0, 4); + if (edgeTable[cubeindex] & 512) interpolate(9, 1, 5); + if (edgeTable[cubeindex] & 1024) interpolate(10, 2, 6); + if (edgeTable[cubeindex] & 2048) interpolate(11, 3, 7); + + int currentVertCount = vertices.size(); + + for (int i = 0; triTable[cubeindex][i] != -1; i += 3) { + Eigen::Vector3i tri; + for(int j=0; j<3; ++j) { + int table_idx = triTable[cubeindex][i+j]; + tri[j] = currentVertCount + j; + vertices.push_back(vertlist_pos[table_idx]); + colors.push_back(vertlist_color[table_idx]); + } + triangles.push_back(tri); + currentVertCount += 3; + } + } + + std::pair getScalarFieldValue(const PointType& pos, int objectId, float radius) { + auto neighbors = findInRadius(pos, radius, objectId); + float density = 0.0f; + PointType accumulatedColor = PointType::Zero(); + float totalWeight = 0.0f; + + if (neighbors.empty()) { + return {0.0f, {0.0f, 0.0f, 0.0f}}; + } + + for (auto& neighbor : neighbors) { + float distSq = (neighbor->position - pos).squaredNorm(); + float rSq = radius * radius; + if (distSq < rSq) { + float x = distSq / rSq; + float w = (1.0f - x) * (1.0f - x); + density += w; + accumulatedColor += neighbor->color * w; + totalWeight += w; + } + } + + if (totalWeight > 1e-6) { + return {density, accumulatedColor / totalWeight}; + } + + return {0.0f, {0.0f, 0.0f, 0.0f}}; + } + + std::unique_ptr mergeMeshes(std::vector& meshes, int objectId) { + if (meshes.empty()) return nullptr; + + std::vector mergedVertices; + std::vector> mergedPolys; + std::map vertexMap; + + for (auto& mesh_ptr : meshes) { + if (!mesh_ptr) continue; + Mesh& mesh = *mesh_ptr; + + auto mesh_verts = mesh.vertices(); + auto mesh_tris = mesh.polys(); + std::vector index_map(mesh_verts.size()); + + for (size_t i = 0; i < mesh_verts.size(); ++i) { + auto it = vertexMap.find(mesh_verts[i]); + if (it == vertexMap.end()) { + int new_idx = mergedVertices.size(); + vertexMap[mesh_verts[i]] = new_idx; + mergedVertices.push_back(mesh_verts[i]); + index_map[i] = new_idx; + } else { + index_map[i] = it->second; + } + } + + for (auto& poly : mesh_tris) { + mergedPolys.push_back({index_map[poly[0]], index_map[poly[1]], index_map[poly[2]]}); + } + } + + if (mergedVertices.empty()) return nullptr; + + return std::make_unique(objectId, mergedVertices, mergedPolys, std::vector{ {1.f, 1.f, 1.f} }); + } + public: Octree(const PointType& minBound, const PointType& maxBound, size_t maxPointsPerNode=16, size_t maxDepth = 16) : root_(std::make_unique(minBound, maxBound)), maxPointsPerNode(maxPointsPerNode), @@ -1194,6 +1334,81 @@ public: return surfaceNodes; } + std::shared_ptr generateMesh(int objectId, float isolevel = 0.5f, int resolution = 32) { + TIME_FUNCTION; + if (!root_) return nullptr; + + std::vector> nodes; + collectNodesByObjectId(root_.get(), objectId, 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.0f; + 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; + + 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 hasInside = false; + bool hasOutside = false; + 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) hasInside = true; + else hasOutside = true; + } + + if(hasInside && hasOutside) { + 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); + } + void printStats(std::ostream& os = std::cout) const { if (!root_) { os << "[Octree Stats] Tree is null/empty." << std::endl;