Skip to content

feat(text): add MSDF font atlas text rendering module - #591

Open
stormmuller wants to merge 1 commit into
devfrom
claude/github-issue-584-erecu0
Open

feat(text): add MSDF font atlas text rendering module#591
stormmuller wants to merge 1 commit into
devfrom
claude/github-issue-584-erecu0

Conversation

@stormmuller

Copy link
Copy Markdown
Member

Summary

Adds a new @forge-game-engine/forge/text module implementing the MSDF (multi-channel signed distance field) text rendering path proposed in #584:

  • FontAtlas type + loadFontAtlas (/asset-loading) - parses an msdf-atlas-gen JSON metrics file and loads its atlas PNG (via the existing ImageCache) into glyph advances, kerning pairs, and line metrics, all normalized to em units. Kept data-only (no WebGL) so /asset-loading keeps its existing zero-dependency-on-rendering direction.
  • MSDF fragment shader + createMsdfTextRenderable (/rendering) - reuses the existing sprite.vert shader and its instance data layout (spriteInstanceDataSegment) completely unchanged, paired with a new msdf.frag shader (median-of-three-channels reconstruction + fwidth-based screen-space-derivative antialiasing, the standard technique from msdf-atlas-gen's own docs). One renderable is built per FontAtlas and shared across every entity using that font, exactly like a sprite's Renderable.
  • TextEcsComponent/addTextComponent, TextMeshEcsComponent, and createTextShapingEcsSystem (/text) - shapeText (pure, fully unit-tested) word-wraps, applies kerning, and lays out lines by alignment/line-spacing/pivot into glyph quads. The shaping system is dirty-tracked: it only re-shapes an entity when text, font, fontSize, wrapWidth, lineSpacing, alignment, or pivot actually changed, not every frame.
  • createRenderEcsSystem text integration - per the issue's own risk analysis, this skips the larger "generalize nine-slice into a shared SubQuad[] expansion" refactor in favor of the issue's suggested fallback: an isolated pushTextRenderCommands/buildTextCameraCommands pair that turns each glyph into a SpriteEcsComponent-shaped render command. This reuses the sprite instancing/batching/depth-sort pipeline completely unchanged (zero modifications to the existing sprite/nine-slice code path) while still letting text interleave correctly with sprites in one sorted command buffer.

Not included (follow-up work)

Flagged explicitly in the new docs' "Current limitations" section:

  • No font shipped with the engine - generating a real MSDF atlas requires the msdf-atlas-gen toolchain, which isn't available in this environment. Every project currently supplies its own atlas.
  • No Canvas2D prototyping escape hatch - the issue proposes a documented fillText-to-texture fallback for quick prototyping; not built here.
  • No text effects (outline/glow/shadow) - these fall out as extra MSDF shader parameters later.
  • No demo - without a real generated font atlas to point one at, a /documentation-site/src/pages/demos/text demo would have nothing genuine to render. Everything else in the module (shaping math, dirty tracking, shader, render-system integration) has full unit-test coverage instead.

Related issue(s)

Closes #584

Verification checklist

  • npm run check-types passes with 0 errors
  • npm test passes (1084 tests, 161 files)
  • npm run lint passes with 0 errors
  • npm run cspell passes with 0 errors
  • npm run check-exports passes (@forge-game-engine/forge/text resolves 🟢 ESM/bundler like every other subpath)
  • New public API is exported from each module's index.ts, /src/index.ts, and package.json exports (new ./text entry)
  • Documentation added under /documentation-site/docs/docs/text (overview + an MSDF Text guide covering atlas generation, wrapping/alignment, registration order, and dirty tracking), plus updates to the asset-loading and rendering section docs to cross-link it
  • No existing demo touches TextEcsComponent/the new msdf shader, so none needed updating - but since this PR does touch /rendering (render-system.ts, the shader cache, shader index), I still ran npm run build, documentation-site's npm run typecheck/npm run build, and loaded the Nine-Slice demo (the closest existing exerciser of the sprite render-command path this PR adds a sibling branch next to) plus the new /docs/text and /docs/text/msdf-text pages in a real browser (Playwright/Chromium) to confirm no regressions and no MDX/build errors

Changelog

  • Added a bullet under ## [Unreleased]#### Added in CHANGELOG.md

Generated by Claude Code

Adds `@forge-game-engine/forge/text`: `loadFontAtlas`/`FontAtlas`
(`/asset-loading`) parse an msdf-atlas-gen JSON metrics file and atlas PNG;
`createMsdfTextRenderable` (`/rendering`) builds the GPU-side renderable,
reusing the sprite vertex shader/instance data layout unchanged, paired
with a new MSDF fragment shader (median-of-three-channels + screen-space-
derivative antialiasing); `TextEcsComponent`/`addTextComponent` and
`createTextShapingEcsSystem` (`/text`) shape a string - word-wrapping,
kerning, alignment, line spacing - into glyph quads held on a
`TextMeshEcsComponent`, dirty-tracked so re-shaping only happens when a
shape-affecting field actually changes.

`createRenderEcsSystem` draws text through an isolated expansion branch
that turns each glyph into a `SpriteEcsComponent`-shaped render command,
so text batches and depth-sorts with sprites in the same command buffer
without touching the sprite/nine-slice hot path.

Closes #584. Ships the MSDF rendering path from the issue; a default
shipped font, a Canvas2D prototyping escape hatch, and text effects
(outline/glow/shadow) are follow-up work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113gCpCEDFQad8Y9dJuahSq
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.40288% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/asset-loading/load-font-atlas.ts 90.00% 0 Missing and 2 partials ⚠️
src/rendering/systems/render-system.ts 91.30% 0 Missing and 2 partials ⚠️
src/text/utilities/shape-text.ts 98.30% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(text): text rendering — MSDF font atlases, shaping, and a TextEcsComponent

2 participants