@babylonjs/lite 1.9.0 → 1.10.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/index.js +596 -526
  3. package/dist/index.js.map +1 -1
  4. package/index.d.ts +107 -4
  5. package/lib/engine/engine.js +1 -1
  6. package/lib/engine/engine.js.map +1 -1
  7. package/lib/frame-graph/render-task.js +6 -4
  8. package/lib/frame-graph/render-task.js.map +1 -1
  9. package/lib/index.js +4 -1
  10. package/lib/index.js.map +1 -1
  11. package/lib/loader-gltf/gltf-ext-quantization.js +4 -1
  12. package/lib/loader-gltf/gltf-ext-quantization.js.map +1 -1
  13. package/lib/loader-gltf/gltf-feature-registry.js +11 -1
  14. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  15. package/lib/loader-gltf/gltf-feature-variants.js +11 -1
  16. package/lib/loader-gltf/gltf-feature-variants.js.map +1 -1
  17. package/lib/loader-gltf/gltf-json-asset.js +8 -1
  18. package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
  19. package/lib/loader-gltf/gltf-material.js +4 -29
  20. package/lib/loader-gltf/gltf-material.js.map +1 -1
  21. package/lib/loader-gltf/gltf-parser.js +1 -8
  22. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  23. package/lib/loader-gltf/gltf-pbr-builder.js +1 -15
  24. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  25. package/lib/loader-gltf/gltf-variants.js +5 -10
  26. package/lib/loader-gltf/gltf-variants.js.map +1 -1
  27. package/lib/loader-gltf/load-gltf.js +19 -29
  28. package/lib/loader-gltf/load-gltf.js.map +1 -1
  29. package/lib/material/pbr/background-dds-skybox.js +35 -93
  30. package/lib/material/pbr/background-dds-skybox.js.map +1 -1
  31. package/lib/material/pbr/background-ground.js +13 -18
  32. package/lib/material/pbr/background-ground.js.map +1 -1
  33. package/lib/material/pbr/background-hdr-skybox.js +35 -93
  34. package/lib/material/pbr/background-hdr-skybox.js.map +1 -1
  35. package/lib/material/pbr/background-solid-skybox.js +35 -93
  36. package/lib/material/pbr/background-solid-skybox.js.map +1 -1
  37. package/lib/material/pbr/fragments/clearcoat-fragment.js +5 -1
  38. package/lib/material/pbr/fragments/clearcoat-fragment.js.map +1 -1
  39. package/lib/material/pbr/fragments/iridescence-fragment.js +4 -1
  40. package/lib/material/pbr/fragments/iridescence-fragment.js.map +1 -1
  41. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +7 -2
  42. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  43. package/lib/material/pbr/fragments/sheen-fragment.js +2 -1
  44. package/lib/material/pbr/fragments/sheen-fragment.js.map +1 -1
  45. package/lib/material/pbr/fragments/subsurface-fragment.js +2 -1
  46. package/lib/material/pbr/fragments/subsurface-fragment.js.map +1 -1
  47. package/lib/material/pbr/fragments/unlit-fragment.js +1 -2
  48. package/lib/material/pbr/fragments/unlit-fragment.js.map +1 -1
  49. package/lib/material/pbr/pbr-compose.js +2 -0
  50. package/lib/material/pbr/pbr-compose.js.map +1 -1
  51. package/lib/material/pbr/pbr-flag-bits.js +1 -16
  52. package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
  53. package/lib/material/pbr/pbr-flags.js +1 -1
  54. package/lib/material/pbr/pbr-fog-wgsl.js +1 -1
  55. package/lib/material/pbr/pbr-fog-wgsl.js.map +1 -1
  56. package/lib/material/pbr/pbr-geometry-output-shader.js +2 -1
  57. package/lib/material/pbr/pbr-geometry-output-shader.js.map +1 -1
  58. package/lib/material/pbr/pbr-geometry-view.js +2 -1
  59. package/lib/material/pbr/pbr-geometry-view.js.map +1 -1
  60. package/lib/material/pbr/pbr-renderable.js +4 -0
  61. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  62. package/lib/material/pbr/pbr-template-gamma.js +7 -0
  63. package/lib/material/pbr/pbr-template-gamma.js.map +1 -0
  64. package/lib/material/pbr/pbr-template.js +2 -2
  65. package/lib/material/pbr/pbr-template.js.map +1 -1
  66. package/lib/material/standard/skybox-cubemap.js +1 -1
  67. package/lib/material/standard/skybox-cubemap.js.map +1 -1
  68. package/lib/material/standard/standard-template.js +1 -1
  69. package/lib/material/standard/standard-template.js.map +1 -1
  70. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js +3 -5
  71. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js.map +1 -1
  72. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js +3 -5
  73. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js.map +1 -1
  74. package/lib/particle/node/blocks/particle-condition-block.js +55 -0
  75. package/lib/particle/node/blocks/particle-condition-block.js.map +1 -0
  76. package/lib/particle/node/blocks/particle-float-to-int-block.js +28 -0
  77. package/lib/particle/node/blocks/particle-float-to-int-block.js.map +1 -0
  78. package/lib/particle/node/blocks/particle-gradient-block.js +32 -0
  79. package/lib/particle/node/blocks/particle-gradient-block.js.map +1 -0
  80. package/lib/particle/node/blocks/particle-gradient-value-block.js +11 -0
  81. package/lib/particle/node/blocks/particle-gradient-value-block.js.map +1 -0
  82. package/lib/particle/node/blocks/particle-lerp-block.js +2 -24
  83. package/lib/particle/node/blocks/particle-lerp-block.js.map +1 -1
  84. package/lib/particle/node/blocks/particle-lerp.js +27 -0
  85. package/lib/particle/node/blocks/particle-lerp.js.map +1 -0
  86. package/lib/particle/node/blocks/particle-vector-length-block.js +30 -0
  87. package/lib/particle/node/blocks/particle-vector-length-block.js.map +1 -0
  88. package/lib/particle/node/blocks/system-block.js +13 -3
  89. package/lib/particle/node/blocks/system-block.js.map +1 -1
  90. package/lib/particle/node/blocks/texture-source-block.js +2 -1
  91. package/lib/particle/node/blocks/texture-source-block.js.map +1 -1
  92. package/lib/particle/node/blocks/update-size-block.js +19 -0
  93. package/lib/particle/node/blocks/update-size-block.js.map +1 -0
  94. package/lib/particle/node/npe-build-state.js +10 -0
  95. package/lib/particle/node/npe-build-state.js.map +1 -1
  96. package/lib/particle/node/npe-registry.js +12 -0
  97. package/lib/particle/node/npe-registry.js.map +1 -1
  98. package/lib/particle/particle-system.js +7 -2
  99. package/lib/particle/particle-system.js.map +1 -1
  100. package/lib/picking/billboard-pick-pipeline.js +269 -0
  101. package/lib/picking/billboard-pick-pipeline.js.map +1 -0
  102. package/lib/picking/gpu-picker.js +37 -37
  103. package/lib/picking/gpu-picker.js.map +1 -1
  104. package/lib/picking/gs-picking-pipeline.js +26 -1
  105. package/lib/picking/gs-picking-pipeline.js.map +1 -1
  106. package/lib/picking/pick-contributor.js +13 -0
  107. package/lib/picking/pick-contributor.js.map +1 -0
  108. package/lib/picking/picking-info.js.map +1 -1
  109. package/lib/scene/scene-core.js +2 -2
  110. package/lib/scene/scene-core.js.map +1 -1
  111. package/lib/shader/wgsl-fog.js +18 -0
  112. package/lib/shader/wgsl-fog.js.map +1 -0
  113. package/lib/shader/wgsl-helpers.js +1 -16
  114. package/lib/shader/wgsl-helpers.js.map +1 -1
  115. package/lib/sprite/billboard-scene.js +2 -0
  116. package/lib/sprite/billboard-scene.js.map +1 -1
  117. package/lib/sprite/picking/pick-billboard.js +16 -0
  118. package/lib/sprite/picking/pick-billboard.js.map +1 -0
  119. package/lib/sprite/picking/pick-sprite-2d.js +41 -0
  120. package/lib/sprite/picking/pick-sprite-2d.js.map +1 -0
  121. package/lib/sprite/shared/sprite-atlas.js +4 -1
  122. package/lib/sprite/shared/sprite-atlas.js.map +1 -1
  123. package/lib/sprite/sprite-2d.js.map +1 -1
  124. package/lib/sprite/sprite-pipeline.js +18 -1
  125. package/lib/sprite/sprite-pipeline.js.map +1 -1
  126. package/lib/sprite/sprite-renderer.js +12 -6
  127. package/lib/sprite/sprite-renderer.js.map +1 -1
  128. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"render-task.js","sources":["../../../src/frame-graph/render-task.ts"],"sourcesContent":["/**\n * RenderTask — a frame-graph task that records a single `RenderPass`,\n * binds the scene's `RenderTarget`, and draws renderables into it.\n *\n * - `record()` builds bucketed `DrawBinding` lists from `_renderables`\n * (opaque / direct / transparent), sorts opaque + direct by\n * `order`, then creates a `RenderPass` wired to the task's render target.\n * The pass owns its\n * `GPURenderPassDescriptor` and the per-pass-encoder body lives in a\n * closure passed to `setRenderPassExecuteFunc`.\n * - Before `RenderPass._execute()` begins the GPU pass: writes the scene UBO, refreshes lights, updates\n * per-binding UBOs, mirrors live `scene.clearColor` + `clr` onto the\n * render pass. Shared task execution then calls `_execute()`. The\n * `RenderPass` itself patches the swapchain view + clearColor + loadOp\n * and brackets the body with `beginRenderPass` / `end`.\n *\n * Renderable population:\n * - Explicit: push into `_renderables` directly, or `addMesh(mesh, opts)`\n * which builds a (mesh, material) Renderable at `record()` time.\n * - Auto scene mirror: when `_renderables` is empty at record() time, copy the\n * scene's renderables. Re-sync happens automatically when the scene's\n * `_renderableVersion` changes between frames (mesh add/remove, material swap).\n *\n * The engine `scRT` is just another `RenderTarget` here: a task that\n * targets it (`rt`) or resolves into it (`rst`) re-reads its per-frame color view\n * at execute time (the swap texture is re-acquired each frame). `clr: false`\n * switches color + depth `loadOp` to `\"load\"` so multiple scenes can share the\n * swapchain in one frame (e.g., a 3D scene + a UI overlay scene).\n */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { _vis } from \"../engine/engine.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { Renderable, DrawBinding, DrawUpdateContext } from \"../render/renderable.js\";\nimport type { RenderTargetSignature } from \"../engine/render-target.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\nimport type { Material } from \"../material/material.js\";\nimport type { RenderTarget } from \"../engine/render-target.js\";\nimport { buildRenderTarget, disposeRenderTarget } from \"../engine/render-target.js\";\nimport { getViewMatrix } from \"../camera/camera.js\";\nimport { getSceneBindGroupLayout } from \"../render/scene-helpers.js\";\nimport { _packSceneUniforms } from \"./scene-uniforms-pack.js\";\nimport { createEmptyUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport { SCENE_UBO_BYTES } from \"../shader/scene-uniforms-size.js\";\nimport { ensureSceneLightState, refreshSceneLightsUBO } from \"../render/lights-ubo.js\";\nimport type { Task } from \"./task.js\";\n\n/** Configuration for `createRenderTask`: render target, clear state, optional camera override, and transmission settings. */\nexport interface RenderTaskConfig {\n name: string;\n /** TODO: rt should not live in this config long-term. Until texture\n * management is virtualized, callers must provide the concrete target; once\n * virtualized, the task should create/manage its own render target. */\n rt: RenderTarget;\n /** Optional single-sample resolve target. When `rt` is multisampled\n * (`sampleCount > 1`), the color attachment resolves into this target's\n * color texture at end-of-pass — letting an MSAA render feed a post-process\n * that requires a single-sample source, without an extra resolve pass.\n * Caller contract (not validated): must be single-sample with a color\n * format and size matching `rt`; WebGPU errors at pass-encode time if not.\n * Ignored when `rt` is single-sample. */\n rst?: RenderTarget;\n /** Optional separate depth/stencil attachment. The pass binds this target's\n * depth view instead of `rt`'s own, and uses its `depthStencilFormat` for\n * pipeline signature matching. The colour `rt` must omit `depthStencilFormat`\n * (so it allocates no internal depth) and match this target in size + sample\n * count. Two ownership modes, distinguished by `_eager`:\n * - `_eager` depth (e.g. a `GeometryRendererTask` output): the task neither\n * builds nor clears nor disposes it — it loads it (`loadOp: \"load\"`) and the\n * caller owns clearing. This is how scenes reuse a pre-rendered depth buffer.\n * - non-`_eager` depth: the task owns it — builds/rebuilds it in `record()`,\n * clears it (`loadOp: \"clear\"`), and disposes it. Used by the default\n * single-sample scene task, whose colour `rt` is the depth-less\n * engine `scRT`. */\n depth?: RenderTarget;\n /** Background clear color. May be mutated frame-to-frame. */\n clrColor?: GPUColorDict;\n /** When true, color `loadOp` is \"clear\"; when false, \"load\" (overlays previous\n * color content). Depth is always cleared when rt-owned and always loaded when\n * supplied via `depth`. */\n clr?: boolean;\n /** Per-pass camera override. Null/undefined uses `scene.camera`. */\n cam?: Camera | null;\n /** Use canvas dimensions, not render-target dimensions, for this pass's scene UBO aspect. */\n cs?: boolean;\n /** Scene-texture transmission settings. `copyCount: 0` copies before every transmissive draw.\n * `generateMipmaps: false` allocates only mip 0 for the refraction texture and skips mip generation.\n * `mipLevelCount` caps the generated chain when a material only samples low explicit LODs.\n * `grabDepth: true` also snapshots the task's DEPTH attachment at the same mid-pass grab (see\n * `TransmissionOptions.grabDepth`). */\n transmission?: { copyCount?: number; generateMipmaps?: boolean; mipLevelCount?: number; grabDepth?: boolean };\n}\n\n/** A frame-graph task that records a single `RenderPass`, binds the scene's `RenderTarget`, and draws renderables into it. */\nexport interface RenderTask extends Task {\n readonly name: string;\n /** Render tasks are scene-bound because they consume scene camera, lights, and renderables. */\n readonly scene: SceneContext;\n /** Live task configuration. Mutating `clr` or `clrColor` affects subsequent frames. */\n /** @internal */\n readonly _config: RenderTaskConfig;\n /** @internal */\n _autoFromScene: boolean;\n\n /** Source-of-truth renderables. Bucketed binding lists below are derived from\n * this list at `record()` (or re-sync when auto-filled and `_renderableVersion` changes). */\n /** @internal */\n _renderables: Renderable[];\n /** @internal */\n _opaqueBindings: DrawBinding[];\n /** @internal */\n _directBindings: DrawBinding[];\n /** @internal */\n _transparentBindings: DrawBinding[];\n /** Cached opaque render bundle. Invalidated by renderable list mutations\n * (`_lastVersion`) and visibility changes (`_lastVis`). */\n /** @internal */\n _opaqueBundles: GPURenderBundle[];\n /** @internal */\n _lastVersion: number;\n /** @internal */\n _lastVis: number;\n /** @internal True once `record()` has run — the task is \"live\" (GPU target allocated, material batch builders\n * present). A runtime `addMesh` after this resolves + re-buckets THIS task immediately (no frame-graph rebuild). */\n _recorded: boolean;\n\n /** @internal */\n _renderPassDescriptor: GPURenderPassDescriptor;\n /** @internal */\n _colorAttachment: GPURenderPassColorAttachment;\n /** @internal External depth source from `config.depth`. When unset,\n * the pass uses `config.rt._depthView`. */\n _depthSrc?: RenderTarget;\n /** @internal External depth/stencil `loadOp` (\"load\" when `config.depth` is\n * set). When unset, defaults to `\"clear\"`. */\n _depthLoadOp?: GPULoadOp;\n\n /** Per-task scene UBO + bind group. Created eagerly in createRenderTask\n * so renderables can reference `_sceneBG` at `bind()` time. Written each\n * frame by `writePassSceneUBO`. Destroyed in `dispose()`. */\n /** @internal */\n _sceneUBO: GPUBuffer;\n /** @internal */\n _sceneBG: GPUBindGroup;\n /** @internal */\n _lightsUBO: GPUBuffer;\n /** @internal */\n _suData: Float32Array;\n /** @internal */\n _su: unknown[];\n /** Optional transmission-enabled execute path: copies the scene texture for refraction and draws transmissive\n * renderables. Present only when the task was configured with `transmission`. Returns the number of draw calls issued. */\n /** @internal */\n _executeWithTransmission?(sampleCount: number): number;\n /** @internal */\n _targetSignature: RenderTargetSignature;\n\n /** Add a mesh to this task's explicit render list with an optional per-pass material override. Resolved via\n * `material._buildGroup._rebuildSingle`, so the mesh's material family must already have been registered with the\n * scene (its batch builder has run). BEFORE the first `record()` the add is QUEUED and drained at record() time;\n * AFTER (a live/runtime add) it is resolved + re-bucketed into THIS task immediately, so the mesh renders on the\n * next frame without a `frameGraph.build()` (which would re-allocate shared GPU resources across all tasks). */\n addMesh(mesh: Mesh, opts?: { material?: Material }): void;\n /** @internal */\n _pendingMeshes: { mesh: Mesh; material: Material }[];\n}\n\ninterface MutableDrawUpdateContext {\n targetWidth: number;\n targetHeight: number;\n _camera?: Camera | null;\n}\n\n/** Create a render pass task. GPU resources (target textures + descriptor)\n * are not allocated until `record()` runs (via `frameGraph.build()`).\n *\n * Swapchain-targeted tasks acquire the swap view per-frame at execute time. */\nexport function createRenderTask(config: RenderTaskConfig, engine: EngineContext, scene: SceneContext): RenderTask {\n const sc = scene as SceneContext;\n config.clrColor ??= { r: 0.2, g: 0.2, b: 0.3, a: 1.0 };\n config.clr ??= true;\n const desc = config.rt._descriptor;\n // Render upright: row 0 of the GPU texture is the top of the scene. Every\n // RT (offscreen or swapchain) renders without a projection Y-flip; pipelines\n // use the default ccw front face; downstream samplers see upright pixels.\n const targetSignature = {\n _colorFormat: desc.format,\n _depthStencilFormat: config.depth?._descriptor.dFormat ?? desc.dFormat,\n _depthCompare: desc._depthCompare,\n _sampleCount: desc.samples ?? 1,\n };\n\n const sceneBGL = getSceneBindGroupLayout(engine);\n const sceneUBO = createEmptyUniformBuffer(engine, SCENE_UBO_BYTES);\n const lightsUBO = ensureSceneLightState(engine, sc)._buffer;\n const sceneBG = engine._device.createBindGroup({\n layout: sceneBGL,\n entries: [\n { binding: 0, resource: { buffer: sceneUBO } },\n { binding: 1, resource: { buffer: lightsUBO } },\n ],\n });\n const colorAttachment = { loadOp: \"clear\", storeOp: \"store\" } as GPURenderPassColorAttachment;\n const updateContext: MutableDrawUpdateContext = { targetWidth: 0, targetHeight: 0 };\n const task: RenderTask = {\n name: config.name,\n _config: config,\n engine: engine,\n scene: sc,\n _passes: [],\n _autoFromScene: false,\n _renderables: [],\n _opaqueBindings: [],\n _directBindings: [],\n _transparentBindings: [],\n _opaqueBundles: [],\n _lastVersion: -1,\n _lastVis: 0,\n _recorded: false,\n _renderPassDescriptor: { colorAttachments: [colorAttachment] },\n _colorAttachment: colorAttachment,\n _depthSrc: config.depth,\n _depthLoadOp: config.depth ? (config.depth._eager ? \"load\" : \"clear\") : undefined,\n _sceneUBO: sceneUBO,\n _sceneBG: sceneBG,\n _lightsUBO: lightsUBO,\n _suData: new F32(SCENE_UBO_BYTES / 4),\n _su: [],\n _targetSignature: targetSignature,\n _pendingMeshes: [],\n addMesh(mesh, opts) {\n const material = opts?.material ?? mesh.material;\n if (!material) {\n return;\n }\n task._pendingMeshes.push({ mesh, material });\n if (task._recorded) {\n // Live (post-record) add: resolve + re-bucket THIS task now — buildBindings clears its bundle cache\n // so it re-records on the next execute. Deliberately NOT a frame-graph rebuild (that re-allocates the\n // shared scene UBO + every task's render target mid-frame and crashes the in-flight submit).\n resolvePendingMeshes(task, sc);\n // A live add makes the render list explicit (mirrors record(), where a pending mesh forces\n // _autoFromScene = false). Without this, an auto-mirroring task's next scene-version resync in\n // prepareRenderTaskPass would clear _renderables and drop the just-added mesh.\n task._autoFromScene = false;\n buildBindings(task, engine, targetSignature);\n }\n },\n record(): void {\n if (task._autoFromScene) {\n task._renderables.length = 0;\n }\n resolvePendingMeshes(task, sc);\n task._autoFromScene = task._renderables.length === 0;\n if (task._autoFromScene) {\n task._renderables.push(...sc._renderables);\n }\n // Read config.rt dynamically — transmission retargeting swaps it after\n // the task is created, and the engine scRT must never be rebuilt.\n const rt = config.rt;\n buildRenderTarget(rt, engine);\n if (config.rst && (rt._descriptor.samples ?? 1) > 1) {\n buildRenderTarget(config.rst, engine);\n }\n // A non-eager external depth (e.g. the default single-sample scene task's\n // depth, whose colour rt is the depth-less scRT) is task-managed:\n // build/rebuild it here. Eager depths (GeometryRendererTask outputs) are\n // pre-built and skipped by buildRenderTarget.\n if (config.depth && !config.depth._eager) {\n buildRenderTarget(config.depth, engine);\n }\n updateContext.targetWidth = rt._width;\n updateContext.targetHeight = rt._height;\n refreshTaskSceneBindGroup(task, engine);\n buildBindings(task, engine, targetSignature);\n buildRenderPassDescriptor(task, rt);\n task._recorded = true; // task is now live — a subsequent addMesh resolves + re-buckets immediately\n },\n execute(): number {\n return executePass(task, engine, targetSignature, updateContext);\n },\n dispose(): void {\n task._passes.length = 0;\n // disposeRenderTarget no-ops on the engine scRT and on eager\n // GeometryRendererTask depth outputs (both `_eager`), and on an undefined\n // rst/depth — so these can be passed unconditionally.\n disposeRenderTarget(config.rt);\n disposeRenderTarget(config.rst);\n disposeRenderTarget(config.depth);\n task._opaqueBindings.length = 0;\n task._directBindings.length = 0;\n task._transparentBindings.length = 0;\n task._renderables.length = 0;\n task._opaqueBundles.length = 0;\n task._sceneUBO.destroy();\n },\n };\n return task;\n}\n\n/** Remove a mesh from this task's renderable + binding lists. Idempotent. */\nexport function removeMeshFromTask(task: RenderTask, mesh: object): void {\n // Not a renderable-bearing task (e.g. a post/effect task that also carries `_config`): nothing to\n // remove. Guard keeps callers that scan all frame-graph tasks (removeFromScene) shape-safe.\n if (!task._renderables) {\n return;\n }\n let removed = false;\n for (let i = task._renderables.length - 1; i >= 0; i--) {\n if (task._renderables[i]!.mesh === mesh) {\n task._renderables.splice(i, 1);\n removed = true;\n }\n }\n for (const arr of [task._opaqueBindings, task._directBindings, task._transparentBindings]) {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (arr[i]!.renderable.mesh === mesh) {\n arr.splice(i, 1);\n removed = true;\n }\n }\n }\n if (removed) {\n task._opaqueBundles.length = 0;\n task._lastVersion = -1;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction resolvePendingMeshes(task: RenderTask, sc: SceneContext): void {\n if (task._pendingMeshes.length === 0) {\n return;\n }\n for (const { mesh, material } of task._pendingMeshes) {\n const rebuild = material._buildGroup?._rebuildSingle;\n if (!rebuild) {\n throw new Error();\n }\n const renderable = rebuild(sc, mesh, material);\n if (!task._renderables.includes(renderable)) {\n task._renderables.push(renderable);\n }\n }\n task._pendingMeshes.length = 0;\n}\n\n/** Per-frame back-to-front sort for transparent bindings using the active camera. */\nfunction sortTransparentBindings(task: RenderTask, camera: Camera | null | undefined): void {\n const arr = task._transparentBindings;\n if (arr.length <= 1 || !camera) {\n return;\n }\n const v = getViewMatrix(camera);\n for (const b of arr) {\n const wc = b.renderable._worldCenter;\n b._sortDistance = wc ? wc[0]! * v[2]! + wc[1]! * v[6]! + wc[2]! * v[10]! + v[14]! : 0;\n }\n arr.sort((a, b) => b._sortDistance! - a._sortDistance! || a.renderable.order - b.renderable.order);\n}\n\n/** (Re)bucket task._renderables into bound lists. */\nfunction buildBindings(task: RenderTask, eng: EngineContext, targetSignature: RenderTargetSignature): void {\n const opaque = task._opaqueBindings;\n const direct = task._directBindings;\n const transparent = task._transparentBindings;\n opaque.length = 0;\n direct.length = 0;\n transparent.length = 0;\n for (const r of task._renderables) {\n const binding = r.bind(eng, targetSignature);\n if (r.isTransparent || r._transmissive) {\n transparent.push(binding);\n } else if (r._direct) {\n direct.push(binding);\n } else {\n opaque.push(binding);\n }\n }\n opaque.sort((a, b) => a.renderable.order - b.renderable.order);\n direct.sort((a, b) => a.renderable.order - b.renderable.order);\n task._opaqueBundles.length = 0;\n task._lastVersion = (task.scene as SceneContext)._renderableVersion;\n}\n\nfunction buildRenderPassDescriptor(task: RenderTask, rt: RenderTarget): void {\n const att = task._colorAttachment;\n att.view = rt._colorView!;\n // End-of-pass MSAA resolve into a caller-supplied single-sample target.\n // record() only builds the target's color view for an MSAA rt, so its\n // presence is the gate. The swapchain case is wired per-frame in\n // executePass (its view changes each frame); this custom view is stable.\n att.resolveTarget = task._config.rst?._colorView ?? undefined;\n task._renderPassDescriptor.colorAttachments = rt._colorView ? [att] : [];\n\n const depthSrc = task._depthSrc ?? rt;\n const depthView = depthSrc._depthView;\n let depthAttachment: GPURenderPassDepthStencilAttachment | undefined;\n if (depthView) {\n const dd = depthSrc._descriptor;\n const loadOp = task._depthLoadOp ?? \"clear\";\n depthAttachment = {\n view: depthView,\n depthClearValue: dd._depthClearValue ?? 0,\n depthLoadOp: loadOp,\n depthStoreOp: \"store\",\n };\n if (dd.dFormat?.includes(\"stencil\")) {\n depthAttachment.stencilClearValue = 0;\n depthAttachment.stencilLoadOp = loadOp;\n depthAttachment.stencilStoreOp = \"store\";\n }\n }\n\n task._renderPassDescriptor.depthStencilAttachment = depthAttachment;\n}\n\nfunction prepareRenderTaskPass(task: RenderTask, eng: EngineContext, targetSignature: RenderTargetSignature, context: DrawUpdateContext): void {\n const sc = task.scene as SceneContext;\n // Auto-resync when the source scene mutates.\n if (task._autoFromScene && task._lastVersion !== sc._renderableVersion) {\n task._renderables.length = 0;\n task._renderables.push(...sc._renderables);\n buildBindings(task, eng, targetSignature);\n }\n\n // Pre-pass work — runs before beginRenderPass. Updates the task-owned scene\n // UBO, scene-wide lights UBO, and per-binding UBOs. The scene bind group may\n // also need a refresh (lights buffer can be resized when glTF lights\n // extension raises MAX_LIGHTS after this task was first recorded).\n refreshTaskSceneBindGroup(task, eng);\n const camera = task._config.cam ?? sc.camera;\n sc._clusteredLightUpdater?.(camera, context.targetWidth, context.targetHeight);\n writePassSceneUBO(task, eng, sc, camera);\n refreshSceneLightsUBO(eng, sc);\n // Expose the active camera to per-binding `update()` calls. Some renderables\n // (e.g. transparent billboard systems) need it to compute view-space sort\n // depths during their update.\n (context as MutableDrawUpdateContext)._camera = camera;\n updateBindings(task._opaqueBindings, context);\n updateBindings(task._directBindings, context);\n updateBindings(task._transparentBindings, context);\n // Per-frame back-to-front sort for transparent bindings — must run AFTER\n // updateBindings so renderables that compute `_worldCenter` inside their\n // own `update()` (billboard systems) are seen with current values.\n sortTransparentBindings(task, camera);\n}\n\nfunction executePass(task: RenderTask, eng: EngineContext, targetSignature: RenderTargetSignature, context: DrawUpdateContext): number {\n const sc = task.scene;\n const sampleCount = targetSignature._sampleCount;\n prepareRenderTaskPass(task, eng, targetSignature, context);\n const att = task._colorAttachment;\n const cfg = task._config;\n if (cfg.rt._colorView) {\n // The engine scRT's color view is re-acquired every frame, so re-read\n // it here. Offscreen color views are stable between rebuilds — leaving att.view\n // untouched preserves an external override (swapchain-overlay shares the base\n // scene's MSAA color view). The resolve target (rst) is re-read each frame so an\n // `rst === scRT` picks up its fresh per-frame view.\n if (cfg.rt === eng.scRT) {\n att.view = cfg.rt._colorView;\n }\n att.resolveTarget = cfg.rst?._colorView ?? undefined;\n att.clearValue = task._autoFromScene ? sc.clearColor : cfg.clrColor!;\n att.loadOp = cfg.clr ? \"clear\" : \"load\";\n }\n if (task._executeWithTransmission) {\n return task._executeWithTransmission(sampleCount);\n }\n const pass = eng._currentEncoder.beginRenderPass(task._renderPassDescriptor);\n const draws = executePassBody(task, pass);\n pass.end();\n return draws;\n}\n\n/** Body of the registered `RenderPass`. Receives the live render-pass encoder\n * and issues all draws (viewport/scissor, group(0) bind, opaque bundle replay,\n * then direct-draws non-transparent direct + transparent). Returns the draw count. */\nfunction executePassBody(task: RenderTask, pass: GPURenderPassEncoder): number {\n const eng = task.engine as EngineContext;\n const cfg = task._config;\n const rt = cfg.rt;\n const scene = task.scene as SceneContext;\n const opaqueBindings = task._opaqueBindings;\n const opaqueBundles = task._opaqueBundles;\n const sceneBG = task._sceneBG;\n\n const camera = cfg.cam ?? scene.camera;\n const v = camera?.viewport;\n if (v) {\n const rw = rt._width;\n const rh = rt._height;\n const x = Math.floor(v.x * rw);\n const y = Math.floor((1 - v.y - v.height) * rh);\n const w = Math.ceil((v.x + v.width) * rw) - x;\n const h = Math.ceil((1 - v.y) * rh) - y;\n pass.setViewport(x, y, w, h, 0, 1);\n pass.setScissorRect(x, y, w, h);\n }\n // Scene bind group (group 0) is task-owned and identical for every draw in this pass.\n pass.setBindGroup(0, sceneBG);\n\n // Opaque: cached render bundle. Invalidated by scene mutation (_renderableVersion)\n // or visibility version (_vis). The bundle records group(0) at its start so it can\n // be replayed standalone (executeBundles inherits no inherited state).\n if (task._lastVersion !== scene._renderableVersion || task._lastVis !== _vis || opaqueBundles.length === 0) {\n const desc = rt._descriptor;\n const be = eng._device.createRenderBundleEncoder({\n colorFormats: desc.format ? [desc.format] : [],\n // Use the task's target signature, not the RT descriptor: a depth\n // override (config.depth) supplies the depth format externally, so\n // the cached opaque pipelines are built with it while the colour RT\n // carries no depthStencilFormat of its own. The bundle encoder's\n // attachment state must match those pipelines exactly.\n depthStencilFormat: task._targetSignature._depthStencilFormat,\n sampleCount: desc.samples ?? 1,\n });\n be.setBindGroup(0, sceneBG);\n drawList(be, opaqueBindings, eng);\n opaqueBundles[0] = be.finish();\n task._lastVersion = scene._renderableVersion;\n task._lastVis = _vis;\n }\n let draws = opaqueBindings.length;\n pass.executeBundles(opaqueBundles);\n // executeBundles invalidates pass bind-group state — rebind group 0 before further draws.\n pass.setBindGroup(0, sceneBG);\n draws += drawList(pass, task._directBindings, eng);\n draws += drawList(pass, task._transparentBindings, eng);\n return draws;\n}\n\nfunction refreshTaskSceneBindGroup(task: RenderTask, eng: EngineContext): void {\n const lightsUBO = ensureSceneLightState(eng, task.scene as SceneContext)._buffer;\n if (lightsUBO === task._lightsUBO) {\n return;\n }\n task._lightsUBO = lightsUBO;\n task._sceneBG = eng._device.createBindGroup({\n layout: getSceneBindGroupLayout(eng),\n entries: [\n { binding: 0, resource: { buffer: task._sceneUBO } },\n { binding: 1, resource: { buffer: lightsUBO } },\n ],\n });\n task._opaqueBundles.length = 0;\n task._lastVersion = -1;\n}\n\n/** Write the canonical SceneUniforms struct to the task-owned scene UBO.\n * Bails before touching scratch/GPU when all inputs are unchanged. */\nfunction writePassSceneUBO(task: RenderTask, eng: EngineContext, scene: SceneContext, camera: Camera | null): void {\n if (!camera) {\n return;\n }\n\n const v = camera.viewport;\n const rt = task._config.rt;\n const aspect = (task._config.cs ? eng.canvas.width / eng.canvas.height : rt._width / rt._height) * (v ? v.width / v.height : 1);\n const fog = scene.fog;\n const img = scene.imageProcessing;\n const envRotationY = scene.envRotationY || 0;\n const wv = camera.worldMatrixVersion;\n const s = task._su;\n if (s[0] === camera && s[1] === fog && s[2] === wv && s[3] === aspect && s[4] === envRotationY && s[5] === img.exposure && s[6] === img.contrast) {\n return;\n }\n s[0] = camera;\n s[1] = fog;\n s[2] = wv;\n s[3] = aspect;\n s[4] = envRotationY;\n s[5] = img.exposure;\n s[6] = img.contrast;\n\n const data = task._suData;\n _packSceneUniforms(data, eng, scene, camera, aspect);\n const contribs = scene._sceneUboContributors;\n if (contribs) {\n for (const c of contribs) {\n c(data, scene);\n }\n }\n eng._device.queue.writeBuffer(task._sceneUBO, 0, data as Float32Array<ArrayBuffer>);\n}\n\nfunction updateBindings(list: readonly DrawBinding[], context: DrawUpdateContext): void {\n for (const b of list) {\n b.update?.(context);\n }\n}\n\n/** Iterate DrawBindings, deduping setPipeline. */\nfunction drawList(enc: GPURenderPassEncoder | GPURenderBundleEncoder, list: readonly DrawBinding[], engine: EngineContext): number {\n let lp: GPURenderPipeline | null = null;\n let draws = 0;\n for (const b of list) {\n const mesh = b.renderable.mesh;\n if (mesh && mesh.visible === false) {\n continue;\n }\n if (b.pipeline !== lp) {\n enc.setPipeline(b.pipeline);\n lp = b.pipeline;\n }\n draws += b.draw(enc, engine);\n }\n return draws;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render-task.js","sources":["../../../src/frame-graph/render-task.ts"],"sourcesContent":["/**\n * RenderTask — a frame-graph task that records a single `RenderPass`,\n * binds the scene's `RenderTarget`, and draws renderables into it.\n *\n * - `record()` builds bucketed `DrawBinding` lists from `_renderables`\n * (opaque / direct / transparent), sorts opaque + direct by\n * `order`, then creates a `RenderPass` wired to the task's render target.\n * The pass owns its\n * `GPURenderPassDescriptor` and the per-pass-encoder body lives in a\n * closure passed to `setRenderPassExecuteFunc`.\n * - Before `RenderPass._execute()` begins the GPU pass: writes the scene UBO, refreshes lights, updates\n * per-binding UBOs, mirrors live `scene.clearColor` + `clr` onto the\n * render pass. Shared task execution then calls `_execute()`. The\n * `RenderPass` itself patches the swapchain view + clearColor + loadOp\n * and brackets the body with `beginRenderPass` / `end`.\n *\n * Renderable population:\n * - Explicit: push into `_renderables` directly, or `addMesh(mesh, opts)`\n * which builds a (mesh, material) Renderable at `record()` time.\n * - Auto scene mirror: when `_renderables` is empty at record() time, copy the\n * scene's renderables. Re-sync happens automatically when the scene's\n * `_renderableVersion` changes between frames (mesh add/remove, material swap).\n *\n * The engine `scRT` is just another `RenderTarget` here: a task that\n * targets it (`rt`) or resolves into it (`rst`) re-reads its per-frame color view\n * at execute time (the swap texture is re-acquired each frame). `clr: false`\n * switches color + depth `loadOp` to `\"load\"` so multiple scenes can share the\n * swapchain in one frame (e.g., a 3D scene + a UI overlay scene).\n */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { _vis } from \"../engine/engine.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { Renderable, DrawBinding, DrawUpdateContext } from \"../render/renderable.js\";\nimport type { RenderTargetSignature } from \"../engine/render-target.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\nimport type { Material } from \"../material/material.js\";\nimport type { RenderTarget } from \"../engine/render-target.js\";\nimport { buildRenderTarget, disposeRenderTarget } from \"../engine/render-target.js\";\nimport { getViewMatrix } from \"../camera/camera.js\";\nimport { getSceneBindGroupLayout } from \"../render/scene-helpers.js\";\nimport { _packSceneUniforms } from \"./scene-uniforms-pack.js\";\nimport { createEmptyUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport { SCENE_UBO_BYTES } from \"../shader/scene-uniforms-size.js\";\nimport { ensureSceneLightState, refreshSceneLightsUBO } from \"../render/lights-ubo.js\";\nimport type { Task } from \"./task.js\";\n\n/** Configuration for `createRenderTask`: render target, clear state, optional camera override, and transmission settings. */\nexport interface RenderTaskConfig {\n name: string;\n /** TODO: rt should not live in this config long-term. Until texture\n * management is virtualized, callers must provide the concrete target; once\n * virtualized, the task should create/manage its own render target. */\n rt: RenderTarget;\n /** Optional single-sample resolve target. When `rt` is multisampled\n * (`sampleCount > 1`), the color attachment resolves into this target's\n * color texture at end-of-pass — letting an MSAA render feed a post-process\n * that requires a single-sample source, without an extra resolve pass.\n * Caller contract (not validated): must be single-sample with a color\n * format and size matching `rt`; WebGPU errors at pass-encode time if not.\n * Ignored when `rt` is single-sample. */\n rst?: RenderTarget;\n /** Optional separate depth/stencil attachment. The pass binds this target's\n * depth view instead of `rt`'s own, and uses its `depthStencilFormat` for\n * pipeline signature matching. The colour `rt` must omit `depthStencilFormat`\n * (so it allocates no internal depth) and match this target in size + sample\n * count. Two ownership modes, distinguished by `_eager`:\n * - `_eager` depth (e.g. a `GeometryRendererTask` output): the task neither\n * builds nor clears nor disposes it — it loads it (`loadOp: \"load\"`) and the\n * caller owns clearing. This is how scenes reuse a pre-rendered depth buffer.\n * - non-`_eager` depth: the task owns it — builds/rebuilds it in `record()`,\n * clears it (`loadOp: \"clear\"`), and disposes it. Used by the default\n * single-sample scene task, whose colour `rt` is the depth-less\n * engine `scRT`. */\n depth?: RenderTarget;\n /** Background clear color. May be mutated frame-to-frame. */\n clrColor?: GPUColorDict;\n /** When true, color `loadOp` is \"clear\"; when false, \"load\" (overlays previous\n * color content). Depth is always cleared when rt-owned and always loaded when\n * supplied via `depth`. */\n clr?: boolean;\n /** Per-pass camera override. Null/undefined uses `scene.camera`. */\n cam?: Camera | null;\n /** Use canvas dimensions, not render-target dimensions, for this pass's scene UBO aspect. */\n cs?: boolean;\n /** Scene-texture transmission settings. `copyCount: 0` copies before every transmissive draw.\n * `generateMipmaps: false` allocates only mip 0 for the refraction texture and skips mip generation.\n * `mipLevelCount` caps the generated chain when a material only samples low explicit LODs.\n * `grabDepth: true` also snapshots the task's DEPTH attachment at the same mid-pass grab (see\n * `TransmissionOptions.grabDepth`). */\n transmission?: { copyCount?: number; generateMipmaps?: boolean; mipLevelCount?: number; grabDepth?: boolean };\n}\n\n/** A frame-graph task that records a single `RenderPass`, binds the scene's `RenderTarget`, and draws renderables into it. */\nexport interface RenderTask extends Task {\n readonly name: string;\n /** Render tasks are scene-bound because they consume scene camera, lights, and renderables. */\n readonly scene: SceneContext;\n /** Live task configuration. Mutating `clr` or `clrColor` affects subsequent frames. */\n /** @internal */\n readonly _config: RenderTaskConfig;\n /** @internal */\n _autoFromScene: boolean;\n\n /** Source-of-truth renderables. Bucketed binding lists below are derived from\n * this list at `record()` (or re-sync when auto-filled and `_renderableVersion` changes). */\n /** @internal */\n _renderables: Renderable[];\n /** @internal */\n _opaqueBindings: DrawBinding[];\n /** @internal */\n _directBindings: DrawBinding[];\n /** @internal */\n _transparentBindings: DrawBinding[];\n /** Cached opaque render bundle. Invalidated by renderable list mutations\n * (`_lastVersion`) and visibility changes (`_lastVis`). */\n /** @internal */\n _opaqueBundles: GPURenderBundle[];\n /** @internal */\n _lastVersion: number;\n /** @internal */\n _lastVis: number;\n /** @internal True once `record()` has run — the task is \"live\" (GPU target allocated, material batch builders\n * present). A runtime `addMesh` after this resolves + re-buckets THIS task immediately (no frame-graph rebuild). */\n _recorded: boolean;\n\n /** @internal */\n _renderPassDescriptor: GPURenderPassDescriptor;\n /** @internal */\n _colorAttachment: GPURenderPassColorAttachment;\n /** @internal External depth source from `config.depth`. When unset,\n * the pass uses `config.rt._depthView`. */\n _depthSrc?: RenderTarget;\n /** @internal External depth/stencil `loadOp` (\"load\" when `config.depth` is\n * set). When unset, defaults to `\"clear\"`. */\n _depthLoadOp?: GPULoadOp;\n\n /** Per-task scene UBO + bind group. Created eagerly in createRenderTask\n * so renderables can reference `_sceneBG` at `bind()` time. Written each\n * frame by `_writePassSceneUBO`. Destroyed in `dispose()`. */\n /** @internal */\n _sceneUBO: GPUBuffer;\n /** @internal */\n _sceneBG: GPUBindGroup;\n /** @internal */\n _lightsUBO: GPUBuffer;\n /** @internal */\n _suData: Float32Array;\n /** @internal */\n _su: unknown[];\n /** Optional transmission-enabled execute path: copies the scene texture for refraction and draws transmissive\n * renderables. Present only when the task was configured with `transmission`. Returns the number of draw calls issued. */\n /** @internal */\n _executeWithTransmission?(sampleCount: number): number;\n /** @internal */\n _targetSignature: RenderTargetSignature;\n\n /** Add a mesh to this task's explicit render list with an optional per-pass material override. Resolved via\n * `material._buildGroup._rebuildSingle`, so the mesh's material family must already have been registered with the\n * scene (its batch builder has run). BEFORE the first `record()` the add is QUEUED and drained at record() time;\n * AFTER (a live/runtime add) it is resolved + re-bucketed into THIS task immediately, so the mesh renders on the\n * next frame without a `frameGraph.build()` (which would re-allocate shared GPU resources across all tasks). */\n addMesh(mesh: Mesh, opts?: { material?: Material }): void;\n /** @internal */\n _pendingMeshes: { mesh: Mesh; material: Material }[];\n}\n\ninterface MutableDrawUpdateContext {\n targetWidth: number;\n targetHeight: number;\n _camera?: Camera | null;\n}\n\n/** Create a render pass task. GPU resources (target textures + descriptor)\n * are not allocated until `record()` runs (via `frameGraph.build()`).\n *\n * Swapchain-targeted tasks acquire the swap view per-frame at execute time. */\nexport function createRenderTask(config: RenderTaskConfig, engine: EngineContext, scene: SceneContext): RenderTask {\n const sc = scene as SceneContext;\n config.clrColor ??= { r: 0.2, g: 0.2, b: 0.3, a: 1.0 };\n config.clr ??= true;\n const desc = config.rt._descriptor;\n // Render upright: row 0 of the GPU texture is the top of the scene. Every\n // RT (offscreen or swapchain) renders without a projection Y-flip; pipelines\n // use the default ccw front face; downstream samplers see upright pixels.\n const targetSignature = {\n _colorFormat: desc.format,\n _depthStencilFormat: config.depth?._descriptor.dFormat ?? desc.dFormat,\n _depthCompare: desc._depthCompare,\n _sampleCount: desc.samples ?? 1,\n };\n\n const sceneBGL = getSceneBindGroupLayout(engine);\n const sceneUBO = createEmptyUniformBuffer(engine, SCENE_UBO_BYTES);\n const lightsUBO = ensureSceneLightState(engine, sc)._buffer;\n const sceneBG = engine._device.createBindGroup({\n layout: sceneBGL,\n entries: [\n { binding: 0, resource: { buffer: sceneUBO } },\n { binding: 1, resource: { buffer: lightsUBO } },\n ],\n });\n const colorAttachment = { loadOp: \"clear\", storeOp: \"store\" } as GPURenderPassColorAttachment;\n const updateContext: MutableDrawUpdateContext = { targetWidth: 0, targetHeight: 0 };\n const task: RenderTask = {\n name: config.name,\n _config: config,\n engine: engine,\n scene: sc,\n _passes: [],\n _autoFromScene: false,\n _renderables: [],\n _opaqueBindings: [],\n _directBindings: [],\n _transparentBindings: [],\n _opaqueBundles: [],\n _lastVersion: -1,\n _lastVis: 0,\n _recorded: false,\n _renderPassDescriptor: { colorAttachments: [colorAttachment] },\n _colorAttachment: colorAttachment,\n _depthSrc: config.depth,\n _depthLoadOp: config.depth ? (config.depth._eager ? \"load\" : \"clear\") : undefined,\n _sceneUBO: sceneUBO,\n _sceneBG: sceneBG,\n _lightsUBO: lightsUBO,\n _suData: new F32(SCENE_UBO_BYTES / 4),\n _su: [],\n _targetSignature: targetSignature,\n _pendingMeshes: [],\n addMesh(mesh, opts) {\n const material = opts?.material ?? mesh.material;\n if (!material) {\n return;\n }\n task._pendingMeshes.push({ mesh, material });\n if (task._recorded) {\n // Live (post-record) add: resolve + re-bucket THIS task now — buildBindings clears its bundle cache\n // so it re-records on the next execute. Deliberately NOT a frame-graph rebuild (that re-allocates the\n // shared scene UBO + every task's render target mid-frame and crashes the in-flight submit).\n resolvePendingMeshes(task, sc);\n // A live add makes the render list explicit (mirrors record(), where a pending mesh forces\n // _autoFromScene = false). Without this, an auto-mirroring task's next scene-version resync in\n // prepareRenderTaskPass would clear _renderables and drop the just-added mesh.\n task._autoFromScene = false;\n buildBindings(task, engine, targetSignature);\n }\n },\n record(): void {\n if (task._autoFromScene) {\n task._renderables.length = 0;\n }\n resolvePendingMeshes(task, sc);\n task._autoFromScene = task._renderables.length === 0;\n if (task._autoFromScene) {\n task._renderables.push(...sc._renderables);\n }\n // Read config.rt dynamically — transmission retargeting swaps it after\n // the task is created, and the engine scRT must never be rebuilt.\n const rt = config.rt;\n buildRenderTarget(rt, engine);\n if (config.rst && (rt._descriptor.samples ?? 1) > 1) {\n buildRenderTarget(config.rst, engine);\n }\n // A non-eager external depth (e.g. the default single-sample scene task's\n // depth, whose colour rt is the depth-less scRT) is task-managed:\n // build/rebuild it here. Eager depths (GeometryRendererTask outputs) are\n // pre-built and skipped by buildRenderTarget.\n if (config.depth && !config.depth._eager) {\n buildRenderTarget(config.depth, engine);\n }\n updateContext.targetWidth = rt._width;\n updateContext.targetHeight = rt._height;\n refreshTaskSceneBindGroup(task, engine);\n buildBindings(task, engine, targetSignature);\n buildRenderPassDescriptor(task, rt);\n task._recorded = true; // task is now live — a subsequent addMesh resolves + re-buckets immediately\n },\n execute(): number {\n return executePass(task, engine, targetSignature, updateContext);\n },\n dispose(): void {\n task._passes.length = 0;\n // disposeRenderTarget no-ops on the engine scRT and on eager\n // GeometryRendererTask depth outputs (both `_eager`), and on an undefined\n // rst/depth — so these can be passed unconditionally.\n disposeRenderTarget(config.rt);\n disposeRenderTarget(config.rst);\n disposeRenderTarget(config.depth);\n task._opaqueBindings.length = 0;\n task._directBindings.length = 0;\n task._transparentBindings.length = 0;\n task._renderables.length = 0;\n task._opaqueBundles.length = 0;\n task._sceneUBO.destroy();\n },\n };\n return task;\n}\n\n/** Remove a mesh from this task's renderable + binding lists. Idempotent. */\nexport function removeMeshFromTask(task: RenderTask, mesh: object): void {\n // Not a renderable-bearing task (e.g. a post/effect task that also carries `_config`): nothing to\n // remove. Guard keeps callers that scan all frame-graph tasks (removeFromScene) shape-safe.\n if (!task._renderables) {\n return;\n }\n let removed = false;\n for (let i = task._renderables.length - 1; i >= 0; i--) {\n if (task._renderables[i]!.mesh === mesh) {\n task._renderables.splice(i, 1);\n removed = true;\n }\n }\n for (const arr of [task._opaqueBindings, task._directBindings, task._transparentBindings]) {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (arr[i]!.renderable.mesh === mesh) {\n arr.splice(i, 1);\n removed = true;\n }\n }\n }\n if (removed) {\n task._opaqueBundles.length = 0;\n task._lastVersion = -1;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction resolvePendingMeshes(task: RenderTask, sc: SceneContext): void {\n if (task._pendingMeshes.length === 0) {\n return;\n }\n for (const { mesh, material } of task._pendingMeshes) {\n const rebuild = material._buildGroup?._rebuildSingle;\n if (!rebuild) {\n throw new Error();\n }\n const renderable = rebuild(sc, mesh, material);\n if (!task._renderables.includes(renderable)) {\n task._renderables.push(renderable);\n }\n }\n task._pendingMeshes.length = 0;\n}\n\n/** Per-frame back-to-front sort for transparent bindings using the active camera. */\nfunction sortTransparentBindings(task: RenderTask, camera: Camera | null | undefined): void {\n const arr = task._transparentBindings;\n if (arr.length <= 1 || !camera) {\n return;\n }\n const v = getViewMatrix(camera);\n for (const b of arr) {\n const wc = b.renderable._worldCenter;\n b._sortDistance = wc ? wc[0]! * v[2]! + wc[1]! * v[6]! + wc[2]! * v[10]! + v[14]! : 0;\n }\n arr.sort((a, b) => b._sortDistance! - a._sortDistance! || a.renderable.order - b.renderable.order);\n}\n\n/** (Re)bucket task._renderables into bound lists. */\nfunction buildBindings(task: RenderTask, eng: EngineContext, targetSignature: RenderTargetSignature): void {\n const opaque = task._opaqueBindings;\n const direct = task._directBindings;\n const transparent = task._transparentBindings;\n opaque.length = 0;\n direct.length = 0;\n transparent.length = 0;\n for (const r of task._renderables) {\n const binding = r.bind(eng, targetSignature);\n if (r.isTransparent || r._transmissive) {\n transparent.push(binding);\n } else if (r._direct) {\n direct.push(binding);\n } else {\n opaque.push(binding);\n }\n }\n opaque.sort((a, b) => a.renderable.order - b.renderable.order);\n direct.sort((a, b) => a.renderable.order - b.renderable.order);\n task._opaqueBundles.length = 0;\n task._lastVersion = (task.scene as SceneContext)._renderableVersion;\n}\n\nfunction buildRenderPassDescriptor(task: RenderTask, rt: RenderTarget): void {\n const att = task._colorAttachment;\n att.view = rt._colorView!;\n // End-of-pass MSAA resolve into a caller-supplied single-sample target.\n // record() only builds the target's color view for an MSAA rt, so its\n // presence is the gate. The swapchain case is wired per-frame in\n // executePass (its view changes each frame); this custom view is stable.\n att.resolveTarget = task._config.rst?._colorView ?? undefined;\n task._renderPassDescriptor.colorAttachments = rt._colorView ? [att] : [];\n\n const depthSrc = task._depthSrc ?? rt;\n const depthView = depthSrc._depthView;\n let depthAttachment: GPURenderPassDepthStencilAttachment | undefined;\n if (depthView) {\n const dd = depthSrc._descriptor;\n const loadOp = task._depthLoadOp ?? \"clear\";\n depthAttachment = {\n view: depthView,\n depthClearValue: dd._depthClearValue ?? 0,\n depthLoadOp: loadOp,\n depthStoreOp: \"store\",\n };\n if (dd.dFormat?.includes(\"stencil\")) {\n depthAttachment.stencilClearValue = 0;\n depthAttachment.stencilLoadOp = loadOp;\n depthAttachment.stencilStoreOp = \"store\";\n }\n }\n\n task._renderPassDescriptor.depthStencilAttachment = depthAttachment;\n}\n\nfunction prepareRenderTaskPass(task: RenderTask, eng: EngineContext, targetSignature: RenderTargetSignature, context: DrawUpdateContext): void {\n const sc = task.scene as SceneContext;\n // Auto-resync when the source scene mutates.\n if (task._autoFromScene && task._lastVersion !== sc._renderableVersion) {\n task._renderables.length = 0;\n task._renderables.push(...sc._renderables);\n buildBindings(task, eng, targetSignature);\n }\n\n // Pre-pass work — runs before beginRenderPass. Updates the task-owned scene\n // UBO, scene-wide lights UBO, and per-binding UBOs. The scene bind group may\n // also need a refresh (lights buffer can be resized when glTF lights\n // extension raises MAX_LIGHTS after this task was first recorded).\n refreshTaskSceneBindGroup(task, eng);\n const camera = task._config.cam ?? sc.camera;\n sc._clusteredLightUpdater?.(camera, context.targetWidth, context.targetHeight);\n _writePassSceneUBO(task, eng, sc, camera);\n refreshSceneLightsUBO(eng, sc);\n // Expose the active camera to per-binding `update()` calls. Some renderables\n // (e.g. transparent billboard systems) need it to compute view-space sort\n // depths during their update.\n (context as MutableDrawUpdateContext)._camera = camera;\n updateBindings(task._opaqueBindings, context);\n updateBindings(task._directBindings, context);\n updateBindings(task._transparentBindings, context);\n // Per-frame back-to-front sort for transparent bindings — must run AFTER\n // updateBindings so renderables that compute `_worldCenter` inside their\n // own `update()` (billboard systems) are seen with current values.\n sortTransparentBindings(task, camera);\n}\n\nfunction executePass(task: RenderTask, eng: EngineContext, targetSignature: RenderTargetSignature, context: DrawUpdateContext): number {\n const sc = task.scene;\n const sampleCount = targetSignature._sampleCount;\n prepareRenderTaskPass(task, eng, targetSignature, context);\n const att = task._colorAttachment;\n const cfg = task._config;\n if (cfg.rt._colorView) {\n // The engine scRT's color view is re-acquired every frame, so re-read\n // it here. Offscreen color views are stable between rebuilds — leaving att.view\n // untouched preserves an external override (swapchain-overlay shares the base\n // scene's MSAA color view). The resolve target (rst) is re-read each frame so an\n // `rst === scRT` picks up its fresh per-frame view.\n if (cfg.rt === eng.scRT) {\n att.view = cfg.rt._colorView;\n }\n att.resolveTarget = cfg.rst?._colorView ?? undefined;\n att.clearValue = task._autoFromScene ? sc.clearColor : cfg.clrColor!;\n att.loadOp = cfg.clr ? \"clear\" : \"load\";\n }\n if (task._executeWithTransmission) {\n return task._executeWithTransmission(sampleCount);\n }\n const pass = eng._currentEncoder.beginRenderPass(task._renderPassDescriptor);\n const draws = executePassBody(task, pass);\n pass.end();\n return draws;\n}\n\n/** Body of the registered `RenderPass`. Receives the live render-pass encoder\n * and issues all draws (viewport/scissor, group(0) bind, opaque bundle replay,\n * then direct-draws non-transparent direct + transparent). Returns the draw count. */\nfunction executePassBody(task: RenderTask, pass: GPURenderPassEncoder): number {\n const eng = task.engine as EngineContext;\n const cfg = task._config;\n const rt = cfg.rt;\n const scene = task.scene as SceneContext;\n const opaqueBindings = task._opaqueBindings;\n const opaqueBundles = task._opaqueBundles;\n const sceneBG = task._sceneBG;\n\n const camera = cfg.cam ?? scene.camera;\n const v = camera?.viewport;\n if (v) {\n const rw = rt._width;\n const rh = rt._height;\n const x = Math.floor(v.x * rw);\n const y = Math.floor((1 - v.y - v.height) * rh);\n const w = Math.ceil((v.x + v.width) * rw) - x;\n const h = Math.ceil((1 - v.y) * rh) - y;\n pass.setViewport(x, y, w, h, 0, 1);\n pass.setScissorRect(x, y, w, h);\n }\n // Scene bind group (group 0) is task-owned and identical for every draw in this pass.\n pass.setBindGroup(0, sceneBG);\n\n // Opaque: cached render bundle. Invalidated by scene mutation (_renderableVersion)\n // or visibility version (_vis). The bundle records group(0) at its start so it can\n // be replayed standalone (executeBundles inherits no inherited state).\n if (task._lastVersion !== scene._renderableVersion || task._lastVis !== _vis || opaqueBundles.length === 0) {\n const desc = rt._descriptor;\n const be = eng._device.createRenderBundleEncoder({\n colorFormats: desc.format ? [desc.format] : [],\n // Use the task's target signature, not the RT descriptor: a depth\n // override (config.depth) supplies the depth format externally, so\n // the cached opaque pipelines are built with it while the colour RT\n // carries no depthStencilFormat of its own. The bundle encoder's\n // attachment state must match those pipelines exactly.\n depthStencilFormat: task._targetSignature._depthStencilFormat,\n sampleCount: desc.samples ?? 1,\n });\n be.setBindGroup(0, sceneBG);\n drawList(be, opaqueBindings, eng);\n opaqueBundles[0] = be.finish();\n task._lastVersion = scene._renderableVersion;\n task._lastVis = _vis;\n }\n let draws = opaqueBindings.length;\n pass.executeBundles(opaqueBundles);\n // executeBundles invalidates pass bind-group state — rebind group 0 before further draws.\n pass.setBindGroup(0, sceneBG);\n draws += drawList(pass, task._directBindings, eng);\n draws += drawList(pass, task._transparentBindings, eng);\n return draws;\n}\n\nfunction refreshTaskSceneBindGroup(task: RenderTask, eng: EngineContext): void {\n const lightsUBO = ensureSceneLightState(eng, task.scene as SceneContext)._buffer;\n if (lightsUBO === task._lightsUBO) {\n return;\n }\n task._lightsUBO = lightsUBO;\n task._sceneBG = eng._device.createBindGroup({\n layout: getSceneBindGroupLayout(eng),\n entries: [\n { binding: 0, resource: { buffer: task._sceneUBO } },\n { binding: 1, resource: { buffer: lightsUBO } },\n ],\n });\n task._opaqueBundles.length = 0;\n task._lastVersion = -1;\n}\n\n/** Write the canonical SceneUniforms struct to the task-owned scene UBO.\n * Bails before touching scratch/GPU when all inputs are unchanged.\n * @internal Exported for unit testing of the scene-UBO change-detection guard. */\nexport function _writePassSceneUBO(task: RenderTask, eng: EngineContext, scene: SceneContext, camera: Camera | null): void {\n if (!camera) {\n return;\n }\n\n const v = camera.viewport;\n const rt = task._config.rt;\n const aspect = (task._config.cs ? eng.canvas.width / eng.canvas.height : rt._width / rt._height) * (v ? v.width / v.height : 1);\n const fog = scene.fog;\n const img = scene.imageProcessing;\n const envRotationY = scene.envRotationY || 0;\n const wv = camera.worldMatrixVersion;\n // `envTextures` identity is tracked so an environment loaded (or swapped) AFTER the scene has reached\n // steady state invalidates this cache. Its spherical-harmonics irradiance and `lodGenerationScale` are\n // written into the scene UBO below (via `_packSceneUniforms` + the env-SH contributor); without tracking\n // it, a late env load would change none of the other guarded inputs, so the UBO would never be rewritten\n // and the model would keep zero irradiance (dark diffuse, specular-only \"mirror\" look).\n const envTextures = scene._envTextures;\n const s = task._su;\n if (s[0] === camera && s[1] === fog && s[2] === wv && s[3] === aspect && s[4] === envRotationY && s[5] === img.exposure && s[6] === img.contrast && s[7] === envTextures) {\n return;\n }\n s[0] = camera;\n s[1] = fog;\n s[2] = wv;\n s[3] = aspect;\n s[4] = envRotationY;\n s[5] = img.exposure;\n s[6] = img.contrast;\n s[7] = envTextures;\n\n const data = task._suData;\n _packSceneUniforms(data, eng, scene, camera, aspect);\n const contribs = scene._sceneUboContributors;\n if (contribs) {\n for (const c of contribs) {\n c(data, scene);\n }\n }\n eng._device.queue.writeBuffer(task._sceneUBO, 0, data as Float32Array<ArrayBuffer>);\n}\n\nfunction updateBindings(list: readonly DrawBinding[], context: DrawUpdateContext): void {\n for (const b of list) {\n b.update?.(context);\n }\n}\n\n/** Iterate DrawBindings, deduping setPipeline. */\nfunction drawList(enc: GPURenderPassEncoder | GPURenderBundleEncoder, list: readonly DrawBinding[], engine: EngineContext): number {\n let lp: GPURenderPipeline | null = null;\n let draws = 0;\n for (const b of list) {\n const mesh = b.renderable.mesh;\n if (mesh && mesh.visible === false) {\n continue;\n }\n if (b.pipeline !== lp) {\n enc.setPipeline(b.pipeline);\n lp = b.pipeline;\n }\n draws += b.draw(enc, engine);\n }\n return draws;\n}\n"],"names":[],"mappings":"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"}
