@babylonjs/lite 1.4.0 → 1.6.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 (152) hide show
  1. package/dist/index.js +512 -512
  2. package/dist/index.js.map +1 -1
  3. package/index.d.ts +829 -28
  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/camera/geospatial-camera-controls.js +22 -0
  41. package/lib/camera/geospatial-camera-controls.js.map +1 -1
  42. package/lib/camera/geospatial-camera-fly.js +2 -1
  43. package/lib/camera/geospatial-camera-fly.js.map +1 -1
  44. package/lib/effect/effect-renderer.js +1 -1
  45. package/lib/effect/effect-renderer.js.map +1 -1
  46. package/lib/engine/engine.js +1 -1
  47. package/lib/index.js +15 -1
  48. package/lib/index.js.map +1 -1
  49. package/lib/light/types.js.map +1 -1
  50. package/lib/loader-gltf/animation-pointer-basecolor.js +25 -0
  51. package/lib/loader-gltf/animation-pointer-basecolor.js.map +1 -0
  52. package/lib/loader-gltf/animation-pointer-ext.js +244 -0
  53. package/lib/loader-gltf/animation-pointer-ext.js.map +1 -0
  54. package/lib/loader-gltf/animation-pointer-lights.js +46 -0
  55. package/lib/loader-gltf/animation-pointer-lights.js.map +1 -0
  56. package/lib/loader-gltf/animation-pointer.js +4 -1
  57. package/lib/loader-gltf/animation-pointer.js.map +1 -1
  58. package/lib/loader-gltf/gltf-animation.js +5 -3
  59. package/lib/loader-gltf/gltf-animation.js.map +1 -1
  60. package/lib/loader-gltf/gltf-color-normalize.js +10 -1
  61. package/lib/loader-gltf/gltf-color-normalize.js.map +1 -1
  62. package/lib/loader-gltf/gltf-feature-animation-pointer.js +67 -47
  63. package/lib/loader-gltf/gltf-feature-animation-pointer.js.map +1 -1
  64. package/lib/loader-gltf/gltf-feature-lights-punctual.js +51 -9
  65. package/lib/loader-gltf/gltf-feature-lights-punctual.js.map +1 -1
  66. package/lib/loader-gltf/gltf-feature-primitive.js +20 -0
  67. package/lib/loader-gltf/gltf-feature-primitive.js.map +1 -0
  68. package/lib/loader-gltf/gltf-feature-registry.js +25 -0
  69. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  70. package/lib/loader-gltf/gltf-feature-skeleton.js +18 -3
  71. package/lib/loader-gltf/gltf-feature-skeleton.js.map +1 -1
  72. package/lib/loader-gltf/gltf-interleave.js +3 -2
  73. package/lib/loader-gltf/gltf-interleave.js.map +1 -1
  74. package/lib/loader-gltf/gltf-light-pointer-state.js +18 -0
  75. package/lib/loader-gltf/gltf-light-pointer-state.js.map +1 -0
  76. package/lib/loader-gltf/gltf-parser.js +7 -1
  77. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  78. package/lib/loader-gltf/gltf-pbr-builder-ext.js +1 -1
  79. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  80. package/lib/loader-gltf/gltf-pbr-builder.js +1 -1
  81. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  82. package/lib/loader-gltf/gltf-sampler-denorm.js +20 -0
  83. package/lib/loader-gltf/gltf-sampler-denorm.js.map +1 -0
  84. package/lib/loader-gltf/gltf-sampler-desc.js +11 -2
  85. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  86. package/lib/loader-gltf/gltf-uv-denorm.js +28 -0
  87. package/lib/loader-gltf/gltf-uv-denorm.js.map +1 -0
  88. package/lib/loader-gltf/load-gltf.js +15 -6
  89. package/lib/loader-gltf/load-gltf.js.map +1 -1
  90. package/lib/material/material-rebuild.js +4 -0
  91. package/lib/material/material-rebuild.js.map +1 -1
  92. package/lib/material/mesh-features.js +8 -1
  93. package/lib/material/mesh-features.js.map +1 -1
  94. package/lib/material/pbr/fragments/reflectance-fragment.js +1 -1
  95. package/lib/material/pbr/fragments/reflectance-fragment.js.map +1 -1
  96. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +1 -1
  97. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  98. package/lib/material/pbr/pbr-pipeline.js +7 -3
  99. package/lib/material/pbr/pbr-pipeline.js.map +1 -1
  100. package/lib/material/pbr/pbr-primitive-resolver.js +34 -0
  101. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -0
  102. package/lib/material/pbr/pbr-renderable.js +1 -1
  103. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  104. package/lib/material/shader/shader-material.js +9 -5
  105. package/lib/material/shader/shader-material.js.map +1 -1
  106. package/lib/material/shader/shader-thin-instance.js +1 -1
  107. package/lib/material/shader/shader-thin-instance.js.map +1 -1
  108. package/lib/material/standard/standard-renderable.js +1 -1
  109. package/lib/material/standard/standard-renderable.js.map +1 -1
  110. package/lib/mesh/mesh-dispose.js +1 -0
  111. package/lib/mesh/mesh-dispose.js.map +1 -1
  112. package/lib/mesh/thin-instance-cull-binding.js +15 -6
  113. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  114. package/lib/post-process/taa.js +193 -0
  115. package/lib/post-process/taa.js.map +1 -0
  116. package/lib/scene/scene-core.js +1 -0
  117. package/lib/scene/scene-core.js.map +1 -1
  118. package/lib/scene/scene-material-swap.js +2 -0
  119. package/lib/scene/scene-material-swap.js.map +1 -1
  120. package/lib/shadow/csm-shadow-task-hooks.js +67 -9
  121. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  122. package/lib/sprite/billboard-custom-shader.js +32 -32
  123. package/lib/sprite/billboard-custom-shader.js.map +1 -1
  124. package/lib/sprite/billboard-pipeline.js +54 -56
  125. package/lib/sprite/billboard-pipeline.js.map +1 -1
  126. package/lib/sprite/custom-shader-core.js +1 -1
  127. package/lib/sprite/custom-shader-core.js.map +1 -1
  128. package/lib/sprite/shared/sprite-atlas.js +2 -2
  129. package/lib/sprite/shared/sprite-atlas.js.map +1 -1
  130. package/lib/sprite/sprite-2d-coverage-gamma.js +58 -0
  131. package/lib/sprite/sprite-2d-coverage-gamma.js.map +1 -0
  132. package/lib/sprite/sprite-2d-uvscroll.js +39 -0
  133. package/lib/sprite/sprite-2d-uvscroll.js.map +1 -0
  134. package/lib/sprite/sprite-2d.js +6 -36
  135. package/lib/sprite/sprite-2d.js.map +1 -1
  136. package/lib/sprite/sprite-coverage-gamma-hook.js +10 -0
  137. package/lib/sprite/sprite-coverage-gamma-hook.js.map +1 -0
  138. package/lib/sprite/sprite-custom-shader.js +2 -2
  139. package/lib/sprite/sprite-custom-shader.js.map +1 -1
  140. package/lib/sprite/sprite-pipeline.js +61 -73
  141. package/lib/sprite/sprite-pipeline.js.map +1 -1
  142. package/lib/sprite/sprite-renderable.js +5 -5
  143. package/lib/sprite/sprite-renderable.js.map +1 -1
  144. package/lib/sprite/sprite-renderer.js +4 -4
  145. package/lib/sprite/sprite-renderer.js.map +1 -1
  146. package/lib/sprite/sprite-scene.js +1 -1
  147. package/lib/sprite/sprite-scene.js.map +1 -1
  148. package/lib/text/_gpu/text-pipeline.js +1 -1
  149. package/lib/text/_gpu/text-pipeline.js.map +1 -1
  150. package/lib/text/text-renderer.js +3 -1
  151. package/lib/text/text-renderer.js.map +1 -1
  152. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animation-pointer-lights.js","sources":["../../../src/loader-gltf/animation-pointer-lights.ts"],"sourcesContent":["/** KHR_animation_pointer — punctual-light pointer handler.\n *\n * Dynamic-imported by the animation-pointer feature ONLY when a channel targets a\n * KHR_lights_punctual light property, so scenes that don't animate lights never pay\n * for it. On import it appends its handler to the shared resolver registry. */\nimport type { LightBase } from \"../light/types.js\";\nimport { getGltfPunctualLight } from \"./gltf-light-pointer-state.js\";\nimport { _appendPointerHandlers, type PointerFactory } from \"./animation-pointer.js\";\n\nconst _lightHandlers: [RegExp, PointerFactory][] = [\n // /extensions/KHR_lights_punctual/lights/{l}/{color|intensity|range|spot/outerConeAngle}\n [\n /^\\/extensions\\/KHR_lights_punctual\\/lights\\/(\\d+)\\/(color|intensity|range|spot\\/outerConeAngle)$/,\n (m, ctx) => {\n const lightIdx = +m[1]!;\n const field = m[2]!;\n const getLight = ():\n | (LightBase & {\n diffuse?: [number, number, number];\n specular?: [number, number, number];\n intensity?: number;\n range?: number;\n angle?: number;\n })\n | null => {\n return (getGltfPunctualLight(ctx._json, lightIdx) as ReturnType<typeof getLight>) ?? null;\n };\n return {\n arity: field === \"color\" ? 3 : 1,\n writer: (out, off) => {\n const light = getLight();\n if (!light) {\n return;\n }\n if (field === \"color\") {\n // Mutate the existing diffuse/specular arrays in place — pointer\n // writers can run every frame, so avoid allocating a tuple per keyframe.\n // The UBO refresh is driven by `_bumpLightVersion()` below, not the assignment.\n if (light.diffuse) {\n light.diffuse[0] = out[off]!;\n light.diffuse[1] = out[off + 1]!;\n light.diffuse[2] = out[off + 2]!;\n }\n if (light.specular) {\n light.specular[0] = out[off]!;\n light.specular[1] = out[off + 1]!;\n light.specular[2] = out[off + 2]!;\n }\n } else if (field === \"intensity\") {\n light.intensity = out[off]!;\n } else if (field === \"range\") {\n light.range = out[off]!;\n } else if (light.lightType === \"spot\") {\n light.angle = out[off]! * 2;\n }\n light._bumpLightVersion?.();\n },\n };\n },\n ],\n];\n\n_appendPointerHandlers(_lightHandlers);\n"],"names":[],"mappings":";;;AASA,MAAM,cAAA,GAA6C;AAAA;AAAA,EAE/C;AAAA,IACI,kGAAA;AAAA,IACA,CAAC,GAAG,GAAA,KAAQ;AACR,MAAA,MAAM,QAAA,GAAW,CAAC,CAAA,CAAE,CAAC,CAAA;AACrB,MAAA,MAAM,KAAA,GAAQ,EAAE,CAAC,CAAA;AACjB,MAAA,MAAM,WAAW,MAQH;AACV,QAAA,OAAQ,oBAAA,CAAqB,GAAA,CAAI,KAAA,EAAO,QAAQ,CAAA,IAAqC,IAAA;AAAA,MACzF,CAAA;AACA,MAAA,OAAO;AAAA,QACH,KAAA,EAAO,KAAA,KAAU,OAAA,GAAU,CAAA,GAAI,CAAA;AAAA,QAC/B,MAAA,EAAQ,CAAC,GAAA,EAAK,GAAA,KAAQ;AAClB,UAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,UAAA,IAAI,CAAC,KAAA,EAAO;AACR,YAAA;AAAA,UACJ;AACA,UAAA,IAAI,UAAU,OAAA,EAAS;AAInB,YAAA,IAAI,MAAM,OAAA,EAAS;AACf,cAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,GAAI,GAAA,CAAI,GAAG,CAAA;AAC1B,cAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,GAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAC9B,cAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,GAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,YAClC;AACA,YAAA,IAAI,MAAM,QAAA,EAAU;AAChB,cAAA,KAAA,CAAM,QAAA,CAAS,CAAC,CAAA,GAAI,GAAA,CAAI,GAAG,CAAA;AAC3B,cAAA,KAAA,CAAM,QAAA,CAAS,CAAC,CAAA,GAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAC/B,cAAA,KAAA,CAAM,QAAA,CAAS,CAAC,CAAA,GAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,YACnC;AAAA,UACJ,CAAA,MAAA,IAAW,UAAU,WAAA,EAAa;AAC9B,YAAA,KAAA,CAAM,SAAA,GAAY,IAAI,GAAG,CAAA;AAAA,UAC7B,CAAA,MAAA,IAAW,UAAU,OAAA,EAAS;AAC1B,YAAA,KAAA,CAAM,KAAA,GAAQ,IAAI,GAAG,CAAA;AAAA,UACzB,CAAA,MAAA,IAAW,KAAA,CAAM,SAAA,KAAc,MAAA,EAAQ;AACnC,YAAA,KAAA,CAAM,KAAA,GAAQ,GAAA,CAAI,GAAG,CAAA,GAAK,CAAA;AAAA,UAC9B;AACA,UAAA,KAAA,CAAM,iBAAA,IAAoB;AAAA,QAC9B;AAAA,OACJ;AAAA,IACJ;AAAA;AAER,CAAA;AAEA,sBAAA,CAAuB,cAAc,CAAA"}
@@ -203,6 +203,9 @@ const _registry = [
203
203
  }
204
204
  ]
205
205
  ];
206
+ function _appendPointerHandlers(handlers) {
207
+ _registry.push(...handlers);
208
+ }
206
209
  const _warned = /* @__PURE__ */ new Set();
