Skip to content

Per-Viewport SDF Text

App: apps/split_text_demo/

A 2-player horizontal split (each cell has its own third-person camera) that draws SDF text with Graphics.perViewportOverlays(true), so text is replayed into each viewport — the same flag that already replayed the retained-UI HUD now also covers SDF text (Camera-authoring follow-up, ADR 0105).

  • World-space nameplates (“ONE” over player 0, “TWO” over player 1) are re-projected by that cell’s own camera. Each player’s name sits above their own cube in their own view; the other player’s nameplate is off-screen there. This is the piece per-viewport text adds over the pre-existing per-viewport HUD.
  • A screen-space banner (“PER-VIEWPORT TEXT”) is scaled into both cells, like the retained-UI HUD.

Without perViewportOverlays(true) (or with no split-screen viewports), text draws once full-screen against the main camera — the byte-identical legacy path — and would miss the split entirely.

import "engine" for Graphics, Window, Scene, SplitScreen, SplitLayout, Text
var split = SplitScreen.new(scene, players) // players = List of Node (one camera each)
split.layout(SplitLayout.horizontal) // two columns, side by side
var name = Text.new("ONE") // a world-space nameplate above player 0's cube
var banner = Text.new("PER-VIEWPORT TEXT") // a screen-space banner (pixels)
Graphics.perViewportOverlays(true) // replay overlays (HUD + text) into each cell, scissored
// in draw(): submit each label ONCE — the renderer replays it per cell.
name.draw(worldModel) // re-projected by each cell's own view/proj
banner.drawScreen(Window.getWidth() / 2 - banner.width / 2, 22) // scaled into each cell
Terminal window
% ./plume3d split_text_demo

Each cell shows only its own player’s world-space nameplate over their cube, plus the shared screen-space banner near the top of both cells. Auto-screenshots split_text.png (~frame 60, after the split cameras settle) then exits.

Graphics.perViewportOverlays(on) already replayed the retained-UI HUD per cell (Camera v2 / CAM-18, ADR 0064). ADR 0105 extends the same flag to SDF text: record_text_draws loops the split-screen viewports and, for each, sets the sub-rect viewport/scissor and re-accumulates the labels —

  • screen-space labels use the full-window ortho, so the sub-rect viewport scales them into the cell (exactly like the HUD), and
  • world-space labels are drawn against that viewport’s projectionMatrix * viewMatrix, so a world label projects to the correct on-screen spot for each player’s camera.

The model-space glyph geometry is identical across cells (same text, same layout order) — only the baked MVP push-constant differs — so re-accumulating per viewport overwrites the shared text buffer with byte-identical data each pass while each cell’s recordDraw bakes its own per-cell MVPs. Depth-test stays on, so world text is occluded by that cell’s own geometry. Flag off / no viewports → one full-screen draw against the main camera, byte-identical to the legacy path.