@vgai/engine 0.5.17 → 0.5.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/renderer-config.d.ts +83 -0
- package/dist/adapter/renderer-config.d.ts.map +1 -0
- package/dist/adapter/renderer-config.js +121 -0
- package/dist/adapter/root-adapter.d.ts +2 -2
- package/dist/adapter/root-adapter.d.ts.map +1 -1
- package/dist/manifest/schema.js +1 -1
- package/dist/pixi/authoring.d.ts.map +1 -1
- package/dist/pixi/authoring.js +134 -26
- package/dist/runtime/mount-manifest.js +1 -1
- package/dist/world3d-react/index.d.ts +1 -1
- package/dist/world3d-react/index.d.ts.map +1 -1
- package/dist/world3d-react/index.js +1 -1
- package/dist/world3d-react/r3f-adapter.d.ts +2 -2
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
- package/dist/world3d-react/r3f-adapter.js +1 -1
- package/dist/world3d-react/renderer-config.d.ts +7 -69
- package/dist/world3d-react/renderer-config.d.ts.map +1 -1
- package/dist/world3d-react/renderer-config.js +7 -108
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +1 -1
- package/schemas/vgai-project.schema.json +1 -1
- package/src/adapter/renderer-config.ts +178 -0
- package/src/adapter/root-adapter.ts +2 -2
- package/src/manifest/schema.ts +1 -1
- package/src/pixi/authoring.ts +137 -27
- package/src/runtime/mount-manifest.ts +1 -1
- package/src/world3d-react/index.ts +4 -4
- package/src/world3d-react/r3f-adapter.tsx +2 -2
- package/src/world3d-react/renderer-config.ts +7 -164
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapter/renderer-config.ts — the seam a WORLD uses to state how its own frame is rendered.
|
|
3
|
+
*
|
|
4
|
+
* It lives in the ADAPTER SEAM, not in a surface entry, because `WorldRendererConfig` IS part of
|
|
5
|
+
* the root contract: `MountedThreeRoot.rendererConfig` reports it (`./root-adapter.ts`) and
|
|
6
|
+
* `./root-seam-contract.ts` already lists it as a contract field. The seam defines the shape; the
|
|
7
|
+
* surface entries and the editor CONFORM to it — `world3d-react/r3f-adapter.tsx` is the declarer,
|
|
8
|
+
* `runtime/create-runtime.ts` the host, `editor/src/components/ViewportPanel.tsx` the editor's
|
|
9
|
+
* applier. Do NOT move it back under `world3d-react/`: nothing here touches react, and a core
|
|
10
|
+
* adapter file reaching into a react entry is exactly what `react-core-import-ban.test.ts` forbids
|
|
11
|
+
* (that ban carries no type-only carve-out on purpose — unlike its pixi sibling, whose carve-out
|
|
12
|
+
* exists only because `PIXI.Container` is a third-party type core cannot relocate).
|
|
13
|
+
*
|
|
14
|
+
* The host owns the `WebGLRenderer` (`ThreeHostContext.renderer`) and configures it with this
|
|
15
|
+
* engine's defaults: ACES tone mapping, sRGB output, PCF-soft shadows. Those defaults are right for
|
|
16
|
+
* a world authored against them and WRONG for a world that was authored against a different
|
|
17
|
+
* engine's pipeline — an imported Godot 3 GLES2 game does gamma-space lighting with no tonemapper
|
|
18
|
+
* at all, so ACES quietly desaturates and darkens every colour its author picked.
|
|
19
|
+
*
|
|
20
|
+
* So a world may DECLARE the pipeline it was authored for, and `createR3FAdapter` applies it to the
|
|
21
|
+
* host's renderer for the life of the mount, restoring what it found on dispose. Three properties
|
|
22
|
+
* of that shape are load-bearing:
|
|
23
|
+
*
|
|
24
|
+
* - **It is per-renderer, never engine-wide.** Every field here is a `WebGLRenderer` instance
|
|
25
|
+
* property, and a play root gets its own renderer (`create-runtime.ts`). Nothing here reaches a
|
|
26
|
+
* module-level three global, so one world's declaration cannot change how the editor's own
|
|
27
|
+
* viewport, another root, or a thumbnail bake renders.
|
|
28
|
+
* - **Absent means "leave the host's value alone".** Every field is optional and an omitted one
|
|
29
|
+
* is never written, so declaring a tone mapping does not silently reset the clear colour.
|
|
30
|
+
* - **It is restored on dispose.** The renderer outlives the mount, so a world that did not put
|
|
31
|
+
* back what it found would leak its pipeline into whatever mounts next.
|
|
32
|
+
*
|
|
33
|
+
* This is deliberately NOT a general render-settings system. It carries what a world can honestly
|
|
34
|
+
* state about its own colour pipeline and nothing else; a property the host fixes at CONSTRUCTION
|
|
35
|
+
* (the WebGL context's `antialias` attribute, and therefore the MSAA sample count) cannot be
|
|
36
|
+
* declared here, because there would be no honest moment to apply it.
|
|
37
|
+
*/
|
|
38
|
+
import type * as THREE from 'three';
|
|
39
|
+
/** The tone-mapping operators three exposes, named as data rather than as three's numeric enum. */
|
|
40
|
+
export type WorldToneMapping = 'none' | 'linear' | 'reinhard' | 'cineon' | 'aces' | 'agx' | 'neutral';
|
|
41
|
+
/**
|
|
42
|
+
* The output transfer function the frame is written with.
|
|
43
|
+
*
|
|
44
|
+
* - `srgb` — three's own default and this engine's: linear lighting, sRGB encode on output.
|
|
45
|
+
* - `srgb-linear` — NO output transform. This is what a gamma-space renderer needs: the shading
|
|
46
|
+
* result is already in display space and encoding it a second time washes the frame out.
|
|
47
|
+
*/
|
|
48
|
+
export type WorldOutputColorSpace = 'srgb' | 'srgb-linear';
|
|
49
|
+
/**
|
|
50
|
+
* The shadow-map filter, named as data rather than as three's numeric enum.
|
|
51
|
+
*
|
|
52
|
+
* This is a renderer INSTANCE property (`WebGLRenderer.shadowMap.type`), not a context attribute,
|
|
53
|
+
* so unlike MSAA it has an honest moment at which a world can ask for it — which is the whole test
|
|
54
|
+
* this file's header states. A source engine that declares its own shadow filter (Godot 3's
|
|
55
|
+
* `rendering/quality/shadows/filter_mode`) would otherwise inherit whatever the host built with.
|
|
56
|
+
*/
|
|
57
|
+
export type WorldShadowMapType = 'basic' | 'pcf' | 'pcf-soft' | 'vsm';
|
|
58
|
+
/** What a world may declare about the renderer that draws it. Every field is optional; see header. */
|
|
59
|
+
export interface WorldRendererConfig {
|
|
60
|
+
readonly toneMapping?: WorldToneMapping | undefined;
|
|
61
|
+
readonly toneMappingExposure?: number | undefined;
|
|
62
|
+
readonly outputColorSpace?: WorldOutputColorSpace | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* `WebGLRenderer.shadowMap.type`. Writing it after a shadow map has already been built needs
|
|
65
|
+
* `shadowMap.needsUpdate`, which this function sets — three caches the compiled depth material
|
|
66
|
+
* per type and would otherwise keep filtering with the previous one.
|
|
67
|
+
*/
|
|
68
|
+
readonly shadowMapType?: WorldShadowMapType | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* The colour the frame is cleared to, as a CSS hex string. The renderer's existing clear ALPHA
|
|
71
|
+
* is preserved: a stacked canvas is transparent on purpose (`create-runtime.ts` gives every
|
|
72
|
+
* non-bottom root `alpha: true`), and forcing it opaque here would hide every layer below.
|
|
73
|
+
*/
|
|
74
|
+
readonly clearColor?: string | undefined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Apply `config` to `renderer`, returning the restore function that puts back what was there.
|
|
78
|
+
*
|
|
79
|
+
* `three` is passed in rather than imported for values so the enum constants come from the HOST's
|
|
80
|
+
* three instance — the same identity rule `r3f-adapter.tsx` follows for the scene and camera.
|
|
81
|
+
*/
|
|
82
|
+
export declare function applyWorldRendererConfig(three: typeof THREE, renderer: THREE.WebGLRenderer, config: WorldRendererConfig): () => void;
|
|
83
|
+
//# sourceMappingURL=renderer-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer-config.d.ts","sourceRoot":"","sources":["../../src/adapter/renderer-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,mGAAmG;AACnG,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,MAAM,GACN,KAAK,GACL,SAAS,CAAC;AAEd;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,aAAa,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,KAAK,CAAC;AAEtE,sGAAsG;AACtG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC9D;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACxD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,KAAK,EACnB,QAAQ,EAAE,KAAK,CAAC,aAAa,EAC7B,MAAM,EAAE,mBAAmB,GAC1B,MAAM,IAAI,CA+EZ"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapter/renderer-config.ts — the seam a WORLD uses to state how its own frame is rendered.
|
|
3
|
+
*
|
|
4
|
+
* It lives in the ADAPTER SEAM, not in a surface entry, because `WorldRendererConfig` IS part of
|
|
5
|
+
* the root contract: `MountedThreeRoot.rendererConfig` reports it (`./root-adapter.ts`) and
|
|
6
|
+
* `./root-seam-contract.ts` already lists it as a contract field. The seam defines the shape; the
|
|
7
|
+
* surface entries and the editor CONFORM to it — `world3d-react/r3f-adapter.tsx` is the declarer,
|
|
8
|
+
* `runtime/create-runtime.ts` the host, `editor/src/components/ViewportPanel.tsx` the editor's
|
|
9
|
+
* applier. Do NOT move it back under `world3d-react/`: nothing here touches react, and a core
|
|
10
|
+
* adapter file reaching into a react entry is exactly what `react-core-import-ban.test.ts` forbids
|
|
11
|
+
* (that ban carries no type-only carve-out on purpose — unlike its pixi sibling, whose carve-out
|
|
12
|
+
* exists only because `PIXI.Container` is a third-party type core cannot relocate).
|
|
13
|
+
*
|
|
14
|
+
* The host owns the `WebGLRenderer` (`ThreeHostContext.renderer`) and configures it with this
|
|
15
|
+
* engine's defaults: ACES tone mapping, sRGB output, PCF-soft shadows. Those defaults are right for
|
|
16
|
+
* a world authored against them and WRONG for a world that was authored against a different
|
|
17
|
+
* engine's pipeline — an imported Godot 3 GLES2 game does gamma-space lighting with no tonemapper
|
|
18
|
+
* at all, so ACES quietly desaturates and darkens every colour its author picked.
|
|
19
|
+
*
|
|
20
|
+
* So a world may DECLARE the pipeline it was authored for, and `createR3FAdapter` applies it to the
|
|
21
|
+
* host's renderer for the life of the mount, restoring what it found on dispose. Three properties
|
|
22
|
+
* of that shape are load-bearing:
|
|
23
|
+
*
|
|
24
|
+
* - **It is per-renderer, never engine-wide.** Every field here is a `WebGLRenderer` instance
|
|
25
|
+
* property, and a play root gets its own renderer (`create-runtime.ts`). Nothing here reaches a
|
|
26
|
+
* module-level three global, so one world's declaration cannot change how the editor's own
|
|
27
|
+
* viewport, another root, or a thumbnail bake renders.
|
|
28
|
+
* - **Absent means "leave the host's value alone".** Every field is optional and an omitted one
|
|
29
|
+
* is never written, so declaring a tone mapping does not silently reset the clear colour.
|
|
30
|
+
* - **It is restored on dispose.** The renderer outlives the mount, so a world that did not put
|
|
31
|
+
* back what it found would leak its pipeline into whatever mounts next.
|
|
32
|
+
*
|
|
33
|
+
* This is deliberately NOT a general render-settings system. It carries what a world can honestly
|
|
34
|
+
* state about its own colour pipeline and nothing else; a property the host fixes at CONSTRUCTION
|
|
35
|
+
* (the WebGL context's `antialias` attribute, and therefore the MSAA sample count) cannot be
|
|
36
|
+
* declared here, because there would be no honest moment to apply it.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Apply `config` to `renderer`, returning the restore function that puts back what was there.
|
|
40
|
+
*
|
|
41
|
+
* `three` is passed in rather than imported for values so the enum constants come from the HOST's
|
|
42
|
+
* three instance — the same identity rule `r3f-adapter.tsx` follows for the scene and camera.
|
|
43
|
+
*/
|
|
44
|
+
export function applyWorldRendererConfig(three, renderer, config) {
|
|
45
|
+
// A host that mounts a world WITHOUT rasterizing it hands the adapter a duck-typed renderer —
|
|
46
|
+
// the editor's design session (`createDesignTimeRenderer`: four members, deliberately never
|
|
47
|
+
// widened) and jsdom test harnesses both do. Such a surface has no colour pipeline to configure:
|
|
48
|
+
// the frame the user sees is drawn by a DIFFERENT renderer (the editor's own), so applying the
|
|
49
|
+
// world's config there is meaningless — and calling `getClearColor` on it is a TypeError that
|
|
50
|
+
// unmounts the whole world at edit time (measured: every Godot port's edit viewport blanked with
|
|
51
|
+
// '"world" failed to mount — renderer.getClearColor is not a function'). Detect the real
|
|
52
|
+
// `WebGLRenderer` surface by the one method this function must call, and no-op otherwise.
|
|
53
|
+
if (typeof renderer.getClearColor !== 'function') {
|
|
54
|
+
return () => { };
|
|
55
|
+
}
|
|
56
|
+
const toneMappings = {
|
|
57
|
+
none: three.NoToneMapping,
|
|
58
|
+
linear: three.LinearToneMapping,
|
|
59
|
+
reinhard: three.ReinhardToneMapping,
|
|
60
|
+
cineon: three.CineonToneMapping,
|
|
61
|
+
aces: three.ACESFilmicToneMapping,
|
|
62
|
+
agx: three.AgXToneMapping,
|
|
63
|
+
neutral: three.NeutralToneMapping,
|
|
64
|
+
};
|
|
65
|
+
const colorSpaces = {
|
|
66
|
+
srgb: three.SRGBColorSpace,
|
|
67
|
+
'srgb-linear': three.LinearSRGBColorSpace,
|
|
68
|
+
};
|
|
69
|
+
const shadowMapTypes = {
|
|
70
|
+
basic: three.BasicShadowMap,
|
|
71
|
+
pcf: three.PCFShadowMap,
|
|
72
|
+
'pcf-soft': three.PCFSoftShadowMap,
|
|
73
|
+
vsm: three.VSMShadowMap,
|
|
74
|
+
};
|
|
75
|
+
const restores = [];
|
|
76
|
+
if (config.toneMapping !== undefined) {
|
|
77
|
+
const previous = renderer.toneMapping;
|
|
78
|
+
renderer.toneMapping = toneMappings[config.toneMapping];
|
|
79
|
+
restores.push(() => {
|
|
80
|
+
renderer.toneMapping = previous;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (config.toneMappingExposure !== undefined) {
|
|
84
|
+
const previous = renderer.toneMappingExposure;
|
|
85
|
+
renderer.toneMappingExposure = config.toneMappingExposure;
|
|
86
|
+
restores.push(() => {
|
|
87
|
+
renderer.toneMappingExposure = previous;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (config.outputColorSpace !== undefined) {
|
|
91
|
+
const previous = renderer.outputColorSpace;
|
|
92
|
+
renderer.outputColorSpace = colorSpaces[config.outputColorSpace];
|
|
93
|
+
restores.push(() => {
|
|
94
|
+
renderer.outputColorSpace = previous;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (config.shadowMapType !== undefined && renderer.shadowMap !== undefined) {
|
|
98
|
+
const previous = renderer.shadowMap.type;
|
|
99
|
+
renderer.shadowMap.type = shadowMapTypes[config.shadowMapType];
|
|
100
|
+
renderer.shadowMap.needsUpdate = true;
|
|
101
|
+
restores.push(() => {
|
|
102
|
+
renderer.shadowMap.type = previous;
|
|
103
|
+
renderer.shadowMap.needsUpdate = true;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (config.clearColor !== undefined) {
|
|
107
|
+
const previousColor = new three.Color();
|
|
108
|
+
renderer.getClearColor(previousColor);
|
|
109
|
+
// Alpha is READ BACK and re-passed, never assumed: see `clearColor`'s doc above.
|
|
110
|
+
const alpha = renderer.getClearAlpha();
|
|
111
|
+
renderer.setClearColor(new three.Color(config.clearColor), alpha);
|
|
112
|
+
restores.push(() => {
|
|
113
|
+
renderer.setClearColor(previousColor, alpha);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return () => {
|
|
117
|
+
// Reverse order, so a field written twice (it cannot be, today) unwinds correctly.
|
|
118
|
+
for (let i = restores.length - 1; i >= 0; i--)
|
|
119
|
+
restores[i]?.();
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Container } from 'pixi.js';
|
|
10
10
|
import type * as THREE from 'three';
|
|
11
|
-
import type { WorldRendererConfig } from '../world3d-react/renderer-config';
|
|
12
11
|
import type { AdapterSurface } from './adapter-surface';
|
|
13
12
|
import type { AuthoringAdapter } from './authoring';
|
|
14
13
|
import type { HostContextFor } from './host-context';
|
|
14
|
+
import type { WorldRendererConfig } from './renderer-config';
|
|
15
15
|
import type { SystemAdapters } from './system-adapter';
|
|
16
16
|
/**
|
|
17
17
|
* The ingested-world observation contract (T7.4 slice 2, "the
|
|
@@ -91,7 +91,7 @@ export interface MountedThreeRoot extends MountedRootBase {
|
|
|
91
91
|
/**
|
|
92
92
|
* The colour pipeline this world was authored for, REPORTED rather than
|
|
93
93
|
* applied — the adapter has already applied it to the renderer its own host
|
|
94
|
-
* handed it (
|
|
94
|
+
* handed it (`./renderer-config.ts`).
|
|
95
95
|
*
|
|
96
96
|
* It is here because the host that MOUNTS a world is not always the host
|
|
97
97
|
* that DRAWS it. The editor's design session mounts against a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/root-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/root-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/manifest/schema.js
CHANGED
|
@@ -483,7 +483,7 @@ export const GameManifestSchema = z
|
|
|
483
483
|
antialias: z
|
|
484
484
|
.boolean()
|
|
485
485
|
.describe('Whether every three root is built with a multisampled drawing buffer. This is the ' +
|
|
486
|
-
'ONE render property a world cannot declare for itself (
|
|
486
|
+
'ONE render property a world cannot declare for itself (adapter/' +
|
|
487
487
|
'renderer-config.ts): a WebGL context fixes its sample count at CREATION from this ' +
|
|
488
488
|
'boolean, long before a world mounts, so it belongs to the PROJECT. The runtime ' +
|
|
489
489
|
'reader is mount-manifest.ts, which threads it into createHostRenderer for each ' +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authoring.d.ts","sourceRoot":"","sources":["../../src/pixi/authoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;GAMG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,gBAAgB,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAClE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACzD,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AACD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"authoring.d.ts","sourceRoot":"","sources":["../../src/pixi/authoring.ts"],"names":[],"mappings":"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"}
|
package/dist/pixi/authoring.js
CHANGED
|
@@ -120,62 +120,141 @@ function isPixiDisplay(value) {
|
|
|
120
120
|
* never reached `_cannon` inside the budget.
|
|
121
121
|
*/
|
|
122
122
|
function viewOwnerLabel(view) {
|
|
123
|
-
const
|
|
123
|
+
const search = { seen: new WeakSet(), budget: OWNER_SEARCH_BUDGET };
|
|
124
124
|
let host = view.parent;
|
|
125
125
|
for (let hops = 0; hops < 8 && host && typeof host === 'object'; hops++) {
|
|
126
|
-
|
|
126
|
+
if (search.budget <= 0)
|
|
127
|
+
return undefined;
|
|
128
|
+
const found = labelFromHolder(host, view, search) ?? findHeldOwner(host, view, 2, search);
|
|
127
129
|
if (found)
|
|
128
130
|
return found;
|
|
129
131
|
host = host.parent;
|
|
130
132
|
}
|
|
131
133
|
return undefined;
|
|
132
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* THE CEILING ON ONE NODE'S OWNER HUNT — what keeps a hierarchy read LINEAR in
|
|
137
|
+
* the size of the tree instead of quadratic in it.
|
|
138
|
+
*
|
|
139
|
+
* The hunt below is a search of the GAME's object graph, and a game's object
|
|
140
|
+
* graph is small: `this._cannon`, `allSystems.get('hud')`, `_decor[3]` are all
|
|
141
|
+
* found within a few dozen examinations. What is not small is anything the
|
|
142
|
+
* search can leak into — Pixi's own live render graph is the measured one, and
|
|
143
|
+
* naming a key blacklist is a fix for the leak you already found, not for the
|
|
144
|
+
* class. So every examination is charged against this budget and the search
|
|
145
|
+
* simply gives up when it runs out: the node then falls through to its texture
|
|
146
|
+
* alias / constructor / kind name, which is the same honest answer it gets
|
|
147
|
+
* today whenever the hunt finds nothing.
|
|
148
|
+
*
|
|
149
|
+
* MEASURED (2026-08-20, `packages/editor/scripts/scale-harness`): entering play
|
|
150
|
+
* on a 20 000-node `@pixi/react` world blocked the main thread for 199.5
|
|
151
|
+
* SECONDS in one synchronous run, 69% of all profiler samples inside this
|
|
152
|
+
* search, because `parentRenderGroup` reaches an array of every node in the
|
|
153
|
+
* tree and the search walked it once per unlabeled node. `vgai play`,
|
|
154
|
+
* `screenshot` and `stop` all timed out against a tab that was heartbeating
|
|
155
|
+
* normally the whole time.
|
|
156
|
+
*/
|
|
157
|
+
const OWNER_SEARCH_BUDGET = 2_000;
|
|
158
|
+
/** Charge one examination. `false` means the hunt is over. */
|
|
159
|
+
function spend(search) {
|
|
160
|
+
if (search.budget <= 0)
|
|
161
|
+
return false;
|
|
162
|
+
search.budget -= 1;
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
133
165
|
function isGameFieldKey(key) {
|
|
134
166
|
// Skip display-list indices (`0`, `1`) and Pixi internals (`children`).
|
|
135
|
-
|
|
167
|
+
// The membership test is on the UNDERSCORED-STRIPPED key because that is how
|
|
168
|
+
// Pixi v8 spells most of them (`_position`, `_bounds`, `_texture`), and a
|
|
169
|
+
// set listing the bare names was silently matching none of those.
|
|
170
|
+
if (!/^_?[A-Za-z][A-Za-zA-Z0-9]*$/.test(key))
|
|
171
|
+
return false;
|
|
172
|
+
return !PIXI_INTERNAL_KEY.has(key.replace(/^_+/, ''));
|
|
136
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Field names that belong to Pixi (or to its EventEmitter base), never to the
|
|
176
|
+
* game. Read off a live `pixi.js` v8 `Container`/`Sprite`'s own keys, so this
|
|
177
|
+
* is a transcription rather than a guess — but it is the OPTIMIZATION, not the
|
|
178
|
+
* correctness boundary: {@link OWNER_SEARCH_BUDGET} is what bounds the search
|
|
179
|
+
* on a Pixi version whose internals this list has never heard of. Spending the
|
|
180
|
+
* budget on plausible game fields instead of on the render graph is what keeps
|
|
181
|
+
* the heuristic's hit rate while it is bounded.
|
|
182
|
+
*
|
|
183
|
+
* `view` is deliberately absent: `.view` is the very field the hunt is looking
|
|
184
|
+
* for.
|
|
185
|
+
*/
|
|
137
186
|
const PIXI_INTERNAL_KEY = new Set([
|
|
138
187
|
'parent',
|
|
139
188
|
'children',
|
|
140
189
|
'transform',
|
|
141
190
|
'position',
|
|
191
|
+
'origin',
|
|
142
192
|
'scale',
|
|
143
193
|
'pivot',
|
|
144
194
|
'skew',
|
|
195
|
+
'anchor',
|
|
145
196
|
'worldTransform',
|
|
146
197
|
'localTransform',
|
|
198
|
+
'groupTransform',
|
|
199
|
+
'relativeGroupTransform',
|
|
200
|
+
// The render graph — the measured leak. `parentRenderGroup` is on every
|
|
201
|
+
// container once a world has rendered and reaches every node in the tree.
|
|
202
|
+
'renderGroup',
|
|
203
|
+
'parentRenderGroup',
|
|
204
|
+
'parentRenderGroupIndex',
|
|
205
|
+
'parentRenderLayer',
|
|
206
|
+
'relativeRenderGroupDepth',
|
|
207
|
+
'renderPipeId',
|
|
208
|
+
'instructionSet',
|
|
209
|
+
'childrenToUpdate',
|
|
210
|
+
'childrenRenderablesToUpdate',
|
|
211
|
+
'effects',
|
|
212
|
+
'filters',
|
|
213
|
+
'mask',
|
|
214
|
+
'bounds',
|
|
215
|
+
'boundsArea',
|
|
216
|
+
'visualBounds',
|
|
217
|
+
'gpuData',
|
|
218
|
+
'texture',
|
|
219
|
+
'events',
|
|
220
|
+
'eventsCount',
|
|
221
|
+
'updateFlags',
|
|
147
222
|
]);
|
|
148
|
-
function labelFromHolder(host, view,
|
|
149
|
-
if (seen.has(host))
|
|
223
|
+
function labelFromHolder(host, view, search) {
|
|
224
|
+
if (search.seen.has(host))
|
|
150
225
|
return undefined;
|
|
151
|
-
seen.add(host);
|
|
152
|
-
const asBag = nameInBag(host, view, undefined,
|
|
226
|
+
search.seen.add(host);
|
|
227
|
+
const asBag = nameInBag(host, view, undefined, search);
|
|
153
228
|
if (asBag)
|
|
154
229
|
return asBag;
|
|
155
230
|
for (const [key, value] of Object.entries(host)) {
|
|
231
|
+
if (!spend(search))
|
|
232
|
+
return undefined;
|
|
156
233
|
if (!isGameFieldKey(key))
|
|
157
234
|
continue;
|
|
158
235
|
const named = nameIfHoldsView(host, key, value, view);
|
|
159
236
|
if (named)
|
|
160
237
|
return named;
|
|
161
|
-
const nested = nameInBag(value, view, key,
|
|
238
|
+
const nested = nameInBag(value, view, key, search);
|
|
162
239
|
if (nested)
|
|
163
240
|
return nested;
|
|
164
241
|
}
|
|
165
242
|
return undefined;
|
|
166
243
|
}
|
|
167
|
-
function nameInBag(value, view, fieldKey,
|
|
244
|
+
function nameInBag(value, view, fieldKey, search) {
|
|
168
245
|
const entries = bagEntries(value);
|
|
169
246
|
if (!entries)
|
|
170
247
|
return undefined;
|
|
171
248
|
for (const [itemKey, item] of entries) {
|
|
172
|
-
|
|
249
|
+
if (!spend(search))
|
|
250
|
+
return undefined;
|
|
251
|
+
const named = nameBagItem(value, item, usableBagKey(itemKey) ?? fieldKey, view, search);
|
|
173
252
|
if (named)
|
|
174
253
|
return named;
|
|
175
254
|
}
|
|
176
255
|
return undefined;
|
|
177
256
|
}
|
|
178
|
-
function nameBagItem(bag, item, key, view,
|
|
257
|
+
function nameBagItem(bag, item, key, view, search) {
|
|
179
258
|
if (key) {
|
|
180
259
|
const named = nameIfHoldsView(item && typeof item === 'object' ? item : bag, key, item, view);
|
|
181
260
|
if (named)
|
|
@@ -187,7 +266,7 @@ function nameBagItem(bag, item, key, view, seen) {
|
|
|
187
266
|
return named;
|
|
188
267
|
}
|
|
189
268
|
if (item && typeof item === 'object' && !isPixiDisplay(item)) {
|
|
190
|
-
return labelFromHolder(item, view,
|
|
269
|
+
return labelFromHolder(item, view, search);
|
|
191
270
|
}
|
|
192
271
|
return undefined;
|
|
193
272
|
}
|
|
@@ -196,6 +275,29 @@ function usableBagKey(key) {
|
|
|
196
275
|
return undefined;
|
|
197
276
|
return isGameFieldKey(key) || key.includes('-') ? (fieldLabel(key) ?? key) : undefined;
|
|
198
277
|
}
|
|
278
|
+
function* indexedBagEntries(value) {
|
|
279
|
+
for (let index = 0; index < value.length; index++)
|
|
280
|
+
yield [String(index), value[index]];
|
|
281
|
+
}
|
|
282
|
+
function* mapBagEntries(value) {
|
|
283
|
+
for (const [key, item] of value.entries())
|
|
284
|
+
yield [String(key), item];
|
|
285
|
+
}
|
|
286
|
+
function* setBagEntries(value) {
|
|
287
|
+
let index = 0;
|
|
288
|
+
for (const item of value) {
|
|
289
|
+
yield [String(index), item];
|
|
290
|
+
index += 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* A bag's entries, LAZILY.
|
|
295
|
+
*
|
|
296
|
+
* Lazy because the caller is budgeted ({@link OWNER_SEARCH_BUDGET}) and must be
|
|
297
|
+
* able to stop: materializing the pairs first made a 20 000-element array cost
|
|
298
|
+
* 20 000 allocations before the first one was even looked at, which is a cost
|
|
299
|
+
* no budget above it can decline.
|
|
300
|
+
*/
|
|
199
301
|
function bagEntries(value) {
|
|
200
302
|
if (!value || typeof value !== 'object')
|
|
201
303
|
return undefined;
|
|
@@ -203,18 +305,13 @@ function bagEntries(value) {
|
|
|
203
305
|
// their overridable `map`: one shipped Pixi game returns its elements rather
|
|
204
306
|
// than `[key, value]` pairs there, which made hierarchy labeling throw while
|
|
205
307
|
// destructuring the result. The authoring seam treats game objects as
|
|
206
|
-
// untrusted observations, so normalize with indexed reads
|
|
207
|
-
if (Array.isArray(value))
|
|
208
|
-
|
|
209
|
-
for (let index = 0; index < value.length; index++) {
|
|
210
|
-
entries.push([String(index), value[index]]);
|
|
211
|
-
}
|
|
212
|
-
return entries;
|
|
213
|
-
}
|
|
308
|
+
// untrusted observations, so normalize with indexed reads of our own.
|
|
309
|
+
if (Array.isArray(value))
|
|
310
|
+
return indexedBagEntries(value);
|
|
214
311
|
if (value instanceof Map)
|
|
215
|
-
return
|
|
312
|
+
return mapBagEntries(value);
|
|
216
313
|
if (value instanceof Set)
|
|
217
|
-
return
|
|
314
|
+
return setBagEntries(value);
|
|
218
315
|
if (Object.getPrototypeOf(value) !== Object.prototype)
|
|
219
316
|
return undefined;
|
|
220
317
|
return Object.entries(value);
|
|
@@ -232,16 +329,27 @@ function nameIfHoldsView(host, key, value, view) {
|
|
|
232
329
|
}
|
|
233
330
|
return undefined;
|
|
234
331
|
}
|
|
235
|
-
function findHeldOwner(held, view, depth,
|
|
236
|
-
const direct = labelFromHolder(held, view,
|
|
332
|
+
function findHeldOwner(held, view, depth, search) {
|
|
333
|
+
const direct = labelFromHolder(held, view, search);
|
|
237
334
|
if (direct)
|
|
238
335
|
return direct;
|
|
239
336
|
if (depth <= 0)
|
|
240
337
|
return undefined;
|
|
241
|
-
|
|
338
|
+
// BY KEY, exactly like `labelFromHolder`'s own scan. Iterating `Object.values`
|
|
339
|
+
// here was the leak that made a hierarchy read quadratic: `parentRenderGroup`
|
|
340
|
+
// sits on EVERY container once a world has rendered, and descending into it
|
|
341
|
+
// reaches `childrenToUpdate[depth].list` — an array of every node in the
|
|
342
|
+
// tree — so the search for one node's owner walked the whole tree, for every
|
|
343
|
+
// unlabeled node. `isGameFieldKey` already knows which keys belong to Pixi
|
|
344
|
+
// rather than to the game; this scan simply has to ask it too.
|
|
345
|
+
for (const [key, value] of Object.entries(held)) {
|
|
346
|
+
if (!spend(search))
|
|
347
|
+
return undefined;
|
|
348
|
+
if (!isGameFieldKey(key))
|
|
349
|
+
continue;
|
|
242
350
|
if (!value || typeof value !== 'object' || isPixiDisplay(value))
|
|
243
351
|
continue;
|
|
244
|
-
const found = findHeldOwner(value, view, depth - 1,
|
|
352
|
+
const found = findHeldOwner(value, view, depth - 1, search);
|
|
245
353
|
if (found)
|
|
246
354
|
return found;
|
|
247
355
|
}
|
|
@@ -234,7 +234,7 @@ export async function mountManifestRoots(opts) {
|
|
|
234
234
|
headless: opts.headless,
|
|
235
235
|
// `rendering.antialias` reaches the WebGL context at CONSTRUCTION and can be honoured
|
|
236
236
|
// nowhere else — see the manifest schema's own `rendering` block and
|
|
237
|
-
// `
|
|
237
|
+
// `adapter/renderer-config.ts`'s header for why it is not a world-level declaration.
|
|
238
238
|
...(manifest.rendering === undefined ? {} : { antialias: manifest.rendering.antialias }),
|
|
239
239
|
seed: resolvedSeed,
|
|
240
240
|
playtest: opts.playtest,
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
* should pull it into its graph; a deep import keeps this barrel's peer
|
|
40
40
|
* contract to `react`/`@react-three/fiber`/`three` alone.
|
|
41
41
|
*/
|
|
42
|
+
export { applyWorldRendererConfig, type WorldOutputColorSpace, type WorldRendererConfig, type WorldToneMapping, } from '../adapter/renderer-config';
|
|
42
43
|
export { EngineBridge, type EngineBridgeValue, useGameContext } from './engine-bridge';
|
|
43
44
|
export { type CreateR3FAdapterOptions, createR3FAdapter } from './r3f-adapter';
|
|
44
45
|
export { r3fRootFactory, resolveR3FEntryAdapter } from './r3f-root-factory';
|
|
45
|
-
export { applyWorldRendererConfig, type WorldOutputColorSpace, type WorldRendererConfig, type WorldToneMapping, } from './renderer-config';
|
|
46
46
|
export { createR3FRootContext, DEFAULT_INPUT_MAP_PATH, type R3FGameContext, type R3FRootContextOptions, type R3FRootRuntime, } from './world-context';
|
|
47
47
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/world3d-react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,EAAE,KAAK,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/world3d-react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EACL,wBAAwB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,GACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,EAAE,KAAK,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC"}
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
* should pull it into its graph; a deep import keeps this barrel's peer
|
|
40
40
|
* contract to `react`/`@react-three/fiber`/`three` alone.
|
|
41
41
|
*/
|
|
42
|
+
export { applyWorldRendererConfig, } from '../adapter/renderer-config';
|
|
42
43
|
export { EngineBridge, useGameContext } from './engine-bridge';
|
|
43
44
|
export { createR3FAdapter } from './r3f-adapter';
|
|
44
45
|
export { r3fRootFactory, resolveR3FEntryAdapter } from './r3f-root-factory';
|
|
45
|
-
export { applyWorldRendererConfig, } from './renderer-config';
|
|
46
46
|
export { createR3FRootContext, DEFAULT_INPUT_MAP_PATH, } from './world-context';
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { type ReactNode } from 'react';
|
|
22
22
|
import type { RootAdapter } from '../adapter';
|
|
23
|
-
import { type WorldRendererConfig } from '
|
|
23
|
+
import { type WorldRendererConfig } from '../adapter/renderer-config';
|
|
24
24
|
/** What {@link createR3FAdapter} needs to build one `RootAdapter`. */
|
|
25
25
|
export interface CreateR3FAdapterOptions {
|
|
26
26
|
/** Stable id (telemetry/registry/conformance) — `RootAdapter.id`. */
|
|
@@ -45,7 +45,7 @@ export interface CreateR3FAdapterOptions {
|
|
|
45
45
|
/** The colour pipeline this world was AUTHORED for, applied to the host's renderer for the life
|
|
46
46
|
* of the mount and restored on dispose. Omit it (every world here does) to keep the host's own
|
|
47
47
|
* defaults; declare it when the world's colours were picked against a different engine's
|
|
48
|
-
* pipeline — see
|
|
48
|
+
* pipeline — see `../adapter/renderer-config.ts`. */
|
|
49
49
|
readonly renderer?: WorldRendererConfig | undefined;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r3f-adapter.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-adapter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,OAAO,EAA2B,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAoB,WAAW,EAAoB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"r3f-adapter.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-adapter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,OAAO,EAA2B,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAoB,WAAW,EAAoB,MAAM,YAAY,CAAC;AAClF,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AA+DhG,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;uBAEmB;IACnB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B;;;;0CAIsC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C;;;0DAGsD;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;CACrD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,WAAW,CA2kB9E"}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { advance, createRoot, extend, flushSync, events as pointerEvents, useFrame, } from '@react-three/fiber';
|
|
22
22
|
import { createElement, Fragment, useEffect, useMemo } from 'react';
|
|
23
|
+
import { applyWorldRendererConfig } from '../adapter/renderer-config';
|
|
23
24
|
import { registerRenderVitals } from '../dev/register-render-vitals';
|
|
24
25
|
import { createRenderDebugAdapter, frameCaptureContextFor, } from '../dev/render-debug-adapter';
|
|
25
26
|
import { collectRenderMemory } from '../dev/render-memory';
|
|
@@ -30,7 +31,6 @@ import { createSoftParticleDepthPass } from '../render/soft-particle-depth';
|
|
|
30
31
|
import { getDebugRegistry } from '../runtime/debug-registry';
|
|
31
32
|
import { devBuildEnabled } from '../runtime/dev-build';
|
|
32
33
|
import { EngineBridge } from './engine-bridge';
|
|
33
|
-
import { applyWorldRendererConfig } from './renderer-config';
|
|
34
34
|
import { createR3FRootContext, DEFAULT_INPUT_MAP_PATH, wireGameInputSeams } from './world-context';
|
|
35
35
|
/**
|
|
36
36
|
* Scene depth for any soft-particle system this world mounted, drawn between
|