@forgeax/engine-gltf 0.0.0-dev.8d955ade1c79

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 (220) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +204 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/animation-target-errors.unit.test.d.ts +2 -0
  5. package/dist/__tests__/animation-target-errors.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/bridge-material-samplers.unit.test.d.ts +2 -0
  7. package/dist/__tests__/bridge-material-samplers.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/bridge-material-values.unit.test.d.ts +2 -0
  9. package/dist/__tests__/bridge-material-values.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/bridge-mixed-color.unit.test.d.ts +2 -0
  11. package/dist/__tests__/bridge-mixed-color.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/bridge-sampler-required.test-d.d.ts +2 -0
  13. package/dist/__tests__/bridge-sampler-required.test-d.d.ts.map +1 -0
  14. package/dist/__tests__/bridge-skin-shader-route.unit.test.d.ts +2 -0
  15. package/dist/__tests__/bridge-skin-shader-route.unit.test.d.ts.map +1 -0
  16. package/dist/__tests__/bridge-skin-stride.unit.test.d.ts +2 -0
  17. package/dist/__tests__/bridge-skin-stride.unit.test.d.ts.map +1 -0
  18. package/dist/__tests__/bridge-test-helpers.d.ts +5 -0
  19. package/dist/__tests__/bridge-test-helpers.d.ts.map +1 -0
  20. package/dist/__tests__/bridge.unit.test.d.ts +2 -0
  21. package/dist/__tests__/bridge.unit.test.d.ts.map +1 -0
  22. package/dist/__tests__/cli-gltf.integration.test.d.ts +2 -0
  23. package/dist/__tests__/cli-gltf.integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/color-0-accessor.unit.test.d.ts +2 -0
  25. package/dist/__tests__/color-0-accessor.unit.test.d.ts.map +1 -0
  26. package/dist/__tests__/data-uri-surface.unit.test.d.ts +2 -0
  27. package/dist/__tests__/data-uri-surface.unit.test.d.ts.map +1 -0
  28. package/dist/__tests__/extension-admission.unit.test.d.ts +2 -0
  29. package/dist/__tests__/extension-admission.unit.test.d.ts.map +1 -0
  30. package/dist/__tests__/gltf-error-derived-views.test-d.d.ts +2 -0
  31. package/dist/__tests__/gltf-error-derived-views.test-d.d.ts.map +1 -0
  32. package/dist/__tests__/gltf-error-migration.test.d.ts +2 -0
  33. package/dist/__tests__/gltf-error-migration.test.d.ts.map +1 -0
  34. package/dist/__tests__/gltf-error-policy-owner.unit.test.d.ts +2 -0
  35. package/dist/__tests__/gltf-error-policy-owner.unit.test.d.ts.map +1 -0
  36. package/dist/__tests__/gltf-importer.test.d.ts +2 -0
  37. package/dist/__tests__/gltf-importer.test.d.ts.map +1 -0
  38. package/dist/__tests__/gltf-local-artifacts.test.d.ts +2 -0
  39. package/dist/__tests__/gltf-local-artifacts.test.d.ts.map +1 -0
  40. package/dist/__tests__/gltf.unit.test.d.ts +2 -0
  41. package/dist/__tests__/gltf.unit.test.d.ts.map +1 -0
  42. package/dist/__tests__/malformed-buffer-data-uri.integration.test.d.ts +2 -0
  43. package/dist/__tests__/malformed-buffer-data-uri.integration.test.d.ts.map +1 -0
  44. package/dist/__tests__/material-missing-uv.unit.test.d.ts +2 -0
  45. package/dist/__tests__/material-missing-uv.unit.test.d.ts.map +1 -0
  46. package/dist/__tests__/material-pack-refs.integration.test.d.ts +2 -0
  47. package/dist/__tests__/material-pack-refs.integration.test.d.ts.map +1 -0
  48. package/dist/__tests__/material-per-slot-ir.unit.test.d.ts +2 -0
  49. package/dist/__tests__/material-per-slot-ir.unit.test.d.ts.map +1 -0
  50. package/dist/__tests__/material-texture-transform.unit.test.d.ts +2 -0
  51. package/dist/__tests__/material-texture-transform.unit.test.d.ts.map +1 -0
  52. package/dist/__tests__/material-unused-uv.unit.test.d.ts +2 -0
  53. package/dist/__tests__/material-unused-uv.unit.test.d.ts.map +1 -0
  54. package/dist/__tests__/meshopt-decode.unit.test.d.ts +2 -0
  55. package/dist/__tests__/meshopt-decode.unit.test.d.ts.map +1 -0
  56. package/dist/__tests__/meshopt-falsifier.unit.test.d.ts +2 -0
  57. package/dist/__tests__/meshopt-falsifier.unit.test.d.ts.map +1 -0
  58. package/dist/__tests__/meshopt-mode-filter-owner.test-d.d.ts +2 -0
  59. package/dist/__tests__/meshopt-mode-filter-owner.test-d.d.ts.map +1 -0
  60. package/dist/__tests__/meshopt-real-matrix.integration.test.d.ts +2 -0
  61. package/dist/__tests__/meshopt-real-matrix.integration.test.d.ts.map +1 -0
  62. package/dist/__tests__/morph-import.integration.test.d.ts +2 -0
  63. package/dist/__tests__/morph-import.integration.test.d.ts.map +1 -0
  64. package/dist/__tests__/morph-import.unit.test.d.ts +2 -0
  65. package/dist/__tests__/morph-import.unit.test.d.ts.map +1 -0
  66. package/dist/__tests__/parse-decode.unit.test.d.ts +2 -0
  67. package/dist/__tests__/parse-decode.unit.test.d.ts.map +1 -0
  68. package/dist/__tests__/parse-gltf-multi-uv.test.d.ts +2 -0
  69. package/dist/__tests__/parse-gltf-multi-uv.test.d.ts.map +1 -0
  70. package/dist/__tests__/parse-gltf-skin-attrs.unit.test.d.ts +2 -0
  71. package/dist/__tests__/parse-gltf-skin-attrs.unit.test.d.ts.map +1 -0
  72. package/dist/__tests__/parse-gltf.unit.test.d.ts +2 -0
  73. package/dist/__tests__/parse-gltf.unit.test.d.ts.map +1 -0
  74. package/dist/__tests__/pick-e2e.integration.test.d.ts +2 -0
  75. package/dist/__tests__/pick-e2e.integration.test.d.ts.map +1 -0
  76. package/dist/__tests__/public-surface.unit.test.d.ts +2 -0
  77. package/dist/__tests__/public-surface.unit.test.d.ts.map +1 -0
  78. package/dist/__tests__/source-key-diagnostics.integration.test.d.ts +2 -0
  79. package/dist/__tests__/source-key-diagnostics.integration.test.d.ts.map +1 -0
  80. package/dist/__tests__/source-key-producer.integration.test.d.ts +2 -0
  81. package/dist/__tests__/source-key-producer.integration.test.d.ts.map +1 -0
  82. package/dist/__tests__/source-key.unit.test.d.ts +2 -0
  83. package/dist/__tests__/source-key.unit.test.d.ts.map +1 -0
  84. package/dist/accessor/decode-accessor.d.ts +75 -0
  85. package/dist/accessor/decode-accessor.d.ts.map +1 -0
  86. package/dist/accessor/decode-color.d.ts +15 -0
  87. package/dist/accessor/decode-color.d.ts.map +1 -0
  88. package/dist/accessor/index.d.ts +3 -0
  89. package/dist/accessor/index.d.ts.map +1 -0
  90. package/dist/bridge.d.ts +103 -0
  91. package/dist/bridge.d.ts.map +1 -0
  92. package/dist/check-extensions.d.ts +29 -0
  93. package/dist/check-extensions.d.ts.map +1 -0
  94. package/dist/cli-gltf.d.ts +8 -0
  95. package/dist/cli-gltf.d.ts.map +1 -0
  96. package/dist/cli-gltf.mjs +2435 -0
  97. package/dist/cli-gltf.mjs.map +1 -0
  98. package/dist/data-uri.d.ts +6 -0
  99. package/dist/data-uri.d.ts.map +1 -0
  100. package/dist/errors.d.ts +201 -0
  101. package/dist/errors.d.ts.map +1 -0
  102. package/dist/extract-image-bytes.d.ts +42 -0
  103. package/dist/extract-image-bytes.d.ts.map +1 -0
  104. package/dist/gltf-importer.d.ts +21 -0
  105. package/dist/gltf-importer.d.ts.map +1 -0
  106. package/dist/image-color-space.d.ts +35 -0
  107. package/dist/image-color-space.d.ts.map +1 -0
  108. package/dist/importer-entry.d.ts +10 -0
  109. package/dist/importer-entry.d.ts.map +1 -0
  110. package/dist/importer-entry.mjs +3378 -0
  111. package/dist/importer-entry.mjs.map +1 -0
  112. package/dist/index.d.ts +25 -0
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.mjs +3673 -0
  115. package/dist/index.mjs.map +1 -0
  116. package/dist/meshopt-decode.d.ts +39 -0
  117. package/dist/meshopt-decode.d.ts.map +1 -0
  118. package/dist/node-file-entry.d.ts +14 -0
  119. package/dist/node-file-entry.d.ts.map +1 -0
  120. package/dist/node-file-entry.mjs +1946 -0
  121. package/dist/node-file-entry.mjs.map +1 -0
  122. package/dist/node-path.d.ts +14 -0
  123. package/dist/node-path.d.ts.map +1 -0
  124. package/dist/parse-animation.d.ts +70 -0
  125. package/dist/parse-animation.d.ts.map +1 -0
  126. package/dist/parse-glb-chunks.d.ts +25 -0
  127. package/dist/parse-glb-chunks.d.ts.map +1 -0
  128. package/dist/parse-glb.d.ts +3 -0
  129. package/dist/parse-glb.d.ts.map +1 -0
  130. package/dist/parse-gltf-header.d.ts +18 -0
  131. package/dist/parse-gltf-header.d.ts.map +1 -0
  132. package/dist/parse-gltf.d.ts +263 -0
  133. package/dist/parse-gltf.d.ts.map +1 -0
  134. package/dist/parse-skin.d.ts +70 -0
  135. package/dist/parse-skin.d.ts.map +1 -0
  136. package/dist/reimport-reuse-meta.d.ts +48 -0
  137. package/dist/reimport-reuse-meta.d.ts.map +1 -0
  138. package/dist/serialize-meta.d.ts +2 -0
  139. package/dist/serialize-meta.d.ts.map +1 -0
  140. package/dist/source-key.d.ts +44 -0
  141. package/dist/source-key.d.ts.map +1 -0
  142. package/dist/sub-asset-key.d.ts +12 -0
  143. package/dist/sub-asset-key.d.ts.map +1 -0
  144. package/dist/to-asset-pack.d.ts +2 -0
  145. package/dist/to-asset-pack.d.ts.map +1 -0
  146. package/dist/transform.d.ts +37 -0
  147. package/dist/transform.d.ts.map +1 -0
  148. package/package.json +92 -0
  149. package/src/__tests__/animation-target-errors.unit.test.ts +51 -0
  150. package/src/__tests__/bridge-material-samplers.unit.test.ts +67 -0
  151. package/src/__tests__/bridge-material-values.unit.test.ts +106 -0
  152. package/src/__tests__/bridge-mixed-color.unit.test.ts +100 -0
  153. package/src/__tests__/bridge-sampler-required.test-d.ts +59 -0
  154. package/src/__tests__/bridge-skin-shader-route.unit.test.ts +42 -0
  155. package/src/__tests__/bridge-skin-stride.unit.test.ts +115 -0
  156. package/src/__tests__/bridge-test-helpers.ts +9 -0
  157. package/src/__tests__/bridge.unit.test.ts +1227 -0
  158. package/src/__tests__/cli-gltf.integration.test.ts +122 -0
  159. package/src/__tests__/color-0-accessor.unit.test.ts +174 -0
  160. package/src/__tests__/data-uri-surface.unit.test.ts +26 -0
  161. package/src/__tests__/extension-admission.unit.test.ts +19 -0
  162. package/src/__tests__/fixtures/color-0/float-vec3.gltf +13 -0
  163. package/src/__tests__/fixtures/color-0/interleaved-vec3.gltf +6 -0
  164. package/src/__tests__/fixtures/color-0/normalized-ubyte.gltf +6 -0
  165. package/src/__tests__/fixtures/color-0/normalized-ushort.gltf +6 -0
  166. package/src/__tests__/gltf-error-derived-views.test-d.ts +152 -0
  167. package/src/__tests__/gltf-error-migration.test.ts +147 -0
  168. package/src/__tests__/gltf-error-policy-owner.unit.test.ts +185 -0
  169. package/src/__tests__/gltf-importer.test.ts +12 -0
  170. package/src/__tests__/gltf-local-artifacts.test.ts +11 -0
  171. package/src/__tests__/gltf.unit.test.ts +2975 -0
  172. package/src/__tests__/malformed-buffer-data-uri.integration.test.ts +140 -0
  173. package/src/__tests__/material-missing-uv.unit.test.ts +29 -0
  174. package/src/__tests__/material-pack-refs.integration.test.ts +58 -0
  175. package/src/__tests__/material-per-slot-ir.unit.test.ts +90 -0
  176. package/src/__tests__/material-texture-transform.unit.test.ts +105 -0
  177. package/src/__tests__/material-unused-uv.unit.test.ts +44 -0
  178. package/src/__tests__/meshopt-decode.unit.test.ts +87 -0
  179. package/src/__tests__/meshopt-falsifier.unit.test.ts +92 -0
  180. package/src/__tests__/meshopt-mode-filter-owner.test-d.ts +79 -0
  181. package/src/__tests__/meshopt-real-matrix.integration.test.ts +370 -0
  182. package/src/__tests__/morph-import.integration.test.ts +115 -0
  183. package/src/__tests__/morph-import.unit.test.ts +62 -0
  184. package/src/__tests__/parse-decode.unit.test.ts +919 -0
  185. package/src/__tests__/parse-gltf-multi-uv.test.ts +238 -0
  186. package/src/__tests__/parse-gltf-skin-attrs.unit.test.ts +191 -0
  187. package/src/__tests__/parse-gltf.unit.test.ts +133 -0
  188. package/src/__tests__/pick-e2e.integration.test.ts +238 -0
  189. package/src/__tests__/public-surface.unit.test.ts +10 -0
  190. package/src/__tests__/source-key-diagnostics.integration.test.ts +140 -0
  191. package/src/__tests__/source-key-producer.integration.test.ts +171 -0
  192. package/src/__tests__/source-key.unit.test.ts +159 -0
  193. package/src/accessor/decode-accessor.ts +309 -0
  194. package/src/accessor/decode-color.ts +128 -0
  195. package/src/accessor/index.ts +15 -0
  196. package/src/bridge.ts +928 -0
  197. package/src/check-extensions.ts +85 -0
  198. package/src/cli-gltf.ts +339 -0
  199. package/src/data-uri.ts +29 -0
  200. package/src/errors.ts +386 -0
  201. package/src/extract-image-bytes.ts +343 -0
  202. package/src/gltf-importer.ts +976 -0
  203. package/src/image-color-space.ts +104 -0
  204. package/src/importer-entry.ts +24 -0
  205. package/src/index.ts +148 -0
  206. package/src/meshopt-decode.ts +200 -0
  207. package/src/node-file-entry.ts +98 -0
  208. package/src/node-path.ts +41 -0
  209. package/src/parse-animation.ts +286 -0
  210. package/src/parse-glb-chunks.ts +167 -0
  211. package/src/parse-glb.ts +16 -0
  212. package/src/parse-gltf-header.ts +58 -0
  213. package/src/parse-gltf.ts +1731 -0
  214. package/src/parse-skin.ts +238 -0
  215. package/src/reimport-reuse-meta.ts +134 -0
  216. package/src/serialize-meta.ts +24 -0
  217. package/src/source-key.ts +106 -0
  218. package/src/sub-asset-key.ts +61 -0
  219. package/src/to-asset-pack.ts +8 -0
  220. package/src/transform.ts +117 -0