package/lib/index.js CHANGED
@@ -77,6 +77,7 @@ export { createMaterialView } from './material/material-view.js';
77
77
  export { markMaterialUboDirty } from './material/material-dirty.js';
78
78
  export { rebuildMaterial } from './material/material-rebuild.js';
79
79
  export { setSceneImageProcessing } from './scene/scene-image-processing.js';
80
+ export { rebuildScenePbrPipelines } from './scene/scene-rebuild.js';
80
81
  export { StandardToneMapping } from './material/pbr/tone-mapping.js';
81
82
  export { AcesToneMapping } from './material/pbr/pbr-aces-wgsl.js';
82
83
  export { NeutralToneMapping } from './material/pbr/pbr-neutral-wgsl.js';
@@ -175,7 +176,7 @@ export { attachPositionGizmoToNode, attachRotationGizmoToNode, attachScaleGizmoT
175
176
  export { attachCameraGizmoToCamera, createCameraGizmo, disposeCameraGizmo } from './gizmo/camera-gizmo.js';
176
177
  export { attachLightGizmoToLight, createLightGizmo, disposeLightGizmo } from './gizmo/light-gizmo.js';
177
178
  export { attachBoundingBoxGizmoToNode, createBoundingBoxGizmo, disposeBoundingBoxGizmo } from './gizmo/bounding-box-gizmo.js';
178
- export { createGridSpriteAtlas, loadSpriteAtlas } from './sprite/shared/sprite-atlas.js';
179
+ export { createGridSpriteAtlas, disposeSpriteAtlas, loadSpriteAtlas } from './sprite/shared/sprite-atlas.js';
179
180
  export { appendSpriteAtlasFrames, createSpriteAtlasFromFrames } from './sprite/shared/sprite-atlas-packer.js';
180
181
  export { spriteBlendAdditive, spriteBlendAlpha, spriteBlendMultiply, spriteBlendPremultiplied } from './sprite/sprite-blend.js';
181
182
  export { addSprite2DIndex, clearSprite2DLayer, createSprite2DLayer, removeSprite2DIndex, setSprite2DFrameIndex, setSprite2DShaderParams, updateSprite2DIndex } from './sprite/sprite-2d.js';
@@ -183,6 +184,8 @@ export { setSprite2DCoverageGamma } from './sprite/sprite-2d-coverage-gamma.js';
183
184
  export { setSprite2DUvOffset } from './sprite/sprite-2d-uvscroll.js';
184
185
  export { createSprite2DCustomShader } from './sprite/sprite-custom-shader.js';
185
186
  export { addSprite2D, getSprite2DHandleIndex, isSprite2DHandleAlive, removeSprite2D, setSprite2DFrame, updateSprite2D } from './sprite/sprite-2d-handle.js';
187
+ export { pickSprite2D } from './sprite/picking/pick-sprite-2d.js';
188
+ export { pickBillboardSprite } from './sprite/picking/pick-billboard.js';
186
189
  export { addDepthHostedSpriteLayer } from './sprite/sprite-scene.js';
187
190
  export { billboardBlendAdditive, billboardBlendAlpha, billboardBlendCutout, billboardBlendPremultiplied } from './sprite/billboard-blend.js';
188
191
  export { addBillboardSpriteIndex, clearBillboardSprites, createAxisLockedBillboardSystem, createFacingBillboardSystem, removeBillboardSpriteIndex, setBillboardShaderParams, setBillboardSpriteFrameIndex, updateBillboardSpriteIndex } from './sprite/billboard-sprite.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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -48,9 +48,12 @@ const feature = {
48
48
  }
