Skip to content

Latest commit

 

History

History
393 lines (312 loc) · 16 KB

File metadata and controls

393 lines (312 loc) · 16 KB

STATE.md — Phaser 3D fork (running state)

Purpose. This file is the canonical entry point for anyone (human or AI agent) opening this repository cold. Read it first; it tells you what shipped, what is in progress, what conventions to follow and where everything lives. Keep it in sync with reality — every PR that changes scope should touch this file.

Last updated: 2026-05-24.


1. Project at a glance

This is a fork of Phaser 4 that adds a minimal, web-first 3D rendering pipeline alongside the existing 2D renderer. The 3D side lives in its own folders so syncing with upstream Phaser stays cheap (see rewrite.md for the exact list of patched 2D files).

  • Engine fork: in src/, with the 3D engine isolated in six new folders (see §3).
  • Reference sandbox (Vite + React + Monaco): examples/vite-3d/. ~30 scenes that exercise every shipped feature.
  • Comparative sandbox: examples/three/ (Vite + Three.js). The same stress-test and instancing demos rebuilt on Three.js for honest cross-engine draw-call comparison.
  • glTF samples (Khronos): examples/gltf-samples/ (read-only reference; copy individual models into examples/vite-3d/public/assets/gltf/ as needed).

The renderer ships behind a webpack build flag (WEBGL3D_RENDERER), so a Phaser build without the flag behaves like vanilla upstream.


2. How to build and run

Engine

# from the repo root
npm install
npm run build            # produces build/phaser.js (~9.9 MB UMD)

The examples/vite-3d sandbox consumes the freshly built build/phaser.js via its dev plugin, so iterating on the engine is "rebuild the engine, refresh the sandbox tab".

Sandbox

cd examples/vite-3d
npm install
npm run dev              # http://localhost:5173 (or next free port)
# or for a static drop you can host anywhere:
npm run build && npx serve dist

The Vite plugin also copies build/phaser.js into dist/phaser-build.js on vite build, so the resulting dist/ is fully self-contained.

Three.js comparative sandbox

cd examples/three
npm install
npm run dev              # http://localhost:5273

Two pages: / (stress, one mesh per cube) and /instanced.html (one THREE.InstancedMesh). Used for benchmarking against the equivalent Phaser scenes in examples/vite-3d/#/stress-test and #/instanced-cubes.


3. Where the 3D engine lives

Six folders, all new — they do not collide with upstream Phaser.

Folder What it contains
src/renderer/webgl3d/ WebGL3DRenderer, MaterialManager, Material, GLSL shaders (8 base + 4 instanced + 4 skinned variants).
src/cameras/3d/ Camera3D, PerspectiveCamera, OrthographicCamera, Frustum, Ray3D, CameraManager3D.
src/gameobjects3d/ Object3D, Mesh3D, SkinnedMesh3D, InstancedMesh3D, Billboard3D, BlobShadow3D, primitives (Cube, Plane), GameObjectFactory3D.
src/lights3d/ Light3D base + Ambient, Directional, Point, Spot subclasses + LightManager3D.
src/loader3d/ GLTFFile, GLTFParser, GLTFAsset (glTF 2.0 loader).
src/animation3d/ AnimationMixer3D (linear blend skinning + crossfade).

Patched 2D files (9 of them) are listed in rewrite.md. That is the file to consult when syncing with a new upstream Phaser.


4. Features shipped

This is the closed list of what the engine can do today. Every entry maps to documentation in docs/WEBGL3D.md and at least one example in the sandbox.

Renderer

  • Render type Phaser.WEBGL3D = 9 (build-flagged).
  • WebGL2 only; throws at CreateRenderer time if the device lacks WebGL2.
  • 8 base shader programs: unlit_color, unlit_textured, lit_color, lit_textured + their :skinned variants for linear blend skinning.
  • 4 additional :instanced shader programs for GPU instancing (color × textured, lit and unlit; no skinned-instanced yet).
  • Per-camera linear fog, vertex snapping (u_snap) and affine UV mapping (u_affine) for stylized looks.
  • Colour space toggle: webgl3d.colorSpace: 'linear' | 'srgb' Game config flag.
  • Frustum culling per mesh (bounding sphere test against camera frustum planes).
  • renderer.stats with drawCalls, meshesDrawn, meshesCulled.

