@babylonjs/lite 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/index.js +444 -368
  3. package/dist/index.js.map +1 -1
  4. package/index.d.ts +208 -6
  5. package/lib/asset-container.js.map +1 -1
  6. package/lib/audio/index.js +1 -0
  7. package/lib/audio/index.js.map +1 -1
  8. package/lib/audio/media-stream-output.js +44 -0
  9. package/lib/audio/media-stream-output.js.map +1 -0
  10. package/lib/engine/engine.js +1 -1
  11. package/lib/frame-graph/render-task.js +1 -1
  12. package/lib/frame-graph/render-task.js.map +1 -1
  13. package/lib/frame-graph/shadow-inputs.js +10 -1
  14. package/lib/frame-graph/shadow-inputs.js.map +1 -1
  15. package/lib/frame-graph/transmission.js +1 -16
  16. package/lib/frame-graph/transmission.js.map +1 -1
  17. package/lib/index.js +6 -3
  18. package/lib/index.js.map +1 -1
  19. package/lib/loader-gltf/gltf-json-asset.js +1 -1
  20. package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
  21. package/lib/loader-gltf/gltf-multi-buffer.js +2 -10
  22. package/lib/loader-gltf/gltf-multi-buffer.js.map +1 -1
  23. package/lib/loader-gltf/gltf-pbr-builder-ext.js +4 -18
  24. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  25. package/lib/loader-gltf/gltf-pbr-builder.js +17 -14
  26. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  27. package/lib/loader-gltf/gltf-sampler-desc.js +3 -16
  28. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  29. package/lib/material/node/blocks/ambient-occlusion-block.js +1 -4
  30. package/lib/material/node/blocks/ambient-occlusion-block.js.map +1 -1
  31. package/lib/material/node/blocks/biplanar-block.js +3 -6
  32. package/lib/material/node/blocks/biplanar-block.js.map +1 -1
  33. package/lib/material/node/blocks/image-source.js +2 -5
  34. package/lib/material/node/blocks/image-source.js.map +1 -1
  35. package/lib/material/node/blocks/input-block.js +5 -26
  36. package/lib/material/node/blocks/input-block.js.map +1 -1
  37. package/lib/material/node/blocks/pbr-mr-helper-full.js +5 -8
  38. package/lib/material/node/blocks/pbr-mr-helper-full.js.map +1 -1
  39. package/lib/material/node/blocks/reflection-texture-block.js +1 -4
  40. package/lib/material/node/blocks/reflection-texture-block.js.map +1 -1
  41. package/lib/material/node/blocks/texture-block.js +2 -5
  42. package/lib/material/node/blocks/texture-block.js.map +1 -1
  43. package/lib/material/node/blocks/triplanar-block.js +3 -6
  44. package/lib/material/node/blocks/triplanar-block.js.map +1 -1
  45. package/lib/material/node/node-emitter.js +24 -1
  46. package/lib/material/node/node-emitter.js.map +1 -1
  47. package/lib/material/node/node-geometry-renderable.js +2 -2
  48. package/lib/material/node/node-material.js +1 -22
  49. package/lib/material/node/node-material.js.map +1 -1
  50. package/lib/material/node/node-types.js.map +1 -1
  51. package/lib/material/pbr/fragments/shadow-only-fragment.js +68 -0
  52. package/lib/material/pbr/fragments/shadow-only-fragment.js.map +1 -0
  53. package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
  54. package/lib/material/pbr/pbr-material.js.map +1 -1
  55. package/lib/material/pbr/pbr-primitive-resolver.js +2 -2
  56. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -1
  57. package/lib/material/pbr/pbr-renderable.js +3 -0
  58. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  59. package/lib/material/pbr/pbr-template.js +1 -0
  60. package/lib/material/pbr/pbr-template.js.map +1 -1
  61. package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js +2 -8
  62. package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js.map +1 -1
  63. package/lib/mesh/get-mesh-geometry.js +24 -0
  64. package/lib/mesh/get-mesh-geometry.js.map +1 -0
  65. package/lib/mesh/mesh-dispose.js +2 -0
  66. package/lib/mesh/mesh-dispose.js.map +1 -1
  67. package/lib/mesh/mesh.js +2 -8
  68. package/lib/mesh/mesh.js.map +1 -1
  69. package/lib/mesh/thin-instance-cull-binding.js +54 -3
  70. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  71. package/lib/mesh/thin-instance-gpu-culling.js +241 -24
  72. package/lib/mesh/thin-instance-gpu-culling.js.map +1 -1
  73. package/lib/mesh/thin-instance.js +112 -1
  74. package/lib/mesh/thin-instance.js.map +1 -1
  75. package/lib/picking/gpu-picker.js +4 -2
  76. package/lib/picking/gpu-picker.js.map +1 -1
  77. package/lib/picking/picking-pipeline.js +1 -1
  78. package/lib/picking/picking-pipeline.js.map +1 -1
  79. package/lib/picking/picking-shader.js +38 -16
  80. package/lib/picking/picking-shader.js.map +1 -1
  81. package/lib/scene/scene-core.js +7 -2
  82. package/lib/scene/scene-core.js.map +1 -1
  83. package/lib/scene/scene-node.js +2 -6
  84. package/lib/scene/scene-node.js.map +1 -1
  85. package/lib/scene/scene-remove.js +70 -1
  86. package/lib/scene/scene-remove.js.map +1 -1
  87. package/lib/scene/transform-node.js +3 -0
  88. package/lib/scene/transform-node.js.map +1 -1
  89. package/lib/scene/world-matrix-state.js +7 -1
  90. package/lib/scene/world-matrix-state.js.map +1 -1
  91. package/lib/shadow/csm-directional-shadow-generator.js +22 -1
  92. package/lib/shadow/csm-directional-shadow-generator.js.map +1 -1
  93. package/lib/shadow/csm-shadow-task-hooks.js +19 -11
  94. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  95. package/lib/shadow/esm-directional-shadow-generator.js +2 -51
  96. package/lib/shadow/esm-directional-shadow-generator.js.map +1 -1
  97. package/lib/shadow/pcf-directional-shadow-generator.js +2 -48
  98. package/lib/shadow/pcf-directional-shadow-generator.js.map +1 -1
  99. package/lib/shadow/shadow-base.js +48 -1
  100. package/lib/shadow/shadow-base.js.map +1 -1
  101. package/lib/text/text-renderable.js +2 -10
  102. package/lib/text/text-renderable.js.map +1 -1
  103. package/lib/texture/texture-array.js +96 -0
  104. package/lib/texture/texture-array.js.map +1 -0
  105. package/package.json +3 -3
@@ -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/** 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
+ {"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 */\nexport function 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;AAMO,SAAS,gBAAA,CAAiB,KAAa,OAAA,EAAyB;AACnE,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,3 +1,5 @@
1
+ import { resolveBufferUri } from './gltf-json-asset.js';
2
+
1
3
  async function loadMultiBuffer(json, baseUrl) {
2
4
  const buffers = json.buffers ?? [];
3
5
  const arrays = await Promise.all(buffers.map((b) => b?.uri ? fetch(resolveBufferUri(b.uri, baseUrl)).then((r) => r.arrayBuffer()) : Promise.resolve(new ArrayBuffer(0))));
@@ -18,16 +20,6 @@ async function loadMultiBuffer(json, baseUrl) {
18
20
  }
19
21
  return new DataView(out.buffer);
20
22
  }
21
- function resolveBufferUri(uri, baseUrl) {
22
- if (baseUrl) {
23
- return new URL(uri, baseUrl + "x").href;
24
- }
25
- try {
26
- return new URL(uri).href;
27
- } catch {
28
- throw new Error("loadGltf: relative buffer URI needs a base URL.");
29
- }
30
- }
31
23
 
32
24
  export { loadMultiBuffer };
33
25
  //# sourceMappingURL=gltf-multi-buffer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-multi-buffer.js","sources":["../../../src/loader-gltf/gltf-multi-buffer.ts"],"sourcesContent":["/** Multi-buffer glTF support.\n *\n * Dynamic-imported by load-gltf.ts ONLY when an asset declares more than one\n * buffer (separate geometry / animation / skin `.bin` files — e.g. Khronos\n * AnimatedTriangle, SimpleSkin). Single-buffer glTF and GLB assets never load\n * this module, so they pay zero bytes for it.\n *\n * Lite reads every accessor through one `binChunk` DataView and ignores\n * `bufferView.buffer`. To keep that fast single-chunk reader unchanged, this\n * fetches all buffers, concatenates them (each 4-byte aligned so float\n * accessors stay aligned), and rewrites every `bufferView.byteOffset` to its\n * global position in the concatenated chunk (and resets `buffer` to 0). */\n\nexport async function loadMultiBuffer(json: any, baseUrl: string): Promise<DataView> {\n const buffers: any[] = json.buffers ?? [];\n const arrays = await Promise.all(buffers.map((b) => (b?.uri ? fetch(resolveBufferUri(b.uri, baseUrl)).then((r) => r.arrayBuffer()) : Promise.resolve(new ArrayBuffer(0)))));\n\n // Place each buffer at a 4-byte-aligned offset; record starts for the rewrite.\n const offsets: number[] = [];\n let total = 0;\n for (const a of arrays) {\n offsets.push(total);\n total += a.byteLength;\n total = (total + 3) & ~3;\n }\n\n const out = new Uint8Array(total);\n for (let i = 0; i < arrays.length; i++) {\n out.set(new Uint8Array(arrays[i]!), offsets[i]!);\n }\n\n for (const bv of json.bufferViews ?? []) {\n bv.byteOffset = (bv.byteOffset ?? 0) + offsets[bv.buffer ?? 0]!;\n bv.buffer = 0;\n }\n\n return new DataView(out.buffer);\n}\n\nfunction resolveBufferUri(uri: string, baseUrl: string): string {\n if (baseUrl) {\n return new URL(uri, baseUrl + \"x\").href;\n }\n try {\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":"AAaA,eAAsB,eAAA,CAAgB,MAAW,OAAA,EAAoC;AACjF,EAAA,MAAM,OAAA,GAAiB,IAAA,CAAK,OAAA,IAAW,EAAC;AACxC,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAO,CAAA,EAAG,GAAA,GAAM,KAAA,CAAM,gBAAA,CAAiB,EAAE,GAAA,EAAK,OAAO,CAAC,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,aAAa,CAAA,GAAI,OAAA,CAAQ,OAAA,CAAQ,IAAI,WAAA,CAAY,CAAC,CAAC,CAAE,CAAC,CAAA;AAG1K,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACpB,IAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAClB,IAAA,KAAA,IAAS,CAAA,CAAE,UAAA;AACX,IAAA,KAAA,GAAS,KAAA,GAAQ,IAAK,EAAC;AAAA,EAC3B;AAEA,EAAA,MAAM,GAAA,GAAM,IAAI,UAAA,CAAW,KAAK,CAAA;AAChC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AACpC,IAAA,GAAA,CAAI,GAAA,CAAI,IAAI,UAAA,CAAW,MAAA,CAAO,CAAC,CAAE,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAE,CAAA;AAAA,EACnD;AAEA,EAAA,KAAA,MAAW,EAAA,IAAM,IAAA,CAAK,WAAA,IAAe,EAAC,EAAG;AACrC,IAAA,EAAA,CAAG,cAAc,EAAA,CAAG,UAAA,IAAc,KAAK,OAAA,CAAQ,EAAA,CAAG,UAAU,CAAC,CAAA;AAC7D,IAAA,EAAA,CAAG,MAAA,GAAS,CAAA;AAAA,EAChB;AAEA,EAAA,OAAO,IAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AAClC;AAEA,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;AACA,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-multi-buffer.js","sources":["../../../src/loader-gltf/gltf-multi-buffer.ts"],"sourcesContent":["/** Multi-buffer glTF support.\n *\n * Dynamic-imported by load-gltf.ts ONLY when an asset declares more than one\n * buffer (separate geometry / animation / skin `.bin` files — e.g. Khronos\n * AnimatedTriangle, SimpleSkin). Single-buffer glTF and GLB assets never load\n * this module, so they pay zero bytes for it.\n *\n * Lite reads every accessor through one `binChunk` DataView and ignores\n * `bufferView.buffer`. To keep that fast single-chunk reader unchanged, this\n * fetches all buffers, concatenates them (each 4-byte aligned so float\n * accessors stay aligned), and rewrites every `bufferView.byteOffset` to its\n * global position in the concatenated chunk (and resets `buffer` to 0). */\n\nimport { resolveBufferUri } from \"./gltf-json-asset.js\";\n\nexport async function loadMultiBuffer(json: any, baseUrl: string): Promise<DataView> {\n const buffers: any[] = json.buffers ?? [];\n const arrays = await Promise.all(buffers.map((b) => (b?.uri ? fetch(resolveBufferUri(b.uri, baseUrl)).then((r) => r.arrayBuffer()) : Promise.resolve(new ArrayBuffer(0)))));\n\n // Place each buffer at a 4-byte-aligned offset; record starts for the rewrite.\n const offsets: number[] = [];\n let total = 0;\n for (const a of arrays) {\n offsets.push(total);\n total += a.byteLength;\n total = (total + 3) & ~3;\n }\n\n const out = new Uint8Array(total);\n for (let i = 0; i < arrays.length; i++) {\n out.set(new Uint8Array(arrays[i]!), offsets[i]!);\n }\n\n for (const bv of json.bufferViews ?? []) {\n bv.byteOffset = (bv.byteOffset ?? 0) + offsets[bv.buffer ?? 0]!;\n bv.buffer = 0;\n }\n\n return new DataView(out.buffer);\n}\n"],"names":[],"mappings":";;AAeA,eAAsB,eAAA,CAAgB,MAAW,OAAA,EAAoC;AACjF,EAAA,MAAM,OAAA,GAAiB,IAAA,CAAK,OAAA,IAAW,EAAC;AACxC,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAO,CAAA,EAAG,GAAA,GAAM,KAAA,CAAM,gBAAA,CAAiB,EAAE,GAAA,EAAK,OAAO,CAAC,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,aAAa,CAAA,GAAI,OAAA,CAAQ,OAAA,CAAQ,IAAI,WAAA,CAAY,CAAC,CAAC,CAAE,CAAC,CAAA;AAG1K,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACpB,IAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAClB,IAAA,KAAA,IAAS,CAAA,CAAE,UAAA;AACX,IAAA,KAAA,GAAS,KAAA,GAAQ,IAAK,EAAC;AAAA,EAC3B;AAEA,EAAA,MAAM,GAAA,GAAM,IAAI,UAAA,CAAW,KAAK,CAAA;AAChC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AACpC,IAAA,GAAA,CAAI,GAAA,CAAI,IAAI,UAAA,CAAW,MAAA,CAAO,CAAC,CAAE,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAE,CAAA;AAAA,EACnD;AAEA,EAAA,KAAA,MAAW,EAAA,IAAM,IAAA,CAAK,WAAA,IAAe,EAAC,EAAG;AACrC,IAAA,EAAA,CAAG,cAAc,EAAA,CAAG,UAAA,IAAc,KAAK,OAAA,CAAQ,EAAA,CAAG,UAAU,CAAC,CAAA;AAC7D,IAAA,EAAA,CAAG,MAAA,GAAS,CAAA;AAAA,EAChB;AAEA,EAAA,OAAO,IAAI,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AAClC;;;;"}