49
49
  const componentCount = TYPE_COMPONENTS[a.type] ?? 1;
50
50
  const stride = bufferViews[a.bufferView]?.byteStride;
51
+ const compBytes = COMPONENT_BYTES[a.componentType];
51
52
  const signed = a.componentType === BYTE || a.componentType === SHORT;
52
53
  const stridedFloat = a.componentType === FLOAT && stride !== void 0 && stride !== componentCount * 4;
53
- if (signed || a.normalized === true || stridedFloat) {
54
+ const unsignedInt = a.componentType === UNSIGNED_BYTE || a.componentType === UNSIGNED_SHORT;
55
+ const stridedUnsignedInt = unsignedInt && a.normalized !== true && a.type !== "VEC4" && stride !== void 0 && compBytes !== void 0 && stride !== componentCount * compBytes;
56
+ if (signed || a.normalized === true || stridedFloat || stridedUnsignedInt) {
54
57
  convert.push(i);
55
58
  appended = align4(appended + a.count * componentCount * 4);
56
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-ext-quantization.js","sources":["../../../src/loader-gltf/gltf-ext-quantization.ts"],"sourcesContent":["/**\n * KHR_mesh_quantization (+ post-meshopt) dequantization feature.\n *\n * Runs as a `preParse` hook, after EXT_meshopt_compression. The core accessor\n * reader (`resolveAccessor`) only understands tightly-packed FLOAT / UBYTE /\n * USHORT / UINT data and ignores `byteStride`; quantized assets store vertex\n * attributes (and meshopt-filtered animation outputs) as normalized or signed\n * integers, sometimes padded by a `byteStride`. This feature rewrites every such\n * accessor into a freshly-appended, tightly-packed FLOAT bufferView so the rest\n * of the loader stays completely unaware of quantization. It is dynamic-imported\n * only when `extensionsUsed` lists KHR_mesh_quantization, so non-quantized scenes\n * pay nothing.\n *\n * Conversion rule (role-agnostic, derived from the accessor alone):\n * - signed component types (BYTE/SHORT) → FLOAT (core would otherwise throw)\n * - `normalized` accessors → FLOAT (core would otherwise read raw ints)\n * - strided FLOAT accessors → tightly-packed FLOAT (core ignores byteStride)\n * Unsigned non-normalized integer accessors (JOINTS_0, indices) are left intact:\n * they are already tight and the skeleton / index paths expect integers.\n */\n\nimport { U8, DV } from \"../engine/typed-arrays.js\";\nimport type { GltfFeature } from \"./gltf-feature.js\";\n\nconst BYTE = 5120;\nconst UNSIGNED_BYTE = 5121;\nconst SHORT = 5122;\nconst UNSIGNED_SHORT = 5123;\nconst FLOAT = 5126;\n\nconst TYPE_COMPONENTS: Record<string, number> = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 };\nconst COMPONENT_BYTES: Record<number, number> = { 5120: 1, 5121: 1, 5122: 2, 5123: 2, 5125: 4, 5126: 4 };\n\ninterface Accessor {\n bufferView?: number;\n byteOffset?: number;\n componentType: number;\n normalized?: boolean;\n count: number;\n type: string;\n}\n\ninterface BufferView {\n buffer?: number;\n byteOffset?: number;\n byteLength: number;\n byteStride?: number;\n}\n\nfunction align4(n: number): number {\n return (n + 3) & ~3;\n}\n\n/** Read one component as a float, applying glTF normalization when requested. */\nfunction readComponent(view: DataView, offset: number, componentType: number, normalized: boolean): number {\n switch (componentType) {\n case BYTE: {\n const c = view.getInt8(offset);\n return normalized ? Math.max(c / 127, -1) : c;\n }\n case UNSIGNED_BYTE: {\n const c = view.getUint8(offset);\n return normalized ? c / 255 : c;\n }\n case SHORT: {\n const c = view.getInt16(offset, true);\n return normalized ? Math.max(c / 32767, -1) : c;\n }\n case UNSIGNED_SHORT: {\n const c = view.getUint16(offset, true);\n return normalized ? c / 65535 : c;\n }\n case FLOAT:\n return view.getFloat32(offset, true);\n default:\n throw new Error(`KHR_mesh_quantization: unsupported componentType ${componentType}`);\n }\n}\n\nconst feature: GltfFeature = {\n id: \"KHR_mesh_quantization\",\n async preParse(json, binChunk) {\n const accessors: Accessor[] = json.accessors ?? [];\n const bufferViews: BufferView[] = json.bufferViews ?? [];\n\n // Decide which accessors need rewriting and how many float bytes to append.\n const convert: number[] = [];\n let appended = 0;\n for (let i = 0; i < accessors.length; i++) {\n const a = accessors[i]!;\n if (a.bufferView === undefined) {\n continue;\n }\n const componentCount = TYPE_COMPONENTS[a.type] ?? 1;\n const stride = bufferViews[a.bufferView]?.byteStride;\n const signed = a.componentType === BYTE || a.componentType === SHORT;\n const stridedFloat = a.componentType === FLOAT && stride !== undefined && stride !== componentCount * 4;\n if (signed || a.normalized === true || stridedFloat) {\n convert.push(i);\n appended = align4(appended + a.count * componentCount * 4);\n }\n }\n\n if (convert.length === 0) {\n return;\n }\n\n // Build a new buffer: existing data (normalized to offset 0) + appended floats.\n const baseLen = align4(binChunk.byteLength);\n const out = new ArrayBuffer(baseLen + appended);\n new U8(out).set(new U8(binChunk.buffer, binChunk.byteOffset, binChunk.byteLength));\n const outView = new DV(out);\n\n let cursor = baseLen;\n for (const i of convert) {\n const a = accessors[i]!;\n const bv = bufferViews[a.bufferView!]!;\n const componentCount = TYPE_COMPONENTS[a.type] ?? 1;\n const compBytes = COMPONENT_BYTES[a.componentType]!;\n const stride = bv.byteStride ?? componentCount * compBytes;\n // bufferView/accessor byteOffsets are relative to the DataView's own\n // byteOffset (DataView getters add it back), matching resolveAccessor.\n const srcBase = (bv.byteOffset ?? 0) + (a.byteOffset ?? 0);\n\n const dstOffset = cursor;\n for (let v = 0; v < a.count; v++) {\n for (let c = 0; c < componentCount; c++) {\n const value = readComponent(binChunk, srcBase + v * stride + c * compBytes, a.componentType, !!a.normalized);\n outView.setFloat32(dstOffset + (v * componentCount + c) * 4, value, true);\n }\n }\n\n const byteLength = a.count * componentCount * 4;\n const newBvIndex = bufferViews.length;\n bufferViews.push({ buffer: 0, byteOffset: dstOffset, byteLength });\n a.bufferView = newBvIndex;\n a.byteOffset = 0;\n a.componentType = FLOAT;\n a.normalized = false;\n cursor = align4(cursor + byteLength);\n }\n\n return outView;\n },\n};\n\nexport default feature;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-ext-quantization.js","sources":["../../../src/loader-gltf/gltf-ext-quantization.ts"],"sourcesContent":["/**\n * KHR_mesh_quantization (+ post-meshopt) dequantization feature.\n *\n * Runs as a `preParse` hook, after EXT_meshopt_compression. The core accessor\n * reader (`resolveAccessor`) only understands tightly-packed FLOAT / UBYTE /\n * USHORT / UINT data and ignores `byteStride`; quantized assets store vertex\n * attributes (and meshopt-filtered animation outputs) as normalized or signed\n * integers, sometimes padded by a `byteStride`. This feature rewrites every such\n * accessor into a freshly-appended, tightly-packed FLOAT bufferView so the rest\n * of the loader stays completely unaware of quantization. It is dynamic-imported\n * only when `extensionsUsed` lists KHR_mesh_quantization, so non-quantized scenes\n * pay nothing.\n *\n * Conversion rule (role-agnostic, derived from the accessor alone):\n * - signed component types (BYTE/SHORT) → FLOAT (core would otherwise throw)\n * - `normalized` accessors → FLOAT (core would otherwise read raw ints)\n * - strided FLOAT accessors → tightly-packed FLOAT (core ignores byteStride)\n * - strided unsigned non-normalized integer accessors that are NOT VEC4 →\n * tightly-packed FLOAT (core / interleave bind them as float32 and ignore\n * byteStride, so an over-strided UNSIGNED_SHORT/BYTE POSITION — e.g. the\n * quantized Duck's UNSIGNED_SHORT VEC3 POSITION with byteStride 8 — is\n * otherwise read as raw bytes and renders as corrupted geometry). VEC4 is\n * excluded because JOINTS_0/1 are the only unsigned non-normalized VEC4\n * vertex attribute, and the skeleton feature reads them as Uint8/Uint16 and\n * de-strides them itself — they must never be flattened here.\n * Tight (non-strided) unsigned integer accessors are left intact: indices and\n * tight JOINTS_0/1 are already correct and the index / skeleton paths expect\n * integers. (WEIGHTS_n are FLOAT or normalized, so they are handled by the\n * FLOAT / `normalized` branches above, not here.)\n */\n\nimport { U8, DV } from \"../engine/typed-arrays.js\";\nimport type { GltfFeature } from \"./gltf-feature.js\";\n\nconst BYTE = 5120;\nconst UNSIGNED_BYTE = 5121;\nconst SHORT = 5122;\nconst UNSIGNED_SHORT = 5123;\nconst FLOAT = 5126;\n\nconst TYPE_COMPONENTS: Record<string, number> = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 };\nconst COMPONENT_BYTES: Record<number, number> = { 5120: 1, 5121: 1, 5122: 2, 5123: 2, 5125: 4, 5126: 4 };\n\ninterface Accessor {\n bufferView?: number;\n byteOffset?: number;\n componentType: number;\n normalized?: boolean;\n count: number;\n type: string;\n}\n\ninterface BufferView {\n buffer?: number;\n byteOffset?: number;\n byteLength: number;\n byteStride?: number;\n}\n\nfunction align4(n: number): number {\n return (n + 3) & ~3;\n}\n\n/** Read one component as a float, applying glTF normalization when requested. */\nfunction readComponent(view: DataView, offset: number, componentType: number, normalized: boolean): number {\n switch (componentType) {\n case BYTE: {\n const c = view.getInt8(offset);\n return normalized ? Math.max(c / 127, -1) : c;\n }\n case UNSIGNED_BYTE: {\n const c = view.getUint8(offset);\n return normalized ? c / 255 : c;\n }\n case SHORT: {\n const c = view.getInt16(offset, true);\n return normalized ? Math.max(c / 32767, -1) : c;\n }\n case UNSIGNED_SHORT: {\n const c = view.getUint16(offset, true);\n return normalized ? c / 65535 : c;\n }\n case FLOAT:\n return view.getFloat32(offset, true);\n default:\n throw new Error(`KHR_mesh_quantization: unsupported componentType ${componentType}`);\n }\n}\n\nconst feature: GltfFeature = {\n id: \"KHR_mesh_quantization\",\n async preParse(json, binChunk) {\n const accessors: Accessor[] = json.accessors ?? [];\n const bufferViews: BufferView[] = json.bufferViews ?? [];\n\n // Decide which accessors need rewriting and how many float bytes to append.\n const convert: number[] = [];\n let appended = 0;\n for (let i = 0; i < accessors.length; i++) {\n const a = accessors[i]!;\n if (a.bufferView === undefined) {\n continue;\n }\n const componentCount = TYPE_COMPONENTS[a.type] ?? 1;\n const stride = bufferViews[a.bufferView]?.byteStride;\n const compBytes = COMPONENT_BYTES[a.componentType];\n const signed = a.componentType === BYTE || a.componentType === SHORT;\n const stridedFloat = a.componentType === FLOAT && stride !== undefined && stride !== componentCount * 4;\n // Over-strided unsigned non-normalized integer attributes (e.g. quantized\n // UNSIGNED_SHORT/BYTE POSITION with a padded byteStride) are bound as\n // float32 by the interleave/tight paths, which ignore byteStride — so\n // de-stride them into tightly-packed FLOAT. VEC4 (JOINTS_n) is excluded.\n const unsignedInt = a.componentType === UNSIGNED_BYTE || a.componentType === UNSIGNED_SHORT;\n const stridedUnsignedInt =\n unsignedInt && a.normalized !== true && a.type !== \"VEC4\" && stride !== undefined && compBytes !== undefined && stride !== componentCount * compBytes;\n if (signed || a.normalized === true || stridedFloat || stridedUnsignedInt) {\n convert.push(i);\n appended = align4(appended + a.count * componentCount * 4);\n }\n }\n\n if (convert.length === 0) {\n return;\n }\n\n // Build a new buffer: existing data (normalized to offset 0) + appended floats.\n const baseLen = align4(binChunk.byteLength);\n const out = new ArrayBuffer(baseLen + appended);\n new U8(out).set(new U8(binChunk.buffer, binChunk.byteOffset, binChunk.byteLength));\n const outView = new DV(out);\n\n let cursor = baseLen;\n for (const i of convert) {\n const a = accessors[i]!;\n const bv = bufferViews[a.bufferView!]!;\n const componentCount = TYPE_COMPONENTS[a.type] ?? 1;\n const compBytes = COMPONENT_BYTES[a.componentType]!;\n const stride = bv.byteStride ?? componentCount * compBytes;\n // bufferView/accessor byteOffsets are relative to the DataView's own\n // byteOffset (DataView getters add it back), matching resolveAccessor.\n const srcBase = (bv.byteOffset ?? 0) + (a.byteOffset ?? 0);\n\n const dstOffset = cursor;\n for (let v = 0; v < a.count; v++) {\n for (let c = 0; c < componentCount; c++) {\n const value = readComponent(binChunk, srcBase + v * stride + c * compBytes, a.componentType, !!a.normalized);\n outView.setFloat32(dstOffset + (v * componentCount + c) * 4, value, true);\n }\n }\n\n const byteLength = a.count * componentCount * 4;\n const newBvIndex = bufferViews.length;\n bufferViews.push({ buffer: 0, byteOffset: dstOffset, byteLength });\n a.bufferView = newBvIndex;\n a.byteOffset = 0;\n a.componentType = FLOAT;\n a.normalized = false;\n cursor = align4(cursor + byteLength);\n }\n\n return outView;\n },\n};\n\nexport default feature;\n"],"names":[],"mappings":"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"}
@@ -48,6 +48,16 @@ async function loadGltfFeatures(json) {
48
48
  const mods = await Promise.all(_features.flatMap(([t, load]) => (typeof t === "string" ? used.includes(t) : t(json)) ? [load()] : []));
49
49
  return mods.map((m) => m.default);
50
50
  }
51
+ async function runGltfMaterialFeatures(mat, features, ctx) {
52
+ const fragments = await Promise.all(features.map((feature) => feature.applyMaterial(mat, ctx)));
53
+ let layers;
54
+ for (const fragment of fragments) {
55
+ if (fragment) {
56
+ Object.assign(layers ??= {}, fragment);
57
+ }
58
+ }
59
+ return layers;
60
+ }
51
61
  function hasGltfExtras(json) {
52
62
  const hasExtras = (item) => item?.extras !== void 0;
53
63
  return hasExtras(json.asset) || !!json.nodes?.some(hasExtras) || !!json.materials?.some(hasExtras) || !!json.animations?.some(hasExtras) || !!json.meshes?.some(hasExtras) || anyPrimitive(json, hasExtras);
@@ -71,5 +81,5 @@ function hasNegDetNode(json) {
71
81
  });
72
82
  }
