@@ -170,9 +170,9 @@ void MeshBuilder::buildOne(std::filesystem::path path, int gen) {
170170 csg::MeshCache cache;
171171 csg::MeshEvaluator meshEval (cache);
172172 meshEval.useManifoldSphere = m_useManifoldSphere.load ();
173- manifold::Manifold manifoldMesh ;
173+ std::vector< manifold::Manifold> rootMeshes ;
174174 try {
175- manifoldMesh = meshEval.evaluate (scene);
175+ rootMeshes = meshEval.evaluate (scene);
176176 } catch (const std::exception& e) {
177177 result->errorMsg = std::string (" Mesh error: " ) + e.what ();
178178 storeError (std::move (result));
@@ -184,18 +184,78 @@ void MeshBuilder::buildOne(std::filesystem::path path, int gen) {
184184 // ---- Phase: Converting to vertex buffers ----
185185 m_phase = BuildPhase::Converting;
186186
187- // Capture mesh properties before flat-shading (volume is exact, counts comparable)
188- {
189- result->volume = manifoldMesh.Volume ();
190- result->surfaceArea = manifoldMesh.SurfaceArea ();
187+ // Each root is converted independently and appended — this keeps objects
188+ // that are spatially inside other objects visible (no boolean union across roots).
189+ std::vector<uint32_t > rootVertStart; // per-root start index into result->verts
190+ rootVertStart.reserve (rootMeshes.size ());
191+
192+ for (const auto & m : rootMeshes) {
193+ rootVertStart.push_back (static_cast <uint32_t >(result->verts .size ()));
194+
195+ result->volume += m.Volume ();
196+ result->surfaceArea += m.SurfaceArea ();
191197
192- auto rawMesh = manifoldMesh .GetMeshGL ();
193- result->triCount = static_cast <uint32_t >(rawMesh.triVerts .size () / 3 );
194- result->vertCount = static_cast <uint32_t >(
198+ auto rawMesh = m .GetMeshGL ();
199+ result->triCount + = static_cast <uint32_t >(rawMesh.triVerts .size () / 3 );
200+ result->vertCount + = static_cast <uint32_t >(
195201 rawMesh.numProp > 0 ? rawMesh.vertProperties .size () / rawMesh.numProp : 0 );
202+
203+ std::vector<render::Vertex> verts;
204+ std::vector<uint32_t > indices;
205+ manifoldToMesh (m, verts, indices);
206+
207+ // Offset indices by the current vertex count before appending
208+ const auto base = static_cast <uint32_t >(result->verts .size ());
209+ for (auto & idx : indices) idx += base;
210+
211+ result->verts .insert (result->verts .end (), verts.begin (), verts.end ());
212+ result->indices .insert (result->indices .end (), indices.begin (), indices.end ());
196213 }
197214
198- manifoldToMesh (manifoldMesh, result->verts , result->indices );
215+ // ---- Optional: pairwise overlap detection ----
216+ if (m_warnOverlappingRoots.load () && rootMeshes.size () > 1 ) {
217+ // Compute per-root AABB from the already-converted vertex data
218+ const auto totalVerts = static_cast <uint32_t >(result->verts .size ());
219+ auto rootAABB = [&](std::size_t ri) -> std::pair<glm::vec3, glm::vec3> {
220+ uint32_t start = rootVertStart[ri];
221+ uint32_t end = (ri + 1 < rootVertStart.size ())
222+ ? rootVertStart[ri + 1 ] : totalVerts;
223+ glm::vec3 bmin{ 1e30f, 1e30f, 1e30f};
224+ glm::vec3 bmax{-1e30f, -1e30f, -1e30f};
225+ for (uint32_t vi = start; vi < end; ++vi) {
226+ bmin = glm::min (bmin, result->verts [vi].pos );
227+ bmax = glm::max (bmax, result->verts [vi].pos );
228+ }
229+ return {bmin, bmax};
230+ };
231+
232+ auto aabbOverlap = [](glm::vec3 mn1, glm::vec3 mx1,
233+ glm::vec3 mn2, glm::vec3 mx2) {
234+ return (mn1.x <= mx2.x && mx1.x >= mn2.x ) &&
235+ (mn1.y <= mx2.y && mx1.y >= mn2.y ) &&
236+ (mn1.z <= mx2.z && mx1.z >= mn2.z );
237+ };
238+
239+ for (std::size_t i = 0 ; i < rootMeshes.size (); ++i) {
240+ if (gen != m_currentGen.load ()) return ; // newer build queued — abort
241+ auto [mn1, mx1] = rootAABB (i);
242+ for (std::size_t j = i + 1 ; j < rootMeshes.size (); ++j) {
243+ auto [mn2, mx2] = rootAABB (j);
244+ if (!aabbOverlap (mn1, mx1, mn2, mx2)) continue ;
245+
246+ // AABBs overlap — run exact Manifold intersection
247+ manifold::Manifold sect = rootMeshes[i] ^ rootMeshes[j];
248+ if (std::abs (sect.Volume ()) > 1e-6 ) {
249+ lang::Diagnostic warn;
250+ warn.level = lang::DiagLevel::Warning;
251+ warn.message = " Objects " + std::to_string (i + 1 ) +
252+ " and " + std::to_string (j + 1 ) +
253+ " overlap — wrap in union() or difference() if intentional" ;
254+ result->diags .push_back (std::move (warn));
255+ }
256+ }
257+ }
258+ }
199259 result->elapsedMs = elapsedMs ();
200260
201261 m_elapsedMs = result->elapsedMs ;
0 commit comments