Loaded 3DSOLID faces rendered on a ~0.06 m grid and crawled against their own
wireframe at UTM scale. Both ACIS tessellation paths cast each vertex f64→f32 in
the samplers, and only split the double-single low half (verts_low) when the
body carried a `transform` record. But many files — budweiser among them — store
solid geometry in absolute coordinates with no body transform, so verts_low
stayed empty: the GPU got position_low = 0 and reconstructed an already
f32-quantized absolute position, while the feature edges (split unconditionally
from f64) stayed exact.
Both paths now accumulate vertices in f64 and hand them to one finalize step:
finalize_mesh(name, verts_f64, normals, indices, color, xform)
the only place a solid vertex becomes f32 — it computes the world coordinate in
f64 (body placement applied, or identity), then splits into (verts, verts_low)
unconditionally, exactly as attach_feature_edges already does for the wireframe.
push_quad / tess_plane_face / tess_cone_face / tess_sphere_face /
tess_torus_face / tess_spline_face and the truck append_group / bespoke_face /
append_spline_faces all carry f64; apply_body_transform is gone.
Verified on budweiser (all solids, body_transform = NONE): verts_low is now
populated with the captured residual, max |low| ≈ 0.0624 m. The Model-tab
(truck_tess::polygon_to_result) and edge paths were already correct and are
unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Curved 3-D solid faces used fixed per-LOD grid counts, so a partial arc (a
sphere cap, a torus C-arc, a short cone wall) was sampled as finely as a whole
rim, and the density knobs were scattered across four files.
Every curved face (sphere / cone / torus / spline), feature edge and isoline
now derives its segment count from a chord-height tolerance plus the arc's own
radius and span — the same model the 2-D circle/arc/ellipse wires use
(tess_util::arc_segments): a big rim samples finer than a small one, a short arc
proportionally less than a full turn, and facet error is size-independent.
All tolerances live in one block at the top of solid3d_tess: LodConfig is now a
single chord fraction per LOD (arc_segs / circle_segs derive counts), plus
EDGE_CHORD_FRAC, TRUCK_CHORD_FRAC and BOUNDARY_CHORD_FRAC. FACETRES scales the
fraction. The cone height is a straight generator (no curvature) so it meshes in
one division. Removes the scattered constants (circ_segs/grid_u/grid_v,
CURVE_REL_TOL, BOUNDARY_SEGS, per-site M / EDGE_SEGS / ring_segs).
Adaptive counts come out lower, not higher: the € solid drops 3056→1780 tris, a
full sphere stays at 1024, five-solid total is 2989.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An ACIS spline-surface stored as `nurbs` (rational) carries a weight on each
control point, so the control net is a stream of x, y, z, w tuples. The
parser read plain x, y, z triples, shifting every control point by one float
and scrambling the whole net — the surface, and the solid it bounds,
rendered badly deformed. Non-rational `nubs` surfaces were unaffected, which
is why only some solids (rational lofts/sweeps with varying weights) broke.
Detect `nurbs` vs `nubs`; for rational surfaces read the fourth weight
component and build a truck `NurbsSurface` from homogeneous (xw, yw, zw, w)
control points so the weights actually shape the surface. Both the truck
B-rep path and the bespoke fallback share this parser.
Verified on VillaSavoye_MartinHedin2.dwg solid 0x24A1EE (handle 2400750): the
tessellated surface now stays within the solid's true extent instead of
flying outside it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the 30 flat modules under src/scene/ into six folders by role:
convert (entity→geometry), text (fonts/shaping), model (render models),
pick (hit-test/selection/grips/xclip), view (camera/transform/render),
cache (block + property caches). pipeline/ is unchanged.
Full path migration — all `scene::X` references become
`scene::<group>::X`, no compatibility re-exports. render.rs items that
were `pub(super)` (parent was scene) become `pub(in crate::scene)` to
keep their original scene-wide visibility.
No behavior change; build and tests are green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>