@babylonjs/lite 1.12.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/index.d.ts CHANGED
@@ -4428,6 +4428,18 @@ export declare function getMasterVolume(engine: AudioEngine): number;
4428
4428
  * values above as the documented set to match against. */
4429
4429
  export declare function getMaterialFamily(material: Material): string | undefined;
4430
4430
 
4431
+ /** Return caller-owned copies of the CPU geometry retained by a mesh.
4432
+ * Returns `null` when positions, normals, or indices are unavailable. */
4433
+ export declare function getMeshGeometry(mesh: Mesh): {
4434
+ positions: Float32Array;
4435
+ normals: Float32Array;
4436
+ indices: Uint32Array;
4437
+ uvs?: Float32Array;
4438
+ uvs2?: Float32Array;
4439
+ tangents?: Float32Array;
4440
+ colors?: Float32Array;
4441
+ } | null;
4442
+
4431
4443
  /** Get the current seed used by Recast's randomized queries. */
4432
4444
  export declare function getNavigationRandomSeed(plugin: NavigationPlugin): number;
4433
4445
 
@@ -7334,9 +7346,12 @@ export declare function removeBillboardSprite(handle: BillboardSpriteHandle): vo
7334
7346
  */
7335
7347
  export declare function removeBillboardSpriteIndex(system: BillboardSpriteSystem, index: number): void;
7336
7348
 
7337
- /** Remove a mesh from the scene and destroy its GPU resources.
7349
+ /** Remove an entity from the scene, undoing what `addToScene` did. Accepts the same
7350
+ * union as {@link addToScene}: a Mesh, light, camera, shadow generator, transform node,
7351
+ * or a whole AssetContainer. Safe to call more than once (idempotent).
7352
+ *
7338
7353
  * Standalone function for tree-shaking — only included when actually used. */
7339
- export declare function removeFromScene(scene: SceneContext, mesh: Mesh): void;
7354
+ export declare function removeFromScene(scene: SceneContext, entity: Mesh | LightBase | Camera | ShadowGenerator | TransformNode | AssetContainer): void;
7340
7355
 
