cad-editor/docs/tessellation.md
Hakan Seven a994a148fe perf(render): batch solid meshes, fix per-pick freezes on heavy 3D models
Opening / navigating a heavy 3D DWG (e.g. a BIM model with ~10k ACIS solids)
was effectively frozen. Several independent costs, all addressed here:

- Face3D edge/fill buffers were re-uploaded on every camera move (keyed on
  camera_generation). They are world-space and selection-independent, so gate
  them on (geometry_epoch, fill_mode) instead — a pan no longer re-walks every
  wire.
- Solid meshes were drawn one buffer + one draw call per solid (~10k draws/
  frame), which strangled the GPU front end. Concatenate every solid's LOD0
  into a few large buffers (split only to stay under the 256 MB buffer cap) and
  draw the whole set in a handful of calls. Built once per geometry epoch, so
  selection / hover never re-pack it.
- Selection / hover highlight is now a small tinted overlay of just the picked
  solids, drawn last with an Always-depth pipeline so it shows on top even when
  the solid is occluded — instead of re-tinting and re-uploading all meshes.
- Hover / pick hit-testing scanned every triangle of every solid each move
  (seconds on a heavy model). Add a 3D-AABB screen-rect broad phase
  (MeshLodSet now carries z_aabb) and reuse the renderer's cached expanded mesh
  set, so picking is O(solids) cheap projections plus the few solids actually
  under the cursor.
- Block-internal hatch hit-testing exploded every INSERT on every hover.
  Cache the result per geometry epoch and skip blocks that contain no hatch.
- Curved-surface (cone/sphere/torus) tessellation tolerance is now radius-
  relative (CURVE_REL_TOL) so a cylinder's facet count is size-independent and
  matches the circle/arc wire tessellation, instead of exploding on large radii.

Also adds docs/tessellation.md mapping every EntityType to its tessellation
path (truck B-rep vs truck curve topology vs direct).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 22:46:29 +03:00