@@ -1,8 +1,6 @@
1
- import { U8 } from '../engine/typed-arrays.js';
2
1
  import { cloneTexture2D } from '../texture/texture-2d.js';
3
2
  import { getPbrGroupBuilder } from '../material/pbr/pbr-material.js';
4
- import { linearToSrgbByte } from '../math/color.js';
5
- import { uploadTex } from './gltf-pbr-builder.js';
3
+ import { uploadBaseColorFactorTexture, uploadOrmFactorTexture, uploadTex } from './gltf-pbr-builder.js';
6
4
 
7
5
  function wrapTexCoord(tex, texInfo) {
8
6
  if (!texInfo) {
@@ -51,17 +49,7 @@ function buildDefaultPbrTexturesExt(engine, mat, sampler, generateMipmaps, getCa
51
49
  };
52
50
  const raw = mat._rawMatDef ?? {};
53
51
  const pbr = raw.pbrMetallicRoughness ?? {};
54
- const baseColorTexture = mat._baseColorImage ? wrap(pickTex(mat._baseColorImage, true, pbr.baseColorTexture), pbr.baseColorTexture) : (() => {
55
- const f = mat._baseColorFactor;
56
- return uploadTex(
57
- engine,
58
- null,
59
- true,
60
- sampler,
61
- generateMipmaps,
62
- new U8([linearToSrgbByte(f[0]), linearToSrgbByte(f[1]), linearToSrgbByte(f[2]), Math.round(Math.max(0, Math.min(1, f[3])) * 255)])
63
- );
64
- })();
52
+ const baseColorTexture = mat._baseColorImage ? wrap(pickTex(mat._baseColorImage, true, pbr.baseColorTexture), pbr.baseColorTexture) : uploadBaseColorFactorTexture(engine, mat._baseColorFactor, sampler, generateMipmaps);
65
53
  const normalTexture = mat._normalImage ? wrap(pickTex(mat._normalImage, false, raw.normalTexture), raw.normalTexture) : void 0;
66
54
  const emissiveTexture = mat._emissiveImage ? wrap(pickTex(mat._emissiveImage, true, raw.emissiveTexture), raw.emissiveTexture) : void 0;
67
55
  const occlusionOnUv2 = mat._occlusionTexCoord !== 0 && mat._occlusionImage && !mat._metallicRoughnessImage;
@@ -69,15 +57,13 @@ function buildDefaultPbrTexturesExt(engine, mat, sampler, generateMipmaps, getCa
69
57
  const single = mat._metallicRoughnessImage ?? (occlusionOnUv2 ? null : mat._occlusionImage);
70
58
  let ormTexture;
71
59
  if (occlusionOnUv2) {
72
- const clamp = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255);
73
- ormTexture = uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));
60
+ ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, sampler, generateMipmaps);
74
61
  occlusionTexture = wrap(pickTex(mat._occlusionImage, false, raw.occlusionTexture), raw.occlusionTexture);
