Split Screen
App: apps/split_screen_demo/
Local-multiplayer split-screen (Camera epic, ADR 0059; animated merge ADR 0060).
SplitScreen.new(scene, players) gives each player its own third-person camera in its own screen
region; the renderer draws the scene once per viewport with a divider between them. With dynamic
merge/split the four views fuse into one group-framed camera when the players cluster, and split back
into the grid when they spread — Kronnect-style — and the change is an animated blend, not a
hard-switch.
import "engine" for Scene, SplitScreen, SplitLayout
var split = SplitScreen.new(scene, players) // players = List of Nodesplit.layout(SplitLayout.grid) // 2×2 for four playerssplit.distance(7)split.height(2)split.dynamic(true)split.mergeDistance(5) // spread below this → merge to one viewsplit.splitDistance(11) // spread above this → split apart (hysteresis)split.transitionDuration(0.6) // animate the merge/split over 0.6s (0 = instant)draw() is just scene.draw() + the mesh draws — there is no per-viewport camera code.
% ./plume3d split_screen_demoFour players hold at the corners (a 2×2 grid split), then sweep to a tight ring at the centre —
triggering one animated merge into the group view that frames all four. Screenshots
split_grid (spread), split_transition (mid-blend: cells converging, dividers fading), and
split_merged (one group view).
How it works
Section titled “How it works”GraphicsState.viewports carries a list of {rect, view, projection}; the renderer draws the scene
once per viewport into its sub-rect (each with its own mesh-UBO instance), then clears thin divider
strips. An empty list is the byte-for-byte legacy single-camera path. Group framing fits all players
by their bounding sphere → fov pull-back.
Animated merge (ADR 0060)
Section titled “Animated merge (ADR 0060)”The dynamic state machine decides the target (merged vs split) with a hysteresis band + dwell
timer; the visual change is animated by mergeFraction 0→1 over transitionDuration. During the
blend the split cells are held in place and each cell’s camera eases from its own player pose
toward the shared group pose (smoothstep) while the dividers fade; then the cells collapse to one
fullscreen group viewport. Because every cell converges to the same group pose, the cells show
identical pixels at the instant they collapse — so there is no overlap artefact and no render-to-
texture needed. transitionDuration(0) restores the instant switch.
v1 scope: up to 4 viewports; overlays (sprites/text/UI) render once full-screen. The Kronnect rect peel (viewports physically sliding) and picture-in-picture / overlapping views still need render-to-texture — a later, additive refinement. See ADR 0059 + 0060.