73
83
 
74
- export { loadGltfFeatures };
84
+ export { loadGltfFeatures, runGltfMaterialFeatures };
75
85
  //# sourceMappingURL=gltf-feature-registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-feature-registry.js","sources":["../../../src/loader-gltf/gltf-feature-registry.ts"],"sourcesContent":["/**\n * glTF optional-feature registry.\n *\n * The `_features` table maps every optional glTF capability to a `[trigger, load]`\n * pair: a cheap trigger plus a dynamic `import()` of the feature's `GltfFeature`\n * module. A trigger is either an exact `extensionsUsed` name (the common case) or\n * a predicate over the parsed JSON for features that aren't a simple extension\n * membership (ORM compositing, skeletons, morph targets, animations, dielectric\n * material cluster).\n *\n * This whole module is itself dynamically imported by the core loader\n * (`load-gltf.ts`) ONLY when the asset can possibly trigger at least one feature\n * — so assets that use no optional features (plain metallic-roughness GLBs) never\n * pay for the table or its import thunks.\n *\n * The core loader knows zero feature names; new extensions are added here alone.\n */\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport { anyPrimitive, needsOrmComposite } from \"./gltf-parser.js\";\n\n/** Dynamic `import()` of a feature's `GltfFeature` module. */\ntype Loader = () => Promise<{ default: GltfFeature }>;\n/** Either an exact `extensionsUsed` name, or a predicate over the parsed JSON. */\ntype Trigger = string | ((json: any) => boolean);\n\nconst M = \"KHR_materials_\";\n\nconst _features: [Trigger, Loader][] = [\n // Pre-parse features (buffer-level): order matters — meshopt decompresses\n // bufferViews first, then quantization dequantizes the resulting accessors.\n [\"EXT_meshopt_compression\", () => import(\"./gltf-feature-meshopt.js\")],\n [\"KHR_mesh_quantization\", () => import(\"./gltf-ext-quantization.js\")],\n // Pre-mesh features (geometry decompression)\n [\"KHR_draco_mesh_compression\", () => import(\"./gltf-feature-draco.js\")],\n // Material extensions\n [M + \"clearcoat\", () => import(\"./gltf-ext-clearcoat.js\")],\n [M + \"iridescence\", () => import(\"./gltf-ext-iridescence.js\")],\n [M + \"emissive_strength\", () => import(\"./gltf-ext-emissive-strength.js\")],\n [M + \"sheen\", () => import(\"./gltf-ext-sheen.js\")],\n [M + \"anisotropy\", () => import(\"./gltf-ext-anisotropy.js\")],\n [M + \"diffuse_transmission\", () => import(\"./gltf-ext-diffuse-transmission.js\")],\n [M + \"unlit\", () => import(\"./gltf-ext-unlit.js\")],\n [M + \"pbrSpecularGlossiness\", () => import(\"./gltf-ext-spec-gloss.js\")],\n // Dielectric cluster (ior/specular/transmission/volume/dispersion) — any of the five triggers the\n // loader; transmission refraction is wired dynamically by the PBR material path when needed.\n [(j) => [\"transmission\", \"volume\", \"ior\", \"specular\", \"dispersion\"].some((e) => j.extensionsUsed?.includes(M + e)), () => import(\"./gltf-ext-dielectric.js\")],\n [\"KHR_texture_transform\", () => import(\"./gltf-ext-uv-transform.js\")],\n [\"KHR_texture_basisu\", () => import(\"./gltf-ext-basisu.js\")],\n [needsOrmComposite, () => import(\"./gltf-ext-orm.js\")],\n // Per-mesh features (predicates inlined to avoid eager imports)\n [(j) => !!j.skins?.length && anyPrimitive(j, (p) => p.attributes?.JOINTS_0 !== undefined), () => import(\"./gltf-feature-skeleton.js\")],\n [(j) => anyPrimitive(j, (p) => !!p.targets?.length), () => import(\"./gltf-feature-morph.js\")],\n // Non-triangle primitive topology (POINTS/LINES/LINE_STRIP/TRIANGLE_STRIP) or a\n // negative-determinant node (negative scale / mirrored matrix): both need the lazy primitive\n // feature (topology threading + winding reversal). Triangle-list positive-winding never triggers.\n [(j) => hasNegDetNode(j) || anyPrimitive(j, (p) => p.mode !== undefined && p.mode !== 4), () => import(\"./gltf-feature-primitive.js\")],\n // Per-asset features\n [hasGltfExtras, () => import(\"./gltf-feature-extras.js\")],\n [\"KHR_lights_punctual\", () => import(\"./gltf-feature-lights-punctual.js\")],\n [(j) => !!j.animations?.length, () => import(\"./gltf-feature-animations.js\")],\n // Non-Float32 / normalized animation sampler accessors (e.g. Animation_SamplerType normalized\n // BYTE/SHORT rotation) need the lazy denorm converter; plain float samplers never load it.\n [hasNonFloatAnimSampler, () => import(\"./gltf-sampler-denorm.js\")],\n [M + \"variants\", () => import(\"./gltf-feature-variants.js\")],\n [\"KHR_node_visibility\", () => import(\"./gltf-ext-node-visibility.js\")],\n [\"KHR_animation_pointer\", () => import(\"./gltf-feature-animation-pointer.js\")],\n [\"EXT_mesh_gpu_instancing\", () => import(\"./gltf-feature-gpu-instancing.js\")],\n [\"KHR_xmp_json_ld\", () => import(\"./gltf-feature-xmp.js\")],\n];\n\n/** Dynamic-import every feature the asset triggers. */\nexport async function loadGltfFeatures(json: any): Promise<GltfFeature[]> {\n const used: string[] = json.extensionsUsed ?? [];\n const mods = await Promise.all(_features.flatMap(([t, load]) => ((typeof t === \"string\" ? used.includes(t) : t(json)) ? [load()] : [])));\n return mods.map((m) => m.default);\n}\n\nfunction hasGltfExtras(json: any): boolean {\n const hasExtras = (item: any): boolean => item?.extras !== undefined;\n return (\n hasExtras(json.asset) ||\n !!json.nodes?.some(hasExtras) ||\n !!json.materials?.some(hasExtras) ||\n !!json.animations?.some(hasExtras) ||\n !!json.meshes?.some(hasExtras) ||\n anyPrimitive(json, hasExtras)\n );\n}\n\n/** True if any animation sampler reads a non-Float32 input/output accessor (normalized BYTE/SHORT\n * rotation output, normalized UNSIGNED_BYTE flags, …) — the only case that needs the lazy sampler\n * denorm converter. Plain Float32 samplers (the overwhelming majority) skip it. */\nfunction hasNonFloatAnimSampler(json: any): boolean {\n const accessors = json.accessors;\n return !!(json.animations as any[] | undefined)?.some((a) =>\n a.samplers?.some((s: any) => accessors[s.input]?.componentType !== 5126 || accessors[s.output]?.componentType !== 5126)\n );\n}\n\n/** True if any node introduces a negative-determinant local transform — a\n * negative scale (odd number of negative components) or a `matrix` with a\n * negative 3x3 determinant. Such a node (or a child of one) can flip a mesh's\n * net world determinant positive, reversing its triangle winding. Gates the\n * lazy negative-winding feature so positive-scale / pure-TRS assets never load\n * it. A negative-determinant node whose meshes' net world determinant stays\n * negative over-triggers harmlessly (the feature then finds a non-positive\n * determinant per mesh and flags nothing). */\nfunction hasNegDetNode(json: any): boolean {\n return !!(json.nodes as any[] | undefined)?.some((n) => {\n if (n.scale) {\n return n.scale[0] * n.scale[1] * n.scale[2] < 0;\n }\n if (n.matrix) {\n const m = n.matrix;\n return m[0] * (m[5] * m[10] - m[6] * m[9]) + m[1] * (m[6] * m[8] - m[4] * m[10]) + m[2] * (m[4] * m[9] - m[5] * m[8]) < 0;\n }\n return false;\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-feature-registry.js","sources":["../../../src/loader-gltf/gltf-feature-registry.ts"],"sourcesContent":["/**\n * glTF optional-feature registry.\n *\n * The `_features` table maps every optional glTF capability to a `[trigger, load]`\n * pair: a cheap trigger plus a dynamic `import()` of the feature's `GltfFeature`\n * module. A trigger is either an exact `extensionsUsed` name (the common case) or\n * a predicate over the parsed JSON for features that aren't a simple extension\n * membership (ORM compositing, skeletons, morph targets, animations, dielectric\n * material cluster).\n *\n * This whole module is itself dynamically imported by the core loader\n * (`load-gltf.ts`) ONLY when the asset can possibly trigger at least one feature\n * — so assets that use no optional features (plain metallic-roughness GLBs) never\n * pay for the table or its import thunks.\n *\n * The core loader knows zero feature names; new extensions are added here alone.\n */\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport type { GltfMatExtCtx, GltfMaterialData } from \"./gltf-material.js\";\nimport type { PbrMaterialProps } from \"../material/pbr/pbr-material.js\";\nimport { anyPrimitive, needsOrmComposite } from \"./gltf-parser.js\";\n\n/** Dynamic `import()` of a feature's `GltfFeature` module. */\ntype Loader = () => Promise<{ default: GltfFeature }>;\n/** Either an exact `extensionsUsed` name, or a predicate over the parsed JSON. */\ntype Trigger = string | ((json: any) => boolean);\n\nconst M = \"KHR_materials_\";\n\nconst _features: [Trigger, Loader][] = [\n // Pre-parse features (buffer-level): order matters — meshopt decompresses\n // bufferViews first, then quantization dequantizes the resulting accessors.\n [\"EXT_meshopt_compression\", () => import(\"./gltf-feature-meshopt.js\")],\n [\"KHR_mesh_quantization\", () => import(\"./gltf-ext-quantization.js\")],\n // Pre-mesh features (geometry decompression)\n [\"KHR_draco_mesh_compression\", () => import(\"./gltf-feature-draco.js\")],\n // Material extensions\n [M + \"clearcoat\", () => import(\"./gltf-ext-clearcoat.js\")],\n [M + \"iridescence\", () => import(\"./gltf-ext-iridescence.js\")],\n [M + \"emissive_strength\", () => import(\"./gltf-ext-emissive-strength.js\")],\n [M + \"sheen\", () => import(\"./gltf-ext-sheen.js\")],\n [M + \"anisotropy\", () => import(\"./gltf-ext-anisotropy.js\")],\n [M + \"diffuse_transmission\", () => import(\"./gltf-ext-diffuse-transmission.js\")],\n [M + \"unlit\", () => import(\"./gltf-ext-unlit.js\")],\n [M + \"pbrSpecularGlossiness\", () => import(\"./gltf-ext-spec-gloss.js\")],\n // Dielectric cluster (ior/specular/transmission/volume/dispersion) — any of the five triggers the\n // loader; transmission refraction is wired dynamically by the PBR material path when needed.\n [(j) => [\"transmission\", \"volume\", \"ior\", \"specular\", \"dispersion\"].some((e) => j.extensionsUsed?.includes(M + e)), () => import(\"./gltf-ext-dielectric.js\")],\n [\"KHR_texture_transform\", () => import(\"./gltf-ext-uv-transform.js\")],\n [\"KHR_texture_basisu\", () => import(\"./gltf-ext-basisu.js\")],\n [needsOrmComposite, () => import(\"./gltf-ext-orm.js\")],\n // Per-mesh features (predicates inlined to avoid eager imports)\n [(j) => !!j.skins?.length && anyPrimitive(j, (p) => p.attributes?.JOINTS_0 !== undefined), () => import(\"./gltf-feature-skeleton.js\")],\n [(j) => anyPrimitive(j, (p) => !!p.targets?.length), () => import(\"./gltf-feature-morph.js\")],\n // Non-triangle primitive topology (POINTS/LINES/LINE_STRIP/TRIANGLE_STRIP) or a\n // negative-determinant node (negative scale / mirrored matrix): both need the lazy primitive\n // feature (topology threading + winding reversal). Triangle-list positive-winding never triggers.\n [(j) => hasNegDetNode(j) || anyPrimitive(j, (p) => p.mode !== undefined && p.mode !== 4), () => import(\"./gltf-feature-primitive.js\")],\n // Per-asset features\n [hasGltfExtras, () => import(\"./gltf-feature-extras.js\")],\n [\"KHR_lights_punctual\", () => import(\"./gltf-feature-lights-punctual.js\")],\n [(j) => !!j.animations?.length, () => import(\"./gltf-feature-animations.js\")],\n // Non-Float32 / normalized animation sampler accessors (e.g. Animation_SamplerType normalized\n // BYTE/SHORT rotation) need the lazy denorm converter; plain float samplers never load it.\n [hasNonFloatAnimSampler, () => import(\"./gltf-sampler-denorm.js\")],\n [M + \"variants\", () => import(\"./gltf-feature-variants.js\")],\n [\"KHR_node_visibility\", () => import(\"./gltf-ext-node-visibility.js\")],\n [\"KHR_animation_pointer\", () => import(\"./gltf-feature-animation-pointer.js\")],\n [\"EXT_mesh_gpu_instancing\", () => import(\"./gltf-feature-gpu-instancing.js\")],\n [\"KHR_xmp_json_ld\", () => import(\"./gltf-feature-xmp.js\")],\n];\n\n/** Dynamic-import every feature the asset triggers. */\nexport async function loadGltfFeatures(json: any): Promise<GltfFeature[]> {\n const used: string[] = json.extensionsUsed ?? [];\n const mods = await Promise.all(_features.flatMap(([t, load]) => ((typeof t === \"string\" ? used.includes(t) : t(json)) ? [load()] : [])));\n return mods.map((m) => m.default);\n}\n\n/** Run every active material feature and merge its PBR fragment. */\nexport async function runGltfMaterialFeatures(mat: GltfMaterialData, features: GltfFeature[], ctx: GltfMatExtCtx): Promise<Partial<PbrMaterialProps> | undefined> {\n const fragments = await Promise.all(features.map((feature) => feature.applyMaterial!(mat, ctx)));\n let layers: Partial<PbrMaterialProps> | undefined;\n for (const fragment of fragments) {\n if (fragment) {\n Object.assign((layers ??= {}), fragment);\n }\n }\n return layers;\n}\n\nfunction hasGltfExtras(json: any): boolean {\n const hasExtras = (item: any): boolean => item?.extras !== undefined;\n return (\n hasExtras(json.asset) ||\n !!json.nodes?.some(hasExtras) ||\n !!json.materials?.some(hasExtras) ||\n !!json.animations?.some(hasExtras) ||\n !!json.meshes?.some(hasExtras) ||\n anyPrimitive(json, hasExtras)\n );\n}\n\n/** True if any animation sampler reads a non-Float32 input/output accessor (normalized BYTE/SHORT\n * rotation output, normalized UNSIGNED_BYTE flags, …) — the only case that needs the lazy sampler\n * denorm converter. Plain Float32 samplers (the overwhelming majority) skip it. */\nfunction hasNonFloatAnimSampler(json: any): boolean {\n const accessors = json.accessors;\n return !!(json.animations as any[] | undefined)?.some((a) =>\n a.samplers?.some((s: any) => accessors[s.input]?.componentType !== 5126 || accessors[s.output]?.componentType !== 5126)\n );\n}\n\n/** True if any node introduces a negative-determinant local transform — a\n * negative scale (odd number of negative components) or a `matrix` with a\n * negative 3x3 determinant. Such a node (or a child of one) can flip a mesh's\n * net world determinant positive, reversing its triangle winding. Gates the\n * lazy negative-winding feature so positive-scale / pure-TRS assets never load\n * it. A negative-determinant node whose meshes' net world determinant stays\n * negative over-triggers harmlessly (the feature then finds a non-positive\n * determinant per mesh and flags nothing). */\nfunction hasNegDetNode(json: any): boolean {\n return !!(json.nodes as any[] | undefined)?.some((n) => {\n if (n.scale) {\n return n.scale[0] * n.scale[1] * n.scale[2] < 0;\n }\n if (n.matrix) {\n const m = n.matrix;\n return m[0] * (m[5] * m[10] - m[6] * m[9]) + m[1] * (m[6] * m[8] - m[4] * m[10]) + m[2] * (m[4] * m[9] - m[5] * m[8]) < 0;\n }\n return false;\n });\n}\n"],"names":[],"mappings":"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"}