75
62
  } else if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {
76
63
  const ormTi = mat._metallicRoughnessImage ? pbr.metallicRoughnessTexture : raw.occlusionTexture;
77
64
  ormTexture = wrap(pickTex(single, false, ormTi), ormTi);
78
65
  } else if (!single) {
79
- const clamp = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255);
80
- ormTexture = uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));
66
+ ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, sampler, generateMipmaps);
81
67
  } else {
82
68
  ormTexture = wrap(pickTex(mat._metallicRoughnessImage, false, pbr.metallicRoughnessTexture), pbr.metallicRoughnessTexture);
83
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-pbr-builder-ext.js","sources":["../../../src/loader-gltf/gltf-pbr-builder-ext.ts"],"sourcesContent":["/** Lazy-loaded slow path for PBR material assembly.\n * Only pulled into bundles whose glTF uses features that require per-texture\n * wrapping (e.g. KHR_texture_transform) or occlusion on UV2 (texCoord=1 with\n * no shared MR image). Scene1 (BoomBox) and any vanilla-PBR glTF skip this\n * module entirely. */\n\nimport { U8 } from \"../engine/typed-arrays.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport { cloneTexture2D } from \"../texture/texture-2d.js\";\nimport type { PbrMaterialProps } from \"../material/pbr/pbr-material.js\";\nimport { getPbrGroupBuilder } from \"../material/pbr/pbr-material.js\";\nimport type { GltfMaterialData } from \"./gltf-material.js\";\nimport { linearToSrgbByte } from \"../math/color.js\";\nimport type { TextureWrapFn, GenerateMipmapsFn } from \"./gltf-pbr-builder.js\";\nimport { uploadTex } from \"./gltf-pbr-builder.js\";\n\nexport interface PbrTexturesExt {\n baseColorTexture: Texture2D;\n ormTexture: Texture2D;\n normalTexture: Texture2D | undefined;\n emissiveTexture: Texture2D | undefined;\n occlusionTexture: Texture2D | undefined;\n}\n\n/** Stamp `_texCoord=1` on a clone when textureInfo selects UV1 and the\n * wrapTex layer didn't already set it (i.e. scene has no KHR_texture_transform). */\nfunction wrapTexCoord(tex: Texture2D, texInfo: unknown): Texture2D {\n if (!texInfo) {\n return tex;\n }\n if ((tex as { _texCoord?: 0 | 1 })._texCoord === 1) {\n return tex;\n }\n const ti = texInfo as { texCoord?: number; extensions?: { KHR_texture_transform?: { texCoord?: number } } };\n const tc = ti.extensions?.KHR_texture_transform?.texCoord ?? ti.texCoord;\n return tc === 1 ? cloneTexture2D(tex, { _texCoord: 1 }) : tex;\n}\n\n/** True when occlusion shares the ORM image with metallic-roughness but must be sampled\n * with its OWN UV transform — i.e. occlusion references a distinct glTF texture object,\n * or it carries its own KHR_texture_transform that an animation pointer can drive apart\n * from the MR transform. Drives the orm-unpack split in buildDefaultPbrTexturesExt. */\nfunction occlusionNeedsSplit(raw: {\n occlusionTexture?: { index?: number; extensions?: { KHR_texture_transform?: unknown } };\n pbrMetallicRoughness?: { metallicRoughnessTexture?: { index?: number } };\n}): boolean {\n const occ = raw.occlusionTexture;\n const mr = raw.pbrMetallicRoughness?.metallicRoughnessTexture;\n if (!occ || !mr) {\n return false;\n }\n return occ.index !== mr.index || occ.extensions?.KHR_texture_transform != null;\n}\n\n/** Build textures with wrapTex + occlusionOnUv2 support. Mirrors master's\n * default texture building but honors per-textureInfo wrapping so\n * KHR_texture_transform can attach per-texture UV state. */\nexport function buildDefaultPbrTexturesExt(\n engine: EngineContext,\n mat: GltfMaterialData,\n sampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n getCachedTex: (bitmap: ImageBitmap, srgb: boolean) => Texture2D,\n wrapTex: TextureWrapFn,\n samplerFor?: (texInfo: unknown) => GPUSampler\n): PbrTexturesExt {\n const wrap: TextureWrapFn = (tex, ti) => wrapTexCoord(wrapTex(tex, ti), ti);\n // When the asset declares non-default glTF samplers, upload each texture with its own\n // sampler (wrap/filter), caching per (sampler, image, srgb) exactly like the fast-path\n // buildSampledPbrTextures. Without samplerFor (common case) reuse the shared default-\n // sampler cache. A GPUTexture is sampler-independent, so the same image with two\n // samplers yields two Texture2D wrappers, matching the fast path.\n const _localCache = samplerFor ? new Map<GPUSampler, Map<number, Texture2D>>() : null;\n const _ids = samplerFor ? new Map<ImageBitmap, number>() : null;\n let _nextId = 0;\n const pickTex = (image: ImageBitmap, srgb: boolean, texInfo: unknown): Texture2D => {\n if (!samplerFor) {\n return getCachedTex(image, srgb);\n }\n const s = samplerFor(texInfo);\n let bySampler = _localCache!.get(s);\n if (!bySampler) {\n _localCache!.set(s, (bySampler = new Map()));\n }\n let id = _ids!.get(image);\n if (id === undefined) {\n _ids!.set(image, (id = _nextId++));\n }\n const key = id * 2 + (srgb ? 1 : 0);\n let tex = bySampler.get(key);\n if (!tex) {\n tex = uploadTex(engine, image, srgb, s, generateMipmaps);\n bySampler.set(key, tex);\n }\n return tex;\n };\n const raw = mat._rawMatDef ?? {};\n const pbr = raw.pbrMetallicRoughness ?? {};\n const baseColorTexture = mat._baseColorImage\n ? wrap(pickTex(mat._baseColorImage, true, pbr.baseColorTexture), pbr.baseColorTexture)\n : (() => {\n const f = mat._baseColorFactor;\n return uploadTex(\n engine,\n null,\n true,\n sampler,\n generateMipmaps,\n new U8([linearToSrgbByte(f[0]), linearToSrgbByte(f[1]), linearToSrgbByte(f[2]), Math.round(Math.max(0, Math.min(1, f[3])) * 255)])\n );\n })();\n const normalTexture = mat._normalImage ? wrap(pickTex(mat._normalImage, false, raw.normalTexture), raw.normalTexture) : undefined;\n const emissiveTexture = mat._emissiveImage ? wrap(pickTex(mat._emissiveImage, true, raw.emissiveTexture), raw.emissiveTexture) : undefined;\n\n const occlusionOnUv2 = mat._occlusionTexCoord !== 0 && mat._occlusionImage && !mat._metallicRoughnessImage;\n let occlusionTexture: Texture2D | undefined;\n const single = mat._metallicRoughnessImage ?? (occlusionOnUv2 ? null : mat._occlusionImage);\n let ormTexture: Texture2D;\n if (occlusionOnUv2) {\n const clamp = (v: number) => Math.round(Math.max(0, Math.min(1, v)) * 255);\n ormTexture = uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));\n occlusionTexture = wrap(pickTex(mat._occlusionImage!, false, raw.occlusionTexture), raw.occlusionTexture);\n } else if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {\n const ormTi = mat._metallicRoughnessImage ? pbr.metallicRoughnessTexture : raw.occlusionTexture;\n ormTexture = wrap(pickTex(single, false, ormTi), ormTi);\n } else if (!single) {\n const clamp = (v: number) => Math.round(Math.max(0, Math.min(1, v)) * 255);\n ormTexture = uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));\n } else {\n ormTexture = wrap(pickTex(mat._metallicRoughnessImage!, false, pbr.metallicRoughnessTexture), pbr.metallicRoughnessTexture);\n }\n // Independent-occlusion UV transform (orm-unpack): occlusion and metallic-roughness\n // share the ORM texture (same image), but the glTF gives occlusion its OWN\n // KHR_texture_transform (or a distinct texture object) so the two can be animated\n // independently via KHR_animation_pointer. Sampling occlusion with MR's transform\n // (the single ormUV) would wrongly animate it. Build a transform-carrying occlusion\n // texture (shares the ORM GPU image) so the shader can sample occlusion with occlUV.\n // Requires the same underlying image as the ORM texture, since the shader re-samples\n // ormTexture at occlUV.\n if (!occlusionTexture && mat._occlusionImage && mat._occlusionImage === mat._metallicRoughnessImage && occlusionNeedsSplit(raw)) {\n occlusionTexture = wrap(pickTex(mat._occlusionImage, false, raw.occlusionTexture), raw.occlusionTexture);\n }\n return { baseColorTexture, ormTexture, normalTexture, emissiveTexture, occlusionTexture };\n}\n\n/** Slow-path assembly: adds occlusionTexCoord and occlusionTexture props. */\nexport function assemblePbrPropsExt(mat: GltfMaterialData, tex: PbrTexturesExt, extLayers: Partial<PbrMaterialProps> | undefined): PbrMaterialProps {\n const ef = mat._emissiveFactor;\n // See gltf-pbr-builder.ts: emissiveFactor [1,1,1] is a no-op only with an emissive texture;\n // with no texture it is a real full-white emissive that must be applied (Material_03).\n const defaultFactor = (ef[0] === 0 && ef[1] === 0 && ef[2] === 0) || (!!tex.emissiveTexture && ef[0] === 1 && ef[1] === 1 && ef[2] === 1);\n // Precompute UV-transform presence so the renderer doesn't scan 5 textures\n // per mesh. Any wrapped texture with `_hasTx=true` (set by gltf-ext-uv-transform)\n // flips this once at build time; omitted entirely on fast path.\n const hasAnyUvTx =\n !!(tex.baseColorTexture as { _hasTx?: true })._hasTx ||\n !!(tex.normalTexture as { _hasTx?: true } | undefined)?._hasTx ||\n !!(tex.ormTexture as { _hasTx?: true })._hasTx ||\n !!(tex.emissiveTexture as { _hasTx?: true } | undefined)?._hasTx ||\n !!(tex.occlusionTexture as { _hasTx?: true } | undefined)?._hasTx;\n // Per-channel UV1 (TEXCOORD_1) selection bitmask. Computed here on the slow path — the only\n // place a texture can carry texCoord:1 — so the always-loaded fast path just reads `_uv2Mask`.\n // Bit literals are a private contract with createPbrTemplateExt's decode (baseColor=1, orm=2,\n // normal=4, emissive=8, specGloss=16, occlusion=32). specGloss arrives via extLayers. Occlusion\n // reads `mat._occlusionTexCoord` (set from the glTF material's occlusionTexture.texCoord for\n // every path, including KHR_texture_basisu, since occlusion-on-UV1 always routes through here).\n const tc1 = (t: unknown): boolean => (t as { _texCoord?: number } | undefined)?._texCoord === 1;\n const uv2Mask =\n (tc1(tex.baseColorTexture) ? 1 : 0) |\n (tc1(tex.ormTexture) ? 2 : 0) |\n (tc1(tex.normalTexture) ? 4 : 0) |\n (tc1(tex.emissiveTexture) ? 8 : 0) |\n (tc1((extLayers as { specGlossTexture?: unknown } | undefined)?.specGlossTexture) ? 16 : 0) |\n (mat._occlusionTexCoord === 1 ? 32 : 0);\n return {\n baseColorTexture: tex.baseColorTexture,\n normalTexture: tex.normalTexture,\n ormTexture: tex.ormTexture,\n emissiveTexture: tex.emissiveTexture,\n ...(mat._baseColorImage && !isDefaultBaseColorFactor(mat._baseColorFactor) ? { baseColorFactor: mat._baseColorFactor } : undefined),\n doubleSided: mat._doubleSided,\n occlusionStrength: mat._occlusionImage ? 1.0 : 0,\n ...(mat._occlusionTexCoord ? { occlusionTexCoord: mat._occlusionTexCoord } : undefined),\n ...(tex.occlusionTexture ? { occlusionTexture: tex.occlusionTexture } : undefined),\n ...(mat._normalScale !== 1 ? { normalTextureScale: mat._normalScale } : undefined),\n ...(mat._metallicRoughnessImage ? { metallicFactor: mat._metallicFactor, roughnessFactor: mat._roughnessFactor } : undefined),\n ...(!defaultFactor ? { emissiveColor: [ef[0], ef[1], ef[2]] as [number, number, number] } : undefined),\n enableSpecularAA: true,\n ...(mat._alphaMode === \"BLEND\" ? { alphaBlend: true, alpha: mat._baseColorFactor[3] } : undefined),\n ...(mat._alphaMode === \"MASK\" ? { alpha: mat._baseColorFactor[3], alphaCutOff: mat._alphaCutoff } : undefined),\n ...(hasAnyUvTx ? { _hasUvTx: true } : undefined),\n ...(mat._rawMatDef?.name ? { name: mat._rawMatDef.name as string } : undefined),\n ...extLayers,\n ...(uv2Mask ? { _uv2Mask: uv2Mask } : undefined),\n _buildGroup: getPbrGroupBuilder(),\n _uboVersion: 0,\n } as PbrMaterialProps;\n}\n\nfunction isDefaultBaseColorFactor(f: readonly number[]): boolean {\n return f[0] === 1 && f[1] === 1 && f[2] === 1 && f[3] === 1;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-pbr-builder-ext.js","sources":["../../../src/loader-gltf/gltf-pbr-builder-ext.ts"],"sourcesContent":["/** Lazy-loaded slow path for PBR material assembly.\n * Only pulled into bundles whose glTF uses features that require per-texture\n * wrapping (e.g. KHR_texture_transform) or occlusion on UV2 (texCoord=1 with\n * no shared MR image). Scene1 (BoomBox) and any vanilla-PBR glTF skip this\n * module entirely. */\n\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport { cloneTexture2D } from \"../texture/texture-2d.js\";\nimport type { PbrMaterialProps } from \"../material/pbr/pbr-material.js\";\nimport { getPbrGroupBuilder } from \"../material/pbr/pbr-material.js\";\nimport type { GltfMaterialData } from \"./gltf-material.js\";\nimport type { TextureWrapFn, GenerateMipmapsFn } from \"./gltf-pbr-builder.js\";\nimport { uploadBaseColorFactorTexture, uploadOrmFactorTexture, uploadTex } from \"./gltf-pbr-builder.js\";\n\nexport interface PbrTexturesExt {\n baseColorTexture: Texture2D;\n ormTexture: Texture2D;\n normalTexture: Texture2D | undefined;\n emissiveTexture: Texture2D | undefined;\n occlusionTexture: Texture2D | undefined;\n}\n\n/** Stamp `_texCoord=1` on a clone when textureInfo selects UV1 and the\n * wrapTex layer didn't already set it (i.e. scene has no KHR_texture_transform). */\nfunction wrapTexCoord(tex: Texture2D, texInfo: unknown): Texture2D {\n if (!texInfo) {\n return tex;\n }\n if ((tex as { _texCoord?: 0 | 1 })._texCoord === 1) {\n return tex;\n }\n const ti = texInfo as { texCoord?: number; extensions?: { KHR_texture_transform?: { texCoord?: number } } };\n const tc = ti.extensions?.KHR_texture_transform?.texCoord ?? ti.texCoord;\n return tc === 1 ? cloneTexture2D(tex, { _texCoord: 1 }) : tex;\n}\n\n/** True when occlusion shares the ORM image with metallic-roughness but must be sampled\n * with its OWN UV transform — i.e. occlusion references a distinct glTF texture object,\n * or it carries its own KHR_texture_transform that an animation pointer can drive apart\n * from the MR transform. Drives the orm-unpack split in buildDefaultPbrTexturesExt. */\nfunction occlusionNeedsSplit(raw: {\n occlusionTexture?: { index?: number; extensions?: { KHR_texture_transform?: unknown } };\n pbrMetallicRoughness?: { metallicRoughnessTexture?: { index?: number } };\n}): boolean {\n const occ = raw.occlusionTexture;\n const mr = raw.pbrMetallicRoughness?.metallicRoughnessTexture;\n if (!occ || !mr) {\n return false;\n }\n return occ.index !== mr.index || occ.extensions?.KHR_texture_transform != null;\n}\n\n/** Build textures with wrapTex + occlusionOnUv2 support. Mirrors master's\n * default texture building but honors per-textureInfo wrapping so\n * KHR_texture_transform can attach per-texture UV state. */\nexport function buildDefaultPbrTexturesExt(\n engine: EngineContext,\n mat: GltfMaterialData,\n sampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n getCachedTex: (bitmap: ImageBitmap, srgb: boolean) => Texture2D,\n wrapTex: TextureWrapFn,\n samplerFor?: (texInfo: unknown) => GPUSampler\n): PbrTexturesExt {\n const wrap: TextureWrapFn = (tex, ti) => wrapTexCoord(wrapTex(tex, ti), ti);\n // When the asset declares non-default glTF samplers, upload each texture with its own\n // sampler (wrap/filter), caching per (sampler, image, srgb) exactly like the fast-path\n // buildSampledPbrTextures. Without samplerFor (common case) reuse the shared default-\n // sampler cache. A GPUTexture is sampler-independent, so the same image with two\n // samplers yields two Texture2D wrappers, matching the fast path.\n const _localCache = samplerFor ? new Map<GPUSampler, Map<number, Texture2D>>() : null;\n const _ids = samplerFor ? new Map<ImageBitmap, number>() : null;\n let _nextId = 0;\n const pickTex = (image: ImageBitmap, srgb: boolean, texInfo: unknown): Texture2D => {\n if (!samplerFor) {\n return getCachedTex(image, srgb);\n }\n const s = samplerFor(texInfo);\n let bySampler = _localCache!.get(s);\n if (!bySampler) {\n _localCache!.set(s, (bySampler = new Map()));\n }\n let id = _ids!.get(image);\n if (id === undefined) {\n _ids!.set(image, (id = _nextId++));\n }\n const key = id * 2 + (srgb ? 1 : 0);\n let tex = bySampler.get(key);\n if (!tex) {\n tex = uploadTex(engine, image, srgb, s, generateMipmaps);\n bySampler.set(key, tex);\n }\n return tex;\n };\n const raw = mat._rawMatDef ?? {};\n const pbr = raw.pbrMetallicRoughness ?? {};\n const baseColorTexture = mat._baseColorImage\n ? wrap(pickTex(mat._baseColorImage, true, pbr.baseColorTexture), pbr.baseColorTexture)\n : uploadBaseColorFactorTexture(engine, mat._baseColorFactor, sampler, generateMipmaps);\n const normalTexture = mat._normalImage ? wrap(pickTex(mat._normalImage, false, raw.normalTexture), raw.normalTexture) : undefined;\n const emissiveTexture = mat._emissiveImage ? wrap(pickTex(mat._emissiveImage, true, raw.emissiveTexture), raw.emissiveTexture) : undefined;\n\n const occlusionOnUv2 = mat._occlusionTexCoord !== 0 && mat._occlusionImage && !mat._metallicRoughnessImage;\n let occlusionTexture: Texture2D | undefined;\n const single = mat._metallicRoughnessImage ?? (occlusionOnUv2 ? null : mat._occlusionImage);\n let ormTexture: Texture2D;\n if (occlusionOnUv2) {\n ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, sampler, generateMipmaps);\n occlusionTexture = wrap(pickTex(mat._occlusionImage!, false, raw.occlusionTexture), raw.occlusionTexture);\n } else if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {\n const ormTi = mat._metallicRoughnessImage ? pbr.metallicRoughnessTexture : raw.occlusionTexture;\n ormTexture = wrap(pickTex(single, false, ormTi), ormTi);\n } else if (!single) {\n ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, sampler, generateMipmaps);\n } else {\n ormTexture = wrap(pickTex(mat._metallicRoughnessImage!, false, pbr.metallicRoughnessTexture), pbr.metallicRoughnessTexture);\n }\n // Independent-occlusion UV transform (orm-unpack): occlusion and metallic-roughness\n // share the ORM texture (same image), but the glTF gives occlusion its OWN\n // KHR_texture_transform (or a distinct texture object) so the two can be animated\n // independently via KHR_animation_pointer. Sampling occlusion with MR's transform\n // (the single ormUV) would wrongly animate it. Build a transform-carrying occlusion\n // texture (shares the ORM GPU image) so the shader can sample occlusion with occlUV.\n // Requires the same underlying image as the ORM texture, since the shader re-samples\n // ormTexture at occlUV.\n if (!occlusionTexture && mat._occlusionImage && mat._occlusionImage === mat._metallicRoughnessImage && occlusionNeedsSplit(raw)) {\n occlusionTexture = wrap(pickTex(mat._occlusionImage, false, raw.occlusionTexture), raw.occlusionTexture);\n }\n return { baseColorTexture, ormTexture, normalTexture, emissiveTexture, occlusionTexture };\n}\n\n/** Slow-path assembly: adds occlusionTexCoord and occlusionTexture props. */\nexport function assemblePbrPropsExt(mat: GltfMaterialData, tex: PbrTexturesExt, extLayers: Partial<PbrMaterialProps> | undefined): PbrMaterialProps {\n const ef = mat._emissiveFactor;\n // See gltf-pbr-builder.ts: emissiveFactor [1,1,1] is a no-op only with an emissive texture;\n // with no texture it is a real full-white emissive that must be applied (Material_03).\n const defaultFactor = (ef[0] === 0 && ef[1] === 0 && ef[2] === 0) || (!!tex.emissiveTexture && ef[0] === 1 && ef[1] === 1 && ef[2] === 1);\n // Precompute UV-transform presence so the renderer doesn't scan 5 textures\n // per mesh. Any wrapped texture with `_hasTx=true` (set by gltf-ext-uv-transform)\n // flips this once at build time; omitted entirely on fast path.\n const hasAnyUvTx =\n !!(tex.baseColorTexture as { _hasTx?: true })._hasTx ||\n !!(tex.normalTexture as { _hasTx?: true } | undefined)?._hasTx ||\n !!(tex.ormTexture as { _hasTx?: true })._hasTx ||\n !!(tex.emissiveTexture as { _hasTx?: true } | undefined)?._hasTx ||\n !!(tex.occlusionTexture as { _hasTx?: true } | undefined)?._hasTx;\n // Per-channel UV1 (TEXCOORD_1) selection bitmask. Computed here on the slow path — the only\n // place a texture can carry texCoord:1 — so the always-loaded fast path just reads `_uv2Mask`.\n // Bit literals are a private contract with createPbrTemplateExt's decode (baseColor=1, orm=2,\n // normal=4, emissive=8, specGloss=16, occlusion=32). specGloss arrives via extLayers. Occlusion\n // reads `mat._occlusionTexCoord` (set from the glTF material's occlusionTexture.texCoord for\n // every path, including KHR_texture_basisu, since occlusion-on-UV1 always routes through here).\n const tc1 = (t: unknown): boolean => (t as { _texCoord?: number } | undefined)?._texCoord === 1;\n const uv2Mask =\n (tc1(tex.baseColorTexture) ? 1 : 0) |\n (tc1(tex.ormTexture) ? 2 : 0) |\n (tc1(tex.normalTexture) ? 4 : 0) |\n (tc1(tex.emissiveTexture) ? 8 : 0) |\n (tc1((extLayers as { specGlossTexture?: unknown } | undefined)?.specGlossTexture) ? 16 : 0) |\n (mat._occlusionTexCoord === 1 ? 32 : 0);\n return {\n baseColorTexture: tex.baseColorTexture,\n normalTexture: tex.normalTexture,\n ormTexture: tex.ormTexture,\n emissiveTexture: tex.emissiveTexture,\n ...(mat._baseColorImage && !isDefaultBaseColorFactor(mat._baseColorFactor) ? { baseColorFactor: mat._baseColorFactor } : undefined),\n doubleSided: mat._doubleSided,\n occlusionStrength: mat._occlusionImage ? 1.0 : 0,\n ...(mat._occlusionTexCoord ? { occlusionTexCoord: mat._occlusionTexCoord } : undefined),\n ...(tex.occlusionTexture ? { occlusionTexture: tex.occlusionTexture } : undefined),\n ...(mat._normalScale !== 1 ? { normalTextureScale: mat._normalScale } : undefined),\n ...(mat._metallicRoughnessImage ? { metallicFactor: mat._metallicFactor, roughnessFactor: mat._roughnessFactor } : undefined),\n ...(!defaultFactor ? { emissiveColor: [ef[0], ef[1], ef[2]] as [number, number, number] } : undefined),\n enableSpecularAA: true,\n ...(mat._alphaMode === \"BLEND\" ? { alphaBlend: true, alpha: mat._baseColorFactor[3] } : undefined),\n ...(mat._alphaMode === \"MASK\" ? { alpha: mat._baseColorFactor[3], alphaCutOff: mat._alphaCutoff } : undefined),\n ...(hasAnyUvTx ? { _hasUvTx: true } : undefined),\n ...(mat._rawMatDef?.name ? { name: mat._rawMatDef.name as string } : undefined),\n ...extLayers,\n ...(uv2Mask ? { _uv2Mask: uv2Mask } : undefined),\n _buildGroup: getPbrGroupBuilder(),\n _uboVersion: 0,\n } as PbrMaterialProps;\n}\n\nfunction isDefaultBaseColorFactor(f: readonly number[]): boolean {\n return f[0] === 1 && f[1] === 1 && f[2] === 1 && f[3] === 1;\n}\n"],"names":[],"mappings":"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"}
@@ -33,6 +33,20 @@ function uploadTex(engine, bitmap, srgb, sampler, generateMipmaps, fallback) {
33
33
  engine._dlr?.b(result, bitmap, srgb, !!bitmap, fallback);
34
34
  return result;
35
35
  }
36
+ function uploadBaseColorFactorTexture(engine, factor, sampler, generateMipmaps) {
37
+ return uploadTex(
38
+ engine,
39
+ null,
40
+ true,
41
+ sampler,
42
+ generateMipmaps,
43
+ new U8([linearToSrgbByte(factor[0]), linearToSrgbByte(factor[1]), linearToSrgbByte(factor[2]), Math.round(Math.max(0, Math.min(1, factor[3])) * 255)])
44
+ );
45
+ }
46
+ function uploadOrmFactorTexture(engine, roughness, metallic, sampler, generateMipmaps) {
47
+ const clamp = (value) => Math.round(Math.max(0, Math.min(1, value)) * 255);
48
+ return uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(roughness), clamp(metallic), 255]));
49
+ }
36
50
  function assemblePbrProps(mat, baseColorTexture, ormTexture, normalTexture, emissiveTexture, extLayers) {
37
51
  const ef = mat._emissiveFactor;
38
52
  const defaultFactor = ef[0] === 0 && ef[1] === 0 && ef[2] === 0 || !!emissiveTexture && ef[0] === 1 && ef[1] === 1 && ef[2] === 1;
@@ -60,17 +74,7 @@ function isDefaultBaseColorFactor(f) {
60
74
  return f[0] === 1 && f[1] === 1 && f[2] === 1 && f[3] === 1;
61
75
  }
62
76
  function buildDefaultPbrTextures(engine, mat, sampler, generateMipmaps, getCachedTex) {
63
- const baseColorTexture = mat._baseColorImage ? getCachedTex(mat._baseColorImage, true) : (() => {
64
- const f = mat._baseColorFactor;
65
- return uploadTex(
66
- engine,
67
- null,
68
- true,
69
- sampler,
70
- generateMipmaps,
71
- new U8([linearToSrgbByte(f[0]), linearToSrgbByte(f[1]), linearToSrgbByte(f[2]), Math.round(Math.max(0, Math.min(1, f[3])) * 255)])
72
- );
73
- })();
77
+ const baseColorTexture = mat._baseColorImage ? getCachedTex(mat._baseColorImage, true) : uploadBaseColorFactorTexture(engine, mat._baseColorFactor, sampler, generateMipmaps);
74
78
  const normalTexture = mat._normalImage ? getCachedTex(mat._normalImage, false) : void 0;
75
79
  const emissiveTexture = mat._emissiveImage ? getCachedTex(mat._emissiveImage, true) : void 0;
76
80
  const single = mat._metallicRoughnessImage ?? mat._occlusionImage;
@@ -78,13 +82,12 @@ function buildDefaultPbrTextures(engine, mat, sampler, generateMipmaps, getCache
78
82
  if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {
79
83
  ormTexture = getCachedTex(single, false);
80
84
  } else if (!single) {
81
- const clamp = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255);
82
- ormTexture = uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));
85
+ ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, sampler, generateMipmaps);
83
86
  } else {
84
87
  ormTexture = getCachedTex(mat._metallicRoughnessImage, false);
85
88
  }
