Repository navigation
Export mesh layers when the field is empty (#662) - #686
Merged
Merged
Conversation
clay_document_mesh_combined meshed the field through clay_document_mesh and returned its refusal before looking at a mesh layer, so a document whose only visible geometry is mesh layers -- every SDF layer hidden, no SDF item at all, or every surface group hidden -- could not be exported. The visible mesh layers are now placed first. With none visible the call is still exactly clay_document_mesh, refusals included. With some, the field goes through mesh_tape in an EmptyField::ContributesNothing mode: an empty tape or a mesh the hidden-group drop leaves without triangles yields no field part instead of an error, while every other failure (malformed params, unbounded scene, over-fine voxel, mesher error) is still returned. The voxel-size pricing moves unchanged out of mesh_tape into price_mesh_voxel to keep it within the complexity target.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
clay_document_mesh_combinedmeshed the field first, throughclay_document_mesh, and returned that call's error before it considered any mesh layer.clay_document_meshrefuses an empty tape withCLAY_ERROR_INVALID_ARGUMENT("empty document") and a field that the hidden-group drop leaves with no triangles withCLAY_ERROR_BACKEND("meshing produced no triangles"). So a document whose only visible geometry is mesh layers could not be exported through the one call that exists to export mesh layers:ClaySpaceDesktop worked around it by transforming and concatenating each visible mesh layer itself (CyberdyneCorp/ClaySpaceDesktop#213).
Root cause and design
The combined call was implemented as "field, then layers", with the field's refusals treated as the call's. Those refusals are right for
clay_document_mesh, which was asked for a surface and has none to give, and wrong for the combined call when the mesh layers are the surface.place_visible_mesh_layers/place_mesh_layer, pulled out of the call unchanged), because whether there are any decides what an empty field means.clay_document_mesh, so the header's "returns exactly what clay_document_mesh would" stays literally true, refusals included. A document with nothing visible at all is stillCLAY_ERROR_INVALID_ARGUMENT.mesh_tapewith a newEmptyField::ContributesNothingmode. An empty tape, or a mesh the hidden-group drop leaves with no triangles, yields no field part rather than an error. The standalone calls (clay_document_mesh,clay_document_mesh_sdf_layer) keep the defaultRefuse.clay_document_meshand reading its error as "empty".CLAY_ERROR_INVALID_ARGUMENTalso means malformed params or an over-fine voxel size, andCLAY_ERROR_BACKENDalso means a mesher failure; swallowing either would turn a real error into a silent export of the mesh layers alone. Those are all still returned, andclay_mesh_paramsis validated whether or not the field is empty.mesh_tape's voxel-size pricing (caller's size or extent over resolution, the finite check, the grid-sample ceiling) moves verbatim intoprice_mesh_voxel. The two empty checks alone tookmesh_tapefrom cognitive complexity 15 to 19; it is now 14.clay_document_mesh_combinedgoes from 22 to 7.OpenSpec:
openspec/changes/export-mesh-layers-without-a-fieldmodifies the c-abi requirement "Exporting a document with its imported meshes is explicit", with two new scenarios (mesh-only document exports; nothing visible is still refused). The header comment aboveclay_document_mesh_combinedand thedocs/08-mesh-readback.mdtable row say the same.Tests
tests/unit/test_c_mesh_layer.cpp, four new cases. Each export case places a tetrahedron mesh layer at x = 5 and asserts the result is that tetrahedron alone (4 vertices, 12 indices, bounds entirely past x = 4), and assertsclay_document_meshalone still refuses:clay_mesh_params(struct_size = 0) is refused even with a mesh layer visible.Proof they fail on the unfixed code (the test file over
main'sclay_c.cpp):The fourth case passes either way by design: it guards that the refusals that should survive still do.
Verification
ctest --preset cpu-only: 10/10 passed (all four unit shards).clay_unit_tests '-sf=*test_c_mesh*': 86 cases, 247275 assertions, all pass.npx @fission-ai/openspec@1.12.0 validate --all --strict: 78 passed.-Wall -Wextra -Wpedantic -Wshadow -Werror -fsyntax-onlyonbindings/c/clay_c.cppand the test file: clean.tools/release_check.py --skip-slow: version, configure, build, tests (11/11), cpu parity (48 cases), layering, dialect, licenses, task-symbols, bindings (imported pyclay), kernels, c-abi, openspec all PASS. Thedeviceand fourhardware/*rows fail onmaintoo: they compare the tree against the last recorded device/hardware run, andmainhas moved since (include/clay/eval/bake_volume.hin A full brick build disagrees with its own raw field inside the band -- up to 0.263 on random documents, and a Move leaves those bricks stale #649, 48 engine files since the device gate). This change touches neither.ABI
ABI 0.124.0 -> 0.124.0 (unchanged). No signature, struct or entry point moves; a call that used to refuse now succeeds on documents with an empty field and a visible mesh layer. Hosts keeping a transform-and-concat fallback on refusal can drop it; keeping it is harmless.
Closes #662