feat(labels): MSDF text label foundations (Phases 1-2 + WGSL shader)#38
Merged
feat(labels): MSDF text label foundations (Phases 1-2 + WGSL shader)#38
Conversation
… ignore) Previous commit f4011e8 added the .gitignore exception but git's 'parent directory excluded' rule means files under /data/raw/ can't be re-included via .gitignore alone — the rule needs a force-add for the initial track, after which the explicit !/data/raw/fonts/JetBrainsMono-Regular.ttf line keeps it tracked across future status checks.
Co-Authored-By: Claude <noreply@anthropic.com>
skipLibCheck:true masks this from tsc but the IDE flags it as TS1038
('declare modifier cannot be used in an already ambient context').
Inside a 'declare module' block, function declarations are already
ambient — the inner 'declare' is noise.
Co-Authored-By: Claude <noreply@anthropic.com>
Implements layoutLabel() which converts a text string into GlyphQuad attribute tuples using BMFont metrics, with kerning and silent drop of missing glyphs. Five unit tests cover count, sequential positioning, kerning, total width, and UV emission. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
skymap | b6bdbd7 | Commit Preview URL Branch Preview URL |
May 07 2026, 10:07 PM |
The 1024² atlas was ~93% empty: 100 glyphs (95 ASCII + 5 unit symbols) at fontSize 42 only need a ~213×512 bounding box. Dropping to 512² keeps a comfortable packing margin while quartering the rgba16float upload (4 MB → 1 MB). PNG-on-disk savings are modest (91.5 → 84.6 KB) because the original empty area compressed well, but GPU memory is the real win. 256² would require ≥12 rows of ~48 px and doesn't fit.
rulkens
added a commit
that referenced
this pull request
May 7, 2026
….wesl Brings the MSDF labels foundation (added on main in #38) under the WESL convention: directory layout, three-file split, lib/camera adoption. The Uniforms struct now embeds 'cam: CameraUniforms' (80-byte universal prefix) instead of declaring its own viewProj+viewport pair — both layouts are byte-equivalent at offsets 0..79, so no CPU-side change is required when the labels renderer eventually wires up a uniform writer. No consumer existed yet (foundation-only PR), so no renderer.ts to update. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.
Summary
Lands Phases 1–2 (plus the labels WGSL shader from Phase 3) of the MSDF text label plan (
docs/superpowers/plans/2026-05-07-msdf-labels.md). Stops short of the renderer code (Tasks 8–11) and engine integration (Phase 4) because both will conflict with the parallel engine + WGSL→WESL rewrite happening in another worktree.What's in:
data/raw/fonts/JetBrainsMono-Regular.ttf(267 KB OFL-licensed, force-added past the/data/ignore with an explicit allow rule).tools/buildFontAtlas.ts+ minimal.d.tsshim formsdf-bmfont-xml(no upstream types).npm run build-font— deterministic across reruns (verified by md5).public/fonts/jetbrains-mono.png(1024² RGBA MSDF, 92 KB) +.json(100 glyphs, 33 KB).fontMetrics.ts— BMFont JSON →FontMetrics(atlas dims, distance range, glyph map, kerning map).labelLayout.ts—(text, metrics) → GlyphQuad[], monospace-friendly (kerning-aware), silently drops missing glyphs.youAreHereVisibility.ts— distance → fade alpha (smoothstep between 0.6 and 2.0 Mpc).labels.wgslshader onlylabelRenderer.tsis deferred), so this is dead code onmainuntil the renderer lands.What's NOT in (deferred):
labelRenderer.ts,markerLines.wgsl,markerLineRenderer.ts— Tasks 8–11. They'd?raw-import shader files the parallel rewrite is converting WGSL→WESL, and would also need to wire into engine pieces being restructured. Re-targeting against the post-rewrite engine + WESL layout makes more sense than building twice.Plan-vs-codebase notes
A few small drift items the plan didn't account for, surfaced and fixed inline:
/data/is gitignored. The plan's font-asset path needed an explicit allow rule + initial force-add; .gitignore won't re-include files whose ancestor is excluded.msdf-bmfont-xml@^2.7.0from the plan landed as2.8.0(current).noUncheckedIndexedAccess: trueintsconfig.json— the plan's test fixtures and renderer state code needed!non-null assertions on array accesses.skipLibCheck: truemasked a TS1038 in the .d.ts shim (declareinsidedeclare module); fixed so the IDE diagnostic is clean too.Test plan
npm run typecheck— bothtsconfig.jsonandtsconfig.tools.jsonpass.npm test— 908 passing (was 895 onmain); 12 new tests across the 3 pure-logic modules.npm run build-font— emits expected sizes; deterministic across reruns.public/fonts/jetbrains-mono.png— should look like a dark image with multicoloured glyph shapes (RGB-channel SDF encoding).Notes for follow-up branch
When the engine rewrite + WESL conversion lands, the deferred work becomes:
labels.wgslto whatever WESL layout the rewrite settled on.state.gpu.*/renderFrame.tsshape (per the survey on top of the existing plan).markerLines.wgsl) directly in WESL.🤖 Generated with Claude Code