86
89
  return { baseColorTexture, ormTexture, normalTexture, emissiveTexture };
87
90
  }
88
91
 
89
- export { assemblePbrProps, buildDefaultPbrTextures, identityTexWrap, uploadTex };
92
+ export { assemblePbrProps, buildDefaultPbrTextures, identityTexWrap, uploadBaseColorFactorTexture, uploadOrmFactorTexture, uploadTex };
90
93
  //# sourceMappingURL=gltf-pbr-builder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-pbr-builder.js","sources":["../../../src/loader-gltf/gltf-pbr-builder.ts"],"sourcesContent":["/** Shared PBR-material assembly + texture upload + ext-layer merging.\n * Used by both the core loader (`load-gltf.ts`) and the variants loader\n * (`gltf-variants.ts`) so they can't drift. */\n\nimport { U8 } from \"../engine/typed-arrays.js\";\nimport { TU } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { PbrMaterialProps } from \"../material/pbr/pbr-material.js\";\nimport { getPbrGroupBuilder } from \"../material/pbr/pbr-material.js\";\nimport type { GltfMaterialData } from \"./gltf-material.js\";\nimport { mipLevelCount } from \"../texture/mip-count.js\";\nimport { linearToSrgbByte } from \"../math/color.js\";\n\n/** Texture post-processor composed from every active feature's `wrapTexture`\n * hook. Identity when no feature contributes one (common case). Kept simple\n * so the core loader stays feature-agnostic and tree-shakes cleanly. */\nexport type TextureWrapFn = (tex: Texture2D, texInfo: unknown) => Texture2D;\nexport const identityTexWrap: TextureWrapFn = (tex) => tex;\n\nexport type GenerateMipmapsFn = (engine: EngineContext, texture: GPUTexture, face?: number) => void;\n\nexport function uploadTex(\n engine: EngineContext,\n bitmap: ImageBitmap | null,\n srgb: boolean,\n sampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n fallback?: Uint8Array\n): Texture2D {\n const device = engine._device;\n const w = bitmap?.width ?? 1;\n const h = bitmap?.height ?? 1;\n const fmt: GPUTextureFormat = srgb ? \"rgba8unorm-srgb\" : \"rgba8unorm\";\n const mips = bitmap ? mipLevelCount(w, h) : 1;\n const tex = device.createTexture({\n size: { width: w, height: h },\n format: fmt,\n usage: TU.TEXTURE_BINDING | TU.COPY_DST | TU.COPY_SRC | TU.RENDER_ATTACHMENT,\n mipLevelCount: mips,\n });\n if (bitmap) {\n device.queue.copyExternalImageToTexture({ source: bitmap }, { texture: tex, premultipliedAlpha: false }, { width: w, height: h });\n generateMipmaps(engine, tex);\n } else {\n device.queue.writeTexture({ texture: tex }, (fallback ?? new U8([255, 255, 255, 255])) as Uint8Array<ArrayBuffer>, { bytesPerRow: 4 }, { width: 1, height: 1 });\n }\n const result: Texture2D = {\n texture: tex,\n view: tex.createView(),\n sampler,\n width: w,\n height: h,\n };\n engine._dlr?.b(result, bitmap, srgb, !!bitmap, fallback);\n return result;\n}\n\n/** Assemble a PbrMaterialProps from parsed glTF material data + already-uploaded\n * textures + per-ext fragment overrides. Fast-path: no wrapTex, no occlusionOnUv2,\n * no occlusionTexture. Slow-path additions live in gltf-pbr-builder-ext.ts. */\nexport function assemblePbrProps(\n mat: GltfMaterialData,\n baseColorTexture: Texture2D,\n ormTexture: Texture2D,\n normalTexture: Texture2D | undefined,\n emissiveTexture: Texture2D | undefined,\n extLayers: Partial<PbrMaterialProps> | undefined\n): PbrMaterialProps {\n const ef = mat._emissiveFactor;\n // emissiveFactor multiplies the emissive texture, so [1,1,1] is a no-op WHEN a texture is\n // present. With no emissive texture, [1,1,1] is a real full-white emissive that must be applied\n // (the glTF default is [0,0,0]) — otherwise the surface renders unlit/dark (Material_03).\n const defaultFactor = (ef[0] === 0 && ef[1] === 0 && ef[2] === 0) || (!!emissiveTexture && ef[0] === 1 && ef[1] === 1 && ef[2] === 1);\n return {\n baseColorTexture,\n normalTexture,\n ormTexture,\n emissiveTexture,\n ...(mat._baseColorImage && !isDefaultBaseColorFactor(mat._baseColorFactor) ? { baseColorFactor: mat._baseColorFactor } : undefined),\n doubleSided: mat._doubleSided,\n occlusionStrength: mat._occlusionImage ? 1.0 : 0,\n ...(mat._normalScale !== 1 ? { normalTextureScale: mat._normalScale } : undefined),\n ...(mat._metallicRoughnessImage ? { metallicFactor: mat._metallicFactor, roughnessFactor: mat._roughnessFactor } : undefined),\n ...(!defaultFactor ? { emissiveColor: [ef[0], ef[1], ef[2]] as [number, number, number] } : undefined),\n enableSpecularAA: true,\n ...(mat._alphaMode === \"BLEND\" ? { alphaBlend: true, alpha: mat._baseColorFactor[3] } : undefined),\n ...(mat._alphaMode === \"MASK\" ? { alpha: mat._baseColorFactor[3], alphaCutOff: mat._alphaCutoff } : undefined),\n ...(mat._rawMatDef?.name ? { name: mat._rawMatDef.name as string } : undefined),\n ...extLayers,\n _buildGroup: getPbrGroupBuilder(),\n _uboVersion: 0,\n } as PbrMaterialProps;\n}\n\nfunction isDefaultBaseColorFactor(f: readonly number[]): boolean {\n return f[0] === 1 && f[1] === 1 && f[2] === 1 && f[3] === 1;\n}\n\n/** Build the always-present default textures (base color + ORM) from a parsed glTF material.\n * Fast-path version: no wrapTex, no occlusion-on-uv2 handling. The slow path lives\n * in gltf-pbr-builder-ext.ts and is lazy-loaded only when needed. */\nexport function buildDefaultPbrTextures(\n engine: EngineContext,\n mat: GltfMaterialData,\n sampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n getCachedTex: (bitmap: ImageBitmap, srgb: boolean) => Texture2D\n): { baseColorTexture: Texture2D; ormTexture: Texture2D; normalTexture: Texture2D | undefined; emissiveTexture: Texture2D | undefined } {\n const baseColorTexture = mat._baseColorImage\n ? getCachedTex(mat._baseColorImage, true)\n : (() => {\n const f = mat._baseColorFactor;\n return uploadTex(\n engine,\n null,\n true,\n sampler,\n generateMipmaps,\n new U8([linearToSrgbByte(f[0]), linearToSrgbByte(f[1]), linearToSrgbByte(f[2]), Math.round(Math.max(0, Math.min(1, f[3])) * 255)])\n );\n })();\n const normalTexture = mat._normalImage ? getCachedTex(mat._normalImage, false) : undefined;\n const emissiveTexture = mat._emissiveImage ? getCachedTex(mat._emissiveImage, true) : undefined;\n\n const single = mat._metallicRoughnessImage ?? mat._occlusionImage;\n let ormTexture: Texture2D;\n if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {\n ormTexture = getCachedTex(single, false);\n } else if (!single) {\n const clamp = (v: number) => Math.round(Math.max(0, Math.min(1, v)) * 255);\n ormTexture = uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));\n } else {\n ormTexture = getCachedTex(mat._metallicRoughnessImage!, false);\n }\n return { baseColorTexture, ormTexture, normalTexture, emissiveTexture };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-pbr-builder.js","sources":["../../../src/loader-gltf/gltf-pbr-builder.ts"],"sourcesContent":["/** Shared PBR-material assembly + texture upload + ext-layer merging.\n * Used by both the core loader (`load-gltf.ts`) and the variants loader\n * (`gltf-variants.ts`) so they can't drift. */\n\nimport { U8 } from \"../engine/typed-arrays.js\";\nimport { TU } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { PbrMaterialProps } from \"../material/pbr/pbr-material.js\";\nimport { getPbrGroupBuilder } from \"../material/pbr/pbr-material.js\";\nimport type { GltfMaterialData } from \"./gltf-material.js\";\nimport { mipLevelCount } from \"../texture/mip-count.js\";\nimport { linearToSrgbByte } from \"../math/color.js\";\n\n/** Texture post-processor composed from every active feature's `wrapTexture`\n * hook. Identity when no feature contributes one (common case). Kept simple\n * so the core loader stays feature-agnostic and tree-shakes cleanly. */\nexport type TextureWrapFn = (tex: Texture2D, texInfo: unknown) => Texture2D;\nexport const identityTexWrap: TextureWrapFn = (tex) => tex;\n\nexport type GenerateMipmapsFn = (engine: EngineContext, texture: GPUTexture, face?: number) => void;\n\nexport function uploadTex(\n engine: EngineContext,\n bitmap: ImageBitmap | null,\n srgb: boolean,\n sampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n fallback?: Uint8Array\n): Texture2D {\n const device = engine._device;\n const w = bitmap?.width ?? 1;\n const h = bitmap?.height ?? 1;\n const fmt: GPUTextureFormat = srgb ? \"rgba8unorm-srgb\" : \"rgba8unorm\";\n const mips = bitmap ? mipLevelCount(w, h) : 1;\n const tex = device.createTexture({\n size: { width: w, height: h },\n format: fmt,\n usage: TU.TEXTURE_BINDING | TU.COPY_DST | TU.COPY_SRC | TU.RENDER_ATTACHMENT,\n mipLevelCount: mips,\n });\n if (bitmap) {\n device.queue.copyExternalImageToTexture({ source: bitmap }, { texture: tex, premultipliedAlpha: false }, { width: w, height: h });\n generateMipmaps(engine, tex);\n } else {\n device.queue.writeTexture({ texture: tex }, (fallback ?? new U8([255, 255, 255, 255])) as Uint8Array<ArrayBuffer>, { bytesPerRow: 4 }, { width: 1, height: 1 });\n }\n const result: Texture2D = {\n texture: tex,\n view: tex.createView(),\n sampler,\n width: w,\n height: h,\n };\n engine._dlr?.b(result, bitmap, srgb, !!bitmap, fallback);\n return result;\n}\n\nexport function uploadBaseColorFactorTexture(engine: EngineContext, factor: readonly number[], sampler: GPUSampler, generateMipmaps: GenerateMipmapsFn): Texture2D {\n return uploadTex(\n engine,\n null,\n true,\n sampler,\n generateMipmaps,\n new U8([linearToSrgbByte(factor[0]!), linearToSrgbByte(factor[1]!), linearToSrgbByte(factor[2]!), Math.round(Math.max(0, Math.min(1, factor[3]!)) * 255)])\n );\n}\n\nexport function uploadOrmFactorTexture(engine: EngineContext, roughness: number, metallic: number, sampler: GPUSampler, generateMipmaps: GenerateMipmapsFn): Texture2D {\n const clamp = (value: number): number => Math.round(Math.max(0, Math.min(1, value)) * 255);\n return uploadTex(engine, null, false, sampler, generateMipmaps, new U8([255, clamp(roughness), clamp(metallic), 255]));\n}\n\n/** Assemble a PbrMaterialProps from parsed glTF material data + already-uploaded\n * textures + per-ext fragment overrides. Fast-path: no wrapTex, no occlusionOnUv2,\n * no occlusionTexture. Slow-path additions live in gltf-pbr-builder-ext.ts. */\nexport function assemblePbrProps(\n mat: GltfMaterialData,\n baseColorTexture: Texture2D,\n ormTexture: Texture2D,\n normalTexture: Texture2D | undefined,\n emissiveTexture: Texture2D | undefined,\n extLayers: Partial<PbrMaterialProps> | undefined\n): PbrMaterialProps {\n const ef = mat._emissiveFactor;\n // emissiveFactor multiplies the emissive texture, so [1,1,1] is a no-op WHEN a texture is\n // present. With no emissive texture, [1,1,1] is a real full-white emissive that must be applied\n // (the glTF default is [0,0,0]) — otherwise the surface renders unlit/dark (Material_03).\n const defaultFactor = (ef[0] === 0 && ef[1] === 0 && ef[2] === 0) || (!!emissiveTexture && ef[0] === 1 && ef[1] === 1 && ef[2] === 1);\n return {\n baseColorTexture,\n normalTexture,\n ormTexture,\n emissiveTexture,\n ...(mat._baseColorImage && !isDefaultBaseColorFactor(mat._baseColorFactor) ? { baseColorFactor: mat._baseColorFactor } : undefined),\n doubleSided: mat._doubleSided,\n occlusionStrength: mat._occlusionImage ? 1.0 : 0,\n ...(mat._normalScale !== 1 ? { normalTextureScale: mat._normalScale } : undefined),\n ...(mat._metallicRoughnessImage ? { metallicFactor: mat._metallicFactor, roughnessFactor: mat._roughnessFactor } : undefined),\n ...(!defaultFactor ? { emissiveColor: [ef[0], ef[1], ef[2]] as [number, number, number] } : undefined),\n enableSpecularAA: true,\n ...(mat._alphaMode === \"BLEND\" ? { alphaBlend: true, alpha: mat._baseColorFactor[3] } : undefined),\n ...(mat._alphaMode === \"MASK\" ? { alpha: mat._baseColorFactor[3], alphaCutOff: mat._alphaCutoff } : undefined),\n ...(mat._rawMatDef?.name ? { name: mat._rawMatDef.name as string } : undefined),\n ...extLayers,\n _buildGroup: getPbrGroupBuilder(),\n _uboVersion: 0,\n } as PbrMaterialProps;\n}\n\nfunction isDefaultBaseColorFactor(f: readonly number[]): boolean {\n return f[0] === 1 && f[1] === 1 && f[2] === 1 && f[3] === 1;\n}\n\n/** Build the always-present default textures (base color + ORM) from a parsed glTF material.\n * Fast-path version: no wrapTex, no occlusion-on-uv2 handling. The slow path lives\n * in gltf-pbr-builder-ext.ts and is lazy-loaded only when needed. */\nexport function buildDefaultPbrTextures(\n engine: EngineContext,\n mat: GltfMaterialData,\n sampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n getCachedTex: (bitmap: ImageBitmap, srgb: boolean) => Texture2D\n): { baseColorTexture: Texture2D; ormTexture: Texture2D; normalTexture: Texture2D | undefined; emissiveTexture: Texture2D | undefined } {\n const baseColorTexture = mat._baseColorImage ? getCachedTex(mat._baseColorImage, true) : uploadBaseColorFactorTexture(engine, mat._baseColorFactor, sampler, generateMipmaps);\n const normalTexture = mat._normalImage ? getCachedTex(mat._normalImage, false) : undefined;\n const emissiveTexture = mat._emissiveImage ? getCachedTex(mat._emissiveImage, true) : undefined;\n\n const single = mat._metallicRoughnessImage ?? mat._occlusionImage;\n let ormTexture: Texture2D;\n if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {\n ormTexture = getCachedTex(single, false);\n } else if (!single) {\n ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, sampler, generateMipmaps);\n } else {\n ormTexture = getCachedTex(mat._metallicRoughnessImage!, false);\n }\n return { baseColorTexture, ormTexture, normalTexture, emissiveTexture };\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,5 @@