7341
7356
  /**
7342
7357
  * Remove one logical hierarchy instance with O(1) swap-remove semantics.
@@ -1 +1 @@
1
- {"version":3,"file":"asset-container.js","sources":["../../src/asset-container.ts"],"sourcesContent":["import type { SceneNode } from \"./scene/scene-node.js\";\nimport type { LightBase } from \"./light/types.js\";\nimport type { AnimationGroup } from \"./animation/animation-group.js\";\nimport type { MaterialVariantData } from \"./loader-gltf/material-variants.js\";\nimport type { Mesh } from \"./mesh/mesh.js\";\nimport type { Skeleton } from \"./skeleton/bone-control.js\";\nimport type { SceneContext } from \"./scene/scene-core.js\";\n\n/**\n * Result returned by loadGltf / loadBabylon.\n * Pass directly to addToScene() — it handles all fields automatically.\n *\n * - glTF: entities = [root TransformNode], animationGroups = loaded clips\n * - .babylon: entities = root SceneNodes (Mesh/TransformNode) + LightBase, clearColor from file\n */\nexport interface AssetContainer {\n /** Scene entities. glTF: [root TransformNode]. .babylon: root nodes + lights. */\n entities: Array<SceneNode | LightBase>;\n /** Animation groups from the file. addToScene() registers them with the scene-owned AnimationManager by default. */\n animationGroups?: AnimationGroup[];\n /** Scene background color declared in the file. addToScene() applies it to scene.clearColor. */\n clearColor?: GPUColorDict;\n /** Camera parsed from the file. addToScene() sets it as scene.camera when present. */\n camera?: import(\"./camera/camera.js\").Camera;\n /** KHR_materials_variants data. Use selectVariant() / getVariantNames() to interact. */\n materialVariants?: MaterialVariantData;\n /** KHR_xmp_json_ld metadata. `packets` are the JSON-LD packets declared at the\n * document level; `assetPacket` is the packet referenced by `asset` (if any). */\n xmpMetadata?: { packets: unknown[]; assetPacket?: unknown };\n /** Bone-control handles, one per glTF skin. Present only when\n * `enableBoneControl()` was called before loading; otherwise `undefined`.\n * Drive bones via `getBoneByName()` + the `setBone*` functions. */\n skeletons?: Skeleton[];\n /** Deferred scene-wiring hook contributed by a loader feature that needs the\n * target `SceneContext` (which the loader itself never sees). `addToScene()`\n * invokes it once, synchronously, while processing the container. Used by\n * `EXT_lights_image_based` to install its image-based-light environment\n * (spherical harmonics + specular cubemap) onto the scene. Lazy features own\n * the closure so the core loader/scene stay feature-agnostic.\n * @internal */\n _sceneSetup?: (scene: SceneContext) => void;\n}\n\n/**\n * Flatten a loaded asset container's entity tree to its renderable `Mesh` nodes\n * (those carrying GPU geometry), matching the flat `meshes` array Babylon.js\n * loaders return. Useful for camera-framing and per-mesh inspection after a load.\n *\n * Tree-shakeable: only callers that import this pull it into their bundle.\n */\nexport function getContainerMeshes(container: AssetContainer): Mesh[] {\n const meshes: Mesh[] = [];\n const seen = new Set<unknown>();\n const visit = (node: SceneNode): void => {\n if (seen.has(node)) {\n return;\n }\n seen.add(node);\n if ((node as unknown as { _gpu?: unknown })._gpu) {\n meshes.push(node as unknown as Mesh);\n }\n const children = (node as unknown as { children?: SceneNode[] }).children;\n if (children) {\n for (const child of children) {\n visit(child);\n }\n }\n };\n for (const entity of container.entities) {\n // Lights have no scene-graph children to walk; skip them.\n if (\"lightType\" in (entity as object)) {\n continue;\n }\n visit(entity as SceneNode);\n }\n return meshes;\n}\n"],"names":[],"mappings":"AAkDO,SAAS,mBAAmB,SAAA,EAAmC;AAClE,EAAA,MAAM,SAAiB,EAAC;AACxB,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAa;AAC9B,EAAA,MAAM,KAAA,GAAQ,CAAC,IAAA,KAA0B;AACrC,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG;AAChB,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AACb,IAAA,IAAK,KAAuC,IAAA,EAAM;AAC9C,MAAA,MAAA,CAAO,KAAK,IAAuB,CAAA;AAAA,IACvC;AACA,IAAA,MAAM,WAAY,IAAA,CAA+C,QAAA;AACjE,IAAA,IAAI,QAAA,EAAU;AACV,MAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC1B,QAAA,KAAA,CAAM,KAAK,CAAA;AAAA,MACf;AAAA,IACJ;AAAA,EACJ,CAAA;AACA,EAAA,KAAA,MAAW,MAAA,IAAU,UAAU,QAAA,EAAU;AAErC,IAAA,IAAI,eAAgB,MAAA,EAAmB;AACnC,MAAA;AAAA,IACJ;AACA,IAAA,KAAA,CAAM,MAAmB,CAAA;AAAA,EAC7B;AACA,EAAA,OAAO,MAAA;AACX;;;;"}
1
+ {"version":3,"file":"asset-container.js","sources":["../../src/asset-container.ts"],"sourcesContent":["import type { SceneNode } from \"./scene/scene-node.js\";\nimport type { LightBase } from \"./light/types.js\";\nimport type { AnimationGroup } from \"./animation/animation-group.js\";\nimport type { MaterialVariantData } from \"./loader-gltf/material-variants.js\";\nimport type { Mesh } from \"./mesh/mesh.js\";\nimport type { Skeleton } from \"./skeleton/bone-control.js\";\nimport type { SceneContext } from \"./scene/scene-core.js\";\n\n/**\n * Result returned by loadGltf / loadBabylon.\n * Pass directly to addToScene() — it handles all fields automatically.\n *\n * - glTF: entities = [root TransformNode], animationGroups = loaded clips\n * - .babylon: entities = root SceneNodes (Mesh/TransformNode) + LightBase, clearColor from file\n */\nexport interface AssetContainer {\n /** Scene entities. glTF: [root TransformNode]. .babylon: root nodes + lights. */\n entities: Array<SceneNode | LightBase>;\n /** Animation groups from the file. addToScene() registers them with the scene-owned AnimationManager by default. */\n animationGroups?: AnimationGroup[];\n /** Scene background color declared in the file. addToScene() applies it to scene.clearColor. */\n clearColor?: GPUColorDict;\n /** Camera parsed from the file. addToScene() sets it as scene.camera when present. */\n camera?: import(\"./camera/camera.js\").Camera;\n /** KHR_materials_variants data. Use selectVariant() / getVariantNames() to interact. */\n materialVariants?: MaterialVariantData;\n /** KHR_xmp_json_ld metadata. `packets` are the JSON-LD packets declared at the\n * document level; `assetPacket` is the packet referenced by `asset` (if any). */\n xmpMetadata?: { packets: unknown[]; assetPacket?: unknown };\n /** Bone-control handles, one per glTF skin. Present only when\n * `enableBoneControl()` was called before loading; otherwise `undefined`.\n * Drive bones via `getBoneByName()` + the `setBone*` functions. */\n skeletons?: Skeleton[];\n /** @internal Per-frame animation tick closure pushed onto `scene._beforeRender` by\n * `addToScene(scene, container)`. Stored so `removeFromScene(scene, container)` can\n * splice it back out, keeping the two calls symmetric. */\n _beforeRenderHook?: (deltaMs: number) => void;\n /** Deferred scene-wiring hook contributed by a loader feature that needs the\n * target `SceneContext` (which the loader itself never sees). `addToScene()`\n * invokes it once, synchronously, while processing the container. Used by\n * `EXT_lights_image_based` to install its image-based-light environment\n * (spherical harmonics + specular cubemap) onto the scene. Lazy features own\n * the closure so the core loader/scene stay feature-agnostic.\n * @internal */\n _sceneSetup?: (scene: SceneContext) => void;\n}\n\n/**\n * Flatten a loaded asset container's entity tree to its renderable `Mesh` nodes\n * (those carrying GPU geometry), matching the flat `meshes` array Babylon.js\n * loaders return. Useful for camera-framing and per-mesh inspection after a load.\n *\n * Tree-shakeable: only callers that import this pull it into their bundle.\n */\nexport function getContainerMeshes(container: AssetContainer): Mesh[] {\n const meshes: Mesh[] = [];\n const seen = new Set<unknown>();\n const visit = (node: SceneNode): void => {\n if (seen.has(node)) {\n return;\n }\n seen.add(node);\n if ((node as unknown as { _gpu?: unknown })._gpu) {\n meshes.push(node as unknown as Mesh);\n }\n const children = (node as unknown as { children?: SceneNode[] }).children;\n if (children) {\n for (const child of children) {\n visit(child);\n }\n }\n };\n for (const entity of container.entities) {\n // Lights have no scene-graph children to walk; skip them.\n if (\"lightType\" in (entity as object)) {\n continue;\n }\n visit(entity as SceneNode);\n }\n return meshes;\n}\n"],"names":[],"mappings":"AAsDO,SAAS,mBAAmB,SAAA,EAAmC;AAClE,EAAA,MAAM,SAAiB,EAAC;AACxB,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAa;AAC9B,EAAA,MAAM,KAAA,GAAQ,CAAC,IAAA,KAA0B;AACrC,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG;AAChB,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AACb,IAAA,IAAK,KAAuC,IAAA,EAAM;AAC9C,MAAA,MAAA,CAAO,KAAK,IAAuB,CAAA;AAAA,IACvC;AACA,IAAA,MAAM,WAAY,IAAA,CAA+C,QAAA;AACjE,IAAA,IAAI,QAAA,EAAU;AACV,MAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC1B,QAAA,KAAA,CAAM,KAAK,CAAA;AAAA,MACf;AAAA,IACJ;AAAA,EACJ,CAAA;AACA,EAAA,KAAA,MAAW,MAAA,IAAU,UAAU,QAAA,EAAU;AAErC,IAAA,IAAI,eAAgB,MAAA,EAAmB;AACnC,MAAA;AAAA,IACJ;AACA,IAAA,KAAA,CAAM,MAAmB,CAAA;AAAA,EAC7B;AACA,EAAA,OAAO,MAAA;AACX;;;;"}
@@ -2,7 +2,7 @@ import { _setHpmAllocator } from '../math/_matrix-allocator.js';
2
2
  import { isDomCanvas, _buildSurface, resizeSurface, _refreshScRT, setSurfaceSize } from './surface.js';