93 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Entity tessellation paths
How each acadrust `EntityType` becomes drawable geometry in OpenCADStudio, and
whether it passes through the [truck](https://github.com/ricosjp/truck) modeling
kernel.
There are **two distinct meanings of "through truck"** in the codebase, and they
must not be conflated:
1. **truck B-rep meshing** — the entity is rebuilt as a truck `Shell`/`Solid`
and triangulated by `MeshableShape::triangulation()`. This is the only path
that produces filled *mesh* triangles from a kernel.
2. **truck curve topology** — the entity is expressed as a truck `Edge`/`Wire`/
`Vertex` and sampled into line segments by truck's `ParameterDivision1D`. It
touches truck *topology* but never triangulates a surface; the output is a
polyline.
Everything else is **direct**: points/segments/triangles are emitted straight
from the entity's own data without any truck object.
Entry points: `src/scene/convert/tess.rs` (`tessellate_entity`, the per-entity
dispatcher) → `src/scene/convert/tessellate.rs` (`tessellate`, the `TruckObject`
router) and `src/scene/convert/truck_tess.rs` (curve sampling).
## Summary
| Path | Entities |
|---|---|
| **truck B-rep mesh** (Shell → `triangulation`, with a direct per-surface fallback) | `Solid3D`, `Region`, `Body`, `Surface` |
| **truck curve topology** (Edge/Wire → `ParameterDivision1D` sampling) | `Line`, `Arc`, `Circle`, `Ellipse`, `Spline`, `LwPolyline`, `Polyline`, `Polyline2D`, `Polyline3D`, `Point`*non-thick variants only* |
| **direct** (no truck — segments/triangles emitted directly) | everything else |
> The Model/Design-tab primitives (BOX, SPHERE, CYLINDER, CONE, WEDGE, TORUS,
> PYRAMID, extrude/revolve/loft/sweep) are also true truck B-rep meshes
> (`truck_tess::tessellate_solid`), but they are created as `Solid3D`
> placeholders carrying a truck B-rep, not as their own `EntityType`.
The only call site of `shell.triangulation()` for an imported entity is
`acis_to_truck::tessellate_sat_truck`. The four ACIS solids dispatch through
`solid3d_tess::tessellate_acis`, which tries that truck kernel first and falls
back to a bespoke per-surface LOD sampler (`tessellate_sat_lods`) when truck
cannot rebuild a face — so their path is **truck-with-direct-fallback**.
Curved-surface tessellation density is radius-relative (`CURVE_REL_TOL`, a
fraction of the surface radius) so a cylinder's facet count matches the
circle/arc wire tessellation instead of exploding on large radii.
## Full table
| Entity | Output | Path | Notes |
|---|---|---|---|
| Arc | wire | direct (truck curve topology) | non-thick: truck `Edge` (`builder::circle_arc`) sampled via `ParameterDivision1D`; thick: direct `Lines` |
| AttributeDefinition | wire | direct | routes through the Text/MText LFF glyph stroke pipeline |
| AttributeEntity | wire | direct | same as AttributeDefinition; values supplied per Insert |
| Block | none | n/a | block-definition sentinel; not tessellated (referenced via Insert) |
| BlockEnd | none | n/a | block-definition end marker; no output |
| Body | mesh | truck-with-direct-fallback | 3D ACIS body → truck `Shell``triangulation`; fallback per-surface sampler |
| Circle | wire | direct (truck curve topology) | non-thick: truck `Wire` of two half-circle edges; thick: direct `Lines` |
| Dimension | wire | direct | baked-block path recurses on `D###` sub-entities; synthesis path emits lines/arrows/LFF text |
| Ellipse | wire | direct (truck curve topology) | truck `Wire`/`Edge` over a `BSplineCurve`, `ParameterDivision1D` sampled |
| Face3D | both | direct | edge `Lines` + direct fan-triangulated `fill_tris`; no truck B-rep |
| Hatch | both | direct | boundary outline not emitted to the wire set (#131 OOM); fill rasterized on GPU |
| Insert | wire | direct | expands block children and tessellates each via its own path; XCLIP filter applied |
| Leader | wire | direct | leader path + arrowhead + landing, direct `Lines` |
| Line | wire | direct (truck curve topology) | non-thick: truck `Edge` (`builder::line`); thick: direct `Lines` |
| LwPolyline | wire | direct (truck curve topology) | `plinegen` true: truck `Contour` with bulge arcs; else direct `SegmentedLines` |
| Mesh | both | direct | SubD mesh: edge `Lines` + direct fan-triangulated `fill_tris` |
| MLine | wire | direct | spine + offset lines + caps, direct `Lines` |
| MText | wire | direct | wrap-aware multi-line LFF glyph layout; inline formatting codes |
| MultiLeader | wire | direct | leader + landing + LFF text + frame + fill |
| Ole2Frame | wire | direct | bounding rectangle + diagonal cross |
| Point | wire | direct (truck topology) | truck `Vertex` → cross marker sized by PDSIZE |
| PolyfaceMesh | both | direct | face list: closed-polyline edges + direct fan-triangulated `fill_tris` |
| PolygonMesh | both | direct | M×N grid wireframe + direct fan-triangulated `fill_tris` |
| Polyline | wire | direct (truck curve topology) | heavy 3D polyline; truck `Contour` + bulge arcs |
| Polyline2D | wire | direct (truck curve topology) | 2D polyline with bulge; truck `Contour` |
| Polyline3D | wire | direct (truck curve topology) | linear-edge truck `Contour`, no bulge/thickness |
| RasterImage | wire | direct | boundary rectangle / clipping polygon |
| Ray | wire | direct | two-point `[base, base + dir×1e6]`, no sampling |
| Region | mesh | truck-with-direct-fallback | 2D planar ACIS body; same truck path as Solid3D |
| Seqend | none | n/a | vertex-sequence terminator sentinel; no output |
| Shape | wire | direct | small diamond marker at the insertion point |
| Solid | wire | direct | 2D SOLID: four quad edges as direct `Lines` |
| Solid3D | mesh | truck-with-direct-fallback | 3DSOLID: parse SAT/SAB → truck `Shell``triangulation`; fallback sampler |
| Spline | wire | direct (truck curve topology) | NURBS/BSpline truck `Edge`, `ParameterDivision1D` sampled |
| Table | both | direct | cell fills (`fill_tris`) + LFF cell text + grid lines |
| Text | wire | direct | LFF-font stroked glyph polylines |
| Tolerance | wire | direct | feature-control frame grid + per-cell LFF symbols |
| Underlay | wire | direct | boundary rectangle of the PDF/DWF reference |
| Unknown | none | n/a | unrecognized-entity sentinel; no output |
| Viewport | wire | direct | content-viewport frame rectangle (sheet viewport skipped) |
| Wipeout | wire | direct | boundary rectangle / clipping polygon |
| XLine | wire | direct | three-point infinite line, no sampling |