1
1
  import { getOrCreateSampler } from '../resource/gpu-pool.js';
2
- import { U8 } from '../engine/typed-arrays.js';
3
- import { linearToSrgbByte } from '../math/color.js';
4
- import { uploadTex } from './gltf-pbr-builder.js';
2
+ import { uploadBaseColorFactorTexture, uploadOrmFactorTexture } from './gltf-pbr-builder.js';
5
3
 
6
4
  function gltfTexSamplerDesc(json, texInfo) {
7
5
  const s = json.textures?.[texInfo.index]?.sampler != null ? json.samplers?.[json.textures[texInfo.index].sampler] : void 0;
@@ -41,17 +39,7 @@ function buildSampledPbrTextures(engine, mat, defaultSampler, generateMipmaps, s
41
39
  const tex = getCachedTex(bitmap, srgb);
42
40
  return s === defaultSampler ? tex : { ...tex, sampler: s };
43
41
  };
44
- const baseColorTexture = mat._baseColorImage ? cached(mat._baseColorImage, true, pbr.baseColorTexture) : (() => {
45
- const f = mat._baseColorFactor;
46
- return uploadTex(
47
- engine,
48
- null,
49
- true,
50
- defaultSampler,
51
- generateMipmaps,
52
- new U8([linearToSrgbByte(f[0]), linearToSrgbByte(f[1]), linearToSrgbByte(f[2]), Math.round(Math.max(0, Math.min(1, f[3])) * 255)])
53
- );
54
- })();
42
+ const baseColorTexture = mat._baseColorImage ? cached(mat._baseColorImage, true, pbr.baseColorTexture) : uploadBaseColorFactorTexture(engine, mat._baseColorFactor, defaultSampler, generateMipmaps);
55
43
  const normalTexture = mat._normalImage ? cached(mat._normalImage, false, def.normalTexture) : void 0;
56
44
  const emissiveTexture = mat._emissiveImage ? cached(mat._emissiveImage, true, def.emissiveTexture) : void 0;
57
45
  const single = mat._metallicRoughnessImage ?? mat._occlusionImage;
@@ -60,8 +48,7 @@ function buildSampledPbrTextures(engine, mat, defaultSampler, generateMipmaps, s
60
48
  if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {
61
49
  ormTexture = cached(single, false, ormTexInfo);
62
50
  } else if (!single) {
63
- const clamp = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255);
64
- ormTexture = uploadTex(engine, null, false, defaultSampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));
51
+ ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, defaultSampler, generateMipmaps);
65
52
  } else {
66
53
  ormTexture = cached(mat._metallicRoughnessImage, false, pbr.metallicRoughnessTexture);
67
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-sampler-desc.js","sources":["../../../src/loader-gltf/gltf-sampler-desc.ts"],"sourcesContent":["import { getOrCreateSampler } from \"../resource/gpu-pool.js\";\nimport { U8 } from \"../engine/typed-arrays.js\";\nimport { linearToSrgbByte } from \"../math/color.js\";\nimport { uploadTex } from \"./gltf-pbr-builder.js\";\nimport type { GenerateMipmapsFn } from \"./gltf-pbr-builder.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { GltfMaterialData } from \"./gltf-material.js\";\n\n/** Map a glTF textureInfo's sampler (wrapS/wrapT/magFilter/minFilter) to a WebGPU sampler\n * descriptor. glTF wrap: 33071 CLAMP_TO_EDGE, 33648 MIRRORED_REPEAT, else REPEAT.\n * glTF filter: 9728 NEAREST else LINEAR; min/mip from the combined min filter enum. */\nfunction gltfTexSamplerDesc(json: any, texInfo: any): GPUSamplerDescriptor {\n const s = json.textures?.[texInfo.index]?.sampler != null ? json.samplers?.[json.textures[texInfo.index].sampler] : undefined;\n const wrap = (m: number | undefined): GPUAddressMode => (m === 33071 ? \"clamp-to-edge\" : m === 33648 ? \"mirror-repeat\" : \"repeat\");\n const minF: number | undefined = s?.minFilter;\n const minNearest = minF === 9728 || minF === 9984 || minF === 9986;\n const mipNearest = minF === 9984 || minF === 9985;\n // glTF non-mipmap min filters (9728 NEAREST, 9729 LINEAR) mean \"sample mip 0 only\".\n // The shared uploaded GPU texture always carries a full mip chain, so clamp the LOD\n // to 0 for these filters (matching BJS `noMipmap`); otherwise a minified texture\n // (e.g. small SDF text in a top-down view) would sample blurred mips and render\n // softer/darker than BJS. Mipmapped filters (9984–9987) leave LOD unclamped.\n const noMip = minF === 9728 || minF === 9729;\n const magLinear = s?.magFilter !== 9728;\n return {\n magFilter: magLinear ? \"linear\" : \"nearest\",\n minFilter: minNearest ? \"nearest\" : \"linear\",\n mipmapFilter: mipNearest ? \"nearest\" : \"linear\",\n addressModeU: wrap(s?.wrapS),\n addressModeV: wrap(s?.wrapT),\n ...(noMip ? { lodMaxClamp: 0 } : undefined),\n // WebGPU forbids anisotropy unless mag/min/mip filters are ALL linear; gate on\n // every filter (incl. mipNearest, e.g. glTF LINEAR_MIPMAP_NEAREST) or createSampler throws.\n // Also disable it for the clamped-mip path (single LOD → anisotropy is meaningless).\n maxAnisotropy: magLinear && !minNearest && !mipNearest && !noMip ? 4 : 1,\n };\n}\n\n/** Build a per-texture sampler resolver honoring each texture's glTF sampler\n * (wrap/filter). Loaded lazily only when an asset declares a non-default sampler;\n * the common case (default repeat/linear) uses one shared sampler and never loads this.\n * `texInfo == null` (factor textures) falls back to `defaultSampler`.\n * @internal */\nexport function makeSamplerFor(engine: EngineContext, json: any, defaultSampler: GPUSampler): (texInfo: any) => GPUSampler {\n return (texInfo: any): GPUSampler => {\n if (texInfo == null) {\n return defaultSampler;\n }\n const desc = gltfTexSamplerDesc(json, texInfo);\n // A non-mipmap sampler (lodMaxClamp 0) is created directly: the shared cache key omits\n // the LOD clamp, so caching it there could alias a full-mip sampler with identical\n // filter/wrap. These are rare (SDF/UI textures), so per-call creation is cheaper than\n // growing the universal sampler key — which would move every non-glTF scene's bundle.\n return desc.lodMaxClamp === 0 ? engine._device.createSampler(desc) : getOrCreateSampler(engine, desc);\n };\n}\n\n/** Sampler-aware variant of buildDefaultPbrTextures. Mirrors the core fast path but wraps\n * each shared GPU texture with the sampler resolved from its glTF textureInfo (wrap/filter),\n * so clamp/mirror/nearest assets render correctly without re-uploading identical images.\n * Lazy-loaded only for non-default-sampler assets — the common path stays byte-identical.\n * @internal */\nexport function buildSampledPbrTextures(\n engine: EngineContext,\n mat: GltfMaterialData,\n defaultSampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n samplerFor: (texInfo: any) => GPUSampler,\n getCachedTex: (bitmap: ImageBitmap, srgb: boolean) => Texture2D\n): { baseColorTexture: Texture2D; ormTexture: Texture2D; normalTexture: Texture2D | undefined; emissiveTexture: Texture2D | undefined } {\n const def = mat._rawMatDef ?? {};\n const pbr = def.pbrMetallicRoughness ?? {};\n const cached = (bitmap: ImageBitmap, srgb: boolean, texInfo: any): Texture2D => {\n const s = samplerFor(texInfo);\n const tex = getCachedTex(bitmap, srgb);\n return s === defaultSampler ? tex : { ...tex, sampler: s };\n };\n\n const baseColorTexture = mat._baseColorImage\n ? cached(mat._baseColorImage, true, pbr.baseColorTexture)\n : (() => {\n const f = mat._baseColorFactor;\n return uploadTex(\n engine,\n null,\n true,\n defaultSampler,\n generateMipmaps,\n new U8([linearToSrgbByte(f[0]), linearToSrgbByte(f[1]), linearToSrgbByte(f[2]), Math.round(Math.max(0, Math.min(1, f[3])) * 255)])\n );\n })();\n const normalTexture = mat._normalImage ? cached(mat._normalImage, false, def.normalTexture) : undefined;\n const emissiveTexture = mat._emissiveImage ? cached(mat._emissiveImage, true, def.emissiveTexture) : undefined;\n\n const single = mat._metallicRoughnessImage ?? mat._occlusionImage;\n const ormTexInfo = mat._metallicRoughnessImage ? pbr.metallicRoughnessTexture : def.occlusionTexture;\n let ormTexture: Texture2D;\n if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {\n ormTexture = cached(single, false, ormTexInfo);\n } else if (!single) {\n const clamp = (v: number) => Math.round(Math.max(0, Math.min(1, v)) * 255);\n ormTexture = uploadTex(engine, null, false, defaultSampler, generateMipmaps, new U8([255, clamp(mat._roughnessFactor), clamp(mat._metallicFactor), 255]));\n } else {\n ormTexture = cached(mat._metallicRoughnessImage!, false, pbr.metallicRoughnessTexture);\n }\n return { baseColorTexture, ormTexture, normalTexture, emissiveTexture };\n}\n"],"names":[],"mappings":";;;;;AAYA,SAAS,kBAAA,CAAmB,MAAW,OAAA,EAAoC;AACvE,EAAA,MAAM,IAAI,IAAA,CAAK,QAAA,GAAW,OAAA,CAAQ,KAAK,GAAG,OAAA,IAAW,IAAA,GAAO,IAAA,CAAK,QAAA,GAAW,KAAK,QAAA,CAAS,OAAA,CAAQ,KAAK,CAAA,CAAE,OAAO,CAAA,GAAI,MAAA;AACpH,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAA2C,CAAA,KAAM,QAAQ,eAAA,GAAkB,CAAA,KAAM,QAAQ,eAAA,GAAkB,QAAA;AACzH,EAAA,MAAM,OAA2B,CAAA,EAAG,SAAA;AACpC,EAAA,MAAM,UAAA,GAAa,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,QAAQ,IAAA,KAAS,IAAA;AAC9D,EAAA,MAAM,UAAA,GAAa,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,IAAA;AAM7C,EAAA,MAAM,KAAA,GAAQ,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,IAAA;AACxC,EAAA,MAAM,SAAA,GAAY,GAAG,SAAA,KAAc,IAAA;AACnC,EAAA,OAAO;AAAA,IACH,SAAA,EAAW,YAAY,QAAA,GAAW,SAAA;AAAA,IAClC,SAAA,EAAW,aAAa,SAAA,GAAY,QAAA;AAAA,IACpC,YAAA,EAAc,aAAa,SAAA,GAAY,QAAA;AAAA,IACvC,YAAA,EAAc,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA;AAAA,IAC3B,YAAA,EAAc,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA;AAAA,IAC3B,GAAI,KAAA,GAAQ,EAAE,WAAA,EAAa,GAAE,GAAI,MAAA;AAAA;AAAA;AAAA;AAAA,IAIjC,aAAA,EAAe,aAAa,CAAC,UAAA,IAAc,CAAC,UAAA,IAAc,CAAC,QAAQ,CAAA,GAAI;AAAA,GAC3E;AACJ;AAOO,SAAS,cAAA,CAAe,MAAA,EAAuB,IAAA,EAAW,cAAA,EAA0D;AACvH,EAAA,OAAO,CAAC,OAAA,KAA6B;AACjC,IAAA,IAAI,WAAW,IAAA,EAAM;AACjB,MAAA,OAAO,cAAA;AAAA,IACX;AACA,IAAA,MAAM,IAAA,GAAO,kBAAA,CAAmB,IAAA,EAAM,OAAO,CAAA;AAK7C,IAAA,OAAO,IAAA,CAAK,WAAA,KAAgB,CAAA,GAAI,MAAA,CAAO,OAAA,CAAQ,cAAc,IAAI,CAAA,GAAI,kBAAA,CAAmB,MAAA,EAAQ,IAAI,CAAA;AAAA,EACxG,CAAA;AACJ;AAOO,SAAS,wBACZ,MAAA,EACA,GAAA,EACA,cAAA,EACA,eAAA,EACA,YACA,YAAA,EACoI;AACpI,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,UAAA,IAAc,EAAC;AAC/B,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,oBAAA,IAAwB,EAAC;AACzC,EAAA,MAAM,MAAA,GAAS,CAAC,MAAA,EAAqB,IAAA,EAAe,OAAA,KAA4B;AAC5E,IAAA,MAAM,CAAA,GAAI,WAAW,OAAO,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,MAAA,EAAQ,IAAI,CAAA;AACrC,IAAA,OAAO,MAAM,cAAA,GAAiB,GAAA,GAAM,EAAE,GAAG,GAAA,EAAK,SAAS,CAAA,EAAE;AAAA,EAC7D,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,GAAA,CAAI,eAAA,GACvB,MAAA,CAAO,GAAA,CAAI,iBAAiB,IAAA,EAAM,GAAA,CAAI,gBAAgB,CAAA,GAAA,CACrD,MAAM;AACH,IAAA,MAAM,IAAI,GAAA,CAAI,gBAAA;AACd,IAAA,OAAO,SAAA;AAAA,MACH,MAAA;AAAA,MACA,IAAA;AAAA,MACA,IAAA;AAAA,MACA,cAAA;AAAA,MACA,eAAA;AAAA,MACA,IAAI,EAAA,CAAG,CAAC,gBAAA,CAAiB,EAAE,CAAC,CAAC,CAAA,EAAG,gBAAA,CAAiB,CAAA,CAAE,CAAC,CAAC,CAAA,EAAG,iBAAiB,CAAA,CAAE,CAAC,CAAC,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,GAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAC,CAAA,GAAI,GAAG,CAAC,CAAC;AAAA,KACrI;AAAA,EACJ,CAAA,GAAG;AACT,EAAA,MAAM,aAAA,GAAgB,IAAI,YAAA,GAAe,MAAA,CAAO,IAAI,YAAA,EAAc,KAAA,EAAO,GAAA,CAAI,aAAa,CAAA,GAAI,MAAA;AAC9F,EAAA,MAAM,eAAA,GAAkB,IAAI,cAAA,GAAiB,MAAA,CAAO,IAAI,cAAA,EAAgB,IAAA,EAAM,GAAA,CAAI,eAAe,CAAA,GAAI,MAAA;AAErG,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,uBAAA,IAA2B,GAAA,CAAI,eAAA;AAClD,EAAA,MAAM,UAAA,GAAa,GAAA,CAAI,uBAAA,GAA0B,GAAA,CAAI,2BAA2B,GAAA,CAAI,gBAAA;AACpF,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI,MAAA,KAAW,CAAC,GAAA,CAAI,uBAAA,IAA2B,CAAC,IAAI,eAAA,IAAmB,GAAA,CAAI,uBAAA,KAA4B,GAAA,CAAI,eAAA,CAAA,EAAkB;AACzH,IAAA,UAAA,GAAa,MAAA,CAAO,MAAA,EAAQ,KAAA,EAAO,UAAU,CAAA;AAAA,EACjD,CAAA,MAAA,IAAW,CAAC,MAAA,EAAQ;AAChB,IAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAc,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAC,CAAC,IAAI,GAAG,CAAA;AACzE,IAAA,UAAA,GAAa,SAAA,CAAU,QAAQ,IAAA,EAAM,KAAA,EAAO,gBAAgB,eAAA,EAAiB,IAAI,GAAG,CAAC,GAAA,EAAK,MAAM,GAAA,CAAI,gBAAgB,GAAG,KAAA,CAAM,GAAA,CAAI,eAAe,CAAA,EAAG,GAAG,CAAC,CAAC,CAAA;AAAA,EAC5J,CAAA,MAAO;AACH,IAAA,UAAA,GAAa,MAAA,CAAO,GAAA,CAAI,uBAAA,EAA0B,KAAA,EAAO,IAAI,wBAAwB,CAAA;AAAA,EACzF;AACA,EAAA,OAAO,EAAE,gBAAA,EAAkB,UAAA,EAAY,aAAA,EAAe,eAAA,EAAgB;AAC1E;;;;"}
1
+ {"version":3,"file":"gltf-sampler-desc.js","sources":["../../../src/loader-gltf/gltf-sampler-desc.ts"],"sourcesContent":["import { getOrCreateSampler } from \"../resource/gpu-pool.js\";\nimport { uploadBaseColorFactorTexture, uploadOrmFactorTexture } from \"./gltf-pbr-builder.js\";\nimport type { GenerateMipmapsFn } from \"./gltf-pbr-builder.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { GltfMaterialData } from \"./gltf-material.js\";\n\n/** Map a glTF textureInfo's sampler (wrapS/wrapT/magFilter/minFilter) to a WebGPU sampler\n * descriptor. glTF wrap: 33071 CLAMP_TO_EDGE, 33648 MIRRORED_REPEAT, else REPEAT.\n * glTF filter: 9728 NEAREST else LINEAR; min/mip from the combined min filter enum. */\nfunction gltfTexSamplerDesc(json: any, texInfo: any): GPUSamplerDescriptor {\n const s = json.textures?.[texInfo.index]?.sampler != null ? json.samplers?.[json.textures[texInfo.index].sampler] : undefined;\n const wrap = (m: number | undefined): GPUAddressMode => (m === 33071 ? \"clamp-to-edge\" : m === 33648 ? \"mirror-repeat\" : \"repeat\");\n const minF: number | undefined = s?.minFilter;\n const minNearest = minF === 9728 || minF === 9984 || minF === 9986;\n const mipNearest = minF === 9984 || minF === 9985;\n // glTF non-mipmap min filters (9728 NEAREST, 9729 LINEAR) mean \"sample mip 0 only\".\n // The shared uploaded GPU texture always carries a full mip chain, so clamp the LOD\n // to 0 for these filters (matching BJS `noMipmap`); otherwise a minified texture\n // (e.g. small SDF text in a top-down view) would sample blurred mips and render\n // softer/darker than BJS. Mipmapped filters (9984–9987) leave LOD unclamped.\n const noMip = minF === 9728 || minF === 9729;\n const magLinear = s?.magFilter !== 9728;\n return {\n magFilter: magLinear ? \"linear\" : \"nearest\",\n minFilter: minNearest ? \"nearest\" : \"linear\",\n mipmapFilter: mipNearest ? \"nearest\" : \"linear\",\n addressModeU: wrap(s?.wrapS),\n addressModeV: wrap(s?.wrapT),\n ...(noMip ? { lodMaxClamp: 0 } : undefined),\n // WebGPU forbids anisotropy unless mag/min/mip filters are ALL linear; gate on\n // every filter (incl. mipNearest, e.g. glTF LINEAR_MIPMAP_NEAREST) or createSampler throws.\n // Also disable it for the clamped-mip path (single LOD → anisotropy is meaningless).\n maxAnisotropy: magLinear && !minNearest && !mipNearest && !noMip ? 4 : 1,\n };\n}\n\n/** Build a per-texture sampler resolver honoring each texture's glTF sampler\n * (wrap/filter). Loaded lazily only when an asset declares a non-default sampler;\n * the common case (default repeat/linear) uses one shared sampler and never loads this.\n * `texInfo == null` (factor textures) falls back to `defaultSampler`.\n * @internal */\nexport function makeSamplerFor(engine: EngineContext, json: any, defaultSampler: GPUSampler): (texInfo: any) => GPUSampler {\n return (texInfo: any): GPUSampler => {\n if (texInfo == null) {\n return defaultSampler;\n }\n const desc = gltfTexSamplerDesc(json, texInfo);\n // A non-mipmap sampler (lodMaxClamp 0) is created directly: the shared cache key omits\n // the LOD clamp, so caching it there could alias a full-mip sampler with identical\n // filter/wrap. These are rare (SDF/UI textures), so per-call creation is cheaper than\n // growing the universal sampler key — which would move every non-glTF scene's bundle.\n return desc.lodMaxClamp === 0 ? engine._device.createSampler(desc) : getOrCreateSampler(engine, desc);\n };\n}\n\n/** Sampler-aware variant of buildDefaultPbrTextures. Mirrors the core fast path but wraps\n * each shared GPU texture with the sampler resolved from its glTF textureInfo (wrap/filter),\n * so clamp/mirror/nearest assets render correctly without re-uploading identical images.\n * Lazy-loaded only for non-default-sampler assets — the common path stays byte-identical.\n * @internal */\nexport function buildSampledPbrTextures(\n engine: EngineContext,\n mat: GltfMaterialData,\n defaultSampler: GPUSampler,\n generateMipmaps: GenerateMipmapsFn,\n samplerFor: (texInfo: any) => GPUSampler,\n getCachedTex: (bitmap: ImageBitmap, srgb: boolean) => Texture2D\n): { baseColorTexture: Texture2D; ormTexture: Texture2D; normalTexture: Texture2D | undefined; emissiveTexture: Texture2D | undefined } {\n const def = mat._rawMatDef ?? {};\n const pbr = def.pbrMetallicRoughness ?? {};\n const cached = (bitmap: ImageBitmap, srgb: boolean, texInfo: any): Texture2D => {\n const s = samplerFor(texInfo);\n const tex = getCachedTex(bitmap, srgb);\n return s === defaultSampler ? tex : { ...tex, sampler: s };\n };\n\n const baseColorTexture = mat._baseColorImage\n ? cached(mat._baseColorImage, true, pbr.baseColorTexture)\n : uploadBaseColorFactorTexture(engine, mat._baseColorFactor, defaultSampler, generateMipmaps);\n const normalTexture = mat._normalImage ? cached(mat._normalImage, false, def.normalTexture) : undefined;\n const emissiveTexture = mat._emissiveImage ? cached(mat._emissiveImage, true, def.emissiveTexture) : undefined;\n\n const single = mat._metallicRoughnessImage ?? mat._occlusionImage;\n const ormTexInfo = mat._metallicRoughnessImage ? pbr.metallicRoughnessTexture : def.occlusionTexture;\n let ormTexture: Texture2D;\n if (single && (!mat._metallicRoughnessImage || !mat._occlusionImage || mat._metallicRoughnessImage === mat._occlusionImage)) {\n ormTexture = cached(single, false, ormTexInfo);\n } else if (!single) {\n ormTexture = uploadOrmFactorTexture(engine, mat._roughnessFactor, mat._metallicFactor, defaultSampler, generateMipmaps);\n } else {\n ormTexture = cached(mat._metallicRoughnessImage!, false, pbr.metallicRoughnessTexture);\n }\n return { baseColorTexture, ormTexture, normalTexture, emissiveTexture };\n}\n"],"names":[],"mappings":"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"}
@@ -43,9 +43,6 @@ fn nme_computeAo(depthTex: texture_2d<f32>, depthSamp: sampler, fragCoord: vec4<
43
43
  return clamp(1.0 - occlusion / 16.0, 0.0, 1.0);
44
44
  }