3
3
  import { disposeGpuResourceRetirements } from './gpu-resource-retirement.js';
4
4
 
5
- const VERSION = /* @__PURE__ */ (() => "1.12.0" )();
5
+ const VERSION = /* @__PURE__ */ (() => "1.13.0" )();
6
6
  let _vis = 0;
7
7
  function bumpVisibilityEpoch() {
8
8
  _vis = _vis + 1 | 0;
package/lib/index.js CHANGED
@@ -51,6 +51,7 @@ export { createSpotLight } from './light/spot-light.js';
51
51
  export { addClusteredLightContainer, createClusteredLightContainer, createClusteredPointLight, markClusteredLightContainerDirty } from './light/clustered.js';
52
52
  export { MAX_LIGHTS, setMaxLights } from './light/types.js';
53
53
  export { createBox, createCapsule, createCylinder, createDisc, createExtrudeShape, createGround, createGroundFromHeightMap, createMeshFromData, createPlane, createPolyhedron, createRibbon, createSphere, createTorus, createTorusKnot, createTube, invalidateRenderBundles, resizeMeshGeometry, updateMeshColors, updateMeshGeometry, updateMeshGeometryCapacity, updateMeshNormals, updateMeshPositions, updateMeshTangents, updateMeshUv2, updateMeshUvs } from './mesh/mesh-factories.js';
54
+ export { getMeshGeometry } from './mesh/get-mesh-geometry.js';
54
55
  export { createBoxData } from './mesh/create-box.js';
55
56
  export { createSphereData } from './mesh/create-sphere.js';
56
57
  export { createCylinderData } from './mesh/create-cylinder.js';
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -27,8 +27,8 @@ _installPbrPrimitiveResolver((meshFeatures, hasDoubleSided) => {
27
27
  return {
28
28
  topology,
29
29
  ...stripIndexFormat ? { stripIndexFormat } : void 0,
30
- cullMode: noCull || hasDoubleSided ? "none" : reverseWinding ? "front" : "back",
31
- frontFace: "ccw"
30
+ cullMode: noCull || hasDoubleSided ? "none" : "back",
31
+ frontFace: reverseWinding ? "cw" : "ccw"
32
32
  };
33
33
  });
34
34
  //# sourceMappingURL=pbr-primitive-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pbr-primitive-resolver.js","sources":["../../../../src/material/pbr/pbr-primitive-resolver.ts"],"sourcesContent":["/** Installs the PBR pipeline's primitive-state resolver AND the extra mesh-feature encoder, which\n * together handle non-triangle glTF topologies (POINTS / LINES / LINE_STRIP / TRIANGLE_STRIP) and\n * negative-determinant winding reversal. Imported for side effect only by the glTF primitive +\n * negative-winding features, so triangle-list positive-winding scenes (the overwhelming majority)\n * never bundle this code and keep their renderer + pipeline chunks byte-identical.\n *\n * These bit constants are deliberately defined locally (not exported from mesh-features.ts): a bit\n * used only inside this lazy module must not leak into the shared mesh-features chunk, or it would\n * grow every scene's bundle. */\nimport type { Mesh } from \"../../mesh/mesh.js\";\nimport { _installMeshFeatureExtra } from \"../mesh-features.js\";\nimport { _installPbrPrimitiveResolver } from \"./pbr-pipeline.js\";\n\n/** Mesh world transform has a positive determinant (mirrored vs the RH→LH root): its triangle\n * winding is reversed, so back-face culling must flip (cull \"front\"). */\nconst MSH_REVERSE_WINDING = 1 << 11;\n/** Non-triangle-list primitive topology, encoded as a 3-bit index in bits 12-14 (1=point-list,\n * 2=line-list, 3=line-strip, 4=triangle-strip; 0=triangle-list). */\nconst MSH_TOPOLOGY_SHIFT = 12;\n/** A line-strip / triangle-strip mesh uses a uint32 index buffer (vs uint16); WebGPU needs the\n * pipeline's `stripIndexFormat` to match the index buffer for indexed strip draws. */\nconst MSH_INDEX_U32 = 1 << 15;\n\n// Encode the topology + negative-winding bits from the per-mesh flags set by the loader/feature.\n_installMeshFeatureExtra((mesh: Mesh): number => {\n let f = 0;\n if ((mesh as { _reverseWinding?: boolean })._reverseWinding) {\n f |= MSH_REVERSE_WINDING;\n }\n const topo = (mesh as { _topology?: number })._topology;\n if (topo) {\n f |= topo << MSH_TOPOLOGY_SHIFT;\n // Strips need the pipeline stripIndexFormat to match the index buffer; flag uint32 so the\n // pipeline picks the right format. Lite always draws indexed.\n if (topo >= 3 && mesh._gpu.indexFormat === \"uint32\") {\n f |= MSH_INDEX_U32;\n }\n }\n return f;\n});\n\n_installPbrPrimitiveResolver((meshFeatures, hasDoubleSided): GPUPrimitiveState => {\n // A mirrored mesh (positive world determinant, e.g. KHR negative node scale) has reversed\n // triangle winding, so back-face culling must cull the FRONT face instead. Matches BJS, which\n // flips sideOrientation when the world matrix determinant is negative.\n const reverseWinding = (meshFeatures & MSH_REVERSE_WINDING) !== 0;\n // Non-triangle-list primitive topology. Points and lines have no faces to cull; for a strip the\n // material's culling still applies.\n const topoIdx = (meshFeatures >> MSH_TOPOLOGY_SHIFT) & 7;\n const topology: GPUPrimitiveTopology =\n topoIdx === 1 ? \"point-list\" : topoIdx === 2 ? \"line-list\" : topoIdx === 3 ? \"line-strip\" : topoIdx === 4 ? \"triangle-strip\" : \"triangle-list\";\n const noCull = topoIdx >= 1 && topoIdx <= 3;\n // Indexed strip draws need stripIndexFormat to match the index buffer.\n const stripIndexFormat: GPUIndexFormat | undefined = topoIdx >= 3 ? (meshFeatures & MSH_INDEX_U32 ? \"uint32\" : \"uint16\") : undefined;\n return {\n topology,\n ...(stripIndexFormat ? { stripIndexFormat } : undefined),\n cullMode: noCull || hasDoubleSided ? \"none\" : reverseWinding ? \"front\" : \"back\",\n frontFace: \"ccw\",\n };\n});\n"],"names":[],"mappings":";;;AAeA,MAAM,sBAAsB,CAAA,IAAK,EAAA;AAGjC,MAAM,kBAAA,GAAqB,EAAA;AAG3B,MAAM,gBAAgB,CAAA,IAAK,EAAA;AAG3B,wBAAA,CAAyB,CAAC,IAAA,KAAuB;AAC7C,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,IAAK,KAAuC,eAAA,EAAiB;AACzD,IAAA,CAAA,IAAK,mBAAA;AAAA,EACT;AACA,EAAA,MAAM,OAAQ,IAAA,CAAgC,SAAA;AAC9C,EAAA,IAAI,IAAA,EAAM;AACN,IAAA,CAAA,IAAK,IAAA,IAAQ,kBAAA;AAGb,IAAA,IAAI,IAAA,IAAQ,CAAA,IAAK,IAAA,CAAK,IAAA,CAAK,gBAAgB,QAAA,EAAU;AACjD,MAAA,CAAA,IAAK,aAAA;AAAA,IACT;AAAA,EACJ;AACA,EAAA,OAAO,CAAA;AACX,CAAC,CAAA;AAED,4BAAA,CAA6B,CAAC,cAAc,cAAA,KAAsC;AAI9E,EAAA,MAAM,cAAA,GAAA,CAAkB,eAAe,mBAAA,MAAyB,CAAA;AAGhE,EAAA,MAAM,OAAA,GAAW,gBAAgB,kBAAA,GAAsB,CAAA;AACvD,EAAA,MAAM,QAAA,GACF,OAAA,KAAY,CAAA,GAAI,YAAA,GAAe,OAAA,KAAY,CAAA,GAAI,WAAA,GAAc,OAAA,KAAY,CAAA,GAAI,YAAA,GAAe,OAAA,KAAY,CAAA,GAAI,gBAAA,GAAmB,eAAA;AACnI,EAAA,MAAM,MAAA,GAAS,OAAA,IAAW,CAAA,IAAK,OAAA,IAAW,CAAA;AAE1C,EAAA,MAAM,mBAA+C,OAAA,IAAW,CAAA,GAAK,YAAA,GAAe,aAAA,GAAgB,WAAW,QAAA,GAAY,MAAA;AAC3H,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA,GAAI,gBAAA,GAAmB,EAAE,gBAAA,EAAiB,GAAI,MAAA;AAAA,IAC9C,QAAA,EAAU,MAAA,IAAU,cAAA,GAAiB,MAAA,GAAS,iBAAiB,OAAA,GAAU,MAAA;AAAA,IACzE,SAAA,EAAW;AAAA,GACf;AACJ,CAAC,CAAA"}
1
+ {"version":3,"file":"pbr-primitive-resolver.js","sources":["../../../../src/material/pbr/pbr-primitive-resolver.ts"],"sourcesContent":["/** Installs the PBR pipeline's primitive-state resolver AND the extra mesh-feature encoder, which\n * together handle non-triangle glTF topologies (POINTS / LINES / LINE_STRIP / TRIANGLE_STRIP) and\n * negative-determinant winding reversal. Imported for side effect only by the glTF primitive +\n * negative-winding features, so triangle-list positive-winding scenes (the overwhelming majority)\n * never bundle this code and keep their renderer + pipeline chunks byte-identical.\n *\n * These bit constants are deliberately defined locally (not exported from mesh-features.ts): a bit\n * used only inside this lazy module must not leak into the shared mesh-features chunk, or it would\n * grow every scene's bundle. */\nimport type { Mesh } from \"../../mesh/mesh.js\";\nimport { _installMeshFeatureExtra } from \"../mesh-features.js\";\nimport { _installPbrPrimitiveResolver } from \"./pbr-pipeline.js\";\n\n/** Mesh world transform has a positive determinant (mirrored vs the RH→LH root): its triangle\n * winding is reversed, so back-face culling must flip (cull \"front\"). */\nconst MSH_REVERSE_WINDING = 1 << 11;\n/** Non-triangle-list primitive topology, encoded as a 3-bit index in bits 12-14 (1=point-list,\n * 2=line-list, 3=line-strip, 4=triangle-strip; 0=triangle-list). */\nconst MSH_TOPOLOGY_SHIFT = 12;\n/** A line-strip / triangle-strip mesh uses a uint32 index buffer (vs uint16); WebGPU needs the\n * pipeline's `stripIndexFormat` to match the index buffer for indexed strip draws. */\nconst MSH_INDEX_U32 = 1 << 15;\n\n// Encode the topology + negative-winding bits from the per-mesh flags set by the loader/feature.\n_installMeshFeatureExtra((mesh: Mesh): number => {\n let f = 0;\n if ((mesh as { _reverseWinding?: boolean })._reverseWinding) {\n f |= MSH_REVERSE_WINDING;\n }\n const topo = (mesh as { _topology?: number })._topology;\n if (topo) {\n f |= topo << MSH_TOPOLOGY_SHIFT;\n // Strips need the pipeline stripIndexFormat to match the index buffer; flag uint32 so the\n // pipeline picks the right format. Lite always draws indexed.\n if (topo >= 3 && mesh._gpu.indexFormat === \"uint32\") {\n f |= MSH_INDEX_U32;\n }\n }\n return f;\n});\n\n_installPbrPrimitiveResolver((meshFeatures, hasDoubleSided): GPUPrimitiveState => {\n // `reverseWinding` marks a mesh mirrored relative to Lite's world space: the loader sets it when\n // the node's world-matrix determinant is positive. (Lite applies a RH→LH root flip with det < 0,\n // so an un-mirrored glTF node lands at a negative world determinant and a mirrored one — e.g. KHR\n // negative node scale — at a positive one.) A mirrored mesh has reversed triangle winding, so we\n // flip the pipeline's `frontFace` (ccw→cw) rather than the cull face: WebGPU derives\n // `@builtin(front_facing)` from `frontFace`, so this keeps the double-sided shader's front-facing\n // normal flip correct (a cullMode flip would leave front_facing evaluated against the un-mirrored\n // ccw winding, wrongly inverting the shading normal on the visible outer surface → black). BJS\n // handles the same case by flipping sideOrientation (the GL front-face winding); it states the\n // condition as a negative determinant because it measures the sign in its own opposite-handed space.\n const reverseWinding = (meshFeatures & MSH_REVERSE_WINDING) !== 0;\n // Non-triangle-list primitive topology. Points and lines have no faces to cull; for a strip the\n // material's culling still applies.\n const topoIdx = (meshFeatures >> MSH_TOPOLOGY_SHIFT) & 7;\n const topology: GPUPrimitiveTopology =\n topoIdx === 1 ? \"point-list\" : topoIdx === 2 ? \"line-list\" : topoIdx === 3 ? \"line-strip\" : topoIdx === 4 ? \"triangle-strip\" : \"triangle-list\";\n const noCull = topoIdx >= 1 && topoIdx <= 3;\n // Indexed strip draws need stripIndexFormat to match the index buffer.\n const stripIndexFormat: GPUIndexFormat | undefined = topoIdx >= 3 ? (meshFeatures & MSH_INDEX_U32 ? \"uint32\" : \"uint16\") : undefined;\n return {\n topology,\n ...(stripIndexFormat ? { stripIndexFormat } : undefined),\n cullMode: noCull || hasDoubleSided ? \"none\" : \"back\",\n frontFace: reverseWinding ? \"cw\" : \"ccw\",\n };\n});\n"],"names":[],"mappings":";;;AAeA,MAAM,sBAAsB,CAAA,IAAK,EAAA;AAGjC,MAAM,kBAAA,GAAqB,EAAA;AAG3B,MAAM,gBAAgB,CAAA,IAAK,EAAA;AAG3B,wBAAA,CAAyB,CAAC,IAAA,KAAuB;AAC7C,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,IAAK,KAAuC,eAAA,EAAiB;AACzD,IAAA,CAAA,IAAK,mBAAA;AAAA,EACT;AACA,EAAA,MAAM,OAAQ,IAAA,CAAgC,SAAA;AAC9C,EAAA,IAAI,IAAA,EAAM;AACN,IAAA,CAAA,IAAK,IAAA,IAAQ,kBAAA;AAGb,IAAA,IAAI,IAAA,IAAQ,CAAA,IAAK,IAAA,CAAK,IAAA,CAAK,gBAAgB,QAAA,EAAU;AACjD,MAAA,CAAA,IAAK,aAAA;AAAA,IACT;AAAA,EACJ;AACA,EAAA,OAAO,CAAA;AACX,CAAC,CAAA;AAED,4BAAA,CAA6B,CAAC,cAAc,cAAA,KAAsC;AAW9E,EAAA,MAAM,cAAA,GAAA,CAAkB,eAAe,mBAAA,MAAyB,CAAA;AAGhE,EAAA,MAAM,OAAA,GAAW,gBAAgB,kBAAA,GAAsB,CAAA;AACvD,EAAA,MAAM,QAAA,GACF,OAAA,KAAY,CAAA,GAAI,YAAA,GAAe,OAAA,KAAY,CAAA,GAAI,WAAA,GAAc,OAAA,KAAY,CAAA,GAAI,YAAA,GAAe,OAAA,KAAY,CAAA,GAAI,gBAAA,GAAmB,eAAA;AACnI,EAAA,MAAM,MAAA,GAAS,OAAA,IAAW,CAAA,IAAK,OAAA,IAAW,CAAA;AAE1C,EAAA,MAAM,mBAA+C,OAAA,IAAW,CAAA,GAAK,YAAA,GAAe,aAAA,GAAgB,WAAW,QAAA,GAAY,MAAA;AAC3H,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA,GAAI,gBAAA,GAAmB,EAAE,gBAAA,EAAiB,GAAI,MAAA;AAAA,IAC9C,QAAA,EAAU,MAAA,IAAU,cAAA,GAAiB,MAAA,GAAS,MAAA;AAAA,IAC9C,SAAA,EAAW,iBAAiB,IAAA,GAAO;AAAA,GACvC;AACJ,CAAC,CAAA"}
@@ -0,0 +1,24 @@
1
+ function getMeshGeometry(mesh) {
2
+ const positions = mesh._cpuPositions;
3
+ const normals = mesh._cpuNormals;
4
+ const indices = mesh._cpuIndices;
5
+ if (!positions || !normals || !indices) {
6
+ return null;
7
+ }
8
+ const uvs = mesh._cpuUvs;
9
+ const uvs2 = mesh._cpuUv2s;
10
+ const tangents = mesh._cpuTangents;
11
+ const colors = mesh._cpuColors;
12
+ return {
13
+ positions: positions.slice(),
14
+ normals: normals.slice(),
15
+ indices: indices.slice(),
16
+ ...uvs ? { uvs: uvs.slice() } : {},
17
+ ...uvs2 ? { uvs2: uvs2.slice() } : {},
18
+ ...tangents ? { tangents: tangents.slice() } : {},
19
+ ...colors ? { colors: colors.slice() } : {}
20
+ };
21
+ }
22
+
23
+ export { getMeshGeometry };
24
+ //# sourceMappingURL=get-mesh-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-mesh-geometry.js","sources":["../../../src/mesh/get-mesh-geometry.ts"],"sourcesContent":["import type { Mesh } from \"./mesh.js\";\n\n/** Return caller-owned copies of the CPU geometry retained by a mesh.\n * Returns `null` when positions, normals, or indices are unavailable. */\nexport function getMeshGeometry(mesh: Mesh): {\n positions: Float32Array;\n normals: Float32Array;\n indices: Uint32Array;\n uvs?: Float32Array;\n uvs2?: Float32Array;\n tangents?: Float32Array;\n colors?: Float32Array;\n} | null {\n const positions = mesh._cpuPositions;\n const normals = mesh._cpuNormals;\n const indices = mesh._cpuIndices;\n if (!positions || !normals || !indices) {\n return null;\n }\n\n const uvs = mesh._cpuUvs;\n const uvs2 = mesh._cpuUv2s;\n const tangents = mesh._cpuTangents;\n const colors = mesh._cpuColors;\n return {\n positions: positions.slice(),\n normals: normals.slice(),\n indices: indices.slice(),\n ...(uvs ? { uvs: uvs.slice() } : {}),\n ...(uvs2 ? { uvs2: uvs2.slice() } : {}),\n ...(tangents ? { tangents: tangents.slice() } : {}),\n ...(colors ? { colors: colors.slice() } : {}),\n };\n}\n"],"names":[],"mappings":"AAIO,SAAS,gBAAgB,IAAA,EAQvB;AACL,EAAA,MAAM,YAAY,IAAA,CAAK,aAAA;AACvB,EAAA,MAAM,UAAU,IAAA,CAAK,WAAA;AACrB,EAAA,MAAM,UAAU,IAAA,CAAK,WAAA;AACrB,EAAA,IAAI,CAAC,SAAA,IAAa,CAAC,OAAA,IAAW,CAAC,OAAA,EAAS;AACpC,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,MAAM,MAAM,IAAA,CAAK,OAAA;AACjB,EAAA,MAAM,OAAO,IAAA,CAAK,QAAA;AAClB,EAAA,MAAM,WAAW,IAAA,CAAK,YAAA;AACtB,EAAA,MAAM,SAAS,IAAA,CAAK,UAAA;AACpB,EAAA,OAAO;AAAA,IACH,SAAA,EAAW,UAAU,KAAA,EAAM;AAAA,IAC3B,OAAA,EAAS,QAAQ,KAAA,EAAM;AAAA,IACvB,OAAA,EAAS,QAAQ,KAAA,EAAM;AAAA,IACvB,GAAI,MAAM,EAAE,GAAA,EAAK,IAAI,KAAA,EAAM,KAAM,EAAC;AAAA,IAClC,GAAI,OAAO,EAAE,IAAA,EAAM,KAAK,KAAA,EAAM,KAAM,EAAC;AAAA,IACrC,GAAI,WAAW,EAAE,QAAA,EAAU,SAAS,KAAA,EAAM,KAAM,EAAC;AAAA,IACjD,GAAI,SAAS,EAAE,MAAA,EAAQ,OAAO,KAAA,EAAM,KAAM;AAAC,GAC/C;AACJ;;;;"}
@@ -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
- ctx._beforeRender.push((deltaMs) => {
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;
@@ -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 // 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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"}
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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"}
@@ -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, mesh) {
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babylonjs/lite",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "A lightweight, tree-shakable, WebGPU-first rendering library derived from Babylon.js.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://doc.babylonjs.com/lite/",
@@ -34,7 +34,7 @@
34
34
  }
35
35
  },
36
36
  "babylonLiteRelease": {
37
- "azureBuildId": "56431",
38
- "sourceVersion": "cc7a45f9ac8732b65f7fd37d5d66ef26f7a86df5"
37
+ "azureBuildId": "56473",
38
+ "sourceVersion": "fffc8e67aa3c941fc391417a6ba24ca9f9346938"
39
39
  }
40
40
  }