feat(text): add MSDF font atlas text rendering module - #591
Open
stormmuller wants to merge 1 commit into
Open
Conversation
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 Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
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
Adds a new
@forge-game-engine/forge/textmodule implementing the MSDF (multi-channel signed distance field) text rendering path proposed in #584:FontAtlastype +loadFontAtlas(/asset-loading) - parses anmsdf-atlas-genJSON metrics file and loads its atlas PNG (via the existingImageCache) into glyph advances, kerning pairs, and line metrics, all normalized to em units. Kept data-only (no WebGL) so/asset-loadingkeeps its existing zero-dependency-on-rendering direction.createMsdfTextRenderable(/rendering) - reuses the existingsprite.vertshader and its instance data layout (spriteInstanceDataSegment) completely unchanged, paired with a newmsdf.fragshader (median-of-three-channels reconstruction +fwidth-based screen-space-derivative antialiasing, the standard technique frommsdf-atlas-gen's own docs). One renderable is built perFontAtlasand shared across every entity using that font, exactly like a sprite'sRenderable.TextEcsComponent/addTextComponent,TextMeshEcsComponent, andcreateTextShapingEcsSystem(/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 whentext,font,fontSize,wrapWidth,lineSpacing,alignment, orpivotactually changed, not every frame.createRenderEcsSystemtext integration - per the issue's own risk analysis, this skips the larger "generalize nine-slice into a sharedSubQuad[]expansion" refactor in favor of the issue's suggested fallback: an isolatedpushTextRenderCommands/buildTextCameraCommandspair that turns each glyph into aSpriteEcsComponent-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:
msdf-atlas-gentoolchain, which isn't available in this environment. Every project currently supplies its own atlas.fillText-to-texture fallback for quick prototyping; not built here./documentation-site/src/pages/demos/textdemo 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-typespasses with 0 errorsnpm testpasses (1084 tests, 161 files)npm run lintpasses with 0 errorsnpm run cspellpasses with 0 errorsnpm run check-exportspasses (@forge-game-engine/forge/textresolves 🟢 ESM/bundler like every other subpath)index.ts,/src/index.ts, andpackage.jsonexports(new./textentry)/documentation-site/docs/docs/text(overview + an MSDF Text guide covering atlas generation, wrapping/alignment, registration order, and dirty tracking), plus updates to theasset-loadingandrenderingsection docs to cross-link itTextEcsComponent/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 rannpm run build,documentation-site'snpm 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/textand/docs/text/msdf-textpages in a real browser (Playwright/Chromium) to confirm no regressions and no MDX/build errorsChangelog
## [Unreleased]→#### AddedinCHANGELOG.mdGenerated by Claude Code