@babylonjs/lite 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/index.js +381 -375
  2. package/dist/index.js.map +1 -1
  3. package/index.d.ts +757 -0
  4. package/lib/audio/analyzer.js +65 -0
  5. package/lib/audio/analyzer.js.map +1 -0
  6. package/lib/audio/audio-bus.js +38 -0
  7. package/lib/audio/audio-bus.js.map +1 -0
  8. package/lib/audio/audio-engine.js +188 -0
  9. package/lib/audio/audio-engine.js.map +1 -0
  10. package/lib/audio/audio-fetch.js +18 -0
  11. package/lib/audio/audio-fetch.js.map +1 -0
  12. package/lib/audio/audio-param.js +96 -0
  13. package/lib/audio/audio-param.js.map +1 -0
  14. package/lib/audio/audio-signal.js +46 -0
  15. package/lib/audio/audio-signal.js.map +1 -0
  16. package/lib/audio/bus.js +33 -0
  17. package/lib/audio/bus.js.map +1 -0
  18. package/lib/audio/host-types.js +2 -0
  19. package/lib/audio/host-types.js.map +1 -0
  20. package/lib/audio/index.js +12 -0
  21. package/lib/audio/index.js.map +1 -0
  22. package/lib/audio/sound-buffer.js +59 -0
  23. package/lib/audio/sound-buffer.js.map +1 -0
  24. package/lib/audio/sound-source.js +57 -0
  25. package/lib/audio/sound-source.js.map +1 -0
  26. package/lib/audio/sound-sub-graph.js +72 -0
  27. package/lib/audio/sound-sub-graph.js.map +1 -0
  28. package/lib/audio/spatial.js +466 -0
  29. package/lib/audio/spatial.js.map +1 -0
  30. package/lib/audio/static-sound.js +313 -0
  31. package/lib/audio/static-sound.js.map +1 -0
  32. package/lib/audio/stereo.js +40 -0
  33. package/lib/audio/stereo.js.map +1 -0
  34. package/lib/audio/streaming-sound.js +377 -0
  35. package/lib/audio/streaming-sound.js.map +1 -0
  36. package/lib/audio/unmute-ui.js +72 -0
  37. package/lib/audio/unmute-ui.js.map +1 -0
  38. package/lib/audio/visualizer.js +101 -0
  39. package/lib/audio/visualizer.js.map +1 -0
  40. package/lib/engine/engine.js +1 -1
  41. package/lib/index.js +11 -0
  42. package/lib/index.js.map +1 -1
  43. package/lib/light/types.js.map +1 -1
  44. package/lib/loader-gltf/animation-pointer-basecolor.js +25 -0
  45. package/lib/loader-gltf/animation-pointer-basecolor.js.map +1 -0
  46. package/lib/loader-gltf/animation-pointer-ext.js +244 -0
  47. package/lib/loader-gltf/animation-pointer-ext.js.map +1 -0
  48. package/lib/loader-gltf/animation-pointer-lights.js +46 -0
  49. package/lib/loader-gltf/animation-pointer-lights.js.map +1 -0
  50. package/lib/loader-gltf/animation-pointer.js +4 -1
  51. package/lib/loader-gltf/animation-pointer.js.map +1 -1
  52. package/lib/loader-gltf/gltf-animation.js +5 -3
  53. package/lib/loader-gltf/gltf-animation.js.map +1 -1
  54. package/lib/loader-gltf/gltf-color-normalize.js +10 -1
  55. package/lib/loader-gltf/gltf-color-normalize.js.map +1 -1
  56. package/lib/loader-gltf/gltf-feature-animation-pointer.js +67 -47
  57. package/lib/loader-gltf/gltf-feature-animation-pointer.js.map +1 -1
  58. package/lib/loader-gltf/gltf-feature-lights-punctual.js +51 -9
  59. package/lib/loader-gltf/gltf-feature-lights-punctual.js.map +1 -1
  60. package/lib/loader-gltf/gltf-feature-primitive.js +20 -0
  61. package/lib/loader-gltf/gltf-feature-primitive.js.map +1 -0
  62. package/lib/loader-gltf/gltf-feature-registry.js +25 -0
  63. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  64. package/lib/loader-gltf/gltf-feature-skeleton.js +18 -3
  65. package/lib/loader-gltf/gltf-feature-skeleton.js.map +1 -1
  66. package/lib/loader-gltf/gltf-interleave.js +3 -2
  67. package/lib/loader-gltf/gltf-interleave.js.map +1 -1
  68. package/lib/loader-gltf/gltf-light-pointer-state.js +18 -0
  69. package/lib/loader-gltf/gltf-light-pointer-state.js.map +1 -0
  70. package/lib/loader-gltf/gltf-parser.js +7 -1
  71. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  72. package/lib/loader-gltf/gltf-pbr-builder-ext.js +1 -1
  73. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  74. package/lib/loader-gltf/gltf-pbr-builder.js +1 -1
  75. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  76. package/lib/loader-gltf/gltf-sampler-denorm.js +20 -0
  77. package/lib/loader-gltf/gltf-sampler-denorm.js.map +1 -0
  78. package/lib/loader-gltf/gltf-sampler-desc.js +11 -2
  79. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  80. package/lib/loader-gltf/gltf-uv-denorm.js +28 -0
  81. package/lib/loader-gltf/gltf-uv-denorm.js.map +1 -0
  82. package/lib/loader-gltf/load-gltf.js +15 -6
  83. package/lib/loader-gltf/load-gltf.js.map +1 -1
  84. package/lib/material/material-rebuild.js +4 -0
  85. package/lib/material/material-rebuild.js.map +1 -1
  86. package/lib/material/mesh-features.js +8 -1
  87. package/lib/material/mesh-features.js.map +1 -1
  88. package/lib/material/pbr/fragments/reflectance-fragment.js +1 -1
  89. package/lib/material/pbr/fragments/reflectance-fragment.js.map +1 -1
  90. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +1 -1
  91. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  92. package/lib/material/pbr/pbr-pipeline.js +7 -3
  93. package/lib/material/pbr/pbr-pipeline.js.map +1 -1
  94. package/lib/material/pbr/pbr-primitive-resolver.js +34 -0
  95. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -0
  96. package/lib/material/pbr/pbr-renderable.js +1 -1
  97. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  98. package/lib/material/shader/shader-material.js +9 -5
  99. package/lib/material/shader/shader-material.js.map +1 -1
  100. package/lib/material/shader/shader-thin-instance.js +1 -1
  101. package/lib/material/shader/shader-thin-instance.js.map +1 -1
  102. package/lib/material/standard/standard-renderable.js +1 -1
  103. package/lib/material/standard/standard-renderable.js.map +1 -1
  104. package/lib/mesh/mesh-dispose.js +1 -0
  105. package/lib/mesh/mesh-dispose.js.map +1 -1
  106. package/lib/mesh/thin-instance-cull-binding.js +15 -6
  107. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  108. package/lib/scene/scene-core.js +1 -0
  109. package/lib/scene/scene-core.js.map +1 -1
  110. package/lib/scene/scene-material-swap.js +2 -0
  111. package/lib/scene/scene-material-swap.js.map +1 -1
  112. package/lib/shadow/csm-shadow-task-hooks.js +67 -9
  113. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  114. package/lib/sprite/sprite-2d.js +4 -0
  115. package/lib/sprite/sprite-2d.js.map +1 -1
  116. package/lib/sprite/sprite-pipeline.js +25 -22
  117. package/lib/sprite/sprite-pipeline.js.map +1 -1
  118. package/lib/text/_gpu/text-pipeline.js +1 -1
  119. package/lib/text/_gpu/text-pipeline.js.map +1 -1
  120. package/lib/text/text-renderer.js +3 -1
  121. package/lib/text/text-renderer.js.map +1 -1
  122. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-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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"}