45
45
  `;
46
- function sanitize(name) {
47
- return name.replace(/[^A-Za-z0-9_]/g, "_");
48
- }
49
46
  function num(value, fallback) {
50
47
  return typeof value === "number" ? `${value}` : `${fallback}`;
51
48
  }
@@ -58,7 +55,7 @@ const emitter = {
58
55
  throw new Error(`NodeMaterial: AmbientOcclusionBlock "${block.name}" requires an ImageSourceBlock source`);
59
56
  }
60
57
  const producer = ctx.graph.blocks.get(source.blockId);
61
- const bindingName = sanitize(producer?.name || `ao${block.id}`);
58
+ const bindingName = ctx.sanitize(producer?.name || `ao${block.id}`);
62
59
  if (!state.textures.find((t) => t.name === bindingName)) {
63
60
  state.textures.push({ name: bindingName, kind: "texture2d", texture: null });
64
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ambient-occlusion-block.js","sources":["../../../../../src/material/node/blocks/ambient-occlusion-block.ts"],"sourcesContent":["import type { BlockEmitter, NodeExpr } from \"../node-types.js\";\n\nconst HELPER_KEY = \"nme_ambientOcclusion\";\nconst HELPER_WGSL = `\nfn nme_aoNormalFromDepth(depthTex: texture_2d<f32>, depthSamp: sampler, depth: f32, coords: vec2<f32>, radius: f32) -> vec3<f32> {\n let offset1 = vec2<f32>(0.0, radius);\n let offset2 = vec2<f32>(radius, 0.0);\n let depth1 = textureSampleLevel(depthTex, depthSamp, coords + offset1, 0.0).r;\n let depth2 = textureSampleLevel(depthTex, depthSamp, coords + offset2, 0.0).r;\n let p1 = vec3<f32>(offset1, depth1 - depth);\n let p2 = vec3<f32>(offset2, depth2 - depth);\n var normal = cross(p1, p2);\n normal.z = -normal.z;\n return normalize(normal);\n}\n\nfn nme_aoRandom(uv: vec2<f32>) -> vec3<f32> {\n let x = fract(sin(dot(uv, vec2<f32>(12.9898, 78.233))) * 43758.5453);\n let y = fract(sin(dot(uv, vec2<f32>(39.3468, 11.135))) * 24634.6345);\n let z = fract(sin(dot(uv, vec2<f32>(73.156, 52.235))) * 12414.2347);\n return normalize(vec3<f32>(x, y, z));\n}\n\nfn nme_computeAo(depthTex: texture_2d<f32>, depthSamp: sampler, fragCoord: vec4<f32>, screenSize: vec2<f32>, radius: f32, area: f32, fallOff: f32) -> f32 {\n let uv = fragCoord.xy / screenSize;\n let random = nme_aoRandom(uv * 4.0);\n let depth = textureSampleLevel(depthTex, depthSamp, uv, 0.0).r;\n let position = vec3<f32>(uv, depth);\n let normal = nme_aoNormalFromDepth(depthTex, depthSamp, depth, uv, radius);\n let radiusDepth = radius / depth;\n let sampleSphere = array<vec3<f32>, 16>(\n vec3<f32>(0.5381, 0.1856, -0.4319), vec3<f32>(0.1379, 0.2486, 0.4430), vec3<f32>(0.3371, 0.5679, -0.0057), vec3<f32>(-0.6999, -0.0451, -0.0019),\n vec3<f32>(0.0689, -0.1598, -0.8547), vec3<f32>(0.0560, 0.0069, -0.1843), vec3<f32>(-0.0146, 0.1402, 0.0762), vec3<f32>(0.0100, -0.1924, -0.0344),\n vec3<f32>(-0.3577, -0.5301, -0.4358), vec3<f32>(-0.3169, 0.1063, 0.0158), vec3<f32>(0.0103, -0.5869, 0.0046), vec3<f32>(-0.0897, -0.4940, 0.3287),\n vec3<f32>(0.7119, -0.0154, -0.0918), vec3<f32>(-0.0533, 0.0596, -0.5411), vec3<f32>(0.0352, -0.0631, 0.5460), vec3<f32>(-0.4776, 0.2847, -0.0271)\n );\n var occlusion = 0.0;\n for (var i = 0; i < 16; i = i + 1) {\n let ray = radiusDepth * reflect(sampleSphere[i], random);\n let hemiRay = position + sign(dot(ray, normal)) * ray;\n let occlusionDepth = textureSample(depthTex, depthSamp, clamp(hemiRay.xy, vec2<f32>(0.001), vec2<f32>(0.999))).r;\n let difference = depth - occlusionDepth;\n occlusion += step(fallOff, difference) * (1.0 - smoothstep(fallOff, area, difference));\n }\n return clamp(1.0 - occlusion / 16.0, 0.0, 1.0);\n}\n`;\n\nfunction sanitize(name: string): string {\n return name.replace(/[^A-Za-z0-9_]/g, \"_\");\n}\n\nfunction num(value: unknown, fallback: number): string {\n return typeof value === \"number\" ? `${value}` : `${fallback}`;\n}\n\nexport const emitter: BlockEmitter = {\n className: \"AmbientOcclusionBlock\",\n stage: \"fragment\",\n emit(block, _outputName, stage, state, ctx): NodeExpr {\n const source = block.inputs.get(\"source\")?.source;\n if (!source) {\n throw new Error(`NodeMaterial: AmbientOcclusionBlock \"${block.name}\" requires an ImageSourceBlock source`);\n }\n const producer = ctx.graph.blocks.get(source.blockId);\n const bindingName = sanitize(producer?.name || `ao${block.id}`);\n if (!state.textures.find((t) => t.name === bindingName)) {\n state.textures.push({ name: bindingName, kind: \"texture2d\", texture: null });\n }\n state.fragment.helpers.set(HELPER_KEY, HELPER_WGSL);\n const screenSize = ctx.cast(ctx.resolve(block, \"screenSize\", stage, state), \"vec2f\").expr;\n return {\n expr: `nme_computeAo(nodeTex_${bindingName}, nodeSamp_${bindingName}, _NME_FRAG_COORD_, ${screenSize}, ${num(block.serialized.radius, 0.0001)}, ${num(block.serialized.area, 0.0075)}, ${num(block.serialized.fallOff, 0.000001)})`,\n type: \"f32\",\n };\n },\n};\n"],"names":[],"mappings":"AAEA,MAAM,UAAA,GAAa,sBAAA;AACnB,MAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AA6CpB,SAAS,SAAS,IAAA,EAAsB;AACpC,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,gBAAA,EAAkB,GAAG,CAAA;AAC7C;AAEA,SAAS,GAAA,CAAI,OAAgB,QAAA,EAA0B;AACnD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,GAAG,KAAK,CAAA,CAAA,GAAK,GAAG,QAAQ,CAAA,CAAA;AAC/D;AAEO,MAAM,OAAA,GAAwB;AAAA,EACjC,SAAA,EAAW,uBAAA;AAAA,EACX,KAAA,EAAO,UAAA;AAAA,EACP,IAAA,CAAK,KAAA,EAAO,WAAA,EAAa,KAAA,EAAO,OAAO,GAAA,EAAe;AAClD,IAAA,MAAM,MAAA,GAAS,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAA,EAAG,MAAA;AAC3C,IAAA,IAAI,CAAC,MAAA,EAAQ;AACT,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,qCAAA,EAAwC,KAAA,CAAM,IAAI,CAAA,qCAAA,CAAuC,CAAA;AAAA,IAC7G;AACA,IAAA,MAAM,WAAW,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,OAAO,OAAO,CAAA;AACpD,IAAA,MAAM,cAAc,QAAA,CAAS,QAAA,EAAU,QAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,EAAE,CAAA,CAAE,CAAA;AAC9D,IAAA,IAAI,CAAC,MAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,WAAW,CAAA,EAAG;AACrD,MAAA,KAAA,CAAM,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,aAAa,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,IAAA,EAAM,CAAA;AAAA,IAC/E;AACA,IAAA,KAAA,CAAM,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,UAAA,EAAY,WAAW,CAAA;AAClD,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,KAAA,EAAO,YAAA,EAAc,KAAA,EAAO,KAAK,CAAA,EAAG,OAAO,CAAA,CAAE,IAAA;AACrF,IAAA,OAAO;AAAA,MACH,IAAA,EAAM,CAAA,sBAAA,EAAyB,WAAW,CAAA,WAAA,EAAc,WAAW,CAAA,oBAAA,EAAuB,UAAU,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,MAAA,EAAQ,IAAM,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,IAAA,EAAM,KAAM,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,OAAA,EAAS,IAAQ,CAAC,CAAA,CAAA,CAAA;AAAA,MAChO,IAAA,EAAM;AAAA,KACV;AAAA,EACJ;AACJ;;;;"}
1
+ {"version":3,"file":"ambient-occlusion-block.js","sources":["../../../../../src/material/node/blocks/ambient-occlusion-block.ts"],"sourcesContent":["import type { BlockEmitter, NodeExpr } from \"../node-types.js\";\n\nconst HELPER_KEY = \"nme_ambientOcclusion\";\nconst HELPER_WGSL = `\nfn nme_aoNormalFromDepth(depthTex: texture_2d<f32>, depthSamp: sampler, depth: f32, coords: vec2<f32>, radius: f32) -> vec3<f32> {\n let offset1 = vec2<f32>(0.0, radius);\n let offset2 = vec2<f32>(radius, 0.0);\n let depth1 = textureSampleLevel(depthTex, depthSamp, coords + offset1, 0.0).r;\n let depth2 = textureSampleLevel(depthTex, depthSamp, coords + offset2, 0.0).r;\n let p1 = vec3<f32>(offset1, depth1 - depth);\n let p2 = vec3<f32>(offset2, depth2 - depth);\n var normal = cross(p1, p2);\n normal.z = -normal.z;\n return normalize(normal);\n}\n\nfn nme_aoRandom(uv: vec2<f32>) -> vec3<f32> {\n let x = fract(sin(dot(uv, vec2<f32>(12.9898, 78.233))) * 43758.5453);\n let y = fract(sin(dot(uv, vec2<f32>(39.3468, 11.135))) * 24634.6345);\n let z = fract(sin(dot(uv, vec2<f32>(73.156, 52.235))) * 12414.2347);\n return normalize(vec3<f32>(x, y, z));\n}\n\nfn nme_computeAo(depthTex: texture_2d<f32>, depthSamp: sampler, fragCoord: vec4<f32>, screenSize: vec2<f32>, radius: f32, area: f32, fallOff: f32) -> f32 {\n let uv = fragCoord.xy / screenSize;\n let random = nme_aoRandom(uv * 4.0);\n let depth = textureSampleLevel(depthTex, depthSamp, uv, 0.0).r;\n let position = vec3<f32>(uv, depth);\n let normal = nme_aoNormalFromDepth(depthTex, depthSamp, depth, uv, radius);\n let radiusDepth = radius / depth;\n let sampleSphere = array<vec3<f32>, 16>(\n vec3<f32>(0.5381, 0.1856, -0.4319), vec3<f32>(0.1379, 0.2486, 0.4430), vec3<f32>(0.3371, 0.5679, -0.0057), vec3<f32>(-0.6999, -0.0451, -0.0019),\n vec3<f32>(0.0689, -0.1598, -0.8547), vec3<f32>(0.0560, 0.0069, -0.1843), vec3<f32>(-0.0146, 0.1402, 0.0762), vec3<f32>(0.0100, -0.1924, -0.0344),\n vec3<f32>(-0.3577, -0.5301, -0.4358), vec3<f32>(-0.3169, 0.1063, 0.0158), vec3<f32>(0.0103, -0.5869, 0.0046), vec3<f32>(-0.0897, -0.4940, 0.3287),\n vec3<f32>(0.7119, -0.0154, -0.0918), vec3<f32>(-0.0533, 0.0596, -0.5411), vec3<f32>(0.0352, -0.0631, 0.5460), vec3<f32>(-0.4776, 0.2847, -0.0271)\n );\n var occlusion = 0.0;\n for (var i = 0; i < 16; i = i + 1) {\n let ray = radiusDepth * reflect(sampleSphere[i], random);\n let hemiRay = position + sign(dot(ray, normal)) * ray;\n let occlusionDepth = textureSample(depthTex, depthSamp, clamp(hemiRay.xy, vec2<f32>(0.001), vec2<f32>(0.999))).r;\n let difference = depth - occlusionDepth;\n occlusion += step(fallOff, difference) * (1.0 - smoothstep(fallOff, area, difference));\n }\n return clamp(1.0 - occlusion / 16.0, 0.0, 1.0);\n}\n`;\n\nfunction num(value: unknown, fallback: number): string {\n return typeof value === \"number\" ? `${value}` : `${fallback}`;\n}\n\nexport const emitter: BlockEmitter = {\n className: \"AmbientOcclusionBlock\",\n stage: \"fragment\",\n emit(block, _outputName, stage, state, ctx): NodeExpr {\n const source = block.inputs.get(\"source\")?.source;\n if (!source) {\n throw new Error(`NodeMaterial: AmbientOcclusionBlock \"${block.name}\" requires an ImageSourceBlock source`);\n }\n const producer = ctx.graph.blocks.get(source.blockId);\n const bindingName = ctx.sanitize(producer?.name || `ao${block.id}`);\n if (!state.textures.find((t) => t.name === bindingName)) {\n state.textures.push({ name: bindingName, kind: \"texture2d\", texture: null });\n }\n state.fragment.helpers.set(HELPER_KEY, HELPER_WGSL);\n const screenSize = ctx.cast(ctx.resolve(block, \"screenSize\", stage, state), \"vec2f\").expr;\n return {\n expr: `nme_computeAo(nodeTex_${bindingName}, nodeSamp_${bindingName}, _NME_FRAG_COORD_, ${screenSize}, ${num(block.serialized.radius, 0.0001)}, ${num(block.serialized.area, 0.0075)}, ${num(block.serialized.fallOff, 0.000001)})`,\n type: \"f32\",\n };\n },\n};\n"],"names":[],"mappings":"AAEA,MAAM,UAAA,GAAa,sBAAA;AACnB,MAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AA6CpB,SAAS,GAAA,CAAI,OAAgB,QAAA,EAA0B;AACnD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,GAAG,KAAK,CAAA,CAAA,GAAK,GAAG,QAAQ,CAAA,CAAA;AAC/D;AAEO,MAAM,OAAA,GAAwB;AAAA,EACjC,SAAA,EAAW,uBAAA;AAAA,EACX,KAAA,EAAO,UAAA;AAAA,EACP,IAAA,CAAK,KAAA,EAAO,WAAA,EAAa,KAAA,EAAO,OAAO,GAAA,EAAe;AAClD,IAAA,MAAM,MAAA,GAAS,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAA,EAAG,MAAA;AAC3C,IAAA,IAAI,CAAC,MAAA,EAAQ;AACT,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,qCAAA,EAAwC,KAAA,CAAM,IAAI,CAAA,qCAAA,CAAuC,CAAA;AAAA,IAC7G;AACA,IAAA,MAAM,WAAW,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,OAAO,OAAO,CAAA;AACpD,IAAA,MAAM,WAAA,GAAc,IAAI,QAAA,CAAS,QAAA,EAAU,QAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,EAAE,CAAA,CAAE,CAAA;AAClE,IAAA,IAAI,CAAC,MAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,WAAW,CAAA,EAAG;AACrD,MAAA,KAAA,CAAM,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,aAAa,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,IAAA,EAAM,CAAA;AAAA,IAC/E;AACA,IAAA,KAAA,CAAM,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,UAAA,EAAY,WAAW,CAAA;AAClD,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,KAAA,EAAO,YAAA,EAAc,KAAA,EAAO,KAAK,CAAA,EAAG,OAAO,CAAA,CAAE,IAAA;AACrF,IAAA,OAAO;AAAA,MACH,IAAA,EAAM,CAAA,sBAAA,EAAyB,WAAW,CAAA,WAAA,EAAc,WAAW,CAAA,oBAAA,EAAuB,UAAU,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,MAAA,EAAQ,IAAM,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,IAAA,EAAM,KAAM,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,OAAA,EAAS,IAAQ,CAAC,CAAA,CAAA,CAAA;AAAA,MAChO,IAAA,EAAM;AAAA,KACV;AAAA,EACJ;AACJ;;;;"}
@@ -6,16 +6,13 @@ const OUTPUT = {
6
6
  b: { swizzle: ".z", type: "f32" },
7
7
  a: { swizzle: ".w", type: "f32" }
8
8
  };