Materials

  • Lit / unlit families × color / textured shapes. Smooth or flat shading toggle (shading: 'smooth' | 'flat').
  • Emissive (emissive, emissiveIntensity, emissiveTexture).
  • Normal maps (normalTexture, normalScale), TBN derived in the fragment shader from screen-space derivatives (no TANGENT attribute required).
  • Ambient occlusion (occlusionTexture, occlusionStrength).
  • Specular (Blinn-Phong) (specular, shininess).
  • Texture transform (textureTransform: { offset, scale, rotation }), glTF KHR_texture_transform compatible.
  • TEXCOORD_1 (texCoord: 0 | 1).
  • Alpha modes: transparent, alphaTest, depthWrite, blendMode: 'normal' | 'additive' | 'multiply'.
  • Vertex colours (vertexColors: true).

Lighting

  • Slots: ambient (1), directional (1), points[] (up to 4), spots[] (up to 4). All consumed by every lit_* shader.
  • Presets: lights3d.preset('studio' | 'moody' | 'none').
  • Spot lights with inner / outer cone half-angles; cone cosines are precomputed on upload.

Cameras

  • PerspectiveCamera and OrthographicCamera (in Phaser.Cameras.ThreeD).
  • High-level helpers on Camera3D: fixed(position, target), follow(target, { offset, lookAtOffset }), orbit(target, { distance, yaw, pitch }), firstPerson(position, yaw, pitch), clearControl().
  • getRay(x, y, width, height, out?) for picking.
  • setFog(color, near, far) linear fog.

Scene graph + helpers

  • Object3D base class with parent / children / world matrix.
  • Object3D#lookAt(target, yawOnly?).
  • add3D.cube, add3D.plane, add3D.material, add3D.gltf, add3D.existing, add3D.remove.
  • add3D.raycast(ray, roots?) and add3D.raycastFromPointer(pointer) for picking (bounding-sphere precision).
  • add3D.billboard (camera-facing plane) and add3D.blobShadow (contact shadow projected on a horizontal plane).
  • root.findNode, root.findNodes, root.findMesh, root.findMeshes on glTF instances.
  • add3D.instancedMesh, add3D.instancedCube, add3D.instancedPlane for GPU instancing via gl.drawElementsInstanced. InstancedMesh3D exposes setMatrixAt, setPositionAt, setPositionScaleAt, setColorAt, commitInstances().

Loader (glTF 2.0)

  • .glb and .gltf + external buffers / images.
  • Mesh primitives: POSITION, NORMAL, TEXCOORD_0, TEXCOORD_1, COLOR_0, JOINTS_0, WEIGHTS_0, indices.
  • Node hierarchy (TRS or 4×4 matrix), skins (up to 64 joints), animations (LINEAR and STEP samplers on TRS channels; CUBICSPLINE downgrades to LINEAR with a warning).
  • PBR metallic-roughness → lit_color / lit_textured with downgrade notes for the ignored bits (metallic, roughness, etc.).
  • Extensions honoured: KHR_lights_punctual (directional, point, spot), KHR_materials_unlit, KHR_texture_transform.
  • alphaMode: OPAQUE / BLEND / MASK all handled (MASK via shader- side alpha test).

Animations

  • AnimationMixer3D with play(clipName, opts), stop, stopAll, crossFade, weight blending, looping. Auto-subscribes to scene UPDATE event by default.

5. Sandbox: examples shipped (~30, in 8 groups)

Stable URL pattern: #/<id>. Each example has a "Code" tab driven by @monaco-editor/react that shows the source verbatim. Source code lives in examples/vite-3d/src/examples/<id>.js.

