forked from 1b-agent/opencadstudio-upstream
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
374cf7e89f
commit
896b6c385c
5 files changed
+208
-20
No files matched your search
@@ -39,6 +39,8 @@ pub struct ViewportData {
|
||||
/// 3DFACE entity wires — separated so they are uploaded to the dedicated
|
||||
/// face3d pipeline (fill + batched edges) instead of N individual WireGpu.
|
||||
pub(in crate::scene) face3d_wires: Arc<Vec<WireModel>>,
|
||||
/// SDF text-quad vertices (Phase 2b). Empty unless `OCS_TEXT_SDF` is set.
|
||||
pub(in crate::scene) text_verts: Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>>,
|
||||
/// Per-entity normalized draw-order depth (handle.value() → (0,1)), used
|
||||
/// by the wire / face3d pipelines as a clip-z bias. WireModels carry no
|
||||
/// depth field (84 construction sites); the bias is looked up by handle
|
||||
@@ -298,6 +300,7 @@ impl shader::Primitive for Primitive {
|
||||
}
|
||||
if geo_changed {
|
||||
inner.upload_images(device, queue, &vp.images[..]);
|
||||
inner.upload_text(device, queue, &vp.text_verts[..]);
|
||||
}
|
||||
inner.cached_epoch = cur_key;
|
||||
}
|
||||
@@ -772,6 +775,59 @@ pub(crate) fn adapt_to_bg(color: [f32; 4], bg: [f32; 4]) -> [f32; 4] {
|
||||
// ── Primitive builder helpers (called by ViewportPane's shader::Program impl) ──
|
||||
|
||||
impl Scene {
|
||||
/// SDF text-quad vertices for the current document's TEXT entities. Behind
|
||||
/// the `OCS_TEXT_SDF` env flag (Phase 2b bring-up) so the default render
|
||||
/// path is untouched; empty otherwise. Glyphs are baked into the shared
|
||||
/// atlas as a side effect. Both the stroke path and this share
|
||||
/// `text_run_placement`, so quads land exactly where the strokes do.
|
||||
#[allow(dead_code)] // consumed by the text render pass in the GPU integration step
|
||||
pub(in crate::scene) fn sdf_text_vertices(
|
||||
&self,
|
||||
) -> Vec<crate::scene::pipeline::text_gpu::TextVertex> {
|
||||
self.sdf_text_vertices_enabled(std::env::var_os("OCS_TEXT_SDF").is_some())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn sdf_text_vertices_enabled(
|
||||
&self,
|
||||
enabled: bool,
|
||||
) -> Vec<crate::scene::pipeline::text_gpu::TextVertex> {
|
||||
use crate::scene::pipeline::text_gpu;
|
||||
use crate::scene::text::{glyph_quads, sdf_atlas};
|
||||
if !enabled {
|
||||
return Vec::new();
|
||||
}
|
||||
let Ok(mut atlas) = sdf_atlas::text_atlas().lock() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
for e in self.document.entities() {
|
||||
if let acadrust::EntityType::Text(t) = e {
|
||||
let p = crate::entities::text::text_run_placement(t, &self.document);
|
||||
let color = self.render_style(e).0;
|
||||
let quads = glyph_quads::layout_glyph_quads(
|
||||
&mut atlas,
|
||||
p.height,
|
||||
p.rotation,
|
||||
p.width_factor,
|
||||
p.oblique_angle,
|
||||
1.0,
|
||||
&p.font,
|
||||
&p.value,
|
||||
);
|
||||
text_gpu::push_glyph_vertices(
|
||||
&mut out,
|
||||
&quads,
|
||||
[p.origin[0], p.origin[1], p.elevation],
|
||||
1.0,
|
||||
color,
|
||||
0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Build the unified multi-viewport `Primitive` for the current layout.
|
||||
/// Model layout → one full-window viewport (more once tiled); paper
|
||||
/// layout → one viewport per floating content viewport. Each entry is
|
||||
@@ -1053,10 +1109,18 @@ impl Scene {
|
||||
self.meshes_arc()
|
||||
};
|
||||
|
||||
// SDF text quads (Phase 2b, behind OCS_TEXT_SDF). Model/content only —
|
||||
// the paper sheet must not draw model-space text onto the sheet.
|
||||
let text_verts = if inst.paper_sheet {
|
||||
Arc::new(Vec::new())
|
||||
} else {
|
||||
Arc::new(self.sdf_text_vertices())
|
||||
};
|
||||
Some(ViewportData {
|
||||
wires: all_wires,
|
||||
preview_wires,
|
||||
face3d_wires,
|
||||
text_verts,
|
||||
draw_depths: self.draw_depth_map(),
|
||||
hatches,
|
||||
wipeout_hatches,
|
||||
|
||||
Reference in new issue
Block a user