@@ -1,6 +1,25 @@
1
1
  import { MAX_LIGHTS, setMaxLights } from '../light/types.js';
2
2
  import { computeNodeWorldMatrix } from './gltf-parser.js';
3
+ import { setGltfPunctualLight } from './gltf-light-pointer-state.js';
3
4
 
5
+ function bindAnimatedLightVersion(light) {
6
+ const baseGet = Object.getOwnPropertyDescriptor(light, "_lightVersion")?.get;
7
+ let extra = 0;
8
+ Object.defineProperty(light, "_lightVersion", {
9
+ get() {
10
+ return (baseGet ? baseGet.call(light) : 0) + (light.worldMatrixVersion ?? 0) + extra;
11
+ },
12
+ enumerable: false,
13
+ configurable: true
14
+ });
15
+ Object.defineProperty(light, "_bumpLightVersion", {
16
+ value: () => {
17
+ extra++;
18
+ },
19
+ enumerable: false,
20
+ configurable: true
21
+ });
22
+ }
4
23
  const feature = {
5
24
  id: "KHR_lights_punctual",
6
25
  async applyAsset(_meshes, _root, ctx) {
@@ -28,15 +47,23 @@ const feature = {
28
47
  if (!def) {
29
48
  continue;
30
49
  }
31
- const world = computeNodeWorldMatrix(ctx._json, nodeIdx, ctx._parentMap, ctx._worldMatrixCache);
32
- const px = world[12];
33
- const py = world[13];
34
- const pz = world[14];
35
- const fx = -world[8];
36
- const fy = -world[9];
37
- const fz = -world[10];
38
- const flen = Math.hypot(fx, fy, fz) || 1;
39
- const dir = [fx / flen, fy / flen, fz / flen];
50
+ const sourceNode = ctx._nodeMap?.[nodeIdx];
51
+ let px, py, pz;
52
+ let dir;
53
+ if (sourceNode) {
54
+ px = py = pz = 0;
55
+ dir = [0, 0, -1];
56
+ } else {
57
+ const world = computeNodeWorldMatrix(ctx._json, nodeIdx, ctx._parentMap, ctx._worldMatrixCache);
58
+ px = world[12];
59
+ py = world[13];
60
+ pz = world[14];
61
+ const fx = -world[8];
62
+ const fy = -world[9];
63
+ const fz = -world[10];
64
+ const flen = Math.hypot(fx, fy, fz) || 1;
65
+ dir = [fx / flen, fy / flen, fz / flen];
66
+ }
40
67
  const color = def.color ? [def.color[0], def.color[1], def.color[2]] : [1, 1, 1];
41
68
  const intensity = def.intensity ?? 1;
42
69
  const range = def.range !== void 0 ? def.range : Number.MAX_VALUE;
@@ -46,13 +73,23 @@ const feature = {
46
73
  pl.diffuse = color;
47
74
  pl.specular = color;
48
75
  pl.range = range;
76
+ if (sourceNode) {
77
+ pl.parent = sourceNode;
78
+ }
79
+ bindAnimatedLightVersion(pl);
49
80
  lights.push(pl);
81
+ setGltfPunctualLight(ctx._json, lightIdx, pl);
50
82
  } else if (def.type === "directional") {
51
83
  const { createDirectionalLight } = await import('../light/directional-light.js');
52
84
  const dl = createDirectionalLight(dir, intensity);
53
85
  dl.diffuse = color;
54
86
  dl.specular = color;
87
+ if (sourceNode) {
88
+ dl.parent = sourceNode;
89
+ }
90
+ bindAnimatedLightVersion(dl);
55
91
  lights.push(dl);
92
+ setGltfPunctualLight(ctx._json, lightIdx, dl);
56
93
  } else if (def.type === "spot") {
57
94
  const { createSpotLight } = await import('../light/spot-light.js');
58
95
  const outer = def.spot?.outerConeAngle ?? Math.PI / 4;
@@ -60,7 +97,12 @@ const feature = {
60
97
  sl.diffuse = color;
61
98
  sl.specular = color;
62
99
  sl.range = range;
100
+ if (sourceNode) {
101
+ sl.parent = sourceNode;
102
+ }
103
+ bindAnimatedLightVersion(sl);
63
104
  lights.push(sl);
105
+ setGltfPunctualLight(ctx._json, lightIdx, sl);
64
106
  }
65
107
  }
66
108
  return { entities: lights };
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-feature-lights-punctual.js","sources":["../../../src/loader-gltf/gltf-feature-lights-punctual.ts"],"sourcesContent":["/** KHR_lights_punctual glTF extension.\n * Parses point / directional / spot lights from the asset's top-level\n * KHR_lights_punctual.lights array and instantiates one Lite light per\n * referencing node, baking the node's world transform into the light's\n * position / direction. Lights are contributed via the AssetContainer's\n * `entities` array; addToScene() picks them up through the `lightType`\n * branch of its traversal. */\n\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport type { LightBase } from \"../light/types.js\";\nimport { MAX_LIGHTS, setMaxLights } from \"../light/types.js\";\nimport { computeNodeWorldMatrix } from \"./gltf-parser.js\";\n\ninterface GltfLightDef {\n type: \"point\" | \"directional\" | \"spot\";\n color?: [number, number, number];\n intensity?: number;\n range?: number;\n spot?: { innerConeAngle?: number; outerConeAngle?: number };\n}\n\nconst feature: GltfFeature = {\n id: \"KHR_lights_punctual\",\n async applyAsset(_meshes, _root, ctx) {\n const defs: GltfLightDef[] | undefined = ctx._json.extensions?.KHR_lights_punctual?.lights;\n if (!defs?.length) {\n return {};\n }\n const lights: LightBase[] = [];\n const nodes = ctx._json.nodes ?? [];\n // Count lights first so we can raise MAX_LIGHTS before any pipeline is\n // compiled (loadGltf runs before addToScene which triggers pipeline\n // creation). Lite's MAX_LIGHTS is scene-wide, so cover every punctual\n // light declared by the asset rather than Babylon's per-material cap.\n let lightNodeCount = 0;\n for (let i = 0; i < nodes.length; i++) {\n if (nodes[i]?.extensions?.KHR_lights_punctual?.light !== undefined) {\n lightNodeCount++;\n }\n }\n if (lightNodeCount > MAX_LIGHTS) {\n setMaxLights(lightNodeCount);\n }\n for (let nodeIdx = 0; nodeIdx < nodes.length; nodeIdx++) {\n const lightIdx: number | undefined = nodes[nodeIdx]?.extensions?.KHR_lights_punctual?.light;\n if (lightIdx === undefined) {\n continue;\n }\n const def = defs[lightIdx];\n if (!def) {\n continue;\n }\n const world = computeNodeWorldMatrix(ctx._json, nodeIdx, ctx._parentMap, ctx._worldMatrixCache);\n const px = world[12]!;\n const py = world[13]!;\n const pz = world[14]!;\n // glTF convention: light forward is -Z in local space. Extract world-space forward\n // by transforming (0,0,-1) through the node's upper-3x3. Normalize defensively.\n const fx = -world[8]!;\n const fy = -world[9]!;\n const fz = -world[10]!;\n const flen = Math.hypot(fx, fy, fz) || 1;\n const dir: [number, number, number] = [fx / flen, fy / flen, fz / flen];\n const color: [number, number, number] = def.color ? [def.color[0]!, def.color[1]!, def.color[2]!] : [1, 1, 1];\n const intensity = def.intensity ?? 1;\n const range = def.range !== undefined ? def.range : Number.MAX_VALUE;\n\n if (def.type === \"point\") {\n const { createPointLight } = await import(\"../light/point-light.js\");\n const pl = createPointLight([px, py, pz], intensity);\n pl.diffuse = color;\n pl.specular = color;\n pl.range = range;\n lights.push(pl);\n } else if (def.type === \"directional\") {\n const { createDirectionalLight } = await import(\"../light/directional-light.js\");\n const dl = createDirectionalLight(dir, intensity);\n dl.diffuse = color;\n dl.specular = color;\n lights.push(dl);\n } else if (def.type === \"spot\") {\n const { createSpotLight } = await import(\"../light/spot-light.js\");\n const outer = def.spot?.outerConeAngle ?? Math.PI / 4;\n const sl = createSpotLight([px, py, pz], dir, outer * 2, 1, intensity);\n sl.diffuse = color;\n sl.specular = color;\n sl.range = range;\n lights.push(sl);\n }\n }\n return { entities: lights };\n },\n};\nexport default feature;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gltf-feature-lights-punctual.js","sources":["../../../src/loader-gltf/gltf-feature-lights-punctual.ts"],"sourcesContent":["/** KHR_lights_punctual glTF extension.\n * Parses point / directional / spot lights from the asset's top-level\n * KHR_lights_punctual.lights array and instantiates one Lite light per\n * referencing node, baking the node's world transform into the light's\n * position / direction. Lights are contributed via the AssetContainer's\n * `entities` array; addToScene() picks them up through the `lightType`\n * branch of its traversal. */\n\nimport type { GltfFeature } from \"./gltf-feature.js\";\nimport type { LightBase } from \"../light/types.js\";\nimport { MAX_LIGHTS, setMaxLights } from \"../light/types.js\";\nimport { computeNodeWorldMatrix } from \"./gltf-parser.js\";\nimport { setGltfPunctualLight } from \"./gltf-light-pointer-state.js\";\n\ninterface GltfLightDef {\n type: \"point\" | \"directional\" | \"spot\";\n color?: [number, number, number];\n intensity?: number;\n range?: number;\n spot?: { innerConeAngle?: number; outerConeAngle?: number };\n}\n\n/** Fold the light's `worldMatrixVersion` (ancestor / animated-node motion) and a bump\n * counter into its `_lightVersion`. The shared lights-UBO refresh (computeLightsVersion\n * sums `_lightVersion`) then picks up an animated light NODE (KHR_animation_pointer node\n * TRS) and direct color/intensity/range pointer writes — with NO change to the core light\n * or lights-UBO code. All cost lives in this lazy KHR_lights_punctual feature, so scenes\n * without punctual lights stay byte-identical. Harmless for static lights (constant offset). */\nfunction bindAnimatedLightVersion(light: LightBase & { worldMatrixVersion?: number }): void {\n const baseGet = Object.getOwnPropertyDescriptor(light, \"_lightVersion\")?.get;\n let extra = 0;\n Object.defineProperty(light, \"_lightVersion\", {\n get(): number {\n return (baseGet ? (baseGet.call(light) as number) : 0) + (light.worldMatrixVersion ?? 0) + extra;\n },\n enumerable: false,\n configurable: true,\n });\n Object.defineProperty(light, \"_bumpLightVersion\", {\n value: () => {\n extra++;\n },\n enumerable: false,\n configurable: true,\n });\n}\n\nconst feature: GltfFeature = {\n id: \"KHR_lights_punctual\",\n async applyAsset(_meshes, _root, ctx) {\n const defs: GltfLightDef[] | undefined = ctx._json.extensions?.KHR_lights_punctual?.lights;\n if (!defs?.length) {\n return {};\n }\n const lights: LightBase[] = [];\n const nodes = ctx._json.nodes ?? [];\n // Count lights first so we can raise MAX_LIGHTS before any pipeline is\n // compiled (loadGltf runs before addToScene which triggers pipeline\n // creation). Lite's MAX_LIGHTS is scene-wide, so cover every punctual\n // light declared by the asset rather than Babylon's per-material cap.\n let lightNodeCount = 0;\n for (let i = 0; i < nodes.length; i++) {\n if (nodes[i]?.extensions?.KHR_lights_punctual?.light !== undefined) {\n lightNodeCount++;\n }\n }\n if (lightNodeCount > MAX_LIGHTS) {\n setMaxLights(lightNodeCount);\n }\n for (let nodeIdx = 0; nodeIdx < nodes.length; nodeIdx++) {\n const lightIdx: number | undefined = nodes[nodeIdx]?.extensions?.KHR_lights_punctual?.light;\n if (lightIdx === undefined) {\n continue;\n }\n const def = defs[lightIdx];\n if (!def) {\n continue;\n }\n // Prefer parenting the light to its source SceneNode so that an animated\n // light node (KHR_animation_pointer / node TRS channels) drives the light's\n // world position + direction every frame. The light then carries only its\n // glTF-local transform (origin, forward = -Z) and inherits the node's world\n // matrix via the shared parent chain. Falls back to baking the world\n // transform when the node is unreachable (no SceneNode), which keeps the\n // exact previous behaviour for assets without a node hierarchy.\n const sourceNode = ctx._nodeMap?.[nodeIdx];\n let px: number, py: number, pz: number;\n let dir: [number, number, number];\n if (sourceNode) {\n // Local: at node origin, forward = -Z. World = nodeWorld · local.\n px = py = pz = 0;\n dir = [0, 0, -1];\n } else {\n const world = computeNodeWorldMatrix(ctx._json, nodeIdx, ctx._parentMap, ctx._worldMatrixCache);\n px = world[12]!;\n py = world[13]!;\n pz = world[14]!;\n // glTF convention: light forward is -Z in local space. Extract world-space forward\n // by transforming (0,0,-1) through the node's upper-3x3. Normalize defensively.\n const fx = -world[8]!;\n const fy = -world[9]!;\n const fz = -world[10]!;\n const flen = Math.hypot(fx, fy, fz) || 1;\n dir = [fx / flen, fy / flen, fz / flen];\n }\n const color: [number, number, number] = def.color ? [def.color[0]!, def.color[1]!, def.color[2]!] : [1, 1, 1];\n const intensity = def.intensity ?? 1;\n const range = def.range !== undefined ? def.range : Number.MAX_VALUE;\n\n if (def.type === \"point\") {\n const { createPointLight } = await import(\"../light/point-light.js\");\n const pl = createPointLight([px, py, pz], intensity);\n pl.diffuse = color;\n pl.specular = color;\n pl.range = range;\n if (sourceNode) {\n pl.parent = sourceNode;\n }\n bindAnimatedLightVersion(pl);\n lights.push(pl);\n setGltfPunctualLight(ctx._json, lightIdx, pl);\n } else if (def.type === \"directional\") {\n const { createDirectionalLight } = await import(\"../light/directional-light.js\");\n const dl = createDirectionalLight(dir, intensity);\n dl.diffuse = color;\n dl.specular = color;\n if (sourceNode) {\n dl.parent = sourceNode;\n }\n bindAnimatedLightVersion(dl);\n lights.push(dl);\n setGltfPunctualLight(ctx._json, lightIdx, dl);\n } else if (def.type === \"spot\") {\n const { createSpotLight } = await import(\"../light/spot-light.js\");\n const outer = def.spot?.outerConeAngle ?? Math.PI / 4;\n const sl = createSpotLight([px, py, pz], dir, outer * 2, 1, intensity);\n sl.diffuse = color;\n sl.specular = color;\n sl.range = range;\n if (sourceNode) {\n sl.parent = sourceNode;\n }\n bindAnimatedLightVersion(sl);\n lights.push(sl);\n setGltfPunctualLight(ctx._json, lightIdx, sl);\n }\n }\n return { entities: lights };\n },\n};\nexport default feature;\n"],"names":[],"mappings":";;;;AA4BA,SAAS,yBAAyB,KAAA,EAA0D;AACxF,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,wBAAA,CAAyB,KAAA,EAAO,eAAe,CAAA,EAAG,GAAA;AACzE,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,MAAA,CAAO,cAAA,CAAe,OAAO,eAAA,EAAiB;AAAA,IAC1C,GAAA,GAAc;AACV,MAAA,OAAA,CAAQ,OAAA,GAAW,QAAQ,IAAA,CAAK,KAAK,IAAe,CAAA,KAAM,KAAA,CAAM,sBAAsB,CAAA,CAAA,GAAK,KAAA;AAAA,IAC/F,CAAA;AAAA,IACA,UAAA,EAAY,KAAA;AAAA,IACZ,YAAA,EAAc;AAAA,GACjB,CAAA;AACD,EAAA,MAAA,CAAO,cAAA,CAAe,OAAO,mBAAA,EAAqB;AAAA,IAC9C,OAAO,MAAM;AACT,MAAA,KAAA,EAAA;AAAA,IACJ,CAAA;AAAA,IACA,UAAA,EAAY,KAAA;AAAA,IACZ,YAAA,EAAc;AAAA,GACjB,CAAA;AACL;AAEA,MAAM,OAAA,GAAuB;AAAA,EACzB,EAAA,EAAI,qBAAA;AAAA,EACJ,MAAM,UAAA,CAAW,OAAA,EAAS,KAAA,EAAO,GAAA,EAAK;AAClC,IAAA,MAAM,IAAA,GAAmC,GAAA,CAAI,KAAA,CAAM,UAAA,EAAY,mBAAA,EAAqB,MAAA;AACpF,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACf,MAAA,OAAO,EAAC;AAAA,IACZ;AACA,IAAA,MAAM,SAAsB,EAAC;AAC7B,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,IAAS,EAAC;AAKlC,IAAA,IAAI,cAAA,GAAiB,CAAA;AACrB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACnC,MAAA,IAAI,MAAM,CAAC,CAAA,EAAG,UAAA,EAAY,mBAAA,EAAqB,UAAU,MAAA,EAAW;AAChE,QAAA,cAAA,EAAA;AAAA,MACJ;AAAA,IACJ;AACA,IAAA,IAAI,iBAAiB,UAAA,EAAY;AAC7B,MAAA,YAAA,CAAa,cAAc,CAAA;AAAA,IAC/B;AACA,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,KAAA,CAAM,QAAQ,OAAA,EAAA,EAAW;AACrD,MAAA,MAAM,QAAA,GAA+B,KAAA,CAAM,OAAO,CAAA,EAAG,YAAY,mBAAA,EAAqB,KAAA;AACtF,MAAA,IAAI,aAAa,MAAA,EAAW;AACxB,QAAA;AAAA,MACJ;AACA,MAAA,MAAM,GAAA,GAAM,KAAK,QAAQ,CAAA;AACzB,MAAA,IAAI,CAAC,GAAA,EAAK;AACN,QAAA;AAAA,MACJ;AAQA,MAAA,MAAM,UAAA,GAAa,GAAA,CAAI,QAAA,GAAW,OAAO,CAAA;AACzC,MAAA,IAAI,IAAY,EAAA,EAAY,EAAA;AAC5B,MAAA,IAAI,GAAA;AACJ,MAAA,IAAI,UAAA,EAAY;AAEZ,QAAA,EAAA,GAAK,KAAK,EAAA,GAAK,CAAA;AACf,QAAA,GAAA,GAAM,CAAC,CAAA,EAAG,CAAA,EAAG,EAAE,CAAA;AAAA,MACnB,CAAA,MAAO;AACH,QAAA,MAAM,KAAA,GAAQ,uBAAuB,GAAA,CAAI,KAAA,EAAO,SAAS,GAAA,CAAI,UAAA,EAAY,IAAI,iBAAiB,CAAA;AAC9F,QAAA,EAAA,GAAK,MAAM,EAAE,CAAA;AACb,QAAA,EAAA,GAAK,MAAM,EAAE,CAAA;AACb,QAAA,EAAA,GAAK,MAAM,EAAE,CAAA;AAGb,QAAA,MAAM,EAAA,GAAK,CAAC,KAAA,CAAM,CAAC,CAAA;AACnB,QAAA,MAAM,EAAA,GAAK,CAAC,KAAA,CAAM,CAAC,CAAA;AACnB,QAAA,MAAM,EAAA,GAAK,CAAC,KAAA,CAAM,EAAE,CAAA;AACpB,QAAA,MAAM,OAAO,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,IAAK,CAAA;AACvC,QAAA,GAAA,GAAM,CAAC,EAAA,GAAK,IAAA,EAAM,EAAA,GAAK,IAAA,EAAM,KAAK,IAAI,CAAA;AAAA,MAC1C;AACA,MAAA,MAAM,KAAA,GAAkC,IAAI,KAAA,GAAQ,CAAC,IAAI,KAAA,CAAM,CAAC,GAAI,GAAA,CAAI,KAAA,CAAM,CAAC,CAAA,EAAI,GAAA,CAAI,MAAM,CAAC,CAAE,IAAI,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA;AAC5G,MAAA,MAAM,SAAA,GAAY,IAAI,SAAA,IAAa,CAAA;AACnC,MAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,KAAU,MAAA,GAAY,GAAA,CAAI,QAAQ,MAAA,CAAO,SAAA;AAE3D,MAAA,IAAI,GAAA,CAAI,SAAS,OAAA,EAAS;AACtB,QAAA,MAAM,EAAE,gBAAA,EAAiB,GAAI,MAAM,OAAO,yBAAyB,CAAA;AACnE,QAAA,MAAM,KAAK,gBAAA,CAAiB,CAAC,IAAI,EAAA,EAAI,EAAE,GAAG,SAAS,CAAA;AACnD,QAAA,EAAA,CAAG,OAAA,GAAU,KAAA;AACb,QAAA,EAAA,CAAG,QAAA,GAAW,KAAA;AACd,QAAA,EAAA,CAAG,KAAA,GAAQ,KAAA;AACX,QAAA,IAAI,UAAA,EAAY;AACZ,UAAA,EAAA,CAAG,MAAA,GAAS,UAAA;AAAA,QAChB;AACA,QAAA,wBAAA,CAAyB,EAAE,CAAA;AAC3B,QAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AACd,QAAA,oBAAA,CAAqB,GAAA,CAAI,KAAA,EAAO,QAAA,EAAU,EAAE,CAAA;AAAA,MAChD,CAAA,MAAA,IAAW,GAAA,CAAI,IAAA,KAAS,aAAA,EAAe;AACnC,QAAA,MAAM,EAAE,sBAAA,EAAuB,GAAI,MAAM,OAAO,+BAA+B,CAAA;AAC/E,QAAA,MAAM,EAAA,GAAK,sBAAA,CAAuB,GAAA,EAAK,SAAS,CAAA;AAChD,QAAA,EAAA,CAAG,OAAA,GAAU,KAAA;AACb,QAAA,EAAA,CAAG,QAAA,GAAW,KAAA;AACd,QAAA,IAAI,UAAA,EAAY;AACZ,UAAA,EAAA,CAAG,MAAA,GAAS,UAAA;AAAA,QAChB;AACA,QAAA,wBAAA,CAAyB,EAAE,CAAA;AAC3B,QAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AACd,QAAA,oBAAA,CAAqB,GAAA,CAAI,KAAA,EAAO,QAAA,EAAU,EAAE,CAAA;AAAA,MAChD,CAAA,MAAA,IAAW,GAAA,CAAI,IAAA,KAAS,MAAA,EAAQ;AAC5B,QAAA,MAAM,EAAE,eAAA,EAAgB,GAAI,MAAM,OAAO,wBAAwB,CAAA;AACjE,QAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,IAAA,EAAM,cAAA,IAAkB,KAAK,EAAA,GAAK,CAAA;AACpD,QAAA,MAAM,EAAA,GAAK,eAAA,CAAgB,CAAC,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,EAAG,GAAA,EAAK,KAAA,GAAQ,CAAA,EAAG,CAAA,EAAG,SAAS,CAAA;AACrE,QAAA,EAAA,CAAG,OAAA,GAAU,KAAA;AACb,QAAA,EAAA,CAAG,QAAA,GAAW,KAAA;AACd,QAAA,EAAA,CAAG,KAAA,GAAQ,KAAA;AACX,QAAA,IAAI,UAAA,EAAY;AACZ,UAAA,EAAA,CAAG,MAAA,GAAS,UAAA;AAAA,QAChB;AACA,QAAA,wBAAA,CAAyB,EAAE,CAAA;AAC3B,QAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AACd,QAAA,oBAAA,CAAqB,GAAA,CAAI,KAAA,EAAO,QAAA,EAAU,EAAE,CAAA;AAAA,MAChD;AAAA,IACJ;AACA,IAAA,OAAO,EAAE,UAAU,MAAA,EAAO;AAAA,EAC9B;AACJ;;;;"}
@@ -0,0 +1,20 @@
1
+ import '../material/pbr/pbr-primitive-resolver.js';
2
+
3
+ const feature = {
4
+ id: "_primitive",
5
+ async applyMesh(meshData, mesh) {
6
+ const mode = meshData._primitive?.mode;
7
+ const topo = mode === 0 ? 1 : mode === 1 ? 2 : mode === 3 ? 3 : mode === 5 ? 4 : void 0;
8
+ if (topo) {
9
+ mesh._topology = topo;
10
+ }
11
+ const wm = meshData._worldMatrix;
12
+ const det3 = wm[0] * (wm[5] * wm[10] - wm[6] * wm[9]) + wm[1] * (wm[6] * wm[8] - wm[4] * wm[10]) + wm[2] * (wm[4] * wm[9] - wm[5] * wm[8]);
13
+ if (det3 > 0) {
14
+ mesh._reverseWinding = true;
15
+ }
16
+ }
17
+ };
18
+
19
+ export { feature as default };
20
+ //# sourceMappingURL=gltf-feature-primitive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gltf-feature-primitive.js","sources":["../../../src/loader-gltf/gltf-feature-primitive.ts"],"sourcesContent":["/** Primitive-state feature (non-triangle topology + negative-determinant winding) — dynamically\n * imported, gated on a non-triangle primitive mode OR a negative-determinant node.\n *\n * Records `_topology` (POINTS/LINES/LINE_STRIP/TRIANGLE_STRIP) and/or `_reverseWinding` on each\n * affected mesh and installs the PBR pipeline's primitive resolver (topology + stripIndexFormat +\n * culling). The common triangle-list positive-winding case never loads this module, so the core\n * loader + pipeline chunks stay byte-identical. */\nimport \"../material/pbr/pbr-primitive-resolver.js\";\nimport type { GltfFeature } from \"./gltf-feature.js\";\n\nconst feature: GltfFeature = {\n id: \"_primitive\",\n async applyMesh(meshData, mesh) {\n // Non-triangle topology index from the glTF primitive mode. The unsupported LINE_LOOP(2) /\n // TRIANGLE_FAN(6) modes are left as a triangle list (matching BJS, which can't render them).\n const mode = (meshData as { _primitive?: { mode?: number } })._primitive?.mode;\n const topo = mode === 0 ? 1 : mode === 1 ? 2 : mode === 3 ? 3 : mode === 5 ? 4 : undefined;\n if (topo) {\n (mesh as { _topology?: number })._topology = topo;\n }\n // A mesh whose net world-matrix determinant is positive (mirrored vs the RH→LH root flip) has\n // reversed triangle winding; flag it so the pipeline culls \"front\" (matching BJS, which flips\n // sideOrientation on negative determinant). Normal meshes have a negative world determinant.\n const wm = meshData._worldMatrix as unknown as ArrayLike<number>;\n const det3 = wm[0]! * (wm[5]! * wm[10]! - wm[6]! * wm[9]!) + wm[1]! * (wm[6]! * wm[8]! - wm[4]! * wm[10]!) + wm[2]! * (wm[4]! * wm[9]! - wm[5]! * wm[8]!);\n if (det3 > 0) {\n (mesh as { _reverseWinding?: boolean })._reverseWinding = true;\n }\n },\n};\nexport default feature;\n"],"names":[],"mappings":";;AAUA,MAAM,OAAA,GAAuB;AAAA,EACzB,EAAA,EAAI,YAAA;AAAA,EACJ,MAAM,SAAA,CAAU,QAAA,EAAU,IAAA,EAAM;AAG5B,IAAA,MAAM,IAAA,GAAQ,SAAgD,UAAA,EAAY,IAAA;AAC1E,IAAA,MAAM,IAAA,GAAO,IAAA,KAAS,CAAA,GAAI,CAAA,GAAI,IAAA,KAAS,CAAA,GAAI,CAAA,GAAI,IAAA,KAAS,CAAA,GAAI,CAAA,GAAI,IAAA,KAAS,CAAA,GAAI,CAAA,GAAI,MAAA;AACjF,IAAA,IAAI,IAAA,EAAM;AACN,MAAC,KAAgC,SAAA,GAAY,IAAA;AAAA,IACjD;AAIA,IAAA,MAAM,KAAK,QAAA,CAAS,YAAA;AACpB,IAAA,MAAM,IAAA,GAAO,GAAG,CAAC,CAAA,IAAM,GAAG,CAAC,CAAA,GAAK,GAAG,EAAE,CAAA,GAAK,GAAG,CAAC,CAAA,GAAK,GAAG,CAAC,CAAA,CAAA,GAAM,GAAG,CAAC,CAAA,IAAM,GAAG,CAAC,CAAA,GAAK,GAAG,CAAC,CAAA,GAAK,GAAG,CAAC,CAAA,GAAK,GAAG,EAAE,CAAA,CAAA,GAAM,GAAG,CAAC,CAAA,IAAM,GAAG,CAAC,CAAA,GAAK,GAAG,CAAC,CAAA,GAAK,GAAG,CAAC,CAAA,GAAK,GAAG,CAAC,CAAA,CAAA;AACtJ,IAAA,IAAI,OAAO,CAAA,EAAG;AACV,MAAC,KAAuC,eAAA,GAAkB,IAAA;AAAA,IAC9D;AAAA,EACJ;AACJ;;;;"}
@@ -26,10 +26,17 @@ const _features = [
26
26
  // Per-mesh features (predicates inlined to avoid eager imports)
27
27
  [(j) => !!j.skins?.length && anyPrimitive(j, (p) => p.attributes?.JOINTS_0 !== void 0), () => import('./gltf-feature-skeleton.js')],
28
28
  [(j) => anyPrimitive(j, (p) => !!p.targets?.length), () => import('./gltf-feature-morph.js')],
29
+ // Non-triangle primitive topology (POINTS/LINES/LINE_STRIP/TRIANGLE_STRIP) or a
30
+ // negative-determinant node (negative scale / mirrored matrix): both need the lazy primitive
31
+ // feature (topology threading + winding reversal). Triangle-list positive-winding never triggers.
32
+ [(j) => hasNegDetNode(j) || anyPrimitive(j, (p) => p.mode !== void 0 && p.mode !== 4), () => import('./gltf-feature-primitive.js')],
29
33
  // Per-asset features
30
34
  [hasGltfExtras, () => import('./gltf-feature-extras.js')],
31
35
  ["KHR_lights_punctual", () => import('./gltf-feature-lights-punctual.js')],
32
36
  [(j) => !!j.animations?.length, () => import('./gltf-feature-animations.js')],
37
+ // Non-Float32 / normalized animation sampler accessors (e.g. Animation_SamplerType normalized
38
+ // BYTE/SHORT rotation) need the lazy denorm converter; plain float samplers never load it.
39
+ [hasNonFloatAnimSampler, () => import('./gltf-sampler-denorm.js')],
33
40
  [M + "variants", () => import('./gltf-feature-variants.js')],
34
41
  ["KHR_node_visibility", () => import('./gltf-ext-node-visibility.js')],
35
42
  ["KHR_animation_pointer", () => import('./gltf-feature-animation-pointer.js')],
@@ -45,6 +52,24 @@ function hasGltfExtras(json) {
45
52
  const hasExtras = (item) => item?.extras !== void 0;
46
53
  return hasExtras(json.asset) || !!json.nodes?.some(hasExtras) || !!json.materials?.some(hasExtras) || !!json.animations?.some(hasExtras) || !!json.meshes?.some(hasExtras) || anyPrimitive(json, hasExtras);
47
54
  }
55
+ function hasNonFloatAnimSampler(json) {
56
+ const accessors = json.accessors;
57
+ return !!json.animations?.some(
58
+ (a) => a.samplers?.some((s) => accessors[s.input]?.componentType !== 5126 || accessors[s.output]?.componentType !== 5126)
59
+ );
60
+ }
61
+ function hasNegDetNode(json) {
62
+ return !!json.nodes?.some((n) => {
63
+ if (n.scale) {
64
+ return n.scale[0] * n.scale[1] * n.scale[2] < 0;
65
+ }
66
+ if (n.matrix) {
67
+ const m = n.matrix;
68
+ return m[0] * (m[5] * m[10] - m[6] * m[9]) + m[1] * (m[6] * m[8] - m[4] * m[10]) + m[2] * (m[4] * m[9] - m[5] * m[8]) < 0;
69
+ }
70
+ return false;
71
+ });
72
+ }
48
73
 
49
74
  export { loadGltfFeatures };
50
75
  //# sourceMappingURL=gltf-feature-registry.js.map