@babylonjs/lite 1.11.0 → 1.13.0
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/CHANGELOG.md +35 -0
- package/dist/index.js +444 -368
- package/dist/index.js.map +1 -1
- package/index.d.ts +208 -6
- package/lib/asset-container.js.map +1 -1
- package/lib/audio/index.js +1 -0
- package/lib/audio/index.js.map +1 -1
- package/lib/audio/media-stream-output.js +44 -0
- package/lib/audio/media-stream-output.js.map +1 -0
- package/lib/engine/engine.js +1 -1
- package/lib/frame-graph/render-task.js +1 -1
- package/lib/frame-graph/render-task.js.map +1 -1
- package/lib/frame-graph/shadow-inputs.js +10 -1
- package/lib/frame-graph/shadow-inputs.js.map +1 -1
- package/lib/frame-graph/transmission.js +1 -16
- package/lib/frame-graph/transmission.js.map +1 -1
- package/lib/index.js +6 -3
- package/lib/index.js.map +1 -1
- package/lib/loader-gltf/gltf-json-asset.js +1 -1
- package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
- package/lib/loader-gltf/gltf-multi-buffer.js +2 -10
- package/lib/loader-gltf/gltf-multi-buffer.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder-ext.js +4 -18
- package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder.js +17 -14
- package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
- package/lib/loader-gltf/gltf-sampler-desc.js +3 -16
- package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
- package/lib/material/node/blocks/ambient-occlusion-block.js +1 -4
- package/lib/material/node/blocks/ambient-occlusion-block.js.map +1 -1
- package/lib/material/node/blocks/biplanar-block.js +3 -6
- package/lib/material/node/blocks/biplanar-block.js.map +1 -1
- package/lib/material/node/blocks/image-source.js +2 -5
- package/lib/material/node/blocks/image-source.js.map +1 -1
- package/lib/material/node/blocks/input-block.js +5 -26
- package/lib/material/node/blocks/input-block.js.map +1 -1
- package/lib/material/node/blocks/pbr-mr-helper-full.js +5 -8
- package/lib/material/node/blocks/pbr-mr-helper-full.js.map +1 -1
- package/lib/material/node/blocks/reflection-texture-block.js +1 -4
- package/lib/material/node/blocks/reflection-texture-block.js.map +1 -1
- package/lib/material/node/blocks/texture-block.js +2 -5
- package/lib/material/node/blocks/texture-block.js.map +1 -1
- package/lib/material/node/blocks/triplanar-block.js +3 -6
- package/lib/material/node/blocks/triplanar-block.js.map +1 -1
- package/lib/material/node/node-emitter.js +24 -1
- package/lib/material/node/node-emitter.js.map +1 -1
- package/lib/material/node/node-geometry-renderable.js +2 -2
- package/lib/material/node/node-material.js +1 -22
- package/lib/material/node/node-material.js.map +1 -1
- package/lib/material/node/node-types.js.map +1 -1
- package/lib/material/pbr/fragments/shadow-only-fragment.js +68 -0
- package/lib/material/pbr/fragments/shadow-only-fragment.js.map +1 -0
- package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
- package/lib/material/pbr/pbr-material.js.map +1 -1
- package/lib/material/pbr/pbr-primitive-resolver.js +2 -2
- package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -1
- package/lib/material/pbr/pbr-renderable.js +3 -0
- package/lib/material/pbr/pbr-renderable.js.map +1 -1
- package/lib/material/pbr/pbr-template.js +1 -0
- package/lib/material/pbr/pbr-template.js.map +1 -1
- package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js +2 -8
- package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js.map +1 -1
- package/lib/mesh/get-mesh-geometry.js +24 -0
- package/lib/mesh/get-mesh-geometry.js.map +1 -0
- package/lib/mesh/mesh-dispose.js +2 -0
- package/lib/mesh/mesh-dispose.js.map +1 -1
- package/lib/mesh/mesh.js +2 -8
- package/lib/mesh/mesh.js.map +1 -1
- package/lib/mesh/thin-instance-cull-binding.js +54 -3
- package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
- package/lib/mesh/thin-instance-gpu-culling.js +241 -24
- package/lib/mesh/thin-instance-gpu-culling.js.map +1 -1
- package/lib/mesh/thin-instance.js +112 -1
- package/lib/mesh/thin-instance.js.map +1 -1
- package/lib/picking/gpu-picker.js +4 -2
- package/lib/picking/gpu-picker.js.map +1 -1
- package/lib/picking/picking-pipeline.js +1 -1
- package/lib/picking/picking-pipeline.js.map +1 -1
- package/lib/picking/picking-shader.js +38 -16
- package/lib/picking/picking-shader.js.map +1 -1
- package/lib/scene/scene-core.js +7 -2
- package/lib/scene/scene-core.js.map +1 -1
- package/lib/scene/scene-node.js +2 -6
- package/lib/scene/scene-node.js.map +1 -1
- package/lib/scene/scene-remove.js +70 -1
- package/lib/scene/scene-remove.js.map +1 -1
- package/lib/scene/transform-node.js +3 -0
- package/lib/scene/transform-node.js.map +1 -1
- package/lib/scene/world-matrix-state.js +7 -1
- package/lib/scene/world-matrix-state.js.map +1 -1
- package/lib/shadow/csm-directional-shadow-generator.js +22 -1
- package/lib/shadow/csm-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/csm-shadow-task-hooks.js +19 -11
- package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
- package/lib/shadow/esm-directional-shadow-generator.js +2 -51
- package/lib/shadow/esm-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/pcf-directional-shadow-generator.js +2 -48
- package/lib/shadow/pcf-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/shadow-base.js +48 -1
- package/lib/shadow/shadow-base.js.map +1 -1
- package/lib/text/text-renderable.js +2 -10
- package/lib/text/text-renderable.js.map +1 -1
- package/lib/texture/texture-array.js +96 -0
- package/lib/texture/texture-array.js.map +1 -0
- package/package.json +3 -3
|
@@ -3,6 +3,7 @@ const PICK_DISCARD_INPUT = (
|
|
|
3
3
|
`
|
|
4
4
|
struct PickDiscardInput {
|
|
5
5
|
worldPos: vec3f,
|
|
6
|
+
fragmentCoord: vec2f,
|
|
6
7
|
pickId: u32,
|
|
7
8
|
thinInstanceIndex: u32,
|
|
8
9
|
hasThinInstance: u32,
|
|
@@ -21,18 +22,39 @@ return false;
|
|
|
21
22
|
function pickDiscardSource(opts) {
|
|
22
23
|
return opts?.discardWgsl ?? DEFAULT_PICK_DISCARD;
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
+
const DEFAULT_PICK_WORLD_ADJUST = (
|
|
26
|
+
/* wgsl */
|
|
27
|
+
`
|
|
28
|
+
fn adjustPickWorld(worldPos: vec3f, instanceExtras: vec4f, thinInstanceIndex: u32) -> vec3f {
|
|
29
|
+
return worldPos;
|
|
30
|
+
}
|
|
31
|
+
`
|
|
32
|
+
);
|
|
33
|
+
function pickWorldAdjustSource(opts) {
|
|
34
|
+
return opts?.worldAdjustWgsl ?? DEFAULT_PICK_WORLD_ADJUST;
|
|
35
|
+
}
|
|
36
|
+
function pickStorageDecls(opts) {
|
|
25
37
|
const storage = opts?.storage;
|
|
26
38
|
if (!storage || storage.length === 0) {
|
|
27
39
|
return "";
|
|
28
40
|
}
|
|
29
41
|
return storage.map((s, binding) => `@group(2) @binding(${binding}) var<storage, read> ${s.name}: ${s.type};`).join("\n");
|
|
30
42
|
}
|
|
43
|
+
const PICK_SCENE = (
|
|
44
|
+
/* wgsl */
|
|
45
|
+
`
|
|
46
|
+
struct SceneUniforms {
|
|
47
|
+
viewProjection: mat4x4f,
|
|
48
|
+
fragmentCoord: vec2f,
|
|
49
|
+
};
|
|
50
|
+
@group(0) @binding(0) var<uniform> scene: SceneUniforms;
|
|
51
|
+
`
|
|
52
|
+
);
|
|
31
53
|
const PICK_FS = (
|
|
32
54
|
/* wgsl */
|
|
33
55
|
`
|
|
34
56
|
struct VsOut {
|
|
35
|
-
@builtin(position)
|
|
57
|
+
@builtin(position) p: vec4f,
|
|
36
58
|
@location(0) @interpolate(flat) pickId: u32,
|
|
37
59
|
@location(1) worldPos: vec3f,
|
|
38
60
|
@location(2) @interpolate(flat) thinInstanceIndex: u32,
|
|
@@ -41,13 +63,12 @@ struct VsOut {
|
|
|
41
63
|
};
|
|
42
64
|
struct FsOut { @location(0) color: vec4f, @location(1) depth: vec4f };
|
|
43
65
|
@fragment fn fs(input: VsOut) -> FsOut {
|
|
44
|
-
|
|
45
|
-
if (shouldDiscardPick(discardInput)) { discard; }
|
|
66
|
+
if (shouldDiscardPick(PickDiscardInput(input.worldPos, scene.fragmentCoord, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; }
|
|
46
67
|
let id = input.pickId;
|
|
47
68
|
let r = f32((id >> 16u) & 0xFFu) / 255.0;
|
|
48
69
|
let g = f32((id >> 8u) & 0xFFu) / 255.0;
|
|
49
70
|
let b = f32(id & 0xFFu) / 255.0;
|
|
50
|
-
return FsOut(vec4f(r, g, b, 1.0), vec4f(input.
|
|
71
|
+
return FsOut(vec4f(r, g, b, 1.0), vec4f(input.p.z, 0.0, 0.0, 0.0));
|
|
51
72
|
}
|
|
52
73
|
`
|
|
53
74
|
);
|
|
@@ -55,21 +76,21 @@ function pickingShaderSource(opts) {
|
|
|
55
76
|
return (
|
|
56
77
|
/* wgsl */
|
|
57
78
|
`
|
|
58
|
-
|
|
79
|
+
${PICK_SCENE}
|
|
59
80
|
struct MeshUniforms {
|
|
60
81
|
world: mat4x4f,
|
|
61
82
|
pickId: u32,
|
|
62
83
|
};
|
|
63
|
-
@group(0) @binding(0) var<uniform> scene: SceneUniforms;
|
|
64
84
|
@group(1) @binding(0) var<uniform> mesh: MeshUniforms;
|
|
65
85
|
${PICK_DISCARD_INPUT}
|
|
66
|
-
${
|
|
86
|
+
${pickStorageDecls(opts)}
|
|
67
87
|
${pickDiscardSource(opts)}
|
|
88
|
+
${pickWorldAdjustSource(opts)}
|
|
68
89
|
${PICK_FS}
|
|
69
90
|
@vertex fn vs(@location(0) position: vec3f) -> VsOut {
|
|
70
91
|
var out: VsOut;
|
|
71
|
-
let wp = (mesh.world * vec4f(position, 1.0)).xyz;
|
|
72
|
-
out.
|
|
92
|
+
let wp = adjustPickWorld((mesh.world * vec4f(position, 1.0)).xyz, vec4f(0.0), 0xffffffffu);
|
|
93
|
+
out.p = scene.viewProjection * vec4f(wp, 1.0);
|
|
73
94
|
out.pickId = mesh.pickId;
|
|
74
95
|
out.worldPos = wp;
|
|
75
96
|
out.thinInstanceIndex = 0xffffffffu;
|
|
@@ -84,16 +105,16 @@ function pickingThinInstanceShaderSource(opts) {
|
|
|
84
105
|
return (
|
|
85
106
|
/* wgsl */
|
|
86
107
|
`
|
|
87
|
-
|
|
108
|
+
${PICK_SCENE}
|
|
88
109
|
struct TIMeshUniforms {
|
|
89
110
|
baseMeshPickId: u32,
|
|
90
111
|
};
|
|
91
|
-
@group(0) @binding(0) var<uniform> scene: SceneUniforms;
|
|
92
112
|
@group(1) @binding(0) var<uniform> tiMesh: TIMeshUniforms;
|
|
93
113
|
@group(1) @binding(1) var<storage, read> instances: array<mat4x4f>;
|
|
94
114
|
${PICK_DISCARD_INPUT}
|
|
95
|
-
${
|
|
115
|
+
${pickStorageDecls(opts)}
|
|
96
116
|
${pickDiscardSource(opts)}
|
|
117
|
+
${pickWorldAdjustSource(opts)}
|
|
97
118
|
${PICK_FS}
|
|
98
119
|
@vertex fn vs(@location(0) position: vec3f, @builtin(instance_index) instanceIndex: u32) -> VsOut {
|
|
99
120
|
let m = instances[instanceIndex];
|
|
@@ -107,14 +128,15 @@ vec4f(m[1].xyz, 0.0),
|
|
|
107
128
|
vec4f(m[2].xyz, 0.0),
|
|
108
129
|
vec4f(m[3].xyz, 1.0),
|
|
109
130
|
);
|
|
131
|
+
let extras = vec4f(m[0].w, m[1].w, m[2].w, m[3].w);
|
|
110
132
|
var out: VsOut;
|
|
111
|
-
let wp = (world * vec4f(position, 1.0)).xyz;
|
|
112
|
-
out.
|
|
133
|
+
let wp = adjustPickWorld((world * vec4f(position, 1.0)).xyz, extras, instanceIndex);
|
|
134
|
+
out.p = scene.viewProjection * vec4f(wp, 1.0);
|
|
113
135
|
out.pickId = tiMesh.baseMeshPickId + instanceIndex;
|
|
114
136
|
out.worldPos = wp;
|
|
115
137
|
out.thinInstanceIndex = instanceIndex;
|
|
116
138
|
out.hasThinInstance = 1u;
|
|
117
|
-
out.instanceExtras =
|
|
139
|
+
out.instanceExtras = extras;
|
|
118
140
|
return out;
|
|
119
141
|
}
|
|
120
142
|
`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"picking-shader.js","sources":["../../../src/picking/picking-shader.ts"],"sourcesContent":["/** WGSL shaders for GPU pick-ID rendering.\n * Outputs pick ID as rgba8unorm (location 0) and depth as r32float (location 1). */\n\nexport interface PickingShaderOptions {\n readonly discardWgsl?: string | null;\n readonly storage?: readonly { readonly name: string; readonly type: string }[];\n}\n\nconst PICK_DISCARD_INPUT = /* wgsl */ `\nstruct PickDiscardInput {\nworldPos: vec3f,\npickId: u32,\nthinInstanceIndex: u32,\nhasThinInstance: u32,\ninstanceExtras: vec4f,\n};\n`;\n\nconst DEFAULT_PICK_DISCARD = /* wgsl */ `\nfn shouldDiscardPick(input: PickDiscardInput) -> bool {\nreturn false;\n}\n`;\n\nfunction pickDiscardSource(opts?: PickingShaderOptions): string {\n return opts?.discardWgsl ?? DEFAULT_PICK_DISCARD;\n}\n\nfunction
|
|
1
|
+
{"version":3,"file":"picking-shader.js","sources":["../../../src/picking/picking-shader.ts"],"sourcesContent":["/** WGSL shaders for GPU pick-ID rendering.\n * Outputs pick ID as rgba8unorm (location 0) and depth as r32float (location 1). */\n\nexport interface PickingShaderOptions {\n readonly discardWgsl?: string | null;\n /** Optional WGSL replacing `adjustPickWorld` (default: identity). Internal pick pipelines can mirror\n * world-space vertex displacement using the declared storage, thin-instance index, and spare matrix w lanes. */\n readonly worldAdjustWgsl?: string | null;\n readonly storage?: readonly { readonly name: string; readonly type: string }[];\n}\n\nconst PICK_DISCARD_INPUT = /* wgsl */ `\nstruct PickDiscardInput {\nworldPos: vec3f,\nfragmentCoord: vec2f,\npickId: u32,\nthinInstanceIndex: u32,\nhasThinInstance: u32,\ninstanceExtras: vec4f,\n};\n`;\n\nconst DEFAULT_PICK_DISCARD = /* wgsl */ `\nfn shouldDiscardPick(input: PickDiscardInput) -> bool {\nreturn false;\n}\n`;\n\nfunction pickDiscardSource(opts?: PickingShaderOptions): string {\n return opts?.discardWgsl ?? DEFAULT_PICK_DISCARD;\n}\n\nconst DEFAULT_PICK_WORLD_ADJUST = /* wgsl */ `\nfn adjustPickWorld(worldPos: vec3f, instanceExtras: vec4f, thinInstanceIndex: u32) -> vec3f {\nreturn worldPos;\n}\n`;\n\nfunction pickWorldAdjustSource(opts?: PickingShaderOptions): string {\n return opts?.worldAdjustWgsl ?? DEFAULT_PICK_WORLD_ADJUST;\n}\n\nfunction pickStorageDecls(opts?: PickingShaderOptions): string {\n const storage = opts?.storage;\n if (!storage || storage.length === 0) {\n return \"\";\n }\n return storage.map((s, binding) => `@group(2) @binding(${binding}) var<storage, read> ${s.name}: ${s.type};`).join(\"\\n\");\n}\n\n// ─── Shared structs + fragment shader ───────────────────────────────\n\nconst PICK_SCENE = /* wgsl */ `\nstruct SceneUniforms {\nviewProjection: mat4x4f,\nfragmentCoord: vec2f,\n};\n@group(0) @binding(0) var<uniform> scene: SceneUniforms;\n`;\n\nconst PICK_FS = /* wgsl */ `\nstruct VsOut {\n@builtin(position) p: vec4f,\n@location(0) @interpolate(flat) pickId: u32,\n@location(1) worldPos: vec3f,\n@location(2) @interpolate(flat) thinInstanceIndex: u32,\n@location(3) @interpolate(flat) hasThinInstance: u32,\n@location(4) @interpolate(flat) instanceExtras: vec4f,\n};\nstruct FsOut { @location(0) color: vec4f, @location(1) depth: vec4f };\n@fragment fn fs(input: VsOut) -> FsOut {\nif (shouldDiscardPick(PickDiscardInput(input.worldPos, scene.fragmentCoord, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; }\nlet id = input.pickId;\nlet r = f32((id >> 16u) & 0xFFu) / 255.0;\nlet g = f32((id >> 8u) & 0xFFu) / 255.0;\nlet b = f32(id & 0xFFu) / 255.0;\nreturn FsOut(vec4f(r, g, b, 1.0), vec4f(input.p.z, 0.0, 0.0, 0.0));\n}\n`;\n\n// ─── Regular mesh picking shader ────────────────────────────────────\n\nexport function pickingShaderSource(opts?: PickingShaderOptions): string {\n return /* wgsl */ `\n${PICK_SCENE}\nstruct MeshUniforms {\nworld: mat4x4f,\npickId: u32,\n};\n@group(1) @binding(0) var<uniform> mesh: MeshUniforms;\n${PICK_DISCARD_INPUT}\n${pickStorageDecls(opts)}\n${pickDiscardSource(opts)}\n${pickWorldAdjustSource(opts)}\n${PICK_FS}\n@vertex fn vs(@location(0) position: vec3f) -> VsOut {\nvar out: VsOut;\nlet wp = adjustPickWorld((mesh.world * vec4f(position, 1.0)).xyz, vec4f(0.0), 0xffffffffu);\nout.p = scene.viewProjection * vec4f(wp, 1.0);\nout.pickId = mesh.pickId;\nout.worldPos = wp;\nout.thinInstanceIndex = 0xffffffffu;\nout.hasThinInstance = 0u;\nout.instanceExtras = vec4f(0.0);\nreturn out;\n}\n`;\n}\n\n// ─── Thin-instance picking shader ───────────────────────────────────\n\nexport function pickingThinInstanceShaderSource(opts?: PickingShaderOptions): string {\n return /* wgsl */ `\n${PICK_SCENE}\nstruct TIMeshUniforms {\nbaseMeshPickId: u32,\n};\n@group(1) @binding(0) var<uniform> tiMesh: TIMeshUniforms;\n@group(1) @binding(1) var<storage, read> instances: array<mat4x4f>;\n${PICK_DISCARD_INPUT}\n${pickStorageDecls(opts)}\n${pickDiscardSource(opts)}\n${pickWorldAdjustSource(opts)}\n${PICK_FS}\n@vertex fn vs(@location(0) position: vec3f, @builtin(instance_index) instanceIndex: u32) -> VsOut {\nlet m = instances[instanceIndex];\n// Treat the instance placement as an AFFINE transform: force the basis columns' homogeneous w to 0 and the\n// translation column's w to 1. Thin-instanced ShaderMaterials may pack per-instance data in those spare w\n// lanes (a sanctioned pattern — Lite injects world0..world3 and the app's own vertex shader zeroes them\n// before transforming). Picking only needs the transform, so packed values are exposed separately.\nlet world = mat4x4f(\nvec4f(m[0].xyz, 0.0),\nvec4f(m[1].xyz, 0.0),\nvec4f(m[2].xyz, 0.0),\nvec4f(m[3].xyz, 1.0),\n);\nlet extras = vec4f(m[0].w, m[1].w, m[2].w, m[3].w);\nvar out: VsOut;\nlet wp = adjustPickWorld((world * vec4f(position, 1.0)).xyz, extras, instanceIndex);\nout.p = scene.viewProjection * vec4f(wp, 1.0);\nout.pickId = tiMesh.baseMeshPickId + instanceIndex;\nout.worldPos = wp;\nout.thinInstanceIndex = instanceIndex;\nout.hasThinInstance = 1u;\nout.instanceExtras = extras;\nreturn out;\n}\n`;\n}\n"],"names":[],"mappings":"AAWA,MAAM,kBAAA;AAAA;AAAA,EAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAWtC,MAAM,oBAAA;AAAA;AAAA,EAAkC;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAMxC,SAAS,kBAAkB,IAAA,EAAqC;AAC5D,EAAA,OAAO,MAAM,WAAA,IAAe,oBAAA;AAChC;AAEA,MAAM,yBAAA;AAAA;AAAA,EAAuC;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAM7C,SAAS,sBAAsB,IAAA,EAAqC;AAChE,EAAA,OAAO,MAAM,eAAA,IAAmB,yBAAA;AACpC;AAEA,SAAS,iBAAiB,IAAA,EAAqC;AAC3D,EAAA,MAAM,UAAU,IAAA,EAAM,OAAA;AACtB,EAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG;AAClC,IAAA,OAAO,EAAA;AAAA,EACX;AACA,EAAA,OAAO,QAAQ,GAAA,CAAI,CAAC,CAAA,EAAG,OAAA,KAAY,sBAAsB,OAAO,CAAA,qBAAA,EAAwB,CAAA,CAAE,IAAI,KAAK,CAAA,CAAE,IAAI,CAAA,CAAA,CAAG,CAAA,CAAE,KAAK,IAAI,CAAA;AAC3H;AAIA,MAAM,UAAA;AAAA;AAAA,EAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAQ9B,MAAM,OAAA;AAAA;AAAA,EAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAsBpB,SAAS,oBAAoB,IAAA,EAAqC;AACrE,EAAA;AAAA;AAAA,IAAkB;AAAA,EACpB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMV,kBAAkB;AAAA,EAClB,gBAAA,CAAiB,IAAI,CAAC;AAAA,EACtB,iBAAA,CAAkB,IAAI,CAAC;AAAA,EACvB,qBAAA,CAAsB,IAAI,CAAC;AAAA,EAC3B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaT;AAIO,SAAS,gCAAgC,IAAA,EAAqC;AACjF,EAAA;AAAA;AAAA,IAAkB;AAAA,EACpB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMV,kBAAkB;AAAA,EAClB,gBAAA,CAAiB,IAAI,CAAC;AAAA,EACtB,iBAAA,CAAkB,IAAI,CAAC;AAAA,EACvB,qBAAA,CAAsB,IAAI,CAAC;AAAA,EAC3B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBT;;;;"}
|
package/lib/scene/scene-core.js
CHANGED
|
@@ -115,11 +115,13 @@ function addToScene(scene, entity) {
|
|
|
115
115
|
const engine = ctx.surface.engine;
|
|
116
116
|
const groups = result.animationGroups;
|
|
117
117
|
ctx.animationGroups.push(...groups);
|
|
118
|
-
|
|
118
|
+
const hook = (deltaMs) => {
|
|
119
119
|
for (const g of groups) {
|
|
120
120
|
tickAnimation(g, deltaMs, engine);
|
|
121
121
|
}
|
|
122
|
-
}
|
|
122
|
+
};
|
|
123
|
+
result._beforeRenderHook = hook;
|
|
124
|
+
ctx._beforeRender.push(hook);
|
|
123
125
|
}
|
|
124
126
|
result._sceneSetup?.(ctx);
|
|
125
127
|
return;
|
|
@@ -243,6 +245,9 @@ async function registerSceneWithShadowSupport(scene) {
|
|
|
243
245
|
}
|
|
244
246
|
const byOrder = (a, b) => a.order - b.order;
|
|
245
247
|
async function ensureShadowTask(engine, scene) {
|
|
248
|
+
if (scene._frameGraph._tasks.some((task) => task.name === "shadow")) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
246
251
|
const { createShadowTask } = await import('../frame-graph/shadow-task.js');
|
|
247
252
|
scene._frameGraph._tasks.unshift(createShadowTask(engine, scene));
|
|
248
253
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-core.js","sources":["../../../src/scene/scene-core.ts"],"sourcesContent":["import type { EngineContext, RenderingContext } from \"../engine/engine.js\";\nimport { _vis, isRenderingContextRegistered, registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { LightBase } from \"../light/types.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { registerMeshScene, unregisterMeshScene, enqueueMaterialSwap } from \"./mesh-scene-registry.js\";\nimport { processMaterialSwaps } from \"./scene-material-swap.js\";\nimport type { AnimationGroup } from \"../animation/animation-group.js\";\nimport { tickAnimation } from \"../animation/animation-tick.js\";\nimport type { ShadowGenerator } from \"../shadow/shadow-generator.js\";\nimport type { FogConfig } from \"../material/standard/standard-material.js\";\nimport type { Renderable, PrePassRenderable, SceneUniformUpdater, MeshGroupBuilder } from \"../render/renderable.js\";\nimport type { TransformNode } from \"./transform-node.js\";\nimport type { SceneNode } from \"./scene-node.js\";\nimport type { EnvironmentTextures } from \"../loader-env/load-env.js\";\nimport type { FrameGraph } from \"../frame-graph/frame-graph.js\";\nimport { createFrameGraph, _appendTask } from \"../frame-graph/frame-graph.js\";\nimport { createRenderTask } from \"../frame-graph/render-task.js\";\nimport { createRenderTarget } from \"../engine/render-target.js\";\nimport type { AssetContainer } from \"../asset-container.js\";\nimport type { SceneLightGpuState } from \"../render/lights-ubo.js\";\nimport type { ClusteredLightContainer } from \"../light/clustered.js\";\nimport type { PickSource } from \"../picking/pick-contributor.js\";\nimport type { ToneMapping } from \"../material/pbr/tone-mapping.js\";\n\n/** Image processing configuration. */\nexport interface ImageProcessingConfig {\n exposure: number;\n contrast: number;\n toneMappingEnabled: boolean;\n /**\n * Tone mapping algorithm applied by PBR materials when `toneMappingEnabled` is true.\n * Undefined means the default {@link StandardToneMapping} (exponential). Assign a\n * built-in ({@link StandardToneMapping}, {@link AcesToneMapping}, {@link NeutralToneMapping})\n * or a custom {@link ToneMapping}.\n *\n * This is baked into the PBR shaders at `registerScene()` time. To change it after\n * registration, use `setSceneImageProcessing` so the affected pipelines are rebuilt.\n */\n toneMapping?: ToneMapping;\n}\n\n/** A clipping plane expressed as the coefficients `[a, b, c, d]` of `a·x + b·y + c·z + d`. */\nexport type ClipPlane = readonly [number, number, number, number];\n\n/** Top-level scene context — pure state, no attached methods. */\nexport interface SceneContext extends RenderingContext {\n /** Surface this scene renders into. Set at scene-creation time and immutable\n * afterwards — the default render task is sized and MSAA-matched to this surface,\n * and `registerScene` attaches the scene to it. For the engine's primary surface\n * (the common single-canvas case) this is the engine itself. The owning engine is\n * reachable via `scene.surface.engine`. */\n readonly surface: SurfaceContext;\n clearColor: GPUColorDict;\n camera: Camera | null;\n lights: LightBase[];\n imageProcessing: ImageProcessingConfig;\n\n /** All meshes added to the scene (standard + PBR). */\n meshes: Mesh[];\n\n /** Animation groups loaded from glTF or created manually. */\n animationGroups: AnimationGroup[];\n\n /** Fog configuration. Null = no fog. */\n fog: FogConfig | null;\n\n /** Scene clip plane as (normal.x, normal.y, normal.z, d). Matches Babylon.js Plane `dot(worldPosition, plane) > 0` discard semantics. */\n clipPlane: ClipPlane | null;\n\n /** Shadow generators registered on this scene. */\n shadowGenerators: ShadowGenerator[];\n\n /** Background material primaryColor (linear RGB). Default from Babylon createDefaultEnvironment. */\n environmentPrimaryColor?: [number, number, number];\n\n /** Environment cubemap Y rotation in radians. */\n envRotationY?: number;\n\n /** Fixed delta time in ms for deterministic animation. 0 = use real rAF delta. */\n fixedDeltaMs: number;\n\n /** All renderables in this scene. The active frame-graph tasks bucket them\n * (opaque / direct / transparent) at bind time based on `isTransparent`, `_direct`, and `_transmissive`. */\n /** @internal */\n _renderables: Renderable[];\n /** @internal Pre-pass work (shadow maps, compute, etc.). */\n _prePasses: PrePassRenderable[];\n /** Pick sources — one per optional pickable entity (GS mesh, billboard system, …). Registered by\n * the entity module via `registerPickSource` when the entity is added; each is pure data + a\n * dynamic-import thunk the GPU picker resolves (once) on the first pick, so rendering the entity\n * pulls no pick-pipeline bytes. Scene-core stays pick-agnostic apart from this opaque list. */\n /** @internal */\n _pickSources: PickSource[];\n /** @internal Scene uniform updaters (one per shared UBO). */\n _uniformUpdaters: SceneUniformUpdater[];\n /** @internal Opt-in feature writers for the SceneUniforms UBO (fog, clip plane, env SH).\n * Populated lazily via the scene-ubo-extras seam; run by the render task. */\n _sceneUboContributors?: ((data: Float32Array, scene: SceneContext) => void)[];\n /** @internal Per-frame callbacks run before rendering (animation, physics, etc.). */\n _beforeRender: ((deltaMs: number) => void)[];\n /** @internal Deferred builders — registered by loaders/factories, run once at startEngine(). */\n _deferredBuilders: (() => void | Promise<void>)[];\n /** @internal Mesh group registry — maps builder to its mesh list (internal bookkeeping). */\n _groups: Map<MeshGroupBuilder, Mesh[]>;\n\n // ─── Dispose infrastructure ────────────────────────────────\n /** @internal Shared cleanup callbacks (scene UBOs, lights UBOs, etc.). Registered by builders. */\n _disposables: (() => void)[];\n /** @internal Per-mesh cleanup callbacks (mesh UBOs, bind groups). For material swap + dispose. */\n _meshDisposables: Map<Mesh, (() => void)[]>;\n /** @internal Per-mesh cleanup callbacks for AUX (material-OVERRIDE) view packets that an explicit render\n * task registered on a mesh it does not own — e.g. a depth-prepass / SSAO no-colour view of a wall. Kept\n * SEPARATE from `_meshDisposables` because a MAIN-material swap (`processMaterialSwaps`, which rebuilds\n * only the main renderable) must NOT tear these down: they belong to another task, whose cached bundle\n * would then replay a destroyed system UBO (\"used in submit while destroyed\"). Drained only on a real mesh\n * removal (`removeFromScene`) and scene dispose, exactly like `_meshDisposables` minus the swap path. */\n _meshAuxDisposables: Map<Mesh, (() => void)[]>;\n /** @internal Meshes whose material was changed via setter — drained before each render frame. */\n _materialSwapQueue: Mesh[];\n /** @internal Monotonic counter bumped when the renderable list changes (add/remove/rebuild). */\n _renderableVersion: number;\n /** @internal Monotonic counter bumped ONLY when a material's renderables are rebuilt/swapped (material\n * swap drain or `rebuildMaterial`) — NOT on a geometry resize (which bumps `_renderableVersion` alone).\n * Lets consumers that cache material-view-derived GPU state (e.g. the CSM shadow tasks' no-color material\n * views) cheaply re-record on a geometry-only edit and only fully rebuild when a caster's material UBOs\n * were actually destroyed/recreated (which would otherwise leave their cached views dangling). */\n _materialEpoch: number;\n /** True once the initial deferred build (buildScene) has run. Meshes added after\n * this point are materialized via the per-frame swap drain rather than the\n * boot-only deferred-builder path. */\n /** @internal */\n _built: boolean;\n /** Builders whose deferred group build has COMPLETED. A mesh that joins one of these groups after the fact\n * (post-boot, or a glTF prop whose async load resolves DURING buildScene's drain and joins an already-built\n * group) is materialized via the per-frame swap drain: its group builder has already run and won't see it,\n * so without this it would cast shadows (it's in ctx.meshes) but never be drawn in the color pass. */\n /** @internal */\n _builtGroups: Set<MeshGroupBuilder>;\n\n // ─── Stashed internal state (typed to avoid `as any` casts) ────\n /** @internal */\n _envTextures?: EnvironmentTextures;\n /** @internal Scene-owned shared LightsUniforms UBO state (group 0 binding 1). */\n _lightGpuState?: SceneLightGpuState;\n\n /** Frame graph driving this scene's rendering. Created eagerly by\n * `createSceneContext` with a default `RenderTask` that mirrors\n * `_renderables` into the swapchain. User code may add additional tasks\n * (offscreen RTTs, post-FX, UI overlays, etc.). */\n /** @internal */\n _frameGraph: FrameGraph;\n\n /** @internal Optional clustered point-light container. Only populated by the clustered-light extension API. */\n _clusteredLightContainer?: ClusteredLightContainer;\n /** @internal Updates clustered light cells for the camera used by the current render pass. */\n _clusteredLightUpdater?: (camera: Camera | null | undefined, targetWidth: number, targetHeight: number) => void;\n}\n\n/** Options passed to the scene-context factory. */\nexport interface SceneContextOptions {\n defaultRenderTask?: boolean;\n}\n\n/** Create an empty scene context bound to the given `surface`. The default render task\n * is built against the surface's format, MSAA configuration, and swapchain RT — the\n * scene is permanently bound to that surface. Pass `engine` directly (since\n * `EngineContext extends SurfaceContext`) for the common single-canvas case, or pass\n * an auxiliary surface created via `createSurface`. */\nexport function createSceneContext(surface: SurfaceContext, options?: SceneContextOptions): SceneContext {\n const eng = surface.engine;\n\n // Closures below capture `ctx` by-reference via this object.\n const ctxLocal: Omit<SceneContext, \"_frameGraph\"> = {\n surface,\n clearColor: { r: 0.2, g: 0.2, b: 0.3, a: 1.0 },\n camera: null,\n lights: [],\n meshes: [],\n animationGroups: [],\n fog: null,\n clipPlane: null,\n shadowGenerators: [],\n imageProcessing: { exposure: 1.0, contrast: 1.0, toneMappingEnabled: false },\n _renderables: [],\n _prePasses: [],\n _pickSources: [],\n _uniformUpdaters: [],\n fixedDeltaMs: 0,\n _beforeRender: [],\n _deferredBuilders: [],\n _groups: new Map(),\n _disposables: [],\n _meshDisposables: new Map(),\n _meshAuxDisposables: new Map(),\n _materialSwapQueue: [],\n _renderableVersion: 0,\n _materialEpoch: 0,\n _built: false,\n _builtGroups: new Set(),\n _drawCallsPre: 0,\n\n _update(): void {\n // When the engine was created with `useFloatingOrigin: true`, mark\n // the active camera so `getViewMatrix` knows to zero its\n // translation column (the GPU view × world product is then the\n // eye-relative result the LWR offset trick produces). For non-LWR\n // engines `eng.useFloatingOrigin` is false and this is a single\n // boolean check per frame — the inner branch is dead.\n if (eng.useFloatingOrigin && ctx.camera && !ctx.camera._useFloatingOrigin) {\n ctx.camera._useFloatingOrigin = true;\n ctx.camera._viewVer = -1;\n ctx.camera._vpVer = -1;\n }\n const d = ctx.fixedDeltaMs > 0 ? ctx.fixedDeltaMs : eng._currentDelta;\n const encoder = eng._currentEncoder;\n let draws = 0;\n for (const cb of ctx._beforeRender) {\n cb(d);\n }\n if (ctx._materialSwapQueue.length > 0) {\n processMaterialSwaps(ctx);\n }\n for (const pp of ctx._prePasses) {\n draws += pp.execute(encoder, eng);\n }\n for (const u of ctx._uniformUpdaters) {\n u.update(eng);\n }\n ctx._drawCallsPre = draws;\n },\n _record(): number {\n return ctx._frameGraph.execute();\n },\n _resize(): void {\n // Canvas backing-store changed: rebuild the frame graph so canvas-sized\n // render targets get re-allocated at the new pixel size before the next record.\n ctx._frameGraph.build();\n },\n };\n\n const ctx = ctxLocal as SceneContext;\n // Eagerly attach the frame graph + a default swapchain render-pass task. The\n // graph drives all GPU work for this scene; user code can add more tasks\n // (offscreen RTTs, post-FX, UI overlays) before/after.\n const fg = createFrameGraph(eng);\n ctx._frameGraph = fg;\n if (options?.defaultRenderTask !== false) {\n // MSAA: render into an MSAA colour RT (which owns depth) and resolve into the\n // single-sample scRT. No MSAA: render straight into the colour-only\n // scRT with a task-owned single-sample depth buffer it builds/clears/frees.\n // All three reads (format / msaaSamples / scRT) come from the bound `surface`.\n const msaa = surface.msaaSamples > 1;\n const rt = msaa\n ? createRenderTarget({ lbl: \"scene-color\", format: surface.format, dFormat: \"depth24plus-stencil8\", samples: surface.msaaSamples, size: surface })\n : surface.scRT;\n const depth = msaa ? undefined : createRenderTarget({ lbl: \"scene-depth\", dFormat: \"depth24plus-stencil8\", samples: 1, size: surface });\n _appendTask(fg, createRenderTask({ name: \"scene\", rt, rst: msaa ? surface.scRT : undefined, depth, clrColor: ctx.clearColor }, eng, ctx));\n }\n ctx._disposables.push(() => fg.dispose());\n return ctx;\n}\n\n/** Register a callback to run before each rendered frame. */\nexport function onBeforeRender(scene: SceneContext, cb: (deltaMs: number) => void): void {\n (scene as SceneContext)._beforeRender.unshift(cb);\n}\n\n/** Register a callback to run when `disposeScene` is called. Used to tie\n * user-owned GPU resources (e.g. a `SpriteRenderer`) to the scene's lifetime. */\nexport function onSceneDispose(scene: SceneContext, cb: () => void): void {\n (scene as SceneContext)._disposables.push(cb);\n}\n\n/** Get the scene's frame graph. Always non-null — created in `createSceneContext`. */\nexport function getFrameGraph(scene: SceneContext): FrameGraph {\n return (scene as SceneContext)._frameGraph;\n}\n\nexport interface DeferredSceneRenderables {\n renderables: readonly Renderable[];\n dispose?: () => void;\n}\n\n/** @internal Register optional scene-hosted render work without teaching `addToScene` about the feature. */\nexport function addDeferredSceneRenderables(\n scene: SceneContext,\n build: (engine: EngineContext, scene: SceneContext) => DeferredSceneRenderables | Promise<DeferredSceneRenderables>\n): void {\n const ctx = scene as SceneContext;\n ctx._deferredBuilders.push(async () => {\n const built = await build(ctx.surface.engine, ctx);\n ctx._renderables.push(...built.renderables);\n if (built.dispose) {\n ctx._disposables.push(built.dispose);\n }\n });\n}\n\n/**\n * Adds an entity (mesh, light, camera, transform node, shadow generator, or asset container)\n * to the scene, dispatching on its type. Asset containers are unpacked and each contained\n * entity added recursively. Optional scene-hosted systems such as depth-hosted sprites\n * expose their own opt-in add functions so mesh-only scenes do not pay feature-specific\n * routing bytes here.\n * @param scene - The owning scene (pillar 4b: entities never reference the scene themselves).\n * @param entity - The entity (or asset container) to add.\n */\nexport function addToScene(scene: SceneContext, entity: Mesh | LightBase | Camera | ShadowGenerator | TransformNode | AssetContainer): void {\n const ctx = scene as SceneContext;\n // AssetContainer from loadGltf / loadBabylon — process each field present\n if (\"entities\" in entity) {\n const result = entity as AssetContainer;\n for (const e of result.entities) {\n addToScene(scene, e);\n }\n if (result.clearColor) {\n ctx.clearColor = result.clearColor;\n }\n if (result.camera && !ctx.camera) {\n ctx.camera = result.camera;\n }\n if (result.animationGroups?.length) {\n const engine = ctx.surface.engine;\n const groups = result.animationGroups;\n ctx.animationGroups.push(...groups);\n ctx._beforeRender.push((deltaMs: number) => {\n for (const g of groups) {\n tickAnimation(g, deltaMs, engine);\n }\n });\n }\n // Feature-owned scene wiring (e.g. EXT_lights_image_based installs its IBL\n // environment). Runs synchronously so the environment is registered before\n // registerScene() builds the scene UBO / PBR renderables.\n result._sceneSetup?.(ctx);\n return;\n }\n if (\"_gpu\" in entity && \"material\" in entity) {\n const mesh = entity as unknown as Mesh;\n ctx.meshes.push(mesh);\n registerMeshScene(ctx, mesh);\n const build = mesh.material ? (mesh.material as unknown as { _buildGroup?: MeshGroupBuilder })._buildGroup : undefined;\n if (build) {\n let group = ctx._groups.get(build);\n if (!group) {\n group = [];\n ctx._groups.set(build, group);\n ctx._deferredBuilders.push(async () => {\n const result = await build(ctx, group!);\n ctx._renderables.push(...result.renderables);\n if (result.updater) {\n ctx._uniformUpdaters.push(result.updater);\n }\n // This group's meshes now have renderables; a mesh that joins it LATER (post-boot or\n // mid-drain) won't be seen by this builder and must be materialized via a swap instead.\n ctx._builtGroups.add(build);\n });\n }\n group.push(mesh);\n // Materialize this mesh's renderable through the per-frame material-swap drain when the boot-only\n // deferred builder won't cover it: either after the initial build (`_built`), or when joining a group\n // whose builder has ALREADY completed (`_builtGroups`) — e.g. a glTF prop whose async load resolves\n // mid-drain and joins an already-built group. A mesh joining a group whose builder has NOT yet run (a\n // brand-new group, or one still pending in the drain) is built by that builder, so it must NOT enqueue\n // here — that would insert a SECOND renderable for it. buildScene drains the queue at the end.\n if (ctx._built || ctx._builtGroups.has(build)) {\n enqueueMaterialSwap(ctx, mesh);\n }\n }\n } else if (\"lightType\" in entity) {\n ctx.lights.push(entity as LightBase);\n }\n // Recurse into children of meshes, lights, cameras — set parent links\n const kids = (entity as unknown as SceneNode).children;\n if (kids?.length) {\n for (const child of kids) {\n (child as unknown as SceneNode).parent = entity as unknown as SceneNode;\n addToScene(scene, child);\n }\n }\n}\n\n/** Release all GPU resources owned by this scene. */\nexport function disposeScene(scene: SceneContext): void {\n const ctx = scene as SceneContext;\n unregisterRenderingContext(ctx.surface, ctx);\n for (const fn of ctx._disposables) {\n fn();\n }\n for (const fns of ctx._meshDisposables.values()) {\n for (const fn of fns) {\n fn();\n }\n }\n ctx._meshDisposables.clear();\n for (const fns of ctx._meshAuxDisposables.values()) {\n for (const fn of fns) {\n fn();\n }\n }\n ctx._meshAuxDisposables.clear();\n for (const mesh of ctx.meshes) {\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene.\n if (unregisterMeshScene(ctx, mesh)) {\n disposeMeshGpu(mesh);\n }\n }\n ctx.meshes.length = 0;\n ctx._renderables.length = 0;\n ctx._prePasses.length = 0;\n ctx._pickSources.length = 0;\n ctx._uniformUpdaters.length = 0;\n ctx._beforeRender.length = 0;\n ctx._deferredBuilders.length = 0;\n ctx._disposables.length = 0;\n ctx._materialSwapQueue.length = 0;\n ctx.lights.length = 0;\n ctx.animationGroups.length = 0;\n ctx.shadowGenerators.length = 0;\n ctx.camera = null;\n}\n\n/** @internal Run all deferred builders (called by registerScene's boot step before the first frame). */\nexport async function buildScene(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n // Discard material-swap requests enqueued during scene SETUP — a mesh added, then re-materialed before boot\n // via the mesh.material setter (e.g. scene12 assigns each row's material AFTER addToScene). The deferred\n // builders below build every group's meshes fresh with their FINAL material, so those swaps are redundant;\n // processing them would insert a SECOND renderable per mesh (double-draw). Only swaps enqueued DURING the\n // drain below — an async mesh that joins an already-built group (see addToScene/_builtGroups) — must survive.\n ctx._materialSwapQueue.length = 0;\n while (ctx._deferredBuilders.length > 0) {\n const builders = [...ctx._deferredBuilders];\n ctx._deferredBuilders = [];\n await Promise.all(builders.map(async (b) => b()));\n }\n // Build the renderables for any meshes that joined an already-built group mid-drain (queued above) before\n // the first frame, instead of leaving them casting shadows but invisible in the color pass.\n processMaterialSwaps(ctx);\n ctx._renderableVersion++;\n ctx._built = true;\n}\n\n/**\n * Register a scene with the engine. Builds deferred work, sorts renderables by order,\n * and adds the scene to its bound surface's render list in overlay order. The scene is\n * always attached to `scene.surface` (which equals the engine itself in the\n * single-canvas case).\n */\nexport async function registerScene(scene: SceneContext): Promise<void> {\n const ctx = scene;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\n/**\n * Register a scene with the engine and install the scene-owned shadow frame-graph task.\n * Use only for scenes that generate shadow maps. Like {@link registerScene}, the scene\n * is attached to `scene.surface` (and its owning engine is `scene.surface.engine`).\n */\nexport async function registerSceneWithShadowSupport(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await ensureShadowTask(surface.engine, ctx);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\nconst byOrder = (a: Renderable, b: Renderable): number => a.order - b.order;\n\nasync function ensureShadowTask(engine: EngineContext, scene: SceneContext): Promise<void> {\n const { createShadowTask } = await import(\"../frame-graph/shadow-task.js\");\n scene._frameGraph._tasks.unshift(createShadowTask(engine, scene));\n}\n\n/** Remove a previously-registered scene. Idempotent. Does not dispose scene resources.\n * The scene is always removed from `scene.surface`. */\nexport function unregisterScene(scene: SceneContext): void {\n unregisterRenderingContext(scene.surface, scene as SceneContext);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scene-core.js","sources":["../../../src/scene/scene-core.ts"],"sourcesContent":["import type { EngineContext, RenderingContext } from \"../engine/engine.js\";\nimport { _vis, isRenderingContextRegistered, registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { LightBase } from \"../light/types.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { registerMeshScene, unregisterMeshScene, enqueueMaterialSwap } from \"./mesh-scene-registry.js\";\nimport { processMaterialSwaps } from \"./scene-material-swap.js\";\nimport type { AnimationGroup } from \"../animation/animation-group.js\";\nimport { tickAnimation } from \"../animation/animation-tick.js\";\nimport type { ShadowGenerator } from \"../shadow/shadow-generator.js\";\nimport type { FogConfig } from \"../material/standard/standard-material.js\";\nimport type { Renderable, PrePassRenderable, SceneUniformUpdater, MeshGroupBuilder } from \"../render/renderable.js\";\nimport type { TransformNode } from \"./transform-node.js\";\nimport type { SceneNode } from \"./scene-node.js\";\nimport type { EnvironmentTextures } from \"../loader-env/load-env.js\";\nimport type { FrameGraph } from \"../frame-graph/frame-graph.js\";\nimport { createFrameGraph, _appendTask } from \"../frame-graph/frame-graph.js\";\nimport { createRenderTask } from \"../frame-graph/render-task.js\";\nimport { createRenderTarget } from \"../engine/render-target.js\";\nimport type { AssetContainer } from \"../asset-container.js\";\nimport type { SceneLightGpuState } from \"../render/lights-ubo.js\";\nimport type { ClusteredLightContainer } from \"../light/clustered.js\";\nimport type { PickSource } from \"../picking/pick-contributor.js\";\nimport type { ToneMapping } from \"../material/pbr/tone-mapping.js\";\n\n/** Image processing configuration. */\nexport interface ImageProcessingConfig {\n exposure: number;\n contrast: number;\n toneMappingEnabled: boolean;\n /**\n * Tone mapping algorithm applied by PBR materials when `toneMappingEnabled` is true.\n * Undefined means the default {@link StandardToneMapping} (exponential). Assign a\n * built-in ({@link StandardToneMapping}, {@link AcesToneMapping}, {@link NeutralToneMapping})\n * or a custom {@link ToneMapping}.\n *\n * This is baked into the PBR shaders at `registerScene()` time. To change it after\n * registration, use `setSceneImageProcessing` so the affected pipelines are rebuilt.\n */\n toneMapping?: ToneMapping;\n}\n\n/** A clipping plane expressed as the coefficients `[a, b, c, d]` of `a·x + b·y + c·z + d`. */\nexport type ClipPlane = readonly [number, number, number, number];\n\n/** Top-level scene context — pure state, no attached methods. */\nexport interface SceneContext extends RenderingContext {\n /** Surface this scene renders into. Set at scene-creation time and immutable\n * afterwards — the default render task is sized and MSAA-matched to this surface,\n * and `registerScene` attaches the scene to it. For the engine's primary surface\n * (the common single-canvas case) this is the engine itself. The owning engine is\n * reachable via `scene.surface.engine`. */\n readonly surface: SurfaceContext;\n clearColor: GPUColorDict;\n camera: Camera | null;\n lights: LightBase[];\n imageProcessing: ImageProcessingConfig;\n\n /** All meshes added to the scene (standard + PBR). */\n meshes: Mesh[];\n\n /** Animation groups loaded from glTF or created manually. */\n animationGroups: AnimationGroup[];\n\n /** Fog configuration. Null = no fog. */\n fog: FogConfig | null;\n\n /** Scene clip plane as (normal.x, normal.y, normal.z, d). Matches Babylon.js Plane `dot(worldPosition, plane) > 0` discard semantics. */\n clipPlane: ClipPlane | null;\n\n /** Shadow generators registered on this scene. */\n shadowGenerators: ShadowGenerator[];\n\n /** Background material primaryColor (linear RGB). Default from Babylon createDefaultEnvironment. */\n environmentPrimaryColor?: [number, number, number];\n\n /** Environment cubemap Y rotation in radians. */\n envRotationY?: number;\n\n /** Fixed delta time in ms for deterministic animation. 0 = use real rAF delta. */\n fixedDeltaMs: number;\n\n /** All renderables in this scene. The active frame-graph tasks bucket them\n * (opaque / direct / transparent) at bind time based on `isTransparent`, `_direct`, and `_transmissive`. */\n /** @internal */\n _renderables: Renderable[];\n /** @internal Pre-pass work (shadow maps, compute, etc.). */\n _prePasses: PrePassRenderable[];\n /** Pick sources — one per optional pickable entity (GS mesh, billboard system, …). Registered by\n * the entity module via `registerPickSource` when the entity is added; each is pure data + a\n * dynamic-import thunk the GPU picker resolves (once) on the first pick, so rendering the entity\n * pulls no pick-pipeline bytes. Scene-core stays pick-agnostic apart from this opaque list. */\n /** @internal */\n _pickSources: PickSource[];\n /** @internal Scene uniform updaters (one per shared UBO). */\n _uniformUpdaters: SceneUniformUpdater[];\n /** @internal Opt-in feature writers for the SceneUniforms UBO (fog, clip plane, env SH).\n * Populated lazily via the scene-ubo-extras seam; run by the render task. */\n _sceneUboContributors?: ((data: Float32Array, scene: SceneContext) => void)[];\n /** @internal Per-frame callbacks run before rendering (animation, physics, etc.). */\n _beforeRender: ((deltaMs: number) => void)[];\n /** @internal Deferred builders — registered by loaders/factories, run once at startEngine(). */\n _deferredBuilders: (() => void | Promise<void>)[];\n /** @internal Mesh group registry — maps builder to its mesh list (internal bookkeeping). */\n _groups: Map<MeshGroupBuilder, Mesh[]>;\n\n // ─── Dispose infrastructure ────────────────────────────────\n /** @internal Shared cleanup callbacks (scene UBOs, lights UBOs, etc.). Registered by builders. */\n _disposables: (() => void)[];\n /** @internal Per-mesh cleanup callbacks (mesh UBOs, bind groups). For material swap + dispose. */\n _meshDisposables: Map<Mesh, (() => void)[]>;\n /** @internal Per-mesh cleanup callbacks for AUX (material-OVERRIDE) view packets that an explicit render\n * task registered on a mesh it does not own — e.g. a depth-prepass / SSAO no-colour view of a wall. Kept\n * SEPARATE from `_meshDisposables` because a MAIN-material swap (`processMaterialSwaps`, which rebuilds\n * only the main renderable) must NOT tear these down: they belong to another task, whose cached bundle\n * would then replay a destroyed system UBO (\"used in submit while destroyed\"). Drained only on a real mesh\n * removal (`removeFromScene`) and scene dispose, exactly like `_meshDisposables` minus the swap path. */\n _meshAuxDisposables: Map<Mesh, (() => void)[]>;\n /** @internal Meshes whose material was changed via setter — drained before each render frame. */\n _materialSwapQueue: Mesh[];\n /** @internal Monotonic counter bumped when the renderable list changes (add/remove/rebuild). */\n _renderableVersion: number;\n /** @internal Monotonic counter bumped ONLY when a material's renderables are rebuilt/swapped (material\n * swap drain or `rebuildMaterial`) — NOT on a geometry resize (which bumps `_renderableVersion` alone).\n * Lets consumers that cache material-view-derived GPU state (e.g. the CSM shadow tasks' no-color material\n * views) cheaply re-record on a geometry-only edit and only fully rebuild when a caster's material UBOs\n * were actually destroyed/recreated (which would otherwise leave their cached views dangling). */\n _materialEpoch: number;\n /** True once the initial deferred build (buildScene) has run. Meshes added after\n * this point are materialized via the per-frame swap drain rather than the\n * boot-only deferred-builder path. */\n /** @internal */\n _built: boolean;\n /** Builders whose deferred group build has COMPLETED. A mesh that joins one of these groups after the fact\n * (post-boot, or a glTF prop whose async load resolves DURING buildScene's drain and joins an already-built\n * group) is materialized via the per-frame swap drain: its group builder has already run and won't see it,\n * so without this it would cast shadows (it's in ctx.meshes) but never be drawn in the color pass. */\n /** @internal */\n _builtGroups: Set<MeshGroupBuilder>;\n\n // ─── Stashed internal state (typed to avoid `as any` casts) ────\n /** @internal */\n _envTextures?: EnvironmentTextures;\n /** @internal Scene-owned shared LightsUniforms UBO state (group 0 binding 1). */\n _lightGpuState?: SceneLightGpuState;\n\n /** Frame graph driving this scene's rendering. Created eagerly by\n * `createSceneContext` with a default `RenderTask` that mirrors\n * `_renderables` into the swapchain. User code may add additional tasks\n * (offscreen RTTs, post-FX, UI overlays, etc.). */\n /** @internal */\n _frameGraph: FrameGraph;\n\n /** @internal Optional clustered point-light container. Only populated by the clustered-light extension API. */\n _clusteredLightContainer?: ClusteredLightContainer;\n /** @internal Updates clustered light cells for the camera used by the current render pass. */\n _clusteredLightUpdater?: (camera: Camera | null | undefined, targetWidth: number, targetHeight: number) => void;\n}\n\n/** Options passed to the scene-context factory. */\nexport interface SceneContextOptions {\n defaultRenderTask?: boolean;\n}\n\n/** Create an empty scene context bound to the given `surface`. The default render task\n * is built against the surface's format, MSAA configuration, and swapchain RT — the\n * scene is permanently bound to that surface. Pass `engine` directly (since\n * `EngineContext extends SurfaceContext`) for the common single-canvas case, or pass\n * an auxiliary surface created via `createSurface`. */\nexport function createSceneContext(surface: SurfaceContext, options?: SceneContextOptions): SceneContext {\n const eng = surface.engine;\n\n // Closures below capture `ctx` by-reference via this object.\n const ctxLocal: Omit<SceneContext, \"_frameGraph\"> = {\n surface,\n clearColor: { r: 0.2, g: 0.2, b: 0.3, a: 1.0 },\n camera: null,\n lights: [],\n meshes: [],\n animationGroups: [],\n fog: null,\n clipPlane: null,\n shadowGenerators: [],\n imageProcessing: { exposure: 1.0, contrast: 1.0, toneMappingEnabled: false },\n _renderables: [],\n _prePasses: [],\n _pickSources: [],\n _uniformUpdaters: [],\n fixedDeltaMs: 0,\n _beforeRender: [],\n _deferredBuilders: [],\n _groups: new Map(),\n _disposables: [],\n _meshDisposables: new Map(),\n _meshAuxDisposables: new Map(),\n _materialSwapQueue: [],\n _renderableVersion: 0,\n _materialEpoch: 0,\n _built: false,\n _builtGroups: new Set(),\n _drawCallsPre: 0,\n\n _update(): void {\n // When the engine was created with `useFloatingOrigin: true`, mark\n // the active camera so `getViewMatrix` knows to zero its\n // translation column (the GPU view × world product is then the\n // eye-relative result the LWR offset trick produces). For non-LWR\n // engines `eng.useFloatingOrigin` is false and this is a single\n // boolean check per frame — the inner branch is dead.\n if (eng.useFloatingOrigin && ctx.camera && !ctx.camera._useFloatingOrigin) {\n ctx.camera._useFloatingOrigin = true;\n ctx.camera._viewVer = -1;\n ctx.camera._vpVer = -1;\n }\n const d = ctx.fixedDeltaMs > 0 ? ctx.fixedDeltaMs : eng._currentDelta;\n const encoder = eng._currentEncoder;\n let draws = 0;\n for (const cb of ctx._beforeRender) {\n cb(d);\n }\n if (ctx._materialSwapQueue.length > 0) {\n processMaterialSwaps(ctx);\n }\n for (const pp of ctx._prePasses) {\n draws += pp.execute(encoder, eng);\n }\n for (const u of ctx._uniformUpdaters) {\n u.update(eng);\n }\n ctx._drawCallsPre = draws;\n },\n _record(): number {\n return ctx._frameGraph.execute();\n },\n _resize(): void {\n // Canvas backing-store changed: rebuild the frame graph so canvas-sized\n // render targets get re-allocated at the new pixel size before the next record.\n ctx._frameGraph.build();\n },\n };\n\n const ctx = ctxLocal as SceneContext;\n // Eagerly attach the frame graph + a default swapchain render-pass task. The\n // graph drives all GPU work for this scene; user code can add more tasks\n // (offscreen RTTs, post-FX, UI overlays) before/after.\n const fg = createFrameGraph(eng);\n ctx._frameGraph = fg;\n if (options?.defaultRenderTask !== false) {\n // MSAA: render into an MSAA colour RT (which owns depth) and resolve into the\n // single-sample scRT. No MSAA: render straight into the colour-only\n // scRT with a task-owned single-sample depth buffer it builds/clears/frees.\n // All three reads (format / msaaSamples / scRT) come from the bound `surface`.\n const msaa = surface.msaaSamples > 1;\n const rt = msaa\n ? createRenderTarget({ lbl: \"scene-color\", format: surface.format, dFormat: \"depth24plus-stencil8\", samples: surface.msaaSamples, size: surface })\n : surface.scRT;\n const depth = msaa ? undefined : createRenderTarget({ lbl: \"scene-depth\", dFormat: \"depth24plus-stencil8\", samples: 1, size: surface });\n _appendTask(fg, createRenderTask({ name: \"scene\", rt, rst: msaa ? surface.scRT : undefined, depth, clrColor: ctx.clearColor }, eng, ctx));\n }\n ctx._disposables.push(() => fg.dispose());\n return ctx;\n}\n\n/** Register a callback to run before each rendered frame. */\nexport function onBeforeRender(scene: SceneContext, cb: (deltaMs: number) => void): void {\n (scene as SceneContext)._beforeRender.unshift(cb);\n}\n\n/** Register a callback to run when `disposeScene` is called. Used to tie\n * user-owned GPU resources (e.g. a `SpriteRenderer`) to the scene's lifetime. */\nexport function onSceneDispose(scene: SceneContext, cb: () => void): void {\n (scene as SceneContext)._disposables.push(cb);\n}\n\n/** Get the scene's frame graph. Always non-null — created in `createSceneContext`. */\nexport function getFrameGraph(scene: SceneContext): FrameGraph {\n return (scene as SceneContext)._frameGraph;\n}\n\nexport interface DeferredSceneRenderables {\n renderables: readonly Renderable[];\n dispose?: () => void;\n}\n\n/** @internal Register optional scene-hosted render work without teaching `addToScene` about the feature. */\nexport function addDeferredSceneRenderables(\n scene: SceneContext,\n build: (engine: EngineContext, scene: SceneContext) => DeferredSceneRenderables | Promise<DeferredSceneRenderables>\n): void {\n const ctx = scene as SceneContext;\n ctx._deferredBuilders.push(async () => {\n const built = await build(ctx.surface.engine, ctx);\n ctx._renderables.push(...built.renderables);\n if (built.dispose) {\n ctx._disposables.push(built.dispose);\n }\n });\n}\n\n/**\n * Adds an entity (mesh, light, camera, transform node, shadow generator, or asset container)\n * to the scene, dispatching on its type. Asset containers are unpacked and each contained\n * entity added recursively. Optional scene-hosted systems such as depth-hosted sprites\n * expose their own opt-in add functions so mesh-only scenes do not pay feature-specific\n * routing bytes here.\n * @param scene - The owning scene (pillar 4b: entities never reference the scene themselves).\n * @param entity - The entity (or asset container) to add.\n */\nexport function addToScene(scene: SceneContext, entity: Mesh | LightBase | Camera | ShadowGenerator | TransformNode | AssetContainer): void {\n const ctx = scene as SceneContext;\n // AssetContainer from loadGltf / loadBabylon — process each field present\n if (\"entities\" in entity) {\n const result = entity as AssetContainer;\n for (const e of result.entities) {\n addToScene(scene, e);\n }\n if (result.clearColor) {\n ctx.clearColor = result.clearColor;\n }\n if (result.camera && !ctx.camera) {\n ctx.camera = result.camera;\n }\n if (result.animationGroups?.length) {\n const engine = ctx.surface.engine;\n const groups = result.animationGroups;\n ctx.animationGroups.push(...groups);\n const hook = (deltaMs: number): void => {\n for (const g of groups) {\n tickAnimation(g, deltaMs, engine);\n }\n };\n result._beforeRenderHook = hook;\n ctx._beforeRender.push(hook);\n }\n // Feature-owned scene wiring (e.g. EXT_lights_image_based installs its IBL\n // environment). Runs synchronously so the environment is registered before\n // registerScene() builds the scene UBO / PBR renderables.\n result._sceneSetup?.(ctx);\n return;\n }\n if (\"_gpu\" in entity && \"material\" in entity) {\n const mesh = entity as unknown as Mesh;\n ctx.meshes.push(mesh);\n registerMeshScene(ctx, mesh);\n const build = mesh.material ? (mesh.material as unknown as { _buildGroup?: MeshGroupBuilder })._buildGroup : undefined;\n if (build) {\n let group = ctx._groups.get(build);\n if (!group) {\n group = [];\n ctx._groups.set(build, group);\n ctx._deferredBuilders.push(async () => {\n const result = await build(ctx, group!);\n ctx._renderables.push(...result.renderables);\n if (result.updater) {\n ctx._uniformUpdaters.push(result.updater);\n }\n // This group's meshes now have renderables; a mesh that joins it LATER (post-boot or\n // mid-drain) won't be seen by this builder and must be materialized via a swap instead.\n ctx._builtGroups.add(build);\n });\n }\n group.push(mesh);\n // Materialize this mesh's renderable through the per-frame material-swap drain when the boot-only\n // deferred builder won't cover it: either after the initial build (`_built`), or when joining a group\n // whose builder has ALREADY completed (`_builtGroups`) — e.g. a glTF prop whose async load resolves\n // mid-drain and joins an already-built group. A mesh joining a group whose builder has NOT yet run (a\n // brand-new group, or one still pending in the drain) is built by that builder, so it must NOT enqueue\n // here — that would insert a SECOND renderable for it. buildScene drains the queue at the end.\n if (ctx._built || ctx._builtGroups.has(build)) {\n enqueueMaterialSwap(ctx, mesh);\n }\n }\n } else if (\"lightType\" in entity) {\n ctx.lights.push(entity as LightBase);\n }\n // Recurse into children of meshes, lights, cameras — set parent links\n const kids = (entity as unknown as SceneNode).children;\n if (kids?.length) {\n for (const child of kids) {\n (child as unknown as SceneNode).parent = entity as unknown as SceneNode;\n addToScene(scene, child);\n }\n }\n}\n\n/** Release all GPU resources owned by this scene. */\nexport function disposeScene(scene: SceneContext): void {\n const ctx = scene as SceneContext;\n unregisterRenderingContext(ctx.surface, ctx);\n for (const fn of ctx._disposables) {\n fn();\n }\n for (const fns of ctx._meshDisposables.values()) {\n for (const fn of fns) {\n fn();\n }\n }\n ctx._meshDisposables.clear();\n for (const fns of ctx._meshAuxDisposables.values()) {\n for (const fn of fns) {\n fn();\n }\n }\n ctx._meshAuxDisposables.clear();\n for (const mesh of ctx.meshes) {\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene.\n if (unregisterMeshScene(ctx, mesh)) {\n disposeMeshGpu(mesh);\n }\n }\n ctx.meshes.length = 0;\n ctx._renderables.length = 0;\n ctx._prePasses.length = 0;\n ctx._pickSources.length = 0;\n ctx._uniformUpdaters.length = 0;\n ctx._beforeRender.length = 0;\n ctx._deferredBuilders.length = 0;\n ctx._disposables.length = 0;\n ctx._materialSwapQueue.length = 0;\n ctx.lights.length = 0;\n ctx.animationGroups.length = 0;\n ctx.shadowGenerators.length = 0;\n ctx.camera = null;\n}\n\n/** @internal Run all deferred builders (called by registerScene's boot step before the first frame). */\nexport async function buildScene(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n // Discard material-swap requests enqueued during scene SETUP — a mesh added, then re-materialed before boot\n // via the mesh.material setter (e.g. scene12 assigns each row's material AFTER addToScene). The deferred\n // builders below build every group's meshes fresh with their FINAL material, so those swaps are redundant;\n // processing them would insert a SECOND renderable per mesh (double-draw). Only swaps enqueued DURING the\n // drain below — an async mesh that joins an already-built group (see addToScene/_builtGroups) — must survive.\n ctx._materialSwapQueue.length = 0;\n while (ctx._deferredBuilders.length > 0) {\n const builders = [...ctx._deferredBuilders];\n ctx._deferredBuilders = [];\n await Promise.all(builders.map(async (b) => b()));\n }\n // Build the renderables for any meshes that joined an already-built group mid-drain (queued above) before\n // the first frame, instead of leaving them casting shadows but invisible in the color pass.\n processMaterialSwaps(ctx);\n ctx._renderableVersion++;\n ctx._built = true;\n}\n\n/**\n * Register a scene with the engine. Builds deferred work, sorts renderables by order,\n * and adds the scene to its bound surface's render list in overlay order. The scene is\n * always attached to `scene.surface` (which equals the engine itself in the\n * single-canvas case).\n */\nexport async function registerScene(scene: SceneContext): Promise<void> {\n const ctx = scene;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\n/**\n * Register a scene with the engine and install the scene-owned shadow frame-graph task.\n * Use only for scenes that generate shadow maps. Like {@link registerScene}, the scene\n * is attached to `scene.surface` (and its owning engine is `scene.surface.engine`).\n */\nexport async function registerSceneWithShadowSupport(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await ensureShadowTask(surface.engine, ctx);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\nconst byOrder = (a: Renderable, b: Renderable): number => a.order - b.order;\n\nasync function ensureShadowTask(engine: EngineContext, scene: SceneContext): Promise<void> {\n // Idempotent: the scene keeps its `_frameGraph` (and its `_tasks`) across unregister/re-register\n // cycles, and `buildScene` does not clear the task list — so a plain unshift would stack a new\n // shadow task on every re-registration. Only add one when the scene has none.\n if (scene._frameGraph._tasks.some((task) => task.name === \"shadow\")) {\n return;\n }\n const { createShadowTask } = await import(\"../frame-graph/shadow-task.js\");\n scene._frameGraph._tasks.unshift(createShadowTask(engine, scene));\n}\n\n/** Remove a previously-registered scene. Idempotent. Does not dispose scene resources.\n * The scene is always removed from `scene.surface`. */\nexport function unregisterScene(scene: SceneContext): void {\n unregisterRenderingContext(scene.surface, scene as SceneContext);\n}\n"],"names":[],"mappings":"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"}
|
package/lib/scene/scene-node.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { mat4Compose } from '../math/mat4-compose.js';
|
|
2
|
-
import { mat4Identity } from '../math/mat4-identity.js';
|
|
3
1
|
import { ObservableVec3 } from '../math/observable-vec3.js';
|
|
4
2
|
import { ObservableQuat } from '../math/observable-quat.js';
|
|
5
|
-
import { createWorldMatrixState, attachWorldMatrixState } from './world-matrix-state.js';
|
|
3
|
+
import { createWorldMatrixState, attachWorldMatrixState, composeTrsLocalMatrix } from './world-matrix-state.js';
|
|
6
4
|
|
|
7
5
|
function eulerToQuat(rx, ry, rz) {
|
|
8
6
|
const cx = Math.cos(rx * 0.5), sx_ = Math.sin(rx * 0.5);
|
|
@@ -78,9 +76,7 @@ function createSceneNodeCore(name, matrix, px = 0, py = 0, pz = 0, qx = 0, qy =
|
|
|
78
76
|
if (matrix) {
|
|
79
77
|
return matrix;
|
|
80
78
|
}
|
|
81
|
-
|
|
82
|
-
const isIdentity = p.x === 0 && p.y === 0 && p.z === 0 && rq2.x === 0 && rq2.y === 0 && rq2.z === 0 && rq2.w === 1 && s.x === 1 && s.y === 1 && s.z === 1;
|
|
83
|
-
return isIdentity ? mat4Identity() : mat4Compose(p.x, p.y, p.z, rq2.x, rq2.y, rq2.z, rq2.w, s.x, s.y, s.z);
|
|
79
|
+
return composeTrsLocalMatrix(node.position, node.rotationQuaternion, node.scaling);
|
|
84
80
|
});
|
|
85
81
|
const onWmDirty = () => {
|
|
86
82
|
if (!matrix) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-node.js","sources":["../../../src/scene/scene-node.ts"],"sourcesContent":["/** SceneNode — common base for all scene entities with TRS, parent, and children.\n *\n * Provides position, rotationQuaternion (source of truth), rotation (Euler XYZ proxy),\n * scaling, parent, worldMatrix, worldMatrixVersion, and children. */\n\nimport type { Mat4 } from \"../math/types.js\";\nimport type { LiteMetadata } from \"../metadata.js\";\nimport type { IWorldMatrixProvider } from \"./parentable.js\";\nimport { mat4Compose } from \"../math/mat4-compose.js\";\nimport { mat4Identity } from \"../math/mat4-identity.js\";\nimport { ObservableVec3 } from \"../math/observable-vec3.js\";\nimport { ObservableQuat } from \"../math/observable-quat.js\";\nimport { createWorldMatrixState, attachWorldMatrixState } from \"./world-matrix-state.js\";\n\n// ─── EulerProxy ──────────────────────────────────────────────────────\n\n/** Bidirectional Euler XYZ view over a quaternion.\n * Reads decompose the current quaternion on the fly; writes convert Euler→quat atomically. */\nexport interface EulerProxy {\n x: number;\n y: number;\n z: number;\n set(x: number, y: number, z: number): void;\n}\n\n// ─── SceneNode ───────────────────────────────────────────────────────\n\n/** Common base for all scene entities: TRS transform, parent/children hierarchy, and a cached world matrix. */\nexport interface SceneNode {\n name: string;\n children: SceneNode[];\n position: ObservableVec3;\n /** Quaternion rotation — source of truth for the local matrix. */\n rotationQuaternion: ObservableQuat;\n /** Euler XYZ bidirectional proxy — reads decompose current quat; writes update quat atomically. */\n rotation: EulerProxy;\n scaling: ObservableVec3;\n parent: IWorldMatrixProvider | null;\n readonly worldMatrix: Mat4;\n readonly worldMatrixVersion: number;\n /** @internal Raw local matrix for glTF matrix nodes. */\n _localMatrix?: Mat4;\n /** Self-visibility. Undefined/true = visible; `false` skips render + camera AABB.\n * Cascade is materialized at write-time by `setSubtreeVisible`. */\n visible?: boolean;\n /** User metadata. glTF loads populate `metadata.gltf.extras` when source extras exist. */\n metadata?: LiteMetadata;\n}\n\n// ─── Math helpers ─────────────────────────────────────────────────────\n\n/** Euler XYZ → quaternion (intrinsic XYZ order). */\nexport function eulerToQuat(rx: number, ry: number, rz: number): [number, number, number, number] {\n const cx = Math.cos(rx * 0.5),\n sx_ = Math.sin(rx * 0.5);\n const cy = Math.cos(ry * 0.5),\n sy_ = Math.sin(ry * 0.5);\n const cz = Math.cos(rz * 0.5),\n sz_ = Math.sin(rz * 0.5);\n return [sx_ * cy * cz + cx * sy_ * sz_, cx * sy_ * cz - sx_ * cy * sz_, cx * cy * sz_ + sx_ * sy_ * cz, cx * cy * cz - sx_ * sy_ * sz_];\n}\n\n/** Quaternion → Euler XYZ (inverse of eulerToQuat). */\nexport function quatToEulerXYZ(qx: number, qy: number, qz: number, qw: number): [number, number, number] {\n const sinY = 2 * (qx * qz + qw * qy);\n const ry = Math.asin(Math.max(-1, Math.min(1, sinY)));\n const rx = Math.atan2(-(2 * (qy * qz - qw * qx)), 1 - 2 * (qx * qx + qy * qy));\n const rz = Math.atan2(-(2 * (qx * qy - qw * qz)), 1 - 2 * (qy * qy + qz * qz));\n return [rx, ry, rz];\n}\n\n/** Create a live bidirectional EulerProxy backed by the given ObservableQuat.\n *\n * Euler⇄quaternion is many-to-one and `quatToEulerXYZ` is unstable at gimbal lock\n * (e.g. yaw near ±π/2), so re-deriving Euler from the quaternion on every read makes\n * per-axis updates (`node.rotation.x = …; node.rotation.y = …`) lossy and can flip the\n * node. To stay stable, the proxy caches the Euler triple it last applied and reuses it\n * while the quaternion is unchanged; it only re-derives from the quaternion when the\n * quaternion was written externally (detected via its version counter). */\nexport function createEulerProxy(rq: ObservableQuat): EulerProxy {\n let ex = 0;\n let ey = 0;\n let ez = 0;\n // Snapshot of rq.version at the last sync. -1 forces an initial derive.\n let syncedVersion = -1;\n\n const sync = (): void => {\n if (rq.version !== syncedVersion) {\n const e = quatToEulerXYZ(rq.x, rq.y, rq.z, rq.w);\n ex = e[0];\n ey = e[1];\n ez = e[2];\n syncedVersion = rq.version;\n }\n };\n\n const apply = (x: number, y: number, z: number): void => {\n ex = x;\n ey = y;\n ez = z;\n const [a, b, c, d] = eulerToQuat(x, y, z);\n rq.set(a, b, c, d);\n // The cached Euler is authoritative for this quaternion value, so adopt the\n // version we just produced — avoids an immediate lossy re-derive on next read.\n syncedVersion = rq.version;\n };\n\n return {\n get x() {\n sync();\n return ex;\n },\n set x(v: number) {\n sync();\n apply(v, ey, ez);\n },\n get y() {\n sync();\n return ey;\n },\n set y(v: number) {\n sync();\n apply(ex, v, ez);\n },\n get z() {\n sync();\n return ez;\n },\n set z(v: number) {\n sync();\n apply(ex, ey, v);\n },\n set: apply,\n };\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────\n\n/** Create a SceneNode with given TRS (position and scaling in cartesian, rotation as quaternion). */\nexport function createSceneNode(name: string, px = 0, py = 0, pz = 0, qx = 0, qy = 0, qz = 0, qw = 1, sx = 1, sy = 1, sz = 1): SceneNode {\n return createSceneNodeCore(name, null, px, py, pz, qx, qy, qz, qw, sx, sy, sz);\n}\n\nexport function createSceneNodeFromMatrix(name: string, matrix: Mat4): SceneNode {\n return createSceneNodeCore(name, matrix);\n}\n\nfunction createSceneNodeCore(name: string, matrix: Mat4 | null, px = 0, py = 0, pz = 0, qx = 0, qy = 0, qz = 0, qw = 1, sx = 1, sy = 1, sz = 1): SceneNode {\n const wm = createWorldMatrixState(() => {\n if (matrix) {\n return matrix;\n }\n const p = node.position,\n rq = node.rotationQuaternion,\n s = node.scaling;\n const isIdentity = p.x === 0 && p.y === 0 && p.z === 0 && rq.x === 0 && rq.y === 0 && rq.z === 0 && rq.w === 1 && s.x === 1 && s.y === 1 && s.z === 1;\n return isIdentity ? mat4Identity() : mat4Compose(p.x, p.y, p.z, rq.x, rq.y, rq.z, rq.w, s.x, s.y, s.z);\n });\n const onWmDirty = () => {\n if (!matrix) {\n wm.markLocalDirty();\n }\n };\n\n const rq = new ObservableQuat(qx, qy, qz, qw, onWmDirty);\n\n const node: SceneNode = {\n name,\n children: [],\n position: new ObservableVec3(px, py, pz, onWmDirty),\n rotationQuaternion: rq,\n rotation: createEulerProxy(rq),\n scaling: new ObservableVec3(sx, sy, sz, onWmDirty),\n get parent() {\n return wm.parent;\n },\n set parent(v) {\n wm.parent = v;\n },\n get worldMatrix() {\n return wm.getWorldMatrix();\n },\n get worldMatrixVersion() {\n return wm.getWorldMatrixVersion();\n },\n };\n if (matrix) {\n node._localMatrix = matrix;\n }\n attachWorldMatrixState(node, wm);\n return node;\n}\n"],"names":["rq"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scene-node.js","sources":["../../../src/scene/scene-node.ts"],"sourcesContent":["/** SceneNode — common base for all scene entities with TRS, parent, and children.\n *\n * Provides position, rotationQuaternion (source of truth), rotation (Euler XYZ proxy),\n * scaling, parent, worldMatrix, worldMatrixVersion, and children. */\n\nimport type { Mat4 } from \"../math/types.js\";\nimport type { LiteMetadata } from \"../metadata.js\";\nimport type { IWorldMatrixProvider } from \"./parentable.js\";\nimport { ObservableVec3 } from \"../math/observable-vec3.js\";\nimport { ObservableQuat } from \"../math/observable-quat.js\";\nimport { createWorldMatrixState, attachWorldMatrixState, composeTrsLocalMatrix } from \"./world-matrix-state.js\";\n\n// ─── EulerProxy ──────────────────────────────────────────────────────\n\n/** Bidirectional Euler XYZ view over a quaternion.\n * Reads decompose the current quaternion on the fly; writes convert Euler→quat atomically. */\nexport interface EulerProxy {\n x: number;\n y: number;\n z: number;\n set(x: number, y: number, z: number): void;\n}\n\n// ─── SceneNode ───────────────────────────────────────────────────────\n\n/** Common base for all scene entities: TRS transform, parent/children hierarchy, and a cached world matrix. */\nexport interface SceneNode {\n name: string;\n children: SceneNode[];\n position: ObservableVec3;\n /** Quaternion rotation — source of truth for the local matrix. */\n rotationQuaternion: ObservableQuat;\n /** Euler XYZ bidirectional proxy — reads decompose current quat; writes update quat atomically. */\n rotation: EulerProxy;\n scaling: ObservableVec3;\n parent: IWorldMatrixProvider | null;\n readonly worldMatrix: Mat4;\n readonly worldMatrixVersion: number;\n /** @internal Raw local matrix for glTF matrix nodes. */\n _localMatrix?: Mat4;\n /** Self-visibility. Undefined/true = visible; `false` skips render + camera AABB.\n * Cascade is materialized at write-time by `setSubtreeVisible`. */\n visible?: boolean;\n /** User metadata. glTF loads populate `metadata.gltf.extras` when source extras exist. */\n metadata?: LiteMetadata;\n}\n\n// ─── Math helpers ─────────────────────────────────────────────────────\n\n/** Euler XYZ → quaternion (intrinsic XYZ order). */\nexport function eulerToQuat(rx: number, ry: number, rz: number): [number, number, number, number] {\n const cx = Math.cos(rx * 0.5),\n sx_ = Math.sin(rx * 0.5);\n const cy = Math.cos(ry * 0.5),\n sy_ = Math.sin(ry * 0.5);\n const cz = Math.cos(rz * 0.5),\n sz_ = Math.sin(rz * 0.5);\n return [sx_ * cy * cz + cx * sy_ * sz_, cx * sy_ * cz - sx_ * cy * sz_, cx * cy * sz_ + sx_ * sy_ * cz, cx * cy * cz - sx_ * sy_ * sz_];\n}\n\n/** Quaternion → Euler XYZ (inverse of eulerToQuat). */\nexport function quatToEulerXYZ(qx: number, qy: number, qz: number, qw: number): [number, number, number] {\n const sinY = 2 * (qx * qz + qw * qy);\n const ry = Math.asin(Math.max(-1, Math.min(1, sinY)));\n const rx = Math.atan2(-(2 * (qy * qz - qw * qx)), 1 - 2 * (qx * qx + qy * qy));\n const rz = Math.atan2(-(2 * (qx * qy - qw * qz)), 1 - 2 * (qy * qy + qz * qz));\n return [rx, ry, rz];\n}\n\n/** Create a live bidirectional EulerProxy backed by the given ObservableQuat.\n *\n * Euler⇄quaternion is many-to-one and `quatToEulerXYZ` is unstable at gimbal lock\n * (e.g. yaw near ±π/2), so re-deriving Euler from the quaternion on every read makes\n * per-axis updates (`node.rotation.x = …; node.rotation.y = …`) lossy and can flip the\n * node. To stay stable, the proxy caches the Euler triple it last applied and reuses it\n * while the quaternion is unchanged; it only re-derives from the quaternion when the\n * quaternion was written externally (detected via its version counter). */\nexport function createEulerProxy(rq: ObservableQuat): EulerProxy {\n let ex = 0;\n let ey = 0;\n let ez = 0;\n // Snapshot of rq.version at the last sync. -1 forces an initial derive.\n let syncedVersion = -1;\n\n const sync = (): void => {\n if (rq.version !== syncedVersion) {\n const e = quatToEulerXYZ(rq.x, rq.y, rq.z, rq.w);\n ex = e[0];\n ey = e[1];\n ez = e[2];\n syncedVersion = rq.version;\n }\n };\n\n const apply = (x: number, y: number, z: number): void => {\n ex = x;\n ey = y;\n ez = z;\n const [a, b, c, d] = eulerToQuat(x, y, z);\n rq.set(a, b, c, d);\n // The cached Euler is authoritative for this quaternion value, so adopt the\n // version we just produced — avoids an immediate lossy re-derive on next read.\n syncedVersion = rq.version;\n };\n\n return {\n get x() {\n sync();\n return ex;\n },\n set x(v: number) {\n sync();\n apply(v, ey, ez);\n },\n get y() {\n sync();\n return ey;\n },\n set y(v: number) {\n sync();\n apply(ex, v, ez);\n },\n get z() {\n sync();\n return ez;\n },\n set z(v: number) {\n sync();\n apply(ex, ey, v);\n },\n set: apply,\n };\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────\n\n/** Create a SceneNode with given TRS (position and scaling in cartesian, rotation as quaternion). */\nexport function createSceneNode(name: string, px = 0, py = 0, pz = 0, qx = 0, qy = 0, qz = 0, qw = 1, sx = 1, sy = 1, sz = 1): SceneNode {\n return createSceneNodeCore(name, null, px, py, pz, qx, qy, qz, qw, sx, sy, sz);\n}\n\nexport function createSceneNodeFromMatrix(name: string, matrix: Mat4): SceneNode {\n return createSceneNodeCore(name, matrix);\n}\n\nfunction createSceneNodeCore(name: string, matrix: Mat4 | null, px = 0, py = 0, pz = 0, qx = 0, qy = 0, qz = 0, qw = 1, sx = 1, sy = 1, sz = 1): SceneNode {\n const wm = createWorldMatrixState(() => {\n if (matrix) {\n return matrix;\n }\n return composeTrsLocalMatrix(node.position, node.rotationQuaternion, node.scaling);\n });\n const onWmDirty = () => {\n if (!matrix) {\n wm.markLocalDirty();\n }\n };\n\n const rq = new ObservableQuat(qx, qy, qz, qw, onWmDirty);\n\n const node: SceneNode = {\n name,\n children: [],\n position: new ObservableVec3(px, py, pz, onWmDirty),\n rotationQuaternion: rq,\n rotation: createEulerProxy(rq),\n scaling: new ObservableVec3(sx, sy, sz, onWmDirty),\n get parent() {\n return wm.parent;\n },\n set parent(v) {\n wm.parent = v;\n },\n get worldMatrix() {\n return wm.getWorldMatrix();\n },\n get worldMatrixVersion() {\n return wm.getWorldMatrixVersion();\n },\n };\n if (matrix) {\n node._localMatrix = matrix;\n }\n attachWorldMatrixState(node, wm);\n return node;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,76 @@ import { unregisterMeshScene } from './mesh-scene-registry.js';
|
|
|
2
2
|
import { disposeMeshGpu } from '../mesh/mesh-dispose.js';
|
|
3
3
|
import { removeMeshFromTask } from '../frame-graph/render-task.js';
|
|
4
4
|
|
|
5
|
-
function removeFromScene(scene,
|
|
5
|
+
function removeFromScene(scene, entity) {
|
|
6
|
+
if ("entities" in entity) {
|
|
7
|
+
const container = entity;
|
|
8
|
+
for (const e of container.entities) {
|
|
9
|
+
removeFromScene(scene, e);
|
|
10
|
+
}
|
|
11
|
+
if (container.camera && scene.camera === container.camera) {
|
|
12
|
+
scene.camera = null;
|
|
13
|
+
}
|
|
14
|
+
const groups = container.animationGroups;
|
|
15
|
+
if (groups?.length) {
|
|
16
|
+
for (const g of groups) {
|
|
17
|
+
spliceOut(scene.animationGroups, g);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const hook = container._beforeRenderHook;
|
|
21
|
+
if (hook) {
|
|
22
|
+
spliceOut(scene._beforeRender, hook);
|
|
23
|
+
container._beforeRenderHook = void 0;
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if ("_gpu" in entity && "material" in entity) {
|
|
28
|
+
removeMeshFromScene(scene, entity);
|
|
29
|
+
removeChildren(scene, entity);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if ("lightType" in entity) {
|
|
33
|
+
spliceOut(scene.lights, entity);
|
|
34
|
+
const sg = entity.shadowGenerator;
|
|
35
|
+
if (sg) {
|
|
36
|
+
disposeShadowGenerator(scene, sg);
|
|
37
|
+
}
|
|
38
|
+
} else if ("fov" in entity && "nearPlane" in entity) {
|
|
39
|
+
if (scene.camera === entity) {
|
|
40
|
+
scene.camera = null;
|
|
41
|
+
}
|
|
42
|
+
} else if ("_shadowType" in entity && "_light" in entity) {
|
|
43
|
+
disposeShadowGenerator(scene, entity);
|
|
44
|
+
}
|
|
45
|
+
detachParent(entity);
|
|
46
|
+
removeChildren(scene, entity);
|
|
47
|
+
}
|
|
48
|
+
function disposeShadowGenerator(scene, sg) {
|
|
49
|
+
spliceOut(scene.shadowGenerators, sg);
|
|
50
|
+
if (sg._shadowTaskState) {
|
|
51
|
+
sg._shadowTaskState._task.dispose();
|
|
52
|
+
sg._shadowTaskState = void 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function spliceOut(arr, item) {
|
|
56
|
+
const i = arr.indexOf(item);
|
|
57
|
+
if (i >= 0) {
|
|
58
|
+
arr.splice(i, 1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function detachParent(node) {
|
|
62
|
+
if (node && typeof node === "object" && "parent" in node) {
|
|
63
|
+
node.parent = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function removeChildren(scene, node) {
|
|
67
|
+
const kids = node.children;
|
|
68
|
+
if (kids?.length) {
|
|
69
|
+
for (const child of [...kids]) {
|
|
70
|
+
removeFromScene(scene, child);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function removeMeshFromScene(scene, mesh) {
|
|
6
75
|
const fns = scene._meshDisposables.get(mesh);
|
|
7
76
|
let didMutate = false;
|
|
8
77
|
if (fns) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-remove.js","sources":["../../../src/scene/scene-remove.ts"],"sourcesContent":["import type { SceneContext } from \"./scene-core.js\";\nimport { unregisterMeshScene } from \"./mesh-scene-registry.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { removeMeshFromTask } from \"../frame-graph/render-task.js\";\nimport type { RenderTask } from \"../frame-graph/render-task.js\";\n\n/** Remove a mesh from the scene and destroy its GPU resources.\n * Standalone function for tree-shaking — only included when actually used. */\nexport function removeFromScene(scene: SceneContext, mesh: Mesh): void {\n const fns = scene._meshDisposables.get(mesh);\n // Whether this call actually mutated scene state — used to gate the renderable\n // version bump so a no-op removal (mesh never registered) doesn't needlessly\n // invalidate the cached opaque bundle.\n let didMutate = false;\n if (fns) {\n didMutate = true;\n for (const fn of fns) {\n fn();\n }\n scene._meshDisposables.delete(mesh);\n }\n // AUX (override) view packets — depth/SSAO no-colour views another task registered on this mesh. A material\n // swap deliberately leaves these alone (see `_meshAuxDisposables`); a real removal must still free them.\n const auxFns = scene._meshAuxDisposables.get(mesh);\n if (auxFns) {\n didMutate = true;\n for (const fn of auxFns) {\n fn();\n }\n scene._meshAuxDisposables.delete(mesh);\n }\n const mi2 = scene.meshes.indexOf(mesh);\n if (mi2 >= 0) {\n scene.meshes.splice(mi2, 1);\n didMutate = true;\n }\n const i = scene._renderables.findIndex((r) => r.mesh === mesh);\n if (i >= 0) {\n scene._renderables.splice(i, 1);\n didMutate = true;\n }\n // Invalidate any auto-mirroring render task so it rebuilds its binding lists +\n // cached opaque bundle without this mesh BEFORE its GPU buffers (vertex data +\n // per-packet system UBO) are touched again. Done whenever the mesh actually\n // belonged to the scene — NOT gated on owning a standalone renderable: meshes\n // sharing a material at the initial scene build are merged into one combined\n // renderable whose `mesh` is undefined, yet their now-destroyed buffers are\n // still referenced by that renderable's update()/draw() and the cached bundle.\n // Mirrors the version bump done on add (material-swap) and initial build.\n if (didMutate) {\n scene._renderableVersion++;\n }\n // Drop from the material group registry so a later full rebuild (e.g. device-lost\n // recovery) doesn't try to re-materialize a disposed mesh.\n const build = mesh.material?._buildGroup;\n const group = build ? scene._groups.get(build) : undefined;\n if (group) {\n const gi = group.indexOf(mesh);\n if (gi >= 0) {\n group.splice(gi, 1);\n }\n }\n // Drop any pending swap-queue entry (mesh added then removed before the drain).\n const qi = scene._materialSwapQueue.indexOf(mesh);\n if (qi >= 0) {\n scene._materialSwapQueue.splice(qi, 1);\n }\n // Deregister from the world-matrix push registry so a long-lived parent stops\n // retaining/traversing this disposed child on every invalidation. (The parent→\n // child reference is new with the push model; reparent already deregisters, but\n // removal does not go through the parent setter otherwise.)\n mesh.parent = null;\n // Frame-graph eviction: the scene always has a frame graph (created in\n // createSceneContext). Walk its render-pass tasks and drop any binding whose\n // source mesh matches. RenderTasks are identified by carrying `_renderables`\n // (a `_config` field alone is NOT sufficient — post/effect tasks also have one).\n for (const task of scene._frameGraph._tasks) {\n if (\"_renderables\" in (task as object)) {\n removeMeshFromTask(task as RenderTask, mesh);\n }\n }\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene — a single\n // `Mesh` may be added to several scenes, and `disposeMeshGpu` destroys buffers they all share.\n if (unregisterMeshScene(scene, mesh)) {\n disposeMeshGpu(mesh);\n }\n}\n"],"names":[],"mappings":";;;;AASO,SAAS,eAAA,CAAgB,OAAqB,IAAA,EAAkB;AACnE,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,gBAAA,CAAiB,GAAA,CAAI,IAAI,CAAA;AAI3C,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,GAAA,EAAK;AACL,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AAClB,MAAA,EAAA,EAAG;AAAA,IACP;AACA,IAAA,KAAA,CAAM,gBAAA,CAAiB,OAAO,IAAI,CAAA;AAAA,EACtC;AAGA,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,mBAAA,CAAoB,GAAA,CAAI,IAAI,CAAA;AACjD,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,KAAA,MAAW,MAAM,MAAA,EAAQ;AACrB,MAAA,EAAA,EAAG;AAAA,IACP;AACA,IAAA,KAAA,CAAM,mBAAA,CAAoB,OAAO,IAAI,CAAA;AAAA,EACzC;AACA,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA;AACrC,EAAA,IAAI,OAAO,CAAA,EAAG;AACV,IAAA,KAAA,CAAM,MAAA,CAAO,MAAA,CAAO,GAAA,EAAK,CAAC,CAAA;AAC1B,IAAA,SAAA,GAAY,IAAA;AAAA,EAChB;AACA,EAAA,MAAM,CAAA,GAAI,MAAM,YAAA,CAAa,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,IAAI,CAAA;AAC7D,EAAA,IAAI,KAAK,CAAA,EAAG;AACR,IAAA,KAAA,CAAM,YAAA,CAAa,MAAA,CAAO,CAAA,EAAG,CAAC,CAAA;AAC9B,IAAA,SAAA,GAAY,IAAA;AAAA,EAChB;AASA,EAAA,IAAI,SAAA,EAAW;AACX,IAAA,KAAA,CAAM,kBAAA,EAAA;AAAA,EACV;AAGA,EAAA,MAAM,KAAA,GAAQ,KAAK,QAAA,EAAU,WAAA;AAC7B,EAAA,MAAM,QAAQ,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA,GAAI,MAAA;AACjD,EAAA,IAAI,KAAA,EAAO;AACP,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAC7B,IAAA,IAAI,MAAM,CAAA,EAAG;AACT,MAAA,KAAA,CAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,IACtB;AAAA,EACJ;AAEA,EAAA,MAAM,EAAA,GAAK,KAAA,CAAM,kBAAA,CAAmB,OAAA,CAAQ,IAAI,CAAA;AAChD,EAAA,IAAI,MAAM,CAAA,EAAG;AACT,IAAA,KAAA,CAAM,kBAAA,CAAmB,MAAA,CAAO,EAAA,EAAI,CAAC,CAAA;AAAA,EACzC;AAKA,EAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AAKd,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,WAAA,CAAY,MAAA,EAAQ;AACzC,IAAA,IAAI,kBAAmB,IAAA,EAAiB;AACpC,MAAA,kBAAA,CAAmB,MAAoB,IAAI,CAAA;AAAA,IAC/C;AAAA,EACJ;AAGA,EAAA,IAAI,mBAAA,CAAoB,KAAA,EAAO,IAAI,CAAA,EAAG;AAClC,IAAA,cAAA,CAAe,IAAI,CAAA;AAAA,EACvB;AACJ;;;;"}
|
|
1
|
+
{"version":3,"file":"scene-remove.js","sources":["../../../src/scene/scene-remove.ts"],"sourcesContent":["import type { addToScene, SceneContext } from \"./scene-core.js\";\nimport { unregisterMeshScene } from \"./mesh-scene-registry.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { LightBase } from \"../light/types.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { ShadowGenerator } from \"../shadow/shadow-generator.js\";\nimport type { TransformNode } from \"./transform-node.js\";\nimport type { SceneNode } from \"./scene-node.js\";\nimport type { AssetContainer } from \"../asset-container.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { removeMeshFromTask } from \"../frame-graph/render-task.js\";\nimport type { RenderTask } from \"../frame-graph/render-task.js\";\n\n/** Remove an entity from the scene, undoing what `addToScene` did. Accepts the same\n * union as {@link addToScene}: a Mesh, light, camera, shadow generator, transform node,\n * or a whole AssetContainer. Safe to call more than once (idempotent).\n *\n * Standalone function for tree-shaking — only included when actually used. */\nexport function removeFromScene(scene: SceneContext, entity: Mesh | LightBase | Camera | ShadowGenerator | TransformNode | AssetContainer): void {\n // AssetContainer — undo addToScene(scene, container) field by field.\n if (\"entities\" in entity) {\n const container = entity as AssetContainer;\n for (const e of container.entities) {\n removeFromScene(scene, e as Mesh | LightBase | TransformNode);\n }\n if (container.camera && scene.camera === container.camera) {\n scene.camera = null;\n }\n const groups = container.animationGroups;\n if (groups?.length) {\n for (const g of groups) {\n spliceOut(scene.animationGroups, g);\n }\n }\n const hook = container._beforeRenderHook;\n if (hook) {\n spliceOut(scene._beforeRender, hook);\n container._beforeRenderHook = undefined;\n }\n return;\n }\n // Mesh — carries GPU geometry + material. Owns the only heavy removal path.\n if (\"_gpu\" in entity && \"material\" in entity) {\n removeMeshFromScene(scene, entity as unknown as Mesh);\n removeChildren(scene, entity as unknown as SceneNode);\n return;\n }\n // Non-mesh scene nodes (light, camera, shadow generator, transform node) share a\n // detach-parent + unwind-children tail; only the scene-list bookkeeping differs.\n if (\"lightType\" in entity) {\n // Light — drop from the scene list and remove its shadow generator with it.\n spliceOut(scene.lights, entity as LightBase);\n const sg = (entity as LightBase).shadowGenerator;\n if (sg) {\n disposeShadowGenerator(scene, sg);\n }\n } else if (\"fov\" in entity && \"nearPlane\" in entity) {\n // Camera — clear the scene reference if this camera is the active one.\n if (scene.camera === (entity as Camera)) {\n scene.camera = null;\n }\n } else if (\"_shadowType\" in entity && \"_light\" in entity) {\n // Shadow generator removed on its own (not via its light).\n disposeShadowGenerator(scene, entity as ShadowGenerator);\n }\n // TransformNode / any other scene-graph node needs no bookkeeping of its own.\n detachParent(entity);\n removeChildren(scene, entity as unknown as SceneNode);\n}\n\n// Compile-time symmetry guard (zero runtime cost, not part of the public API):\n// removeFromScene must accept exactly the same arguments as addToScene so the two\n// stay mirror images. If either signature drifts, `_ParamsEqual` resolves to `false`,\n// `_AssertTrue<false>` violates its `extends true` constraint, and the build fails.\n// The `declare const` is ambient — it emits no JavaScript and is never exported.\ntype _ParamsEqual<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;\ntype _AssertTrue<T extends true> = T;\ndeclare const _removeMatchesAdd: _AssertTrue<_ParamsEqual<Parameters<typeof addToScene>, Parameters<typeof removeFromScene>>>;\n\n/** Drop a shadow generator from the scene and dispose its task resources exactly once.\n * The disposable render task lives on the lazily-created task state, not the generator\n * itself; nulling the state afterwards keeps repeat removals a safe no-op. */\nfunction disposeShadowGenerator(scene: SceneContext, sg: ShadowGenerator): void {\n spliceOut(scene.shadowGenerators, sg);\n if (sg._shadowTaskState) {\n sg._shadowTaskState._task.dispose();\n sg._shadowTaskState = undefined;\n }\n}\n\n/** Remove the first occurrence of `item` from `arr` if present. */\nfunction spliceOut<T>(arr: T[], item: T): void {\n const i = arr.indexOf(item);\n if (i >= 0) {\n arr.splice(i, 1);\n }\n}\n\n/** Clear an entity's `parent` link when it has one, so the world-matrix child registry\n * stops retaining/walking a removed node during parent invalidation. */\nfunction detachParent(node: unknown): void {\n if (node && typeof node === \"object\" && \"parent\" in node) {\n (node as { parent: unknown }).parent = null;\n }\n}\n\nfunction removeChildren(scene: SceneContext, node: SceneNode): void {\n const kids = node.children;\n if (kids?.length) {\n for (const child of [...kids]) {\n removeFromScene(scene, child as Mesh);\n }\n }\n}\n\n/** Remove a mesh from the scene and destroy its GPU resources.\n * Internal helper — `removeFromScene` dispatches here for the Mesh case. */\nfunction removeMeshFromScene(scene: SceneContext, mesh: Mesh): void {\n const fns = scene._meshDisposables.get(mesh);\n // Whether this call actually mutated scene state — used to gate the renderable\n // version bump so a no-op removal (mesh never registered) doesn't needlessly\n // invalidate the cached opaque bundle.\n let didMutate = false;\n if (fns) {\n didMutate = true;\n for (const fn of fns) {\n fn();\n }\n scene._meshDisposables.delete(mesh);\n }\n // AUX (override) view packets — depth/SSAO no-colour views another task registered on this mesh. A material\n // swap deliberately leaves these alone (see `_meshAuxDisposables`); a real removal must still free them.\n const auxFns = scene._meshAuxDisposables.get(mesh);\n if (auxFns) {\n didMutate = true;\n for (const fn of auxFns) {\n fn();\n }\n scene._meshAuxDisposables.delete(mesh);\n }\n const mi2 = scene.meshes.indexOf(mesh);\n if (mi2 >= 0) {\n scene.meshes.splice(mi2, 1);\n didMutate = true;\n }\n const i = scene._renderables.findIndex((r) => r.mesh === mesh);\n if (i >= 0) {\n scene._renderables.splice(i, 1);\n didMutate = true;\n }\n // Invalidate any auto-mirroring render task so it rebuilds its binding lists +\n // cached opaque bundle without this mesh BEFORE its GPU buffers (vertex data +\n // per-packet system UBO) are touched again. Done whenever the mesh actually\n // belonged to the scene — NOT gated on owning a standalone renderable: meshes\n // sharing a material at the initial scene build are merged into one combined\n // renderable whose `mesh` is undefined, yet their now-destroyed buffers are\n // still referenced by that renderable's update()/draw() and the cached bundle.\n // Mirrors the version bump done on add (material-swap) and initial build.\n if (didMutate) {\n scene._renderableVersion++;\n }\n // Drop from the material group registry so a later full rebuild (e.g. device-lost\n // recovery) doesn't try to re-materialize a disposed mesh.\n const build = mesh.material?._buildGroup;\n const group = build ? scene._groups.get(build) : undefined;\n if (group) {\n const gi = group.indexOf(mesh);\n if (gi >= 0) {\n group.splice(gi, 1);\n }\n }\n // Drop any pending swap-queue entry (mesh added then removed before the drain).\n const qi = scene._materialSwapQueue.indexOf(mesh);\n if (qi >= 0) {\n scene._materialSwapQueue.splice(qi, 1);\n }\n // Deregister from the world-matrix push registry so a long-lived parent stops\n // retaining/traversing this disposed child on every invalidation. (The parent→\n // child reference is new with the push model; reparent already deregisters, but\n // removal does not go through the parent setter otherwise.)\n mesh.parent = null;\n // Frame-graph eviction: the scene always has a frame graph (created in\n // createSceneContext). Walk its render-pass tasks and drop any binding whose\n // source mesh matches. RenderTasks are identified by carrying `_renderables`\n // (a `_config` field alone is NOT sufficient — post/effect tasks also have one).\n for (const task of scene._frameGraph._tasks) {\n if (\"_renderables\" in (task as object)) {\n removeMeshFromTask(task as RenderTask, mesh);\n }\n }\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene — a single\n // `Mesh` may be added to several scenes, and `disposeMeshGpu` destroys buffers they all share.\n if (unregisterMeshScene(scene, mesh)) {\n disposeMeshGpu(mesh);\n }\n}\n"],"names":[],"mappings":"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"}
|