@babylonjs/lite 1.4.0 → 1.5.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 (122) hide show
  1. package/dist/index.js +381 -375
  2. package/dist/index.js.map +1 -1
  3. package/index.d.ts +757 -0
  4. package/lib/audio/analyzer.js +65 -0
  5. package/lib/audio/analyzer.js.map +1 -0
  6. package/lib/audio/audio-bus.js +38 -0
  7. package/lib/audio/audio-bus.js.map +1 -0
  8. package/lib/audio/audio-engine.js +188 -0
  9. package/lib/audio/audio-engine.js.map +1 -0
  10. package/lib/audio/audio-fetch.js +18 -0
  11. package/lib/audio/audio-fetch.js.map +1 -0
  12. package/lib/audio/audio-param.js +96 -0
  13. package/lib/audio/audio-param.js.map +1 -0
  14. package/lib/audio/audio-signal.js +46 -0
  15. package/lib/audio/audio-signal.js.map +1 -0
  16. package/lib/audio/bus.js +33 -0
  17. package/lib/audio/bus.js.map +1 -0
  18. package/lib/audio/host-types.js +2 -0
  19. package/lib/audio/host-types.js.map +1 -0
  20. package/lib/audio/index.js +12 -0
  21. package/lib/audio/index.js.map +1 -0
  22. package/lib/audio/sound-buffer.js +59 -0
  23. package/lib/audio/sound-buffer.js.map +1 -0
  24. package/lib/audio/sound-source.js +57 -0
  25. package/lib/audio/sound-source.js.map +1 -0
  26. package/lib/audio/sound-sub-graph.js +72 -0
  27. package/lib/audio/sound-sub-graph.js.map +1 -0
  28. package/lib/audio/spatial.js +466 -0
  29. package/lib/audio/spatial.js.map +1 -0
  30. package/lib/audio/static-sound.js +313 -0
  31. package/lib/audio/static-sound.js.map +1 -0
  32. package/lib/audio/stereo.js +40 -0
  33. package/lib/audio/stereo.js.map +1 -0
  34. package/lib/audio/streaming-sound.js +377 -0
  35. package/lib/audio/streaming-sound.js.map +1 -0
  36. package/lib/audio/unmute-ui.js +72 -0
  37. package/lib/audio/unmute-ui.js.map +1 -0
  38. package/lib/audio/visualizer.js +101 -0
  39. package/lib/audio/visualizer.js.map +1 -0
  40. package/lib/engine/engine.js +1 -1
  41. package/lib/index.js +11 -0
  42. package/lib/index.js.map +1 -1
  43. package/lib/light/types.js.map +1 -1
  44. package/lib/loader-gltf/animation-pointer-basecolor.js +25 -0
  45. package/lib/loader-gltf/animation-pointer-basecolor.js.map +1 -0
  46. package/lib/loader-gltf/animation-pointer-ext.js +244 -0
  47. package/lib/loader-gltf/animation-pointer-ext.js.map +1 -0
  48. package/lib/loader-gltf/animation-pointer-lights.js +46 -0
  49. package/lib/loader-gltf/animation-pointer-lights.js.map +1 -0
  50. package/lib/loader-gltf/animation-pointer.js +4 -1
  51. package/lib/loader-gltf/animation-pointer.js.map +1 -1
  52. package/lib/loader-gltf/gltf-animation.js +5 -3
  53. package/lib/loader-gltf/gltf-animation.js.map +1 -1
  54. package/lib/loader-gltf/gltf-color-normalize.js +10 -1
  55. package/lib/loader-gltf/gltf-color-normalize.js.map +1 -1
  56. package/lib/loader-gltf/gltf-feature-animation-pointer.js +67 -47
  57. package/lib/loader-gltf/gltf-feature-animation-pointer.js.map +1 -1
  58. package/lib/loader-gltf/gltf-feature-lights-punctual.js +51 -9
  59. package/lib/loader-gltf/gltf-feature-lights-punctual.js.map +1 -1
  60. package/lib/loader-gltf/gltf-feature-primitive.js +20 -0
  61. package/lib/loader-gltf/gltf-feature-primitive.js.map +1 -0
  62. package/lib/loader-gltf/gltf-feature-registry.js +25 -0
  63. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  64. package/lib/loader-gltf/gltf-feature-skeleton.js +18 -3
  65. package/lib/loader-gltf/gltf-feature-skeleton.js.map +1 -1
  66. package/lib/loader-gltf/gltf-interleave.js +3 -2
  67. package/lib/loader-gltf/gltf-interleave.js.map +1 -1
  68. package/lib/loader-gltf/gltf-light-pointer-state.js +18 -0
  69. package/lib/loader-gltf/gltf-light-pointer-state.js.map +1 -0
  70. package/lib/loader-gltf/gltf-parser.js +7 -1
  71. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  72. package/lib/loader-gltf/gltf-pbr-builder-ext.js +1 -1
  73. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  74. package/lib/loader-gltf/gltf-pbr-builder.js +1 -1
  75. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  76. package/lib/loader-gltf/gltf-sampler-denorm.js +20 -0
  77. package/lib/loader-gltf/gltf-sampler-denorm.js.map +1 -0
  78. package/lib/loader-gltf/gltf-sampler-desc.js +11 -2
  79. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  80. package/lib/loader-gltf/gltf-uv-denorm.js +28 -0
  81. package/lib/loader-gltf/gltf-uv-denorm.js.map +1 -0
  82. package/lib/loader-gltf/load-gltf.js +15 -6
  83. package/lib/loader-gltf/load-gltf.js.map +1 -1
  84. package/lib/material/material-rebuild.js +4 -0
  85. package/lib/material/material-rebuild.js.map +1 -1
  86. package/lib/material/mesh-features.js +8 -1
  87. package/lib/material/mesh-features.js.map +1 -1
  88. package/lib/material/pbr/fragments/reflectance-fragment.js +1 -1
  89. package/lib/material/pbr/fragments/reflectance-fragment.js.map +1 -1
  90. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +1 -1
  91. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  92. package/lib/material/pbr/pbr-pipeline.js +7 -3
  93. package/lib/material/pbr/pbr-pipeline.js.map +1 -1
  94. package/lib/material/pbr/pbr-primitive-resolver.js +34 -0
  95. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -0
  96. package/lib/material/pbr/pbr-renderable.js +1 -1
  97. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  98. package/lib/material/shader/shader-material.js +9 -5
  99. package/lib/material/shader/shader-material.js.map +1 -1
  100. package/lib/material/shader/shader-thin-instance.js +1 -1
  101. package/lib/material/shader/shader-thin-instance.js.map +1 -1
  102. package/lib/material/standard/standard-renderable.js +1 -1
  103. package/lib/material/standard/standard-renderable.js.map +1 -1
  104. package/lib/mesh/mesh-dispose.js +1 -0
  105. package/lib/mesh/mesh-dispose.js.map +1 -1
  106. package/lib/mesh/thin-instance-cull-binding.js +15 -6
  107. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  108. package/lib/scene/scene-core.js +1 -0
  109. package/lib/scene/scene-core.js.map +1 -1
  110. package/lib/scene/scene-material-swap.js +2 -0
  111. package/lib/scene/scene-material-swap.js.map +1 -1
  112. package/lib/shadow/csm-shadow-task-hooks.js +67 -9
  113. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  114. package/lib/sprite/sprite-2d.js +4 -0
  115. package/lib/sprite/sprite-2d.js.map +1 -1
  116. package/lib/sprite/sprite-pipeline.js +25 -22
  117. package/lib/sprite/sprite-pipeline.js.map +1 -1
  118. package/lib/text/_gpu/text-pipeline.js +1 -1
  119. package/lib/text/_gpu/text-pipeline.js.map +1 -1
  120. package/lib/text/text-renderer.js +3 -1
  121. package/lib/text/text-renderer.js.map +1 -1
  122. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-feature-registry.js","sources":["../../../src/loader-gltf/gltf-feature-registry.ts"],"sourcesContent":["/**\n * glTF optional-feature registry.\n *\n * The `_features` table maps every optional glTF capability to a `[trigger, load]`\n * pair: a cheap trigger plus a dynamic `import()` of the feature's `GltfFeature`\n * module. A trigger is either an exact `extensionsUsed` name (the common case) or\n * a predicate over the parsed JSON for features that aren't a simple extension\n * membership (ORM compositing, skeletons, morph targets, animations, dielectric\n * material cluster).\n *\n * This whole module is itself dynamically imported by the core loader\n * (`load-gltf.ts`) ONLY when the asset can possibly trigger at least one feature\n * — so assets that use no optional features (plain metallic-roughness GLBs) never\n * pay for the table or its import thunks.\n *\n * The core loader knows zero feature names; new extensions are added here alone.\n */\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport { anyPrimitive, needsOrmComposite } from \"./gltf-parser.js\";\n\n/** Dynamic `import()` of a feature's `GltfFeature` module. */\ntype Loader = () => Promise<{ default: GltfFeature }>;\n/** Either an exact `extensionsUsed` name, or a predicate over the parsed JSON. */\ntype Trigger = string | ((json: any) => boolean);\n\nconst M = \"KHR_materials_\";\n\nconst _features: [Trigger, Loader][] = [\n // Pre-parse features (buffer-level): order matters — meshopt decompresses\n // bufferViews first, then quantization dequantizes the resulting accessors.\n [\"EXT_meshopt_compression\", () => import(\"./gltf-feature-meshopt.js\")],\n [\"KHR_mesh_quantization\", () => import(\"./gltf-ext-quantization.js\")],\n // Pre-mesh features (geometry decompression)\n [\"KHR_draco_mesh_compression\", () => import(\"./gltf-feature-draco.js\")],\n // Material extensions\n [M + \"clearcoat\", () => import(\"./gltf-ext-clearcoat.js\")],\n [M + \"iridescence\", () => import(\"./gltf-ext-iridescence.js\")],\n [M + \"emissive_strength\", () => import(\"./gltf-ext-emissive-strength.js\")],\n [M + \"sheen\", () => import(\"./gltf-ext-sheen.js\")],\n [M + \"anisotropy\", () => import(\"./gltf-ext-anisotropy.js\")],\n [M + \"diffuse_transmission\", () => import(\"./gltf-ext-diffuse-transmission.js\")],\n [M + \"unlit\", () => import(\"./gltf-ext-unlit.js\")],\n [M + \"pbrSpecularGlossiness\", () => import(\"./gltf-ext-spec-gloss.js\")],\n // Dielectric cluster (ior/specular/transmission/volume/dispersion) — any of the five triggers the\n // loader; transmission refraction is wired dynamically by the PBR material path when needed.\n [(j) => [\"transmission\", \"volume\", \"ior\", \"specular\", \"dispersion\"].some((e) => j.extensionsUsed?.includes(M + e)), () => import(\"./gltf-ext-dielectric.js\")],\n [\"KHR_texture_transform\", () => import(\"./gltf-ext-uv-transform.js\")],\n [\"KHR_texture_basisu\", () => import(\"./gltf-ext-basisu.js\")],\n [needsOrmComposite, () => import(\"./gltf-ext-orm.js\")],\n // Per-mesh features (predicates inlined to avoid eager imports)\n [(j) => !!j.skins?.length && anyPrimitive(j, (p) => p.attributes?.JOINTS_0 !== undefined), () => import(\"./gltf-feature-skeleton.js\")],\n [(j) => anyPrimitive(j, (p) => !!p.targets?.length), () => import(\"./gltf-feature-morph.js\")],\n // Per-asset features\n [hasGltfExtras, () => import(\"./gltf-feature-extras.js\")],\n [\"KHR_lights_punctual\", () => import(\"./gltf-feature-lights-punctual.js\")],\n [(j) => !!j.animations?.length, () => import(\"./gltf-feature-animations.js\")],\n [M + \"variants\", () => import(\"./gltf-feature-variants.js\")],\n [\"KHR_node_visibility\", () => import(\"./gltf-ext-node-visibility.js\")],\n [\"KHR_animation_pointer\", () => import(\"./gltf-feature-animation-pointer.js\")],\n [\"EXT_mesh_gpu_instancing\", () => import(\"./gltf-feature-gpu-instancing.js\")],\n [\"KHR_xmp_json_ld\", () => import(\"./gltf-feature-xmp.js\")],\n];\n\n/** Dynamic-import every feature the asset triggers. */\nexport async function loadGltfFeatures(json: any): Promise<GltfFeature[]> {\n const used: string[] = json.extensionsUsed ?? [];\n const mods = await Promise.all(_features.flatMap(([t, load]) => ((typeof t === \"string\" ? used.includes(t) : t(json)) ? [load()] : [])));\n return mods.map((m) => m.default);\n}\n\nfunction hasGltfExtras(json: any): boolean {\n const hasExtras = (item: any): boolean => item?.extras !== undefined;\n return (\n hasExtras(json.asset) ||\n !!json.nodes?.some(hasExtras) ||\n !!json.materials?.some(hasExtras) ||\n !!json.animations?.some(hasExtras) ||\n !!json.meshes?.some(hasExtras) ||\n anyPrimitive(json, hasExtras)\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-feature-registry.js","sources":["../../../src/loader-gltf/gltf-feature-registry.ts"],"sourcesContent":["/**\n * glTF optional-feature registry.\n *\n * The `_features` table maps every optional glTF capability to a `[trigger, load]`\n * pair: a cheap trigger plus a dynamic `import()` of the feature's `GltfFeature`\n * module. A trigger is either an exact `extensionsUsed` name (the common case) or\n * a predicate over the parsed JSON for features that aren't a simple extension\n * membership (ORM compositing, skeletons, morph targets, animations, dielectric\n * material cluster).\n *\n * This whole module is itself dynamically imported by the core loader\n * (`load-gltf.ts`) ONLY when the asset can possibly trigger at least one feature\n * — so assets that use no optional features (plain metallic-roughness GLBs) never\n * pay for the table or its import thunks.\n *\n * The core loader knows zero feature names; new extensions are added here alone.\n */\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport { anyPrimitive, needsOrmComposite } from \"./gltf-parser.js\";\n\n/** Dynamic `import()` of a feature's `GltfFeature` module. */\ntype Loader = () => Promise<{ default: GltfFeature }>;\n/** Either an exact `extensionsUsed` name, or a predicate over the parsed JSON. */\ntype Trigger = string | ((json: any) => boolean);\n\nconst M = \"KHR_materials_\";\n\nconst _features: [Trigger, Loader][] = [\n // Pre-parse features (buffer-level): order matters — meshopt decompresses\n // bufferViews first, then quantization dequantizes the resulting accessors.\n [\"EXT_meshopt_compression\", () => import(\"./gltf-feature-meshopt.js\")],\n [\"KHR_mesh_quantization\", () => import(\"./gltf-ext-quantization.js\")],\n // Pre-mesh features (geometry decompression)\n [\"KHR_draco_mesh_compression\", () => import(\"./gltf-feature-draco.js\")],\n // Material extensions\n [M + \"clearcoat\", () => import(\"./gltf-ext-clearcoat.js\")],\n [M + \"iridescence\", () => import(\"./gltf-ext-iridescence.js\")],\n [M + \"emissive_strength\", () => import(\"./gltf-ext-emissive-strength.js\")],\n [M + \"sheen\", () => import(\"./gltf-ext-sheen.js\")],\n [M + \"anisotropy\", () => import(\"./gltf-ext-anisotropy.js\")],\n [M + \"diffuse_transmission\", () => import(\"./gltf-ext-diffuse-transmission.js\")],\n [M + \"unlit\", () => import(\"./gltf-ext-unlit.js\")],\n [M + \"pbrSpecularGlossiness\", () => import(\"./gltf-ext-spec-gloss.js\")],\n // Dielectric cluster (ior/specular/transmission/volume/dispersion) — any of the five triggers the\n // loader; transmission refraction is wired dynamically by the PBR material path when needed.\n [(j) => [\"transmission\", \"volume\", \"ior\", \"specular\", \"dispersion\"].some((e) => j.extensionsUsed?.includes(M + e)), () => import(\"./gltf-ext-dielectric.js\")],\n [\"KHR_texture_transform\", () => import(\"./gltf-ext-uv-transform.js\")],\n [\"KHR_texture_basisu\", () => import(\"./gltf-ext-basisu.js\")],\n [needsOrmComposite, () => import(\"./gltf-ext-orm.js\")],\n // Per-mesh features (predicates inlined to avoid eager imports)\n [(j) => !!j.skins?.length && anyPrimitive(j, (p) => p.attributes?.JOINTS_0 !== undefined), () => import(\"./gltf-feature-skeleton.js\")],\n [(j) => anyPrimitive(j, (p) => !!p.targets?.length), () => import(\"./gltf-feature-morph.js\")],\n // Non-triangle primitive topology (POINTS/LINES/LINE_STRIP/TRIANGLE_STRIP) or a\n // negative-determinant node (negative scale / mirrored matrix): both need the lazy primitive\n // feature (topology threading + winding reversal). Triangle-list positive-winding never triggers.\n [(j) => hasNegDetNode(j) || anyPrimitive(j, (p) => p.mode !== undefined && p.mode !== 4), () => import(\"./gltf-feature-primitive.js\")],\n // Per-asset features\n [hasGltfExtras, () => import(\"./gltf-feature-extras.js\")],\n [\"KHR_lights_punctual\", () => import(\"./gltf-feature-lights-punctual.js\")],\n [(j) => !!j.animations?.length, () => import(\"./gltf-feature-animations.js\")],\n // Non-Float32 / normalized animation sampler accessors (e.g. Animation_SamplerType normalized\n // BYTE/SHORT rotation) need the lazy denorm converter; plain float samplers never load it.\n [hasNonFloatAnimSampler, () => import(\"./gltf-sampler-denorm.js\")],\n [M + \"variants\", () => import(\"./gltf-feature-variants.js\")],\n [\"KHR_node_visibility\", () => import(\"./gltf-ext-node-visibility.js\")],\n [\"KHR_animation_pointer\", () => import(\"./gltf-feature-animation-pointer.js\")],\n [\"EXT_mesh_gpu_instancing\", () => import(\"./gltf-feature-gpu-instancing.js\")],\n [\"KHR_xmp_json_ld\", () => import(\"./gltf-feature-xmp.js\")],\n];\n\n/** Dynamic-import every feature the asset triggers. */\nexport async function loadGltfFeatures(json: any): Promise<GltfFeature[]> {\n const used: string[] = json.extensionsUsed ?? [];\n const mods = await Promise.all(_features.flatMap(([t, load]) => ((typeof t === \"string\" ? used.includes(t) : t(json)) ? [load()] : [])));\n return mods.map((m) => m.default);\n}\n\nfunction hasGltfExtras(json: any): boolean {\n const hasExtras = (item: any): boolean => item?.extras !== undefined;\n return (\n hasExtras(json.asset) ||\n !!json.nodes?.some(hasExtras) ||\n !!json.materials?.some(hasExtras) ||\n !!json.animations?.some(hasExtras) ||\n !!json.meshes?.some(hasExtras) ||\n anyPrimitive(json, hasExtras)\n );\n}\n\n/** True if any animation sampler reads a non-Float32 input/output accessor (normalized BYTE/SHORT\n * rotation output, normalized UNSIGNED_BYTE flags, …) — the only case that needs the lazy sampler\n * denorm converter. Plain Float32 samplers (the overwhelming majority) skip it. */\nfunction hasNonFloatAnimSampler(json: any): boolean {\n const accessors = json.accessors;\n return !!(json.animations as any[] | undefined)?.some((a) =>\n a.samplers?.some((s: any) => accessors[s.input]?.componentType !== 5126 || accessors[s.output]?.componentType !== 5126)\n );\n}\n\n/** True if any node introduces a negative-determinant local transform — a\n * negative scale (odd number of negative components) or a `matrix` with a\n * negative 3x3 determinant. Such a node (or a child of one) can flip a mesh's\n * net world determinant positive, reversing its triangle winding. Gates the\n * lazy negative-winding feature so positive-scale / pure-TRS assets never load\n * it. A negative-determinant node whose meshes' net world determinant stays\n * negative over-triggers harmlessly (the feature then finds a non-positive\n * determinant per mesh and flags nothing). */\nfunction hasNegDetNode(json: any): boolean {\n return !!(json.nodes as any[] | undefined)?.some((n) => {\n if (n.scale) {\n return n.scale[0] * n.scale[1] * n.scale[2] < 0;\n }\n if (n.matrix) {\n const m = n.matrix;\n return m[0] * (m[5] * m[10] - m[6] * m[9]) + m[1] * (m[6] * m[8] - m[4] * m[10]) + m[2] * (m[4] * m[9] - m[5] * m[8]) < 0;\n }\n return false;\n });\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,19 @@
1
1
  import { resolveAccessor } from './gltf-parser.js';
2
+ import { F32, U16 } from '../engine/typed-arrays.js';
2
3
  import { _boneBuilder } from '../skeleton/bone-control-hooks.js';
3
4
 
5
+ function weightsToFloat32(w) {
6
+ if (w instanceof F32) {
7
+ return w;
8
+ }
9
+ const inv = w instanceof U16 ? 1 / 65535 : 1 / 255;
10
+ const s = w;
11
+ const out = new F32(s.length);
12
+ for (let i = 0; i < s.length; i++) {
13
+ out[i] = s[i] * inv;
14
+ }
15
+ return out;
16
+ }
4
17
  function resolveAttr(name, primitive, decoded, json, binChunk) {
5
18
  if (decoded && decoded._attributes.has(name)) {
6
19
  return decoded._attributes.get(name);
@@ -29,12 +42,14 @@ const feature = {
29
42
  const primitive = meshData._primitive;
30
43
  const decoded = meshData._decoded;
31
44
  const joints = await resolveAttr("JOINTS_0", primitive, decoded, json, binChunk);
32
- const weights = await resolveAttr("WEIGHTS_0", primitive, decoded, json, binChunk);
33
- if (!joints || !weights) {
45
+ const weightsRaw = await resolveAttr("WEIGHTS_0", primitive, decoded, json, binChunk);
46
+ if (!joints || !weightsRaw) {
34
47
  return;
35
48
  }
49
+ const weights = weightsToFloat32(weightsRaw);
36
50
  const joints1 = await resolveAttr("JOINTS_1", primitive, decoded, json, binChunk);
37
- const weights1 = await resolveAttr("WEIGHTS_1", primitive, decoded, json, binChunk);
51
+ const weights1Raw = await resolveAttr("WEIGHTS_1", primitive, decoded, json, binChunk);
52
+ const weights1 = weights1Raw ? weightsToFloat32(weights1Raw) : null;
38
53
  const [{ extractSkin, computeBoneTextureData }, { createSkeleton }] = await Promise.all([import('./gltf-animation.js'), import('../skeleton/create-skeleton.js')]);
39
54
  const skin = extractSkin(json, binChunk, node.skin, meshData._worldMatrix, parentMap, worldMatrixCache);
40
55
  const boneData = computeBoneTextureData(skin);
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-feature-skeleton.js","sources":["../../../src/loader-gltf/gltf-feature-skeleton.ts"],"sourcesContent":["/** Skeletal animation feature. Extracts joints/weights/skin on demand so the\n * core loader doesn't carry any skinning-related code for non-skinned assets. */\n\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport { resolveAccessor } from \"./gltf-parser.js\";\nimport { _boneBuilder } from \"../skeleton/bone-control-hooks.js\";\n\n/** Resolve a vertex attribute by name, preferring any pre-decoded\n * (e.g. Draco) data over the raw accessor. De-strides interleaved sources:\n * `resolveAccessor` assumes tight packing, so strided JOINTS/WEIGHTS — common\n * in skinned rigs that pack both into one bufferView with a byteStride — would\n * otherwise read neighbouring/padding bytes and corrupt the skin (wrong joint\n * indices → exploded or mis-posed mesh). */\nfunction resolveAttr(name: string, primitive: any, decoded: any, json: any, binChunk: DataView): ArrayBufferView | null | Promise<ArrayBufferView> {\n if (decoded && decoded._attributes.has(name)) {\n return decoded._attributes.get(name)!;\n }\n const idx = primitive.attributes?.[name];\n if (idx === undefined) {\n return null;\n }\n const accessor = json.accessors[idx];\n const bv = accessor.bufferView !== undefined ? json.bufferViews[accessor.bufferView] : undefined;\n const compBytes = accessor.componentType === 5126 || accessor.componentType === 5125 ? 4 : accessor.componentType === 5123 || accessor.componentType === 5122 ? 2 : 1;\n const stride = bv?.byteStride;\n // JOINTS_n and WEIGHTS_n are glTF VEC4 attributes.\n if (bv === undefined || stride === undefined || stride === 4 * compBytes) {\n return resolveAccessor(json, binChunk, idx)._data as ArrayBufferView;\n }\n return import(\"./gltf-strided-attribute.js\").then((m) => m.copyStridedAttribute(accessor, bv, binChunk, 4, compBytes));\n}\n\nconst feature: GltfFeature = {\n id: \"_skeleton\",\n async applyMesh(meshData, mesh, ctx) {\n const { _json: json, _binChunk: binChunk, _parentMap: parentMap, _worldMatrixCache: worldMatrixCache } = ctx;\n const node = json.nodes[meshData._nodeIndex];\n if (node.skin === undefined || !json.skins) {\n return;\n }\n const primitive = meshData._primitive;\n const decoded = meshData._decoded;\n const joints = (await resolveAttr(\"JOINTS_0\", primitive, decoded, json, binChunk)) as Uint16Array | Uint8Array | null;\n const weights = (await resolveAttr(\"WEIGHTS_0\", primitive, decoded, json, binChunk)) as Float32Array | null;\n if (!joints || !weights) {\n return;\n }\n const joints1 = (await resolveAttr(\"JOINTS_1\", primitive, decoded, json, binChunk)) as Uint16Array | Uint8Array | null;\n const weights1 = (await resolveAttr(\"WEIGHTS_1\", primitive, decoded, json, binChunk)) as Float32Array | null;\n\n const [{ extractSkin, computeBoneTextureData }, { createSkeleton }] = await Promise.all([import(\"./gltf-animation.js\"), import(\"../skeleton/create-skeleton.js\")]);\n const skin = extractSkin(json, binChunk, node.skin, meshData._worldMatrix, parentMap, worldMatrixCache);\n const boneData = computeBoneTextureData(skin);\n mesh.skeleton = createSkeleton(ctx._engine, joints, weights, skin.jointNodes.length, boneData, joints1, weights1);\n\n // When bone control is enabled, lazily create the asset-wide override map\n // here (per-mesh hook runs before any per-asset hook) so the animation feature\n // and the bone-control builder share the same map race-free.\n if (_boneBuilder && !ctx._boneOverrides) {\n ctx._boneOverrides = new Map();\n }\n },\n async applyAsset(meshes, _root, ctx) {\n // Bone control is opt-in: with the builder hook absent (default), no public\n // skeleton handles are built and `container.skeletons` stays undefined — the\n // whole bone-control implementation tree-shakes away.\n if (!_boneBuilder || !ctx._boneOverrides) {\n return {};\n }\n return _boneBuilder(ctx, meshes, ctx._boneOverrides);\n },\n};\nexport default feature;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-feature-skeleton.js","sources":["../../../src/loader-gltf/gltf-feature-skeleton.ts"],"sourcesContent":["/** Skeletal animation feature. Extracts joints/weights/skin on demand so the\n * core loader doesn't carry any skinning-related code for non-skinned assets. */\n\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport { resolveAccessor } from \"./gltf-parser.js\";\nimport { F32, U16 } from \"../engine/typed-arrays.js\";\nimport { _boneBuilder } from \"../skeleton/bone-control-hooks.js\";\n\n/** Denormalize a resolved WEIGHTS_n attribute to a tight Float32 VEC4 buffer.\n * glTF allows WEIGHTS to be FLOAT or normalized UNSIGNED_BYTE / UNSIGNED_SHORT;\n * the skinning pipeline binds weights as `float32x4`, so integer weights read\n * raw (0..255 / 0..65535) explode the skin (mesh collapses → blank render).\n * Float sources pass through untouched (zero cost for the common case). Kept\n * inline (not lazily imported): the denorm is smaller than a dynamic-import\n * thunk, and it only ships in the already skin-gated feature chunk. */\nfunction weightsToFloat32(w: ArrayBufferView): Float32Array {\n if (w instanceof F32) {\n return w;\n }\n const inv = w instanceof U16 ? 1 / 65535 : 1 / 255;\n const s = w as unknown as { [i: number]: number; length: number };\n const out = new F32(s.length);\n for (let i = 0; i < s.length; i++) {\n out[i] = s[i]! * inv;\n }\n return out;\n}\n\n/** Resolve a vertex attribute by name, preferring any pre-decoded\n * (e.g. Draco) data over the raw accessor. De-strides interleaved sources:\n * `resolveAccessor` assumes tight packing, so strided JOINTS/WEIGHTS — common\n * in skinned rigs that pack both into one bufferView with a byteStride — would\n * otherwise read neighbouring/padding bytes and corrupt the skin (wrong joint\n * indices → exploded or mis-posed mesh). */\nfunction resolveAttr(name: string, primitive: any, decoded: any, json: any, binChunk: DataView): ArrayBufferView | null | Promise<ArrayBufferView> {\n if (decoded && decoded._attributes.has(name)) {\n return decoded._attributes.get(name)!;\n }\n const idx = primitive.attributes?.[name];\n if (idx === undefined) {\n return null;\n }\n const accessor = json.accessors[idx];\n const bv = accessor.bufferView !== undefined ? json.bufferViews[accessor.bufferView] : undefined;\n const compBytes = accessor.componentType === 5126 || accessor.componentType === 5125 ? 4 : accessor.componentType === 5123 || accessor.componentType === 5122 ? 2 : 1;\n const stride = bv?.byteStride;\n // JOINTS_n and WEIGHTS_n are glTF VEC4 attributes.\n if (bv === undefined || stride === undefined || stride === 4 * compBytes) {\n return resolveAccessor(json, binChunk, idx)._data as ArrayBufferView;\n }\n return import(\"./gltf-strided-attribute.js\").then((m) => m.copyStridedAttribute(accessor, bv, binChunk, 4, compBytes));\n}\n\nconst feature: GltfFeature = {\n id: \"_skeleton\",\n async applyMesh(meshData, mesh, ctx) {\n const { _json: json, _binChunk: binChunk, _parentMap: parentMap, _worldMatrixCache: worldMatrixCache } = ctx;\n const node = json.nodes[meshData._nodeIndex];\n if (node.skin === undefined || !json.skins) {\n return;\n }\n const primitive = meshData._primitive;\n const decoded = meshData._decoded;\n const joints = (await resolveAttr(\"JOINTS_0\", primitive, decoded, json, binChunk)) as Uint16Array | Uint8Array | null;\n const weightsRaw = await resolveAttr(\"WEIGHTS_0\", primitive, decoded, json, binChunk);\n if (!joints || !weightsRaw) {\n return;\n }\n const weights = weightsToFloat32(weightsRaw);\n const joints1 = (await resolveAttr(\"JOINTS_1\", primitive, decoded, json, binChunk)) as Uint16Array | Uint8Array | null;\n const weights1Raw = await resolveAttr(\"WEIGHTS_1\", primitive, decoded, json, binChunk);\n const weights1 = weights1Raw ? weightsToFloat32(weights1Raw) : null;\n\n const [{ extractSkin, computeBoneTextureData }, { createSkeleton }] = await Promise.all([import(\"./gltf-animation.js\"), import(\"../skeleton/create-skeleton.js\")]);\n const skin = extractSkin(json, binChunk, node.skin, meshData._worldMatrix, parentMap, worldMatrixCache);\n const boneData = computeBoneTextureData(skin);\n mesh.skeleton = createSkeleton(ctx._engine, joints, weights, skin.jointNodes.length, boneData, joints1, weights1);\n\n // When bone control is enabled, lazily create the asset-wide override map\n // here (per-mesh hook runs before any per-asset hook) so the animation feature\n // and the bone-control builder share the same map race-free.\n if (_boneBuilder && !ctx._boneOverrides) {\n ctx._boneOverrides = new Map();\n }\n },\n async applyAsset(meshes, _root, ctx) {\n // Bone control is opt-in: with the builder hook absent (default), no public\n // skeleton handles are built and `container.skeletons` stays undefined — the\n // whole bone-control implementation tree-shakes away.\n if (!_boneBuilder || !ctx._boneOverrides) {\n return {};\n }\n return _boneBuilder(ctx, meshes, ctx._boneOverrides);\n },\n};\nexport default feature;\n"],"names":[],"mappings":"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"}
@@ -81,7 +81,7 @@ function resolveColorVec4(json, binChunk, idx) {
81
81
  }
82
82
  return out;
83
83
  }
84
- function buildInterleavedPartial(json, binChunk, primitive, worldMatrix, nodeIdx) {
84
+ async function buildInterleavedPartial(json, binChunk, primitive, worldMatrix, nodeIdx) {
85
85
  const attrs = primitive.attributes;
86
86
  let anyStrided = false;
87
87
  for (const name in attrs) {
@@ -112,7 +112,8 @@ function buildInterleavedPartial(json, binChunk, primitive, worldMatrix, nodeIdx
112
112
  vertexCount = pos._count;
113
113
  const nrm = resolveOne("NORMAL", false);
114
114
  vb._n = nrm._il;
115
- const uv = resolveOne("TEXCOORD_0", false);
115
+ const uvIdx = attrs["TEXCOORD_0"];
116
+ const uv = uvIdx !== void 0 && json.accessors[uvIdx].componentType !== FLOAT ? { _tight: (await import('./gltf-uv-denorm.js')).resolveUvVec2(json, binChunk, uvIdx), _count: json.accessors[uvIdx].count } : resolveOne("TEXCOORD_0", false);
116
117
  vb._u = uv._il;
117
118
  const tan = resolveOne("TANGENT", true);
118
119
  vb._t = tan._il;
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-interleave.js","sources":["../../../src/loader-gltf/gltf-interleave.ts"],"sourcesContent":["/**\n * Interleaved (strided) glTF vertex-buffer support — dynamically imported.\n *\n * The engine renders interleaved attributes genuinely: the raw strided\n * bufferView slice is uploaded ONCE as a shared GPU buffer, bound to each\n * attribute slot at byte offset 0, with the per-attribute byte offset encoded in\n * the pipeline vertex layout (`attributes[].offset`) and `arrayStride` set to the\n * bufferView byteStride. This mirrors stock Babylon.js's WebGPU vertex state and\n * avoids a non-zero `setVertexBuffer` bind offset, which corrupts vertex fetch on\n * some AMD (Renoir) / Dawn paths. The loader never rewrites the asset.\n *\n * This whole module is loaded via `await import()` only when an asset actually\n * contains an interleaved bufferView, so non-interleaved scenes pay ZERO bundle\n * cost. The tight CPU copy of position/normal/uv (for AABB, picking, CSG, …) is\n * de-strided LAZILY — only on first CPU read via `installLazyCpu` — so scenes\n * that only render never materialize it.\n */\n\nimport { F32, U32, U16, U8, DV } from \"../engine/typed-arrays.js\";\nimport { BU } from \"../engine/gpu-flags.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport type { Aabb } from \"../math/aabb.js\";\nimport { computeAabb } from \"../math/compute-aabb.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Mesh, MeshGPU } from \"../mesh/mesh.js\";\nimport { initMeshTransform } from \"../mesh/mesh.js\";\nimport type { PbrMaterialProps } from \"../material/pbr/pbr-material.js\";\nimport { createMappedBuffer } from \"../resource/gpu-buffers.js\";\nimport { resolveAccessor, TYPE_SIZES } from \"./gltf-parser.js\";\nimport { computeSmoothNormals } from \"./gltf-normals.js\";\nimport type { GltfMeshData } from \"./load-gltf.js\";\n\nconst FLOAT = 5126;\nconst UNSIGNED_SHORT = 5123;\nconst UNSIGNED_INT = 5125;\nconst UNSIGNED_BYTE = 5121;\n\nconst COMP_BYTES: Record<number, number> = { [UNSIGNED_BYTE]: 1, [UNSIGNED_SHORT]: 2, [UNSIGNED_INT]: 4, [FLOAT]: 4 };\n\nfunction createSequentialIndices(vertexCount: number): Uint16Array | Uint32Array {\n const indices = vertexCount > 0xffff ? new U32(vertexCount) : new U16(vertexCount);\n for (let i = 0; i < vertexCount; i++) {\n indices[i] = i;\n }\n return indices;\n}\n\n/** Interleave descriptor for one attribute sourced from a strided bufferView.\n * The raw slice is shared across attributes of the same bufferView; the\n * pipeline uses `_stride` as arrayStride and binds at `_offset`. */\nexport interface AccessorInterleave {\n /** @internal glTF bufferView index — shared-buffer key (same view → one GPU buffer). */\n _bufferView: number;\n /** @internal Interleave byte stride (bufferView.byteStride) → pipeline arrayStride. */\n _stride: number;\n /** @internal Attribute byte offset within the bufferView → pipeline vertex layout\n * `attributes[].offset` (the shared buffer is bound at offset 0). */\n _offset: number;\n /** @internal glTF component type (FLOAT, UNSIGNED_SHORT, …). */\n _componentType: number;\n /** @internal Components per vertex. */\n _componentCount: number;\n /** @internal Vertex count. */\n _count: number;\n /** @internal Raw bufferView bytes (shared across attributes). Retained after GPU upload\n * so the CPU copy can be de-strided lazily on demand. */\n _slice?: Uint8Array;\n}\n\n/** Per-attribute interleave sources for a primitive (keys mirror MeshVbLayout). */\nexport interface GltfVb {\n /** @internal */\n _p?: AccessorInterleave;\n /** @internal */\n _n?: AccessorInterleave;\n /** @internal */\n _t?: AccessorInterleave;\n /** @internal */\n _u?: AccessorInterleave;\n /** @internal */\n _u2?: AccessorInterleave;\n /** @internal */\n _c?: AccessorInterleave;\n}\n\n/** True if accessor `idx`'s bufferView is interleaved (byteStride present and\n * larger than the attribute's tightly-packed element size). */\nexport function accessorIsStrided(json: any, idx: number): boolean {\n const a = json.accessors[idx];\n const bv = json.bufferViews[a.bufferView];\n const stride: number | undefined = bv.byteStride;\n if (stride === undefined) {\n return false;\n }\n const elemBytes = (TYPE_SIZES[a.type] ?? 1) * (COMP_BYTES[a.componentType] ?? 4);\n return stride !== elemBytes;\n}\n\n/** Resolve a strided accessor into an {@link AccessorInterleave} descriptor. */\nfunction resolveStrided(json: any, binChunk: DataView, accessorIdx: number): AccessorInterleave {\n const accessor = json.accessors[accessorIdx];\n const bufferView = json.bufferViews[accessor.bufferView];\n const ab = binChunk.buffer as ArrayBuffer;\n return {\n _bufferView: accessor.bufferView,\n _stride: bufferView.byteStride,\n _offset: accessor.byteOffset ?? 0,\n _componentType: accessor.componentType,\n _componentCount: TYPE_SIZES[accessor.type] ?? 1,\n _count: accessor.count,\n _slice: new U8(ab, binChunk.byteOffset + (bufferView.byteOffset ?? 0), bufferView.byteLength),\n };\n}\n\n/** De-stride an interleaved attribute into a tight Float32Array, reading raw\n * component values (no normalization) so the result matches what a tight\n * accessor view would have produced. */\nfunction destrideToTight(il: AccessorInterleave): Float32Array {\n const dv = new DV(il._slice!.buffer, il._slice!.byteOffset, il._slice!.byteLength);\n const cb = COMP_BYTES[il._componentType] ?? 4;\n const ct = il._componentType;\n const cc = il._componentCount;\n const out = new F32(il._count * cc);\n for (let v = 0; v < il._count; v++) {\n const rowBase = il._offset + v * il._stride;\n for (let c = 0; c < cc; c++) {\n const off = rowBase + c * cb;\n out[v * cc + c] =\n ct === FLOAT ? dv.getFloat32(off, true) : ct === UNSIGNED_SHORT ? dv.getUint16(off, true) : ct === UNSIGNED_INT ? dv.getUint32(off, true) : dv.getUint8(off);\n }\n }\n return out;\n}\n\n/** Resolve COLOR_0 to a tight float32 VEC4 [0,1] buffer — the vertex-color layout the\n * PBR pipeline binds (float32x4: rgb modulates base color, a modulates alpha). glTF\n * COLOR_0 may be VEC3 or VEC4, FLOAT or normalized UNSIGNED_BYTE/SHORT, and (here)\n * interleaved with a byteStride. Binding the raw strided/ubyte source as float32x4\n * reads neighbouring bytes as floats (garbage / rainbow colors). This normalizes\n * integer types to [0,1], gives a VEC3 source alpha = 1, and de-strides — mirroring\n * the tight path's normalizeColorToVec4. Reads relative to `binChunk`. */\nfunction resolveColorVec4(json: any, binChunk: DataView, idx: number): Float32Array {\n const accessor = json.accessors[idx];\n const ct = accessor.componentType;\n const cb = COMP_BYTES[ct] ?? 4;\n const comps = TYPE_SIZES[accessor.type] ?? 4;\n const bv = json.bufferViews[accessor.bufferView];\n const stride = bv.byteStride ?? comps * cb;\n const inv = ct === UNSIGNED_BYTE ? 1 / 255 : ct === UNSIGNED_SHORT ? 1 / 65535 : 1;\n const base = (bv.byteOffset ?? 0) + (accessor.byteOffset ?? 0);\n const out = new F32(accessor.count * 4);\n for (let v = 0; v < accessor.count; v++) {\n const row = base + v * stride;\n for (let c = 0; c < 4; c++) {\n if (c === 3 && comps < 4) {\n out[v * 4 + 3] = 1;\n break;\n }\n const off = row + c * cb;\n const raw = ct === FLOAT ? binChunk.getFloat32(off, true) : ct === UNSIGNED_SHORT ? binChunk.getUint16(off, true) : binChunk.getUint8(off);\n out[v * 4 + c] = raw * inv;\n }\n }\n return out;\n}\n\n/** Build a mesh-data partial for a primitive, but ONLY if it actually sources\n * ≥1 attribute from an interleaved (strided) bufferView. Returns `undefined`\n * for fully-tight primitives so the caller falls back to its tight path.\n *\n * Strided POSITION/NORMAL/TEXCOORD_0 attributes keep their raw slice in `_vb`\n * (for genuine GPU interleaving) and leave the tight CPU field `null` — the\n * de-strided copy is materialized lazily on first CPU read (see\n * {@link installLazyCpu}). Strided TANGENT/TEXCOORD_1 are eagerly de-strided\n * (they feed device-lost recovery), but no current asset interleaves them.\n * COLOR_0 is always normalized to a tight float32x3 buffer (see\n * {@link resolveColorVec4}). Tight attributes resolve exactly like the core loader. */\nexport function buildInterleavedPartial(json: any, binChunk: DataView, primitive: any, worldMatrix: Mat4, nodeIdx: number): Omit<GltfMeshData, \"_material\"> | undefined {\n const attrs = primitive.attributes;\n\n // Per-primitive gate: bail (→ tight path) unless a vertex attribute is strided.\n let anyStrided = false;\n for (const name in attrs) {\n if (accessorIsStrided(json, attrs[name])) {\n anyStrided = true;\n break;\n }\n }\n if (!anyStrided) {\n return undefined;\n }\n\n const vb: GltfVb = {};\n let vertexCount = 0;\n\n // Resolve one attribute. Returns the interleave descriptor (when the source is\n // strided) and/or a tight CPU array. `eager` de-strides strided sources up-front\n // (TANGENT/UV2/COLOR feed device-lost recovery); lazy ones leave `_tight` null and\n // are de-strided on demand. The caller assigns `vb.<attr>` with a STATIC property\n // name (never a computed `vb[key]`) — a computed write would stay an unmangled\n // literal while every reader uses the mangled static name, corrupting the object\n // across the dynamic-import chunk boundary.\n const resolveOne = (name: string, eager: boolean): { _tight: Float32Array | null; _il?: AccessorInterleave; _count: number } => {\n const idx = attrs[name];\n if (idx === undefined) {\n return { _tight: null, _count: 0 };\n }\n if (accessorIsStrided(json, idx)) {\n const il = resolveStrided(json, binChunk, idx);\n return { _tight: eager ? destrideToTight(il) : null, _il: il, _count: il._count };\n }\n const av = resolveAccessor(json, binChunk, idx);\n return { _tight: av._data as Float32Array, _count: av._count };\n };\n\n const pos = resolveOne(\"POSITION\", false);\n vb._p = pos._il;\n vertexCount = pos._count;\n const nrm = resolveOne(\"NORMAL\", false);\n vb._n = nrm._il;\n const uv = resolveOne(\"TEXCOORD_0\", false);\n vb._u = uv._il;\n const tan = resolveOne(\"TANGENT\", true);\n vb._t = tan._il;\n const uv2 = resolveOne(\"TEXCOORD_1\", true);\n vb._u2 = uv2._il;\n // COLOR_0 is always materialized as a tight float32x4 [0,1] buffer (see\n // resolveColorVec4) — never bound strided — so ubyte/ushort/VEC3 sources don't\n // misalign against the pipeline's float32x4 vertex-color layout.\n const colorIdx = attrs[\"COLOR_0\"];\n const colors = colorIdx !== undefined ? resolveColorVec4(json, binChunk, colorIdx) : null;\n\n const positions = pos._tight;\n let normals = nrm._tight;\n let uvs = uv._tight;\n const tangents = tan._tight;\n const uv2s = uv2._tight;\n\n const idxData = primitive.indices !== undefined ? resolveAccessor(json, binChunk, primitive.indices) : null;\n const indices = idxData\n ? idxData._data instanceof U32\n ? new U32(idxData._data)\n : idxData._data instanceof U8\n ? Uint16Array.from(idxData._data)\n : new U16(idxData._data.buffer, idxData._data.byteOffset, idxData._count)\n : createSequentialIndices(vertexCount);\n\n // Absent (not merely strided) NORMAL: generate smooth normals to match the core\n // loader's tight path. A zero-filled normal buffer makes every lit fragment's\n // worldNormal NaN/black — e.g. a material-less skinned mesh whose interleaved\n // JOINTS/WEIGHTS route it here (SimpleSkin). computeSmoothNormals is statically\n // imported by this lazy interleave chunk, so non-interleaved scenes (which never\n // load this module) pay zero bundle cost for it.\n if (!normals && !vb._n) {\n const tightPos = positions ?? (vb._p ? destrideToTight(vb._p) : new F32(vertexCount * 3));\n normals = computeSmoothNormals(tightPos, indices, vertexCount);\n }\n if (!uvs && !vb._u) {\n uvs = new F32(vertexCount * 2);\n }\n\n // No NORMAL attribute (neither tight nor strided) → flat-shade per the glTF spec.\n const flatNormal = !nrm._tight && !vb._n;\n\n return {\n _positions: positions,\n _normals: normals,\n _tangents: tangents,\n _uvs: uvs,\n _uv2s: uv2s,\n _colors: colors,\n _flatNormal: flatNormal,\n _indices: indices,\n _vertexCount: vertexCount,\n _indexCount: indices.length,\n _worldMatrix: worldMatrix,\n _vb: vb,\n _nodeIndex: nodeIdx,\n _primitive: primitive,\n };\n}\n\n/** Build the GPU geometry for an interleaved mesh: one shared buffer per\n * bufferView for strided attributes (bound at offset 0; the per-attribute byte\n * offset goes into the pipeline vertex layout, matching stock Babylon.js), tight\n * attributes get their own buffer — byte-identical to non-interleaved meshes.\n * The raw `_slice` is intentionally retained on `_vb` so the CPU copy can be\n * de-strided lazily later (see {@link installLazyCpu}). */\nfunction buildInterleavedGpu(engine: EngineContext, m: GltfMeshData): MeshGPU {\n const vbsrc = m._vb!;\n const shared = new Map<number, GPUBuffer>();\n const vbuf = (a: AccessorInterleave | undefined, tight: Float32Array | null): GPUBuffer | null => {\n if (!a) {\n return tight ? createMappedBuffer(engine, tight, BU.VERTEX) : null;\n }\n let b = shared.get(a._bufferView);\n if (!b) {\n shared.set(a._bufferView, (b = createMappedBuffer(engine, a._slice!, BU.VERTEX)));\n }\n return b;\n };\n // Cache key encodes both stride AND byte offset per attribute: the offsets are\n // now baked into the pipeline vertex layout (attributes[].offset), so two meshes\n // with identical strides but different offsets need distinct pipelines.\n const k = (a: AccessorInterleave | undefined) => `${a?._stride ?? 0},${a?._offset ?? 0}`;\n return {\n positionBuffer: vbuf(vbsrc._p, m._positions)!,\n normalBuffer: vbuf(vbsrc._n, m._normals)!,\n tangentBuffer: m._tangents ? vbuf(vbsrc._t, m._tangents) : null,\n uvBuffer: vbuf(vbsrc._u, m._uvs)!,\n uv2Buffer: m._uv2s ? vbuf(vbsrc._u2, m._uv2s) : null,\n colorBuffer: m._colors ? vbuf(vbsrc._c, m._colors) : null,\n indexBuffer: createMappedBuffer(engine, m._indices, BU.INDEX),\n indexCount: m._indexCount,\n indexFormat: (m._indices instanceof U32 ? \"uint32\" : \"uint16\") as GPUIndexFormat,\n _vbLayout: vbsrc,\n _vbKey: `vb${k(vbsrc._p)}.${k(vbsrc._n)}.${k(vbsrc._t)}.${k(vbsrc._u)}`,\n };\n}\n\n/** Build a complete engine mesh from interleaved glTF mesh-data. Owns ALL\n * interleave-specific work (GPU upload, AABB fold, lazy CPU install, device-lost\n * retention) so the core loader's tight path stays byte-identical to the\n * non-interleaved engine — keeping interleave bytes out of every glTF scene that\n * doesn't use it. */\nexport function buildInterleavedMesh(engine: EngineContext, m: GltfMeshData, index: number, material: PbrMaterialProps, name?: string): Mesh {\n const gpu = buildInterleavedGpu(engine, m);\n\n // AABB: fold strided positions straight from the slice; tight positions normally.\n const [boundMin, boundMax] = m._vb!._p ? computeAabbStrided(m._vb!._p, m._worldMatrix) : computeAabb(m._positions!, m._worldMatrix);\n\n const mesh = {\n name: name || `gltf_mesh_${index}`,\n material,\n receiveShadows: false,\n boundMin,\n boundMax,\n skeleton: null,\n morphTargets: null,\n _gpu: gpu,\n _flatNormal: m._flatNormal,\n } as unknown as Mesh;\n initMeshTransform(mesh);\n\n // Lazy CPU geometry: the de-strided tight copy is built only on first read.\n installLazyCpu(mesh, m);\n mesh._cpuIndices = m._indices instanceof U32 ? m._indices : new U32(m._indices);\n engine._dlr?.m(mesh, m._uv2s, m._tangents, m._colors, m._indices, gpu.indexFormat);\n\n return mesh as Mesh;\n}\n\n/** Fold an AABB directly over an interleaved (strided) FLOAT vec3 position\n * source — no tight copy is materialized. Mirrors {@link computeAabb}'s\n * world-transform handling. All current interleaved assets use FLOAT positions. */\nexport function computeAabbStrided(il: AccessorInterleave, world?: Mat4): Aabb {\n const dv = new DV(il._slice!.buffer, il._slice!.byteOffset, il._slice!.byteLength);\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n for (let v = 0; v < il._count; v++) {\n const base = il._offset + v * il._stride;\n const lx = dv.getFloat32(base, true);\n const ly = dv.getFloat32(base + 4, true);\n const lz = dv.getFloat32(base + 8, true);\n let x = lx,\n y = ly,\n z = lz;\n if (world) {\n x = world[0]! * lx + world[4]! * ly + world[8]! * lz + world[12]!;\n y = world[1]! * lx + world[5]! * ly + world[9]! * lz + world[13]!;\n z = world[2]! * lx + world[6]! * ly + world[10]! * lz + world[14]!;\n }\n if (x < minX) {\n minX = x;\n }\n if (x > maxX) {\n maxX = x;\n }\n if (y < minY) {\n minY = y;\n }\n if (y > maxY) {\n maxY = y;\n }\n if (z < minZ) {\n minZ = z;\n }\n if (z > maxZ) {\n maxZ = z;\n }\n }\n return [\n [minX, minY, minZ],\n [maxX, maxY, maxZ],\n ];\n}\n\n/** Install lazy CPU-geometry accessors on an interleaved mesh. Each of\n * `_cpuPositions/_cpuNormals/_cpuUvs` that comes from a strided source is\n * defined as a getter that de-strides a tight copy on first access and caches\n * it; tight attributes are assigned directly. A mesh that is never picked /\n * CSG'd / navigated never materializes the de-strided arrays.\n *\n * The property names are written as STATIC literals (not a computed key) so the\n * minifier mangles them identically to the static reads in the picking /\n * device-lost code — a computed `defineProperty(mesh, key)` would leave the name\n * an unmangled literal and mismatch those reads across the chunk boundary. */\nexport function installLazyCpu(mesh: any, m: GltfMeshData): void {\n const vb = m._vb!;\n if (vb._p) {\n Object.defineProperty(mesh, \"_cpuPositions\", lazyCpuDesc(vb._p));\n } else if (m._positions) {\n mesh._cpuPositions = m._positions;\n }\n if (vb._n) {\n Object.defineProperty(mesh, \"_cpuNormals\", lazyCpuDesc(vb._n));\n } else if (m._normals) {\n mesh._cpuNormals = m._normals;\n }\n if (vb._u) {\n Object.defineProperty(mesh, \"_cpuUvs\", lazyCpuDesc(vb._u));\n } else if (m._uvs) {\n mesh._cpuUvs = m._uvs;\n }\n}\n\n/** Build a caching lazy-getter descriptor that de-strides `il` on first read. */\nfunction lazyCpuDesc(il: AccessorInterleave): PropertyDescriptor {\n let cached: Float32Array | undefined;\n return {\n configurable: true,\n enumerable: true,\n get(): Float32Array {\n return (cached ??= destrideToTight(il));\n },\n set(v: Float32Array): void {\n cached = v;\n },\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-interleave.js","sources":["../../../src/loader-gltf/gltf-interleave.ts"],"sourcesContent":["/**\n * Interleaved (strided) glTF vertex-buffer support — dynamically imported.\n *\n * The engine renders interleaved attributes genuinely: the raw strided\n * bufferView slice is uploaded ONCE as a shared GPU buffer, bound to each\n * attribute slot at byte offset 0, with the per-attribute byte offset encoded in\n * the pipeline vertex layout (`attributes[].offset`) and `arrayStride` set to the\n * bufferView byteStride. This mirrors stock Babylon.js's WebGPU vertex state and\n * avoids a non-zero `setVertexBuffer` bind offset, which corrupts vertex fetch on\n * some AMD (Renoir) / Dawn paths. The loader never rewrites the asset.\n *\n * This whole module is loaded via `await import()` only when an asset actually\n * contains an interleaved bufferView, so non-interleaved scenes pay ZERO bundle\n * cost. The tight CPU copy of position/normal/uv (for AABB, picking, CSG, …) is\n * de-strided LAZILY — only on first CPU read via `installLazyCpu` — so scenes\n * that only render never materialize it.\n */\n\nimport { F32, U32, U16, U8, DV } from \"../engine/typed-arrays.js\";\nimport { BU } from \"../engine/gpu-flags.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport type { Aabb } from \"../math/aabb.js\";\nimport { computeAabb } from \"../math/compute-aabb.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Mesh, MeshGPU } from \"../mesh/mesh.js\";\nimport { initMeshTransform } from \"../mesh/mesh.js\";\nimport type { PbrMaterialProps } from \"../material/pbr/pbr-material.js\";\nimport { createMappedBuffer } from \"../resource/gpu-buffers.js\";\nimport { resolveAccessor, TYPE_SIZES } from \"./gltf-parser.js\";\nimport { computeSmoothNormals } from \"./gltf-normals.js\";\nimport type { GltfMeshData } from \"./load-gltf.js\";\n\nconst FLOAT = 5126;\nconst UNSIGNED_SHORT = 5123;\nconst UNSIGNED_INT = 5125;\nconst UNSIGNED_BYTE = 5121;\n\nconst COMP_BYTES: Record<number, number> = { [UNSIGNED_BYTE]: 1, [UNSIGNED_SHORT]: 2, [UNSIGNED_INT]: 4, [FLOAT]: 4 };\n\nfunction createSequentialIndices(vertexCount: number): Uint16Array | Uint32Array {\n const indices = vertexCount > 0xffff ? new U32(vertexCount) : new U16(vertexCount);\n for (let i = 0; i < vertexCount; i++) {\n indices[i] = i;\n }\n return indices;\n}\n\n/** Interleave descriptor for one attribute sourced from a strided bufferView.\n * The raw slice is shared across attributes of the same bufferView; the\n * pipeline uses `_stride` as arrayStride and binds at `_offset`. */\nexport interface AccessorInterleave {\n /** @internal glTF bufferView index — shared-buffer key (same view → one GPU buffer). */\n _bufferView: number;\n /** @internal Interleave byte stride (bufferView.byteStride) → pipeline arrayStride. */\n _stride: number;\n /** @internal Attribute byte offset within the bufferView → pipeline vertex layout\n * `attributes[].offset` (the shared buffer is bound at offset 0). */\n _offset: number;\n /** @internal glTF component type (FLOAT, UNSIGNED_SHORT, …). */\n _componentType: number;\n /** @internal Components per vertex. */\n _componentCount: number;\n /** @internal Vertex count. */\n _count: number;\n /** @internal Raw bufferView bytes (shared across attributes). Retained after GPU upload\n * so the CPU copy can be de-strided lazily on demand. */\n _slice?: Uint8Array;\n}\n\n/** Per-attribute interleave sources for a primitive (keys mirror MeshVbLayout). */\nexport interface GltfVb {\n /** @internal */\n _p?: AccessorInterleave;\n /** @internal */\n _n?: AccessorInterleave;\n /** @internal */\n _t?: AccessorInterleave;\n /** @internal */\n _u?: AccessorInterleave;\n /** @internal */\n _u2?: AccessorInterleave;\n /** @internal */\n _c?: AccessorInterleave;\n}\n\n/** True if accessor `idx`'s bufferView is interleaved (byteStride present and\n * larger than the attribute's tightly-packed element size). */\nexport function accessorIsStrided(json: any, idx: number): boolean {\n const a = json.accessors[idx];\n const bv = json.bufferViews[a.bufferView];\n const stride: number | undefined = bv.byteStride;\n if (stride === undefined) {\n return false;\n }\n const elemBytes = (TYPE_SIZES[a.type] ?? 1) * (COMP_BYTES[a.componentType] ?? 4);\n return stride !== elemBytes;\n}\n\n/** Resolve a strided accessor into an {@link AccessorInterleave} descriptor. */\nfunction resolveStrided(json: any, binChunk: DataView, accessorIdx: number): AccessorInterleave {\n const accessor = json.accessors[accessorIdx];\n const bufferView = json.bufferViews[accessor.bufferView];\n const ab = binChunk.buffer as ArrayBuffer;\n return {\n _bufferView: accessor.bufferView,\n _stride: bufferView.byteStride,\n _offset: accessor.byteOffset ?? 0,\n _componentType: accessor.componentType,\n _componentCount: TYPE_SIZES[accessor.type] ?? 1,\n _count: accessor.count,\n _slice: new U8(ab, binChunk.byteOffset + (bufferView.byteOffset ?? 0), bufferView.byteLength),\n };\n}\n\n/** De-stride an interleaved attribute into a tight Float32Array, reading raw\n * component values (no normalization) so the result matches what a tight\n * accessor view would have produced. */\nfunction destrideToTight(il: AccessorInterleave): Float32Array {\n const dv = new DV(il._slice!.buffer, il._slice!.byteOffset, il._slice!.byteLength);\n const cb = COMP_BYTES[il._componentType] ?? 4;\n const ct = il._componentType;\n const cc = il._componentCount;\n const out = new F32(il._count * cc);\n for (let v = 0; v < il._count; v++) {\n const rowBase = il._offset + v * il._stride;\n for (let c = 0; c < cc; c++) {\n const off = rowBase + c * cb;\n out[v * cc + c] =\n ct === FLOAT ? dv.getFloat32(off, true) : ct === UNSIGNED_SHORT ? dv.getUint16(off, true) : ct === UNSIGNED_INT ? dv.getUint32(off, true) : dv.getUint8(off);\n }\n }\n return out;\n}\n\n/** Resolve COLOR_0 to a tight float32 VEC4 [0,1] buffer — the vertex-color layout the\n * PBR pipeline binds (float32x4: rgb modulates base color, a modulates alpha). glTF\n * COLOR_0 may be VEC3 or VEC4, FLOAT or normalized UNSIGNED_BYTE/SHORT, and (here)\n * interleaved with a byteStride. Binding the raw strided/ubyte source as float32x4\n * reads neighbouring bytes as floats (garbage / rainbow colors). This normalizes\n * integer types to [0,1], gives a VEC3 source alpha = 1, and de-strides — mirroring\n * the tight path's normalizeColorToVec4. Reads relative to `binChunk`. */\nfunction resolveColorVec4(json: any, binChunk: DataView, idx: number): Float32Array {\n const accessor = json.accessors[idx];\n const ct = accessor.componentType;\n const cb = COMP_BYTES[ct] ?? 4;\n const comps = TYPE_SIZES[accessor.type] ?? 4;\n const bv = json.bufferViews[accessor.bufferView];\n const stride = bv.byteStride ?? comps * cb;\n const inv = ct === UNSIGNED_BYTE ? 1 / 255 : ct === UNSIGNED_SHORT ? 1 / 65535 : 1;\n const base = (bv.byteOffset ?? 0) + (accessor.byteOffset ?? 0);\n const out = new F32(accessor.count * 4);\n for (let v = 0; v < accessor.count; v++) {\n const row = base + v * stride;\n for (let c = 0; c < 4; c++) {\n if (c === 3 && comps < 4) {\n out[v * 4 + 3] = 1;\n break;\n }\n const off = row + c * cb;\n const raw = ct === FLOAT ? binChunk.getFloat32(off, true) : ct === UNSIGNED_SHORT ? binChunk.getUint16(off, true) : binChunk.getUint8(off);\n out[v * 4 + c] = raw * inv;\n }\n }\n return out;\n}\n\n/** Build a mesh-data partial for a primitive, but ONLY if it actually sources\n * ≥1 attribute from an interleaved (strided) bufferView. Returns `undefined`\n * for fully-tight primitives so the caller falls back to its tight path.\n *\n * Strided POSITION/NORMAL/TEXCOORD_0 attributes keep their raw slice in `_vb`\n * (for genuine GPU interleaving) and leave the tight CPU field `null` — the\n * de-strided copy is materialized lazily on first CPU read (see\n * {@link installLazyCpu}). Strided TANGENT/TEXCOORD_1 are eagerly de-strided\n * (they feed device-lost recovery), but no current asset interleaves them.\n * COLOR_0 is always normalized to a tight float32x3 buffer (see\n * {@link resolveColorVec4}). Tight attributes resolve exactly like the core loader. */\nexport async function buildInterleavedPartial(\n json: any,\n binChunk: DataView,\n primitive: any,\n worldMatrix: Mat4,\n nodeIdx: number\n): Promise<Omit<GltfMeshData, \"_material\"> | undefined> {\n const attrs = primitive.attributes;\n\n // Per-primitive gate: bail (→ tight path) unless a vertex attribute is strided.\n let anyStrided = false;\n for (const name in attrs) {\n if (accessorIsStrided(json, attrs[name])) {\n anyStrided = true;\n break;\n }\n }\n if (!anyStrided) {\n return undefined;\n }\n\n const vb: GltfVb = {};\n let vertexCount = 0;\n\n // Resolve one attribute. Returns the interleave descriptor (when the source is\n // strided) and/or a tight CPU array. `eager` de-strides strided sources up-front\n // (TANGENT/UV2/COLOR feed device-lost recovery); lazy ones leave `_tight` null and\n // are de-strided on demand. The caller assigns `vb.<attr>` with a STATIC property\n // name (never a computed `vb[key]`) — a computed write would stay an unmangled\n // literal while every reader uses the mangled static name, corrupting the object\n // across the dynamic-import chunk boundary.\n const resolveOne = (name: string, eager: boolean): { _tight: Float32Array | null; _il?: AccessorInterleave; _count: number } => {\n const idx = attrs[name];\n if (idx === undefined) {\n return { _tight: null, _count: 0 };\n }\n if (accessorIsStrided(json, idx)) {\n const il = resolveStrided(json, binChunk, idx);\n return { _tight: eager ? destrideToTight(il) : null, _il: il, _count: il._count };\n }\n const av = resolveAccessor(json, binChunk, idx);\n return { _tight: av._data as Float32Array, _count: av._count };\n };\n\n const pos = resolveOne(\"POSITION\", false);\n vb._p = pos._il;\n vertexCount = pos._count;\n const nrm = resolveOne(\"NORMAL\", false);\n vb._n = nrm._il;\n // A normalized UNSIGNED_BYTE/SHORT TEXCOORD_0 is materialized as a tight float32x2 [0,1] buffer\n // (never bound strided), so integer UVs don't misalign against the float32x2 vertex layout.\n const uvIdx = attrs[\"TEXCOORD_0\"];\n const uv: { _tight: Float32Array | null; _il?: AccessorInterleave; _count: number } =\n uvIdx !== undefined && json.accessors[uvIdx].componentType !== FLOAT\n ? { _tight: (await import(\"./gltf-uv-denorm.js\")).resolveUvVec2(json, binChunk, uvIdx), _count: json.accessors[uvIdx].count }\n : resolveOne(\"TEXCOORD_0\", false);\n vb._u = uv._il;\n const tan = resolveOne(\"TANGENT\", true);\n vb._t = tan._il;\n const uv2 = resolveOne(\"TEXCOORD_1\", true);\n vb._u2 = uv2._il;\n // COLOR_0 is always materialized as a tight float32x4 [0,1] buffer (see\n // resolveColorVec4) — never bound strided — so ubyte/ushort/VEC3 sources don't\n // misalign against the pipeline's float32x4 vertex-color layout.\n const colorIdx = attrs[\"COLOR_0\"];\n const colors = colorIdx !== undefined ? resolveColorVec4(json, binChunk, colorIdx) : null;\n\n const positions = pos._tight;\n let normals = nrm._tight;\n let uvs = uv._tight;\n const tangents = tan._tight;\n const uv2s = uv2._tight;\n\n const idxData = primitive.indices !== undefined ? resolveAccessor(json, binChunk, primitive.indices) : null;\n const indices = idxData\n ? idxData._data instanceof U32\n ? new U32(idxData._data)\n : idxData._data instanceof U8\n ? Uint16Array.from(idxData._data)\n : new U16(idxData._data.buffer, idxData._data.byteOffset, idxData._count)\n : createSequentialIndices(vertexCount);\n\n // Absent (not merely strided) NORMAL: generate smooth normals to match the core\n // loader's tight path. A zero-filled normal buffer makes every lit fragment's\n // worldNormal NaN/black — e.g. a material-less skinned mesh whose interleaved\n // JOINTS/WEIGHTS route it here (SimpleSkin). computeSmoothNormals is statically\n // imported by this lazy interleave chunk, so non-interleaved scenes (which never\n // load this module) pay zero bundle cost for it.\n if (!normals && !vb._n) {\n const tightPos = positions ?? (vb._p ? destrideToTight(vb._p) : new F32(vertexCount * 3));\n normals = computeSmoothNormals(tightPos, indices, vertexCount);\n }\n if (!uvs && !vb._u) {\n uvs = new F32(vertexCount * 2);\n }\n\n // No NORMAL attribute (neither tight nor strided) → flat-shade per the glTF spec.\n const flatNormal = !nrm._tight && !vb._n;\n\n return {\n _positions: positions,\n _normals: normals,\n _tangents: tangents,\n _uvs: uvs,\n _uv2s: uv2s,\n _colors: colors,\n _flatNormal: flatNormal,\n _indices: indices,\n _vertexCount: vertexCount,\n _indexCount: indices.length,\n _worldMatrix: worldMatrix,\n _vb: vb,\n _nodeIndex: nodeIdx,\n _primitive: primitive,\n };\n}\n\n/** Build the GPU geometry for an interleaved mesh: one shared buffer per\n * bufferView for strided attributes (bound at offset 0; the per-attribute byte\n * offset goes into the pipeline vertex layout, matching stock Babylon.js), tight\n * attributes get their own buffer — byte-identical to non-interleaved meshes.\n * The raw `_slice` is intentionally retained on `_vb` so the CPU copy can be\n * de-strided lazily later (see {@link installLazyCpu}). */\nfunction buildInterleavedGpu(engine: EngineContext, m: GltfMeshData): MeshGPU {\n const vbsrc = m._vb!;\n const shared = new Map<number, GPUBuffer>();\n const vbuf = (a: AccessorInterleave | undefined, tight: Float32Array | null): GPUBuffer | null => {\n if (!a) {\n return tight ? createMappedBuffer(engine, tight, BU.VERTEX) : null;\n }\n let b = shared.get(a._bufferView);\n if (!b) {\n shared.set(a._bufferView, (b = createMappedBuffer(engine, a._slice!, BU.VERTEX)));\n }\n return b;\n };\n // Cache key encodes both stride AND byte offset per attribute: the offsets are\n // now baked into the pipeline vertex layout (attributes[].offset), so two meshes\n // with identical strides but different offsets need distinct pipelines.\n const k = (a: AccessorInterleave | undefined) => `${a?._stride ?? 0},${a?._offset ?? 0}`;\n return {\n positionBuffer: vbuf(vbsrc._p, m._positions)!,\n normalBuffer: vbuf(vbsrc._n, m._normals)!,\n tangentBuffer: m._tangents ? vbuf(vbsrc._t, m._tangents) : null,\n uvBuffer: vbuf(vbsrc._u, m._uvs)!,\n uv2Buffer: m._uv2s ? vbuf(vbsrc._u2, m._uv2s) : null,\n colorBuffer: m._colors ? vbuf(vbsrc._c, m._colors) : null,\n indexBuffer: createMappedBuffer(engine, m._indices, BU.INDEX),\n indexCount: m._indexCount,\n indexFormat: (m._indices instanceof U32 ? \"uint32\" : \"uint16\") as GPUIndexFormat,\n _vbLayout: vbsrc,\n _vbKey: `vb${k(vbsrc._p)}.${k(vbsrc._n)}.${k(vbsrc._t)}.${k(vbsrc._u)}`,\n };\n}\n\n/** Build a complete engine mesh from interleaved glTF mesh-data. Owns ALL\n * interleave-specific work (GPU upload, AABB fold, lazy CPU install, device-lost\n * retention) so the core loader's tight path stays byte-identical to the\n * non-interleaved engine — keeping interleave bytes out of every glTF scene that\n * doesn't use it. */\nexport function buildInterleavedMesh(engine: EngineContext, m: GltfMeshData, index: number, material: PbrMaterialProps, name?: string): Mesh {\n const gpu = buildInterleavedGpu(engine, m);\n\n // AABB: fold strided positions straight from the slice; tight positions normally.\n const [boundMin, boundMax] = m._vb!._p ? computeAabbStrided(m._vb!._p, m._worldMatrix) : computeAabb(m._positions!, m._worldMatrix);\n\n const mesh = {\n name: name || `gltf_mesh_${index}`,\n material,\n receiveShadows: false,\n boundMin,\n boundMax,\n skeleton: null,\n morphTargets: null,\n _gpu: gpu,\n _flatNormal: m._flatNormal,\n } as unknown as Mesh;\n initMeshTransform(mesh);\n\n // Lazy CPU geometry: the de-strided tight copy is built only on first read.\n installLazyCpu(mesh, m);\n mesh._cpuIndices = m._indices instanceof U32 ? m._indices : new U32(m._indices);\n engine._dlr?.m(mesh, m._uv2s, m._tangents, m._colors, m._indices, gpu.indexFormat);\n\n return mesh as Mesh;\n}\n\n/** Fold an AABB directly over an interleaved (strided) FLOAT vec3 position\n * source — no tight copy is materialized. Mirrors {@link computeAabb}'s\n * world-transform handling. All current interleaved assets use FLOAT positions. */\nexport function computeAabbStrided(il: AccessorInterleave, world?: Mat4): Aabb {\n const dv = new DV(il._slice!.buffer, il._slice!.byteOffset, il._slice!.byteLength);\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n for (let v = 0; v < il._count; v++) {\n const base = il._offset + v * il._stride;\n const lx = dv.getFloat32(base, true);\n const ly = dv.getFloat32(base + 4, true);\n const lz = dv.getFloat32(base + 8, true);\n let x = lx,\n y = ly,\n z = lz;\n if (world) {\n x = world[0]! * lx + world[4]! * ly + world[8]! * lz + world[12]!;\n y = world[1]! * lx + world[5]! * ly + world[9]! * lz + world[13]!;\n z = world[2]! * lx + world[6]! * ly + world[10]! * lz + world[14]!;\n }\n if (x < minX) {\n minX = x;\n }\n if (x > maxX) {\n maxX = x;\n }\n if (y < minY) {\n minY = y;\n }\n if (y > maxY) {\n maxY = y;\n }\n if (z < minZ) {\n minZ = z;\n }\n if (z > maxZ) {\n maxZ = z;\n }\n }\n return [\n [minX, minY, minZ],\n [maxX, maxY, maxZ],\n ];\n}\n\n/** Install lazy CPU-geometry accessors on an interleaved mesh. Each of\n * `_cpuPositions/_cpuNormals/_cpuUvs` that comes from a strided source is\n * defined as a getter that de-strides a tight copy on first access and caches\n * it; tight attributes are assigned directly. A mesh that is never picked /\n * CSG'd / navigated never materializes the de-strided arrays.\n *\n * The property names are written as STATIC literals (not a computed key) so the\n * minifier mangles them identically to the static reads in the picking /\n * device-lost code — a computed `defineProperty(mesh, key)` would leave the name\n * an unmangled literal and mismatch those reads across the chunk boundary. */\nexport function installLazyCpu(mesh: any, m: GltfMeshData): void {\n const vb = m._vb!;\n if (vb._p) {\n Object.defineProperty(mesh, \"_cpuPositions\", lazyCpuDesc(vb._p));\n } else if (m._positions) {\n mesh._cpuPositions = m._positions;\n }\n if (vb._n) {\n Object.defineProperty(mesh, \"_cpuNormals\", lazyCpuDesc(vb._n));\n } else if (m._normals) {\n mesh._cpuNormals = m._normals;\n }\n if (vb._u) {\n Object.defineProperty(mesh, \"_cpuUvs\", lazyCpuDesc(vb._u));\n } else if (m._uvs) {\n mesh._cpuUvs = m._uvs;\n }\n}\n\n/** Build a caching lazy-getter descriptor that de-strides `il` on first read. */\nfunction lazyCpuDesc(il: AccessorInterleave): PropertyDescriptor {\n let cached: Float32Array | undefined;\n return {\n configurable: true,\n enumerable: true,\n get(): Float32Array {\n return (cached ??= destrideToTight(il));\n },\n set(v: Float32Array): void {\n cached = v;\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ let _lightsByJson = null;
2
+ function map() {
3
+ return _lightsByJson ??= /* @__PURE__ */ new WeakMap();
4
+ }
5
+ function setGltfPunctualLight(json, lightIndex, light) {
6
+ let lights = map().get(json);
7
+ if (!lights) {
8
+ lights = [];
9
+ map().set(json, lights);
10
+ }
11
+ lights[lightIndex] = light;
12
+ }
13
+ function getGltfPunctualLight(json, lightIndex) {
14
+ return json ? _lightsByJson?.get(json)?.[lightIndex] : void 0;
15
+ }
16
+
17
+ export { getGltfPunctualLight, setGltfPunctualLight };
18
+ //# sourceMappingURL=gltf-light-pointer-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gltf-light-pointer-state.js","sources":["../../../src/loader-gltf/gltf-light-pointer-state.ts"],"sourcesContent":["import type { LightBase } from \"../light/types.js\";\n\nlet _lightsByJson: WeakMap<object, (LightBase | undefined)[]> | null = null;\n\nfunction map(): WeakMap<object, (LightBase | undefined)[]> {\n return (_lightsByJson ??= new WeakMap());\n}\n\nexport function setGltfPunctualLight(json: object, lightIndex: number, light: LightBase): void {\n let lights = map().get(json);\n if (!lights) {\n lights = [];\n map().set(json, lights);\n }\n lights[lightIndex] = light;\n}\n\nexport function getGltfPunctualLight(json: object | undefined, lightIndex: number): LightBase | undefined {\n return json ? _lightsByJson?.get(json)?.[lightIndex] : undefined;\n}\n"],"names":[],"mappings":"AAEA,IAAI,aAAA,GAAmE,IAAA;AAEvE,SAAS,GAAA,GAAkD;AACvD,EAAA,OAAQ,aAAA,yBAAsB,OAAA,EAAQ;AAC1C;AAEO,SAAS,oBAAA,CAAqB,IAAA,EAAc,UAAA,EAAoB,KAAA,EAAwB;AAC3F,EAAA,IAAI,MAAA,GAAS,GAAA,EAAI,CAAE,GAAA,CAAI,IAAI,CAAA;AAC3B,EAAA,IAAI,CAAC,MAAA,EAAQ;AACT,IAAA,MAAA,GAAS,EAAC;AACV,IAAA,GAAA,EAAI,CAAE,GAAA,CAAI,IAAA,EAAM,MAAM,CAAA;AAAA,EAC1B;AACA,EAAA,MAAA,CAAO,UAAU,CAAA,GAAI,KAAA;AACzB;AAEO,SAAS,oBAAA,CAAqB,MAA0B,UAAA,EAA2C;AACtG,EAAA,OAAO,OAAO,aAAA,EAAe,GAAA,CAAI,IAAI,CAAA,GAAI,UAAU,CAAA,GAAI,MAAA;AAC3D;;;;"}
@@ -1,4 +1,4 @@
1
- import { F32, U8, U32, U16 } from '../engine/typed-arrays.js';
1
+ import { F32, I8, I16, U8, U32, U16 } from '../engine/typed-arrays.js';
2
2
  import { mat4ComposeInto } from '../math/mat4-compose-into.js';
3
3
  import { mat4MultiplyInto } from '../math/mat4-multiply-into.js';
4
4
  import { getLoaderTmpLocal } from './_loader-scratch.js';
@@ -35,6 +35,12 @@ function resolveAccessor(json, binChunk, accessorIdx) {
35
35
  case UNSIGNED_BYTE:
36
36
  Ctor = U8;
37
37
  break;
38
+ case 5122:
39
+ Ctor = I16;
40
+ break;
41
+ case 5120:
42
+ Ctor = I8;
43
+ break;
38
44
  default:
39
45
  throw new Error(`Unsupported component type: ${accessor.componentType}`);
40
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-parser.js","sources":["../../../src/loader-gltf/gltf-parser.ts"],"sourcesContent":["/**\n * Low-level glTF/GLB parsing helpers:\n * - Accessor resolution (buffer views → typed arrays)\n * - Image extraction (embedded or external)\n * - Node hierarchy traversal with memoized world-matrix computation\n */\nimport { F32, U32, U16, U8 } from \"../engine/typed-arrays.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport { mat4ComposeInto } from \"../math/mat4-compose-into.js\";\nimport { mat4MultiplyInto } from \"../math/mat4-multiply-into.js\";\nimport type { Mat4Storage } from \"../math/types.js\";\nimport { getLoaderTmpLocal } from \"./_loader-scratch.js\";\n\n// glTF 2.0 component types\nconst FLOAT = 5126;\nconst UNSIGNED_SHORT = 5123;\nconst UNSIGNED_INT = 5125;\nconst UNSIGNED_BYTE = 5121;\n\n// glTF accessor type → component count\nexport const TYPE_SIZES: Record<string, number> = {\n SCALAR: 1,\n VEC2: 2,\n VEC3: 3,\n VEC4: 4,\n MAT2: 4,\n MAT3: 9,\n MAT4: 16,\n};\n\n// --- Accessor Resolution ---\n\nexport interface AccessorView {\n /** @internal */\n _data: ArrayBufferView;\n /** @internal */\n _count: number;\n /** @internal */\n _componentCount: number;\n}\n\nexport function resolveAccessor(json: any, binChunk: DataView, accessorIdx: number): AccessorView {\n const accessor = json.accessors[accessorIdx];\n const componentCount = TYPE_SIZES[accessor.type] ?? 1;\n const count = accessor.count;\n const len = count * componentCount;\n\n let Ctor: Float32ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | Uint8ArrayConstructor;\n switch (accessor.componentType) {\n case FLOAT:\n Ctor = F32;\n break;\n case UNSIGNED_SHORT:\n Ctor = U16;\n break;\n case UNSIGNED_INT:\n Ctor = U32;\n break;\n case UNSIGNED_BYTE:\n Ctor = U8;\n break;\n default:\n throw new Error(`Unsupported component type: ${accessor.componentType}`);\n }\n\n // Spec: an accessor with no `bufferView` is zero-initialized (its values may be supplied by a `sparse`\n // substitution or an extension) — return a zero-filled array instead of dereferencing a missing\n // bufferView. Some skinned rigs ship all-zero morph-target POSITION/NORMAL accessors this way, which\n // otherwise crashed the morph feature with `undefined.byteOffset`.\n const data =\n accessor.bufferView === undefined\n ? new Ctor(len)\n : new Ctor(binChunk.buffer as ArrayBuffer, binChunk.byteOffset + (json.bufferViews[accessor.bufferView].byteOffset ?? 0) + (accessor.byteOffset ?? 0), len);\n\n return { _data: data, _count: count, _componentCount: componentCount };\n}\n\n// --- Image Extraction ---\n\n/** Resolve the image index for a glTF texture, honoring alternate-source\n * extensions such as EXT_texture_webp (WebP decode is native in\n * createImageBitmap, so no extra module is required — we only need to\n * pick the correct image source). */\nexport function getTextureImageIndex(tex: any): number {\n return tex.extensions?.EXT_texture_webp?.source ?? tex.source;\n}\n\n// --- Optional-feature detection (shared by the core loader gate and the\n// dynamically-imported feature registry) ---\n\n/** Returns true if any mesh primitive in the asset matches `pred`. */\nexport function anyPrimitive(json: any, pred: (p: any) => boolean): boolean {\n for (const m of json.meshes ?? []) {\n for (const p of m.primitives ?? []) {\n if (pred(p)) {\n return true;\n }\n }\n }\n return false;\n}\n\n/** Asset has at least one material that needs ORM compositing\n * (separate metallicRoughnessTexture + occlusionTexture pointing at different images). */\nexport function needsOrmComposite(json: any): boolean {\n const mats = json.materials ?? [];\n const textures = json.textures ?? [];\n for (const m of mats) {\n const mr = m.pbrMetallicRoughness?.metallicRoughnessTexture;\n const occ = m.occlusionTexture;\n if (mr && occ && textures[mr.index] && textures[occ.index] && getTextureImageIndex(textures[mr.index]) !== getTextureImageIndex(textures[occ.index])) {\n return true;\n }\n }\n return false;\n}\n\nexport async function resolveImage(json: any, binChunk: DataView, imageIdx: number, baseUrl: string): Promise<ImageBitmap> {\n const image = json.images[imageIdx];\n\n if (image.bufferView !== undefined) {\n // Embedded in binary chunk (GLB)\n const bv = json.bufferViews[image.bufferView];\n const offset = binChunk.byteOffset + (bv.byteOffset ?? 0);\n const slice = binChunk.buffer.slice(offset, offset + bv.byteLength);\n const blob = new Blob([slice as ArrayBuffer], { type: image.mimeType ?? \"image/png\" });\n return createImageBitmap(blob, { premultiplyAlpha: \"none\", colorSpaceConversion: \"none\" });\n }\n\n if (image.uri) {\n // External URI (relative to .gltf base URL)\n const imageUrl = new URL(image.uri, baseUrl + \"x\").href;\n const response = await fetch(imageUrl);\n if (!response.ok) {\n throw new Error(`Failed to load image: ${response.status} ${response.statusText}`);\n }\n const blob = await response.blob();\n const bmp = await createImageBitmap(blob, { premultiplyAlpha: \"none\", colorSpaceConversion: \"none\" });\n return bmp;\n }\n\n throw new Error(\"Image has neither bufferView nor uri\");\n}\n\n// --- Node Hierarchy → World Matrix (Memoized) ---\n\n// Babylon.js RH→LH root: rotation [0,1,0,0] + scale [1,1,-1] = diag(-1,1,1,1)\n// prettier-ignore\nconst RH_TO_LH_ROOT = new F32([-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) as unknown as Mat4;\n\n/** Build a parent index map by scanning node.children arrays once. O(n). */\nexport function buildParentMap(json: any): Map<number, number> {\n const parentMap = new Map<number, number>();\n const nodes = json.nodes ?? [];\n for (let i = 0; i < nodes.length; i++) {\n const children = nodes[i].children;\n if (children) {\n for (const childIdx of children) {\n parentMap.set(childIdx as number, i);\n }\n }\n }\n return parentMap;\n}\n\n/** Look up a node's parent using the pre-built parent map. O(1). */\nexport function findParent(parentMap: Map<number, number>, childIdx: number): number {\n return parentMap.get(childIdx) ?? -1;\n}\n\n/**\n * Compute world matrix for a glTF node with memoization.\n * Uses parentMap for O(1) parent lookup and cache for O(1) repeat queries.\n * Total cost across all nodes: O(n) instead of O(n²).\n *\n * Zero-alloc path for the TRS case (common): `local` is composed into a shared\n * scratch via `mat4ComposeInto`, then multiplied into the freshly-allocated\n * `world` via `mat4MultiplyInto`. Only one Float32Array allocation per node\n * (the cached world matrix itself) instead of two. Recursion always resolves\n * `parentWorld` before touching the scratch, so the shared buffer is safe.\n */\nexport function computeNodeWorldMatrix(json: any, nodeIdx: number, parentMap: Map<number, number>, cache: Map<number, Mat4>): Mat4 {\n const cached = cache.get(nodeIdx);\n if (cached) {\n return cached;\n }\n\n const node = json.nodes[nodeIdx];\n const parentIdx = findParent(parentMap, nodeIdx);\n // Resolve parent FIRST so any recursive call can safely reuse the shared scratch below.\n const parentWorld: Mat4 = parentIdx !== -1 ? computeNodeWorldMatrix(json, parentIdx, parentMap, cache) : RH_TO_LH_ROOT;\n\n let localBuf: import(\"../math/types.js\").Mat4Storage;\n if (node.matrix) {\n // Pre-built matrix — copy into a fresh Float32Array (cannot alias scratch safely across calls).\n localBuf = new F32(node.matrix);\n } else {\n const t = node.translation ?? [0, 0, 0];\n const r = node.rotation ?? [0, 0, 0, 1];\n const s = node.scale ?? [1, 1, 1];\n const local = getLoaderTmpLocal() as unknown as Mat4Storage;\n mat4ComposeInto(local, 0, t[0], t[1], t[2], r[0], r[1], r[2], r[3], s[0], s[1], s[2]);\n localBuf = local;\n }\n\n // Per-node world is allocated fresh because recursion mutates the parser's\n // scratch; it cannot alias the per-call `tmpLocal`. The result is then\n // stashed in the per-load `cache` (Map<nodeIdx, Mat4>) and never shared\n // across loadGltf calls. Allocates F32 directly here — these loader-local\n // world matrices are throwaway intermediaries used only during parsing;\n // mesh runtime world-matrix caches are separately allocated via\n // `allocateMat4()` in `initMeshTransform` and pick up whatever precision\n // the process-global allocator was set to.\n const world = new F32(16) as unknown as Mat4;\n mat4MultiplyInto(world as unknown as Mat4Storage, 0, parentWorld as unknown as Mat4Storage, 0, localBuf, 0);\n\n cache.set(nodeIdx, world);\n return world;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-parser.js","sources":["../../../src/loader-gltf/gltf-parser.ts"],"sourcesContent":["/**\n * Low-level glTF/GLB parsing helpers:\n * - Accessor resolution (buffer views → typed arrays)\n * - Image extraction (embedded or external)\n * - Node hierarchy traversal with memoized world-matrix computation\n */\nimport { F32, U32, U16, U8, I16, I8 } from \"../engine/typed-arrays.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport { mat4ComposeInto } from \"../math/mat4-compose-into.js\";\nimport { mat4MultiplyInto } from \"../math/mat4-multiply-into.js\";\nimport type { Mat4Storage } from \"../math/types.js\";\nimport { getLoaderTmpLocal } from \"./_loader-scratch.js\";\n\n// glTF 2.0 component types\nconst FLOAT = 5126;\nconst UNSIGNED_SHORT = 5123;\nconst UNSIGNED_INT = 5125;\nconst UNSIGNED_BYTE = 5121;\n\n// glTF accessor type → component count\nexport const TYPE_SIZES: Record<string, number> = {\n SCALAR: 1,\n VEC2: 2,\n VEC3: 3,\n VEC4: 4,\n MAT2: 4,\n MAT3: 9,\n MAT4: 16,\n};\n\n// --- Accessor Resolution ---\n\nexport interface AccessorView {\n /** @internal */\n _data: ArrayBufferView;\n /** @internal */\n _count: number;\n /** @internal */\n _componentCount: number;\n}\n\nexport function resolveAccessor(json: any, binChunk: DataView, accessorIdx: number): AccessorView {\n const accessor = json.accessors[accessorIdx];\n const componentCount = TYPE_SIZES[accessor.type] ?? 1;\n const count = accessor.count;\n const len = count * componentCount;\n\n let Ctor: Float32ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | Uint8ArrayConstructor | Int16ArrayConstructor | Int8ArrayConstructor;\n switch (accessor.componentType) {\n case FLOAT:\n Ctor = F32;\n break;\n case UNSIGNED_SHORT:\n Ctor = U16;\n break;\n case UNSIGNED_INT:\n Ctor = U32;\n break;\n case UNSIGNED_BYTE:\n Ctor = U8;\n break;\n case 5122: // SHORT\n Ctor = I16;\n break;\n case 5120: // BYTE\n Ctor = I8;\n break;\n default:\n throw new Error(`Unsupported component type: ${accessor.componentType}`);\n }\n\n // Spec: an accessor with no `bufferView` is zero-initialized (its values may be supplied by a `sparse`\n // substitution or an extension) — return a zero-filled array instead of dereferencing a missing\n // bufferView. Some skinned rigs ship all-zero morph-target POSITION/NORMAL accessors this way, which\n // otherwise crashed the morph feature with `undefined.byteOffset`.\n const data =\n accessor.bufferView === undefined\n ? new Ctor(len)\n : new Ctor(binChunk.buffer as ArrayBuffer, binChunk.byteOffset + (json.bufferViews[accessor.bufferView].byteOffset ?? 0) + (accessor.byteOffset ?? 0), len);\n\n return { _data: data, _count: count, _componentCount: componentCount };\n}\n\n// --- Image Extraction ---\n\n/** Resolve the image index for a glTF texture, honoring alternate-source\n * extensions such as EXT_texture_webp (WebP decode is native in\n * createImageBitmap, so no extra module is required — we only need to\n * pick the correct image source). */\nexport function getTextureImageIndex(tex: any): number {\n return tex.extensions?.EXT_texture_webp?.source ?? tex.source;\n}\n\n// --- Optional-feature detection (shared by the core loader gate and the\n// dynamically-imported feature registry) ---\n\n/** Returns true if any mesh primitive in the asset matches `pred`. */\nexport function anyPrimitive(json: any, pred: (p: any) => boolean): boolean {\n for (const m of json.meshes ?? []) {\n for (const p of m.primitives ?? []) {\n if (pred(p)) {\n return true;\n }\n }\n }\n return false;\n}\n\n/** Asset has at least one material that needs ORM compositing\n * (separate metallicRoughnessTexture + occlusionTexture pointing at different images). */\nexport function needsOrmComposite(json: any): boolean {\n const mats = json.materials ?? [];\n const textures = json.textures ?? [];\n for (const m of mats) {\n const mr = m.pbrMetallicRoughness?.metallicRoughnessTexture;\n const occ = m.occlusionTexture;\n if (mr && occ && textures[mr.index] && textures[occ.index] && getTextureImageIndex(textures[mr.index]) !== getTextureImageIndex(textures[occ.index])) {\n return true;\n }\n }\n return false;\n}\n\nexport async function resolveImage(json: any, binChunk: DataView, imageIdx: number, baseUrl: string): Promise<ImageBitmap> {\n const image = json.images[imageIdx];\n\n if (image.bufferView !== undefined) {\n // Embedded in binary chunk (GLB)\n const bv = json.bufferViews[image.bufferView];\n const offset = binChunk.byteOffset + (bv.byteOffset ?? 0);\n const slice = binChunk.buffer.slice(offset, offset + bv.byteLength);\n const blob = new Blob([slice as ArrayBuffer], { type: image.mimeType ?? \"image/png\" });\n return createImageBitmap(blob, { premultiplyAlpha: \"none\", colorSpaceConversion: \"none\" });\n }\n\n if (image.uri) {\n // External URI (relative to .gltf base URL)\n const imageUrl = new URL(image.uri, baseUrl + \"x\").href;\n const response = await fetch(imageUrl);\n if (!response.ok) {\n throw new Error(`Failed to load image: ${response.status} ${response.statusText}`);\n }\n const blob = await response.blob();\n const bmp = await createImageBitmap(blob, { premultiplyAlpha: \"none\", colorSpaceConversion: \"none\" });\n return bmp;\n }\n\n throw new Error(\"Image has neither bufferView nor uri\");\n}\n\n// --- Node Hierarchy → World Matrix (Memoized) ---\n\n// Babylon.js RH→LH root: rotation [0,1,0,0] + scale [1,1,-1] = diag(-1,1,1,1)\n// prettier-ignore\nconst RH_TO_LH_ROOT = new F32([-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) as unknown as Mat4;\n\n/** Build a parent index map by scanning node.children arrays once. O(n). */\nexport function buildParentMap(json: any): Map<number, number> {\n const parentMap = new Map<number, number>();\n const nodes = json.nodes ?? [];\n for (let i = 0; i < nodes.length; i++) {\n const children = nodes[i].children;\n if (children) {\n for (const childIdx of children) {\n parentMap.set(childIdx as number, i);\n }\n }\n }\n return parentMap;\n}\n\n/** Look up a node's parent using the pre-built parent map. O(1). */\nexport function findParent(parentMap: Map<number, number>, childIdx: number): number {\n return parentMap.get(childIdx) ?? -1;\n}\n\n/**\n * Compute world matrix for a glTF node with memoization.\n * Uses parentMap for O(1) parent lookup and cache for O(1) repeat queries.\n * Total cost across all nodes: O(n) instead of O(n²).\n *\n * Zero-alloc path for the TRS case (common): `local` is composed into a shared\n * scratch via `mat4ComposeInto`, then multiplied into the freshly-allocated\n * `world` via `mat4MultiplyInto`. Only one Float32Array allocation per node\n * (the cached world matrix itself) instead of two. Recursion always resolves\n * `parentWorld` before touching the scratch, so the shared buffer is safe.\n */\nexport function computeNodeWorldMatrix(json: any, nodeIdx: number, parentMap: Map<number, number>, cache: Map<number, Mat4>): Mat4 {\n const cached = cache.get(nodeIdx);\n if (cached) {\n return cached;\n }\n\n const node = json.nodes[nodeIdx];\n const parentIdx = findParent(parentMap, nodeIdx);\n // Resolve parent FIRST so any recursive call can safely reuse the shared scratch below.\n const parentWorld: Mat4 = parentIdx !== -1 ? computeNodeWorldMatrix(json, parentIdx, parentMap, cache) : RH_TO_LH_ROOT;\n\n let localBuf: import(\"../math/types.js\").Mat4Storage;\n if (node.matrix) {\n // Pre-built matrix — copy into a fresh Float32Array (cannot alias scratch safely across calls).\n localBuf = new F32(node.matrix);\n } else {\n const t = node.translation ?? [0, 0, 0];\n const r = node.rotation ?? [0, 0, 0, 1];\n const s = node.scale ?? [1, 1, 1];\n const local = getLoaderTmpLocal() as unknown as Mat4Storage;\n mat4ComposeInto(local, 0, t[0], t[1], t[2], r[0], r[1], r[2], r[3], s[0], s[1], s[2]);\n localBuf = local;\n }\n\n // Per-node world is allocated fresh because recursion mutates the parser's\n // scratch; it cannot alias the per-call `tmpLocal`. The result is then\n // stashed in the per-load `cache` (Map<nodeIdx, Mat4>) and never shared\n // across loadGltf calls. Allocates F32 directly here — these loader-local\n // world matrices are throwaway intermediaries used only during parsing;\n // mesh runtime world-matrix caches are separately allocated via\n // `allocateMat4()` in `initMeshTransform` and pick up whatever precision\n // the process-global allocator was set to.\n const world = new F32(16) as unknown as Mat4;\n mat4MultiplyInto(world as unknown as Mat4Storage, 0, parentWorld as unknown as Mat4Storage, 0, localBuf, 0);\n\n cache.set(nodeIdx, world);\n return world;\n}\n"],"names":[],"mappings":"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"}
@@ -88,7 +88,7 @@ function buildDefaultPbrTexturesExt(engine, mat, sampler, generateMipmaps, getCa
88
88
  }
89
89
  function assemblePbrPropsExt(mat, tex, extLayers) {
90
90
  const ef = mat._emissiveFactor;
91
- const defaultFactor = ef[0] === 1 && ef[1] === 1 && ef[2] === 1 || ef[0] === 0 && ef[1] === 0 && ef[2] === 0;
91
+ const defaultFactor = ef[0] === 0 && ef[1] === 0 && ef[2] === 0 || !!tex.emissiveTexture && ef[0] === 1 && ef[1] === 1 && ef[2] === 1;
92
92
  const hasAnyUvTx = !!tex.baseColorTexture._hasTx || !!tex.normalTexture?._hasTx || !!tex.ormTexture._hasTx || !!tex.emissiveTexture?._hasTx || !!tex.occlusionTexture?._hasTx;
93
93
  return {
94
94
  baseColorTexture: tex.baseColorTexture,
@@ -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 const defaultFactor = (ef[0] === 1 && ef[1] === 1 && ef[2] === 1) || (ef[0] === 0 && ef[1] === 0 && ef[2] === 0);\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 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 _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 { 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 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 _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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"}
@@ -35,7 +35,7 @@ function uploadTex(engine, bitmap, srgb, sampler, generateMipmaps, fallback) {
35
35
  }
36
36
  function assemblePbrProps(mat, baseColorTexture, ormTexture, normalTexture, emissiveTexture, extLayers) {
37
37
  const ef = mat._emissiveFactor;
38
- const defaultFactor = ef[0] === 1 && ef[1] === 1 && ef[2] === 1 || ef[0] === 0 && ef[1] === 0 && ef[2] === 0;
38
+ const defaultFactor = ef[0] === 0 && ef[1] === 0 && ef[2] === 0 || !!emissiveTexture && ef[0] === 1 && ef[1] === 1 && ef[2] === 1;
39
39
  return {
40
40
  baseColorTexture,
41
41
  normalTexture,