207
210
  function resolveAnimationPointer(pointer, ctx) {
208
211
  for (const [rx, make] of _registry) {
@@ -218,5 +221,5 @@ function resolveAnimationPointer(pointer, ctx) {
218
221
  return null;
219
222
  }
220
223
 
221
- export { resolveAnimationPointer };
224
+ export { _appendPointerHandlers, resolveAnimationPointer };
222
225
  //# sourceMappingURL=animation-pointer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"animation-pointer.js","sources":["../../../src/loader-gltf/animation-pointer.ts"],"sourcesContent":["/** KHR_animation_pointer — JSON-pointer resolver registry.\n * Handlers are registered incrementally (one per parity scene). Unknown\n * pointers return null and warn once. */\nimport type { SceneNode } from \"../scene/scene-node.js\";\nimport { setSubtreeVisible } from \"../scene/visibility.js\";\n\nexport interface ResolvedPointer {\n writer: (output: Float32Array, offset: number) => void;\n arity: number;\n}\n\n/** Minimal mutable view of a UV-transform texture slot the pointer writers drive.\n * Mirrors the fields `material/pbr/fragments/uv-transform-fragment.ts` reads. */\nexport interface PointerUvTexture {\n uScale?: number;\n vScale?: number;\n uOffset?: number;\n vOffset?: number;\n /** KHR_texture_transform rotation (radians) — drives the UV matrix's rotation. */\n uAng?: number;\n}\n\n/** Minimal mutable view of a runtime material a pointer can animate. Bumping\n * `_uboVersion` makes the renderable re-upload the material UBO next frame. */\nexport interface PointerMaterial {\n /** @internal */\n _uboVersion: number;\n baseColorTexture?: PointerUvTexture;\n emissiveTexture?: PointerUvTexture;\n normalTexture?: PointerUvTexture;\n ormTexture?: PointerUvTexture;\n /** Independent-occlusion UV carrier (orm-unpack); present only when occlusion\n * is sampled from the ORM texture with its own transform. */\n occlusionTexture?: PointerUvTexture;\n specGlossTexture?: PointerUvTexture;\n /** Runtime emissive (linear RGB) = emissiveFactor × emissiveStrength. */\n emissiveColor?: [number, number, number];\n /** Runtime base-color factor (linear RGBA). */\n baseColorFactor?: [number, number, number, number];\n /** @internal Animated glTF emissiveFactor, kept separate so an emissiveStrength\n * pointer can recombine without losing the factor (and vice-versa). */\n _animEmissiveFactor?: [number, number, number];\n /** @internal Animated KHR_materials_emissive_strength value. */\n _animEmissiveStrength?: number;\n}\n\n/** Recompute emissiveColor = factor × strength after either input animates, then\n * flag the material UBO for re-upload. */\nfunction applyEmissive(mat: PointerMaterial): void {\n if (!mat.emissiveColor) {\n return;\n }\n const f = mat._animEmissiveFactor ?? [0, 0, 0];\n const s = mat._animEmissiveStrength ?? 1;\n mat.emissiveColor[0] = f[0]! * s;\n mat.emissiveColor[1] = f[1]! * s;\n mat.emissiveColor[2] = f[2]! * s;\n mat._uboVersion++;\n}\n\nexport interface PointerContext {\n nodes: readonly (SceneNode | undefined)[];\n /** Runtime materials indexed by glTF material index (built by the pointer feature). */\n materials?: readonly (PointerMaterial | undefined)[];\n}\n\ntype PointerFactory = (match: RegExpExecArray, ctx: PointerContext) => ResolvedPointer | null;\n\n// Maps a KHR_texture_transform pointer's texture-slot segment to the material field.\n// NOTE: metallicRoughnessTexture is intentionally absent. Babylon.js has a long-standing\n// loader bug — its KHR_animation_pointer registration for the MR texture transform omits the\n// `/extensions/KHR_texture_transform/` path segment (and adds a stray leading slash), so the\n// interpolation is never attached and BJS silently skips animating the MR texture transform\n// (offset/scale/rotation stay frozen at their static load-time values). We match BJS for parity\n// (verified: animating MR regresses scene241 col1 MR rows by ~4.9 MAD vs the immutable golden,\n// which renders these spheres with static roughness/metallic). Do NOT animate the MR transform.\nconst TX_SLOT: Record<string, keyof PointerMaterial> = {\n \"pbrMetallicRoughness/baseColorTexture\": \"baseColorTexture\",\n emissiveTexture: \"emissiveTexture\",\n normalTexture: \"normalTexture\",\n occlusionTexture: \"ormTexture\",\n};\n\n/** Resolve a glTF material-extension texture slot to the runtime PBR material's\n * mutable texture object, so a KHR_texture_transform pointer on an extension\n * texture can drive its UV transform. Only slots whose fragment actually applies\n * the per-texture UV transform are listed (others would animate a value the\n * shader ignores). */\nfunction resolveExtTexture(mat: PointerMaterial, ext: string, field: string): PointerUvTexture | undefined {\n const m = mat as unknown as {\n iridescence?: Record<string, unknown>;\n sheen?: Record<string, unknown>;\n clearCoat?: Record<string, unknown>;\n anisotropy?: Record<string, unknown>;\n reflectanceTexture?: PointerUvTexture;\n metallicReflectanceTexture?: PointerUvTexture;\n subsurface?: { translucency?: Record<string, unknown>; refraction?: Record<string, unknown>; thickness?: Record<string, unknown> };\n };\n switch (`${ext}/${field}`) {\n case \"KHR_materials_iridescence/iridescenceTexture\":\n return privateTexture(m.iridescence, \"texture\");\n case \"KHR_materials_iridescence/iridescenceThicknessTexture\":\n return privateTexture(m.iridescence, \"thicknessTexture\");\n case \"KHR_materials_anisotropy/anisotropyTexture\":\n return privateTexture(m.anisotropy, \"texture\");\n case \"KHR_materials_sheen/sheenColorTexture\":\n return privateTexture(m.sheen, \"texture\");\n case \"KHR_materials_sheen/sheenRoughnessTexture\":\n // Drive the separate roughness texture when present; otherwise roughness shares\n // the colour texture (.a), so fall back to animating that single sheen texture.\n return privateTexture(m.sheen, (m.sheen as { roughnessTexture?: unknown })?.roughnessTexture ? \"roughnessTexture\" : \"texture\");\n case \"KHR_materials_clearcoat/clearcoatTexture\":\n return privateTexture(m.clearCoat, \"texture\");\n case \"KHR_materials_clearcoat/clearcoatRoughnessTexture\":\n return privateTexture(m.clearCoat, \"roughnessTexture\");\n case \"KHR_materials_clearcoat/clearcoatNormalTexture\":\n return privateTexture(m.clearCoat, \"bumpTexture\");\n case \"KHR_materials_specular/specularTexture\":\n return privateTexture(m as unknown as Record<string, unknown>, \"metallicReflectanceTexture\");\n case \"KHR_materials_specular/specularColorTexture\":\n return privateTexture(m as unknown as Record<string, unknown>, \"reflectanceTexture\");\n case \"KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture\":\n return privateTexture(m.subsurface?.translucency, \"colorTexture\");\n case \"KHR_materials_diffuse_transmission/diffuseTransmissionTexture\":\n return privateTexture(m.subsurface?.translucency, \"intensityTexture\");\n case \"KHR_materials_transmission/transmissionTexture\":\n return privateTexture(m.subsurface?.refraction, \"texture\");\n case \"KHR_materials_volume/thicknessTexture\":\n return privateTexture(m.subsurface?.thickness, \"texture\");\n default:\n return undefined;\n }\n}\n\n/** Make a material's animated texture wrapper a private copy so that mutating its\n * per-texture UV-transform fields (uOffset/uScale/uAng…) never leaks into other\n * materials that share the same cached GPU texture wrapper. This happens when one\n * image (e.g. a packed ORM/occlusion texture, or a sheen texture) is reused across\n * several materials and only some of them animate its KHR_texture_transform — the\n * shared wrapper would otherwise be mutated for all of them. The clone shares GPU\n * resources (texture/view/sampler) via object spread; only the transform fields\n * become independent. Idempotent via `_animPriv` so multiple channels\n * (offset/scale/rotation) on the same slot reuse one private wrapper.\n * @param parent - The object holding the texture slot (the material, or an extension\n * sub-object such as `material.sheen`).\n * @param key - The slot field name on `parent`. */\nfunction privateTexture(parent: Record<string, unknown> | undefined, key: string): PointerUvTexture | undefined {\n const cur = parent?.[key] as (PointerUvTexture & { _animPriv?: true }) | undefined;\n if (!cur) {\n return undefined;\n }\n if (cur._animPriv) {\n return cur;\n }\n const clone = { ...(cur as object), _animPriv: true } as PointerUvTexture & { _animPriv: true };\n parent![key] = clone;\n return clone;\n}\n\n/** Build an offset/scale/rotation UV-transform writer for a resolved texture. */\nfunction uvTransformWriter(mat: PointerMaterial, tex: PointerUvTexture, kind: string | undefined): ResolvedPointer {\n if (kind === \"rotation\") {\n return {\n arity: 1,\n writer: (out, off) => {\n tex.uAng = out[off]!;\n mat._uboVersion++;\n },\n };\n }\n const isScale = kind === \"scale\";\n return {\n arity: 2,\n writer: (out, off) => {\n if (isScale) {\n tex.uScale = out[off]!;\n tex.vScale = out[off + 1]!;\n } else {\n tex.uOffset = out[off]!;\n tex.vOffset = out[off + 1]!;\n }\n mat._uboVersion++;\n },\n };\n}\n\nconst _registry: [RegExp, PointerFactory][] = [\n // /nodes/{n}/extensions/KHR_node_visibility/visible — scalar (0 = hidden).\n // The setter cascade handles descendants per the KHR_node_visibility spec\n // and bumps the module-scoped visibility epoch so the engine invalidates\n // its cached render bundle.\n [\n /^\\/nodes\\/(\\d+)\\/extensions\\/KHR_node_visibility\\/visible$/,\n (m, ctx) => {\n const n = ctx.nodes[+m[1]!];\n if (!n) {\n return null;\n }\n return {\n arity: 1,\n writer: (out, off) => {\n setSubtreeVisible(n, out[off]! !== 0);\n },\n };\n },\n ],\n // /materials/{m}/.../KHR_texture_transform/{offset|scale|rotation} — animated UV\n // transform. offset/scale are vec2; rotation is a scalar (radians). Mutates the\n // slot texture's uOffset/vOffset, uScale/vScale, or uAng and bumps the material's\n // UBO version so the renderable re-uploads the UV matrix.\n [\n /^\\/materials\\/(\\d+)\\/(pbrMetallicRoughness\\/baseColorTexture|emissiveTexture|normalTexture|occlusionTexture)\\/extensions\\/KHR_texture_transform\\/(offset|scale|rotation)$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat) {\n return null;\n }\n const slot = m[2]!;\n // orm-unpack: when occlusion has its own UV carrier (independent transform), drive\n // that; otherwise occlusion shares the single ORM transform (TX_SLOT fallback).\n const field: keyof PointerMaterial = slot === \"occlusionTexture\" && mat.occlusionTexture ? \"occlusionTexture\" : TX_SLOT[slot]!;\n // Isolate the animated slot so mutating its UV transform can't leak into other\n // materials that share the same cached texture wrapper (e.g. a reused ORM image).\n const tex = privateTexture(mat as unknown as Record<string, unknown>, field as string);\n if (!tex) {\n return null;\n }\n return uvTransformWriter(mat, tex, m[3]);\n },\n ],\n // /materials/{m}/extensions/{KHR_materials_*}/{slot}Texture/.../KHR_texture_transform/{offset|scale|rotation}\n // — animated UV transform on a material-extension texture (iridescence, sheen,\n // diffuse transmission). Resolves the runtime extension texture and drives its\n // UV transform exactly like the core slots.\n [\n /^\\/materials\\/(\\d+)\\/extensions\\/(KHR_materials_\\w+)\\/(\\w+Texture)\\/extensions\\/KHR_texture_transform\\/(offset|scale|rotation)$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n const tex = mat && resolveExtTexture(mat, m[2]!, m[3]!);\n if (!mat || !tex) {\n return null;\n }\n return uvTransformWriter(mat, tex, m[4]);\n },\n ],\n // /materials/{m}/emissiveFactor — vec3. Recombined with emissiveStrength into\n // the runtime emissiveColor. Requires the material to carry an emissive slot\n // (non-zero load-time emissiveFactor) so the UBO field exists.\n [\n /^\\/materials\\/(\\d+)\\/emissiveFactor$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat?.emissiveColor) {\n return null;\n }\n return {\n arity: 3,\n writer: (out, off) => {\n mat._animEmissiveFactor = [out[off]!, out[off + 1]!, out[off + 2]!];\n applyEmissive(mat);\n },\n };\n },\n ],\n // /materials/{m}/extensions/KHR_materials_emissive_strength/emissiveStrength —\n // scalar HDR multiplier on emissiveFactor.\n [\n /^\\/materials\\/(\\d+)\\/extensions\\/KHR_materials_emissive_strength\\/emissiveStrength$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat?.emissiveColor) {\n return null;\n }\n return {\n arity: 1,\n writer: (out, off) => {\n mat._animEmissiveStrength = out[off]!;\n applyEmissive(mat);\n },\n };\n },\n ],\n // /materials/{m}/pbrMetallicRoughness/baseColorFactor — vec4 linear RGBA factor.\n // Only animatable when the material already carries a baseColorFactor UBO slot.\n [\n /^\\/materials\\/(\\d+)\\/pbrMetallicRoughness\\/baseColorFactor$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat?.baseColorFactor) {\n return null;\n }\n return {\n arity: 4,\n writer: (out, off) => {\n mat.baseColorFactor![0] = out[off]!;\n mat.baseColorFactor![1] = out[off + 1]!;\n mat.baseColorFactor![2] = out[off + 2]!;\n mat.baseColorFactor![3] = out[off + 3]!;\n mat._uboVersion++;\n },\n };\n },\n ],\n];\n\nconst _warned = new Set<string>();\n\nexport function resolveAnimationPointer(pointer: string, ctx: PointerContext): ResolvedPointer | null {\n for (const [rx, make] of _registry) {\n const m = rx.exec(pointer);\n if (m) {\n return make(m, ctx);\n }\n }\n if (!_warned.has(pointer)) {\n _warned.add(pointer);\n\n console.warn(`[babylon-lite] KHR_animation_pointer: no handler for \"${pointer}\"`);\n }\n return null;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"animation-pointer.js","sources":["../../../src/loader-gltf/animation-pointer.ts"],"sourcesContent":["/** KHR_animation_pointer — JSON-pointer resolver registry.\n * Handlers are registered incrementally (one per parity scene). Unknown\n * pointers return null and warn once. */\nimport type { SceneNode } from \"../scene/scene-node.js\";\nimport { setSubtreeVisible } from \"../scene/visibility.js\";\n\nexport interface ResolvedPointer {\n writer: (output: Float32Array, offset: number) => void;\n arity: number;\n}\n\n/** Minimal mutable view of a UV-transform texture slot the pointer writers drive.\n * Mirrors the fields `material/pbr/fragments/uv-transform-fragment.ts` reads. */\nexport interface PointerUvTexture {\n uScale?: number;\n vScale?: number;\n uOffset?: number;\n vOffset?: number;\n /** KHR_texture_transform rotation (radians) — drives the UV matrix's rotation. */\n uAng?: number;\n}\n\n/** Minimal mutable view of a runtime material a pointer can animate. Bumping\n * `_uboVersion` makes the renderable re-upload the material UBO next frame. */\nexport interface PointerMaterial {\n /** @internal */\n _uboVersion: number;\n baseColorTexture?: PointerUvTexture;\n emissiveTexture?: PointerUvTexture;\n normalTexture?: PointerUvTexture;\n ormTexture?: PointerUvTexture;\n /** Independent-occlusion UV carrier (orm-unpack); present only when occlusion\n * is sampled from the ORM texture with its own transform. */\n occlusionTexture?: PointerUvTexture;\n specGlossTexture?: PointerUvTexture;\n /** Runtime emissive (linear RGB) = emissiveFactor × emissiveStrength. */\n emissiveColor?: [number, number, number];\n /** Runtime base-color factor (linear RGBA). */\n baseColorFactor?: [number, number, number, number];\n roughnessFactor?: number;\n metallicF0Factor?: number;\n specularWeight?: number;\n /** @internal Runtime material needs the reflectance extension shader path. */\n _hasReflExt?: boolean;\n /** Runtime glTF normalTexture.scale (drives the lazy normal-scale shader mod). */\n normalTextureScale?: number;\n /** Runtime glTF occlusionTexture.strength (applied by the lazy reflectance ext). */\n occlusionStrength?: number;\n transmissive?: boolean;\n subsurface?: {\n refraction?: { intensity?: number; indexOfRefraction?: number; useThicknessAsDepth?: boolean };\n thickness?: { min?: number; max?: number; useGlTFChannel?: boolean };\n tint?: { color?: [number, number, number]; atDistance?: number };\n };\n iridescence?: {\n isEnabled?: boolean;\n intensity?: number;\n indexOfRefraction?: number;\n maximumThickness?: number;\n };\n /** @internal Animated glTF emissiveFactor, kept separate so an emissiveStrength\n * pointer can recombine without losing the factor (and vice-versa). */\n _animEmissiveFactor?: [number, number, number];\n /** @internal Animated KHR_materials_emissive_strength value. */\n _animEmissiveStrength?: number;\n}\n\n/** Recompute emissiveColor = factor × strength after either input animates, then\n * flag the material UBO for re-upload. */\nfunction applyEmissive(mat: PointerMaterial): void {\n if (!mat.emissiveColor) {\n return;\n }\n const f = mat._animEmissiveFactor ?? [0, 0, 0];\n const s = mat._animEmissiveStrength ?? 1;\n mat.emissiveColor[0] = f[0]! * s;\n mat.emissiveColor[1] = f[1]! * s;\n mat.emissiveColor[2] = f[2]! * s;\n mat._uboVersion++;\n}\n\nexport interface PointerContext {\n nodes: readonly (SceneNode | undefined)[];\n /** Runtime materials indexed by glTF material index (built by the pointer feature). */\n materials?: readonly (PointerMaterial | undefined)[];\n /** @internal glTF JSON object key used to resolve KHR_lights_punctual definitions. */\n _json?: object;\n}\n\nexport type PointerFactory = (match: RegExpExecArray, ctx: PointerContext) => ResolvedPointer | null;\n\n// Maps a KHR_texture_transform pointer's texture-slot segment to the material field.\n// NOTE: metallicRoughnessTexture is intentionally absent. Babylon.js has a long-standing\n// loader bug — its KHR_animation_pointer registration for the MR texture transform omits the\n// `/extensions/KHR_texture_transform/` path segment (and adds a stray leading slash), so the\n// interpolation is never attached and BJS silently skips animating the MR texture transform\n// (offset/scale/rotation stay frozen at their static load-time values). We match BJS for parity\n// (verified: animating MR regresses scene241 col1 MR rows by ~4.9 MAD vs the immutable golden,\n// which renders these spheres with static roughness/metallic). Do NOT animate the MR transform.\nconst TX_SLOT: Record<string, keyof PointerMaterial> = {\n \"pbrMetallicRoughness/baseColorTexture\": \"baseColorTexture\",\n emissiveTexture: \"emissiveTexture\",\n normalTexture: \"normalTexture\",\n occlusionTexture: \"ormTexture\",\n};\n\n/** Resolve a glTF material-extension texture slot to the runtime PBR material's\n * mutable texture object, so a KHR_texture_transform pointer on an extension\n * texture can drive its UV transform. Only slots whose fragment actually applies\n * the per-texture UV transform are listed (others would animate a value the\n * shader ignores). */\nfunction resolveExtTexture(mat: PointerMaterial, ext: string, field: string): PointerUvTexture | undefined {\n const m = mat as unknown as {\n iridescence?: Record<string, unknown>;\n sheen?: Record<string, unknown>;\n clearCoat?: Record<string, unknown>;\n anisotropy?: Record<string, unknown>;\n reflectanceTexture?: PointerUvTexture;\n metallicReflectanceTexture?: PointerUvTexture;\n subsurface?: { translucency?: Record<string, unknown>; refraction?: Record<string, unknown>; thickness?: Record<string, unknown> };\n };\n switch (`${ext}/${field}`) {\n case \"KHR_materials_iridescence/iridescenceTexture\":\n return privateTexture(m.iridescence, \"texture\");\n case \"KHR_materials_iridescence/iridescenceThicknessTexture\":\n return privateTexture(m.iridescence, \"thicknessTexture\");\n case \"KHR_materials_anisotropy/anisotropyTexture\":\n return privateTexture(m.anisotropy, \"texture\");\n case \"KHR_materials_sheen/sheenColorTexture\":\n return privateTexture(m.sheen, \"texture\");\n case \"KHR_materials_sheen/sheenRoughnessTexture\":\n // Drive the separate roughness texture when present; otherwise roughness shares\n // the colour texture (.a), so fall back to animating that single sheen texture.\n return privateTexture(m.sheen, (m.sheen as { roughnessTexture?: unknown })?.roughnessTexture ? \"roughnessTexture\" : \"texture\");\n case \"KHR_materials_clearcoat/clearcoatTexture\":\n return privateTexture(m.clearCoat, \"texture\");\n case \"KHR_materials_clearcoat/clearcoatRoughnessTexture\":\n return privateTexture(m.clearCoat, \"roughnessTexture\");\n case \"KHR_materials_clearcoat/clearcoatNormalTexture\":\n return privateTexture(m.clearCoat, \"bumpTexture\");\n case \"KHR_materials_specular/specularTexture\":\n return privateTexture(m as unknown as Record<string, unknown>, \"metallicReflectanceTexture\");\n case \"KHR_materials_specular/specularColorTexture\":\n return privateTexture(m as unknown as Record<string, unknown>, \"reflectanceTexture\");\n case \"KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture\":\n return privateTexture(m.subsurface?.translucency, \"colorTexture\");\n case \"KHR_materials_diffuse_transmission/diffuseTransmissionTexture\":\n return privateTexture(m.subsurface?.translucency, \"intensityTexture\");\n case \"KHR_materials_transmission/transmissionTexture\":\n return privateTexture(m.subsurface?.refraction, \"texture\");\n case \"KHR_materials_volume/thicknessTexture\":\n return privateTexture(m.subsurface?.thickness, \"texture\");\n default:\n return undefined;\n }\n}\n\n/** Make a material's animated texture wrapper a private copy so that mutating its\n * per-texture UV-transform fields (uOffset/uScale/uAng…) never leaks into other\n * materials that share the same cached GPU texture wrapper. This happens when one\n * image (e.g. a packed ORM/occlusion texture, or a sheen texture) is reused across\n * several materials and only some of them animate its KHR_texture_transform — the\n * shared wrapper would otherwise be mutated for all of them. The clone shares GPU\n * resources (texture/view/sampler) via object spread; only the transform fields\n * become independent. Idempotent via `_animPriv` so multiple channels\n * (offset/scale/rotation) on the same slot reuse one private wrapper.\n * @param parent - The object holding the texture slot (the material, or an extension\n * sub-object such as `material.sheen`).\n * @param key - The slot field name on `parent`. */\nfunction privateTexture(parent: Record<string, unknown> | undefined, key: string): PointerUvTexture | undefined {\n const cur = parent?.[key] as (PointerUvTexture & { _animPriv?: true }) | undefined;\n if (!cur) {\n return undefined;\n }\n if (cur._animPriv) {\n return cur;\n }\n const clone = { ...(cur as object), _animPriv: true } as PointerUvTexture & { _animPriv: true };\n parent![key] = clone;\n return clone;\n}\n\n/** Build an offset/scale/rotation UV-transform writer for a resolved texture. */\nfunction uvTransformWriter(mat: PointerMaterial, tex: PointerUvTexture, kind: string | undefined): ResolvedPointer {\n if (kind === \"rotation\") {\n return {\n arity: 1,\n writer: (out, off) => {\n tex.uAng = out[off]!;\n mat._uboVersion++;\n },\n };\n }\n const isScale = kind === \"scale\";\n return {\n arity: 2,\n writer: (out, off) => {\n if (isScale) {\n tex.uScale = out[off]!;\n tex.vScale = out[off + 1]!;\n } else {\n tex.uOffset = out[off]!;\n tex.vOffset = out[off + 1]!;\n }\n mat._uboVersion++;\n },\n };\n}\n\nconst _registry: [RegExp, PointerFactory][] = [\n // /nodes/{n}/extensions/KHR_node_visibility/visible — scalar (0 = hidden).\n // The setter cascade handles descendants per the KHR_node_visibility spec\n // and bumps the module-scoped visibility epoch so the engine invalidates\n // its cached render bundle.\n [\n /^\\/nodes\\/(\\d+)\\/extensions\\/KHR_node_visibility\\/visible$/,\n (m, ctx) => {\n const n = ctx.nodes[+m[1]!];\n if (!n) {\n return null;\n }\n return {\n arity: 1,\n writer: (out, off) => {\n setSubtreeVisible(n, out[off]! !== 0);\n },\n };\n },\n ],\n // /materials/{m}/.../KHR_texture_transform/{offset|scale|rotation} — animated UV\n // transform. offset/scale are vec2; rotation is a scalar (radians). Mutates the\n // slot texture's uOffset/vOffset, uScale/vScale, or uAng and bumps the material's\n // UBO version so the renderable re-uploads the UV matrix.\n [\n /^\\/materials\\/(\\d+)\\/(pbrMetallicRoughness\\/baseColorTexture|emissiveTexture|normalTexture|occlusionTexture)\\/extensions\\/KHR_texture_transform\\/(offset|scale|rotation)$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat) {\n return null;\n }\n const slot = m[2]!;\n // orm-unpack: when occlusion has its own UV carrier (independent transform), drive\n // that; otherwise occlusion shares the single ORM transform (TX_SLOT fallback).\n const field: keyof PointerMaterial = slot === \"occlusionTexture\" && mat.occlusionTexture ? \"occlusionTexture\" : TX_SLOT[slot]!;\n // Isolate the animated slot so mutating its UV transform can't leak into other\n // materials that share the same cached texture wrapper (e.g. a reused ORM image).\n const tex = privateTexture(mat as unknown as Record<string, unknown>, field as string);\n if (!tex) {\n return null;\n }\n return uvTransformWriter(mat, tex, m[3]);\n },\n ],\n // /materials/{m}/extensions/{KHR_materials_*}/{slot}Texture/.../KHR_texture_transform/{offset|scale|rotation}\n // — animated UV transform on a material-extension texture (iridescence, sheen,\n // diffuse transmission). Resolves the runtime extension texture and drives its\n // UV transform exactly like the core slots.\n [\n /^\\/materials\\/(\\d+)\\/extensions\\/(KHR_materials_\\w+)\\/(\\w+Texture)\\/extensions\\/KHR_texture_transform\\/(offset|scale|rotation)$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n const tex = mat && resolveExtTexture(mat, m[2]!, m[3]!);\n if (!mat || !tex) {\n return null;\n }\n return uvTransformWriter(mat, tex, m[4]);\n },\n ],\n // /materials/{m}/emissiveFactor — vec3. Recombined with emissiveStrength into\n // the runtime emissiveColor. Requires the material to carry an emissive slot\n // (non-zero load-time emissiveFactor) so the UBO field exists.\n [\n /^\\/materials\\/(\\d+)\\/emissiveFactor$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat?.emissiveColor) {\n return null;\n }\n return {\n arity: 3,\n writer: (out, off) => {\n mat._animEmissiveFactor = [out[off]!, out[off + 1]!, out[off + 2]!];\n applyEmissive(mat);\n },\n };\n },\n ],\n // /materials/{m}/extensions/KHR_materials_emissive_strength/emissiveStrength —\n // scalar HDR multiplier on emissiveFactor.\n [\n /^\\/materials\\/(\\d+)\\/extensions\\/KHR_materials_emissive_strength\\/emissiveStrength$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat?.emissiveColor) {\n return null;\n }\n return {\n arity: 1,\n writer: (out, off) => {\n mat._animEmissiveStrength = out[off]!;\n applyEmissive(mat);\n },\n };\n },\n ],\n // /materials/{m}/pbrMetallicRoughness/baseColorFactor — vec4 linear RGBA factor.\n // Only animatable when the material already carries a baseColorFactor UBO slot.\n [\n /^\\/materials\\/(\\d+)\\/pbrMetallicRoughness\\/baseColorFactor$/,\n (m, ctx) => {\n const mat = ctx.materials?.[+m[1]!];\n if (!mat?.baseColorFactor) {\n return null;\n }\n return {\n arity: 4,\n writer: (out, off) => {\n mat.baseColorFactor![0] = out[off]!;\n mat.baseColorFactor![1] = out[off + 1]!;\n mat.baseColorFactor![2] = out[off + 2]!;\n mat.baseColorFactor![3] = out[off + 3]!;\n mat._uboVersion++;\n },\n };\n },\n ],\n];\n\n/** Append extended (lazily-loaded) pointer handlers to the resolver registry.\n * Called once by animation-pointer-ext.ts on import. ES-module caching makes the\n * import idempotent, so the handlers are appended exactly once per process. */\nexport function _appendPointerHandlers(handlers: [RegExp, PointerFactory][]): void {\n _registry.push(...handlers);\n}\n\nconst _warned = new Set<string>();\n\nexport function resolveAnimationPointer(pointer: string, ctx: PointerContext): ResolvedPointer | null {\n for (const [rx, make] of _registry) {\n const m = rx.exec(pointer);\n if (m) {\n return make(m, ctx);\n }\n }\n if (!_warned.has(pointer)) {\n _warned.add(pointer);\n\n console.warn(`[babylon-lite] KHR_animation_pointer: no handler for \"${pointer}\"`);\n }\n return null;\n}\n"],"names":[],"mappings":"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"}
@@ -7,9 +7,11 @@ import { computeNodeWorldMatrix, resolveAccessor, findParent } from './gltf-pars
7
7
  import { getLoaderTmpAnim } from './_loader-scratch.js';
8
8
 
9
9
  let _parsePointerChannel = null;
10
- let _convertSampler = null;
11
- function _installPointerHandlers(parser, converter) {
10
+ function _installPointerHandlers(parser) {
12
11
  _parsePointerChannel = parser;
12
+ }
13
+ let _convertSampler = null;
14
+ function _installSamplerConverter(converter) {
13
15
  _convertSampler = converter;
14
16
  }
15
17
  function toSamplerFloat32(src, length, normalized) {
@@ -244,5 +246,5 @@ function hasWritableNodeChannel(clips, nodeTargets, excludedNodeIndices) {
244
246
  return false;
245
247
  }
246
248
 
247
- export { _installPointerHandlers, computeBoneTextureData, extractSkin, parseAnimationData };
249
+ export { _installPointerHandlers, _installSamplerConverter, computeBoneTextureData, extractSkin, parseAnimationData };
248
250
  //# sourceMappingURL=gltf-animation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-animation.js","sources":["../../../src/loader-gltf/gltf-animation.ts"],"sourcesContent":["/**\n * Lazy-loaded animation/skin parsing for glTF.\n * Dynamically imported by load-gltf.ts only when a glTF contains animations or skins.\n *\n * This module is pointer-feature agnostic: KHR_animation_pointer (and the\n * non-Float32 sampler conversion that CubeVisibility-style assets need) are\n * installed via the registration seam below, so scenes that don't declare\n * the extension pay zero bytes for it.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { GltfAnimationData, AnimationClip, AnimationSampler, AnimationChannel, NodeRest, SkeletonBinding, MorphBinding, AnimatedNodeTarget } from \"../animation/types.js\";\nimport { INTERP_LINEAR, INTERP_STEP, INTERP_CUBICSPLINE, PATH_TRANSLATION, PATH_ROTATION, PATH_SCALE, PATH_WEIGHTS } from \"../animation/types.js\";\nimport { mat4Identity } from \"../math/mat4-identity.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { mat4MultiplyInto } from \"../math/mat4-multiply-into.js\";\nimport type { Mat4Storage } from \"../math/types.js\";\nimport { resolveAccessor, computeNodeWorldMatrix, findParent } from \"./gltf-parser.js\";\nimport { getLoaderTmpAnim } from \"./_loader-scratch.js\";\nimport type { SceneNode } from \"../scene/scene-node.js\";\n\n/** Registration seam for KHR_animation_pointer. The pointer feature module\n * calls `_installPointerHandlers` on side-effect import; if never called,\n * pointer channels are skipped and non-Float32 samplers fall back to the\n * aliasing fast path (which throws on misaligned/short accessors). */\nexport type PointerChannelParser = (\n ptr: string,\n channel: any,\n nodeMap: readonly (SceneNode | undefined)[] | undefined,\n json: any,\n meshes: readonly Mesh[]\n) => AnimationChannel | null;\nexport type SamplerConverter = (src: ArrayBufferView, length: number, normalized: boolean) => Float32Array;\nlet _parsePointerChannel: PointerChannelParser | null = null;\nlet _convertSampler: SamplerConverter | null = null;\nexport function _installPointerHandlers(parser: PointerChannelParser, converter: SamplerConverter): void {\n _parsePointerChannel = parser;\n _convertSampler = converter;\n}\n\n/** Convert sampler input/output to Float32Array. Default: reinterpret existing\n * Float32 accessor as Float32Array (legacy behaviour; fast but requires\n * aligned Float32 data). KHR_animation_pointer installs a converter that\n * additionally handles non-Float32 / normalized accessors. */\nfunction toSamplerFloat32(src: ArrayBufferView, length: number, normalized: boolean): Float32Array {\n if (_convertSampler) {\n return _convertSampler(src, length, normalized);\n }\n return new F32(src.buffer, src.byteOffset, length);\n}\n\n/** Parsed skin/skeleton data. */\nexport interface GltfSkinData {\n /** Node indices of joints in this skin. */\n jointNodes: number[];\n /** Inverse bind matrices — one 4×4 per joint (column-major Float32Array). */\n inverseBindMatrices: Float32Array;\n /** World matrices of each joint at rest pose. */\n jointWorldMatrices: Mat4[];\n /** World matrix of the mesh node that owns this skin. */\n meshWorldMatrix: Mat4;\n}\n\n// ─── Skin / Skeleton Extraction ─────────────────────────────────────\n\n/** Resolve a skin's inverse-bind matrices, filling with identities when absent. */\nfunction resolveIBMs(json: any, binChunk: DataView, skin: any): Float32Array {\n const jointCount = skin.joints.length;\n if (skin.inverseBindMatrices !== undefined) {\n const ibmData = resolveAccessor(json, binChunk, skin.inverseBindMatrices);\n return new F32(ibmData._data.buffer, ibmData._data.byteOffset, jointCount * 16);\n }\n const out = new F32(jointCount * 16);\n for (let i = 0; i < jointCount; i++) {\n const o = i * 16;\n out[o] = out[o + 5] = out[o + 10] = out[o + 15] = 1;\n }\n return out;\n}\n\nexport function extractSkin(\n json: any,\n binChunk: DataView,\n skinIdx: number,\n meshWorldMatrix: Mat4,\n parentMap: Map<number, number>,\n worldMatrixCache: Map<number, Mat4>\n): GltfSkinData {\n const skin = json.skins[skinIdx];\n const jointNodes: number[] = skin.joints;\n const inverseBindMatrices = resolveIBMs(json, binChunk, skin);\n const jointWorldMatrices: Mat4[] = jointNodes.map((nodeIdx) => computeNodeWorldMatrix(json, nodeIdx, parentMap, worldMatrixCache));\n return { jointNodes, inverseBindMatrices, jointWorldMatrices, meshWorldMatrix };\n}\n\n/** Compute rest-pose bone texture data. Each bone gets 4 vec4 (one 4×4 matrix).\n * Formula: boneMatrix[i] = inverse(meshWorld) * jointWorld[i] * IBM[i]\n * At rest pose this simplifies to identity for each bone. */\nexport function computeBoneTextureData(skin: GltfSkinData): Float32Array {\n const numBones = skin.jointNodes.length;\n const data = new F32(numBones * 16);\n const invMeshWorld = mat4Invert(skin.meshWorldMatrix) ?? mat4Identity();\n const tmp = getLoaderTmpAnim() as unknown as Mat4Storage;\n for (let i = 0; i < numBones; i++) {\n mat4MultiplyInto(tmp, 0, invMeshWorld as unknown as Mat4Storage, 0, skin.jointWorldMatrices[i]! as unknown as Mat4Storage, 0);\n mat4MultiplyInto(data, i * 16, tmp, 0, skin.inverseBindMatrices, i * 16);\n }\n return data;\n}\n\n// ─── Animation Parsing ──────────────────────────────────────────────\n\nconst INTERP_MAP: Record<string, 0 | 1 | 2> = {\n LINEAR: INTERP_LINEAR,\n STEP: INTERP_STEP,\n CUBICSPLINE: INTERP_CUBICSPLINE,\n};\n\nconst PATH_MAP: Record<string, 0 | 1 | 2 | 3> = {\n translation: PATH_TRANSLATION,\n rotation: PATH_ROTATION,\n scale: PATH_SCALE,\n weights: PATH_WEIGHTS,\n};\n\n/**\n * Parse glTF animation data: clips, node hierarchy, and skeleton bindings.\n * Returns null if no animations, or no drivable state at all (no skeletons,\n * no morphs, no pointer channels).\n *\n * `nodeMap` (optional) maps glTF node index → SceneNode. It's required to\n * resolve KHR_animation_pointer targets that write to node properties.\n */\nexport function parseAnimationData(\n json: any,\n binChunk: DataView,\n meshes: Mesh[],\n parentMap: Map<number, number>,\n worldMatrixCache: Map<number, Mat4>,\n nodeMap?: readonly (SceneNode | undefined)[],\n boneOverrides?: ReadonlyMap<number, import(\"../skeleton/bone-control.js\").BoneOverride>\n): GltfAnimationData | null {\n if (!json.animations || json.animations.length === 0) {\n return null;\n }\n\n let pointerChannelCount = 0;\n\n // Parse animation clips\n const clips: AnimationClip[] = [];\n for (const anim of json.animations) {\n const samplers: AnimationSampler[] = [];\n for (const s of anim.samplers) {\n const inputAcc = resolveAccessor(json, binChunk, s.input);\n const outputAcc = resolveAccessor(json, binChunk, s.output);\n const inNorm = json.accessors[s.input]?.normalized === true;\n const outNorm = json.accessors[s.output]?.normalized === true;\n samplers.push({\n input: toSamplerFloat32(inputAcc._data, inputAcc._count, inNorm),\n output: toSamplerFloat32(outputAcc._data, outputAcc._count * outputAcc._componentCount, outNorm),\n interpolation: INTERP_MAP[s.interpolation ?? \"LINEAR\"] ?? INTERP_LINEAR,\n });\n }\n\n const channels: AnimationChannel[] = [];\n for (const c of anim.channels) {\n // KHR_animation_pointer: delegated to the registered pointer parser\n // (installed by gltf-feature-animation-pointer on side-effect import).\n const ptr = c.target?.extensions?.KHR_animation_pointer?.pointer;\n if (ptr) {\n if (!_parsePointerChannel) {\n continue;\n }\n const ch = _parsePointerChannel(ptr, c, nodeMap, json, meshes);\n if (ch) {\n channels.push(ch);\n pointerChannelCount++;\n }\n continue;\n }\n if (c.target.node === undefined) {\n continue;\n }\n const path = PATH_MAP[c.target.path];\n if (path === undefined) {\n continue;\n }\n channels.push({ samplerIdx: c.sampler, nodeIdx: c.target.node, path });\n }\n\n let duration = 0;\n for (const s of samplers) {\n if (s.input.length > 0) {\n const last = s.input[s.input.length - 1]!;\n if (last > duration) {\n duration = last;\n }\n }\n }\n\n clips.push({ name: anim.name ?? \"\", channels, samplers, duration });\n }\n\n // Build node hierarchy (rest-pose TRS + parent indices)\n const nodeCount = json.nodes?.length ?? 0;\n const nodes: NodeRest[] = [];\n for (let i = 0; i < nodeCount; i++) {\n const n = json.nodes[i];\n const t = n.translation ?? [0, 0, 0];\n const r = n.rotation ?? [0, 0, 0, 1];\n const s = n.scale ?? [1, 1, 1];\n nodes.push({\n parentIdx: findParent(parentMap, i),\n _matrix: n.matrix as Mat4 | undefined,\n tx: t[0],\n ty: t[1],\n tz: t[2],\n rx: r[0],\n ry: r[1],\n rz: r[2],\n rw: r[3],\n sx: s[0],\n sy: s[1],\n sz: s[2],\n });\n }\n\n // Build skeleton bindings (connect skin data to GPU bone textures)\n // First, build node→gpuMesh mapping by replaying extraction order\n const nodeToMeshIndices = new Map<number, number[]>();\n let gpuIdx = 0;\n for (let ni = 0; ni < nodeCount; ni++) {\n const node = json.nodes[ni];\n if (node.mesh === undefined) {\n continue;\n }\n const mesh = json.meshes[node.mesh];\n const indices: number[] = [];\n for (let p = 0; p < mesh.primitives.length; p++) {\n indices.push(gpuIdx++);\n }\n nodeToMeshIndices.set(ni, indices);\n }\n\n const skeletons: SkeletonBinding[] = [];\n for (let nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) {\n const node = json.nodes[nodeIdx];\n if (node.skin === undefined || !json.skins) {\n continue;\n }\n\n const meshIndices = nodeToMeshIndices.get(nodeIdx);\n if (!meshIndices) {\n continue;\n }\n\n const skin = json.skins[node.skin];\n const jointNodes: number[] = skin.joints;\n const inverseBindMatrices = resolveIBMs(json, binChunk, skin);\n\n const meshWorldMatrix = computeNodeWorldMatrix(json, nodeIdx, parentMap, worldMatrixCache);\n const invMeshWorld = mat4Invert(meshWorldMatrix) ?? mat4Identity();\n\n // Create a binding for EACH mesh primitive of this skinned node\n for (const mi of meshIndices) {\n const mesh = meshes[mi];\n const skeleton = mesh?.skeleton;\n if (!skeleton) {\n continue;\n }\n skeletons.push({\n jointNodes,\n inverseBindMatrices,\n invMeshWorld,\n boneTexture: skeleton.boneTexture,\n boneCount: jointNodes.length,\n boneMatrices: skeleton.boneMatrices,\n runtimeSkeleton: skeleton,\n });\n }\n }\n\n // Build morph bindings (connect morph-target meshes to GPU weight buffers)\n const morphBindings: MorphBinding[] = [];\n for (let nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) {\n const node = json.nodes[nodeIdx];\n if (node.mesh === undefined) {\n continue;\n }\n const gltfMesh = json.meshes[node.mesh];\n if (!gltfMesh.primitives?.[0]?.targets?.length) {\n continue;\n }\n\n const meshIndices = nodeToMeshIndices.get(nodeIdx);\n if (!meshIndices) {\n continue;\n }\n\n for (const mi of meshIndices) {\n const mesh = meshes[mi];\n const morphTargets = mesh?.morphTargets;\n if (!morphTargets) {\n continue;\n }\n morphBindings.push({\n nodeIdx,\n weightsBuffer: morphTargets.weightsBuffer,\n weights: morphTargets.weights,\n targetCount: morphTargets.count,\n runtimeMorphTargets: morphTargets,\n });\n }\n }\n\n // Build the node-TRS writeback inputs. `nodeTargets` exposes each glTF node's\n // live scene node (via the structural AnimatedNodeTarget view) so the controller\n // can push evaluated local TRS back onto the scene graph, moving non-skinned\n // node-animated meshes and their descendants. `excludedNodeIndices` lists nodes\n // that MUST NOT be written: skin joints (driven by the skeleton path) plus\n // skinned-mesh nodes and all their ancestors — their bone matrices bake an\n // `invMeshWorld` captured at load, so moving them at runtime would\n // double-transform the skinned vertices.\n const nodeTargets: readonly (AnimatedNodeTarget | undefined)[] = (nodeMap as readonly (AnimatedNodeTarget | undefined)[] | undefined) ?? [];\n const excludedNodeIndices = new Set<number>();\n for (const skin of json.skins ?? []) {\n for (const ji of skin.joints ?? []) {\n excludedNodeIndices.add(ji);\n }\n }\n for (let ni = 0; ni < nodeCount; ni++) {\n if (json.nodes[ni]?.skin === undefined) {\n continue;\n }\n let p = ni;\n while (p >= 0 && !excludedNodeIndices.has(p)) {\n excludedNodeIndices.add(p);\n p = findParent(parentMap, p);\n }\n }\n\n if (\n clips.length === 0 ||\n (skeletons.length === 0 && morphBindings.length === 0 && pointerChannelCount === 0 && !hasWritableNodeChannel(clips, nodeTargets, excludedNodeIndices))\n ) {\n return null;\n }\n const nodeNames: readonly (string | undefined)[] = (json.nodes ?? []).map((n: { name?: string }) => n?.name);\n return { clips, nodes, skeletons, morphBindings, nodeTargets, excludedNodeIndices, nodeNames, boneOverrides };\n}\n\n/** True if any clip animates a non-excluded node that has a live scene target —\n * i.e. there is at least one plain node-TRS channel the controller can write\n * back. Lets purely-skinned/morph/pointer assets short-circuit unchanged. */\nfunction hasWritableNodeChannel(clips: readonly AnimationClip[], nodeTargets: readonly (AnimatedNodeTarget | undefined)[], excludedNodeIndices: ReadonlySet<number>): boolean {\n for (const clip of clips) {\n for (const ch of clip.channels) {\n if (\n (ch.path === PATH_TRANSLATION || ch.path === PATH_ROTATION || ch.path === PATH_SCALE) &&\n ch.nodeIdx >= 0 &&\n !excludedNodeIndices.has(ch.nodeIdx) &&\n nodeTargets[ch.nodeIdx]\n ) {\n return true;\n }\n }\n }\n return false;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-animation.js","sources":["../../../src/loader-gltf/gltf-animation.ts"],"sourcesContent":["/**\n * Lazy-loaded animation/skin parsing for glTF.\n * Dynamically imported by load-gltf.ts only when a glTF contains animations or skins.\n *\n * This module is pointer-feature agnostic: KHR_animation_pointer is installed\n * via the registration seam below, so scenes that don't declare the extension\n * pay zero bytes for pointer resolution.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { GltfAnimationData, AnimationClip, AnimationSampler, AnimationChannel, NodeRest, SkeletonBinding, MorphBinding, AnimatedNodeTarget } from \"../animation/types.js\";\nimport { INTERP_LINEAR, INTERP_STEP, INTERP_CUBICSPLINE, PATH_TRANSLATION, PATH_ROTATION, PATH_SCALE, PATH_WEIGHTS } from \"../animation/types.js\";\nimport { mat4Identity } from \"../math/mat4-identity.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { mat4MultiplyInto } from \"../math/mat4-multiply-into.js\";\nimport type { Mat4Storage } from \"../math/types.js\";\nimport { resolveAccessor, computeNodeWorldMatrix, findParent } from \"./gltf-parser.js\";\nimport { getLoaderTmpAnim } from \"./_loader-scratch.js\";\nimport type { SceneNode } from \"../scene/scene-node.js\";\n\n/** Registration seam for KHR_animation_pointer. The pointer feature module\n * calls `_installPointerHandlers` on side-effect import; if never called,\n * pointer channels are skipped. */\nexport type PointerChannelParser = (\n ptr: string,\n channel: any,\n nodeMap: readonly (SceneNode | undefined)[] | undefined,\n json: any,\n meshes: readonly Mesh[]\n) => AnimationChannel | null;\nlet _parsePointerChannel: PointerChannelParser | null = null;\nexport function _installPointerHandlers(parser: PointerChannelParser): void {\n _parsePointerChannel = parser;\n}\n\n/** Registration seam for the non-Float32 / normalized sampler converter. Installed only by the lazy\n * `gltf-sampler-denorm` module — loaded when an animation sampler accessor is non-float (e.g.\n * glTF-Asset-Generator Animation_SamplerType normalized BYTE/SHORT rotation) or by\n * KHR_animation_pointer. When never installed, `toSamplerFloat32` folds to the aligned-Float32 fast\n * path, so plain float-sampler animations stay byte-identical. */\nexport type SamplerConverter = (src: ArrayBufferView, length: number, normalized: boolean) => Float32Array;\nlet _convertSampler: SamplerConverter | null = null;\nexport function _installSamplerConverter(converter: SamplerConverter): void {\n _convertSampler = converter;\n}\n\n/** Convert a sampler input/output accessor to a tightly-packed Float32Array. Aligned Float32 sources\n * (the overwhelmingly common case) are reinterpreted in place for free; non-Float32 / normalized\n * sources are handled by the lazily-installed converter. */\nfunction toSamplerFloat32(src: ArrayBufferView, length: number, normalized: boolean): Float32Array {\n if (_convertSampler) {\n return _convertSampler(src, length, normalized);\n }\n return new F32(src.buffer as ArrayBuffer, src.byteOffset, length);\n}\n\n/** Parsed skin/skeleton data. */\nexport interface GltfSkinData {\n /** Node indices of joints in this skin. */\n jointNodes: number[];\n /** Inverse bind matrices — one 4×4 per joint (column-major Float32Array). */\n inverseBindMatrices: Float32Array;\n /** World matrices of each joint at rest pose. */\n jointWorldMatrices: Mat4[];\n /** World matrix of the mesh node that owns this skin. */\n meshWorldMatrix: Mat4;\n}\n\n// ─── Skin / Skeleton Extraction ─────────────────────────────────────\n\n/** Resolve a skin's inverse-bind matrices, filling with identities when absent. */\nfunction resolveIBMs(json: any, binChunk: DataView, skin: any): Float32Array {\n const jointCount = skin.joints.length;\n if (skin.inverseBindMatrices !== undefined) {\n const ibmData = resolveAccessor(json, binChunk, skin.inverseBindMatrices);\n return new F32(ibmData._data.buffer, ibmData._data.byteOffset, jointCount * 16);\n }\n const out = new F32(jointCount * 16);\n for (let i = 0; i < jointCount; i++) {\n const o = i * 16;\n out[o] = out[o + 5] = out[o + 10] = out[o + 15] = 1;\n }\n return out;\n}\n\nexport function extractSkin(\n json: any,\n binChunk: DataView,\n skinIdx: number,\n meshWorldMatrix: Mat4,\n parentMap: Map<number, number>,\n worldMatrixCache: Map<number, Mat4>\n): GltfSkinData {\n const skin = json.skins[skinIdx];\n const jointNodes: number[] = skin.joints;\n const inverseBindMatrices = resolveIBMs(json, binChunk, skin);\n const jointWorldMatrices: Mat4[] = jointNodes.map((nodeIdx) => computeNodeWorldMatrix(json, nodeIdx, parentMap, worldMatrixCache));\n return { jointNodes, inverseBindMatrices, jointWorldMatrices, meshWorldMatrix };\n}\n\n/** Compute rest-pose bone texture data. Each bone gets 4 vec4 (one 4×4 matrix).\n * Formula: boneMatrix[i] = inverse(meshWorld) * jointWorld[i] * IBM[i]\n * At rest pose this simplifies to identity for each bone. */\nexport function computeBoneTextureData(skin: GltfSkinData): Float32Array {\n const numBones = skin.jointNodes.length;\n const data = new F32(numBones * 16);\n const invMeshWorld = mat4Invert(skin.meshWorldMatrix) ?? mat4Identity();\n const tmp = getLoaderTmpAnim() as unknown as Mat4Storage;\n for (let i = 0; i < numBones; i++) {\n mat4MultiplyInto(tmp, 0, invMeshWorld as unknown as Mat4Storage, 0, skin.jointWorldMatrices[i]! as unknown as Mat4Storage, 0);\n mat4MultiplyInto(data, i * 16, tmp, 0, skin.inverseBindMatrices, i * 16);\n }\n return data;\n}\n\n// ─── Animation Parsing ──────────────────────────────────────────────\n\nconst INTERP_MAP: Record<string, 0 | 1 | 2> = {\n LINEAR: INTERP_LINEAR,\n STEP: INTERP_STEP,\n CUBICSPLINE: INTERP_CUBICSPLINE,\n};\n\nconst PATH_MAP: Record<string, 0 | 1 | 2 | 3> = {\n translation: PATH_TRANSLATION,\n rotation: PATH_ROTATION,\n scale: PATH_SCALE,\n weights: PATH_WEIGHTS,\n};\n\n/**\n * Parse glTF animation data: clips, node hierarchy, and skeleton bindings.\n * Returns null if no animations, or no drivable state at all (no skeletons,\n * no morphs, no pointer channels).\n *\n * `nodeMap` (optional) maps glTF node index → SceneNode. It's required to\n * resolve KHR_animation_pointer targets that write to node properties.\n */\nexport function parseAnimationData(\n json: any,\n binChunk: DataView,\n meshes: Mesh[],\n parentMap: Map<number, number>,\n worldMatrixCache: Map<number, Mat4>,\n nodeMap?: readonly (SceneNode | undefined)[],\n boneOverrides?: ReadonlyMap<number, import(\"../skeleton/bone-control.js\").BoneOverride>\n): GltfAnimationData | null {\n if (!json.animations || json.animations.length === 0) {\n return null;\n }\n\n let pointerChannelCount = 0;\n\n // Parse animation clips\n const clips: AnimationClip[] = [];\n for (const anim of json.animations) {\n const samplers: AnimationSampler[] = [];\n for (const s of anim.samplers) {\n const inputAcc = resolveAccessor(json, binChunk, s.input);\n const outputAcc = resolveAccessor(json, binChunk, s.output);\n const inNorm = json.accessors[s.input]?.normalized === true;\n const outNorm = json.accessors[s.output]?.normalized === true;\n samplers.push({\n input: toSamplerFloat32(inputAcc._data, inputAcc._count, inNorm),\n output: toSamplerFloat32(outputAcc._data, outputAcc._count * outputAcc._componentCount, outNorm),\n interpolation: INTERP_MAP[s.interpolation ?? \"LINEAR\"] ?? INTERP_LINEAR,\n });\n }\n\n const channels: AnimationChannel[] = [];\n for (const c of anim.channels) {\n // KHR_animation_pointer: delegated to the registered pointer parser\n // (installed by gltf-feature-animation-pointer on side-effect import).\n const ptr = c.target?.extensions?.KHR_animation_pointer?.pointer;\n if (ptr) {\n if (!_parsePointerChannel) {\n continue;\n }\n const ch = _parsePointerChannel(ptr, c, nodeMap, json, meshes);\n if (ch) {\n channels.push(ch);\n pointerChannelCount++;\n }\n continue;\n }\n if (c.target.node === undefined) {\n continue;\n }\n const path = PATH_MAP[c.target.path];\n if (path === undefined) {\n continue;\n }\n channels.push({ samplerIdx: c.sampler, nodeIdx: c.target.node, path });\n }\n\n let duration = 0;\n for (const s of samplers) {\n if (s.input.length > 0) {\n const last = s.input[s.input.length - 1]!;\n if (last > duration) {\n duration = last;\n }\n }\n }\n\n clips.push({ name: anim.name ?? \"\", channels, samplers, duration });\n }\n\n // Build node hierarchy (rest-pose TRS + parent indices)\n const nodeCount = json.nodes?.length ?? 0;\n const nodes: NodeRest[] = [];\n for (let i = 0; i < nodeCount; i++) {\n const n = json.nodes[i];\n const t = n.translation ?? [0, 0, 0];\n const r = n.rotation ?? [0, 0, 0, 1];\n const s = n.scale ?? [1, 1, 1];\n nodes.push({\n parentIdx: findParent(parentMap, i),\n _matrix: n.matrix as Mat4 | undefined,\n tx: t[0],\n ty: t[1],\n tz: t[2],\n rx: r[0],\n ry: r[1],\n rz: r[2],\n rw: r[3],\n sx: s[0],\n sy: s[1],\n sz: s[2],\n });\n }\n\n // Build skeleton bindings (connect skin data to GPU bone textures)\n // First, build node→gpuMesh mapping by replaying extraction order\n const nodeToMeshIndices = new Map<number, number[]>();\n let gpuIdx = 0;\n for (let ni = 0; ni < nodeCount; ni++) {\n const node = json.nodes[ni];\n if (node.mesh === undefined) {\n continue;\n }\n const mesh = json.meshes[node.mesh];\n const indices: number[] = [];\n for (let p = 0; p < mesh.primitives.length; p++) {\n indices.push(gpuIdx++);\n }\n nodeToMeshIndices.set(ni, indices);\n }\n\n const skeletons: SkeletonBinding[] = [];\n for (let nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) {\n const node = json.nodes[nodeIdx];\n if (node.skin === undefined || !json.skins) {\n continue;\n }\n\n const meshIndices = nodeToMeshIndices.get(nodeIdx);\n if (!meshIndices) {\n continue;\n }\n\n const skin = json.skins[node.skin];\n const jointNodes: number[] = skin.joints;\n const inverseBindMatrices = resolveIBMs(json, binChunk, skin);\n\n const meshWorldMatrix = computeNodeWorldMatrix(json, nodeIdx, parentMap, worldMatrixCache);\n const invMeshWorld = mat4Invert(meshWorldMatrix) ?? mat4Identity();\n\n // Create a binding for EACH mesh primitive of this skinned node\n for (const mi of meshIndices) {\n const mesh = meshes[mi];\n const skeleton = mesh?.skeleton;\n if (!skeleton) {\n continue;\n }\n skeletons.push({\n jointNodes,\n inverseBindMatrices,\n invMeshWorld,\n boneTexture: skeleton.boneTexture,\n boneCount: jointNodes.length,\n boneMatrices: skeleton.boneMatrices,\n runtimeSkeleton: skeleton,\n });\n }\n }\n\n // Build morph bindings (connect morph-target meshes to GPU weight buffers)\n const morphBindings: MorphBinding[] = [];\n for (let nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) {\n const node = json.nodes[nodeIdx];\n if (node.mesh === undefined) {\n continue;\n }\n const gltfMesh = json.meshes[node.mesh];\n if (!gltfMesh.primitives?.[0]?.targets?.length) {\n continue;\n }\n\n const meshIndices = nodeToMeshIndices.get(nodeIdx);\n if (!meshIndices) {\n continue;\n }\n\n for (const mi of meshIndices) {\n const mesh = meshes[mi];\n const morphTargets = mesh?.morphTargets;\n if (!morphTargets) {\n continue;\n }\n morphBindings.push({\n nodeIdx,\n weightsBuffer: morphTargets.weightsBuffer,\n weights: morphTargets.weights,\n targetCount: morphTargets.count,\n runtimeMorphTargets: morphTargets,\n });\n }\n }\n\n // Build the node-TRS writeback inputs. `nodeTargets` exposes each glTF node's\n // live scene node (via the structural AnimatedNodeTarget view) so the controller\n // can push evaluated local TRS back onto the scene graph, moving non-skinned\n // node-animated meshes and their descendants. `excludedNodeIndices` lists nodes\n // that MUST NOT be written: skin joints (driven by the skeleton path) plus\n // skinned-mesh nodes and all their ancestors — their bone matrices bake an\n // `invMeshWorld` captured at load, so moving them at runtime would\n // double-transform the skinned vertices.\n const nodeTargets: readonly (AnimatedNodeTarget | undefined)[] = (nodeMap as readonly (AnimatedNodeTarget | undefined)[] | undefined) ?? [];\n const excludedNodeIndices = new Set<number>();\n for (const skin of json.skins ?? []) {\n for (const ji of skin.joints ?? []) {\n excludedNodeIndices.add(ji);\n }\n }\n for (let ni = 0; ni < nodeCount; ni++) {\n if (json.nodes[ni]?.skin === undefined) {\n continue;\n }\n let p = ni;\n while (p >= 0 && !excludedNodeIndices.has(p)) {\n excludedNodeIndices.add(p);\n p = findParent(parentMap, p);\n }\n }\n\n if (\n clips.length === 0 ||\n (skeletons.length === 0 && morphBindings.length === 0 && pointerChannelCount === 0 && !hasWritableNodeChannel(clips, nodeTargets, excludedNodeIndices))\n ) {\n return null;\n }\n const nodeNames: readonly (string | undefined)[] = (json.nodes ?? []).map((n: { name?: string }) => n?.name);\n return { clips, nodes, skeletons, morphBindings, nodeTargets, excludedNodeIndices, nodeNames, boneOverrides };\n}\n\n/** True if any clip animates a non-excluded node that has a live scene target —\n * i.e. there is at least one plain node-TRS channel the controller can write\n * back. Lets purely-skinned/morph/pointer assets short-circuit unchanged. */\nfunction hasWritableNodeChannel(clips: readonly AnimationClip[], nodeTargets: readonly (AnimatedNodeTarget | undefined)[], excludedNodeIndices: ReadonlySet<number>): boolean {\n for (const clip of clips) {\n for (const ch of clip.channels) {\n if (\n (ch.path === PATH_TRANSLATION || ch.path === PATH_ROTATION || ch.path === PATH_SCALE) &&\n ch.nodeIdx >= 0 &&\n !excludedNodeIndices.has(ch.nodeIdx) &&\n nodeTargets[ch.nodeIdx]\n ) {\n return true;\n }\n }\n }\n return false;\n}\n"],"names":[],"mappings":"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"}
@@ -27,6 +27,15 @@ function normalizeColorToVec4(data, count, comps) {
27
27
  }
28
28
  return out;
29
29
  }
30
+ function normalizeUvToVec2(data, count) {
31
+ const out = new Float32Array(count * 2);
32
+ const inv = data instanceof Uint16Array ? 1 / 65535 : data instanceof Uint8Array ? 1 / 255 : 1;
33
+ const s = data;
34
+ for (let i = 0; i < count * 2; i++) {
35
+ out[i] = s[i] * inv;
36
+ }
37
+ return out;
38
+ }
30
39
 
31
- export { normalizeColorToVec4 };
40
+ export { normalizeColorToVec4, normalizeUvToVec2 };
32
41
  //# sourceMappingURL=gltf-color-normalize.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-color-normalize.js","sources":["../../../src/loader-gltf/gltf-color-normalize.ts"],"sourcesContent":["/** glTF COLOR_0 vertex-color normalization — dynamically imported.\n *\n * Isolated from the core loader so scenes whose assets have no COLOR_0 attribute\n * (the common case) never bundle or fetch this code. Loaded lazily by\n * `load-gltf.ts` only when a primitive actually provides COLOR_0.\n *\n * Zero module-level side effects — safe for tree-shaking.\n */\n\n/** Normalize a glTF COLOR_0 attribute to a tight float32 VEC4 (RGBA) buffer.\n *\n * The PBR vertex pipeline binds vertex color as `float32x4` (stride 16), matching\n * the rest of the engine (procedural meshes, node/shader materials). glTF COLOR_0\n * is far more permissive — it may be VEC3 or VEC4, and its component type may be\n * float OR normalized unsigned byte/short. Binding any other layout to the stride-16\n * float pipeline misaligns every vertex (garbage / black colors). So we always\n * convert here: integer types are normalized to [0,1] (per the glTF spec, integer\n * COLOR_0 is always normalized), a VEC3 source gets alpha = 1.0, and the result is a\n * tight Float32Array RGBA. The rgb modulates base color; the alpha modulates the\n * fragment alpha (so vertex-color alpha drives alpha blending / alpha-clip).\n *\n * `data` is the resolved accessor view (Float32Array | Uint8Array | Uint16Array), `count`\n * the vertex count, and `comps` the component count (3 or 4). */\nexport function normalizeColorToVec4(data: ArrayBufferView, count: number, comps: number): Float32Array {\n const out = new Float32Array(count * 4);\n const hasAlpha = comps >= 4;\n if (data instanceof Float32Array) {\n for (let v = 0; v < count; v++) {\n out[v * 4] = data[v * comps]!;\n out[v * 4 + 1] = data[v * comps + 1]!;\n out[v * 4 + 2] = data[v * comps + 2]!;\n out[v * 4 + 3] = hasAlpha ? data[v * comps + 3]! : 1;\n }\n } else if (data instanceof Uint16Array) {\n const inv = 1 / 65535;\n for (let v = 0; v < count; v++) {\n out[v * 4] = data[v * comps]! * inv;\n out[v * 4 + 1] = data[v * comps + 1]! * inv;\n out[v * 4 + 2] = data[v * comps + 2]! * inv;\n out[v * 4 + 3] = hasAlpha ? data[v * comps + 3]! * inv : 1;\n }\n } else if (data instanceof Uint8Array) {\n const inv = 1 / 255;\n for (let v = 0; v < count; v++) {\n out[v * 4] = data[v * comps]! * inv;\n out[v * 4 + 1] = data[v * comps + 1]! * inv;\n out[v * 4 + 2] = data[v * comps + 2]! * inv;\n out[v * 4 + 3] = hasAlpha ? data[v * comps + 3]! * inv : 1;\n }\n }\n return out;\n}\n"],"names":[],"mappings":"AAuBO,SAAS,oBAAA,CAAqB,IAAA,EAAuB,KAAA,EAAe,KAAA,EAA6B;AACpG,EAAA,MAAM,GAAA,GAAM,IAAI,YAAA,CAAa,KAAA,GAAQ,CAAC,CAAA;AACtC,EAAA,MAAM,WAAW,KAAA,IAAS,CAAA;AAC1B,EAAA,IAAI,gBAAgB,YAAA,EAAc;AAC9B,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC5B,MAAA,GAAA,CAAI,CAAA,GAAI,CAAC,CAAA,GAAI,IAAA,CAAK,IAAI,KAAK,CAAA;AAC3B,MAAA,GAAA,CAAI,IAAI,CAAA,GAAI,CAAC,IAAI,IAAA,CAAK,CAAA,GAAI,QAAQ,CAAC,CAAA;AACnC,MAAA,GAAA,CAAI,IAAI,CAAA,GAAI,CAAC,IAAI,IAAA,CAAK,CAAA,GAAI,QAAQ,CAAC,CAAA;AACnC,MAAA,GAAA,CAAI,CAAA,GAAI,IAAI,CAAC,CAAA,GAAI,WAAW,IAAA,CAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,CAAA,GAAK,CAAA;AAAA,IACvD;AAAA,EACJ,CAAA,MAAA,IAAW,gBAAgB,WAAA,EAAa;AACpC,IAAA,MAAM,MAAM,CAAA,GAAI,KAAA;AAChB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC5B,MAAA,GAAA,CAAI,IAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAA,GAAI,KAAK,CAAA,GAAK,GAAA;AAChC,MAAA,GAAA,CAAI,CAAA,GAAI,IAAI,CAAC,CAAA,GAAI,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,CAAA,GAAK,GAAA;AACxC,MAAA,GAAA,CAAI,CAAA,GAAI,IAAI,CAAC,CAAA,GAAI,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,CAAA,GAAK,GAAA;AACxC,MAAA,GAAA,CAAI,CAAA,GAAI,CAAA,GAAI,CAAC,CAAA,GAAI,QAAA,GAAW,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,CAAA,GAAK,GAAA,GAAM,CAAA;AAAA,IAC7D;AAAA,EACJ,CAAA,MAAA,IAAW,gBAAgB,UAAA,EAAY;AACnC,IAAA,MAAM,MAAM,CAAA,GAAI,GAAA;AAChB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC5B,MAAA,GAAA,CAAI,IAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAA,GAAI,KAAK,CAAA,GAAK,GAAA;AAChC,MAAA,GAAA,CAAI,CAAA,GAAI,IAAI,CAAC,CAAA,GAAI,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,CAAA,GAAK,GAAA;AACxC,MAAA,GAAA,CAAI,CAAA,GAAI,IAAI,CAAC,CAAA,GAAI,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,CAAA,GAAK,GAAA;AACxC,MAAA,GAAA,CAAI,CAAA,GAAI,CAAA,GAAI,CAAC,CAAA,GAAI,QAAA,GAAW,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,CAAA,GAAK,GAAA,GAAM,CAAA;AAAA,IAC7D;AAAA,EACJ;AACA,EAAA,OAAO,GAAA;AACX;;;;"}
1
+ {"version":3,"file":"gltf-color-normalize.js","sources":["../../../src/loader-gltf/gltf-color-normalize.ts"],"sourcesContent":["/** glTF COLOR_0 vertex-color normalization — dynamically imported.\n *\n * Isolated from the core loader so scenes whose assets have no COLOR_0 attribute\n * (the common case) never bundle or fetch this code. Loaded lazily by\n * `load-gltf.ts` only when a primitive actually provides COLOR_0.\n *\n * Zero module-level side effects — safe for tree-shaking.\n */\n\n/** Normalize a glTF COLOR_0 attribute to a tight float32 VEC4 (RGBA) buffer.\n *\n * The PBR vertex pipeline binds vertex color as `float32x4` (stride 16), matching\n * the rest of the engine (procedural meshes, node/shader materials). glTF COLOR_0\n * is far more permissive — it may be VEC3 or VEC4, and its component type may be\n * float OR normalized unsigned byte/short. Binding any other layout to the stride-16\n * float pipeline misaligns every vertex (garbage / black colors). So we always\n * convert here: integer types are normalized to [0,1] (per the glTF spec, integer\n * COLOR_0 is always normalized), a VEC3 source gets alpha = 1.0, and the result is a\n * tight Float32Array RGBA. The rgb modulates base color; the alpha modulates the\n * fragment alpha (so vertex-color alpha drives alpha blending / alpha-clip).\n *\n * `data` is the resolved accessor view (Float32Array | Uint8Array | Uint16Array), `count`\n * the vertex count, and `comps` the component count (3 or 4). */\nexport function normalizeColorToVec4(data: ArrayBufferView, count: number, comps: number): Float32Array {\n const out = new Float32Array(count * 4);\n const hasAlpha = comps >= 4;\n if (data instanceof Float32Array) {\n for (let v = 0; v < count; v++) {\n out[v * 4] = data[v * comps]!;\n out[v * 4 + 1] = data[v * comps + 1]!;\n out[v * 4 + 2] = data[v * comps + 2]!;\n out[v * 4 + 3] = hasAlpha ? data[v * comps + 3]! : 1;\n }\n } else if (data instanceof Uint16Array) {\n const inv = 1 / 65535;\n for (let v = 0; v < count; v++) {\n out[v * 4] = data[v * comps]! * inv;\n out[v * 4 + 1] = data[v * comps + 1]! * inv;\n out[v * 4 + 2] = data[v * comps + 2]! * inv;\n out[v * 4 + 3] = hasAlpha ? data[v * comps + 3]! * inv : 1;\n }\n } else if (data instanceof Uint8Array) {\n const inv = 1 / 255;\n for (let v = 0; v < count; v++) {\n out[v * 4] = data[v * comps]! * inv;\n out[v * 4 + 1] = data[v * comps + 1]! * inv;\n out[v * 4 + 2] = data[v * comps + 2]! * inv;\n out[v * 4 + 3] = hasAlpha ? data[v * comps + 3]! * inv : 1;\n }\n }\n return out;\n}\n\n/** Normalize a glTF TEXCOORD_n attribute to a tight float32 VEC2 buffer.\n *\n * The PBR/standard vertex pipeline binds UVs as `float32x2`. glTF TEXCOORD may\n * be FLOAT or normalized UNSIGNED_BYTE / UNSIGNED_SHORT (glTF-Asset-Generator\n * Mesh_PrimitiveAttribute / Buffer_Interleaved). Binding an integer source to\n * the float32x2 layout misreads every vertex (garbage UVs → wrong texturing),\n * so integer UVs are normalized to [0,1] here. `data` is the resolved accessor\n * view (Float32Array | Uint8Array | Uint16Array) and `count` the vertex count. */\nexport function normalizeUvToVec2(data: ArrayBufferView, count: number): Float32Array {\n const out = new Float32Array(count * 2);\n const inv = data instanceof Uint16Array ? 1 / 65535 : data instanceof Uint8Array ? 1 / 255 : 1;\n const s = data as unknown as { [i: number]: number };\n for (let i = 0; i < count * 2; i++) {\n out[i] = s[i]! * inv;\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { F32, U8, U16, I8, I16 } from '../engine/typed-arrays.js';
1
+ import './gltf-sampler-denorm.js';
2
2
  import { PATH_WEIGHTS, PATH_SCALE, PATH_ROTATION, PATH_TRANSLATION, PATH_POINTER } from '../animation/types.js';
3
3
  import { resolveAnimationPointer } from './animation-pointer.js';
4
4
  import { _installPointerHandlers } from './gltf-animation.js';
@@ -11,6 +11,11 @@ const NODE_TRS_PATH = {
11
11
  };
12
12
  let _matMapKey = null;
13
13
  let _matMap = [];
14
+ const _LIGHT_POINTER_RE = /^\/extensions\/KHR_lights_punctual\/lights\/\d+\/(?:color|intensity|range|spot\/outerConeAngle)$/;
15
+ const _MAT_EXT_POINTER_RE = /^\/materials\/\d+\/(?:pbrMetallicRoughness\/metallicFactor|normalTexture\/scale|occlusionTexture\/strength|extensions\/KHR_materials_(?:transmission|ior|volume|iridescence)\/)/;
16
+ const _BASE_COLOR_POINTER_RE = /^\/materials\/\d+\/pbrMetallicRoughness\/baseColorFactor$/;
17
+ let _matExtMod = null;
18
+ let _baseColorMod = null;
14
19
  function materialMap(json, meshes) {
15
20
  if (meshes === _matMapKey) {
16
21
  return _matMap;
@@ -62,60 +67,75 @@ function materialMap(json, meshes) {
62
67
  }
63
68
  }
64
69
  }
70
+ _matExtMod?.seedExtMaterials(json, map);
65
71
  _matMap = map;
66
72
  return map;
67
73
  }
68
- _installPointerHandlers(
69
- (ptr, c, nodeMap, json, meshes) => {
70
- if (!nodeMap) {
71
- return null;
74
+ _installPointerHandlers((ptr, c, nodeMap, json, meshes) => {
75
+ if (!nodeMap) {
76
+ return null;
77
+ }
78
+ const trs = /^\/nodes\/(\d+)\/(translation|rotation|scale|weights)$/.exec(ptr);
79
+ if (trs) {
80
+ return { samplerIdx: c.sampler, nodeIdx: +trs[1], path: NODE_TRS_PATH[trs[2]] };
81
+ }
82
+ const resolved = resolveAnimationPointer(ptr, { nodes: nodeMap, materials: materialMap(json, meshes), _json: json });
83
+ if (!resolved) {
84
+ return null;
85
+ }
86
+ const ch = {
87
+ samplerIdx: c.sampler,
88
+ nodeIdx: -1,
89
+ path: PATH_POINTER,
90
+ pointerWriter: resolved.writer,
91
+ pointerArity: resolved.arity
92
+ };
93
+ return ch;
94
+ });
95
+ const feature = {
96
+ id: "KHR_animation_pointer",
97
+ // Raw glTF material defs whose pbrMetallicRoughness/baseColorFactor is animated.
98
+ // Collected here (where `json` is available) and consumed in applyMaterial (which
99
+ // only receives the GltfMaterialData). Both hooks live in this lazy feature module,
100
+ // so non-pointer scenes pay zero bytes for the white-fallback handling.
101
+ async preParse(json) {
102
+ let hasLightPointer = false;
103
+ let hasMatExtPointer = false;
104
+ let hasBaseColorPointer = false;
105
+ for (const anim of json.animations ?? []) {
106
+ for (const ch of anim.channels ?? []) {
107
+ const ptr = ch.target?.extensions?.KHR_animation_pointer?.pointer;
108
+ if (!ptr) {
109
+ continue;
110
+ }
111
+ if (_BASE_COLOR_POINTER_RE.test(ptr)) {
112
+ hasBaseColorPointer = true;
113
+ }
114
+ if (_LIGHT_POINTER_RE.test(ptr)) {
115
+ hasLightPointer = true;
116
+ }
117
+ if (_MAT_EXT_POINTER_RE.test(ptr)) {
118
+ hasMatExtPointer = true;
119
+ }
120
+ }
72
121
  }
73
- const trs = /^\/nodes\/(\d+)\/(translation|rotation|scale|weights)$/.exec(ptr);
74
- if (trs) {
75
- return { samplerIdx: c.sampler, nodeIdx: +trs[1], path: NODE_TRS_PATH[trs[2]] };
122
+ if (hasBaseColorPointer) {
123
+ _baseColorMod = await import('./animation-pointer-basecolor.js');
124
+ _baseColorMod.collectBaseColorDefs(json);
76
125
  }
77
- const resolved = resolveAnimationPointer(ptr, { nodes: nodeMap, materials: materialMap(json, meshes) });
78
- if (!resolved) {
79
- return null;
126
+ if (hasLightPointer) {
127
+ await import('./animation-pointer-lights.js');
80
128
  }
81
- const ch = {
82
- samplerIdx: c.sampler,
83
- nodeIdx: -1,
84
- path: PATH_POINTER,
85
- pointerWriter: resolved.writer,
86
- pointerArity: resolved.arity
87
- };
88
- return ch;
89
- },
90
- (src, length, normalized) => {
91
- const out = new F32(length);
92
- if (src instanceof F32) {
93
- for (let i = 0; i < length; i++) {
94
- out[i] = src[i];
95
- }
96
- } else if (src instanceof U8) {
97
- const k = normalized ? 1 / 255 : 1;
98
- for (let i = 0; i < length; i++) {
99
- out[i] = src[i] * k;
100
- }
101
- } else if (src instanceof U16) {
102
- const k = normalized ? 1 / 65535 : 1;
103
- for (let i = 0; i < length; i++) {
104
- out[i] = src[i] * k;
105
- }
106
- } else if (src instanceof I8) {
107
- for (let i = 0; i < length; i++) {
108
- out[i] = normalized ? Math.max(src[i] / 127, -1) : src[i];
109
- }
110
- } else if (src instanceof I16) {
111
- for (let i = 0; i < length; i++) {
112
- out[i] = normalized ? Math.max(src[i] / 32767, -1) : src[i];
113
- }
129
+ if (hasMatExtPointer) {
130
+ _matExtMod = await import('./animation-pointer-ext.js');
114
131
  }
115
- return out;
132
+ },
133
+ // Animated baseColorFactor on untextured materials needs a white 1×1 fallback so the
134
+ // factor isn't double-applied; the logic lives in the lazy base-color module loaded above.
135
+ async applyMaterial(mat) {
136
+ return _baseColorMod?.whiteFallback(mat) ?? null;
116
137
  }
117
- );
118
- const feature = { id: "KHR_animation_pointer" };
138
+ };
119
139
 
120
140
  export { feature as default };
121
141
  //# sourceMappingURL=gltf-feature-animation-pointer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-feature-animation-pointer.js","sources":["../../../src/loader-gltf/gltf-feature-animation-pointer.ts"],"sourcesContent":["/** KHR_animation_pointer glTF feature.\n *\n * Registered in the feature registry gated on `KHR_animation_pointer`, so any\n * scene that doesn't declare the extension pays zero bytes for pointer\n * resolution, the non-Float32 sampler converter, or the visibility cascade.\n *\n * On side-effect import this module installs two callbacks into gltf-animation:\n * 1. A pointer-channel parser (resolves the JSON pointer to a writer fn).\n * 2. A sampler converter that handles the non-Float32/misaligned accessor\n * cases the fast path in gltf-animation can't express (e.g. the 11-byte\n * UNSIGNED_BYTE visibility accessor in CubeVisibility.glb).\n *\n * Node-visibility and node-TRS pointers resolve here directly. Material\n * pointer targets (texture-transform offset/scale/rotation, factors, …) are\n * resolved by `resolveAnimationPointer` in animation-pointer.ts, invoked from\n * the pointer-channel parser installed below. */\n\nimport { F32, U16, I16, U8, I8 } from \"../engine/typed-arrays.js\";\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { AnimationChannel, TargetPath } from \"../animation/types.js\";\nimport { PATH_POINTER, PATH_TRANSLATION, PATH_ROTATION, PATH_SCALE, PATH_WEIGHTS } from \"../animation/types.js\";\nimport type { PointerMaterial } from \"./animation-pointer.js\";\nimport { resolveAnimationPointer } from \"./animation-pointer.js\";\nimport { _installPointerHandlers } from \"./gltf-animation.js\";\n\n// Node TRS/weights pointer targets map 1:1 onto the standard glTF channel paths.\nconst NODE_TRS_PATH: Record<string, TargetPath> = {\n translation: PATH_TRANSLATION,\n rotation: PATH_ROTATION,\n scale: PATH_SCALE,\n weights: PATH_WEIGHTS,\n};\n\n// Material pointers (texture-transform) resolve against the runtime material indexed\n// by glTF material index. Built from the same node→primitive→gpuMesh order the loader\n// uploads in, and memoized per asset (one map per `meshes` array). `mesh.material` is\n// the PbrMaterialProps carrying `_uboVersion` + the UV-transform texture slots.\nlet _matMapKey: readonly Mesh[] | null = null;\nlet _matMap: (PointerMaterial | undefined)[] = [];\nfunction materialMap(json: any, meshes: readonly Mesh[]): (PointerMaterial | undefined)[] {\n if (meshes === _matMapKey) {\n return _matMap;\n }\n _matMapKey = meshes;\n const map: (PointerMaterial | undefined)[] = [];\n\n // Collect material indices targeted by a baseColorFactor pointer. Those materials\n // must carry a baseColorFactor UBO slot for the animation to have any effect, so\n // we seed `baseColorFactor` below — this runs at load (before the first render\n // computes material flags), forcing PBR2_HAS_BASE_COLOR_FACTOR on.\n const baseColorAnimated = new Set<number>();\n // Materials whose texture UV transform is animated. The loader only enables the\n // UV-transform machinery (PBR2_HAS_UV_TRANSFORM) when a texture carries a\n // *non-identity* static KHR_texture_transform. A material whose transform is\n // identity at load but animated at runtime (e.g. an occlusion rotation that\n // starts at 0) would otherwise compile without the per-texture UV matrices, so\n // the animation writes a transform the shader never samples. Force the flag for\n // these materials so the animation actually drives the UV.\n const uvTransformAnimated = new Set<number>();\n for (const anim of json.animations ?? []) {\n for (const ch of anim.channels ?? []) {\n const ptr = ch.target?.extensions?.KHR_animation_pointer?.pointer as string | undefined;\n const m = ptr && /^\\/materials\\/(\\d+)\\/pbrMetallicRoughness\\/baseColorFactor$/.exec(ptr);\n if (m) {\n baseColorAnimated.add(+m[1]!);\n }\n const tx = ptr && /^\\/materials\\/(\\d+)\\/.*\\/KHR_texture_transform\\/(offset|scale|rotation)$/.exec(ptr);\n if (tx) {\n uvTransformAnimated.add(+tx[1]!);\n }\n }\n }\n\n const nodes = json.nodes ?? [];\n let gpuIdx = 0;\n for (let ni = 0; ni < nodes.length; ni++) {\n const meshRef = nodes[ni]?.mesh;\n if (meshRef === undefined) {\n continue;\n }\n const prims = json.meshes?.[meshRef]?.primitives ?? [];\n for (let p = 0; p < prims.length; p++) {\n const matIdx = prims[p]?.material;\n const mesh = meshes[gpuIdx++];\n if (matIdx !== undefined && mesh) {\n const pm = mesh.material as unknown as PointerMaterial;\n const def = json.materials?.[matIdx];\n // Seed the separated emissive factor/strength from the asset so an\n // emissiveFactor or emissiveStrength pointer can recombine them\n // (emissiveColor is stored pre-multiplied at load).\n if (def && pm.emissiveColor) {\n const ef = def.emissiveFactor ?? [0, 0, 0];\n pm._animEmissiveFactor = [ef[0] ?? 0, ef[1] ?? 0, ef[2] ?? 0];\n pm._animEmissiveStrength = def.extensions?.KHR_materials_emissive_strength?.emissiveStrength ?? 1;\n }\n // Force a baseColorFactor slot when a pointer animates it (the loader\n // omits it for untextured/default materials).\n if (baseColorAnimated.has(matIdx) && !pm.baseColorFactor) {\n const bcf = def?.pbrMetallicRoughness?.baseColorFactor ?? [1, 1, 1, 1];\n pm.baseColorFactor = [bcf[0] ?? 1, bcf[1] ?? 1, bcf[2] ?? 1, bcf[3] ?? 1];\n }\n // Force the per-texture UV-transform machinery when a pointer animates a\n // texture transform that is identity at load (so the matrices exist for the\n // animation to drive — see uvTransformAnimated above).\n if (uvTransformAnimated.has(matIdx)) {\n (pm as { _hasUvTx?: boolean })._hasUvTx = true;\n }\n map[matIdx] = pm;\n }\n }\n }\n _matMap = map;\n return map;\n}\n\n_installPointerHandlers(\n (ptr, c, nodeMap, json, meshes) => {\n if (!nodeMap) {\n return null;\n }\n // A /nodes/{n}/{translation|rotation|scale|weights} pointer is semantically\n // identical to a standard glTF channel on node n. Emit a standard channel so it\n // flows through the proven topological node-TRS / morph writeback (which moves the\n // node AND its descendants) instead of an opaque per-node writer.\n const trs = /^\\/nodes\\/(\\d+)\\/(translation|rotation|scale|weights)$/.exec(ptr);\n if (trs) {\n return { samplerIdx: c.sampler, nodeIdx: +trs[1]!, path: NODE_TRS_PATH[trs[2]!]! };\n }\n // Only build the material map when a non-node pointer is actually present.\n const resolved = resolveAnimationPointer(ptr, { nodes: nodeMap, materials: materialMap(json, meshes) });\n if (!resolved) {\n return null;\n }\n const ch: AnimationChannel = {\n samplerIdx: c.sampler,\n nodeIdx: -1,\n path: PATH_POINTER,\n pointerWriter: resolved.writer,\n pointerArity: resolved.arity,\n };\n return ch;\n },\n (src, length, normalized) => {\n // Convert any animation-sampler payload to a standalone Float32Array.\n // Handles the cases the aligned-Float32 fast path can't express.\n const out = new F32(length);\n if (src instanceof F32) {\n for (let i = 0; i < length; i++) {\n out[i] = src[i]!;\n }\n } else if (src instanceof U8) {\n const k = normalized ? 1 / 255 : 1;\n for (let i = 0; i < length; i++) {\n out[i] = src[i]! * k;\n }\n } else if (src instanceof U16) {\n const k = normalized ? 1 / 65535 : 1;\n for (let i = 0; i < length; i++) {\n out[i] = src[i]! * k;\n }\n } else if (src instanceof I8) {\n for (let i = 0; i < length; i++) {\n out[i] = normalized ? Math.max(src[i]! / 127, -1) : src[i]!;\n }\n } else if (src instanceof I16) {\n for (let i = 0; i < length; i++) {\n out[i] = normalized ? Math.max(src[i]! / 32767, -1) : src[i]!;\n }\n }\n return out;\n }\n);\n\nconst feature: GltfFeature = { id: \"KHR_animation_pointer\" };\nexport default feature;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-feature-animation-pointer.js","sources":["../../../src/loader-gltf/gltf-feature-animation-pointer.ts"],"sourcesContent":["/** KHR_animation_pointer glTF feature.\n *\n * Registered in the feature registry gated on `KHR_animation_pointer`, so any\n * scene that doesn't declare the extension pays zero bytes for pointer\n * resolution, the non-Float32 sampler converter, or the visibility cascade.\n *\n * On side-effect import this module installs a pointer-channel parser into\n * gltf-animation (resolves the JSON pointer to a writer fn) and pulls in the\n * lazy sampler converter (`gltf-sampler-denorm`) that handles the\n * non-Float32/misaligned accessor cases the fast path can't express (e.g. the\n * 11-byte UNSIGNED_BYTE visibility accessor in CubeVisibility.glb).\n *\n * Node-visibility and node-TRS pointers resolve here directly. Material\n * pointer targets (texture-transform offset/scale/rotation, factors, …) are\n * resolved by `resolveAnimationPointer` in animation-pointer.ts, invoked from\n * the pointer-channel parser installed below. */\n\nimport \"./gltf-sampler-denorm.js\";\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { AnimationChannel, TargetPath } from \"../animation/types.js\";\nimport { PATH_POINTER, PATH_TRANSLATION, PATH_ROTATION, PATH_SCALE, PATH_WEIGHTS } from \"../animation/types.js\";\nimport type { PointerMaterial } from \"./animation-pointer.js\";\nimport { resolveAnimationPointer } from \"./animation-pointer.js\";\nimport { _installPointerHandlers } from \"./gltf-animation.js\";\n\n// Node TRS/weights pointer targets map 1:1 onto the standard glTF channel paths.\nconst NODE_TRS_PATH: Record<string, TargetPath> = {\n translation: PATH_TRANSLATION,\n rotation: PATH_ROTATION,\n scale: PATH_SCALE,\n weights: PATH_WEIGHTS,\n};\n\n// Material pointers (texture-transform) resolve against the runtime material indexed\n// by glTF material index. Built from the same node→primitive→gpuMesh order the loader\n// uploads in, and memoized per asset (one map per `meshes` array). `mesh.material` is\n// the PbrMaterialProps carrying `_uboVersion` + the UV-transform texture slots.\nlet _matMapKey: readonly Mesh[] | null = null;\nlet _matMap: (PointerMaterial | undefined)[] = [];\n\n// Punctual-light property pointers — writers live in animation-pointer-lights.ts.\nconst _LIGHT_POINTER_RE = /^\\/extensions\\/KHR_lights_punctual\\/lights\\/\\d+\\/(?:color|intensity|range|spot\\/outerConeAngle)$/;\n// Material factor / extension pointers — writers + load-time seeding live in\n// animation-pointer-ext.ts.\nconst _MAT_EXT_POINTER_RE =\n /^\\/materials\\/\\d+\\/(?:pbrMetallicRoughness\\/metallicFactor|normalTexture\\/scale|occlusionTexture\\/strength|extensions\\/KHR_materials_(?:transmission|ior|volume|iridescence)\\/)/;\n// Animated baseColorFactor white-fallback pointer — handling lives in\n// animation-pointer-basecolor.ts.\nconst _BASE_COLOR_POINTER_RE = /^\\/materials\\/\\d+\\/pbrMetallicRoughness\\/baseColorFactor$/;\n\n// Populated in preParse from the lazily-imported sub-modules, so materialMap + applyMaterial\n// can delegate without re-importing. Each sub-module is fetched only when its pointer is\n// present, so a node-only scene (scene34) loads none of them.\nlet _matExtMod: typeof import(\"./animation-pointer-ext.js\") | null = null;\nlet _baseColorMod: typeof import(\"./animation-pointer-basecolor.js\") | null = null;\nfunction materialMap(json: any, meshes: readonly Mesh[]): (PointerMaterial | undefined)[] {\n if (meshes === _matMapKey) {\n return _matMap;\n }\n _matMapKey = meshes;\n const map: (PointerMaterial | undefined)[] = [];\n\n // Collect material indices targeted by a baseColorFactor pointer. Those materials\n // must carry a baseColorFactor UBO slot for the animation to have any effect, so\n // we seed `baseColorFactor` below — this runs at load (before the first render\n // computes material flags), forcing PBR2_HAS_BASE_COLOR_FACTOR on.\n const baseColorAnimated = new Set<number>();\n // Materials whose texture UV transform is animated. The loader only enables the\n // UV-transform machinery (PBR2_HAS_UV_TRANSFORM) when a texture carries a\n // *non-identity* static KHR_texture_transform. A material whose transform is\n // identity at load but animated at runtime (e.g. an occlusion rotation that\n // starts at 0) would otherwise compile without the per-texture UV matrices, so\n // the animation writes a transform the shader never samples. Force the flag for\n // these materials so the animation actually drives the UV.\n const uvTransformAnimated = new Set<number>();\n for (const anim of json.animations ?? []) {\n for (const ch of anim.channels ?? []) {\n const ptr = ch.target?.extensions?.KHR_animation_pointer?.pointer as string | undefined;\n const m = ptr && /^\\/materials\\/(\\d+)\\/pbrMetallicRoughness\\/baseColorFactor$/.exec(ptr);\n if (m) {\n baseColorAnimated.add(+m[1]!);\n }\n const tx = ptr && /^\\/materials\\/(\\d+)\\/.*\\/KHR_texture_transform\\/(offset|scale|rotation)$/.exec(ptr);\n if (tx) {\n uvTransformAnimated.add(+tx[1]!);\n }\n }\n }\n\n const nodes = json.nodes ?? [];\n let gpuIdx = 0;\n for (let ni = 0; ni < nodes.length; ni++) {\n const meshRef = nodes[ni]?.mesh;\n if (meshRef === undefined) {\n continue;\n }\n const prims = json.meshes?.[meshRef]?.primitives ?? [];\n for (let p = 0; p < prims.length; p++) {\n const matIdx = prims[p]?.material;\n const mesh = meshes[gpuIdx++];\n if (matIdx !== undefined && mesh) {\n const pm = mesh.material as unknown as PointerMaterial;\n const def = json.materials?.[matIdx];\n // Seed the separated emissive factor/strength from the asset so an\n // emissiveFactor or emissiveStrength pointer can recombine them\n // (emissiveColor is stored pre-multiplied at load).\n if (def && pm.emissiveColor) {\n const ef = def.emissiveFactor ?? [0, 0, 0];\n pm._animEmissiveFactor = [ef[0] ?? 0, ef[1] ?? 0, ef[2] ?? 0];\n pm._animEmissiveStrength = def.extensions?.KHR_materials_emissive_strength?.emissiveStrength ?? 1;\n }\n // Force a baseColorFactor slot when a pointer animates it (the loader\n // omits it for untextured/default materials).\n if (baseColorAnimated.has(matIdx) && !pm.baseColorFactor) {\n const bcf = def?.pbrMetallicRoughness?.baseColorFactor ?? [1, 1, 1, 1];\n pm.baseColorFactor = [bcf[0] ?? 1, bcf[1] ?? 1, bcf[2] ?? 1, bcf[3] ?? 1];\n }\n // Force the per-texture UV-transform machinery when a pointer animates a\n // texture transform that is identity at load (so the matrices exist for the\n // animation to drive — see uvTransformAnimated above).\n if (uvTransformAnimated.has(matIdx)) {\n (pm as { _hasUvTx?: boolean })._hasUvTx = true;\n }\n map[matIdx] = pm;\n }\n }\n }\n // Material factor / extension seeding (transmission, IOR, volume, occlusion strength)\n // lives in the lazy module loaded by preParse only when such a pointer is present.\n _matExtMod?.seedExtMaterials(json, map);\n _matMap = map;\n return map;\n}\n\n_installPointerHandlers((ptr, c, nodeMap, json, meshes) => {\n if (!nodeMap) {\n return null;\n }\n // A /nodes/{n}/{translation|rotation|scale|weights} pointer is semantically\n // identical to a standard glTF channel on node n. Emit a standard channel so it\n // flows through the proven topological node-TRS / morph writeback (which moves the\n // node AND its descendants) instead of an opaque per-node writer.\n const trs = /^\\/nodes\\/(\\d+)\\/(translation|rotation|scale|weights)$/.exec(ptr);\n if (trs) {\n return { samplerIdx: c.sampler, nodeIdx: +trs[1]!, path: NODE_TRS_PATH[trs[2]!]! };\n }\n // Only build the material map when a non-node pointer is actually present.\n const resolved = resolveAnimationPointer(ptr, { nodes: nodeMap, materials: materialMap(json, meshes), _json: json });\n if (!resolved) {\n return null;\n }\n const ch: AnimationChannel = {\n samplerIdx: c.sampler,\n nodeIdx: -1,\n path: PATH_POINTER,\n pointerWriter: resolved.writer,\n pointerArity: resolved.arity,\n };\n return ch;\n});\n\nconst feature: GltfFeature = {\n id: \"KHR_animation_pointer\",\n // Raw glTF material defs whose pbrMetallicRoughness/baseColorFactor is animated.\n // Collected here (where `json` is available) and consumed in applyMaterial (which\n // only receives the GltfMaterialData). Both hooks live in this lazy feature module,\n // so non-pointer scenes pay zero bytes for the white-fallback handling.\n async preParse(json: any) {\n let hasLightPointer = false;\n let hasMatExtPointer = false;\n let hasBaseColorPointer = false;\n for (const anim of json.animations ?? []) {\n for (const ch of anim.channels ?? []) {\n const ptr = ch.target?.extensions?.KHR_animation_pointer?.pointer as string | undefined;\n if (!ptr) {\n continue;\n }\n if (_BASE_COLOR_POINTER_RE.test(ptr)) {\n hasBaseColorPointer = true;\n }\n if (_LIGHT_POINTER_RE.test(ptr)) {\n hasLightPointer = true;\n }\n if (_MAT_EXT_POINTER_RE.test(ptr)) {\n hasMatExtPointer = true;\n }\n }\n }\n // Each pointer-writer set lives in its own module fetched only when its pointer is\n // present, so a scene that animates just node visibility (scene34) or only lights\n // (scene39) never loads the others — minimal bundle movement for the unused features.\n if (hasBaseColorPointer) {\n _baseColorMod = await import(\"./animation-pointer-basecolor.js\");\n _baseColorMod.collectBaseColorDefs(json);\n }\n if (hasLightPointer) {\n await import(\"./animation-pointer-lights.js\");\n }\n if (hasMatExtPointer) {\n _matExtMod = await import(\"./animation-pointer-ext.js\");\n }\n },\n // Animated baseColorFactor on untextured materials needs a white 1×1 fallback so the\n // factor isn't double-applied; the logic lives in the lazy base-color module loaded above.\n async applyMaterial(mat) {\n return _baseColorMod?.whiteFallback(mat) ?? null;\n },\n};\nexport default feature;\n"],"names":[],"mappings":"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"}