@@ -6,7 +6,17 @@ const feature = {
6
6
  return {};
7
7
  }
8
8
  const { loadVariantMaterials } = await import('./gltf-variants.js');
9
- const materialVariants = await loadVariantMaterials(ctx._json, ctx._binChunk, ctx._baseUrl, variantNames, meshes, ctx._engine, ctx._matExts, ctx._wrapTex);
9
+ const materialVariants = await loadVariantMaterials(
10
+ ctx._json,
11
+ ctx._binChunk,
12
+ ctx._baseUrl,
13
+ variantNames,
14
+ meshes,
15
+ ctx._engine,
16
+ ctx._matExts,
17
+ ctx._runMatExts,
18
+ ctx._wrapTex
19
+ );
10
20
  return { materialVariants };
11
21
  }
12
22
  };
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-feature-variants.js","sources":["../../../src/loader-gltf/gltf-feature-variants.ts"],"sourcesContent":["/** KHR_materials_variants feature.\n * Triggered when the root extension carries variant definitions. Per-asset\n * hook builds variant material data shared with the material-ext driver. */\n\nimport type { GltfFeature } from \"./gltf-feature.js\";\n\nconst feature: GltfFeature = {\n id: \"KHR_materials_variants\",\n async applyAsset(meshes, _root, ctx) {\n const variantNames: string[] | undefined = ctx._json.extensions?.KHR_materials_variants?.variants?.map((v: { name: string }) => v.name);\n if (!variantNames?.length) {\n return {};\n }\n const { loadVariantMaterials } = await import(\"./gltf-variants.js\");\n const materialVariants = await loadVariantMaterials(ctx._json, ctx._binChunk, ctx._baseUrl, variantNames, meshes, ctx._engine, ctx._matExts, ctx._wrapTex);\n return { materialVariants };\n },\n};\nexport default feature;\n"],"names":[],"mappings":"AAMA,MAAM,OAAA,GAAuB;AAAA,EACzB,EAAA,EAAI,wBAAA;AAAA,EACJ,MAAM,UAAA,CAAW,MAAA,EAAQ,KAAA,EAAO,GAAA,EAAK;AACjC,IAAA,MAAM,YAAA,GAAqC,GAAA,CAAI,KAAA,CAAM,UAAA,EAAY,sBAAA,EAAwB,UAAU,GAAA,CAAI,CAAC,CAAA,KAAwB,CAAA,CAAE,IAAI,CAAA;AACtI,IAAA,IAAI,CAAC,cAAc,MAAA,EAAQ;AACvB,MAAA,OAAO,EAAC;AAAA,IACZ;AACA,IAAA,MAAM,EAAE,oBAAA,EAAqB,GAAI,MAAM,OAAO,oBAAoB,CAAA;AAClE,IAAA,MAAM,mBAAmB,MAAM,oBAAA,CAAqB,GAAA,CAAI,KAAA,EAAO,IAAI,SAAA,EAAW,GAAA,CAAI,QAAA,EAAU,YAAA,EAAc,QAAQ,GAAA,CAAI,OAAA,EAAS,GAAA,CAAI,QAAA,EAAU,IAAI,QAAQ,CAAA;AACzJ,IAAA,OAAO,EAAE,gBAAA,EAAiB;AAAA,EAC9B;AACJ;;;;"}
1
+ {"version":3,"file":"gltf-feature-variants.js","sources":["../../../src/loader-gltf/gltf-feature-variants.ts"],"sourcesContent":["/** KHR_materials_variants feature.\n * Triggered when the root extension carries variant definitions. Per-asset\n * hook builds variant material data shared with the material-ext driver. */\n\nimport type { GltfFeature } from \"./gltf-feature.js\";\n\nconst feature: GltfFeature = {\n id: \"KHR_materials_variants\",\n async applyAsset(meshes, _root, ctx) {\n const variantNames: string[] | undefined = ctx._json.extensions?.KHR_materials_variants?.variants?.map((v: { name: string }) => v.name);\n if (!variantNames?.length) {\n return {};\n }\n const { loadVariantMaterials } = await import(\"./gltf-variants.js\");\n const materialVariants = await loadVariantMaterials(\n ctx._json,\n ctx._binChunk,\n ctx._baseUrl,\n variantNames,\n meshes,\n ctx._engine,\n ctx._matExts,\n ctx._runMatExts!,\n ctx._wrapTex\n );\n return { materialVariants };\n },\n};\nexport default feature;\n"],"names":[],"mappings":"AAMA,MAAM,OAAA,GAAuB;AAAA,EACzB,EAAA,EAAI,wBAAA;AAAA,EACJ,MAAM,UAAA,CAAW,MAAA,EAAQ,KAAA,EAAO,GAAA,EAAK;AACjC,IAAA,MAAM,YAAA,GAAqC,GAAA,CAAI,KAAA,CAAM,UAAA,EAAY,sBAAA,EAAwB,UAAU,GAAA,CAAI,CAAC,CAAA,KAAwB,CAAA,CAAE,IAAI,CAAA;AACtI,IAAA,IAAI,CAAC,cAAc,MAAA,EAAQ;AACvB,MAAA,OAAO,EAAC;AAAA,IACZ;AACA,IAAA,MAAM,EAAE,oBAAA,EAAqB,GAAI,MAAM,OAAO,oBAAoB,CAAA;AAClE,IAAA,MAAM,mBAAmB,MAAM,oBAAA;AAAA,MAC3B,GAAA,CAAI,KAAA;AAAA,MACJ,GAAA,CAAI,SAAA;AAAA,MACJ,GAAA,CAAI,QAAA;AAAA,MACJ,YAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA,CAAI,OAAA;AAAA,MACJ,GAAA,CAAI,QAAA;AAAA,MACJ,GAAA,CAAI,WAAA;AAAA,MACJ,GAAA,CAAI;AAAA,KACR;AACA,IAAA,OAAO,EAAE,gBAAA,EAAiB;AAAA,EAC9B;AACJ;;;;"}
@@ -12,6 +12,13 @@ async function parseGltfJsonAsset(buffer, baseUrl) {
12
12
  }
