Functions
installCamera2d
function installCamera2d(cam: { x: number; y: number; zoom: number; install?(a: unknown, b: unknown): void }, screen: { width: number; height: number }): { toWorld(sx: number, sy: number): { x: number; y: number }; toScreen(wx: number, wy: number): { x: number; y: number }; view(): { x: number; y: number; w: number; h: number; cx: number; cy: number } }
From @phaser-ae/kernel/internal
Install the 2D camera transform on the kernel camera: screen = (world − cam) · zoom + half-screen, measured across
the seam in a wrapping world. Snaps the camera to the screen centre, so world (0, 0) starts at the top-left corner.
Returns the transforms and view(), the rectangle the camera sees in world units, derived fresh at each call.
installCamera3d
function installCamera3d( cam: { x: number; y: number; z: number; yaw: number; pitch: number; fov: number; install?(a: unknown, b: unknown, c: unknown): void }, screen: { width: number; height: number }, viewOf: () => View3d, ): { toWorld(sx: number, sy: number): { x: number; y: number }; toScreen(wx: number, wy: number, wz?: number): { x: number; y: number }; screenRay(sx: number, sy: number): { origin: V3; dir: V3 } }
From @phaser-ae/kernel/internal
Install the 3D camera transform. toScreen projects a world point to logical pixels (behind
the camera comes back off-screen); toWorld returns where the screen point meets the y = 0
ground plane, which is what a pointer usually means in a 3D game — screenRay is there for
everything else.
lerpAngle
function lerpAngle(from: number, to: number, a: number): number
From @phaser-ae/kernel/internal
Interpolate an angle along the SHORTEST arc: +π → −π is a hair's turn, not a full spin through 0.
view3dFrom
function view3dFrom(cam: { x: number; y: number; z: number; yaw: number; pitch: number; fov: number }, aspect: number, near: number, far: number, distance = 0): View3d
From @phaser-ae/kernel/internal
Build a 3D camera's matrices from the kernel camera's fields. The kernel camera carries
x y z (the eye), yaw/pitch (where it looks) and fov; a rig writes those, and this
turns them into the matrices once a frame. distance > 0 orbits: the eye is pulled back
that far along the look direction, so x y z is the point of interest instead.
Classes
Gather2d
class Gather2d
From @phaser-ae/kernel/internal
The shared 2D gather. Construct with the roster, the world's layer names, the world's first global layer index (from registerWorld), and the space.
readonly tables: Table2d[]— One table per layer, in layer order; the same objects every frame, rewritten.wrap: Wrap | null— In a wrapping world every object draws at its nearest image to the rendered camera.camera: { render: { x: number; y: number; zoom: number } } | null— The rendered camera; needed for wrap (nearest image) and for the view rect. Null draws at raw positions.screen: { width: number; height: number } | null— The logical screen, for the view rect aggregates cull against.readonly space: 'world' | 'screen'— 'world' goes through the camera; 'screen' is logical pixels.inset: { local: number; above: LayerName | undefined } | null— A SCREEN gather's layer that draws inside a 2D world's stack instead of with the screen's own layers — world-screen'sunderlay— and the world layer it sits directly above (undefined: the first 2D world's first layer). Its table moves into that world's list, drawn in the world pass, and its depth onto the seam between that layer and the next, soroster.atagrees with what is drawn. With no 2D world composed it stays on screen, drawn before the screen's other layers. Set it once, before the first frame. Anaboveno 2D world declares takes the default.constructor(private readonly r: KernelRoster, readonly layers: ReadonlyArray<LayerName>, readonly first = 0, space: 'world' | 'screen' = 'world')worlds(): ReadonlyArray<Gather2d>— The 2D worlds' gathers on this roster, in composed order: the stacks an inset can sit inside.resolveDepth(rule: (slot: number) => number): void— Write depth for every live slot from the world's rule. An inset layer's rule is squeezed onto its seam, keeping its order.tables_(alpha: number): ReadonlyArray<InstanceTable>— Build this frame's tables: live, visible slots per layer sorted by depth, interpolated atalpha; aggregates emit in place. A world'stables()returns this: its own tables in layer order, with any screen layer placed inside this stack straight after the layer it sits on, and a screen gather's inset either handed to its world or first.
Gather3d
class Gather3d
From @phaser-ae/kernel/internal
The gather every 3D world shares: per layer, sort the live, visible slots and write an interpolated mesh instance table from the roster's columns. The same shape as the 2D gather — a world supplies only its depth rule — but the pose is the full 3D one: position xyz, yaw/pitch/roll, scale xyz, and the mesh each instance wears. Construct with the roster, the world's layer names and its first global layer index.
The depth buffer does not make sorting unnecessary. Opaque meshes are sorted FRONT to BACK so early-z rejects the hidden ones, and the renderer draws transparent ones back to front afterwards. The rule a world hands in is distance from the camera, so both orders come from one number.
readonly tables: Table3d[]— One table per layer, in layer order; the same objects every frame, rewritten.meshOf: (() => Uint32Array | undefined) | null— How to find the mesh id column, called once a frame. It must be looked up rather than cached: the roster REALLOCATES its column arrays when it grows, so a reference taken at attach goes stale.billboardOf: (() => Uint8Array | undefined) | null— The same for the billboard flag column.materialOf: (() => Uint32Array | undefined) | null— And for the material index column.surfaceOf: (() => Uint32Array | undefined) | null— And for the extended-surface slot column.pickableOf: (() => Uint8Array | undefined) | null— And for the pickable flag column.quadMesh: number— The mesh instances an aggregate emits are all the same quad; the world sets which.planes: Float32Array | null— Set to this frame's frustum planes to drop objects the camera cannot see; null draws everything.radii: Float32Array | null— Each mesh's cull radius at scale 1 (meshCullRadius), indexed by mesh id; the world keeps it current as meshes are registered and changed. Null, an id past its end or a 0 entry is culled as a unit mesh.culled: number— Objects skipped by the frustum last gather — what culling actually saved.constructor(private readonly r: KernelRoster, readonly layers: ReadonlyArray<LayerName>, readonly first = 0)resolveDepth(rule: (slot: number) => number): void— Write depth for every live slot from the world's rule (distance from the camera).tables_(alpha: number): ReadonlyArray<InstanceTable>— Build this frame's tables: live, visible slots per layer, sorted near→far, interpolated atalpha.
Table2d
class Table2d implements InstanceTable
From @phaser-ae/kernel/internal
One 2D layer's instance table, reused every frame. Its columns grow by doubling and never shrink.
readonly kind: "sprites2d"— Always sprites.space: 'world' | 'screen'— Set by the gather from its world: 'world' goes through the camera, 'screen' is logical pixels.pass: 'world' | 'screen'— Which sprite pass draws it: its space, except a screen layer placed inside a world's stack, which is drawn with the world.count: number— Instances written this frame.order: Uint32Array<ArrayBuffer>— Slot per instance, draw order. Starts at 64 and doubles.x: Float32Array<ArrayBuffer>— Interpolated centre x.y: Float32Array<ArrayBuffer>— Interpolated centre y.z: Float32Array<ArrayBuffer>— z, copied.rot: Float32Array<ArrayBuffer>— Interpolated rotation, radians.sx: Float32Array<ArrayBuffer>— Interpolated scale x.sy: Float32Array<ArrayBuffer>— Interpolated scale y.width: Float32Array<ArrayBuffer>— Width, world units.height: Float32Array<ArrayBuffer>— Height, world units.frame: Uint32Array<ArrayBuffer>— Global frame index.tint: Uint32Array<ArrayBuffer>— Packed rgba8 tint.flags: Uint8Array<ArrayBuffer>— The object'sdrawBy(an aggregate's instances carry their object's).alpha: Float32Array<ArrayBuffer>— Opacity.blend: Uint8Array<ArrayBuffer>— Blend mode index.repeatX: Float32Array<ArrayBuffer>— Texture repeats x.repeatY: Float32Array<ArrayBuffer>— Texture repeats y.scrollX: Float32Array<ArrayBuffer>— UV scroll x.scrollY: Float32Array<ArrayBuffer>— UV scroll y.deformKind: Uint8Array<ArrayBuffer>— Deform kind index.deformAmount: Float32Array<ArrayBuffer>— Deform amount.deformSpeed: Float32Array<ArrayBuffer>— Deform speed.deformPhase: Float32Array<ArrayBuffer>— Deform phase.constructor(readonly layer: LayerName)ensure(n: number): void— Grow every column to hold at leastninstances, doubling. Existing contents are KEPT: an aggregate grows the table from inside its own emit, one instance at a time, so a grow that dropped what was already written would erase the tiles or particles emitted before it and leave holes in the layer.
Table3d
class Table3d implements InstanceTable
From @phaser-ae/kernel/internal
One 3D layer's mesh instance table, reused every frame. Columns a mesh does not use are kept for the shared contract.
readonly kind: "meshes3d"— Always meshes.readonly space: "world"— 3D tables always go through the camera.count: number— Instances written this frame.order: Uint32Array<ArrayBuffer>— Slot per instance, draw order.x: Float32Array<ArrayBuffer>— Interpolated x.y: Float32Array<ArrayBuffer>— Interpolated y (UP).z: Float32Array<ArrayBuffer>— Interpolated z.rot: Float32Array<ArrayBuffer>— Interpolated yaw, radians.pitch: Float32Array<ArrayBuffer>— Interpolated pitch, radians.roll: Float32Array<ArrayBuffer>— Interpolated roll, radians.sx: Float32Array<ArrayBuffer>— Interpolated scale x.sy: Float32Array<ArrayBuffer>— Interpolated scale y.sz: Float32Array<ArrayBuffer>— Interpolated scale z.mesh: Uint32Array<ArrayBuffer>— The mesh each instance wears; the renderer batches by it.billboard: Uint8Array<ArrayBuffer>— 0 the object's own rotation, 1 faces the camera, 2 turns about y only; an aggregate's quads may also use 3 (along a direction carried in rot/pitch/roll) and 4 (flat on XZ).material: Uint32Array<ArrayBuffer>— Which registered material each instance wears; 0 is the default.surface: Uint32Array<ArrayBuffer>— Which extended-surface slot each instance wears; 0 is none.pickable: Uint8Array<ArrayBuffer>— Whether each instance answers a pick; 0 is not.width: Float32Array<ArrayBuffer>— Unused by meshes; the width column the contract requires.height: Float32Array<ArrayBuffer>— Unused by meshes.frame: Uint32Array<ArrayBuffer>— Atlas frame a textured mesh wears; 0 (the white square) means untextured.tint: Uint32Array<ArrayBuffer>— Packed rgba8 colour.flags: Uint8Array<ArrayBuffer>— The object'sdrawBy.alpha: Float32Array<ArrayBuffer>— Opacity; below 1 the renderer draws it in the transparent pass.blend: Uint8Array<ArrayBuffer>— Blend mode index.repeatX: Float32Array<ArrayBuffer>— Unused by meshes.repeatY: Float32Array<ArrayBuffer>— Unused by meshes.scrollX: Float32Array<ArrayBuffer>— Unused by meshes.scrollY: Float32Array<ArrayBuffer>— Unused by meshes.deformKind: Uint8Array<ArrayBuffer>— Unused by meshes.deformAmount: Float32Array<ArrayBuffer>— Unused by meshes.deformSpeed: Float32Array<ArrayBuffer>— Unused by meshes.deformPhase: Float32Array<ArrayBuffer>— Unused by meshes.constructor(readonly layer: LayerName)ensure(n: number): void— Grow every column without erasing rows already emitted by an aggregate this frame.
Constants
LAYER_SPAN
const LAYER_SPAN: 1000000
From @phaser-ae/kernel/internal
Depth = layer × LAYER_SPAN + the world's rule, so a layer never interleaves with its neighbour. A rule must stay within ±5e5.
MESH_COLUMN
const MESH_COLUMN: "mesh3d"
From @phaser-ae/kernel/internal
Where a 3D object's mesh id lives: the column world-3d declares under this key.
Types
View3d
interface View3d
From @phaser-ae/kernel/internal
A 3D camera's matrices for one frame, plus what the renderer and picking both need from it.
eye: V3— Where the camera is.target: V3— What it looks at.view: Mat4— World → view.proj: Mat4— View → clip.viewProj: Mat4— World → clip.invViewProj: Mat4— Clip → world, for rays and unprojection.planes: Float32Array— The six frustum planes, for culling.
