Skip to content

import(): weld exactly-equal vertices in every mesh format; X3D/VRML Switch, LOD and prototypes - #272

Merged
revarbat merged 2 commits into
mainfrom
fix/import-exact-weld
Oct 8, 2026
Merged

revarbat merged 2 commits into
mainfrom
fix/import-exact-weld

Conversation

@revarbat

@revarbat revarbat commented Oct 8, 2026

Copy link
Copy Markdown
Member

Follows #270 (AMF) to the other mesh importers, and fixes three X3D/VRML scene-structure problems found on the way.

Exact-position weld

Only STL and AMF merged vertices at exactly the same position, so a closed OFF, OBJ, 3MF, X3D or VRML file written as a triangle soup imported as an open surface that could not take part in CSG. Behaviour learned only by running OpenSCAD 2026.02.01:

Format OpenSCAD imports it? OpenSCAD welds Now
OFF yes whole file whole file
OBJ yes whole file, across o groups whole file
3MF yes per object per <mesh>
X3D, VRML no (our additions) — per Shape geometry, like AMF/3MF

appendWelded (mesh_import) reuses the existing weldVertices. weldVertices now rejects a face index past the vertex list (OBJ and 3MF never validated, so it used to reach Manifold as VertexOutOfBounds). Also fixed: the 3MF reader's <triangle search matched <triangles> and added a degenerate (0,0,0) triangle to every mesh, silently dropped by Manifold. scene_import.cpp's header no longer claims AMF's unit scales to mm.

X3D / VRML scene structure

  • Switch imports only its whichChoice child (nothing for -1, the default, or out of range); LOD only its first level. Both used to import every child.
  • Prototypes are still not expanded, but no longer fail silently: X3D stops importing a ProtoDeclare body as plain geometry (it ignored IS), and both formats warn: skipped what is not a mesh: 1 ProtoDeclare, 1 ProtoInstance / 2 PROTO instance.

Verification

  • 24 cases per format vs OpenSCAD (soup, duplicated list, genus-1 soup, far from origin up to 1e6, degenerate after weld, multi-object, sphere soup, open meshes, near-coincident jitter, closed controls): every welded case matches OpenSCAD's volume and vertex/face counts at the right granularity.
  • Regression: 375 files (30 seeded models exported in all 7 formats, split-component files, AMF cases), old vs new, re-exported to OFF and 3MF: all byte-identical, identical messages. No timing/memory change on a 125k-vertex sphere.
  • 8 new gtests in tests/test_import_export.cpp, all failing on the old code. Full suite: 1479 passed. test_python_bindings.py: 63 passed.
  • All corpus runs under a timeout + 1.5 GB RSS watchdog; no runaways, no OpenSCAD crashes.

Remaining (unchanged, documented in CLAUDE.md): open meshes in CSG are drawn with a warning where OpenSCAD drops them; near-coincident vertices are not merged (repair=true covers it); prototypes are skipped, not expanded.

🤖 Generated with Claude Code

revarbat and others added 2 commits October 8, 2026 10:07
A closed solid written as a triangle soup, or with its vertex list
duplicated, imported as an open surface ("N boundary edges, M unwelded
vertices") that could not take part in CSG. OpenSCAD 2026.02.01 imports
such an OFF, OBJ or 3MF as a solid; it cannot import X3D or VRML.

Granularity follows OpenSCAD, checked by running it: the whole file for
OBJ (across `o` groups) and OFF (one mesh), each object for 3MF (two open
halves in separate objects stay open there). X3D and VRML weld each
Shape's geometry, like AMF and 3MF. Exact positions only, as for STL and
AMF.

appendWelded() now carries the per-object weld for 3MF, AMF, X3D and
VRML. weldVertices() rejects a face index past the vertex list instead of
reading out of bounds (OBJ and 3MF never checked one). The 3MF reader no
longer reads `<triangles>` as a degenerate triangle.

Also fixes scene_import.cpp's header, which still said AMF's `unit`
scales to millimetres.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…totypes warn

Switch and LOD imported every child, so a file with alternatives drew
all of them on top of each other. Switch now imports only its
whichChoice child (nothing at the spec default -1 or out of range), and
LOD only its first, most detailed level -- VRML's `choice`/`level`
fields, X3D's child elements.

Prototypes are still not expanded, but no longer fail silently. X3D
stops walking ProtoDeclare/ExternProtoDeclare bodies, which were
imported once as plain geometry with their IS connections ignored, and
reports those and ProtoInstance through the skipped-node warning. VRML
remembers every PROTO/EXTERNPROTO name and reports instances of one as
"PROTO instance", where the import used to come out empty with no
explanation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@revarbat
revarbat merged commit 2bccbd6 into main Oct 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant