feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag

Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.

- text::text_run_placement — extracted from to_truck (behaviour-preserved)
  so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
  by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
  TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
  OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
  (rebuilds the R8 atlas texture when it grows), and a text render pass
  drawn over the wires.

Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-06 23:02:34 +03:00
1 parent 374cf7e89f
commit 896b6c385c
5 files changed
+208 -20

No files matched your search

+55 -19
View File
@@ -47,7 +47,55 @@ pub(crate) fn sync_text_alignment_point(t: &mut Text) {
}
}
/// Resolved placement of a TEXT run: the baseline-anchored run origin (WCS xy)
/// plus every parameter needed to lay the glyphs out. Shared by `to_truck` (the
/// stroke path) and the SDF-quad text collector so both place text identically.
pub struct TextPlacement {
/// Run-local origin (glyph space `[0,0]` maps here), WCS xy, kept f64.
pub origin: [f64; 2],
/// Entity elevation (WCS z).
pub elevation: f64,
pub height: f32,
pub rotation: f32,
pub width_factor: f32,
pub oblique_angle: f32,
pub font: String,
/// Raw entity value (as passed to the tessellator).
pub value: String,
/// Full WCS insertion point, for the Insertion snap.
pub wcs_insertion: [f64; 3],
}
fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity {
let p = text_run_placement(t, document);
let snap_pt = glam::DVec3::new(p.wcs_insertion[0], p.wcs_insertion[1], p.wcs_insertion[2]);
// Strokes are in glyph-local space (origin = [0,0]).
let (strokes, fill_tris) = lff::tessellate_text_ex(
[0.0, 0.0],
p.height,
p.rotation,
p.width_factor,
p.oblique_angle,
&p.font,
&p.value,
);
TruckEntity {
object: TruckObject::Text(vec![TextStroke {
strokes,
origin: p.origin,
color: None,
fill_tris,
}]),
snap_pts: vec![(snap_pt, SnapHint::Insertion)],
tangent_geoms: vec![],
key_vertices: vec![],
fill_tris: vec![],
}
}
/// Compute a TEXT entity's run placement (origin + layout params). Extracted
/// from `to_truck` verbatim so the stroke and SDF-quad paths agree exactly.
pub fn text_run_placement(t: &Text, document: &acadrust::CadDocument) -> TextPlacement {
let normal = (t.normal.x, t.normal.y, t.normal.z);
let (wsx, wsy, wsz) = crate::scene::view::transform::ocs_point_to_wcs(
(
@@ -57,7 +105,6 @@ fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity {
),
normal,
);
let snap_pt = glam::DVec3::new(wsx, wsy, wsz);
let resolved_style = resolve_text_style(&t.style, document);
let font_name = resolved_style.font_name;
// AutoCAD text geometry rule: the entity stores the FINAL width factor /
@@ -142,27 +189,16 @@ fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity {
anchor_f64[0] - (anchor_local_x as f64 * cos_r - anchor_local_y as f64 * sin_r),
anchor_f64[1] - (anchor_local_x as f64 * sin_r + anchor_local_y as f64 * cos_r),
];
// Strokes are in glyph-local space (origin = [0,0]).
let (strokes, fill_tris) = lff::tessellate_text_ex(
[0.0, 0.0],
t.height as f32,
TextPlacement {
origin,
elevation: wsz,
height: t.height as f32,
rotation,
width_factor,
oblique_angle,
&font_name,
&t.value,
);
TruckEntity {
object: TruckObject::Text(vec![TextStroke {
strokes,
origin,
color: None,
fill_tris,
}]),
snap_pts: vec![(snap_pt, SnapHint::Insertion)],
tangent_geoms: vec![],
key_vertices: vec![],
fill_tris: vec![],
font: font_name,
value: t.value.clone(),
wcs_insertion: [wsx, wsy, wsz],
}
}