9
- function sanitize(name) {
10
- return name.replace(/[^A-Za-z0-9_]/g, "_");
11
- }
12
9
  function bindingName(block, inputName, ctx, fallback) {
13
10
  const input = block.inputs.get(inputName);
14
11
  if (input?.source) {
15
12
  const producer = ctx.graph.blocks.get(input.source.blockId);
16
- return sanitize(producer?.name || fallback);
13
+ return ctx.sanitize(producer?.name || fallback);
17
14
  }
18
- return sanitize(fallback);
15
+ return ctx.sanitize(fallback);
19
16
  }
20
17
  function ensureBinding(state, name) {
21
18
  if (!state.textures.find((t) => t.name === name)) {
@@ -48,7 +45,7 @@ function emitBiPlanarSample(block, stage, state, ctx) {
48
45
  if (memo) {
49
46
  return memo;
50
47
  }
51
- const baseName = sanitize(block.name || `biPlanar${block.id}`);
48
+ const baseName = ctx.sanitize(block.name || `biPlanar${block.id}`);
52
49
  const texX = bindingName(block, "source", ctx, baseName);
53
50
  const texY = bindingName(block, "sourceY", ctx, texX);
54
51
  ensureBinding(state, texX);
@@ -1 +1 @@
1
- {"version":3,"file":"biplanar-block.js","sources":["../../../../../src/material/node/blocks/biplanar-block.ts"],"sourcesContent":["/** BiPlanarBlock — two-axis variant of triplanar texture projection.\n *\n * Uses the Babylon.js/Iñigo Quilez biplanar method: choose major/median axes\n * from abs(normal), sample those projections with explicit position\n * derivatives, and blend with the shaped two-component weights.\n */\n\nimport type { BlockEmitter, NodeBlock, NodeBuildState, NodeEmitContext, NodeExpr, NodeValueType, Stage } from \"../node-types.js\";\n\nconst OUTPUT: Record<string, { swizzle: string; type: NodeValueType }> = {\n rgba: { swizzle: \"\", type: \"vec4f\" },\n rgb: { swizzle: \".xyz\", type: \"vec3f\" },\n r: { swizzle: \".x\", type: \"f32\" },\n g: { swizzle: \".y\", type: \"f32\" },\n b: { swizzle: \".z\", type: \"f32\" },\n a: { swizzle: \".w\", type: \"f32\" },\n};\n\nfunction sanitize(name: string): string {\n return name.replace(/[^A-Za-z0-9_]/g, \"_\");\n}\n\nfunction bindingName(block: NodeBlock, inputName: string, ctx: NodeEmitContext, fallback: string): string {\n const input = block.inputs.get(inputName);\n if (input?.source) {\n const producer = ctx.graph.blocks.get(input.source.blockId);\n return sanitize(producer?.name || fallback);\n }\n return sanitize(fallback);\n}\n\nfunction ensureBinding(state: NodeBuildState, name: string): void {\n if (!state.textures.find((t) => t.name === name)) {\n state.textures.push({ name, kind: \"texture2d\", texture: null });\n }\n}\n\nfunction applyColorSpace(expr: string, outputName: string, convertToLinear: boolean, convertToGamma: boolean): string {\n if (!convertToLinear && !convertToGamma) {\n return expr;\n }\n const power = convertToLinear ? \"2.2\" : \"0.45454545\";\n if (outputName === \"rgba\") {\n return `vec4<f32>(pow(max(${expr}.xyz, vec3<f32>(0.0)), vec3<f32>(${power})), ${expr}.w)`;\n }\n if (outputName === \"rgb\") {\n return `pow(max(${expr}.xyz, vec3<f32>(0.0)), vec3<f32>(${power}))`;\n }\n if (outputName === \"r\" || outputName === \"g\" || outputName === \"b\") {\n return `pow(max(${expr}${OUTPUT[outputName]!.swizzle}, 0.0), ${power})`;\n }\n return expr;\n}\n\nfunction emitBiPlanarSample(block: NodeBlock, stage: Stage, state: NodeBuildState, ctx: NodeEmitContext): NodeExpr {\n if (stage !== \"fragment\") {\n throw new Error(\"BiPlanarBlock: texture projection is supported only in the fragment stage\");\n }\n\n const stageState = state.fragment;\n const memoKey = `_biplanar_${block.id}_sample`;\n const memo = stageState.memo.get(memoKey);\n if (memo) {\n return memo;\n }\n\n const baseName = sanitize(block.name || `biPlanar${block.id}`);\n const texX = bindingName(block, \"source\", ctx, baseName);\n const texY = bindingName(block, \"sourceY\", ctx, texX);\n ensureBinding(state, texX);\n ensureBinding(state, texY);\n\n const position = ctx.cast(ctx.resolve(block, \"position\", stage, state), \"vec3f\");\n const normal = ctx.cast(ctx.resolve(block, \"normal\", stage, state), \"vec3f\");\n const sharpness = block.inputs.get(\"sharpness\")?.source ? ctx.cast(ctx.resolve(block, \"sharpness\", stage, state), \"f32\").expr : \"1.0\";\n\n const temp = ctx.temp(state, \"bi\");\n const p = `_p${temp}`;\n const dx = `_dx${temp}`;\n const dy = `_dy${temp}`;\n const n = `_n${temp}`;\n const ma = `_ma${temp}`;\n const mi = `_mi${temp}`;\n const me = `_me${temp}`;\n const x = `_x${temp}`;\n const y = `_y${temp}`;\n const w = `_w${temp}`;\n const sample = `_sample${temp}`;\n\n stageState.body.push(`let ${p} = ${position.expr};`);\n stageState.body.push(`let ${dx} = dpdx(${p});`);\n stageState.body.push(`let ${dy} = dpdy(${p});`);\n stageState.body.push(`let ${n} = abs(${normal.expr});`);\n stageState.body.push(`var ${ma}: vec3<i32>;`);\n stageState.body.push(\n `if (${n}.x > ${n}.y && ${n}.x > ${n}.z) { ${ma} = vec3<i32>(0, 1, 2); } else if (${n}.y > ${n}.z) { ${ma} = vec3<i32>(1, 2, 0); } else { ${ma} = vec3<i32>(2, 0, 1); }`\n );\n stageState.body.push(`var ${mi}: vec3<i32>;`);\n stageState.body.push(\n `if (${n}.x < ${n}.y && ${n}.x < ${n}.z) { ${mi} = vec3<i32>(0, 1, 2); } else if (${n}.y < ${n}.z) { ${mi} = vec3<i32>(1, 2, 0); } else { ${mi} = vec3<i32>(2, 0, 1); }`\n );\n stageState.body.push(`let ${me} = vec3<i32>(3, 3, 3) - ${mi} - ${ma};`);\n stageState.body.push(\n `let ${x} = textureSampleGrad(nodeTex_${texX}, nodeSamp_${texX}, vec2<f32>(${p}[${ma}.y], ${p}[${ma}.z]), vec2<f32>(${dx}[${ma}.y], ${dx}[${ma}.z]), vec2<f32>(${dy}[${ma}.y], ${dy}[${ma}.z]));`\n );\n stageState.body.push(\n `let ${y} = textureSampleGrad(nodeTex_${texY}, nodeSamp_${texY}, vec2<f32>(${p}[${me}.y], ${p}[${me}.z]), vec2<f32>(${dx}[${me}.y], ${dx}[${me}.z]), vec2<f32>(${dy}[${me}.y], ${dy}[${me}.z]));`\n );\n stageState.body.push(`var ${w} = vec2<f32>(${n}[${ma}.x], ${n}[${me}.x]);`);\n stageState.body.push(`${w} = clamp((${w} - vec2<f32>(0.5773)) / vec2<f32>(1.0 - 0.5773), vec2<f32>(0.0), vec2<f32>(1.0));`);\n stageState.body.push(`${w} = pow(${w}, vec2<f32>(${sharpness} / 8.0));`);\n stageState.body.push(`let ${sample} = (${x} * ${w}.x + ${y} * ${w}.y) / (${w}.x + ${w}.y);`);\n\n const result = { expr: sample, type: \"vec4f\" } as const;\n stageState.memo.set(memoKey, result);\n return result;\n}\n\nexport const emitter: BlockEmitter = {\n className: \"BiPlanarBlock\",\n emit(block, outputName, stage, state, ctx) {\n if (outputName === \"level\") {\n return { expr: \"1.0\", type: \"f32\" };\n }\n const sample = emitBiPlanarSample(block, stage, state, ctx);\n const out = OUTPUT[outputName] ?? OUTPUT.rgba!;\n const serialized = block.serialized as { convertToLinearSpace?: boolean; convertToGammaSpace?: boolean };\n const converted = applyColorSpace(sample.expr, outputName, serialized.convertToLinearSpace === true, serialized.convertToGammaSpace === true);\n return { expr: converted === sample.expr ? `${sample.expr}${out.swizzle}` : converted, type: out.type };\n },\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"biplanar-block.js","sources":["../../../../../src/material/node/blocks/biplanar-block.ts"],"sourcesContent":["/** BiPlanarBlock — two-axis variant of triplanar texture projection.\n *\n * Uses the Babylon.js/Iñigo Quilez biplanar method: choose major/median axes\n * from abs(normal), sample those projections with explicit position\n * derivatives, and blend with the shaped two-component weights.\n */\n\nimport type { BlockEmitter, NodeBlock, NodeBuildState, NodeEmitContext, NodeExpr, NodeValueType, Stage } from \"../node-types.js\";\n\nconst OUTPUT: Record<string, { swizzle: string; type: NodeValueType }> = {\n rgba: { swizzle: \"\", type: \"vec4f\" },\n rgb: { swizzle: \".xyz\", type: \"vec3f\" },\n r: { swizzle: \".x\", type: \"f32\" },\n g: { swizzle: \".y\", type: \"f32\" },\n b: { swizzle: \".z\", type: \"f32\" },\n a: { swizzle: \".w\", type: \"f32\" },\n};\n\nfunction bindingName(block: NodeBlock, inputName: string, ctx: NodeEmitContext, fallback: string): string {\n const input = block.inputs.get(inputName);\n if (input?.source) {\n const producer = ctx.graph.blocks.get(input.source.blockId);\n return ctx.sanitize(producer?.name || fallback);\n }\n return ctx.sanitize(fallback);\n}\n\nfunction ensureBinding(state: NodeBuildState, name: string): void {\n if (!state.textures.find((t) => t.name === name)) {\n state.textures.push({ name, kind: \"texture2d\", texture: null });\n }\n}\n\nfunction applyColorSpace(expr: string, outputName: string, convertToLinear: boolean, convertToGamma: boolean): string {\n if (!convertToLinear && !convertToGamma) {\n return expr;\n }\n const power = convertToLinear ? \"2.2\" : \"0.45454545\";\n if (outputName === \"rgba\") {\n return `vec4<f32>(pow(max(${expr}.xyz, vec3<f32>(0.0)), vec3<f32>(${power})), ${expr}.w)`;\n }\n if (outputName === \"rgb\") {\n return `pow(max(${expr}.xyz, vec3<f32>(0.0)), vec3<f32>(${power}))`;\n }\n if (outputName === \"r\" || outputName === \"g\" || outputName === \"b\") {\n return `pow(max(${expr}${OUTPUT[outputName]!.swizzle}, 0.0), ${power})`;\n }\n return expr;\n}\n\nfunction emitBiPlanarSample(block: NodeBlock, stage: Stage, state: NodeBuildState, ctx: NodeEmitContext): NodeExpr {\n if (stage !== \"fragment\") {\n throw new Error(\"BiPlanarBlock: texture projection is supported only in the fragment stage\");\n }\n\n const stageState = state.fragment;\n const memoKey = `_biplanar_${block.id}_sample`;\n const memo = stageState.memo.get(memoKey);\n if (memo) {\n return memo;\n }\n\n const baseName = ctx.sanitize(block.name || `biPlanar${block.id}`);\n const texX = bindingName(block, \"source\", ctx, baseName);\n const texY = bindingName(block, \"sourceY\", ctx, texX);\n ensureBinding(state, texX);\n ensureBinding(state, texY);\n\n const position = ctx.cast(ctx.resolve(block, \"position\", stage, state), \"vec3f\");\n const normal = ctx.cast(ctx.resolve(block, \"normal\", stage, state), \"vec3f\");\n const sharpness = block.inputs.get(\"sharpness\")?.source ? ctx.cast(ctx.resolve(block, \"sharpness\", stage, state), \"f32\").expr : \"1.0\";\n\n const temp = ctx.temp(state, \"bi\");\n const p = `_p${temp}`;\n const dx = `_dx${temp}`;\n const dy = `_dy${temp}`;\n const n = `_n${temp}`;\n const ma = `_ma${temp}`;\n const mi = `_mi${temp}`;\n const me = `_me${temp}`;\n const x = `_x${temp}`;\n const y = `_y${temp}`;\n const w = `_w${temp}`;\n const sample = `_sample${temp}`;\n\n stageState.body.push(`let ${p} = ${position.expr};`);\n stageState.body.push(`let ${dx} = dpdx(${p});`);\n stageState.body.push(`let ${dy} = dpdy(${p});`);\n stageState.body.push(`let ${n} = abs(${normal.expr});`);\n stageState.body.push(`var ${ma}: vec3<i32>;`);\n stageState.body.push(\n `if (${n}.x > ${n}.y && ${n}.x > ${n}.z) { ${ma} = vec3<i32>(0, 1, 2); } else if (${n}.y > ${n}.z) { ${ma} = vec3<i32>(1, 2, 0); } else { ${ma} = vec3<i32>(2, 0, 1); }`\n );\n stageState.body.push(`var ${mi}: vec3<i32>;`);\n stageState.body.push(\n `if (${n}.x < ${n}.y && ${n}.x < ${n}.z) { ${mi} = vec3<i32>(0, 1, 2); } else if (${n}.y < ${n}.z) { ${mi} = vec3<i32>(1, 2, 0); } else { ${mi} = vec3<i32>(2, 0, 1); }`\n );\n stageState.body.push(`let ${me} = vec3<i32>(3, 3, 3) - ${mi} - ${ma};`);\n stageState.body.push(\n `let ${x} = textureSampleGrad(nodeTex_${texX}, nodeSamp_${texX}, vec2<f32>(${p}[${ma}.y], ${p}[${ma}.z]), vec2<f32>(${dx}[${ma}.y], ${dx}[${ma}.z]), vec2<f32>(${dy}[${ma}.y], ${dy}[${ma}.z]));`\n );\n stageState.body.push(\n `let ${y} = textureSampleGrad(nodeTex_${texY}, nodeSamp_${texY}, vec2<f32>(${p}[${me}.y], ${p}[${me}.z]), vec2<f32>(${dx}[${me}.y], ${dx}[${me}.z]), vec2<f32>(${dy}[${me}.y], ${dy}[${me}.z]));`\n );\n stageState.body.push(`var ${w} = vec2<f32>(${n}[${ma}.x], ${n}[${me}.x]);`);\n stageState.body.push(`${w} = clamp((${w} - vec2<f32>(0.5773)) / vec2<f32>(1.0 - 0.5773), vec2<f32>(0.0), vec2<f32>(1.0));`);\n stageState.body.push(`${w} = pow(${w}, vec2<f32>(${sharpness} / 8.0));`);\n stageState.body.push(`let ${sample} = (${x} * ${w}.x + ${y} * ${w}.y) / (${w}.x + ${w}.y);`);\n\n const result = { expr: sample, type: \"vec4f\" } as const;\n stageState.memo.set(memoKey, result);\n return result;\n}\n\nexport const emitter: BlockEmitter = {\n className: \"BiPlanarBlock\",\n emit(block, outputName, stage, state, ctx) {\n if (outputName === \"level\") {\n return { expr: \"1.0\", type: \"f32\" };\n }\n const sample = emitBiPlanarSample(block, stage, state, ctx);\n const out = OUTPUT[outputName] ?? OUTPUT.rgba!;\n const serialized = block.serialized as { convertToLinearSpace?: boolean; convertToGammaSpace?: boolean };\n const converted = applyColorSpace(sample.expr, outputName, serialized.convertToLinearSpace === true, serialized.convertToGammaSpace === true);\n return { expr: converted === sample.expr ? `${sample.expr}${out.swizzle}` : converted, type: out.type };\n },\n};\n"],"names":[],"mappings":"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"}
@@ -1,16 +1,13 @@
1
1
  const emitter = {
2
2
  className: "ImageSourceBlock",
3
- emit(block, _outputName, _stage, state, _ctx) {
4
- const bindingName = sanitize(block.name || `img${block.id}`);
3
+ emit(block, _outputName, _stage, state, ctx) {
4
+ const bindingName = ctx.sanitize(block.name || `img${block.id}`);
5
5
  if (!state.textures.find((t) => t.name === bindingName)) {
6
6
  state.textures.push({ name: bindingName, kind: "texture2d", texture: null });
7
7
  }
8
8
  return { expr: bindingName, type: "texture2d" };
9
9
  }
10
10
  };
11
- function sanitize(name) {
12
- return name.replace(/[^A-Za-z0-9_]/g, "_");
13
- }
14
11
 
15
12
  export { emitter };
16
13
  //# sourceMappingURL=image-source.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-source.js","sources":["../../../../../src/material/node/blocks/image-source.ts"],"sourcesContent":["/** ImageSourceBlock — holds a texture reference and feeds a TextureBlock's `source` input.\n *\n * The emitter does not emit any WGSL of its own; it just advertises that a\n * texture binding named after this block exists, so the downstream TextureBlock\n * can refer to it. The actual texture upload is handled at material-build time.\n */\n\nimport type { BlockEmitter } from \"../node-types.js\";\n\nexport const emitter: BlockEmitter = {\n className: \"ImageSourceBlock\",\n emit(block, _outputName, _stage, state, _ctx) {\n const bindingName = sanitize(block.name || `img${block.id}`);\n if (!state.textures.find((t) => t.name === bindingName)) {\n state.textures.push({ name: bindingName, kind: \"texture2d\", texture: null });\n }\n // Return a sentinel — the TextureBlock consumes the binding name via this.\n return { expr: bindingName, type: \"texture2d\" };\n },\n};\n\nfunction sanitize(name: string): string {\n return name.replace(/[^A-Za-z0-9_]/g, \"_\");\n}\n"],"names":[],"mappings":"AASO,MAAM,OAAA,GAAwB;AAAA,EACjC,SAAA,EAAW,kBAAA;AAAA,EACX,IAAA,CAAK,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,OAAO,IAAA,EAAM;AAC1C,IAAA,MAAM,cAAc,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA,GAAA,EAAM,KAAA,CAAM,EAAE,CAAA,CAAE,CAAA;AAC3D,IAAA,IAAI,CAAC,MAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,WAAW,CAAA,EAAG;AACrD,MAAA,KAAA,CAAM,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,aAAa,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,IAAA,EAAM,CAAA;AAAA,IAC/E;AAEA,IAAA,OAAO,EAAE,IAAA,EAAM,WAAA,EAAa,IAAA,EAAM,WAAA,EAAY;AAAA,EAClD;AACJ;AAEA,SAAS,SAAS,IAAA,EAAsB;AACpC,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,gBAAA,EAAkB,GAAG,CAAA;AAC7C;;;;"}
1
+ {"version":3,"file":"image-source.js","sources":["../../../../../src/material/node/blocks/image-source.ts"],"sourcesContent":["/** ImageSourceBlock — holds a texture reference and feeds a TextureBlock's `source` input.\n *\n * The emitter does not emit any WGSL of its own; it just advertises that a\n * texture binding named after this block exists, so the downstream TextureBlock\n * can refer to it. The actual texture upload is handled at material-build time.\n */\n\nimport type { BlockEmitter } from \"../node-types.js\";\n\nexport const emitter: BlockEmitter = {\n className: \"ImageSourceBlock\",\n emit(block, _outputName, _stage, state, ctx) {\n const bindingName = ctx.sanitize(block.name || `img${block.id}`);\n if (!state.textures.find((t) => t.name === bindingName)) {\n state.textures.push({ name: bindingName, kind: \"texture2d\", texture: null });\n }\n // Return a sentinel — the TextureBlock consumes the binding name via this.\n return { expr: bindingName, type: \"texture2d\" };\n },\n};\n"],"names":[],"mappings":"AASO,MAAM,OAAA,GAAwB;AAAA,EACjC,SAAA,EAAW,kBAAA;AAAA,EACX,IAAA,CAAK,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,OAAO,GAAA,EAAK;AACzC,IAAA,MAAM,WAAA,GAAc,IAAI,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA,GAAA,EAAM,KAAA,CAAM,EAAE,CAAA,CAAE,CAAA;AAC/D,IAAA,IAAI,CAAC,MAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,WAAW,CAAA,EAAG;AACrD,MAAA,KAAA,CAAM,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,aAAa,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,IAAA,EAAM,CAAA;AAAA,IAC/E;AAEA,IAAA,OAAO,EAAE,IAAA,EAAM,WAAA,EAAa,IAAA,EAAM,WAAA,EAAY;AAAA,EAClD;AACJ;;;;"}