Basics

  • hello-cube, primitives, stylization

Cameras

  • camera-fixed, camera-follow-orbit, camera-firstperson

Materials

  • material-lit-vs-unlit, emissive-pulse, normal-maps, occlusion-map, specular, alpha-modes, texture-transform, texcoord1

Lighting

  • lights-punctual, spot-lights, spot-flashlight, light-presets, color-space

Scene Graph

  • scene-graph-orbits, lookat-tracker, tweens

glTF Loader

  • gltf-static (DamagedHelmet), gltf-vertex-colors, gltf-unlit, gltf-animated, gltf-skinned (Fox)

Helpers

  • picking-raycast, blob-shadows-billboards, find-mesh

Demos

  • hero (synthwave skyline + WebP foreground overlay + mouse parallax),
  • mini-game-arena,
  • stress-test (1 mesh per cube, CPU draw-call ceiling),
  • instanced-cubes (single InstancedMesh3D, 100 k cap, 1 draw call).

Full registry: examples/vite-3d/src/examples/registry.js.


6. Documentation living in the repo

File Purpose
STATE.md (this file) Project entry point + status.
rewrite.md List of upstream 2D files we modified, with patches and a sync procedure. Required reading before pulling a new Phaser version.
docs/WEBGL3D.md Primary API reference for the 3D pipeline.
docs/WEBGL3D_GAME_OPPORTUNITIES.md Honest matrix of game genres viable with the current renderer (Spanish). The English version of the same data lives in examples/vite-3d/src/data/game-ideas.js and surfaces on the gallery home page.
changelog/v4/4.0/CHANGELOG-v4.NEXT.md All 3D-related entries appended over the development of this fork.
examples/vite-3d/README.md Sandbox-specific guide: how to add a new example, where assets live, how the Game ↔ Code toggle works.
examples/three/README.md What is and is not identical between the Phaser and Three.js stress / instancing ports.
feature-to-implement/webgl3dmix-renderer.md Full plan for the next feature: see §8.
.cursor/skills/webgl3d-api/SKILL.md Agent-facing skill: keeps Cursor / Codex / Claude in line with the project's conventions when editing 3D code.

7. Conventions to follow

Coding conventions

  • No emojis in code, comments, file names, console output. The project is intentionally plain-text.
  • Tabs for indentation in source, 4-space inside docstrings and Markdown.
  • JSDoc on every public method / class / property of the 3D engine, mirroring the upstream Phaser style.
  • Module pattern: every public class lives in its own file under the right src/<area>/ folder and is re-exported from the area's index.js.
  • All edits outside src/{renderer/webgl3d, cameras/3d, gameobjects3d, lights3d, loader3d, animation3d}/ must be gated behind if (typeof WEBGL3D_RENDERER) so a build without the flag stays upstream-shaped. See rewrite.md for the full disciplines list.
  • No reformats of upstream 2D files — only the surgical changes required to wire the 3D path in.

Architectural choices that should not be re-litigated lightly

These were settled during the development sessions; revisit only with strong reason:

  1. WEBGL3D is a separate renderer, not an addition to the 2D WebGLRenderer. This is what keeps rewrite.md short.
  2. InstancedMesh3D is the answer to "too many draw calls", not automatic batching. State sorting / batching is on the backlog but optional.
  3. Skinning + instancing in the same shader is out of scope today. We ship *_skinned and *_instanced as separate shader families; combining them adds complexity not justified by current demand.
  4. Frustum culling is per-mesh, not per-instance. InstancedMesh3D passes the whole batch as one drawable; spatial subdivision is the user's job.
  5. Foreground / HUD layering for v1 of the sandbox uses DOM overlays. The "Phaser-native" path is the proposed WEBGL3DMIX renderer (see §8).
  6. 2D Phaser GameObjects (add.image, add.text, etc.) do not render under WEBGL3D today. Same reason: the 2D pipeline is a different renderer. WEBGL3DMIX exists precisely to close that gap.
  7. Stylization knobs (vertexSnap, affineUV, shading: 'flat') are off by default. Only the #/stylization example shows them on purpose. Earlier demos had them enabled on the floor and read as "broken UV mapping" to the casual observer.

