What this file is for. When a new upstream Phaser version is released, the maintainer wants to drag the official
src/over our fork without overwriting our 3D work. This document is the canonical list of:
- New folders we added that upstream does not know about (just copy them across — they never collide).
- Existing 2D files we modified (these are the only files that need a manual rebase / patch on each upstream sync).
- Files outside
src/that need touching (webpack flags + cache plumbing).Keep this document in sync with reality. Every time you edit a file in
src/that did not originally belong to the 3D fork, add it to §2 with a short description of the patch.
These directories were created by the 3D fork. Upstream Phaser does not contain them, so there is nothing to merge — they live alongside the 2D code and can be copied as-is when transplanted to another Phaser checkout.
| Path | What it owns |
|---|---|
src/renderer/webgl3d/ |
The 3D renderer itself (WebGL3DRenderer, MaterialManager, Material, shaders/). |
src/cameras/3d/ |
Camera3D, PerspectiveCamera, OrthographicCamera, Frustum, Ray3D, CameraManager3D. |
src/gameobjects3d/ |
Object3D, Mesh3D, SkinnedMesh3D, InstancedMesh3D, Billboard3D, BlobShadow3D, primitives, GameObjectFactory3D. |
src/lights3d/ |
Light3D and subclasses (Ambient, Directional, Point, Spot) + LightManager3D. |
src/loader3d/ |
GLTFFile, GLTFParser, GLTFAsset (glTF 2.0 loader). |
src/animation3d/ |
AnimationMixer3D (skeletal animation runtime). |
Procedure on upstream sync: leave these folders alone. They never need patching against upstream because upstream does not touch them.
These are the only files outside §1 that the 3D fork has touched.
They are the points that conflict when arrastras the new upstream src/
on top of ours. Each one has a short description, the exact spot of the
change, and the rationale.
Every patch is gated behind the WEBGL3D_RENDERER webpack flag where
possible, so the upstream behaviour stays intact when the flag is off.
What changed: a single new constant after RIGHT: 8.
// Direction constants … RIGHT: 8,
/**
* Forces Phaser to use the experimental WebGL 3D Renderer. … (full
* docblock as in the file)
*
* @name Phaser.WEBGL3D
* @const
* @type {number}
* @since 4.2.0
*/
WEBGL3D: 9Rebase rule: paste the WEBGL3D: 9 entry into the new upstream
const.js. If upstream has added new direction constants, just make
sure WEBGL3D keeps a unique numeric slot.
What changed: two additions.
-
New cache slot in the constructor:
this.gltf = new BaseCache();
With a
/** @name Phaser.Cache.CacheManager#gltf */block, placed right afterthis.atlas(last upstream cache). -
New entry in the
destroy()keys array:var keys = [ 'binary', 'bitmapFont', 'json', 'physics', 'shader', 'audio', 'video', 'text', 'html', 'tilemap', 'xml', 'atlas', 'gltf' // ← added ];
Rebase rule: re-apply both additions. If upstream adds more cache
slots, push 'gltf' onto whatever keys array they end up with.
What changed: bottom-of-file feature flag block exposing the 3D namespaces.
if (typeof WEBGL3D_RENDERER)
{
Phaser.GameObjects3D = require('./gameobjects3d');
Phaser.Lights3D = require('./lights3d');
// …add new namespaces here if we expose anything else.
}Note that Phaser.Cameras.ThreeD is exposed via src/cameras/index.js
(see 2.6), and Phaser.Renderer.WebGL3D via src/renderer/index.js
(see 2.7); both are pulled in transitively.
Rebase rule: re-add the block at the end of the upstream
phaser.js. The block does not depend on anything 2D-specific.
What changed: three new keys at the end of the injection map.
// WebGL3D scene plugins.
cameras3d: 'cameras3d',
add3D: 'add3D',
lights3d: 'lights3d'These keys are the mapping strings registered by CameraManager3D,
GameObjectFactory3D and LightManager3D via PluginCache.register.
Rebase rule: paste the three lines at the bottom of the map.
What changed: a new feature-flagged block that adds the three 3D
scene plugins to DefaultScene.
if (typeof WEBGL3D_RENDERER)
{
DefaultPlugins.DefaultScene.push('CameraManager3D');
DefaultPlugins.DefaultScene.push('GameObjectFactory3D');
DefaultPlugins.DefaultScene.push('LightManager3D');
}Placed after the existing PLUGIN_CAMERA3D block, before the
PLUGIN_FBINSTANT block.
Rebase rule: paste the block back. Order does not matter as long as
the WEBGL3D_RENDERER flag exists and DefaultPlugins.DefaultScene is
the array we are pushing onto.
What changed: a feature-flagged tail block exposing the 3D camera namespace.
if (typeof WEBGL3D_RENDERER)
{
module.exports.ThreeD = require('./3d');
}Rebase rule: append at the end of the file.
What changed: a feature-flagged tail block alongside the existing
WEBGL_RENDERER / CANVAS_RENDERER blocks.
if (typeof WEBGL3D_RENDERER)
{
module.exports.WebGL3D = require('./webgl3d');
}Rebase rule: append at the end of the file, after the existing renderer blocks.
What changed: three small spots.
-
Validation branch in the
renderTypeswitch:else if (config.renderType === CONST.WEBGL3D) { if (!Features.webGL2) { throw new Error('Cannot create WebGL2 context (required by WEBGL3D renderer), aborting.'); } }
-
Variable declaration alongside the existing two:
var CanvasRenderer; var WebGLRenderer; var WebGL3DRenderer;
-
Early-return branch for the 3D renderer, placed before the regular WebGL / Canvas selection block so it short-circuits:
if (typeof WEBGL3D_RENDERER && config.renderType === CONST.WEBGL3D) { WebGL3DRenderer = require('../renderer/webgl3d/WebGL3DRenderer'); game.renderer = new WebGL3DRenderer(game); return; }
Rebase rule: re-apply all three. If upstream rewrites
CreateRenderer.js significantly (it sometimes does), keep the same
logic: validate Features.webGL2 for WEBGL3D, and short-circuit before
the WebGL / Canvas fork.
What changed: a webGL2 flag plus a testWebGL2() probe.
-
New property on the
Featuresobject (alongsidewebGL: false):webGL2: false -
New JSDoc line in the
Phaser.Device.Featurestypedef:// @property {boolean} webGL2 - Indicates whether WebGL2 is available … -
New probe function inside
init():var testWebGL2 = function () { if (!window['WebGL2RenderingContext']) { return false; } try { var canvas = CanvasPool.createWebGL(this); var ctx = canvas.getContext('webgl2'); CanvasPool.remove(canvas); return !!ctx; } catch (e) { return false; } }; Features.webGL2 = testWebGL2();
Placed right after the existing
Features.webGL = testWebGL();.
Rebase rule: re-add the property, the doc line and the probe. The probe is a self-contained block; it should drop in regardless of upstream changes.
These are not Phaser source files but they live in the same repository and must be kept patched when syncing.
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(true),
"typeof WEBGL3D_RENDERER": JSON.stringify(true), // ← added
...
});Two DefinePlugin blocks (debug + dist). Both need the
WEBGL3D_RENDERER: JSON.stringify(true) line.
Same: one DefinePlugin block, same line added.
Rebase rule: drop the flag back in every webpack config that upstream ships. Tree-shaking depends on it.
- Take the new upstream
src/(andconfig/if it changed) into a scratch folder. - Diff against your fork for each path listed in §2 and §3. The patch is small and well-bounded in every file; review and apply manually.
- Drag the files NOT in §2 and §3 from the new upstream into your fork. They cannot conflict with the 3D fork — none of them are touched.
- Leave §1 folders alone in your fork. They are exclusively yours.
- Run
npm run buildat the repo root. If the build is green, your fork is up to date with upstream + still ships 3D. - Run
npm run buildinexamples/vite-3d. If the sandbox boots, the integration still holds. - Hand-smoke-test
#/hello-cube,#/gltf-static,#/instanced-cubesand#/mix-3d-ui(when it exists). If they render, you are done.
These are the discipline guarantees that keep this list short:
- New 3D code goes in §1 folders only. If you find yourself
wanting to edit a non-3D file, ask first whether a small new helper
inside
src/renderer/webgl3d/could solve it instead. - All non-3D file edits must be gated behind
if (typeof WEBGL3D_RENDERER)so a build without the flag behaves like upstream Phaser. - Every new modification of a 2D file must be reflected in §2 of this document in the same PR. No exceptions.
- No reformats / whitespace-only changes in 2D files. They inflate diffs and cause unnecessary merge conflicts on sync.
- Avoid renaming anything in §2. If a downstream change demands it, document the rename + reason here so the next sync knows.
Some 2D files reference 3D only through the public API (e.g. they
load Phaser.Renderer.WebGL3D through the renderer index). These look
suspicious in a grep for WEBGL3D but are NOT in our patch list:
src/textures/TextureSource.js— already hadthis.renderer.deleteTexture(...). We made sureWebGL3DRendererexposesdeleteTexture; we did not modifyTextureSource.js.src/core/Config.js— thewebgl3dconfig block is read byWebGL3DRendererdirectly viagameConfig.webgl3d. No upstream code change needed.
If you see these turn up in a grep, double-check before adding them to §2; in most cases they belong here in §6 instead.