TaosEngine

Space Combat — a Wing Commander–style dogfight

A self-contained space-combat tech demo: cockpit flight, lead-computing laser cannons, pursuing enemy fighters, a procedural starfield with a lens-flare sun, a textured planet backdrop, a drifting asteroid field, and a full combat HUD — all built on the engine’s deferred render-graph preset. No external model or texture assets; every ship, rock, and planet is generated at startup.

Controls

Input Mouse + Keyboard Gamepad Touch
Steer (pitch/yaw) Mouse Left stick Left joystick
Roll A / D LB / RB ⟲ / ⟳ buttons
Throttle W / S D-pad ↑ / ↓ THR+ / THR-
Afterburner Shift LT BURN
Fire LMB / Space RT FIRE
Engage click canvas (pointer lock) any input any touch

Touch and gamepad are self-installing — a desktop session never sees the touch overlay, and the gamepad wakes on first input. All three input methods drive the same PlayerFlight simultaneously (see flight_model.ts + controls.ts).

Press G for the render-graph visualization overlay.

Rendering pipeline

Built on the high-level Engine + deferredPreset API; features are added/removed and the engine rebuilds the graph each frame. The tail is re-ordered so every bright element blooms:

…sky → deferred lighting → TAA → stars → lens-flare → explosions → bloom → tonemap

Stars, the sun flare, and explosion sparks are all added before bloom; ship engine glow and laser bolts emit via emissiveFactor (see Emissive glow below), so all of them streak through the bloom pass.

Stars — StarsFeature (new engine feature)

src/renderer/features/stars_feature.ts wraps the existing StarsPass: a full-screen pass that adds procedural stars on sky pixels (depth == 1). The feature defaults to a downward sun vector (perpetual night) and full-sphere coverage so stars are always visible in any direction — the right behavior for space. Reads frame.hdr + frame.depth, writes frame.hdr.

Glowing sun — LensFlareFeature (new engine feature + pass)

A screen-space lens flare is the sun. LensFlareFeature (src/renderer/features/lens_flare_feature.ts) projects the sun direction to screen space each frame using the render camera, zeroes the flare when the sun is behind the camera, and hands the pass a screen UV + intensity + color. The pass (lens_flare_pass.ts + lens_flare.wgsl) draws:

and occludes the whole flare by sampling a 3×3 neighborhood of scene depth at the sun’s pixel — so a ship, asteroid, or the planet eclipsing the sun makes the flare vanish, and it fades smoothly as the sun leaves frame. Because it reads the same depth the rest of the scene wrote, no separate occlusion query is needed.

Emissive glow

The deferred geometry shader computes emission as albedo*mer.g + emissiveMap.rgb * emissiveFactor, and the default emissive map is black — so emissiveFactor alone emits nothing. emissive.ts binds a shared 1×1 white emissive map to every emissive material (laser bolts, engine pods, cannon accents), turning emission into 1 * emissiveFactor so they glow and bloom.

Flight model (flight_model.ts)

Arcade, not Newtonian: velocity is locked to the facing direction, so the ship goes where the nose points. Mouse movement feeds a virtual stick that decays back to center; touch/gamepad feed absolute steer/roll/throttle inputs that fold into the same integration. Rotation is integrated in the ship’s local frame (pitch about local X, yaw about local Y, roll about local −Z) by post-multiplying the orientation quaternion. The player ship’s transform is the camera transform — a true cockpit view.

Enemy AI (enemy.ts)

Each enemy pursues the player, adds a per-craft sine jink so a wing doesn’t fly in lockstep, breaks off when it gets too close, banks toward its heading via slerp, and leads its shots with a first-order intercept (leadPoint in math_util.ts), only firing when its nose is on that point. Speeds are tuned slower than the player so dogfights stay readable.

Weapons (lasers.ts)

Pooled emissive bolts oriented along their velocity. Collision is a swept segment-vs-sphere test between the bolt’s previous and current position, so a 240 m/s bolt can’t tunnel through a fighter between frames. Bolts inherit the shooter’s velocity so shots from a moving ship fly true.

Planet (planet.ts)

A distant globe textured entirely on the CPU: an equirectangular albedo built by sampling 3D Perlin fBM on the sphere direction (no date-line seam) — oceans, beaches, vegetation, rock, and polar ice by elevation/latitude — plus a separate slowly-rotating semi-transparent cloud shell. Both are locked to the camera at a fixed world offset so the planet stays an un-reachable backdrop while spinning on its own axis.

Asteroids (asteroids.ts)

A handful of base meshes, each a low-res UV sphere whose vertices are displaced by 3D Perlin fBM and emitted flat-shaded (per-face normals) for a chunky, faceted look, are generated once and reused across instances with random scale / spin / drift. The field is world-fixed, so you fly among the rocks; they tumble, drift, and gently bounce back when they wander too far.

HUD (hud.ts)

A 2D overlay canvas. The main loop projects the world into screen-space figures (resolveScreen clamps off-screen/behind targets to the viewport edge for direction arrows); the HUD draws the center reticle + lead pipper, target brackets with range and a shield bar on the locked target, a top-down radar dish, and the throttle / shield / hull gauges with the wave / kills / hostiles readout.