When in doubt about agent conventions

Read .cursor/skills/webgl3d-api/SKILL.md. It is the contract for how an agent should edit / extend the 3D code.


8. What is in progress

Next big feature: Phaser.WEBGL3DMIX

A new render type that lets a single Phaser.Game host both 3D (add3D.*) and the full 2D GameObject toolkit (add.image, add.text, add.particles, …) on the same canvas frame.

  • Status: proposed, not started.
  • Plan document: feature-to-implement/webgl3dmix-renderer.md.
  • Approach chosen: two <canvas> stacked with z-index, one Phaser.Game, two renderers that each create their own context. Zero modifications to WebGLRenderer.js (so the 2D core stays syncable from upstream with no extra conflicts).
  • Estimated effort: 1–2 weeks of focused work.
  • Why it matters: it is the feature that turns this project from "Phaser with an experimental 3D renderer next to it" into "Phaser that draws 3D, with the full 2D toolkit available on top".

The plan's own Sprint 1 is the keystone (the composer + new render type); if that compiles and renders one frame correctly, the rest is plumbing.

Smaller items in the backlog (nice-to-haves, not blockers)

  • Material batching / state sorting in the 3D render loop. Today it walks the display list in insertion order; sorting by (program, texture, material) would cut state changes ~30 % on large scenes.
  • Skinned + instanced shader variants. Useful for crowds of animated NPCs. Multi-week work; defer until real demand surfaces.
  • Particle system 3D (ParticleEmitter3D). The instancing path is the right substrate; add a CPU / GPU integrator on top.
  • Area lights (disc / line). Ambient + directional + point + spot ship today; area lights are the next lighting tier.
  • glTF morph targets. Currently dropped at load with a warning. Required for face animation in character viewers / VNs.
  • Draco / KTX2 / meshopt via peer dependency. Out of scope for the core repo; document the third-party path instead.
  • Cross-platform polish: smoke-test the gallery on Safari / iOS WebKit. Today's CI is desktop Chromium.

Frequently requested things we have intentionally not done

  • add.image bridge that secretly creates a Plane3D. Rejected in favour of WEBGL3DMIX (see §8). The bridge would lose pixel-perfect 2D rendering and most of the 2D GameObject niceties.
  • Bundled Rapier3D physics. Physics will be a peer dependency or a separate package, never bundled into Phaser core.
  • Full WebGPU rewrite. Out of scope.

9. Active integrations / dependencies

  • @monaco-editor/react 4.7 (sandbox only) — Code tab in examples.
  • react / react-dom / react-router-dom — sandbox shell.
  • three 0.169 (examples/three/ only, never linked into the Phaser core) — comparative benchmark.
  • No new runtime dependencies in the engine itself. It still ships with the same set as upstream Phaser.

10. Quick agent on-boarding checklist

When a new session opens, do the following before touching code:

  1. Read this file (top to bottom). It is short on purpose.
  2. Read rewrite.md. Knowing which 2D files are patched prevents accidental edits that break upstream sync.
  3. Read .cursor/skills/webgl3d-api/SKILL.md. Style + conventions.
  4. Skim docs/WEBGL3D.md Table of Contents to find which section covers the area you are about to change.
  5. If asked to add a new example, follow the recipe in examples/vite-3d/README.md (create a Scene class, register in registry.js, document any new asset under public/assets/).
  6. If asked to add a feature to the engine: drop new files in the relevant src/<area>/ folder; add the export in that folder's index.js; add a JSDoc block; add at least one example in the gallery; add an entry in changelog/v4/4.0/CHANGELOG-v4.NEXT.md; only edit upstream 2D files if absolutely required and document the change in rewrite.md in the same PR.
  7. Run npm run build at the repo root and npm run build in examples/vite-3d before signing off. Lints clean.

Welcome.