@@ -0,0 +1,1946 @@
1
+ import { err, ok, ImportError, IMPORT_ERROR_HINTS } from '@forgeax/engine-types';
2
+ import { vec3, quat, mat4 } from '@forgeax/engine-math';
3
+ import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
4
+
5
+ // src/errors.ts
6
+ var GLTF_MESHOPT_MODES = ["ATTRIBUTES", "TRIANGLES", "INDICES"];
7
+ var GLTF_MESHOPT_FILTERS = ["NONE", "OCTAHEDRAL", "QUATERNION", "EXPONENTIAL"];
8
+ var gltfErrorPolicy = {
9
+ "gltf-malformed-header": {
10
+ expected: "GLB 12-byte header (magic 0x46546C67 + version=2 + length) plus mandatory JSON chunk",
11
+ hint: "verify .glb is not truncated; rerun: forgeax-engine-remote-gltf import <path>"
12
+ },
13
+ "gltf-version-unsupported": {
14
+ expected: 'asset.version === "2.0"',
15
+ hint: 'asset.version must be "2.0"; v1 or v3 not supported'
16
+ },
17
+ "gltf-buffer-out-of-bounds": {
18
+ expected: "accessor byte range within bufferView.byteLength",
19
+ hint: "rebuild .gltf with valid bufferViews; check accessor index; ensure accessor.byteOffset + EFFECTIVE_STRIDE * (count - 1) + element_size <= bufferView.byteLength"
20
+ },
21
+ "gltf-extension-unsupported": {
22
+ expected: "extension listed in v1 allowlist (see EXTENSION_ALLOWLIST in @forgeax/engine-gltf)",
23
+ hint: "see feat-future-gltf-extensions-allowlist; remove this extension or wait for the allowlist to expand"
24
+ },
25
+ "gltf-accessor-type-mismatch": {
26
+ expected: "dense fixed-stride accessor with supported componentType",
27
+ hint: "sparse: see feat-future-gltf-sparse-accessor; morph: see feat-future-gltf-morph; interleaved: see feat-future-gltf-mesh-multi-section"
28
+ },
29
+ "gltf-texture-load-failed": {
30
+ expected: "externalLoader resolved the URI into an ArrayBuffer without throwing",
31
+ hint: "check sidecar meta.json + textures/ directory + vite-plugin-pack /__pack/lookup route"
32
+ },
33
+ "gltf-meta-missing": {
34
+ expected: "sidecar <source>.meta.json (importer: 'gltf') present in same directory",
35
+ hint: "run: forgeax-engine-remote-gltf import <path>"
36
+ },
37
+ "gltf-instancing-count-mismatch": {
38
+ expected: "all instance attribute accessors share the same count",
39
+ hint: "EXT_mesh_gpu_instancing requires TRANSLATION/ROTATION/SCALE accessors to share count; see https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/EXT_mesh_gpu_instancing/README.md#extending-nodes-with-instance-attributes"
40
+ },
41
+ "gltf-image-mime-unsupported": {
42
+ expected: "image/mimeType is image/jpeg or image/png",
43
+ hint: "convert to JPG/PNG via external tool; only image/jpeg and image/png are supported"
44
+ },
45
+ "gltf-skin-joint-count-exceeded": {
46
+ expected: "skin.joints.length <= MAX_JOINTS (256)",
47
+ hint: "reduce joint count below MAX_JOINTS (256) or see OOS-skin-max-joints"
48
+ },
49
+ "gltf-animation-cubicspline-unsupported": {
50
+ expected: "animation sampler interpolation is LINEAR or STEP",
51
+ hint: "see OOS-skin-cubicspline; convert CUBICSPLINE to LINEAR/STEP in DCC tool"
52
+ },
53
+ "gltf-morph-unsupported": {
54
+ expected: "animation channel target path is one of translation, rotation, scale, or weights",
55
+ hint: "animation target path must be translation, rotation, scale, or weights"
56
+ },
57
+ "gltf-skin-joint-name-missing": {
58
+ expected: "every joint node has a non-empty name and belongs to an acyclic hierarchy",
59
+ hint: "ensure every joint node has a non-empty name and the node hierarchy is acyclic"
60
+ },
61
+ "gltf-image-extract-failed": {
62
+ expected: "image bytes extractable from bufferView / data-URI / external URI without corruption",
63
+ hint: "verify the bufferView byte range / data: URI base64 / external URI sibling file is intact next to the .gltf source; rerun: forgeax-engine-remote-gltf import <path>"
64
+ },
65
+ "gltf-skin-attr-asymmetric": {
66
+ expected: "mesh primitive declares JOINTS_0 and WEIGHTS_0 symmetrically (both present or both absent)",
67
+ hint: "glTF spec requires JOINTS_0 and WEIGHTS_0 to appear together for each skinned primitive; add the missing attribute or remove the present one in the DCC tool"
68
+ },
69
+ "gltf-animation-target-invalid": {
70
+ expected: "every animation channel resolves to one uniquely named scene node and stable target ID",
71
+ hint: "name every node in the animated hierarchy and ensure each animated full path is unique"
72
+ },
73
+ "gltf-meshopt-decoder-required": {
74
+ expected: "a required or compressed-only EXT_meshopt_compression bufferView has a ready decoder capability",
75
+ hint: "provide the build-only EXT_meshopt_compression decoder or author a valid core fallback bufferView"
76
+ },
77
+ "gltf-meshopt-decode-failed": {
78
+ expected: "the EXT_meshopt_compression declaration and decoder output are structurally valid",
79
+ hint: "the meshopt decoder accepts the declared compressed range and produces the declared byte count"
80
+ },
81
+ "gltf-morph-invalid": {
82
+ expected: "morph target and default-weight arrays are dense, bounded, and match the base vertex count",
83
+ hint: "re-export dense morph targets with at most eight targets/attributes and matching vertex/default-weight lengths"
84
+ },
85
+ "gltf-color-accessor-unsupported": {
86
+ expected: "COLOR_0 dense accessor uses VEC3/VEC4 FLOAT or normalized UBYTE/USHORT",
87
+ hint: "re-export COLOR_0 with a supported type/component/normalized combination; morph and sparse COLOR_0 remain deferred"
88
+ },
89
+ "gltf-color-accessor-malformed": {
90
+ expected: "COLOR_0 accessor is non-empty, finite, in range, and fully addressable",
91
+ hint: "repair the COLOR_0 accessor count, reference, range, or buffer bounds, then re-import the glTF source"
92
+ },
93
+ "gltf-mesh-bridge-invalid": {
94
+ expected: "a non-empty merged mesh with consistent morph and COLOR_0 cardinality",
95
+ hint: "repair the source primitive and re-import; inspect detail.reason and its typed facts"
96
+ }
97
+ };
98
+ Object.fromEntries(
99
+ Object.entries(gltfErrorPolicy).map(([code, policy]) => [code, policy.hint])
100
+ );
101
+ function gltfErr(code, detail) {
102
+ return {
103
+ code,
104
+ expected: gltfErrorPolicy[code].expected,
105
+ hint: gltfErrorPolicy[code].hint,
106
+ detail
107
+ };
108
+ }
109
+
110
+ // src/accessor/decode-accessor.ts
111
+ var COMPONENT_TYPE = {
112
+ I8: 5120,
113
+ U8: 5121,
114
+ I16: 5122,
115
+ U16: 5123,
116
+ U32: 5125,
117
+ F32: 5126
118
+ };
119
+ var TYPE_COMPONENT_COUNT = {
120
+ SCALAR: 1,
121
+ VEC2: 2,
122
+ VEC3: 3,
123
+ VEC4: 4,
124
+ MAT2: 4,
125
+ MAT3: 9,
126
+ MAT4: 16
127
+ };
128
+ var COMPONENT_BYTE_SIZE = {
129
+ [COMPONENT_TYPE.I8]: 1,
130
+ [COMPONENT_TYPE.U8]: 1,
131
+ [COMPONENT_TYPE.I16]: 2,
132
+ [COMPONENT_TYPE.U16]: 2,
133
+ [COMPONENT_TYPE.U32]: 4,
134
+ [COMPONENT_TYPE.F32]: 4
135
+ };
136
+ function decodeF32Accessor(accessorIndex, accessor, allowedTypes, bufferViews, buffers) {
137
+ if (accessor.componentType !== COMPONENT_TYPE.F32 || !allowedTypes.includes(accessor.type)) {
138
+ return err(
139
+ gltfErr("gltf-accessor-type-mismatch", { accessorIndex, reason: "unknownComponentType" })
140
+ );
141
+ }
142
+ const bufferViewIndex = accessor.bufferView;
143
+ if (bufferViewIndex === void 0) {
144
+ return err(
145
+ gltfErr("gltf-buffer-out-of-bounds", {
146
+ accessor: accessorIndex,
147
+ byteOffset: 0,
148
+ byteLength: 0,
149
+ bufferIndex: 0
150
+ })
151
+ );
152
+ }
153
+ const bufferView = bufferViews[bufferViewIndex];
154
+ if (bufferView === void 0) {
155
+ return err(
156
+ gltfErr("gltf-buffer-out-of-bounds", {
157
+ accessor: accessorIndex,
158
+ byteOffset: 0,
159
+ byteLength: 0,
160
+ bufferIndex: bufferViewIndex
161
+ })
162
+ );
163
+ }
164
+ const buffer = buffers[bufferView.buffer];
165
+ if (buffer === void 0) {
166
+ return err(
167
+ gltfErr("gltf-buffer-out-of-bounds", {
168
+ accessor: accessorIndex,
169
+ byteOffset: bufferView.byteOffset ?? 0,
170
+ byteLength: bufferView.byteLength,
171
+ bufferIndex: bufferView.buffer
172
+ })
173
+ );
174
+ }
175
+ const decoded = decodeAccessor({
176
+ accessor: { ...accessor, bufferView: bufferViewIndex },
177
+ bufferView,
178
+ buffer,
179
+ accessorIndex,
180
+ role: "attribute"
181
+ });
182
+ if (!decoded.ok) return decoded;
183
+ if (decoded.value.kind !== "f32") {
184
+ return err(
185
+ gltfErr("gltf-accessor-type-mismatch", { accessorIndex, reason: "unknownComponentType" })
186
+ );
187
+ }
188
+ return ok(decoded.value.data);
189
+ }
190
+ function decodeAccessor(input, flags = {}) {
191
+ const { accessorIndex, accessor, bufferView, buffer, role } = input;
192
+ if (flags.morph === true) {
193
+ return err(
194
+ gltfErr("gltf-accessor-type-mismatch", {
195
+ accessorIndex,
196
+ reason: "morph"
197
+ })
198
+ );
199
+ }
200
+ if ("sparse" in accessor && accessor.sparse !== void 0) {
201
+ return err(
202
+ gltfErr("gltf-accessor-type-mismatch", {
203
+ accessorIndex,
204
+ reason: "sparse"
205
+ })
206
+ );
207
+ }
208
+ const componentByteSize = COMPONENT_BYTE_SIZE[accessor.componentType];
209
+ const componentCount = TYPE_COMPONENT_COUNT[accessor.type];
210
+ if (componentByteSize === void 0 || componentCount === void 0) {
211
+ return err(
212
+ gltfErr("gltf-accessor-type-mismatch", {
213
+ accessorIndex,
214
+ reason: "unknownComponentType"
215
+ })
216
+ );
217
+ }
218
+ const elementSize = componentByteSize * componentCount;
219
+ const byteStride = bufferView.byteStride;
220
+ if (byteStride !== void 0 && byteStride !== elementSize) {
221
+ return err(
222
+ gltfErr("gltf-accessor-type-mismatch", {
223
+ accessorIndex,
224
+ reason: "interleaved"
225
+ })
226
+ );
227
+ }
228
+ const accessorByteOffset = accessor.byteOffset ?? 0;
229
+ const bufferViewByteOffset = bufferView.byteOffset ?? 0;
230
+ const totalByteLength = elementSize * accessor.count;
231
+ if (accessorByteOffset + totalByteLength > bufferView.byteLength) {
232
+ return err(
233
+ gltfErr("gltf-buffer-out-of-bounds", {
234
+ accessor: accessorIndex,
235
+ byteOffset: bufferViewByteOffset + accessorByteOffset,
236
+ byteLength: totalByteLength,
237
+ bufferIndex: bufferView.buffer
238
+ })
239
+ );
240
+ }
241
+ const absoluteOffset = bufferViewByteOffset + accessorByteOffset;
242
+ if (absoluteOffset + totalByteLength > buffer.byteLength) {
243
+ return err(
244
+ gltfErr("gltf-buffer-out-of-bounds", {
245
+ accessor: accessorIndex,
246
+ byteOffset: absoluteOffset,
247
+ byteLength: totalByteLength,
248
+ bufferIndex: bufferView.buffer
249
+ })
250
+ );
251
+ }
252
+ if (role === "indices" && accessor.componentType === COMPONENT_TYPE.U8) {
253
+ const widened = new Uint16Array(accessor.count);
254
+ for (let i = 0; i < accessor.count; i++) {
255
+ widened[i] = buffer[absoluteOffset + i] ?? 0;
256
+ }
257
+ return ok({ kind: "u16", data: widened });
258
+ }
259
+ if (role === "joints" && accessor.componentType === COMPONENT_TYPE.U8) {
260
+ const totalValues = accessor.count * componentCount;
261
+ const widened = new Uint16Array(totalValues);
262
+ for (let i = 0; i < totalValues; i++) {
263
+ widened[i] = buffer[absoluteOffset + i] ?? 0;
264
+ }
265
+ return ok({ kind: "u16", data: widened });
266
+ }
267
+ if (accessor.componentType === COMPONENT_TYPE.F32) {
268
+ const out = new Float32Array(accessor.count * componentCount);
269
+ const src = new Float32Array(buffer.buffer, buffer.byteOffset + absoluteOffset, out.length);
270
+ out.set(src);
271
+ return ok({ kind: "f32", data: out });
272
+ }
273
+ if (accessor.componentType === COMPONENT_TYPE.U16) {
274
+ const out = new Uint16Array(accessor.count * componentCount);
275
+ const src = new Uint16Array(buffer.buffer, buffer.byteOffset + absoluteOffset, out.length);
276
+ out.set(src);
277
+ return ok({ kind: "u16", data: out });
278
+ }
279
+ if (accessor.componentType === COMPONENT_TYPE.U32) {
280
+ const out = new Uint32Array(accessor.count * componentCount);
281
+ const src = new Uint32Array(buffer.buffer, buffer.byteOffset + absoluteOffset, out.length);
282
+ out.set(src);
283
+ return ok({ kind: "u32", data: out });
284
+ }
285
+ if (accessor.componentType === COMPONENT_TYPE.I16) {
286
+ const out = new Float32Array(accessor.count * componentCount);
287
+ const src = new Int16Array(buffer.buffer, buffer.byteOffset + absoluteOffset, out.length);
288
+ for (let index = 0; index < src.length; index++) {
289
+ const value = src[index] ?? 0;
290
+ out[index] = accessor.normalized === true ? Math.max(-1, value / 32767) : value;
291
+ }
292
+ return ok({ kind: "f32", data: out });
293
+ }
294
+ return err(
295
+ gltfErr("gltf-accessor-type-mismatch", {
296
+ accessorIndex,
297
+ reason: "unknownComponentType"
298
+ })
299
+ );
300
+ }
301
+
302
+ // src/accessor/decode-color.ts
303
+ var FLOAT = 5126;
304
+ var UNSIGNED_BYTE = 5121;
305
+ var UNSIGNED_SHORT = 5123;
306
+ function unsupported(input, reason) {
307
+ return err(
308
+ gltfErr("gltf-color-accessor-unsupported", {
309
+ semantic: input.semantic,
310
+ accessorIndex: input.accessorIndex,
311
+ reason,
312
+ expectedType: "VEC3 or VEC4",
313
+ expectedComponent: "FLOAT or normalized UNSIGNED_BYTE/UNSIGNED_SHORT",
314
+ ...reason === "normalized" ? { expectedNormalized: true } : {}
315
+ })
316
+ );
317
+ }
318
+ function malformed(input, reason) {
319
+ return err(
320
+ gltfErr("gltf-color-accessor-malformed", {
321
+ semantic: input.semantic,
322
+ accessorIndex: input.accessorIndex,
323
+ reason,
324
+ ...reason === "count" ? { expectedCount: "matches POSITION vertex count and is greater than zero" } : {},
325
+ ...reason === "range" || reason === "finite" ? { expectedRange: "[0,1] finite linear values" } : {}
326
+ })
327
+ );
328
+ }
329
+ function decodeColorAccessor(input, flags = {}) {
330
+ const { accessor, bufferView, buffer } = input;
331
+ if (flags.morph === true) return unsupported(input, "morph");
332
+ if (accessor.sparse !== void 0) return unsupported(input, "sparse");
333
+ if (accessor.type !== "VEC3" && accessor.type !== "VEC4") return unsupported(input, "type");
334
+ if (!Number.isSafeInteger(accessor.count) || accessor.count <= 0)
335
+ return malformed(input, "count");
336
+ let componentByteSize;
337
+ if (accessor.componentType === FLOAT) componentByteSize = 4;
338
+ else if (accessor.componentType === UNSIGNED_BYTE) componentByteSize = 1;
339
+ else if (accessor.componentType === UNSIGNED_SHORT) componentByteSize = 2;
340
+ else return unsupported(input, "component");
341
+ if (accessor.componentType === FLOAT) {
342
+ if (accessor.normalized !== void 0) return unsupported(input, "normalized");
343
+ } else if (accessor.normalized !== true) {
344
+ return unsupported(input, "normalized");
345
+ }
346
+ const componentCount = accessor.type === "VEC3" ? 3 : 4;
347
+ const elementByteLength = componentByteSize * componentCount;
348
+ const byteStride = bufferView.byteStride ?? elementByteLength;
349
+ if (!Number.isSafeInteger(byteStride) || byteStride < elementByteLength || byteStride % componentByteSize !== 0) {
350
+ return malformed(input, "bounds");
351
+ }
352
+ if (input.bufferIndex !== void 0 && bufferView.buffer !== input.bufferIndex) {
353
+ return malformed(input, "reference");
354
+ }
355
+ const accessorByteOffset = accessor.byteOffset ?? 0;
356
+ const viewByteOffset = bufferView.byteOffset ?? 0;
357
+ const lastElementEnd = accessorByteOffset + byteStride * (accessor.count - 1) + elementByteLength;
358
+ if (!Number.isSafeInteger(accessorByteOffset) || accessorByteOffset < 0 || lastElementEnd > bufferView.byteLength) {
359
+ return malformed(input, "bounds");
360
+ }
361
+ const absoluteLastElementEnd = viewByteOffset + lastElementEnd;
362
+ if (!Number.isSafeInteger(viewByteOffset) || viewByteOffset < 0 || absoluteLastElementEnd > buffer.byteLength) {
363
+ return malformed(input, "bounds");
364
+ }
365
+ const output = new Float32Array(accessor.count * 4);
366
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
367
+ const base = viewByteOffset + accessorByteOffset;
368
+ for (let vertex = 0; vertex < accessor.count; vertex++) {
369
+ const sourceOffset = base + vertex * byteStride;
370
+ for (let component = 0; component < componentCount; component++) {
371
+ const byteOffset = sourceOffset + component * componentByteSize;
372
+ let value;
373
+ if (accessor.componentType === FLOAT) value = view.getFloat32(byteOffset, true);
374
+ else if (accessor.componentType === UNSIGNED_BYTE) value = view.getUint8(byteOffset) / 255;
375
+ else value = view.getUint16(byteOffset, true) / 65535;
376
+ if (!Number.isFinite(value)) return malformed(input, "finite");
377
+ if (value < 0 || value > 1) return malformed(input, "range");
378
+ output[vertex * 4 + component] = value;
379
+ }
380
+ if (componentCount === 3) output[vertex * 4 + 3] = 1;
381
+ }
382
+ return ok(output);
383
+ }
384
+
385
+ // src/check-extensions.ts
386
+ var EXTENSION_ALLOWLIST = [
387
+ "EXT_mesh_gpu_instancing",
388
+ "EXT_meshopt_compression"
389
+ ];
390
+ var SUPPORTED_EXTENSIONS = [
391
+ ...EXTENSION_ALLOWLIST,
392
+ "KHR_lights_punctual",
393
+ "KHR_texture_transform"
394
+ ];
395
+ function checkExtensions(json) {
396
+ const required = json.extensionsRequired ?? [];
397
+ for (const ext of required) {
398
+ if (!SUPPORTED_EXTENSIONS.includes(ext)) {
399
+ return err(
400
+ gltfErr("gltf-extension-unsupported", {
401
+ extension: ext,
402
+ source: "extensionsRequired"
403
+ })
404
+ );
405
+ }
406
+ }
407
+ const used = json.extensionsUsed ?? [];
408
+ const unsupportedUsed = [];
409
+ for (const ext of used) {
410
+ if (!SUPPORTED_EXTENSIONS.includes(ext)) {
411
+ unsupportedUsed.push(ext);
412
+ }
413
+ }
414
+ return ok({ unsupportedUsed });
415
+ }
416
+
417
+ // src/data-uri.ts
418
+ var DATA_URI_BASE64_RE = /^data:[^;,]*(?:;[^,;]+)*;base64,(.*)$/;
419
+ var Base64DecodeError = class extends Error {
420
+ constructor(cause) {
421
+ super(
422
+ `base64 payload decode failed: ${cause instanceof Error ? cause.message : String(cause)}`
423
+ );
424
+ this.name = "Base64DecodeError";
425
+ }
426
+ };
427
+ function dataUriBase64Payload(uri) {
428
+ const match = DATA_URI_BASE64_RE.exec(uri);
429
+ return match === null ? void 0 : match[1] ?? "";
430
+ }
431
+ function decodeBase64(b64) {
432
+ let binary;
433
+ try {
434
+ binary = atob(b64);
435
+ } catch (cause) {
436
+ throw new Base64DecodeError(cause);
437
+ }
438
+ const out = new Uint8Array(binary.length);
439
+ for (let i = 0; i < binary.length; i++) {
440
+ out[i] = binary.charCodeAt(i);
441
+ }
442
+ return out;
443
+ }
444
+
445
+ // src/meshopt-decode.ts
446
+ function viewBytes(view, buffers) {
447
+ const buffer = buffers[view.buffer];
448
+ const offset = view.byteOffset ?? 0;
449
+ if (buffer === void 0 || view.byteLength <= 0 || offset < 0 || offset + view.byteLength > buffer.length) {
450
+ return void 0;
451
+ }
452
+ return buffer.subarray(offset, offset + view.byteLength);
453
+ }
454
+ function validMode(mode) {
455
+ return GLTF_MESHOPT_MODES.some((candidate) => candidate === mode);
456
+ }
457
+ function validFilter(filter) {
458
+ return GLTF_MESHOPT_FILTERS.some((candidate) => candidate === filter);
459
+ }
460
+ function filterAllowed(mode, filter) {
461
+ return mode === "ATTRIBUTES" || filter === "NONE";
462
+ }
463
+ async function projectMeshoptBufferViews(inputViews, inputBuffers, extensionsRequired, capability) {
464
+ const buffers = [...inputBuffers];
465
+ const bufferViews = inputViews.map((view) => ({ ...view }));
466
+ let decodedCount = 0;
467
+ for (let index = 0; index < bufferViews.length; index++) {
468
+ const view = bufferViews[index];
469
+ if (view === void 0) continue;
470
+ const extension = view.extensions?.EXT_meshopt_compression;
471
+ if (extension === void 0) continue;
472
+ const filter = extension.filter ?? "NONE";
473
+ if (!validMode(extension.mode) || !validFilter(filter) || !filterAllowed(extension.mode, filter) || !Number.isInteger(extension.byteStride) || extension.byteStride <= 0 || !Number.isInteger(extension.count) || extension.count <= 0 || !Number.isInteger(extension.byteLength) || extension.byteLength <= 0) {
474
+ return err(
475
+ gltfErr("gltf-meshopt-decode-failed", {
476
+ bufferView: index,
477
+ actual: "invalid mode/filter/stride/count/byteLength",
478
+ mode: validMode(extension.mode) ? extension.mode : "ATTRIBUTES",
479
+ filter: validFilter(filter) ? filter : "NONE"
480
+ })
481
+ );
482
+ }
483
+ const fallback = viewBytes(view, buffers);
484
+ const required = extensionsRequired.includes("EXT_meshopt_compression");
485
+ if (capability === void 0 && !required && fallback !== void 0) continue;
486
+ if (capability === void 0) {
487
+ return err(
488
+ gltfErr("gltf-meshopt-decoder-required", {
489
+ bufferView: index,
490
+ actual: required ? "required" : "compressed-only",
491
+ hasCoreFallback: fallback !== void 0
492
+ })
493
+ );
494
+ }
495
+ const compressedBuffer = buffers[extension.buffer];
496
+ const compressedOffset = extension.byteOffset ?? 0;
497
+ if (compressedBuffer === void 0 || !Number.isInteger(compressedOffset) || compressedOffset < 0 || compressedOffset + extension.byteLength > compressedBuffer.length) {
498
+ return err(
499
+ gltfErr("gltf-meshopt-decode-failed", {
500
+ bufferView: index,
501
+ actual: "compressed range out of bounds",
502
+ mode: extension.mode,
503
+ filter
504
+ })
505
+ );
506
+ }
507
+ let decoded;
508
+ try {
509
+ decoded = await capability.decode({
510
+ source: compressedBuffer.subarray(
511
+ compressedOffset,
512
+ compressedOffset + extension.byteLength
513
+ ),
514
+ count: extension.count,
515
+ stride: extension.byteStride,
516
+ mode: extension.mode,
517
+ filter
518
+ });
519
+ } catch (cause) {
520
+ return err(
521
+ gltfErr("gltf-meshopt-decode-failed", {
522
+ bufferView: index,
523
+ actual: `decoder rejected compressed range: ${cause instanceof Error ? cause.message : String(cause)}`,
524
+ mode: extension.mode,
525
+ filter
526
+ })
527
+ );
528
+ }
529
+ const expectedLength = extension.count * extension.byteStride;
530
+ if (!(decoded instanceof Uint8Array) || decoded.byteLength !== expectedLength) {
531
+ return err(
532
+ gltfErr("gltf-meshopt-decode-failed", {
533
+ bufferView: index,
534
+ actual: `decoded byteLength=${decoded?.byteLength ?? "invalid"}, expected=${expectedLength}`,
535
+ mode: extension.mode,
536
+ filter
537
+ })
538
+ );
539
+ }
540
+ const decodedBufferIndex = buffers.length;
541
+ buffers.push(decoded);
542
+ const { extensions: _extensions, ...plainView } = view;
543
+ bufferViews[index] = {
544
+ ...plainView,
545
+ buffer: decodedBufferIndex,
546
+ byteOffset: 0,
547
+ byteLength: expectedLength
548
+ };
549
+ decodedCount++;
550
+ }
551
+ return ok({ bufferViews, buffers, decodedCount });
552
+ }
553
+
554
+ // src/node-path.ts
555
+ function buildNodeParentMap(nodes) {
556
+ const parents = /* @__PURE__ */ new Map();
557
+ for (let index = 0; index < nodes.length; index++) {
558
+ for (const child of nodes[index]?.children ?? []) parents.set(child, index);
559
+ }
560
+ return parents;
561
+ }
562
+ function resolveNamedNodePath(nodes, parents, nodeIndex) {
563
+ const reversed = [];
564
+ const visited = /* @__PURE__ */ new Set();
565
+ let current = nodeIndex;
566
+ while (current !== void 0) {
567
+ if (visited.has(current)) {
568
+ return { ok: false, reason: "hierarchy-cycle", nodeIndex: current };
569
+ }
570
+ visited.add(current);
571
+ const name = nodes[current]?.name;
572
+ if (name === void 0 || name.length === 0) {
573
+ return { ok: false, reason: "name-missing", nodeIndex: current };
574
+ }
575
+ reversed.push(name);
576
+ current = parents.get(current);
577
+ }
578
+ return { ok: true, value: reversed.reverse() };
579
+ }
580
+
581
+ // src/parse-animation.ts
582
+ var ANIMATION_ACCESSOR_TYPES = ["SCALAR", "VEC2", "VEC3", "VEC4"];
583
+ function parseAnimation(animationsJson, nodesJson, accessors, bufferViews, buffers) {
584
+ if (animationsJson === void 0 || animationsJson.length === 0) {
585
+ return ok([]);
586
+ }
587
+ const clips = [];
588
+ const parentOf = buildNodeParentMap(nodesJson);
589
+ const nodeByPath = /* @__PURE__ */ new Map();
590
+ const pathById = /* @__PURE__ */ new Map();
591
+ for (let animIdx = 0; animIdx < animationsJson.length; animIdx++) {
592
+ const anim = animationsJson[animIdx];
593
+ if (anim === void 0) continue;
594
+ const decodedSamplers = [];
595
+ for (let sampIdx = 0; sampIdx < anim.samplers.length; sampIdx++) {
596
+ const sampler = anim.samplers[sampIdx];
597
+ if (sampler === void 0) continue;
598
+ const interpolation = sampler.interpolation ?? "LINEAR";
599
+ if (interpolation === "CUBICSPLINE") {
600
+ return err(
601
+ gltfErr("gltf-animation-cubicspline-unsupported", {
602
+ animationIndex: animIdx,
603
+ samplerIndex: sampIdx
604
+ })
605
+ );
606
+ }
607
+ if (interpolation !== "LINEAR" && interpolation !== "STEP") {
608
+ return err(
609
+ gltfErr("gltf-animation-cubicspline-unsupported", {
610
+ animationIndex: animIdx,
611
+ samplerIndex: sampIdx
612
+ })
613
+ );
614
+ }
615
+ const inputAcc = accessors[sampler.input];
616
+ if (inputAcc === void 0) {
617
+ return err(
618
+ gltfErr("gltf-buffer-out-of-bounds", {
619
+ accessor: sampler.input,
620
+ byteOffset: 0,
621
+ byteLength: 0,
622
+ bufferIndex: 0
623
+ })
624
+ );
625
+ }
626
+ const inputResult = decodeF32Accessor(
627
+ sampler.input,
628
+ inputAcc,
629
+ ANIMATION_ACCESSOR_TYPES,
630
+ bufferViews,
631
+ buffers
632
+ );
633
+ if (!inputResult.ok) return err(inputResult.error);
634
+ const outputAcc = accessors[sampler.output];
635
+ if (outputAcc === void 0) {
636
+ return err(
637
+ gltfErr("gltf-buffer-out-of-bounds", {
638
+ accessor: sampler.output,
639
+ byteOffset: 0,
640
+ byteLength: 0,
641
+ bufferIndex: 0
642
+ })
643
+ );
644
+ }
645
+ const outputResult = decodeF32Accessor(
646
+ sampler.output,
647
+ outputAcc,
648
+ ANIMATION_ACCESSOR_TYPES,
649
+ bufferViews,
650
+ buffers
651
+ );
652
+ if (!outputResult.ok) return err(outputResult.error);
653
+ decodedSamplers.push({
654
+ input: inputResult.value,
655
+ output: outputResult.value,
656
+ interpolation
657
+ });
658
+ }
659
+ const channels = [];
660
+ for (let chIdx = 0; chIdx < anim.channels.length; chIdx++) {
661
+ const ch = anim.channels[chIdx];
662
+ if (ch === void 0) continue;
663
+ const samplerRecord = decodedSamplers[ch.sampler];
664
+ if (samplerRecord === void 0) {
665
+ return err(
666
+ gltfErr("gltf-buffer-out-of-bounds", {
667
+ accessor: ch.sampler,
668
+ byteOffset: 0,
669
+ byteLength: 0,
670
+ bufferIndex: 0
671
+ })
672
+ );
673
+ }
674
+ const targetNodeIdx = ch.target.node;
675
+ const path = targetNodeIdx === void 0 ? { ok: false, reason: "name-missing", nodeIndex: -1 } : resolveNamedNodePath(nodesJson, parentOf, targetNodeIdx);
676
+ if (!path.ok) {
677
+ return err(
678
+ gltfErr("gltf-animation-target-invalid", {
679
+ reason: path.reason,
680
+ animationIndex: animIdx,
681
+ channelIndex: chIdx,
682
+ nodeIndex: path.nodeIndex
683
+ })
684
+ );
685
+ }
686
+ if (ch.target.path !== "weights" && ch.target.path !== "translation" && ch.target.path !== "rotation" && ch.target.path !== "scale") {
687
+ return err(
688
+ gltfErr("gltf-morph-unsupported", {
689
+ animationIndex: animIdx,
690
+ channelIndex: chIdx,
691
+ nodeIndex: ch.target.node ?? -1
692
+ })
693
+ );
694
+ }
695
+ const pathKey = JSON.stringify(path.value);
696
+ const previousNode = nodeByPath.get(pathKey);
697
+ if (previousNode !== void 0 && previousNode !== targetNodeIdx) {
698
+ return err(
699
+ gltfErr("gltf-animation-target-invalid", {
700
+ reason: "path-duplicate",
701
+ animationIndex: animIdx,
702
+ channelIndex: chIdx,
703
+ nodeIndex: targetNodeIdx
704
+ })
705
+ );
706
+ }
707
+ nodeByPath.set(pathKey, targetNodeIdx);
708
+ const targetId = deriveAnimationTargetId(path.value);
709
+ const previousPath = pathById.get(targetId);
710
+ if (previousPath !== void 0 && previousPath !== pathKey) {
711
+ return err(
712
+ gltfErr("gltf-animation-target-invalid", {
713
+ reason: "id-collision",
714
+ animationIndex: animIdx,
715
+ channelIndex: chIdx,
716
+ nodeIndex: targetNodeIdx
717
+ })
718
+ );
719
+ }
720
+ pathById.set(targetId, pathKey);
721
+ channels.push({
722
+ targetId,
723
+ targetNodeIndex: targetNodeIdx,
724
+ property: ch.target.path,
725
+ sampler: samplerRecord
726
+ });
727
+ }
728
+ let duration = 0;
729
+ for (const ch of channels) {
730
+ const input = ch.sampler.input;
731
+ if (input.length > 0) {
732
+ const last = input[input.length - 1];
733
+ if (last !== void 0 && last > duration) {
734
+ duration = last;
735
+ }
736
+ }
737
+ }
738
+ const name = anim.name?.trim();
739
+ clips.push({
740
+ ...name === void 0 || name.length === 0 ? {} : { name },
741
+ duration,
742
+ channels
743
+ });
744
+ }
745
+ return ok(clips);
746
+ }
747
+
748
+ // src/parse-gltf-header.ts
749
+ function parseGltfHeader(json, filePath) {
750
+ if (json === null || typeof json !== "object" || !("asset" in json)) {
751
+ return err(
752
+ gltfErr("gltf-malformed-header", {
753
+ filePath,
754
+ byteOffset: 0
755
+ })
756
+ );
757
+ }
758
+ const asset = json.asset;
759
+ if (!asset || typeof asset !== "object" || typeof asset.version !== "string") {
760
+ return err(
761
+ gltfErr("gltf-malformed-header", {
762
+ filePath,
763
+ byteOffset: 0
764
+ })
765
+ );
766
+ }
767
+ if (asset.version !== "2.0") {
768
+ return err(
769
+ gltfErr("gltf-version-unsupported", {
770
+ filePath,
771
+ actualVersion: asset.version
772
+ })
773
+ );
774
+ }
775
+ return ok(json);
776
+ }
777
+
778
+ // src/parse-glb-chunks.ts
779
+ var GLB_HEADER_SIZE = 12;
780
+ var GLB_MAGIC = 1179937895;
781
+ var CHUNK_HEADER_SIZE = 8;
782
+ var CHUNK_TYPE_JSON = 1313821514;
783
+ var CHUNK_TYPE_BIN = 5130562;
784
+ function parseGlbChunks(buffer, filePath) {
785
+ if (buffer.byteLength < GLB_HEADER_SIZE) {
786
+ return err(
787
+ gltfErr("gltf-malformed-header", {
788
+ filePath,
789
+ byteOffset: 0
790
+ })
791
+ );
792
+ }
793
+ const view = new DataView(buffer);
794
+ const magic = view.getUint32(0, true);
795
+ if (magic !== GLB_MAGIC) {
796
+ return err(
797
+ gltfErr("gltf-malformed-header", {
798
+ filePath,
799
+ byteOffset: 0,
800
+ magic
801
+ })
802
+ );
803
+ }
804
+ const version = view.getUint32(4, true);
805
+ if (version !== 2) {
806
+ return err(
807
+ gltfErr("gltf-version-unsupported", {
808
+ filePath,
809
+ actualVersion: String(version)
810
+ })
811
+ );
812
+ }
813
+ const declaredLength = view.getUint32(8, true);
814
+ if (declaredLength !== buffer.byteLength) {
815
+ return err(
816
+ gltfErr("gltf-malformed-header", {
817
+ filePath,
818
+ byteOffset: 8
819
+ })
820
+ );
821
+ }
822
+ let offset = GLB_HEADER_SIZE;
823
+ if (offset + CHUNK_HEADER_SIZE > buffer.byteLength) {
824
+ return err(
825
+ gltfErr("gltf-malformed-header", {
826
+ filePath,
827
+ byteOffset: offset
828
+ })
829
+ );
830
+ }
831
+ const jsonChunkLength = view.getUint32(offset, true);
832
+ const jsonChunkType = view.getUint32(offset + 4, true);
833
+ if (jsonChunkType !== CHUNK_TYPE_JSON) {
834
+ return err(
835
+ gltfErr("gltf-malformed-header", {
836
+ filePath,
837
+ byteOffset: offset + 4
838
+ })
839
+ );
840
+ }
841
+ if (offset + CHUNK_HEADER_SIZE + jsonChunkLength > buffer.byteLength) {
842
+ return err(
843
+ gltfErr("gltf-malformed-header", {
844
+ filePath,
845
+ byteOffset: offset
846
+ })
847
+ );
848
+ }
849
+ const jsonChunk = new Uint8Array(buffer, offset + CHUNK_HEADER_SIZE, jsonChunkLength);
850
+ offset += CHUNK_HEADER_SIZE + jsonChunkLength;
851
+ let binChunk;
852
+ if (offset < buffer.byteLength) {
853
+ if (offset + CHUNK_HEADER_SIZE > buffer.byteLength) {
854
+ return err(
855
+ gltfErr("gltf-malformed-header", {
856
+ filePath,
857
+ byteOffset: offset
858
+ })
859
+ );
860
+ }
861
+ const binChunkLength = view.getUint32(offset, true);
862
+ const binChunkType = view.getUint32(offset + 4, true);
863
+ if (binChunkType !== CHUNK_TYPE_BIN) {
864
+ return err(
865
+ gltfErr("gltf-malformed-header", {
866
+ filePath,
867
+ byteOffset: offset + 4
868
+ })
869
+ );
870
+ }
871
+ if (offset + CHUNK_HEADER_SIZE + binChunkLength > buffer.byteLength) {
872
+ return err(
873
+ gltfErr("gltf-malformed-header", {
874
+ filePath,
875
+ byteOffset: offset
876
+ })
877
+ );
878
+ }
879
+ binChunk = new Uint8Array(buffer, offset + CHUNK_HEADER_SIZE, binChunkLength);
880
+ }
881
+ return ok({
882
+ version,
883
+ length: declaredLength,
884
+ jsonChunk,
885
+ ...binChunk === void 0 ? {} : { binChunk }
886
+ });
887
+ }
888
+
889
+ // src/parse-skin.ts
890
+ var MAX_JOINTS = 256;
891
+ var SKIN_ACCESSOR_TYPES = ["MAT4"];
892
+ function identityMat4() {
893
+ const m = new Float32Array(16);
894
+ m[0] = 1;
895
+ m[5] = 1;
896
+ m[10] = 1;
897
+ m[15] = 1;
898
+ return m;
899
+ }
900
+ function resolveJointPath(nodeIndex, nodes, parentOf, skinIndex, jointPathIndex) {
901
+ const path = resolveNamedNodePath(nodes, parentOf, nodeIndex);
902
+ if (!path.ok) {
903
+ return err(
904
+ gltfErr("gltf-skin-joint-name-missing", {
905
+ reason: path.reason,
906
+ skinIndex,
907
+ jointPathIndex,
908
+ nodeIndex: path.nodeIndex
909
+ })
910
+ );
911
+ }
912
+ return ok(path.value);
913
+ }
914
+ function parseSkin(skinsJson, nodesJson, accessors, bufferViews, buffers) {
915
+ if (skinsJson === void 0 || skinsJson.length === 0) {
916
+ return ok([]);
917
+ }
918
+ const records = [];
919
+ const parentOf = buildNodeParentMap(nodesJson);
920
+ for (let skinIdx = 0; skinIdx < skinsJson.length; skinIdx++) {
921
+ const skin = skinsJson[skinIdx];
922
+ if (skin === void 0) continue;
923
+ const joints = skin.joints;
924
+ if (joints.length > MAX_JOINTS) {
925
+ return err(
926
+ gltfErr("gltf-skin-joint-count-exceeded", {
927
+ skinIndex: skinIdx,
928
+ jointCount: joints.length,
929
+ maxJoints: MAX_JOINTS
930
+ })
931
+ );
932
+ }
933
+ let ibm;
934
+ if (skin.inverseBindMatrices !== void 0) {
935
+ const ibmAccIdx = skin.inverseBindMatrices;
936
+ const ibmAcc = accessors[ibmAccIdx];
937
+ if (ibmAcc === void 0) {
938
+ return err(
939
+ gltfErr("gltf-buffer-out-of-bounds", {
940
+ accessor: ibmAccIdx,
941
+ byteOffset: 0,
942
+ byteLength: 0,
943
+ bufferIndex: 0
944
+ })
945
+ );
946
+ }
947
+ const ibmResult = decodeF32Accessor(
948
+ ibmAccIdx,
949
+ ibmAcc,
950
+ SKIN_ACCESSOR_TYPES,
951
+ bufferViews,
952
+ buffers
953
+ );
954
+ if (!ibmResult.ok) return err(ibmResult.error);
955
+ ibm = ibmResult.value;
956
+ } else {
957
+ ibm = new Float32Array(joints.length * 16);
958
+ for (let j = 0; j < joints.length; j++) {
959
+ ibm.set(identityMat4(), j * 16);
960
+ }
961
+ }
962
+ const jointPaths = [];
963
+ for (let j = 0; j < joints.length; j++) {
964
+ const jointNodeIndex = joints[j];
965
+ if (jointNodeIndex === void 0) continue;
966
+ const pathResult = resolveJointPath(jointNodeIndex, nodesJson, parentOf, skinIdx, j);
967
+ if (!pathResult.ok) return err(pathResult.error);
968
+ jointPaths.push(pathResult.value.join("/"));
969
+ }
970
+ records.push({
971
+ jointCount: joints.length,
972
+ inverseBindMatrices: ibm,
973
+ jointPaths
974
+ });
975
+ }
976
+ return ok(records);
977
+ }
978
+ var IDENTITY = {
979
+ translation: [0, 0, 0],
980
+ rotation: [0, 0, 0, 1],
981
+ scale: [1, 1, 1]
982
+ };
983
+ function tuple3(arr, fallback) {
984
+ if (arr === void 0 || arr.length < 3) return fallback;
985
+ return [arr[0] ?? fallback[0], arr[1] ?? fallback[1], arr[2] ?? fallback[2]];
986
+ }
987
+ function tuple4(arr, fallback) {
988
+ if (arr === void 0 || arr.length < 4) return fallback;
989
+ return [
990
+ arr[0] ?? fallback[0],
991
+ arr[1] ?? fallback[1],
992
+ arr[2] ?? fallback[2],
993
+ arr[3] ?? fallback[3]
994
+ ];
995
+ }
996
+ function decomposeNodeTransform(node, nodeIndex, diagnostics) {
997
+ const hasMatrix = node.matrix !== void 0 && node.matrix.length === 16;
998
+ const hasTrs = node.translation !== void 0 || node.rotation !== void 0 || node.scale !== void 0;
999
+ if (hasMatrix) {
1000
+ if (hasTrs) {
1001
+ console.error(`[warn] node[${nodeIndex}] has both matrix and TRS, matrix takes precedence`);
1002
+ diagnostics.matrixTrsCoexistNodes.push(nodeIndex);
1003
+ }
1004
+ const out_t = vec3.create();
1005
+ const out_r = quat.create();
1006
+ const out_s = vec3.create(1, 1, 1);
1007
+ const m = node.matrix;
1008
+ mat4.decompose(out_t, out_r, out_s, m);
1009
+ return {
1010
+ translation: [out_t[0] ?? 0, out_t[1] ?? 0, out_t[2] ?? 0],
1011
+ rotation: [out_r[0] ?? 0, out_r[1] ?? 0, out_r[2] ?? 0, out_r[3] ?? 1],
1012
+ scale: [out_s[0] ?? 1, out_s[1] ?? 1, out_s[2] ?? 1]
1013
+ };
1014
+ }
1015
+ if (!hasTrs) {
1016
+ return IDENTITY;
1017
+ }
1018
+ return {
1019
+ translation: tuple3(node.translation, IDENTITY.translation),
1020
+ rotation: tuple4(node.rotation, IDENTITY.rotation),
1021
+ scale: tuple3(node.scale, IDENTITY.scale)
1022
+ };
1023
+ }
1024
+
1025
+ // src/parse-gltf.ts
1026
+ function decodeNodeInstancing(nodeIndex, attributes, accessors, bufferViews, buffers) {
1027
+ const tIdx = attributes.TRANSLATION;
1028
+ const rIdx = attributes.ROTATION;
1029
+ const sIdx = attributes.SCALE;
1030
+ let count;
1031
+ const setOrCheck = (label, n2) => {
1032
+ if (count === void 0) {
1033
+ count = n2;
1034
+ return null;
1035
+ }
1036
+ if (n2 !== count) {
1037
+ return gltfErr("gltf-instancing-count-mismatch", {
1038
+ nodeIndex,
1039
+ accessor: label,
1040
+ expectedCount: count,
1041
+ actualCount: n2
1042
+ });
1043
+ }
1044
+ return null;
1045
+ };
1046
+ let tValues;
1047
+ if (tIdx !== void 0) {
1048
+ const acc = accessors[tIdx];
1049
+ if (acc === void 0) return err(unknownAccessor(tIdx));
1050
+ const e = setOrCheck("TRANSLATION", acc.count);
1051
+ if (e !== null) return err(e);
1052
+ const decoded = decodeAttributeAccessor(tIdx, acc, bufferViews, buffers);
1053
+ if (!decoded.ok) return err(decoded.error);
1054
+ tValues = decoded.value;
1055
+ }
1056
+ let rValues;
1057
+ if (rIdx !== void 0) {
1058
+ const acc = accessors[rIdx];
1059
+ if (acc === void 0) return err(unknownAccessor(rIdx));
1060
+ const e = setOrCheck("ROTATION", acc.count);
1061
+ if (e !== null) return err(e);
1062
+ const decoded = decodeAttributeAccessor(rIdx, acc, bufferViews, buffers);
1063
+ if (!decoded.ok) return err(decoded.error);
1064
+ rValues = decoded.value;
1065
+ }
1066
+ let sValues;
1067
+ if (sIdx !== void 0) {
1068
+ const acc = accessors[sIdx];
1069
+ if (acc === void 0) return err(unknownAccessor(sIdx));
1070
+ const e = setOrCheck("SCALE", acc.count);
1071
+ if (e !== null) return err(e);
1072
+ const decoded = decodeAttributeAccessor(sIdx, acc, bufferViews, buffers);
1073
+ if (!decoded.ok) return err(decoded.error);
1074
+ sValues = decoded.value;
1075
+ }
1076
+ const n = count ?? 0;
1077
+ const transforms = new Float32Array(n * 16);
1078
+ const tmp = mat4.create();
1079
+ const tv = vec3.create();
1080
+ const rv = quat.create();
1081
+ rv[3] = 1;
1082
+ const sv = vec3.create(1, 1, 1);
1083
+ for (let i = 0; i < n; i++) {
1084
+ if (tValues !== void 0) {
1085
+ tv[0] = tValues[i * 3] ?? 0;
1086
+ tv[1] = tValues[i * 3 + 1] ?? 0;
1087
+ tv[2] = tValues[i * 3 + 2] ?? 0;
1088
+ } else {
1089
+ tv[0] = 0;
1090
+ tv[1] = 0;
1091
+ tv[2] = 0;
1092
+ }
1093
+ if (rValues !== void 0) {
1094
+ rv[0] = rValues[i * 4] ?? 0;
1095
+ rv[1] = rValues[i * 4 + 1] ?? 0;
1096
+ rv[2] = rValues[i * 4 + 2] ?? 0;
1097
+ rv[3] = rValues[i * 4 + 3] ?? 1;
1098
+ } else {
1099
+ rv[0] = 0;
1100
+ rv[1] = 0;
1101
+ rv[2] = 0;
1102
+ rv[3] = 1;
1103
+ }
1104
+ if (sValues !== void 0) {
1105
+ sv[0] = sValues[i * 3] ?? 1;
1106
+ sv[1] = sValues[i * 3 + 1] ?? 1;
1107
+ sv[2] = sValues[i * 3 + 2] ?? 1;
1108
+ } else {
1109
+ sv[0] = 1;
1110
+ sv[1] = 1;
1111
+ sv[2] = 1;
1112
+ }
1113
+ mat4.compose(tmp, tv, rv, sv);
1114
+ for (let k = 0; k < 16; k++) {
1115
+ transforms[i * 16 + k] = tmp[k] ?? 0;
1116
+ }
1117
+ }
1118
+ return ok({ count: n, transforms });
1119
+ }
1120
+ function unknownAccessor(accessorIndex) {
1121
+ return gltfErr("gltf-accessor-type-mismatch", {
1122
+ accessorIndex,
1123
+ reason: "unknownComponentType"
1124
+ });
1125
+ }
1126
+ function decodeAttributeAccessor(accessorIndex, accessor, bufferViews, buffers) {
1127
+ const view = bufferViews[accessor.bufferView ?? -1];
1128
+ if (view === void 0) return err(unknownAccessor(accessorIndex));
1129
+ const buf = buffers[view.buffer];
1130
+ if (buf === void 0) return err(unknownAccessor(accessorIndex));
1131
+ const decoded = decodeAccessor({
1132
+ accessorIndex,
1133
+ accessor,
1134
+ bufferView: view,
1135
+ buffer: buf,
1136
+ role: "attribute"
1137
+ });
1138
+ if (!decoded.ok) return err(decoded.error);
1139
+ if (decoded.value.kind !== "f32") return err(unknownAccessor(accessorIndex));
1140
+ return ok(decoded.value.data);
1141
+ }
1142
+ function tuple2(values) {
1143
+ if (values === void 0 || values.length < 2) return void 0;
1144
+ return [values[0] ?? 0, values[1] ?? 0];
1145
+ }
1146
+ function parseTextureInfo(info, textures) {
1147
+ const transformJson = info.extensions?.KHR_texture_transform;
1148
+ const offset = tuple2(transformJson?.offset);
1149
+ const scale = tuple2(transformJson?.scale);
1150
+ const transform = offset === void 0 && transformJson?.rotation === void 0 && scale === void 0 ? void 0 : {
1151
+ ...offset === void 0 ? {} : { offset },
1152
+ ...transformJson?.rotation === void 0 ? {} : { rotation: transformJson.rotation },
1153
+ ...scale === void 0 ? {} : { scale }
1154
+ };
1155
+ const sampler = textures[info.index]?.sampler;
1156
+ return {
1157
+ texture: info.index,
1158
+ ...sampler === void 0 ? {} : { sampler },
1159
+ ...info.texCoord === void 0 && transformJson?.texCoord === void 0 ? {} : { texCoord: transformJson?.texCoord ?? info.texCoord },
1160
+ ...transform === void 0 ? {} : { transform }
1161
+ };
1162
+ }
1163
+ async function resolveBuffer(buf, externalLoader, binChunk) {
1164
+ if (buf.uri === void 0) {
1165
+ if (binChunk === void 0) {
1166
+ throw new Error("parseGltf: buffer 0 has no uri and no GLB BIN chunk available");
1167
+ }
1168
+ return binChunk;
1169
+ }
1170
+ const dataPayload = dataUriBase64Payload(buf.uri);
1171
+ if (dataPayload !== void 0) {
1172
+ return decodeBase64(dataPayload);
1173
+ }
1174
+ const arrayBuffer = await externalLoader(buf.uri);
1175
+ return new Uint8Array(arrayBuffer);
1176
+ }
1177
+ function invalidBufferDataUriError(filePath, bufferIndex, cause) {
1178
+ return new ImportError({
1179
+ code: "source-validation-failed",
1180
+ expected: `glTF buffer ${bufferIndex} data URI to contain a valid base64 payload`,
1181
+ hint: IMPORT_ERROR_HINTS["source-validation-failed"],
1182
+ detail: {
1183
+ diagnostics: [
1184
+ {
1185
+ code: "gltf-buffer-data-uri-invalid",
1186
+ severity: "error",
1187
+ sourcePath: filePath,
1188
+ sourceRange: { start: 0, end: 0, line: 1, column: 1 },
1189
+ rule: "gltf-buffer-data-uri-base64",
1190
+ expected: "a valid base64 payload after ;base64,",
1191
+ actual: `buffer ${bufferIndex}: ${cause.message}`,
1192
+ hint: "repair the buffer data URI or provide a valid external .bin sibling"
1193
+ }
1194
+ ]
1195
+ }
1196
+ });
1197
+ }
1198
+ function parsePunctualLights(json, filePath) {
1199
+ const source = json.extensions?.KHR_lights_punctual?.lights ?? [];
1200
+ const lights = [];
1201
+ for (const light of source) {
1202
+ const type = light?.type;
1203
+ if (type !== "directional" && type !== "point" && type !== "spot") {
1204
+ return err(gltfErr("gltf-malformed-header", { filePath, byteOffset: 0 }));
1205
+ }
1206
+ const color = light.color ?? [1, 1, 1];
1207
+ if (color.length < 3 || color.slice(0, 3).some(
1208
+ (value) => typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1
1209
+ )) {
1210
+ return err(gltfErr("gltf-malformed-header", { filePath, byteOffset: 0 }));
1211
+ }
1212
+ const intensity = light.intensity ?? 1;
1213
+ if (typeof intensity !== "number" || !Number.isFinite(intensity) || intensity < 0) {
1214
+ return err(gltfErr("gltf-malformed-header", { filePath, byteOffset: 0 }));
1215
+ }
1216
+ const range = light.range;
1217
+ if (range !== void 0 && (typeof range !== "number" || !Number.isFinite(range) || range < 0)) {
1218
+ return err(gltfErr("gltf-malformed-header", { filePath, byteOffset: 0 }));
1219
+ }
1220
+ let spot;
1221
+ if (type === "spot") {
1222
+ const innerConeAngle = light.spot?.innerConeAngle ?? 0;
1223
+ const outerConeAngle = light.spot?.outerConeAngle ?? Math.PI / 4;
1224
+ if (!Number.isFinite(innerConeAngle) || !Number.isFinite(outerConeAngle) || innerConeAngle < 0 || innerConeAngle >= outerConeAngle || outerConeAngle > Math.PI / 2) {
1225
+ return err(gltfErr("gltf-malformed-header", { filePath, byteOffset: 0 }));
1226
+ }
1227
+ spot = { innerConeAngle, outerConeAngle };
1228
+ }
1229
+ lights.push({
1230
+ type,
1231
+ color: [color[0] ?? 1, color[1] ?? 1, color[2] ?? 1],
1232
+ intensity,
1233
+ ...range === void 0 || range === 0 ? {} : { range },
1234
+ ...spot === void 0 ? {} : { spot }
1235
+ });
1236
+ }
1237
+ return ok(lights);
1238
+ }
1239
+ async function parseGltfWithBin(json, ctx) {
1240
+ const headerResult = parseGltfHeader(json, ctx.filePath);
1241
+ if (!headerResult.ok) return err(headerResult.error);
1242
+ const extResult = checkExtensions(json);
1243
+ if (!extResult.ok) return err(extResult.error);
1244
+ const unsupportedExtensions = extResult.value.unsupportedUsed;
1245
+ const lightsResult = parsePunctualLights(json, ctx.filePath);
1246
+ if (!lightsResult.ok) return err(lightsResult.error);
1247
+ const meshesJson = json.meshes ?? [];
1248
+ const buffersJson = json.buffers ?? [];
1249
+ const buffers = [];
1250
+ for (let i = 0; i < buffersJson.length; i++) {
1251
+ const bufJson = buffersJson[i];
1252
+ if (bufJson === void 0) continue;
1253
+ try {
1254
+ const bytes = await resolveBuffer(bufJson, ctx.externalLoader, ctx.binChunk);
1255
+ buffers.push(bytes);
1256
+ } catch (e) {
1257
+ if (e instanceof Base64DecodeError) {
1258
+ return err(invalidBufferDataUriError(ctx.filePath, i, e));
1259
+ }
1260
+ return err(
1261
+ gltfErr("gltf-malformed-header", {
1262
+ filePath: ctx.filePath,
1263
+ byteOffset: 0
1264
+ })
1265
+ );
1266
+ }
1267
+ }
1268
+ const accessors = json.accessors ?? [];
1269
+ const rawBufferViews = json.bufferViews ?? [];
1270
+ const projected = await projectMeshoptBufferViews(
1271
+ rawBufferViews,
1272
+ buffers,
1273
+ json.extensionsRequired ?? [],
1274
+ ctx.meshopt
1275
+ );
1276
+ if (!projected.ok) return err(projected.error);
1277
+ const bufferViews = projected.value.bufferViews;
1278
+ buffers.splice(0, buffers.length, ...projected.value.buffers);
1279
+ const meshes = [];
1280
+ const meshPrimitiveCount = /* @__PURE__ */ new Map();
1281
+ for (let meshIndex = 0; meshIndex < meshesJson.length; meshIndex++) {
1282
+ const meshJson = meshesJson[meshIndex];
1283
+ if (meshJson === void 0) continue;
1284
+ meshPrimitiveCount.set(meshIndex, meshJson.primitives.length);
1285
+ for (const prim of meshJson.primitives) {
1286
+ if (prim === void 0) continue;
1287
+ const positionAccessorIndex = prim.attributes?.POSITION;
1288
+ if (positionAccessorIndex === void 0) {
1289
+ return err(
1290
+ gltfErr("gltf-accessor-type-mismatch", {
1291
+ accessorIndex: -1,
1292
+ reason: "unknownComponentType"
1293
+ })
1294
+ );
1295
+ }
1296
+ const positionAccessor = accessors[positionAccessorIndex];
1297
+ const positionBufferView = bufferViews[positionAccessor?.bufferView ?? -1];
1298
+ if (positionAccessor === void 0 || positionBufferView === void 0) {
1299
+ return err(
1300
+ gltfErr("gltf-buffer-out-of-bounds", {
1301
+ accessor: positionAccessorIndex,
1302
+ byteOffset: 0,
1303
+ byteLength: 0,
1304
+ bufferIndex: positionBufferView?.buffer ?? 0
1305
+ })
1306
+ );
1307
+ }
1308
+ const positionBuffer = buffers[positionBufferView.buffer];
1309
+ if (positionBuffer === void 0) {
1310
+ return err(
1311
+ gltfErr("gltf-buffer-out-of-bounds", {
1312
+ accessor: positionAccessorIndex,
1313
+ byteOffset: positionBufferView.byteOffset ?? 0,
1314
+ byteLength: positionBufferView.byteLength,
1315
+ bufferIndex: positionBufferView.buffer
1316
+ })
1317
+ );
1318
+ }
1319
+ const positionDecoded = decodeAccessor({
1320
+ accessorIndex: positionAccessorIndex,
1321
+ accessor: positionAccessor,
1322
+ bufferView: positionBufferView,
1323
+ buffer: positionBuffer,
1324
+ role: "attribute"
1325
+ });
1326
+ if (!positionDecoded.ok) return err(positionDecoded.error);
1327
+ if (positionDecoded.value.kind !== "f32") {
1328
+ return err(
1329
+ gltfErr("gltf-accessor-type-mismatch", {
1330
+ accessorIndex: positionAccessorIndex,
1331
+ reason: "unknownComponentType"
1332
+ })
1333
+ );
1334
+ }
1335
+ const positionsDecoded = positionDecoded.value.data;
1336
+ const positions = new Float32Array(positionsDecoded.length);
1337
+ positions.set(positionsDecoded);
1338
+ const attrs = prim.attributes ?? {};
1339
+ let colors0;
1340
+ const colorIdx = attrs.COLOR_0;
1341
+ if (colorIdx !== void 0) {
1342
+ const colorAccessor = accessors[colorIdx];
1343
+ const colorBufferView = bufferViews[colorAccessor?.bufferView ?? -1];
1344
+ if (colorAccessor === void 0 || colorBufferView === void 0) {
1345
+ return err(
1346
+ gltfErr("gltf-color-accessor-malformed", {
1347
+ semantic: "COLOR_0",
1348
+ accessorIndex: colorIdx,
1349
+ reason: "reference"
1350
+ })
1351
+ );
1352
+ }
1353
+ const colorBuffer = buffers[colorBufferView.buffer];
1354
+ if (colorBuffer === void 0) {
1355
+ return err(
1356
+ gltfErr("gltf-color-accessor-malformed", {
1357
+ semantic: "COLOR_0",
1358
+ accessorIndex: colorIdx,
1359
+ reason: "reference"
1360
+ })
1361
+ );
1362
+ }
1363
+ const decoded = decodeColorAccessor({
1364
+ accessorIndex: colorIdx,
1365
+ accessor: colorAccessor,
1366
+ bufferView: colorBufferView,
1367
+ buffer: colorBuffer,
1368
+ bufferIndex: colorBufferView.buffer,
1369
+ semantic: "COLOR_0"
1370
+ });
1371
+ if (!decoded.ok) return err(decoded.error);
1372
+ if (decoded.value.length !== positions.length / 3 * 4) {
1373
+ return err(
1374
+ gltfErr("gltf-color-accessor-malformed", {
1375
+ semantic: "COLOR_0",
1376
+ accessorIndex: colorIdx,
1377
+ reason: "count"
1378
+ })
1379
+ );
1380
+ }
1381
+ colors0 = decoded.value;
1382
+ }
1383
+ let normals;
1384
+ const normalIdx = attrs.NORMAL;
1385
+ if (normalIdx !== void 0) {
1386
+ const acc = accessors[normalIdx];
1387
+ if (acc !== void 0) {
1388
+ const decoded = decodeAttributeAccessor(normalIdx, acc, bufferViews, buffers);
1389
+ if (decoded.ok) {
1390
+ const src = decoded.value;
1391
+ const owned = new Float32Array(src.length);
1392
+ owned.set(src);
1393
+ normals = owned;
1394
+ }
1395
+ }
1396
+ }
1397
+ let texcoord0;
1398
+ const texIdx = attrs.TEXCOORD_0;
1399
+ if (texIdx !== void 0) {
1400
+ const acc = accessors[texIdx];
1401
+ if (acc !== void 0) {
1402
+ const decoded = decodeAttributeAccessor(texIdx, acc, bufferViews, buffers);
1403
+ if (decoded.ok) {
1404
+ const src = decoded.value;
1405
+ const owned = new Float32Array(src.length);
1406
+ owned.set(src);
1407
+ texcoord0 = owned;
1408
+ }
1409
+ }
1410
+ }
1411
+ let texcoord1;
1412
+ let texcoord2;
1413
+ let texcoord3;
1414
+ let texcoord4;
1415
+ let texcoord5;
1416
+ let texcoord6;
1417
+ let texcoord7;
1418
+ for (let k = 1; k <= 7; k++) {
1419
+ const tcIdx = attrs[`TEXCOORD_${k}`];
1420
+ if (tcIdx === void 0) continue;
1421
+ const acc = accessors[tcIdx];
1422
+ if (acc !== void 0) {
1423
+ const decoded = decodeAttributeAccessor(tcIdx, acc, bufferViews, buffers);
1424
+ if (decoded.ok) {
1425
+ const src = decoded.value;
1426
+ const owned = new Float32Array(src.length);
1427
+ owned.set(src);
1428
+ if (k === 1) texcoord1 = owned;
1429
+ else if (k === 2) texcoord2 = owned;
1430
+ else if (k === 3) texcoord3 = owned;
1431
+ else if (k === 4) texcoord4 = owned;
1432
+ else if (k === 5) texcoord5 = owned;
1433
+ else if (k === 6) texcoord6 = owned;
1434
+ else texcoord7 = owned;
1435
+ }
1436
+ }
1437
+ }
1438
+ let tangents;
1439
+ const tanIdx = attrs.TANGENT;
1440
+ if (tanIdx !== void 0) {
1441
+ const acc = accessors[tanIdx];
1442
+ if (acc !== void 0) {
1443
+ const decoded = decodeAttributeAccessor(tanIdx, acc, bufferViews, buffers);
1444
+ if (decoded.ok) {
1445
+ const src = decoded.value;
1446
+ const owned = new Float32Array(src.length);
1447
+ owned.set(src);
1448
+ tangents = owned;
1449
+ }
1450
+ }
1451
+ }
1452
+ const rawTargets = prim.targets ?? [];
1453
+ for (const target of rawTargets) {
1454
+ const colorTarget = target.COLOR_0;
1455
+ if (colorTarget !== void 0) {
1456
+ return err(
1457
+ gltfErr("gltf-color-accessor-unsupported", {
1458
+ semantic: "COLOR_0",
1459
+ accessorIndex: colorTarget,
1460
+ reason: "morph"
1461
+ })
1462
+ );
1463
+ }
1464
+ }
1465
+ const morphAttributeCount = rawTargets.reduce(
1466
+ (count, target) => count + (target.POSITION === void 0 ? 0 : 1) + (target.NORMAL === void 0 ? 0 : 1) + (target.TANGENT === void 0 ? 0 : 1),
1467
+ 0
1468
+ );
1469
+ const morphError = (reason) => err(
1470
+ gltfErr("gltf-morph-invalid", {
1471
+ meshIndex,
1472
+ primitiveIndex: meshJson.primitives.indexOf(prim),
1473
+ reason,
1474
+ targetCount: rawTargets.length,
1475
+ attributeCount: morphAttributeCount,
1476
+ vertexCount: positions.length / 3
1477
+ })
1478
+ );
1479
+ if (rawTargets.length > 8) return morphError("target-count-exceeded");
1480
+ if (morphAttributeCount > 8) return morphError("attribute-count-exceeded");
1481
+ const morphTargets = [];
1482
+ for (const target of rawTargets) {
1483
+ const output = {};
1484
+ for (const [key, accessorIndex] of Object.entries(target)) {
1485
+ if (key !== "POSITION" && key !== "NORMAL" && key !== "TANGENT") continue;
1486
+ const targetAccessor = accessors[accessorIndex];
1487
+ if (targetAccessor === void 0) return morphError("attribute-length-mismatch");
1488
+ const decoded = decodeAttributeAccessor(
1489
+ accessorIndex,
1490
+ targetAccessor,
1491
+ bufferViews,
1492
+ buffers
1493
+ );
1494
+ if (!decoded.ok) {
1495
+ return morphError(
1496
+ decoded.error.code === "gltf-accessor-type-mismatch" ? "sparse-or-unsupported-accessor" : "attribute-length-mismatch"
1497
+ );
1498
+ }
1499
+ const expected = positions.length / 3 * (key === "TANGENT" ? 4 : 3);
1500
+ if (decoded.value.length !== expected) return morphError("attribute-length-mismatch");
1501
+ const owned = new Float32Array(decoded.value.length);
1502
+ owned.set(decoded.value);
1503
+ if (key === "POSITION") output.position = owned;
1504
+ else if (key === "NORMAL") output.normal = owned;
1505
+ else output.tangent = owned;
1506
+ }
1507
+ morphTargets.push(output);
1508
+ }
1509
+ const meshWeights = meshJson.weights;
1510
+ if (meshWeights !== void 0 && meshWeights.length !== rawTargets.length) {
1511
+ return morphError("weights-length-mismatch");
1512
+ }
1513
+ const jointsIdx = attrs.JOINTS_0;
1514
+ const weightsIdx = attrs.WEIGHTS_0;
1515
+ const hasJoints = jointsIdx !== void 0;
1516
+ const hasWeights = weightsIdx !== void 0;
1517
+ if (hasJoints !== hasWeights) {
1518
+ const primitiveIndex = meshJson.primitives.indexOf(prim);
1519
+ return err(
1520
+ gltfErr("gltf-skin-attr-asymmetric", {
1521
+ meshIndex,
1522
+ primitiveIndex,
1523
+ hasJoints,
1524
+ hasWeights
1525
+ })
1526
+ );
1527
+ }
1528
+ let joints0;
1529
+ let weights0;
1530
+ if (hasJoints && hasWeights) {
1531
+ const jointsAccessor = accessors[jointsIdx];
1532
+ const jointsBufferView = bufferViews[jointsAccessor?.bufferView ?? -1];
1533
+ if (jointsAccessor === void 0 || jointsBufferView === void 0) {
1534
+ return err(unknownAccessor(jointsIdx));
1535
+ }
1536
+ const jointsBuffer = buffers[jointsBufferView.buffer];
1537
+ if (jointsBuffer === void 0) return err(unknownAccessor(jointsIdx));
1538
+ const jointsDecoded = decodeAccessor({
1539
+ accessorIndex: jointsIdx,
1540
+ accessor: jointsAccessor,
1541
+ bufferView: jointsBufferView,
1542
+ buffer: jointsBuffer,
1543
+ role: "joints"
1544
+ });
1545
+ if (!jointsDecoded.ok) return err(jointsDecoded.error);
1546
+ if (jointsDecoded.value.kind !== "u16") {
1547
+ return err(
1548
+ gltfErr("gltf-accessor-type-mismatch", {
1549
+ accessorIndex: jointsIdx,
1550
+ reason: "unknownComponentType"
1551
+ })
1552
+ );
1553
+ }
1554
+ const src = jointsDecoded.value.data;
1555
+ const owned = new Uint16Array(src.length);
1556
+ owned.set(src);
1557
+ joints0 = owned;
1558
+ const weightsAccessor = accessors[weightsIdx];
1559
+ const weightsBufferView = bufferViews[weightsAccessor?.bufferView ?? -1];
1560
+ if (weightsAccessor === void 0 || weightsBufferView === void 0) {
1561
+ return err(unknownAccessor(weightsIdx));
1562
+ }
1563
+ const weightsBuffer = buffers[weightsBufferView.buffer];
1564
+ if (weightsBuffer === void 0) return err(unknownAccessor(weightsIdx));
1565
+ const weightsDecoded = decodeAccessor({
1566
+ accessorIndex: weightsIdx,
1567
+ accessor: weightsAccessor,
1568
+ bufferView: weightsBufferView,
1569
+ buffer: weightsBuffer,
1570
+ role: "attribute"
1571
+ });
1572
+ if (!weightsDecoded.ok) return err(weightsDecoded.error);
1573
+ if (weightsDecoded.value.kind !== "f32") {
1574
+ return err(
1575
+ gltfErr("gltf-accessor-type-mismatch", {
1576
+ accessorIndex: weightsIdx,
1577
+ reason: "unknownComponentType"
1578
+ })
1579
+ );
1580
+ }
1581
+ const wsrc = weightsDecoded.value.data;
1582
+ const wowned = new Float32Array(wsrc.length);
1583
+ wowned.set(wsrc);
1584
+ weights0 = wowned;
1585
+ }
1586
+ let indices;
1587
+ if (prim.indices !== void 0) {
1588
+ const indexAccessor = accessors[prim.indices];
1589
+ const indexBufferView = bufferViews[indexAccessor?.bufferView ?? -1];
1590
+ if (indexAccessor === void 0 || indexBufferView === void 0) {
1591
+ return err(
1592
+ gltfErr("gltf-buffer-out-of-bounds", {
1593
+ accessor: prim.indices,
1594
+ byteOffset: 0,
1595
+ byteLength: 0,
1596
+ bufferIndex: indexBufferView?.buffer ?? 0
1597
+ })
1598
+ );
1599
+ }
1600
+ const indexBuffer = buffers[indexBufferView.buffer];
1601
+ if (indexBuffer === void 0) {
1602
+ return err(
1603
+ gltfErr("gltf-buffer-out-of-bounds", {
1604
+ accessor: prim.indices,
1605
+ byteOffset: indexBufferView.byteOffset ?? 0,
1606
+ byteLength: indexBufferView.byteLength,
1607
+ bufferIndex: indexBufferView.buffer
1608
+ })
1609
+ );
1610
+ }
1611
+ const indexDecoded = decodeAccessor({
1612
+ accessorIndex: prim.indices,
1613
+ accessor: indexAccessor,
1614
+ bufferView: indexBufferView,
1615
+ buffer: indexBuffer,
1616
+ role: "indices"
1617
+ });
1618
+ if (!indexDecoded.ok) return err(indexDecoded.error);
1619
+ if (indexDecoded.value.kind === "u16") {
1620
+ const src = indexDecoded.value.data;
1621
+ const owned = new Uint16Array(src.length);
1622
+ owned.set(src);
1623
+ indices = owned;
1624
+ } else if (indexDecoded.value.kind === "u32") {
1625
+ const src = indexDecoded.value.data;
1626
+ const owned = new Uint32Array(src.length);
1627
+ owned.set(src);
1628
+ indices = owned;
1629
+ } else {
1630
+ return err(
1631
+ gltfErr("gltf-accessor-type-mismatch", {
1632
+ accessorIndex: prim.indices,
1633
+ reason: "unknownComponentType"
1634
+ })
1635
+ );
1636
+ }
1637
+ }
1638
+ const meshIr = {
1639
+ ...meshJson.name === void 0 ? {} : { name: meshJson.name },
1640
+ positions,
1641
+ ...normals === void 0 ? {} : { normals },
1642
+ ...texcoord0 === void 0 ? {} : { texcoord0 },
1643
+ ...texcoord1 === void 0 ? {} : { texcoord1 },
1644
+ ...texcoord2 === void 0 ? {} : { texcoord2 },
1645
+ ...texcoord3 === void 0 ? {} : { texcoord3 },
1646
+ ...texcoord4 === void 0 ? {} : { texcoord4 },
1647
+ ...texcoord5 === void 0 ? {} : { texcoord5 },
1648
+ ...texcoord6 === void 0 ? {} : { texcoord6 },
1649
+ ...texcoord7 === void 0 ? {} : { texcoord7 },
1650
+ ...tangents === void 0 ? {} : { tangents },
1651
+ ...colors0 === void 0 ? {} : { colors0 },
1652
+ ...joints0 === void 0 ? {} : { joints0 },
1653
+ ...weights0 === void 0 ? {} : { weights0 },
1654
+ ...morphTargets.length === 0 ? {} : { morphTargets },
1655
+ ...meshWeights === void 0 ? {} : { morphWeights: new Float32Array(meshWeights) },
1656
+ ...indices === void 0 ? {} : { indices },
1657
+ materialIndex: prim.material ?? null,
1658
+ meshIndex
1659
+ };
1660
+ meshes.push(meshIr);
1661
+ }
1662
+ }
1663
+ const texturesJson = json.textures ?? [];
1664
+ const textures = [];
1665
+ for (const texJson of texturesJson) {
1666
+ const texIr = {
1667
+ source: texJson.source ?? -1,
1668
+ ...texJson.sampler === void 0 ? {} : { sampler: texJson.sampler },
1669
+ ...texJson.name === void 0 ? {} : { name: texJson.name }
1670
+ };
1671
+ textures.push(texIr);
1672
+ }
1673
+ const imagesJson = json.images ?? [];
1674
+ const images = [];
1675
+ for (const imgJson of imagesJson) {
1676
+ const mimeType = imgJson.mimeType;
1677
+ if (mimeType !== void 0 && mimeType !== "image/jpeg" && mimeType !== "image/png") {
1678
+ return err(gltfErr("gltf-image-mime-unsupported", { mimeType }));
1679
+ }
1680
+ const imgIr = {
1681
+ ...imgJson.uri === void 0 ? {} : { uri: imgJson.uri },
1682
+ ...imgJson.mimeType === void 0 ? {} : { mimeType: imgJson.mimeType },
1683
+ ...imgJson.bufferView === void 0 ? {} : { bufferView: imgJson.bufferView },
1684
+ ...imgJson.name === void 0 ? {} : { name: imgJson.name }
1685
+ };
1686
+ images.push(imgIr);
1687
+ }
1688
+ for (const img of images) {
1689
+ if (img.uri !== void 0) {
1690
+ if (dataUriBase64Payload(img.uri) !== void 0) continue;
1691
+ try {
1692
+ await ctx.externalLoader(img.uri);
1693
+ } catch (_e) {
1694
+ return err(gltfErr("gltf-texture-load-failed", { uri: img.uri }));
1695
+ }
1696
+ }
1697
+ }
1698
+ const samplersJson = json.samplers ?? [];
1699
+ const samplers = [];
1700
+ for (const sampJson of samplersJson) {
1701
+ samplers.push({
1702
+ ...sampJson.magFilter === void 0 ? {} : { magFilter: sampJson.magFilter },
1703
+ ...sampJson.minFilter === void 0 ? {} : { minFilter: sampJson.minFilter },
1704
+ wrapS: sampJson.wrapS ?? 10497,
1705
+ // REPEAT (glTF default)
1706
+ wrapT: sampJson.wrapT ?? 10497,
1707
+ ...sampJson.name === void 0 ? {} : { name: sampJson.name }
1708
+ });
1709
+ }
1710
+ const materialsJson = json.materials ?? [];
1711
+ const materials = [];
1712
+ for (const matJson of materialsJson) {
1713
+ const pbr = matJson.pbrMetallicRoughness;
1714
+ const baseColor = pbr?.baseColorFactor ?? [1, 1, 1, 1];
1715
+ const baseColor4 = [
1716
+ baseColor[0] ?? 1,
1717
+ baseColor[1] ?? 1,
1718
+ baseColor[2] ?? 1,
1719
+ baseColor[3] ?? 1
1720
+ ];
1721
+ const alphaMode = matJson.alphaMode === "MASK" || matJson.alphaMode === "BLEND" ? matJson.alphaMode : void 0;
1722
+ const alphaCutoff = alphaMode === "MASK" ? matJson.alphaCutoff ?? 0.5 : void 0;
1723
+ const emissiveFactor = matJson.emissiveFactor;
1724
+ const emissiveFactor3 = [
1725
+ emissiveFactor?.[0] ?? 0,
1726
+ emissiveFactor?.[1] ?? 0,
1727
+ emissiveFactor?.[2] ?? 0
1728
+ ];
1729
+ materials.push({
1730
+ ...matJson.name === void 0 ? {} : { name: matJson.name },
1731
+ baseColorFactor: baseColor4,
1732
+ ...emissiveFactor === void 0 ? {} : { emissiveFactor: emissiveFactor3 },
1733
+ metallicFactor: pbr?.metallicFactor ?? 1,
1734
+ roughnessFactor: pbr?.roughnessFactor ?? 1,
1735
+ ...pbr?.baseColorTexture === void 0 ? {} : { baseColorTexture: parseTextureInfo(pbr.baseColorTexture, textures) },
1736
+ ...pbr?.metallicRoughnessTexture === void 0 ? {} : { metallicRoughnessTexture: parseTextureInfo(pbr.metallicRoughnessTexture, textures) },
1737
+ ...matJson.normalTexture === void 0 ? {} : {
1738
+ normalTexture: {
1739
+ ...parseTextureInfo(matJson.normalTexture, textures),
1740
+ ...matJson.normalTexture.scale === void 0 ? {} : { scale: matJson.normalTexture.scale }
1741
+ }
1742
+ },
1743
+ ...matJson.occlusionTexture === void 0 ? {} : {
1744
+ occlusionTexture: {
1745
+ ...parseTextureInfo(matJson.occlusionTexture, textures),
1746
+ ...matJson.occlusionTexture.strength === void 0 ? {} : { strength: matJson.occlusionTexture.strength }
1747
+ }
1748
+ },
1749
+ ...matJson.emissiveTexture === void 0 ? {} : { emissiveTexture: parseTextureInfo(matJson.emissiveTexture, textures) },
1750
+ ...alphaMode === void 0 ? {} : { alphaMode },
1751
+ ...alphaCutoff === void 0 ? {} : { alphaCutoff },
1752
+ ...matJson.doubleSided === true ? { doubleSided: true } : {},
1753
+ ...pbr?.baseColorTexture?.texCoord === void 0 || pbr.baseColorTexture.texCoord === 0 ? {} : { baseColorTexCoord: pbr.baseColorTexture.texCoord }
1754
+ });
1755
+ }
1756
+ const diagnostics = {
1757
+ nodeNames: [],
1758
+ unsupportedExtensions: [...unsupportedExtensions],
1759
+ matrixTrsCoexistNodes: []
1760
+ };
1761
+ const nodes = [];
1762
+ const nodesJson = json.nodes ?? [];
1763
+ for (let nodeIndex = 0; nodeIndex < nodesJson.length; nodeIndex++) {
1764
+ const nodeJson = nodesJson[nodeIndex];
1765
+ if (nodeJson === void 0) continue;
1766
+ const transform = decomposeNodeTransform(nodeJson, nodeIndex, diagnostics);
1767
+ if (nodeJson.name !== void 0) diagnostics.nodeNames.push(nodeJson.name);
1768
+ const instancingExt = nodeJson.extensions?.EXT_mesh_gpu_instancing;
1769
+ let instancing;
1770
+ if (instancingExt !== void 0) {
1771
+ const instancingResult = decodeNodeInstancing(
1772
+ nodeIndex,
1773
+ instancingExt.attributes ?? {},
1774
+ accessors,
1775
+ bufferViews,
1776
+ buffers
1777
+ );
1778
+ if (!instancingResult.ok) return err(instancingResult.error);
1779
+ instancing = instancingResult.value;
1780
+ }
1781
+ const nodeMorphWeights = nodeJson.weights === void 0 ? void 0 : new Float32Array(nodeJson.weights);
1782
+ nodes.push({
1783
+ ...nodeJson.name === void 0 ? {} : { name: nodeJson.name },
1784
+ transform,
1785
+ meshIndex: nodeJson.mesh ?? null,
1786
+ skinIndex: nodeJson.skin ?? null,
1787
+ children: nodeJson.children ?? [],
1788
+ camera: nodeJson.camera ?? null,
1789
+ lightIndex: nodeJson.extensions?.KHR_lights_punctual?.light ?? null,
1790
+ ...instancing === void 0 ? {} : { instancing },
1791
+ ...nodeMorphWeights === void 0 ? {} : { morphWeights: nodeMorphWeights }
1792
+ });
1793
+ }
1794
+ const scenesJson = json.scenes ?? [];
1795
+ const scenes = scenesJson.map((s) => ({
1796
+ ...s.name === void 0 ? {} : { name: s.name },
1797
+ nodes: s.nodes ?? []
1798
+ }));
1799
+ const defaultSceneIndex = json.scene ?? 0;
1800
+ const skinsJson = json.skins;
1801
+ const skinResult = parseSkin(skinsJson, nodesJson, accessors, bufferViews, buffers);
1802
+ if (!skinResult.ok) return err(skinResult.error);
1803
+ const skeletons = skinResult.value;
1804
+ const animationsJson = json.animations;
1805
+ const animResult = parseAnimation(animationsJson, nodesJson, accessors, bufferViews, buffers);
1806
+ if (!animResult.ok) return err(animResult.error);
1807
+ const animationClips = animResult.value;
1808
+ return ok({
1809
+ meshes,
1810
+ materials,
1811
+ nodes,
1812
+ scenes,
1813
+ skeletons,
1814
+ animationClips,
1815
+ textures: textures.length > 0 ? textures : void 0,
1816
+ images: images.length > 0 ? images : void 0,
1817
+ samplers: samplers.length > 0 ? samplers : void 0,
1818
+ defaultSceneIndex,
1819
+ diagnostics: {
1820
+ nodeNames: diagnostics.nodeNames,
1821
+ unsupportedExtensions: diagnostics.unsupportedExtensions,
1822
+ matrixTrsCoexistNodes: diagnostics.matrixTrsCoexistNodes
1823
+ },
1824
+ meshPrimitiveCount,
1825
+ lights: lightsResult.value
1826
+ });
1827
+ }
1828
+ async function parseGltfForImporter(json, externalLoader, filePath, options = {}) {
1829
+ if (json === null || typeof json !== "object") {
1830
+ return err(
1831
+ gltfErr("gltf-malformed-header", {
1832
+ filePath,
1833
+ byteOffset: 0
1834
+ })
1835
+ );
1836
+ }
1837
+ return parseGltfWithBin(json, {
1838
+ externalLoader,
1839
+ filePath,
1840
+ ...options.meshopt === void 0 ? {} : { meshopt: options.meshopt }
1841
+ });
1842
+ }
1843
+ function legacyParseResult(result, filePath, byteOffset) {
1844
+ if (result.ok) return ok(result.value);
1845
+ if (result.error instanceof ImportError) {
1846
+ return err(gltfErr("gltf-malformed-header", { filePath, byteOffset }));
1847
+ }
1848
+ return err(result.error);
1849
+ }
1850
+ async function parseGltf(json, externalLoader, filePath, options = {}) {
1851
+ const result = await parseGltfForImporter(json, externalLoader, filePath, options);
1852
+ return legacyParseResult(result, filePath, 0);
1853
+ }
1854
+ async function parseGlbForImporter(buffer, filePath, options = {}) {
1855
+ const chunksResult = parseGlbChunks(buffer, filePath);
1856
+ if (!chunksResult.ok) return err(chunksResult.error);
1857
+ let json;
1858
+ try {
1859
+ json = JSON.parse(new TextDecoder().decode(chunksResult.value.jsonChunk));
1860
+ } catch (_e) {
1861
+ return err(
1862
+ gltfErr("gltf-malformed-header", {
1863
+ filePath,
1864
+ byteOffset: 12
1865
+ })
1866
+ );
1867
+ }
1868
+ if (json === null || typeof json !== "object") {
1869
+ return err(
1870
+ gltfErr("gltf-malformed-header", {
1871
+ filePath,
1872
+ byteOffset: 12
1873
+ })
1874
+ );
1875
+ }
1876
+ const externalLoader = async (uri) => {
1877
+ throw new Error(`parseGlb: GLB containers must not reference external URIs (got ${uri})`);
1878
+ };
1879
+ return parseGltfWithBin(json, {
1880
+ externalLoader,
1881
+ ...chunksResult.value.binChunk === void 0 ? {} : { binChunk: chunksResult.value.binChunk },
1882
+ filePath,
1883
+ ...options.meshopt === void 0 ? {} : { meshopt: options.meshopt }
1884
+ });
1885
+ }
1886
+ async function parseGlb(buffer, filePath, options = {}) {
1887
+ const result = await parseGlbForImporter(buffer, filePath, options);
1888
+ return legacyParseResult(result, filePath, 12);
1889
+ }
1890
+
1891
+ // src/node-file-entry.ts
1892
+ async function loadFsModule() {
1893
+ const mod = await import('fs/promises');
1894
+ return mod;
1895
+ }
1896
+ async function loadPathModule() {
1897
+ const mod = await import('path');
1898
+ return mod;
1899
+ }
1900
+ async function statExists(fs, path) {
1901
+ try {
1902
+ await fs.stat(path);
1903
+ return true;
1904
+ } catch {
1905
+ return false;
1906
+ }
1907
+ }
1908
+ function metaPathFor(filePath) {
1909
+ return `${filePath}.meta.json`;
1910
+ }
1911
+ async function parseGltfFromFile(filePath) {
1912
+ const fs = await loadFsModule();
1913
+ const path = await loadPathModule();
1914
+ const expectedMetaPath = metaPathFor(filePath);
1915
+ if (!await statExists(fs, expectedMetaPath)) {
1916
+ return err(gltfErr("gltf-meta-missing", { filePath, expectedMetaPath }));
1917
+ }
1918
+ const text = (await fs.readFile(filePath)).toString("utf-8");
1919
+ let json;
1920
+ try {
1921
+ json = JSON.parse(text);
1922
+ } catch (_e) {
1923
+ return err(gltfErr("gltf-malformed-header", { filePath, byteOffset: 0 }));
1924
+ }
1925
+ const baseDir = path.dirname(filePath);
1926
+ const externalLoader = async (uri) => {
1927
+ const abs = path.resolve(baseDir, uri);
1928
+ const buf = await fs.readFile(abs);
1929
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
1930
+ };
1931
+ return parseGltf(json, externalLoader, filePath);
1932
+ }
1933
+ async function parseGlbFromFile(filePath) {
1934
+ const fs = await loadFsModule();
1935
+ const expectedMetaPath = metaPathFor(filePath);
1936
+ if (!await statExists(fs, expectedMetaPath)) {
1937
+ return err(gltfErr("gltf-meta-missing", { filePath, expectedMetaPath }));
1938
+ }
1939
+ const buf = await fs.readFile(filePath);
1940
+ const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
1941
+ return parseGlb(ab, filePath);
1942
+ }
1943
+
1944
+ export { parseGlbFromFile, parseGltfFromFile };
1945
+ //# sourceMappingURL=node-file-entry.mjs.map
1946
+ //# sourceMappingURL=node-file-entry.mjs.map