13
13
  return { json, binChunk, baseUrl };
14
14
  }
15
+ async function resolveExternalImage(uri, baseUrl) {
16
+ const response = await fetch(new URL(uri, baseUrl + "x"));
17
+ if (!response.ok) {
18
+ throw new Error(`Failed to load image: ${response.status} ${response.statusText}`);
19
+ }
20
+ return createImageBitmap(await response.blob(), { premultiplyAlpha: "none", colorSpaceConversion: "none" });
21
+ }
15
22
  function resolveBufferUri(uri, baseUrl) {
16
23
  if (baseUrl) {
17
24
  return new URL(uri, baseUrl + "x").href;
@@ -23,5 +30,5 @@ function resolveBufferUri(uri, baseUrl) {
23
30
  }
24
31
  }
25
32
 
26
- export { parseGltfJsonAsset };
33
+ export { parseGltfJsonAsset, resolveExternalImage };
27
34
  //# sourceMappingURL=gltf-json-asset.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-json-asset.js","sources":["../../../src/loader-gltf/gltf-json-asset.ts"],"sourcesContent":["/** Parse a JSON glTF asset and resolve its buffer data. GLB scenes never load this module. */\nexport async function parseGltfJsonAsset(buffer: ArrayBuffer, baseUrl: string): Promise<{ json: any; binChunk: DataView; baseUrl: string }> {\n const json = JSON.parse(new TextDecoder().decode(buffer));\n const buffers = json.buffers ?? [];\n let binChunk: DataView;\n if (buffers.length > 1) {\n const multiBuffer = await import(\"./gltf-multi-buffer.js\");\n binChunk = await multiBuffer.loadMultiBuffer(json, baseUrl);\n } else if (buffers[0]?.uri) {\n binChunk = new DataView(await fetch(resolveBufferUri(buffers[0].uri, baseUrl)).then((r) => r.arrayBuffer()));\n } else {\n binChunk = new DataView(new ArrayBuffer(0));\n }\n return { json, binChunk, baseUrl };\n}\n\n/** Resolve a glTF buffer `uri` to a fetchable URL. With a base URL (the source was a URL string), relative\n * `.bin` paths resolve against it. Without one (ArrayBuffer/Blob source), only self-contained `data:`/\n * absolute URIs are resolvable — a bare relative path has no base and throws a clear error.\n * @internal */\nfunction resolveBufferUri(uri: string, baseUrl: string): string {\n if (baseUrl) {\n return new URL(uri, baseUrl + \"x\").href;\n }\n try {\n // No base: succeeds for data:/absolute URIs, throws for a relative path (which `new URL` rejects).\n return new URL(uri).href;\n } catch {\n throw new Error(\"loadGltf: relative buffer URI needs a base URL.\");\n }\n}\n"],"names":[],"mappings":"AACA,eAAsB,kBAAA,CAAmB,QAAqB,OAAA,EAA8E;AACxI,EAAA,MAAM,IAAA,GAAO,KAAK,KAAA,CAAM,IAAI,aAAY,CAAE,MAAA,CAAO,MAAM,CAAC,CAAA;AACxD,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,EAAC;AACjC,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACpB,IAAA,MAAM,WAAA,GAAc,MAAM,OAAO,wBAAwB,CAAA;AACzD,IAAA,QAAA,GAAW,MAAM,WAAA,CAAY,eAAA,CAAgB,IAAA,EAAM,OAAO,CAAA;AAAA,EAC9D,CAAA,MAAA,IAAW,OAAA,CAAQ,CAAC,CAAA,EAAG,GAAA,EAAK;AACxB,IAAA,QAAA,GAAW,IAAI,QAAA,CAAS,MAAM,MAAM,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,CAAE,GAAA,EAAK,OAAO,CAAC,EAAE,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,WAAA,EAAa,CAAC,CAAA;AAAA,EAC/G,CAAA,MAAO;AACH,IAAA,QAAA,GAAW,IAAI,QAAA,CAAS,IAAI,WAAA,CAAY,CAAC,CAAC,CAAA;AAAA,EAC9C;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAQ;AACrC;AAMA,SAAS,gBAAA,CAAiB,KAAa,OAAA,EAAyB;AAC5D,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,OAAO,IAAI,GAAA,CAAI,GAAA,EAAK,OAAA,GAAU,GAAG,CAAA,CAAE,IAAA;AAAA,EACvC;AACA,EAAA,IAAI;AAEA,IAAA,OAAO,IAAI,GAAA,CAAI,GAAG,CAAA,CAAE,IAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACJ,IAAA,MAAM,IAAI,MAAM,iDAAiD,CAAA;AAAA,EACrE;AACJ;;;;"}
1
+ {"version":3,"file":"gltf-json-asset.js","sources":["../../../src/loader-gltf/gltf-json-asset.ts"],"sourcesContent":["/** Parse a JSON glTF asset and resolve its buffer data. GLB scenes never load this module. */\nexport async function parseGltfJsonAsset(buffer: ArrayBuffer, baseUrl: string): Promise<{ json: any; binChunk: DataView; baseUrl: string }> {\n const json = JSON.parse(new TextDecoder().decode(buffer));\n const buffers = json.buffers ?? [];\n let binChunk: DataView;\n if (buffers.length > 1) {\n const multiBuffer = await import(\"./gltf-multi-buffer.js\");\n binChunk = await multiBuffer.loadMultiBuffer(json, baseUrl);\n } else if (buffers[0]?.uri) {\n binChunk = new DataView(await fetch(resolveBufferUri(buffers[0].uri, baseUrl)).then((r) => r.arrayBuffer()));\n } else {\n binChunk = new DataView(new ArrayBuffer(0));\n }\n return { json, binChunk, baseUrl };\n}\n\n/** Fetch and decode an image referenced by an external glTF URI. */\nexport async function resolveExternalImage(uri: string, baseUrl: string): Promise<ImageBitmap> {\n const response = await fetch(new URL(uri, baseUrl + \"x\"));\n if (!response.ok) {\n throw new Error(`Failed to load image: ${response.status} ${response.statusText}`);\n }\n return createImageBitmap(await response.blob(), { premultiplyAlpha: \"none\", colorSpaceConversion: \"none\" });\n}\n\n/** Resolve a glTF buffer `uri` to a fetchable URL. With a base URL (the source was a URL string), relative\n * `.bin` paths resolve against it. Without one (ArrayBuffer/Blob source), only self-contained `data:`/\n * absolute URIs are resolvable — a bare relative path has no base and throws a clear error.\n * @internal */\nfunction resolveBufferUri(uri: string, baseUrl: string): string {\n if (baseUrl) {\n return new URL(uri, baseUrl + \"x\").href;\n }\n try {\n // No base: succeeds for data:/absolute URIs, throws for a relative path (which `new URL` rejects).\n return new URL(uri).href;\n } catch {\n throw new Error(\"loadGltf: relative buffer URI needs a base URL.\");\n }\n}\n"],"names":[],"mappings":"AACA,eAAsB,kBAAA,CAAmB,QAAqB,OAAA,EAA8E;AACxI,EAAA,MAAM,IAAA,GAAO,KAAK,KAAA,CAAM,IAAI,aAAY,CAAE,MAAA,CAAO,MAAM,CAAC,CAAA;AACxD,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,EAAC;AACjC,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACpB,IAAA,MAAM,WAAA,GAAc,MAAM,OAAO,wBAAwB,CAAA;AACzD,IAAA,QAAA,GAAW,MAAM,WAAA,CAAY,eAAA,CAAgB,IAAA,EAAM,OAAO,CAAA;AAAA,EAC9D,CAAA,MAAA,IAAW,OAAA,CAAQ,CAAC,CAAA,EAAG,GAAA,EAAK;AACxB,IAAA,QAAA,GAAW,IAAI,QAAA,CAAS,MAAM,MAAM,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,CAAE,GAAA,EAAK,OAAO,CAAC,EAAE,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,WAAA,EAAa,CAAC,CAAA;AAAA,EAC/G,CAAA,MAAO;AACH,IAAA,QAAA,GAAW,IAAI,QAAA,CAAS,IAAI,WAAA,CAAY,CAAC,CAAC,CAAA;AAAA,EAC9C;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAQ;AACrC;AAGA,eAAsB,oBAAA,CAAqB,KAAa,OAAA,EAAuC;AAC3F,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,IAAI,IAAI,GAAA,EAAK,OAAA,GAAU,GAAG,CAAC,CAAA;AACxD,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,IAAA,MAAM,IAAI,MAAM,CAAA,sBAAA,EAAyB,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,EACrF;AACA,EAAA,OAAO,iBAAA,CAAkB,MAAM,QAAA,CAAS,IAAA,EAAK,EAAG,EAAE,gBAAA,EAAkB,MAAA,EAAQ,oBAAA,EAAsB,MAAA,EAAQ,CAAA;AAC9G;AAMA,SAAS,gBAAA,CAAiB,KAAa,OAAA,EAAyB;AAC5D,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,OAAO,IAAI,GAAA,CAAI,GAAA,EAAK,OAAA,GAAU,GAAG,CAAA,CAAE,IAAA;AAAA,EACvC;AACA,EAAA,IAAI;AAEA,IAAA,OAAO,IAAI,GAAA,CAAI,GAAG,CAAA,CAAE,IAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACJ,IAAA,MAAM,IAAI,MAAM,iDAAiD,CAAA;AAAA,EACrE;AACJ;;;;"}
@@ -1,25 +1,8 @@
1
1
  import { getTextureImageIndex, resolveImage } from './gltf-parser.js';
2
2
 
3
3
  async function assembleMaterial(json, binChunk, materialIdx, baseUrl, imageCache) {
4
- const mat = json.materials?.[materialIdx];
5
- if (!mat) {
6
- return {
7
- _baseColorFactor: [1, 1, 1, 1],
8
- _metallicFactor: 1,
9
- _roughnessFactor: 1,
10
- _emissiveFactor: [0, 0, 0],
11
- _baseColorImage: null,
12
- _metallicRoughnessImage: null,
13
- _normalImage: null,
14
- _normalScale: 1,
15
- _occlusionTexCoord: 0,
16
- _occlusionImage: null,
17
- _emissiveImage: null,
18
- _doubleSided: false,
19
- _alphaMode: "OPAQUE",
20
- _alphaCutoff: 0.5
21
- };
22
- }
4
+ const rawMat = json.materials?.[materialIdx];
5
+ const mat = rawMat ?? {};
23
6
  const pbr = mat.pbrMetallicRoughness ?? {};
24
7
  const fetchImg = makeImageFetcher(json, binChunk, baseUrl, imageCache);
25
8
  const [baseColorImg, mrImg, normalImg, occlusionImg, emissiveImg] = await Promise.all([
@@ -44,7 +27,7 @@ async function assembleMaterial(json, binChunk, materialIdx, baseUrl, imageCache
44
27
  _doubleSided: !!mat.doubleSided,
45
28
  _alphaMode: mat.alphaMode ?? "OPAQUE",
46
29
  _alphaCutoff: mat.alphaCutoff ?? 0.5,
47
- _rawMatDef: mat
30
+ _rawMatDef: rawMat
48
31
  };
49
32
  }
50
33
  function makeImageFetcher(json, binChunk, baseUrl, imageCache) {
@@ -53,15 +36,7 @@ function makeImageFetcher(json, binChunk, baseUrl, imageCache) {
53
36
  return Promise.resolve(null);
54
37
  }
55
38
  const imgIdx = getTextureImageIndex(json.textures[texInfo.index]);
56
- if (imageCache) {
57
- let cached = imageCache.get(imgIdx);
58
- if (!cached) {
59
- cached = resolveImage(json, binChunk, imgIdx, baseUrl);
60
- imageCache.set(imgIdx, cached);
61
- }
62
- return cached;
63
- }
64
- return resolveImage(json, binChunk, imgIdx, baseUrl);
39
+ return imageCache[imgIdx] ??= resolveImage(json, binChunk, imgIdx, baseUrl);
65
40
  };
66
41
  }
67
42
 
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-material.js","sources":["../../../src/loader-gltf/gltf-material.ts"],"sourcesContent":["/**\n * glTF core PBR material assembly:\n * - Extracts core PBR properties (baseColor, MR, normal, ORM, emissive)\n * - Handles alpha modes and double-sided flag\n *\n * All KHR material extensions (clearcoat, sheen, anisotropy, spec-gloss, ...)\n * are handled by separate `gltf-ext-*.ts` modules driven by the GltfFeature\n * registry in load-gltf.ts. This core file knows ZERO extension names.\n */\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { getTextureImageIndex, resolveImage } from \"./gltf-parser.js\";\n\n/** Per-load context handed to each material extension's `applyMaterial()`. */\nexport interface GltfMatExtCtx {\n /** @internal Internal engine access for dynamic-only texture extensions that must upload directly. */\n _engine: EngineContext;\n /** Fetch + upload a texture from a glTF textureInfo object.\n * Returns undefined if texInfo is null/undefined. */\n /** @internal */\n _texture(texInfo: unknown, sRGB: boolean): Promise<Texture2D | undefined>;\n /** @internal Upload an arbitrary ImageBitmap (e.g. composited bitmap from an ext). */\n _uploadImage(bitmap: ImageBitmap, sRGB: boolean): Texture2D;\n}\n\n/** Parsed core PBR material data. */\nexport interface GltfMaterialData {\n /** @internal */\n _baseColorFactor: [number, number, number, number];\n /** @internal */\n _metallicFactor: number;\n /** @internal */\n _roughnessFactor: number;\n /** @internal */\n _emissiveFactor: [number, number, number];\n /** @internal */\n _baseColorImage: ImageBitmap | null;\n /** @internal */\n _metallicRoughnessImage: ImageBitmap | null;\n /** @internal */\n _normalImage: ImageBitmap | null;\n /** @internal glTF normalTexture.scale (default 1.0). */\n _normalScale: number;\n /** @internal glTF occlusionTexture.texCoord (default 0). */\n _occlusionTexCoord: number;\n /** @internal */\n _occlusionImage: ImageBitmap | null;\n /** @internal */\n _emissiveImage: ImageBitmap | null;\n /** @internal Whether material is double-sided. */\n _doubleSided: boolean;\n /** @internal glTF alphaMode: \"OPAQUE\" (default), \"BLEND\", or \"MASK\". */\n _alphaMode: string;\n /** @internal glTF alphaCutoff for MASK mode (default 0.5). */\n _alphaCutoff: number;\n /** Raw glTF material definition. Always set so ext modules can read raw\n * extension data + KHR_texture_transform from texture infos. */\n /** @internal */\n _rawMatDef?: any;\n}\n\n/** Assemble core PBR material data from a glTF material definition.\n *\n * Per-material extension parsing/fetching is handled by load-gltf.ts using\n * the GltfMatExt registry — this function only fills in the spec-baseline\n * PBR properties shared by every material. */\nexport async function assembleMaterial(\n json: any,\n binChunk: DataView,\n materialIdx: number,\n baseUrl: string,\n imageCache?: Map<number, Promise<ImageBitmap>>\n): Promise<GltfMaterialData> {\n const mat = json.materials?.[materialIdx];\n if (!mat) {\n return {\n _baseColorFactor: [1, 1, 1, 1],\n _metallicFactor: 1,\n _roughnessFactor: 1,\n _emissiveFactor: [0, 0, 0],\n _baseColorImage: null,\n _metallicRoughnessImage: null,\n _normalImage: null,\n _normalScale: 1,\n _occlusionTexCoord: 0,\n _occlusionImage: null,\n _emissiveImage: null,\n _doubleSided: false,\n _alphaMode: \"OPAQUE\",\n _alphaCutoff: 0.5,\n };\n }\n\n const pbr = mat.pbrMetallicRoughness ?? {};\n const fetchImg = makeImageFetcher(json, binChunk, baseUrl, imageCache);\n\n const [baseColorImg, mrImg, normalImg, occlusionImg, emissiveImg] = await Promise.all([\n fetchImg(pbr.baseColorTexture),\n fetchImg(pbr.metallicRoughnessTexture),\n fetchImg(mat.normalTexture),\n fetchImg(mat.occlusionTexture),\n fetchImg(mat.emissiveTexture),\n ]);\n\n return {\n _baseColorFactor: pbr.baseColorFactor ?? [1, 1, 1, 1],\n _metallicFactor: pbr.metallicFactor ?? 1,\n _roughnessFactor: pbr.roughnessFactor ?? 1,\n _emissiveFactor: mat.emissiveFactor ?? [0, 0, 0],\n _baseColorImage: baseColorImg,\n _metallicRoughnessImage: mrImg,\n _normalImage: normalImg,\n _normalScale: typeof mat.normalTexture?.scale === \"number\" ? mat.normalTexture.scale : 1,\n _occlusionTexCoord: typeof mat.occlusionTexture?.texCoord === \"number\" ? mat.occlusionTexture.texCoord : 0,\n _occlusionImage: occlusionImg,\n _emissiveImage: emissiveImg,\n _doubleSided: !!mat.doubleSided,\n _alphaMode: mat.alphaMode ?? \"OPAQUE\",\n _alphaCutoff: mat.alphaCutoff ?? 0.5,\n _rawMatDef: mat,\n };\n}\n\n/** Build a per-load image fetcher that decodes glTF texture references via\n * the shared image cache. Used by both core assembleMaterial and the ext\n * driver in load-gltf.ts. */\nexport function makeImageFetcher(json: any, binChunk: DataView, baseUrl: string, imageCache?: Map<number, Promise<ImageBitmap>>): (texInfo: any) => Promise<ImageBitmap | null> {\n return (texInfo: any): Promise<ImageBitmap | null> => {\n if (!texInfo) {\n return Promise.resolve(null);\n }\n const imgIdx: number = getTextureImageIndex(json.textures[texInfo.index]);\n if (imageCache) {\n let cached = imageCache.get(imgIdx);\n if (!cached) {\n cached = resolveImage(json, binChunk, imgIdx, baseUrl);\n imageCache.set(imgIdx, cached);\n }\n return cached;\n }\n return resolveImage(json, binChunk, imgIdx, baseUrl);\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-material.js","sources":["../../../src/loader-gltf/gltf-material.ts"],"sourcesContent":["/**\n * glTF core PBR material assembly:\n * - Extracts core PBR properties (baseColor, MR, normal, ORM, emissive)\n * - Handles alpha modes and double-sided flag\n *\n * All KHR material extensions (clearcoat, sheen, anisotropy, spec-gloss, ...)\n * are handled by separate `gltf-ext-*.ts` modules driven by the GltfFeature\n * registry in load-gltf.ts. This core file knows ZERO extension names.\n */\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { getTextureImageIndex, resolveImage } from \"./gltf-parser.js\";\n\n/** Per-load context handed to each material extension's `applyMaterial()`. */\nexport interface GltfMatExtCtx {\n /** @internal Internal engine access for dynamic-only texture extensions that must upload directly. */\n _engine: EngineContext;\n /** Fetch + upload a texture from a glTF textureInfo object.\n * Returns undefined if texInfo is null/undefined. */\n /** @internal */\n _texture(texInfo: unknown, sRGB: boolean): Promise<Texture2D | undefined>;\n /** @internal Upload an arbitrary ImageBitmap (e.g. composited bitmap from an ext). */\n _uploadImage(bitmap: ImageBitmap, sRGB: boolean): Texture2D;\n}\n\n/** Parsed core PBR material data. */\nexport interface GltfMaterialData {\n /** @internal */\n _baseColorFactor: [number, number, number, number];\n /** @internal */\n _metallicFactor: number;\n /** @internal */\n _roughnessFactor: number;\n /** @internal */\n _emissiveFactor: [number, number, number];\n /** @internal */\n _baseColorImage: ImageBitmap | null;\n /** @internal */\n _metallicRoughnessImage: ImageBitmap | null;\n /** @internal */\n _normalImage: ImageBitmap | null;\n /** @internal glTF normalTexture.scale (default 1.0). */\n _normalScale: number;\n /** @internal glTF occlusionTexture.texCoord (default 0). */\n _occlusionTexCoord: number;\n /** @internal */\n _occlusionImage: ImageBitmap | null;\n /** @internal */\n _emissiveImage: ImageBitmap | null;\n /** @internal Whether material is double-sided. */\n _doubleSided: boolean;\n /** @internal glTF alphaMode: \"OPAQUE\" (default), \"BLEND\", or \"MASK\". */\n _alphaMode: string;\n /** @internal glTF alphaCutoff for MASK mode (default 0.5). */\n _alphaCutoff: number;\n /** Raw glTF material definition. Always set so ext modules can read raw\n * extension data + KHR_texture_transform from texture infos. */\n /** @internal */\n _rawMatDef?: any;\n}\n\nexport type GltfImageCache = Array<Promise<ImageBitmap> | undefined>;\n\n/** Assemble core PBR material data from a glTF material definition.\n *\n * Per-material extension parsing/fetching is handled by load-gltf.ts using\n * the GltfMatExt registry — this function only fills in the spec-baseline\n * PBR properties shared by every material. */\nexport async function assembleMaterial(json: any, binChunk: DataView, materialIdx: number, baseUrl: string, imageCache: GltfImageCache): Promise<GltfMaterialData> {\n const rawMat = json.materials?.[materialIdx];\n const mat = rawMat ?? {};\n const pbr = mat.pbrMetallicRoughness ?? {};\n const fetchImg = makeImageFetcher(json, binChunk, baseUrl, imageCache);\n\n const [baseColorImg, mrImg, normalImg, occlusionImg, emissiveImg] = await Promise.all([\n fetchImg(pbr.baseColorTexture),\n fetchImg(pbr.metallicRoughnessTexture),\n fetchImg(mat.normalTexture),\n fetchImg(mat.occlusionTexture),\n fetchImg(mat.emissiveTexture),\n ]);\n\n return {\n _baseColorFactor: pbr.baseColorFactor ?? [1, 1, 1, 1],\n _metallicFactor: pbr.metallicFactor ?? 1,\n _roughnessFactor: pbr.roughnessFactor ?? 1,\n _emissiveFactor: mat.emissiveFactor ?? [0, 0, 0],\n _baseColorImage: baseColorImg,\n _metallicRoughnessImage: mrImg,\n _normalImage: normalImg,\n _normalScale: typeof mat.normalTexture?.scale === \"number\" ? mat.normalTexture.scale : 1,\n _occlusionTexCoord: typeof mat.occlusionTexture?.texCoord === \"number\" ? mat.occlusionTexture.texCoord : 0,\n _occlusionImage: occlusionImg,\n _emissiveImage: emissiveImg,\n _doubleSided: !!mat.doubleSided,\n _alphaMode: mat.alphaMode ?? \"OPAQUE\",\n _alphaCutoff: mat.alphaCutoff ?? 0.5,\n _rawMatDef: rawMat,\n };\n}\n\n/** Build a per-load image fetcher that decodes glTF texture references via\n * the shared image cache. Used by both core assembleMaterial and the ext\n * driver in load-gltf.ts. */\nexport function makeImageFetcher(json: any, binChunk: DataView, baseUrl: string, imageCache: GltfImageCache): (texInfo: any) => Promise<ImageBitmap | null> {\n return (texInfo: any): Promise<ImageBitmap | null> => {\n if (!texInfo) {\n return Promise.resolve(null);\n }\n const imgIdx: number = getTextureImageIndex(json.textures[texInfo.index]);\n return (imageCache[imgIdx] ??= resolveImage(json, binChunk, imgIdx, baseUrl));\n };\n}\n"],"names":[],"mappings":";;AAoEA,eAAsB,gBAAA,CAAiB,IAAA,EAAW,QAAA,EAAoB,WAAA,EAAqB,SAAiB,UAAA,EAAuD;AAC/J,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,SAAA,GAAY,WAAW,CAAA;AAC3C,EAAA,MAAM,GAAA,GAAM,UAAU,EAAC;AACvB,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,oBAAA,IAAwB,EAAC;AACzC,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,IAAA,EAAM,QAAA,EAAU,SAAS,UAAU,CAAA;AAErE,EAAA,MAAM,CAAC,cAAc,KAAA,EAAO,SAAA,EAAW,cAAc,WAAW,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI;AAAA,IAClF,QAAA,CAAS,IAAI,gBAAgB,CAAA;AAAA,IAC7B,QAAA,CAAS,IAAI,wBAAwB,CAAA;AAAA,IACrC,QAAA,CAAS,IAAI,aAAa,CAAA;AAAA,IAC1B,QAAA,CAAS,IAAI,gBAAgB,CAAA;AAAA,IAC7B,QAAA,CAAS,IAAI,eAAe;AAAA,GAC/B,CAAA;AAED,EAAA,OAAO;AAAA,IACH,kBAAkB,GAAA,CAAI,eAAA,IAAmB,CAAC,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,IACpD,eAAA,EAAiB,IAAI,cAAA,IAAkB,CAAA;AAAA,IACvC,gBAAA,EAAkB,IAAI,eAAA,IAAmB,CAAA;AAAA,IACzC,iBAAiB,GAAA,CAAI,cAAA,IAAkB,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,IAC/C,eAAA,EAAiB,YAAA;AAAA,IACjB,uBAAA,EAAyB,KAAA;AAAA,IACzB,YAAA,EAAc,SAAA;AAAA,IACd,YAAA,EAAc,OAAO,GAAA,CAAI,aAAA,EAAe,UAAU,QAAA,GAAW,GAAA,CAAI,cAAc,KAAA,GAAQ,CAAA;AAAA,IACvF,kBAAA,EAAoB,OAAO,GAAA,CAAI,gBAAA,EAAkB,aAAa,QAAA,GAAW,GAAA,CAAI,iBAAiB,QAAA,GAAW,CAAA;AAAA,IACzG,eAAA,EAAiB,YAAA;AAAA,IACjB,cAAA,EAAgB,WAAA;AAAA,IAChB,YAAA,EAAc,CAAC,CAAC,GAAA,CAAI,WAAA;AAAA,IACpB,UAAA,EAAY,IAAI,SAAA,IAAa,QAAA;AAAA,IAC7B,YAAA,EAAc,IAAI,WAAA,IAAe,GAAA;AAAA,IACjC,UAAA,EAAY;AAAA,GAChB;AACJ;AAKO,SAAS,gBAAA,CAAiB,IAAA,EAAW,QAAA,EAAoB,OAAA,EAAiB,UAAA,EAA2E;AACxJ,EAAA,OAAO,CAAC,OAAA,KAA8C;AAClD,IAAA,IAAI,CAAC,OAAA,EAAS;AACV,MAAA,OAAO,OAAA,CAAQ,QAAQ,IAAI,CAAA;AAAA,IAC/B;AACA,IAAA,MAAM,SAAiB,oBAAA,CAAqB,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,KAAK,CAAC,CAAA;AACxE,IAAA,OAAQ,WAAW,MAAM,CAAA,KAAM,aAAa,IAAA,EAAM,QAAA,EAAU,QAAQ,OAAO,CAAA;AAAA,EAC/E,CAAA;AACJ;;;;"}
@@ -82,14 +82,7 @@ async function resolveImage(json, binChunk, imageIdx, baseUrl) {
82
82
  return createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" });
83
83
  }
84
84
  if (image.uri) {
85
- const imageUrl = new URL(image.uri, baseUrl + "x").href;
86
- const response = await fetch(imageUrl);
87
- if (!response.ok) {
88
- throw new Error(`Failed to load image: ${response.status} ${response.statusText}`);
89
- }
90
- const blob = await response.blob();
91
- const bmp = await createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" });
92
- return bmp;
85
+ return (await import('./gltf-json-asset.js')).resolveExternalImage(image.uri, baseUrl);
93
86
  }
94
87
  throw new Error("Image has neither bufferView nor uri");
95
88
  }