@holoscript/core 6.0.4 → 7.0.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 (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -1
  3. package/dist/compiler/index.d.ts +82 -0
  4. package/dist/constants.d.ts +3 -2
  5. package/dist/coordinators/index.d.ts +189 -0
  6. package/dist/index.d.ts +501 -19
  7. package/dist/paper-0c-spike/index.d.ts +29 -0
  8. package/dist/reconstruction/index.d.ts +78 -0
  9. package/dist/tools/index.d.ts +22 -1
  10. package/dist/traitDocs/traitDocs.d.ts +69 -0
  11. package/dist/traits/index.d.ts +61 -0
  12. package/package.json +105 -57
  13. package/dist/GLTFPipeline-DEN36VNK.js +0 -12
  14. package/dist/GLTFPipeline-YPCAK236.cjs +0 -33
  15. package/dist/HoloScriptPlusParser-BN6KVRCM.js +0 -692
  16. package/dist/HoloScriptPlusParser-NC5FE6AL.cjs +0 -697
  17. package/dist/USDZExporter-NTMBNTDB.js +0 -707
  18. package/dist/USDZExporter-UWGWNMMD.cjs +0 -709
  19. package/dist/chunk-27LWW5FU.cjs +0 -410
  20. package/dist/chunk-2CWOLHTE.js +0 -69
  21. package/dist/chunk-33LSUQDD.cjs +0 -835
  22. package/dist/chunk-3FJCK5XA.js +0 -2232
  23. package/dist/chunk-3FXV5S25.cjs +0 -633
  24. package/dist/chunk-3FYZOR7S.cjs +0 -69
  25. package/dist/chunk-3SAK5KFL.cjs +0 -83
  26. package/dist/chunk-4XSNJCLC.cjs +0 -1859
  27. package/dist/chunk-54FHG2MX.js +0 -655
  28. package/dist/chunk-56IF4NTA.js +0 -56
  29. package/dist/chunk-5BGSECRJ.cjs +0 -2237
  30. package/dist/chunk-5GLCTO44.js +0 -161
  31. package/dist/chunk-5O6TQC6M.cjs +0 -1871
  32. package/dist/chunk-5OEOHAAW.cjs +0 -682
  33. package/dist/chunk-5TOFIR6L.js +0 -407
  34. package/dist/chunk-5WUNN6ZN.cjs +0 -428
  35. package/dist/chunk-6TZPZCOJ.cjs +0 -1404
  36. package/dist/chunk-6UEHYZ7V.cjs +0 -773
  37. package/dist/chunk-6XSERL6L.js +0 -5678
  38. package/dist/chunk-72EQECGT.cjs +0 -908
  39. package/dist/chunk-7AGYRWKW.cjs +0 -507
  40. package/dist/chunk-7LCYLPW2.cjs +0 -663
  41. package/dist/chunk-A7PIMZGU.js +0 -263
  42. package/dist/chunk-ATSKSYVE.cjs +0 -51683
  43. package/dist/chunk-B5TCIAPQ.cjs +0 -117
  44. package/dist/chunk-BF2235SI.cjs +0 -5714
  45. package/dist/chunk-BKIGPPVM.js +0 -629
  46. package/dist/chunk-BND3ST2K.cjs +0 -662
  47. package/dist/chunk-BU7VWW2K.js +0 -2522
  48. package/dist/chunk-BWFJEM5A.js +0 -5419
  49. package/dist/chunk-CAUCGUEA.cjs +0 -130
  50. package/dist/chunk-CBJSLA2E.cjs +0 -5423
  51. package/dist/chunk-CDPGV4DN.js +0 -114
  52. package/dist/chunk-CLYEIWA5.cjs +0 -408
  53. package/dist/chunk-CS2EMTGF.js +0 -1966
  54. package/dist/chunk-DMG7C4H2.js +0 -975
  55. package/dist/chunk-DXJQDCFL.js +0 -468
  56. package/dist/chunk-E3FWRT3Z.js +0 -608
  57. package/dist/chunk-E3UALNFA.js +0 -421
  58. package/dist/chunk-EONILVMF.js +0 -762
  59. package/dist/chunk-EQV4R3EV.cjs +0 -769
  60. package/dist/chunk-F3HH56EB.cjs +0 -5538
  61. package/dist/chunk-F4IULT7N.js +0 -1488
  62. package/dist/chunk-FF3Q7ADR.js +0 -380
  63. package/dist/chunk-FFKU7BTK.cjs +0 -1046
  64. package/dist/chunk-FHQVYCH2.js +0 -70
  65. package/dist/chunk-FSF7EQVA.cjs +0 -1974
  66. package/dist/chunk-G4VZTCIB.js +0 -659
  67. package/dist/chunk-G7CT3EMP.cjs +0 -1002
  68. package/dist/chunk-GGHSW2NN.js +0 -51334
  69. package/dist/chunk-GVUR45UF.cjs +0 -777
  70. package/dist/chunk-H5Z7NASB.js +0 -9662
  71. package/dist/chunk-HE7ULERA.cjs +0 -697
  72. package/dist/chunk-HJJLD366.js +0 -1169
  73. package/dist/chunk-HPS76H7Q.js +0 -309
  74. package/dist/chunk-HQUEA5R6.cjs +0 -424
  75. package/dist/chunk-HYNTS5CP.js +0 -422
  76. package/dist/chunk-I6XZ5DZF.cjs +0 -488
  77. package/dist/chunk-IBDG74G2.cjs +0 -1219
  78. package/dist/chunk-IFNQFCTM.js +0 -3098
  79. package/dist/chunk-IHOUYQIA.js +0 -406
  80. package/dist/chunk-ILRBGFVD.cjs +0 -72
  81. package/dist/chunk-IWI6J7FX.js +0 -421
  82. package/dist/chunk-K3JDKWGI.cjs +0 -270
  83. package/dist/chunk-K6EKD26I.js +0 -630
  84. package/dist/chunk-KCQ5NDYC.cjs +0 -3172
  85. package/dist/chunk-KED34ILP.js +0 -832
  86. package/dist/chunk-KSGXLT74.cjs +0 -457
  87. package/dist/chunk-L4G4RLXX.js +0 -767
  88. package/dist/chunk-LDP5OK6G.js +0 -1216
  89. package/dist/chunk-LGGRPKTV.cjs +0 -735
  90. package/dist/chunk-LK5IAVH6.cjs +0 -1173
  91. package/dist/chunk-LSW7P7ML.js +0 -3675
  92. package/dist/chunk-LXJ4667L.cjs +0 -3874
  93. package/dist/chunk-LZ266PCM.cjs +0 -4499
  94. package/dist/chunk-MD6KDHVE.cjs +0 -237
  95. package/dist/chunk-MEASWI2Y.js +0 -126
  96. package/dist/chunk-MPXBVFHQ.js +0 -768
  97. package/dist/chunk-N5FTS757.cjs +0 -415
  98. package/dist/chunk-NCUKG4SK.cjs +0 -3075
  99. package/dist/chunk-NDQNIJEH.js +0 -1715
  100. package/dist/chunk-NOZZ46QU.js +0 -168
  101. package/dist/chunk-OFKWH2IP.js +0 -449
  102. package/dist/chunk-PHENW4MQ.js +0 -65
  103. package/dist/chunk-PMGODDJA.cjs +0 -319
  104. package/dist/chunk-Q4QX3AET.cjs +0 -1505
  105. package/dist/chunk-Q56TT7IL.js +0 -483
  106. package/dist/chunk-Q5JT3YML.cjs +0 -129
  107. package/dist/chunk-Q5SJOGHQ.cjs +0 -4256
  108. package/dist/chunk-QGKNANZ3.js +0 -3071
  109. package/dist/chunk-QLEAOI3Q.cjs +0 -424
  110. package/dist/chunk-R7MA7QWV.js +0 -732
  111. package/dist/chunk-RVYPEYPX.js +0 -678
  112. package/dist/chunk-S2N5TNMA.js +0 -1855
  113. package/dist/chunk-S565K7NK.js +0 -752
  114. package/dist/chunk-SDAMZIRE.js +0 -765
  115. package/dist/chunk-T36DDXSF.js +0 -4250
  116. package/dist/chunk-T4NKMBDL.js +0 -5474
  117. package/dist/chunk-T7LEXZUZ.cjs +0 -2526
  118. package/dist/chunk-TEWCN33Y.js +0 -694
  119. package/dist/chunk-TQSZUOW5.cjs +0 -793
  120. package/dist/chunk-TSH3MSQU.cjs +0 -129
  121. package/dist/chunk-UERJ3YJV.js +0 -504
  122. package/dist/chunk-UPJBDCVF.js +0 -1395
  123. package/dist/chunk-V3NN3HPP.js +0 -4462
  124. package/dist/chunk-VJ3L7CHT.cjs +0 -169
  125. package/dist/chunk-WB4Z63GG.js +0 -229
  126. package/dist/chunk-WDW67DZB.cjs +0 -9728
  127. package/dist/chunk-WGMEAYBG.js +0 -126
  128. package/dist/chunk-X4ZF6CNE.cjs +0 -473
  129. package/dist/chunk-X6SEAXAX.cjs +0 -3681
  130. package/dist/chunk-XCWFGZAA.js +0 -1043
  131. package/dist/chunk-XD6IYDUX.js +0 -903
  132. package/dist/chunk-XG23EUQX.cjs +0 -1717
  133. package/dist/chunk-XRKWN5PL.cjs +0 -173
  134. package/dist/chunk-XUILI4KB.cjs +0 -430
  135. package/dist/chunk-XUTX5CBW.js +0 -126
  136. package/dist/chunk-XWPNTWDN.cjs +0 -755
  137. package/dist/chunk-XXINBTZK.cjs +0 -633
  138. package/dist/chunk-YCXFCXDS.js +0 -1868
  139. package/dist/chunk-YQK5JNFK.js +0 -3725
  140. package/dist/chunk-YYXH45E2.js +0 -426
  141. package/dist/chunk-Z34RLXRA.cjs +0 -612
  142. package/dist/chunk-ZP4D3CSX.cjs +0 -72
  143. package/dist/cli/holoscript-runner.cjs +0 -2259
  144. package/dist/cli/holoscript-runner.js +0 -2234
  145. package/dist/codebase/index.cjs +0 -336
  146. package/dist/codebase/index.js +0 -331
  147. package/dist/compiler/agent-inference.cjs +0 -28
  148. package/dist/compiler/agent-inference.js +0 -5
  149. package/dist/compiler/android-xr.cjs +0 -19
  150. package/dist/compiler/android-xr.js +0 -9
  151. package/dist/compiler/android.cjs +0 -20
  152. package/dist/compiler/android.js +0 -10
  153. package/dist/compiler/ar.cjs +0 -20
  154. package/dist/compiler/ar.js +0 -8
  155. package/dist/compiler/babylon.cjs +0 -17
  156. package/dist/compiler/babylon.js +0 -11
  157. package/dist/compiler/coco.cjs +0 -27
  158. package/dist/compiler/coco.js +0 -4
  159. package/dist/compiler/domain-block-utils.cjs +0 -602
  160. package/dist/compiler/domain-block-utils.js +0 -8
  161. package/dist/compiler/dtdl.cjs +0 -24
  162. package/dist/compiler/dtdl.js +0 -8
  163. package/dist/compiler/gltf-pipeline.cjs +0 -39
  164. package/dist/compiler/gltf-pipeline.js +0 -4
  165. package/dist/compiler/godot.cjs +0 -15
  166. package/dist/compiler/godot.js +0 -9
  167. package/dist/compiler/incremental.cjs +0 -29
  168. package/dist/compiler/incremental.js +0 -4
  169. package/dist/compiler/index.cjs +0 -2578
  170. package/dist/compiler/index.js +0 -2383
  171. package/dist/compiler/ios.cjs +0 -20
  172. package/dist/compiler/ios.js +0 -10
  173. package/dist/compiler/multi-layer.cjs +0 -20
  174. package/dist/compiler/multi-layer.js +0 -11
  175. package/dist/compiler/nodetoy.cjs +0 -23
  176. package/dist/compiler/nodetoy.js +0 -4
  177. package/dist/compiler/openxr.cjs +0 -15
  178. package/dist/compiler/openxr.js +0 -9
  179. package/dist/compiler/playcanvas.cjs +0 -16
  180. package/dist/compiler/playcanvas.js +0 -10
  181. package/dist/compiler/r3f.cjs +0 -34
  182. package/dist/compiler/r3f.js +0 -12
  183. package/dist/compiler/remotion.cjs +0 -23
  184. package/dist/compiler/remotion.js +0 -4
  185. package/dist/compiler/reproducibility.cjs +0 -39
  186. package/dist/compiler/reproducibility.js +0 -4
  187. package/dist/compiler/sdf.cjs +0 -21
  188. package/dist/compiler/sdf.js +0 -9
  189. package/dist/compiler/semantic-scene.cjs +0 -31
  190. package/dist/compiler/semantic-scene.js +0 -4
  191. package/dist/compiler/state.cjs +0 -17
  192. package/dist/compiler/state.js +0 -7
  193. package/dist/compiler/trait-composition.cjs +0 -26
  194. package/dist/compiler/trait-composition.js +0 -5
  195. package/dist/compiler/unity.cjs +0 -15
  196. package/dist/compiler/unity.js +0 -9
  197. package/dist/compiler/unreal.cjs +0 -19
  198. package/dist/compiler/unreal.js +0 -9
  199. package/dist/compiler/urdf.cjs +0 -45
  200. package/dist/compiler/urdf.js +0 -9
  201. package/dist/compiler/usd-physics.cjs +0 -23
  202. package/dist/compiler/usd-physics.js +0 -6
  203. package/dist/compiler/visionos.cjs +0 -15
  204. package/dist/compiler/visionos.js +0 -9
  205. package/dist/compiler/vrchat.cjs +0 -19
  206. package/dist/compiler/vrchat.js +0 -9
  207. package/dist/compiler/vrr.cjs +0 -21
  208. package/dist/compiler/vrr.js +0 -9
  209. package/dist/compiler/wasm.cjs +0 -33
  210. package/dist/compiler/wasm.js +0 -9
  211. package/dist/compiler/webgpu.cjs +0 -15
  212. package/dist/compiler/webgpu.js +0 -9
  213. package/dist/debugger.cjs +0 -21
  214. package/dist/debugger.js +0 -8
  215. package/dist/entries/interop.cjs +0 -62
  216. package/dist/entries/interop.js +0 -5
  217. package/dist/entries/scripting.cjs +0 -68
  218. package/dist/entries/scripting.js +0 -11
  219. package/dist/index.cjs +0 -40921
  220. package/dist/index.js +0 -37763
  221. package/dist/math/vec3.cjs +0 -57
  222. package/dist/math/vec3.js +0 -4
  223. package/dist/ml-dsa-PHCWUBPX.cjs +0 -1178
  224. package/dist/ml-dsa-WYLJHJO6.js +0 -1178
  225. package/dist/parser.cjs +0 -27
  226. package/dist/parser.js +0 -10
  227. package/dist/playwright-5OSAPTNH.js +0 -98660
  228. package/dist/playwright-RL6GY4F7.cjs +0 -98673
  229. package/dist/runtime.cjs +0 -16
  230. package/dist/runtime.js +0 -7
  231. package/dist/storage/index.cjs +0 -37
  232. package/dist/storage/index.js +0 -4
  233. package/dist/traits/index.cjs +0 -14649
  234. package/dist/traits/index.js +0 -12243
  235. package/dist/type-checker.cjs +0 -18
  236. package/dist/type-checker.js +0 -5
@@ -1,2232 +0,0 @@
1
- import { generateHullGeometry, generateMembraneGeometry, generateSplineGeometry } from './chunk-HJJLD366.js';
2
- import { init_R3FCompiler, init_ObjectPool, ASTNodePool, MATERIAL_PRESETS } from './chunk-T36DDXSF.js';
3
- import { init_TraitCompositor, TraitCompositor } from './chunk-DXJQDCFL.js';
4
- import { init_DomainBlockCompilerMixin, compileDomainBlocks, compileMaterialBlock, materialToGLTF } from './chunk-YQK5JNFK.js';
5
- import { init_CompilerBase, CompilerBase } from './chunk-6XSERL6L.js';
6
- import { ANSCapabilityPath } from '@holoscript/platform';
7
-
8
- // src/compiler/GLTFPipeline.ts
9
- init_TraitCompositor();
10
- init_R3FCompiler();
11
- init_CompilerBase();
12
- init_DomainBlockCompilerMixin();
13
-
14
- // src/compiler/gltf/extensions.ts
15
- function createEmissiveStrengthExtension(strength) {
16
- return {
17
- KHR_materials_emissive_strength: {
18
- emissiveStrength: Math.max(0, strength)
19
- }
20
- };
21
- }
22
- function createClearcoatExtension(options) {
23
- return {
24
- KHR_materials_clearcoat: {
25
- clearcoatFactor: options.factor ?? 1,
26
- clearcoatRoughnessFactor: options.roughness ?? 0
27
- }
28
- };
29
- }
30
- function createTransmissionExtension(factor) {
31
- return {
32
- KHR_materials_transmission: {
33
- transmissionFactor: Math.max(0, Math.min(1, factor))
34
- }
35
- };
36
- }
37
- function createIORExtension(ior) {
38
- return {
39
- KHR_materials_ior: {
40
- ior: Math.max(1, ior)
41
- }
42
- };
43
- }
44
- function createSheenExtension(options) {
45
- return {
46
- KHR_materials_sheen: {
47
- sheenColorFactor: options.color ?? [1, 1, 1],
48
- sheenRoughnessFactor: options.roughness ?? 0.5
49
- }
50
- };
51
- }
52
- function createAnisotropyExtension(options) {
53
- return {
54
- KHR_materials_anisotropy: {
55
- anisotropyStrength: Math.max(0, Math.min(1, options.strength ?? 0.5)),
56
- anisotropyRotation: options.rotation ?? 0
57
- }
58
- };
59
- }
60
- function createVolumeExtension(options) {
61
- return {
62
- KHR_materials_volume: {
63
- thicknessFactor: options.thickness ?? 1,
64
- attenuationDistance: options.attenuationDistance ?? 1,
65
- attenuationColor: options.attenuationColor ?? [1, 1, 1]
66
- }
67
- };
68
- }
69
- function createIridescenceExtension(options) {
70
- return {
71
- KHR_materials_iridescence: {
72
- iridescenceFactor: options.factor ?? 1,
73
- iridescenceIor: options.ior ?? 1.3,
74
- iridescenceThicknessMinimum: options.thicknessRange?.[0] ?? 100,
75
- iridescenceThicknessMaximum: options.thicknessRange?.[1] ?? 400
76
- }
77
- };
78
- }
79
- function collectUsedExtensions(gltf) {
80
- const extensions = /* @__PURE__ */ new Set();
81
- const materials = gltf.materials;
82
- if (materials) {
83
- for (const material of materials) {
84
- if (material.extensions) {
85
- Object.keys(material.extensions).forEach((ext) => extensions.add(ext));
86
- }
87
- }
88
- }
89
- const nodes = gltf.nodes;
90
- if (nodes) {
91
- for (const node of nodes) {
92
- if (node.extensions) {
93
- Object.keys(node.extensions).forEach((ext) => extensions.add(ext));
94
- }
95
- }
96
- }
97
- if (gltf.extensions) {
98
- Object.keys(gltf.extensions).forEach((ext) => extensions.add(ext));
99
- }
100
- return Array.from(extensions);
101
- }
102
- function isExtensionRequired(extension) {
103
- const requiredExtensions = [
104
- "KHR_draco_mesh_compression",
105
- "KHR_mesh_quantization",
106
- "KHR_texture_basisu"
107
- ];
108
- return requiredExtensions.includes(extension);
109
- }
110
- function declareExtensions(gltf) {
111
- const usedExtensions = collectUsedExtensions(gltf);
112
- if (usedExtensions.length === 0) return;
113
- gltf.extensionsUsed = usedExtensions;
114
- const required = usedExtensions.filter(isExtensionRequired);
115
- if (required.length > 0) {
116
- gltf.extensionsRequired = required;
117
- }
118
- }
119
-
120
- // src/compiler/GLTFPipeline.ts
121
- init_ObjectPool();
122
- var gltfNodePool = new ASTNodePool(
123
- () => ({ name: "" }),
124
- (node) => {
125
- node.name = "";
126
- node.translation = void 0;
127
- node.rotation = void 0;
128
- node.scale = void 0;
129
- node.mesh = void 0;
130
- node.skin = void 0;
131
- node.children = void 0;
132
- node.camera = void 0;
133
- node.extras = void 0;
134
- },
135
- 0
136
- // Was 10000 — demand-allocate instead of pre-allocating
137
- );
138
- var PRIMITIVE_GENERATORS = {
139
- cube: generateCubeGeometry,
140
- box: generateCubeGeometry,
141
- sphere: generateSphereGeometry,
142
- orb: generateSphereGeometry,
143
- cylinder: generateCylinderGeometry,
144
- cone: generateConeGeometry,
145
- pyramid: generateConeGeometry,
146
- plane: generatePlaneGeometry,
147
- ground: generatePlaneGeometry
148
- };
149
- function generateScaleTexture(size = 512, baseColor = [30, 15, 61], borderColor = [20, 12, 40], highlightColor = [70, 50, 120]) {
150
- const data = new Uint8Array(size * size * 4);
151
- const scaleSize = size / 16;
152
- const hexH = scaleSize;
153
- const hexW = scaleSize * Math.sqrt(3);
154
- function scaleHash(row, col) {
155
- let h = row * 73856093 ^ col * 19349663;
156
- h = (h >> 16 ^ h) * 73244475;
157
- h = (h >> 16 ^ h) * 73244475;
158
- return (h >> 16 ^ h) & 255;
159
- }
160
- for (let y = 0; y < size; y++) {
161
- for (let x = 0; x < size; x++) {
162
- const row = Math.floor(y / (hexH * 0.75));
163
- const isOddRow = row % 2 === 1;
164
- const xOff = isOddRow ? hexW * 0.5 : 0;
165
- const col = Math.floor((x + xOff) / hexW);
166
- const cx = col * hexW - xOff + hexW * 0.5;
167
- const cy = row * hexH * 0.75 + hexH * 0.5;
168
- const dx = (x - cx) / (hexW * 0.5);
169
- const dy = (y - cy) / (hexH * 0.5);
170
- const q = Math.abs(dx);
171
- const r = Math.abs(dy);
172
- const hexDist = Math.max(q, (q + r * Math.sqrt(3)) / 2);
173
- const jitter = (scaleHash(row, col) / 255 - 0.5) * 40;
174
- const jR = Math.round(jitter * 0.4);
175
- const jG = Math.round(jitter * 0.3);
176
- const jB = Math.round(jitter * 0.6);
177
- const idx = (y * size + x) * 4;
178
- if (hexDist > 0.94) {
179
- data[idx] = Math.max(0, borderColor[0] + jR);
180
- data[idx + 1] = Math.max(0, borderColor[1] + jG);
181
- data[idx + 2] = Math.max(0, borderColor[2] + jB);
182
- data[idx + 3] = 255;
183
- } else if (hexDist < 0.12) {
184
- const t = hexDist / 0.12;
185
- const specR = 100, specG = 80, specB = 160;
186
- data[idx] = Math.min(255, Math.round(specR * (1 - t) + highlightColor[0] * t) + jR);
187
- data[idx + 1] = Math.min(255, Math.round(specG * (1 - t) + highlightColor[1] * t) + jG);
188
- data[idx + 2] = Math.min(255, Math.round(specB * (1 - t) + highlightColor[2] * t) + jB);
189
- data[idx + 3] = 255;
190
- } else if (hexDist < 0.35) {
191
- const t = (hexDist - 0.12) / 0.23;
192
- data[idx] = Math.min(255, Math.round(highlightColor[0] * (1 - t) + baseColor[0] * t) + jR);
193
- data[idx + 1] = Math.min(
194
- 255,
195
- Math.round(highlightColor[1] * (1 - t) + baseColor[1] * t) + jG
196
- );
197
- data[idx + 2] = Math.min(
198
- 255,
199
- Math.round(highlightColor[2] * (1 - t) + baseColor[2] * t) + jB
200
- );
201
- data[idx + 3] = 255;
202
- } else {
203
- const t = (hexDist - 0.35) / 0.59;
204
- data[idx] = Math.max(
205
- 0,
206
- Math.min(255, Math.round(baseColor[0] * (1 - t * 0.3) + borderColor[0] * (t * 0.3)) + jR)
207
- );
208
- data[idx + 1] = Math.max(
209
- 0,
210
- Math.min(255, Math.round(baseColor[1] * (1 - t * 0.3) + borderColor[1] * (t * 0.3)) + jG)
211
- );
212
- data[idx + 2] = Math.max(
213
- 0,
214
- Math.min(255, Math.round(baseColor[2] * (1 - t * 0.3) + borderColor[2] * (t * 0.3)) + jB)
215
- );
216
- data[idx + 3] = 255;
217
- }
218
- }
219
- }
220
- return data;
221
- }
222
- function generateScaleNormalMap(size = 512) {
223
- const data = new Uint8Array(size * size * 4);
224
- const scaleSize = size / 16;
225
- const hexH = scaleSize;
226
- const hexW = scaleSize * Math.sqrt(3);
227
- for (let y = 0; y < size; y++) {
228
- for (let x = 0; x < size; x++) {
229
- const row = Math.floor(y / (hexH * 0.75));
230
- const isOddRow = row % 2 === 1;
231
- const xOff = isOddRow ? hexW * 0.5 : 0;
232
- const col = Math.floor((x + xOff) / hexW);
233
- const cx = col * hexW - xOff + hexW * 0.5;
234
- const cy = row * hexH * 0.75 + hexH * 0.5;
235
- const dx = (x - cx) / (hexW * 0.5);
236
- const dy = (y - cy) / (hexH * 0.5);
237
- const q = Math.abs(dx);
238
- const r = Math.abs(dy);
239
- const hexDist = Math.max(q, (q + r * Math.sqrt(3)) / 2);
240
- const idx = (y * size + x) * 4;
241
- let nx = 0, ny = 0, nz = 1;
242
- if (hexDist > 0.92 && hexDist < 1) {
243
- const len = Math.sqrt(dx * dx + dy * dy) || 1;
244
- const strength = 0.55;
245
- nx = dx / len * strength;
246
- ny = dy / len * strength;
247
- nz = Math.sqrt(Math.max(0, 1 - nx * nx - ny * ny));
248
- } else if (hexDist < 0.5) {
249
- const strength = 0.25;
250
- nx = -dx * strength;
251
- ny = -dy * strength;
252
- nz = Math.sqrt(Math.max(0, 1 - nx * nx - ny * ny));
253
- }
254
- data[idx] = Math.round((nx * 0.5 + 0.5) * 255);
255
- data[idx + 1] = Math.round((ny * 0.5 + 0.5) * 255);
256
- data[idx + 2] = Math.round((nz * 0.5 + 0.5) * 255);
257
- data[idx + 3] = 255;
258
- }
259
- }
260
- return data;
261
- }
262
- function encodePNG(pixels, width, height) {
263
- const rowSize = width * 4 + 1;
264
- const rawData = new Uint8Array(rowSize * height);
265
- for (let y = 0; y < height; y++) {
266
- rawData[y * rowSize] = 0;
267
- rawData.set(pixels.slice(y * width * 4, (y + 1) * width * 4), y * rowSize + 1);
268
- }
269
- const deflated = deflateStored(rawData);
270
- const crcTable = [];
271
- for (let n = 0; n < 256; n++) {
272
- let c = n;
273
- for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
274
- crcTable[n] = c;
275
- }
276
- function crc32(buf) {
277
- let crc = 4294967295;
278
- for (let i = 0; i < buf.length; i++) crc = crcTable[(crc ^ buf[i]) & 255] ^ crc >>> 8;
279
- return (crc ^ 4294967295) >>> 0;
280
- }
281
- function writeChunk(type, data) {
282
- const chunk = new Uint8Array(4 + 4 + data.length + 4);
283
- const dv = new DataView(chunk.buffer);
284
- dv.setUint32(0, data.length);
285
- for (let i = 0; i < 4; i++) chunk[4 + i] = type.charCodeAt(i);
286
- chunk.set(data, 8);
287
- const crcInput = new Uint8Array(4 + data.length);
288
- crcInput.set(chunk.slice(4, 8));
289
- crcInput.set(data, 4);
290
- dv.setUint32(8 + data.length, crc32(crcInput));
291
- return chunk;
292
- }
293
- const signature = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
294
- const ihdr = new Uint8Array(13);
295
- const ihdrDV = new DataView(ihdr.buffer);
296
- ihdrDV.setUint32(0, width);
297
- ihdrDV.setUint32(4, height);
298
- ihdr[8] = 8;
299
- ihdr[9] = 6;
300
- ihdr[10] = 0;
301
- ihdr[11] = 0;
302
- ihdr[12] = 0;
303
- const ihdrChunk = writeChunk("IHDR", ihdr);
304
- const idatChunk = writeChunk("IDAT", deflated);
305
- const iendChunk = writeChunk("IEND", new Uint8Array(0));
306
- const png = new Uint8Array(
307
- signature.length + ihdrChunk.length + idatChunk.length + iendChunk.length
308
- );
309
- let offset = 0;
310
- png.set(signature, offset);
311
- offset += signature.length;
312
- png.set(ihdrChunk, offset);
313
- offset += ihdrChunk.length;
314
- png.set(idatChunk, offset);
315
- offset += idatChunk.length;
316
- png.set(iendChunk, offset);
317
- return png;
318
- }
319
- function decimateGeometry(geometry, ratio) {
320
- const cellSize = Math.max(0.01, (1 - ratio) * 0.3);
321
- const positions = geometry.positions;
322
- const normals = geometry.normals;
323
- const uvs = geometry.uvs;
324
- const indices = geometry.indices;
325
- const vertCount = positions.length / 3;
326
- const cellMap = /* @__PURE__ */ new Map();
327
- const remap = new Int32Array(vertCount);
328
- const newPositions = [];
329
- const newNormals = [];
330
- const newUVs = [];
331
- let newIdx = 0;
332
- for (let i = 0; i < vertCount; i++) {
333
- const px = positions[i * 3], py = positions[i * 3 + 1], pz = positions[i * 3 + 2];
334
- const cx = Math.round(px / cellSize), cy = Math.round(py / cellSize), cz = Math.round(pz / cellSize);
335
- const key = `${cx},${cy},${cz}`;
336
- if (cellMap.has(key)) {
337
- remap[i] = cellMap.get(key);
338
- } else {
339
- cellMap.set(key, newIdx);
340
- remap[i] = newIdx;
341
- newPositions.push(px, py, pz);
342
- newNormals.push(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]);
343
- newUVs.push(uvs[i * 2], uvs[i * 2 + 1]);
344
- newIdx++;
345
- }
346
- }
347
- const newIndices = [];
348
- for (let i = 0; i < indices.length; i += 3) {
349
- const a = remap[indices[i]], b = remap[indices[i + 1]], c = remap[indices[i + 2]];
350
- if (a !== b && b !== c && a !== c) {
351
- newIndices.push(a, b, c);
352
- }
353
- }
354
- const newVertexCount = newPositions.length / 3;
355
- const IndexArrayType = newVertexCount > 65535 ? Uint32Array : Uint16Array;
356
- return {
357
- positions: new Float32Array(newPositions),
358
- normals: new Float32Array(newNormals),
359
- uvs: new Float32Array(newUVs),
360
- indices: new IndexArrayType(newIndices)
361
- };
362
- }
363
- var DRAGON_SKELETON = [
364
- { name: "Root", position: [0, 2.5, 0] },
365
- { name: "Spine", position: [0, 2.5, 0], parent: "Root" },
366
- { name: "Neck", position: [0, 3.5, 2], parent: "Spine" },
367
- { name: "Head", position: [0, 4.5, 4.2], parent: "Neck" },
368
- { name: "Jaw", position: [0, 4.2, 4.6], parent: "Head" },
369
- { name: "Tail1", position: [0, 2.2, -2], parent: "Spine" },
370
- { name: "Tail2", position: [0, 1, -3.5], parent: "Tail1" },
371
- { name: "TailTip", position: [0, -0.05, -4.6], parent: "Tail2" },
372
- { name: "LeftWing", position: [-1.2, 3, 0.2], parent: "Spine" },
373
- { name: "LeftWingTip", position: [-2.6, 3.8, 0.2], parent: "LeftWing" },
374
- { name: "RightWing", position: [1.2, 3, 0.2], parent: "Spine" },
375
- { name: "RightWingTip", position: [2.6, 3.8, 0.2], parent: "RightWing" },
376
- { name: "LeftFrontLeg", position: [-0.9, 1.6, 1.2], parent: "Spine" },
377
- { name: "LeftFrontFoot", position: [-0.95, 0.15, 1.4], parent: "LeftFrontLeg" },
378
- { name: "RightFrontLeg", position: [0.9, 1.6, 1.2], parent: "Spine" },
379
- { name: "RightFrontFoot", position: [0.95, 0.15, 1.4], parent: "RightFrontLeg" },
380
- { name: "LeftBackLeg", position: [-0.9, 1.5, -1], parent: "Spine" },
381
- { name: "LeftBackFoot", position: [-0.95, 0.1, -1.15], parent: "LeftBackLeg" },
382
- { name: "RightBackLeg", position: [0.9, 1.5, -1], parent: "Spine" },
383
- { name: "RightBackFoot", position: [0.95, 0.1, -1.15], parent: "RightBackLeg" }
384
- ];
385
- function deflateStored(data) {
386
- const BLOCK_MAX = 65535;
387
- const numBlocks = Math.ceil(data.length / BLOCK_MAX) || 1;
388
- let size = 2 + 4;
389
- for (let i = 0; i < numBlocks; i++) {
390
- size += 5;
391
- const blockLen = Math.min(BLOCK_MAX, data.length - i * BLOCK_MAX);
392
- size += blockLen;
393
- }
394
- const out = new Uint8Array(size);
395
- let pos = 0;
396
- out[pos++] = 120;
397
- out[pos++] = 1;
398
- for (let i = 0; i < numBlocks; i++) {
399
- const blockStart = i * BLOCK_MAX;
400
- const blockLen = Math.min(BLOCK_MAX, data.length - blockStart);
401
- const isLast = i === numBlocks - 1;
402
- out[pos++] = isLast ? 1 : 0;
403
- out[pos++] = blockLen & 255;
404
- out[pos++] = blockLen >> 8 & 255;
405
- out[pos++] = ~blockLen & 255;
406
- out[pos++] = ~blockLen >> 8 & 255;
407
- out.set(data.slice(blockStart, blockStart + blockLen), pos);
408
- pos += blockLen;
409
- }
410
- let a = 1, b = 0;
411
- for (let i = 0; i < data.length; i++) {
412
- a = (a + data[i]) % 65521;
413
- b = (b + a) % 65521;
414
- }
415
- const adler = (b << 16 | a) >>> 0;
416
- out[pos++] = adler >> 24 & 255;
417
- out[pos++] = adler >> 16 & 255;
418
- out[pos++] = adler >> 8 & 255;
419
- out[pos++] = adler & 255;
420
- return out;
421
- }
422
- var ADVANCED_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["spline", "membrane", "hull", "blob", "metaball"]);
423
- function generateCubeGeometry(scale) {
424
- const [sx, sy, sz] = scale.map((s) => s * 0.5);
425
- const positions = new Float32Array([
426
- // Front face
427
- -sx,
428
- -sy,
429
- sz,
430
- sx,
431
- -sy,
432
- sz,
433
- sx,
434
- sy,
435
- sz,
436
- -sx,
437
- sy,
438
- sz,
439
- // Back face
440
- -sx,
441
- -sy,
442
- -sz,
443
- -sx,
444
- sy,
445
- -sz,
446
- sx,
447
- sy,
448
- -sz,
449
- sx,
450
- -sy,
451
- -sz,
452
- // Top face
453
- -sx,
454
- sy,
455
- -sz,
456
- -sx,
457
- sy,
458
- sz,
459
- sx,
460
- sy,
461
- sz,
462
- sx,
463
- sy,
464
- -sz,
465
- // Bottom face
466
- -sx,
467
- -sy,
468
- -sz,
469
- sx,
470
- -sy,
471
- -sz,
472
- sx,
473
- -sy,
474
- sz,
475
- -sx,
476
- -sy,
477
- sz,
478
- // Right face
479
- sx,
480
- -sy,
481
- -sz,
482
- sx,
483
- sy,
484
- -sz,
485
- sx,
486
- sy,
487
- sz,
488
- sx,
489
- -sy,
490
- sz,
491
- // Left face
492
- -sx,
493
- -sy,
494
- -sz,
495
- -sx,
496
- -sy,
497
- sz,
498
- -sx,
499
- sy,
500
- sz,
501
- -sx,
502
- sy,
503
- -sz
504
- ]);
505
- const normals = new Float32Array([
506
- // Front
507
- 0,
508
- 0,
509
- 1,
510
- 0,
511
- 0,
512
- 1,
513
- 0,
514
- 0,
515
- 1,
516
- 0,
517
- 0,
518
- 1,
519
- // Back
520
- 0,
521
- 0,
522
- -1,
523
- 0,
524
- 0,
525
- -1,
526
- 0,
527
- 0,
528
- -1,
529
- 0,
530
- 0,
531
- -1,
532
- // Top
533
- 0,
534
- 1,
535
- 0,
536
- 0,
537
- 1,
538
- 0,
539
- 0,
540
- 1,
541
- 0,
542
- 0,
543
- 1,
544
- 0,
545
- // Bottom
546
- 0,
547
- -1,
548
- 0,
549
- 0,
550
- -1,
551
- 0,
552
- 0,
553
- -1,
554
- 0,
555
- 0,
556
- -1,
557
- 0,
558
- // Right
559
- 1,
560
- 0,
561
- 0,
562
- 1,
563
- 0,
564
- 0,
565
- 1,
566
- 0,
567
- 0,
568
- 1,
569
- 0,
570
- 0,
571
- // Left
572
- -1,
573
- 0,
574
- 0,
575
- -1,
576
- 0,
577
- 0,
578
- -1,
579
- 0,
580
- 0,
581
- -1,
582
- 0,
583
- 0
584
- ]);
585
- const uvs = new Float32Array([
586
- // Front
587
- 0,
588
- 0,
589
- 1,
590
- 0,
591
- 1,
592
- 1,
593
- 0,
594
- 1,
595
- // Back
596
- 1,
597
- 0,
598
- 1,
599
- 1,
600
- 0,
601
- 1,
602
- 0,
603
- 0,
604
- // Top
605
- 0,
606
- 1,
607
- 0,
608
- 0,
609
- 1,
610
- 0,
611
- 1,
612
- 1,
613
- // Bottom
614
- 1,
615
- 1,
616
- 0,
617
- 1,
618
- 0,
619
- 0,
620
- 1,
621
- 0,
622
- // Right
623
- 1,
624
- 0,
625
- 1,
626
- 1,
627
- 0,
628
- 1,
629
- 0,
630
- 0,
631
- // Left
632
- 0,
633
- 0,
634
- 1,
635
- 0,
636
- 1,
637
- 1,
638
- 0,
639
- 1
640
- ]);
641
- const indices = new Uint16Array([
642
- 0,
643
- 1,
644
- 2,
645
- 0,
646
- 2,
647
- 3,
648
- // Front
649
- 4,
650
- 5,
651
- 6,
652
- 4,
653
- 6,
654
- 7,
655
- // Back
656
- 8,
657
- 9,
658
- 10,
659
- 8,
660
- 10,
661
- 11,
662
- // Top
663
- 12,
664
- 13,
665
- 14,
666
- 12,
667
- 14,
668
- 15,
669
- // Bottom
670
- 16,
671
- 17,
672
- 18,
673
- 16,
674
- 18,
675
- 19,
676
- // Right
677
- 20,
678
- 21,
679
- 22,
680
- 20,
681
- 22,
682
- 23
683
- // Left
684
- ]);
685
- return { positions, normals, uvs, indices };
686
- }
687
- function generateSphereGeometry(scale) {
688
- const sx = scale[0] * 0.5;
689
- const sy = scale[1] * 0.5;
690
- const sz = scale[2] * 0.5;
691
- const segments = 32;
692
- const rings = 24;
693
- const positions = [];
694
- const normals = [];
695
- const uvs = [];
696
- const indices = [];
697
- for (let y = 0; y <= rings; y++) {
698
- const v = y / rings;
699
- const theta = v * Math.PI;
700
- for (let x = 0; x <= segments; x++) {
701
- const u = x / segments;
702
- const phi = u * Math.PI * 2;
703
- const nx = Math.sin(theta) * Math.cos(phi);
704
- const ny = Math.cos(theta);
705
- const nz = Math.sin(theta) * Math.sin(phi);
706
- positions.push(nx * sx);
707
- positions.push(ny * sy);
708
- positions.push(nz * sz);
709
- normals.push(nx, ny, nz);
710
- uvs.push(u, 1 - v);
711
- }
712
- }
713
- for (let y = 0; y < rings; y++) {
714
- for (let x = 0; x < segments; x++) {
715
- const a = y * (segments + 1) + x;
716
- const b = a + segments + 1;
717
- indices.push(a, b, a + 1);
718
- indices.push(b, b + 1, a + 1);
719
- }
720
- }
721
- return {
722
- positions: new Float32Array(positions),
723
- normals: new Float32Array(normals),
724
- uvs: new Float32Array(uvs),
725
- indices: new Uint16Array(indices)
726
- };
727
- }
728
- function generateCylinderGeometry(scale) {
729
- const radiusTop = scale[0] * 0.5;
730
- const radiusBottom = scale[2] * 0.5;
731
- const height = scale[1];
732
- const segments = 32;
733
- const positions = [];
734
- const normals = [];
735
- const uvs = [];
736
- const indices = [];
737
- const halfHeight = height / 2;
738
- for (let y = 0; y <= 1; y++) {
739
- const radius = y === 0 ? radiusBottom : radiusTop;
740
- const posY = y === 0 ? -halfHeight : halfHeight;
741
- for (let x = 0; x <= segments; x++) {
742
- const u = x / segments;
743
- const theta = u * Math.PI * 2;
744
- const cosTheta = Math.cos(theta);
745
- const sinTheta = Math.sin(theta);
746
- positions.push(radius * cosTheta, posY, radius * sinTheta);
747
- normals.push(cosTheta, 0, sinTheta);
748
- uvs.push(u, y);
749
- }
750
- }
751
- for (let x = 0; x < segments; x++) {
752
- const a = x;
753
- const b = x + segments + 1;
754
- indices.push(a, b, a + 1);
755
- indices.push(b, b + 1, a + 1);
756
- }
757
- const topCenterIndex = positions.length / 3;
758
- positions.push(0, halfHeight, 0);
759
- normals.push(0, 1, 0);
760
- uvs.push(0.5, 0.5);
761
- for (let x = 0; x <= segments; x++) {
762
- const u = x / segments;
763
- const theta = u * Math.PI * 2;
764
- positions.push(radiusTop * Math.cos(theta), halfHeight, radiusTop * Math.sin(theta));
765
- normals.push(0, 1, 0);
766
- uvs.push(Math.cos(theta) * 0.5 + 0.5, Math.sin(theta) * 0.5 + 0.5);
767
- }
768
- for (let x = 0; x < segments; x++) {
769
- indices.push(topCenterIndex, topCenterIndex + x + 1, topCenterIndex + x + 2);
770
- }
771
- const bottomCenterIndex = positions.length / 3;
772
- positions.push(0, -halfHeight, 0);
773
- normals.push(0, -1, 0);
774
- uvs.push(0.5, 0.5);
775
- for (let x = 0; x <= segments; x++) {
776
- const u = x / segments;
777
- const theta = u * Math.PI * 2;
778
- positions.push(radiusBottom * Math.cos(theta), -halfHeight, radiusBottom * Math.sin(theta));
779
- normals.push(0, -1, 0);
780
- uvs.push(Math.cos(theta) * 0.5 + 0.5, Math.sin(theta) * 0.5 + 0.5);
781
- }
782
- for (let x = 0; x < segments; x++) {
783
- indices.push(bottomCenterIndex, bottomCenterIndex + x + 2, bottomCenterIndex + x + 1);
784
- }
785
- return {
786
- positions: new Float32Array(positions),
787
- normals: new Float32Array(normals),
788
- uvs: new Float32Array(uvs),
789
- indices: new Uint16Array(indices)
790
- };
791
- }
792
- function generateConeGeometry(scale) {
793
- return generateCylinderGeometry([1e-3, scale[1], scale[2]]);
794
- }
795
- function generatePlaneGeometry(scale) {
796
- const [sx, _, sz] = scale.map((s) => s * 0.5);
797
- const positions = new Float32Array([
798
- -sx,
799
- 0,
800
- -sz,
801
- sx,
802
- 0,
803
- -sz,
804
- sx,
805
- 0,
806
- sz,
807
- -sx,
808
- 0,
809
- sz
810
- ]);
811
- const normals = new Float32Array([
812
- 0,
813
- 1,
814
- 0,
815
- 0,
816
- 1,
817
- 0,
818
- 0,
819
- 1,
820
- 0,
821
- 0,
822
- 1,
823
- 0
824
- ]);
825
- const uvs = new Float32Array([
826
- 0,
827
- 0,
828
- 1,
829
- 0,
830
- 1,
831
- 1,
832
- 0,
833
- 1
834
- ]);
835
- const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
836
- return { positions, normals, uvs, indices };
837
- }
838
- var INITIAL_COMPILE_BUFFER_SIZE = 1024 * 1024;
839
- var MAX_RETAINED_BUFFER_SIZE = 1024 * 1024 * 20;
840
- var SHARED_COMPILE_BUFFER = new Uint8Array(INITIAL_COMPILE_BUFFER_SIZE);
841
- var GLTFPipeline = class extends CompilerBase {
842
- constructor(options = {}) {
843
- super();
844
- this.compilerName = "GLTFPipeline";
845
- this.bufferByteLength = 0;
846
- this.accessors = [];
847
- this.bufferViews = [];
848
- this.meshes = [];
849
- this.materials = [];
850
- this.nodes = [];
851
- this.scenes = [];
852
- this.animations = [];
853
- this.materialMap = /* @__PURE__ */ new Map();
854
- this.images = [];
855
- this.textures = [];
856
- this.samplers = [];
857
- this.scaleTextureIndex = -1;
858
- this.scaleNormalTextureIndex = -1;
859
- this.textureIndexMap = /* @__PURE__ */ new Map();
860
- this.stats = {
861
- nodeCount: 0,
862
- meshCount: 0,
863
- materialCount: 0,
864
- textureCount: 0,
865
- animationCount: 0,
866
- totalVertices: 0,
867
- totalTriangles: 0,
868
- fileSizeBytes: 0
869
- };
870
- this._lodCoverages = null;
871
- this._skins = null;
872
- this._jointStartIndex = 0;
873
- this.options = {
874
- format: options.format ?? "glb",
875
- dracoCompression: options.dracoCompression ?? false,
876
- quantize: options.quantize ?? true,
877
- prune: options.prune ?? true,
878
- dedupe: options.dedupe ?? true,
879
- embedTextures: options.embedTextures ?? true,
880
- textureData: options.textureData ?? {},
881
- generator: options.generator ?? "HoloScript GLTFPipeline v1.0.0",
882
- copyright: options.copyright ?? ""
883
- };
884
- this.compositor = new TraitCompositor();
885
- }
886
- getRequiredCapability() {
887
- return ANSCapabilityPath.GLTF;
888
- }
889
- get bufferData() {
890
- return SHARED_COMPILE_BUFFER;
891
- }
892
- set bufferData(v) {
893
- SHARED_COMPILE_BUFFER = v;
894
- }
895
- ensureBufferCapacity(needed) {
896
- if (this.bufferByteLength + needed > this.bufferData.length) {
897
- const newLen = Math.max(this.bufferData.length * 2, this.bufferByteLength + needed);
898
- const newArr = new Uint8Array(newLen);
899
- newArr.set(this.bufferData.subarray(0, this.bufferByteLength));
900
- this.bufferData = newArr;
901
- }
902
- }
903
- appendToBuffer(data) {
904
- this.ensureBufferCapacity(data.length);
905
- this.bufferData.set(data, this.bufferByteLength);
906
- this.bufferByteLength += data.length;
907
- }
908
- padBuffer(alignment) {
909
- const remainder = this.bufferByteLength % alignment;
910
- if (remainder !== 0) {
911
- const padding = alignment - remainder;
912
- this.ensureBufferCapacity(padding);
913
- this.bufferByteLength += padding;
914
- }
915
- }
916
- /**
917
- * Global smooth normals pass.
918
- * Finds vertices at the same 3D position across all meshes and averages
919
- * their normals. This eliminates hard seams where separate meshes meet
920
- * (e.g., neck spline meets the head sphere).
921
- */
922
- smoothNormalsGlobal() {
923
- const QUANT = 100;
924
- const posMap = /* @__PURE__ */ new Map();
925
- for (const mesh of this.meshes) {
926
- for (const prim of mesh.primitives) {
927
- const posIdx = prim.attributes.POSITION;
928
- const nrmIdx = prim.attributes.NORMAL;
929
- if (posIdx === void 0 || nrmIdx === void 0) continue;
930
- const posAcc = this.accessors[posIdx];
931
- const nrmAcc = this.accessors[nrmIdx];
932
- if (!posAcc || !nrmAcc) continue;
933
- const posBV = this.bufferViews[posAcc.bufferView];
934
- const nrmBV = this.bufferViews[nrmAcc.bufferView];
935
- if (!posBV || !nrmBV) continue;
936
- const count = posAcc.count;
937
- for (let i = 0; i < count; i++) {
938
- const pOff = posBV.byteOffset + i * 12;
939
- const px = this.readFloat32(pOff);
940
- const py = this.readFloat32(pOff + 4);
941
- const pz = this.readFloat32(pOff + 8);
942
- const nOff = nrmBV.byteOffset + i * 12;
943
- const nx = this.readFloat32(nOff);
944
- const ny = this.readFloat32(nOff + 4);
945
- const nz = this.readFloat32(nOff + 8);
946
- const key = `${Math.round(px * QUANT)},${Math.round(py * QUANT)},${Math.round(pz * QUANT)}`;
947
- let entries = posMap.get(key);
948
- if (!entries) {
949
- entries = [];
950
- posMap.set(key, entries);
951
- }
952
- entries.push({ nx, ny, nz, byteOffset: nOff });
953
- }
954
- }
955
- }
956
- for (const entries of posMap.values()) {
957
- if (entries.length < 2) continue;
958
- let ax = 0, ay = 0, az = 0;
959
- for (const e of entries) {
960
- ax += e.nx;
961
- ay += e.ny;
962
- az += e.nz;
963
- }
964
- const len = Math.sqrt(ax * ax + ay * ay + az * az);
965
- if (len < 1e-8) continue;
966
- ax /= len;
967
- ay /= len;
968
- az /= len;
969
- for (const e of entries) {
970
- this.writeFloat32(e.byteOffset, ax);
971
- this.writeFloat32(e.byteOffset + 4, ay);
972
- this.writeFloat32(e.byteOffset + 8, az);
973
- }
974
- }
975
- }
976
- /** Read a Float32 from the buffer at byteOffset */
977
- readFloat32(byteOffset) {
978
- const bytes = new Uint8Array(4);
979
- bytes.set(this.bufferData.subarray(byteOffset, byteOffset + 4));
980
- return new Float32Array(bytes.buffer)[0];
981
- }
982
- /** Write a Float32 to the buffer at byteOffset */
983
- writeFloat32(byteOffset, value) {
984
- const f32 = new Float32Array([value]);
985
- const view = new Uint8Array(f32.buffer);
986
- this.bufferData.set(view, byteOffset);
987
- }
988
- /**
989
- * Generate LOD meshes for the MSFT_lod extension.
990
- * Creates 2 lower detail levels (50% and 25%) per mesh.
991
- */
992
- generateLODs() {
993
- const ratios = [0.5, 0.25];
994
- const originalCount = this.meshes.length;
995
- const coverages = [];
996
- for (let mi = 0; mi < originalCount; mi++) {
997
- const mesh = this.meshes[mi];
998
- const lodIds = [];
999
- for (const ratio of ratios) {
1000
- for (const prim of mesh.primitives) {
1001
- const posIdx = prim.attributes.POSITION;
1002
- const nrmIdx = prim.attributes.NORMAL;
1003
- const uvIdx = prim.attributes.TEXCOORD_0;
1004
- const idxIdx = prim.indices;
1005
- if (posIdx === void 0 || nrmIdx === void 0 || idxIdx === void 0) continue;
1006
- const posAcc = this.accessors[posIdx];
1007
- const nrmAcc = this.accessors[nrmIdx];
1008
- const idxAcc = this.accessors[idxIdx];
1009
- if (!posAcc || !nrmAcc || !idxAcc) continue;
1010
- const posBV = this.bufferViews[posAcc.bufferView];
1011
- const nrmBV = this.bufferViews[nrmAcc.bufferView];
1012
- const idxBV = this.bufferViews[idxAcc.bufferView];
1013
- if (!posBV || !nrmBV || !idxBV) continue;
1014
- const vCount = posAcc.count;
1015
- const iCount = idxAcc.count;
1016
- const positions = new Float32Array(vCount * 3);
1017
- const normals = new Float32Array(vCount * 3);
1018
- const uvs = new Float32Array(vCount * 2);
1019
- const indices = new Uint16Array(iCount);
1020
- for (let i = 0; i < vCount * 3; i++) {
1021
- positions[i] = this.readFloat32(posBV.byteOffset + i * 4);
1022
- normals[i] = this.readFloat32(nrmBV.byteOffset + i * 4);
1023
- }
1024
- if (uvIdx !== void 0) {
1025
- const uvAcc = this.accessors[uvIdx];
1026
- const uvBV = uvAcc ? this.bufferViews[uvAcc.bufferView] : null;
1027
- if (uvBV) {
1028
- for (let i = 0; i < vCount * 2; i++) {
1029
- uvs[i] = this.readFloat32(uvBV.byteOffset + i * 4);
1030
- }
1031
- }
1032
- }
1033
- const compSize = idxAcc.componentType === 5123 ? 2 : 4;
1034
- for (let i = 0; i < iCount; i++) {
1035
- const off = idxBV.byteOffset + i * compSize;
1036
- if (compSize === 2) {
1037
- indices[i] = this.bufferData[off] | this.bufferData[off + 1] << 8;
1038
- } else {
1039
- indices[i] = this.readFloat32(off);
1040
- }
1041
- }
1042
- const geom = { positions, normals, uvs, indices };
1043
- const decimated = decimateGeometry(geom, ratio);
1044
- const lodMeshIndex = this.meshes.length;
1045
- const lodPrim = { attributes: {}, material: prim.material };
1046
- lodPrim.attributes.POSITION = this.createAccessor(decimated.positions, "VEC3", true);
1047
- lodPrim.attributes.NORMAL = this.createAccessor(decimated.normals, "VEC3");
1048
- lodPrim.attributes.TEXCOORD_0 = this.createAccessor(decimated.uvs, "VEC2");
1049
- lodPrim.indices = this.createAccessor(decimated.indices, "SCALAR");
1050
- this.meshes.push({ name: `LOD${ratio}_mesh${mi}`, primitives: [lodPrim] });
1051
- lodIds.push(lodMeshIndex);
1052
- }
1053
- }
1054
- if (lodIds.length > 0) {
1055
- mesh["extensions"] = {
1056
- MSFT_lod: { ids: lodIds }
1057
- };
1058
- coverages.push(0.5, 0.2);
1059
- }
1060
- }
1061
- if (coverages.length > 0) {
1062
- this._lodCoverages = coverages;
1063
- }
1064
- }
1065
- /**
1066
- * Build skeleton hierarchy and embed as glTF skin.
1067
- * Creates 20 joint nodes with inverse bind matrices.
1068
- */
1069
- buildSkeleton() {
1070
- const bones = DRAGON_SKELETON;
1071
- const boneNameToIdx = /* @__PURE__ */ new Map();
1072
- const jointStartIndex = this.nodes.length;
1073
- for (let i = 0; i < bones.length; i++) {
1074
- const bone = bones[i];
1075
- const nodeIdx = this.nodes.length;
1076
- boneNameToIdx.set(bone.name, nodeIdx);
1077
- const node = {
1078
- name: `Joint_${bone.name}`,
1079
- translation: bone.position
1080
- };
1081
- this.nodes.push(node);
1082
- }
1083
- for (const bone of bones) {
1084
- if (bone.parent) {
1085
- const parentIdx = boneNameToIdx.get(bone.parent);
1086
- const childIdx = boneNameToIdx.get(bone.name);
1087
- if (parentIdx !== void 0 && childIdx !== void 0) {
1088
- const parentNode = this.nodes[parentIdx];
1089
- if (!parentNode.children) parentNode.children = [];
1090
- parentNode.children.push(childIdx);
1091
- }
1092
- }
1093
- }
1094
- const ibmData = new Float32Array(bones.length * 16);
1095
- for (let i = 0; i < bones.length; i++) {
1096
- const p = bones[i].position;
1097
- const off = i * 16;
1098
- ibmData[off + 0] = 1;
1099
- ibmData[off + 5] = 1;
1100
- ibmData[off + 10] = 1;
1101
- ibmData[off + 15] = 1;
1102
- ibmData[off + 12] = -p[0];
1103
- ibmData[off + 13] = -p[1];
1104
- ibmData[off + 14] = -p[2];
1105
- }
1106
- const ibmAccessor = this.createAccessorRaw(ibmData, "MAT4", 5126);
1107
- const joints = [];
1108
- for (let i = 0; i < bones.length; i++) {
1109
- joints.push(jointStartIndex + i);
1110
- }
1111
- this._skins = [
1112
- {
1113
- skeleton: jointStartIndex,
1114
- joints,
1115
- inverseBindMatrices: ibmAccessor
1116
- }
1117
- ];
1118
- this._jointStartIndex = jointStartIndex;
1119
- }
1120
- /**
1121
- * Skin all mesh nodes to the skeleton.
1122
- * For each vertex, finds the 4 nearest bones by distance and assigns
1123
- * normalized inverse-distance weights. Writes JOINTS_0 (Uint8) and
1124
- * WEIGHTS_0 (Float32) attributes into each mesh primitive.
1125
- */
1126
- skinMeshes() {
1127
- if (!this._skins || this._skins.length === 0) return;
1128
- const bones = DRAGON_SKELETON;
1129
- const bonePositions = bones.map((b) => b.position);
1130
- const originalNodeCount = this._jointStartIndex;
1131
- for (let ni = 0; ni < originalNodeCount; ni++) {
1132
- const node = this.nodes[ni];
1133
- if (node.mesh === void 0) continue;
1134
- const mesh = this.meshes[node.mesh];
1135
- if (!mesh) continue;
1136
- for (const prim of mesh.primitives) {
1137
- const posIdx = prim.attributes.POSITION;
1138
- if (posIdx === void 0) continue;
1139
- const posAcc = this.accessors[posIdx];
1140
- if (!posAcc) continue;
1141
- const posBV = this.bufferViews[posAcc.bufferView];
1142
- if (!posBV) continue;
1143
- const vCount = posAcc.count;
1144
- if (vCount === 0) continue;
1145
- const tx = node.translation ? node.translation[0] : 0;
1146
- const ty = node.translation ? node.translation[1] : 0;
1147
- const tz = node.translation ? node.translation[2] : 0;
1148
- const jointsData = new Uint8Array(vCount * 4);
1149
- const weightsData = new Float32Array(vCount * 4);
1150
- for (let vi = 0; vi < vCount; vi++) {
1151
- const pOff = posBV.byteOffset + vi * 12;
1152
- const vx = this.readFloat32(pOff) + tx;
1153
- const vy = this.readFloat32(pOff + 4) + ty;
1154
- const vz = this.readFloat32(pOff + 8) + tz;
1155
- const distances = [];
1156
- for (let bi = 0; bi < bonePositions.length; bi++) {
1157
- const bp = bonePositions[bi];
1158
- const dx = vx - bp[0], dy = vy - bp[1], dz = vz - bp[2];
1159
- distances.push({ boneIdx: bi, distSq: dx * dx + dy * dy + dz * dz });
1160
- }
1161
- distances.sort((a, b) => a.distSq - b.distSq);
1162
- const top4 = distances.slice(0, 4);
1163
- let totalWeight = 0;
1164
- const rawWeights = [];
1165
- for (const d of top4) {
1166
- const w = 1 / (d.distSq + 1e-4);
1167
- rawWeights.push(w);
1168
- totalWeight += w;
1169
- }
1170
- const off = vi * 4;
1171
- for (let j = 0; j < 4; j++) {
1172
- if (j < top4.length) {
1173
- jointsData[off + j] = top4[j].boneIdx;
1174
- weightsData[off + j] = rawWeights[j] / totalWeight;
1175
- } else {
1176
- jointsData[off + j] = 0;
1177
- weightsData[off + j] = 0;
1178
- }
1179
- }
1180
- }
1181
- prim.attributes.JOINTS_0 = this.createAccessorRaw(jointsData, "VEC4", 5121);
1182
- prim.attributes.WEIGHTS_0 = this.createAccessor(weightsData, "VEC4");
1183
- }
1184
- }
1185
- }
1186
- /**
1187
- * Compile a HoloScript composition to glTF format
1188
- */
1189
- compile(composition, agentToken, outputPath) {
1190
- this.validateCompilerAccess(agentToken, outputPath);
1191
- this.reset();
1192
- this.processComposition(composition);
1193
- this.smoothNormalsGlobal();
1194
- this.generateLODs();
1195
- this.buildSkeleton();
1196
- this.skinMeshes();
1197
- const buffer = this.bufferData.slice(0, this.bufferByteLength);
1198
- if (SHARED_COMPILE_BUFFER.length > MAX_RETAINED_BUFFER_SIZE) {
1199
- SHARED_COMPILE_BUFFER = new Uint8Array(INITIAL_COMPILE_BUFFER_SIZE);
1200
- }
1201
- const gltf = this.buildDocument(composition.name, buffer.byteLength);
1202
- if (this.options.format === "glb") {
1203
- const binary = this.createGLB(gltf, buffer);
1204
- this.stats.fileSizeBytes = binary.byteLength;
1205
- return {
1206
- binary,
1207
- stats: { ...this.stats }
1208
- };
1209
- } else {
1210
- this.stats.fileSizeBytes = JSON.stringify(gltf).length + buffer.byteLength;
1211
- return {
1212
- json: gltf,
1213
- buffer,
1214
- stats: { ...this.stats }
1215
- };
1216
- }
1217
- }
1218
- /**
1219
- * Reset pipeline state for new compilation
1220
- */
1221
- reset() {
1222
- this.bufferByteLength = 0;
1223
- this.accessors = [];
1224
- this.bufferViews = [];
1225
- this.meshes = [];
1226
- this.materials = [];
1227
- this.nodes = [];
1228
- this.scenes = [];
1229
- this.animations = [];
1230
- this.materialMap.clear();
1231
- this.images = [];
1232
- this.textures = [];
1233
- this.samplers = [];
1234
- this.scaleTextureIndex = -1;
1235
- this.scaleNormalTextureIndex = -1;
1236
- this.textureIndexMap.clear();
1237
- this.stats = {
1238
- nodeCount: 0,
1239
- meshCount: 0,
1240
- materialCount: 0,
1241
- textureCount: 0,
1242
- animationCount: 0,
1243
- totalVertices: 0,
1244
- totalTriangles: 0,
1245
- fileSizeBytes: 0
1246
- };
1247
- }
1248
- /**
1249
- * Process the entire composition
1250
- */
1251
- processComposition(composition) {
1252
- const rootNodes = [];
1253
- for (const object of composition.objects || []) {
1254
- const nodeIndex = this.processObject(object);
1255
- if (nodeIndex !== -1) {
1256
- rootNodes.push(nodeIndex);
1257
- }
1258
- }
1259
- for (const group of composition.spatialGroups || []) {
1260
- const nodeIndex = this.processSpatialGroup(group);
1261
- if (nodeIndex !== -1) {
1262
- rootNodes.push(nodeIndex);
1263
- }
1264
- }
1265
- for (const light of composition.lights || []) {
1266
- const nodeIndex = this.processLight(light);
1267
- if (nodeIndex !== -1) {
1268
- rootNodes.push(nodeIndex);
1269
- }
1270
- }
1271
- if (composition.camera) {
1272
- const nodeIndex = this.processCamera(composition.camera);
1273
- if (nodeIndex !== -1) {
1274
- rootNodes.push(nodeIndex);
1275
- }
1276
- }
1277
- for (const timeline of composition.timelines || []) {
1278
- this.processTimeline(timeline);
1279
- }
1280
- const domainBlocks = composition.domainBlocks ?? [];
1281
- if (domainBlocks.length > 0) {
1282
- compileDomainBlocks(
1283
- domainBlocks,
1284
- {
1285
- material: (block) => {
1286
- const mat = compileMaterialBlock(block);
1287
- const gltfMat = materialToGLTF(mat);
1288
- this.materials.push(gltfMat);
1289
- this.stats.materialCount++;
1290
- return "";
1291
- }
1292
- },
1293
- () => ""
1294
- );
1295
- }
1296
- this.scenes.push({
1297
- name: composition.name,
1298
- nodes: rootNodes
1299
- });
1300
- }
1301
- /**
1302
- * Process a single object
1303
- */
1304
- processObject(object) {
1305
- const position = this.extractVec3Prop(object, "position", [0, 0, 0]);
1306
- const rotation = this.extractVec3Prop(object, "rotation", [0, 0, 0]);
1307
- const scale = this.extractVec3Prop(object, "scale", [1, 1, 1]);
1308
- const geometryProp = this.findProp(object, "geometry") || this.findProp(object, "mesh") || this.findProp(object, "type");
1309
- const shapeType = (typeof geometryProp === "string" ? geometryProp : "box").toLowerCase();
1310
- let meshIndex;
1311
- if (PRIMITIVE_GENERATORS[shapeType]) {
1312
- meshIndex = this.createPrimitiveMesh(object.name, shapeType, scale, object);
1313
- } else if (ADVANCED_GEOMETRY_TYPES.has(shapeType)) {
1314
- meshIndex = this.createAdvancedMesh(object.name, shapeType, scale, object);
1315
- }
1316
- const node = gltfNodePool.acquire();
1317
- node.name = object.name || `node_${this.nodes.length}`;
1318
- node.translation = position;
1319
- node.rotation = this.eulerToQuaternion(rotation);
1320
- node.scale = [1, 1, 1];
1321
- if (meshIndex !== void 0) {
1322
- node.mesh = meshIndex;
1323
- }
1324
- if (object.children && object.children.length > 0) {
1325
- const childIndices = [];
1326
- for (const child of object.children) {
1327
- const childIdx = this.processObject(child);
1328
- if (childIdx !== -1) childIndices.push(childIdx);
1329
- }
1330
- if (childIndices.length > 0) node.children = childIndices;
1331
- }
1332
- const nodeIndex = this.nodes.length;
1333
- this.nodes.push(node);
1334
- this.stats.nodeCount++;
1335
- return nodeIndex;
1336
- }
1337
- /**
1338
- * Process a spatial group
1339
- */
1340
- processSpatialGroup(group) {
1341
- const childIndices = [];
1342
- for (const child of group.objects || []) {
1343
- const childIndex = this.processObject(child);
1344
- if (childIndex !== -1) {
1345
- childIndices.push(childIndex);
1346
- }
1347
- }
1348
- if (group.groups) {
1349
- for (const sub of group.groups) {
1350
- const subIdx = this.processSpatialGroup(sub);
1351
- if (subIdx !== -1) childIndices.push(subIdx);
1352
- }
1353
- }
1354
- const posProp = (group.properties || []).find((p) => p.key === "position");
1355
- const position = posProp && Array.isArray(posProp.value) ? [
1356
- Number(posProp.value[0]) || 0,
1357
- Number(posProp.value[1]) || 0,
1358
- Number(posProp.value[2]) || 0
1359
- ] : [0, 0, 0];
1360
- const node = gltfNodePool.acquire();
1361
- node.name = group.name || `group_${this.nodes.length}`;
1362
- node.translation = position;
1363
- if (childIndices.length > 0) {
1364
- node.children = childIndices;
1365
- }
1366
- const nodeIndex = this.nodes.length;
1367
- this.nodes.push(node);
1368
- this.stats.nodeCount++;
1369
- return nodeIndex;
1370
- }
1371
- /**
1372
- * Process a timeline as animation.
1373
- *
1374
- * HoloTimeline has entries: HoloTimelineEntry[] where each entry has
1375
- * { time: number, action: HoloTimelineAction }.
1376
- * We extract 'animate' actions and group them by target + property.
1377
- */
1378
- processTimeline(timeline) {
1379
- if (!timeline.entries || timeline.entries.length === 0) return;
1380
- const animateEntries = timeline.entries.filter((e) => e.action.kind === "animate").map((e) => ({
1381
- time: e.time,
1382
- target: e.action.target,
1383
- properties: e.action.properties
1384
- }));
1385
- if (animateEntries.length === 0) return;
1386
- const channels = [];
1387
- const samplers = [];
1388
- const grouped = /* @__PURE__ */ new Map();
1389
- for (const entry of animateEntries) {
1390
- const targetNodeIndex = this.nodes.findIndex((n) => n.name === entry.target);
1391
- if (targetNodeIndex === -1) continue;
1392
- for (const [prop, value] of Object.entries(entry.properties)) {
1393
- const gltfPath = this.mapPropertyToGLTFPath(prop);
1394
- if (!gltfPath) continue;
1395
- const key = `${targetNodeIndex}:${gltfPath}`;
1396
- if (!grouped.has(key)) grouped.set(key, []);
1397
- grouped.get(key).push({ time: entry.time, value });
1398
- }
1399
- }
1400
- for (const [key, keyframes] of grouped) {
1401
- const [nodeIdxStr, gltfPath] = key.split(":");
1402
- const nodeIdx = parseInt(nodeIdxStr, 10);
1403
- const times = keyframes.map((kf) => kf.time / 1e3);
1404
- const inputAccessor = this.createAccessor(new Float32Array(times), "SCALAR");
1405
- const values = [];
1406
- for (const kf of keyframes) {
1407
- if (Array.isArray(kf.value)) {
1408
- if (gltfPath === "rotation") {
1409
- const quat = this.eulerToQuaternion(kf.value);
1410
- values.push(...quat);
1411
- } else {
1412
- values.push(...kf.value);
1413
- }
1414
- }
1415
- }
1416
- const outputType = gltfPath === "rotation" ? "VEC4" : "VEC3";
1417
- const outputAccessor = this.createAccessor(new Float32Array(values), outputType);
1418
- const samplerIndex = samplers.length;
1419
- samplers.push({ input: inputAccessor, interpolation: "LINEAR", output: outputAccessor });
1420
- channels.push({ sampler: samplerIndex, target: { node: nodeIdx, path: gltfPath } });
1421
- }
1422
- if (channels.length > 0) {
1423
- this.animations.push({
1424
- name: timeline.name || `animation_${this.animations.length}`,
1425
- channels,
1426
- samplers
1427
- });
1428
- this.stats.animationCount++;
1429
- }
1430
- }
1431
- /**
1432
- * Create a primitive mesh
1433
- */
1434
- createPrimitiveMesh(name, shapeType, scale, object) {
1435
- const generator = PRIMITIVE_GENERATORS[shapeType];
1436
- if (!generator) return -1;
1437
- const geometry = generator(scale);
1438
- const positionAccessor = this.createAccessor(geometry.positions, "VEC3", true);
1439
- const normalAccessor = this.createAccessor(geometry.normals, "VEC3");
1440
- const uvAccessor = this.createAccessor(geometry.uvs, "VEC2");
1441
- const indexAccessor = this.createAccessor(geometry.indices, "SCALAR");
1442
- const materialIndex = this.getOrCreateMaterial(object, shapeType);
1443
- const mesh = {
1444
- name: name || `mesh_${this.meshes.length}`,
1445
- primitives: [
1446
- {
1447
- attributes: {
1448
- POSITION: positionAccessor,
1449
- NORMAL: normalAccessor,
1450
- TEXCOORD_0: uvAccessor
1451
- },
1452
- indices: indexAccessor,
1453
- material: materialIndex,
1454
- mode: 4
1455
- // TRIANGLES
1456
- }
1457
- ]
1458
- };
1459
- const meshIndex = this.meshes.length;
1460
- this.meshes.push(mesh);
1461
- this.stats.meshCount++;
1462
- this.stats.totalVertices += geometry.positions.length / 3;
1463
- this.stats.totalTriangles += geometry.indices.length / 3;
1464
- return meshIndex;
1465
- }
1466
- /**
1467
- * Create an advanced mesh (spline, membrane, hull/metaball).
1468
- * These geometry types need additional properties beyond scale.
1469
- */
1470
- createAdvancedMesh(name, shapeType, scale, object) {
1471
- let geometry;
1472
- switch (shapeType) {
1473
- case "spline": {
1474
- const pointsProp = this.findProp(object, "points");
1475
- const radiiProp = this.findProp(object, "radii");
1476
- const segmentsProp = this.findProp(object, "segments");
1477
- const stepsProp = this.findProp(object, "steps");
1478
- const points = this.extractNestedArray(pointsProp, 3);
1479
- const radii = Array.isArray(radiiProp) ? radiiProp.map(Number) : [0.1];
1480
- while (radii.length < points.length) {
1481
- radii.push(radii[radii.length - 1] ?? 0.1);
1482
- }
1483
- const radialSegments = typeof segmentsProp === "number" ? segmentsProp : 24;
1484
- const lengthSteps = typeof stepsProp === "number" ? stepsProp : 8;
1485
- geometry = generateSplineGeometry(points, radii, radialSegments, lengthSteps);
1486
- break;
1487
- }
1488
- case "membrane": {
1489
- const anchorsProp = this.findProp(object, "anchors");
1490
- const subdivisionsProp = this.findProp(object, "subdivisions");
1491
- const bulgeProp = this.findProp(object, "bulge");
1492
- const anchors = this.extractNestedArray(anchorsProp, 3);
1493
- const subdivisions = typeof subdivisionsProp === "number" ? subdivisionsProp : 8;
1494
- const bulge = typeof bulgeProp === "number" ? bulgeProp : 0.15;
1495
- geometry = generateMembraneGeometry(anchors, subdivisions, bulge);
1496
- break;
1497
- }
1498
- case "hull":
1499
- case "blob":
1500
- case "metaball": {
1501
- const blobsProp = this.findProp(object, "blobs");
1502
- const resolutionProp = this.findProp(object, "resolution");
1503
- const thresholdProp = this.findProp(object, "threshold");
1504
- const blobs = [];
1505
- if (Array.isArray(blobsProp)) {
1506
- for (const blobItem of blobsProp) {
1507
- if (blobItem && typeof blobItem === "object") {
1508
- const b = blobItem;
1509
- const center = Array.isArray(b.center) ? b.center.map(Number) : [0, 0, 0];
1510
- const radius = Array.isArray(b.radius) ? b.radius.map(Number) : [0.5, 0.5, 0.5];
1511
- blobs.push({ center, radius });
1512
- }
1513
- }
1514
- }
1515
- if (blobs.length === 0) {
1516
- blobs.push({
1517
- center: [0, 0, 0],
1518
- radius: [scale[0] * 0.5, scale[1] * 0.5, scale[2] * 0.5]
1519
- });
1520
- }
1521
- const resolution = typeof resolutionProp === "number" ? resolutionProp : 24;
1522
- const threshold = typeof thresholdProp === "number" ? thresholdProp : 1;
1523
- geometry = generateHullGeometry(blobs, resolution, threshold);
1524
- break;
1525
- }
1526
- default:
1527
- return -1;
1528
- }
1529
- const positionAccessor = this.createAccessor(geometry.positions, "VEC3", true);
1530
- const normalAccessor = this.createAccessor(geometry.normals, "VEC3");
1531
- const uvAccessor = this.createAccessor(geometry.uvs, "VEC2");
1532
- const indexAccessor = this.createAccessor(geometry.indices, "SCALAR");
1533
- const materialIndex = this.getOrCreateMaterial(object, shapeType);
1534
- const mesh = {
1535
- name: name || `mesh_${this.meshes.length}`,
1536
- primitives: [
1537
- {
1538
- attributes: {
1539
- POSITION: positionAccessor,
1540
- NORMAL: normalAccessor,
1541
- TEXCOORD_0: uvAccessor
1542
- },
1543
- indices: indexAccessor,
1544
- material: materialIndex,
1545
- mode: 4
1546
- // TRIANGLES
1547
- }
1548
- ]
1549
- };
1550
- const meshIndex = this.meshes.length;
1551
- this.meshes.push(mesh);
1552
- this.stats.meshCount++;
1553
- this.stats.totalVertices += geometry.positions.length / 3;
1554
- this.stats.totalTriangles += geometry.indices.length / 3;
1555
- return meshIndex;
1556
- }
1557
- /**
1558
- * Extract a nested array of vec3s from a HoloValue.
1559
- * e.g. [[0, 1, 2], [3, 4, 5]] => [[0,1,2], [3,4,5]]
1560
- */
1561
- extractNestedArray(val, expectedLength) {
1562
- const result = [];
1563
- if (!Array.isArray(val)) return result;
1564
- for (const item of val) {
1565
- if (Array.isArray(item) && item.length >= expectedLength) {
1566
- result.push(item.slice(0, expectedLength).map(Number));
1567
- }
1568
- }
1569
- return result;
1570
- }
1571
- /**
1572
- * Get or create a material from object properties + traits.
1573
- *
1574
- * Material composition order (later overrides earlier):
1575
- * 1. Defaults (white, metallic 0, roughness 0.5)
1576
- * 2. Named material preset (e.g., material: "glass")
1577
- * 3. TraitCompositor output (PBR from visual presets)
1578
- * 4. Direct property overrides (color, opacity)
1579
- */
1580
- getOrCreateMaterial(object, shapeType) {
1581
- let color = [1, 1, 1];
1582
- let metallic = 0;
1583
- let roughness = 0.5;
1584
- let opacity = 1;
1585
- let emissive = [0, 0, 0];
1586
- let transmission = 0;
1587
- let ior = 1.5;
1588
- let thickness = 0;
1589
- let clearcoat = 0;
1590
- let clearcoatRoughness = 0;
1591
- let sheen = 0;
1592
- let sheenRoughness = 0.5;
1593
- let sheenColor = [1, 1, 1];
1594
- let iridescence = 0;
1595
- let iridescenceIOR = 1.3;
1596
- let anisotropy = 0;
1597
- let anisotropyRotation = 0;
1598
- let attenuationColor;
1599
- let attenuationDistance;
1600
- const namedMat = this.findProp(object, "material");
1601
- if (typeof namedMat === "string" && MATERIAL_PRESETS[namedMat]) {
1602
- const preset = MATERIAL_PRESETS[namedMat];
1603
- if (preset.color) color = this.parseColorString(preset.color);
1604
- if (preset.metalness !== void 0) metallic = preset.metalness;
1605
- if (preset.roughness !== void 0) roughness = preset.roughness;
1606
- if (preset.opacity !== void 0) opacity = preset.opacity;
1607
- if (preset.transparent && opacity >= 1) opacity = 0.99;
1608
- if (preset.emissive) emissive = this.parseColorString(preset.emissive);
1609
- if (preset.transmission !== void 0) transmission = preset.transmission;
1610
- if (preset.ior !== void 0) ior = preset.ior;
1611
- if (preset.thickness !== void 0) thickness = preset.thickness;
1612
- if (preset.clearcoat !== void 0) clearcoat = preset.clearcoat;
1613
- if (preset.clearcoatRoughness !== void 0) clearcoatRoughness = preset.clearcoatRoughness;
1614
- if (preset.sheen !== void 0) sheen = preset.sheen;
1615
- if (preset.sheenRoughness !== void 0) sheenRoughness = preset.sheenRoughness;
1616
- if (preset.sheenColor) sheenColor = this.parseColorString(preset.sheenColor);
1617
- if (preset.iridescence !== void 0) iridescence = preset.iridescence;
1618
- if (preset.iridescenceIOR !== void 0) iridescenceIOR = preset.iridescenceIOR;
1619
- if (preset.anisotropy !== void 0) anisotropy = preset.anisotropy;
1620
- if (preset.anisotropyRotation !== void 0) anisotropyRotation = preset.anisotropyRotation;
1621
- if (preset.attenuationColor)
1622
- attenuationColor = this.parseColorString(preset.attenuationColor);
1623
- if (preset.attenuationDistance !== void 0)
1624
- attenuationDistance = preset.attenuationDistance;
1625
- }
1626
- const traitNames = (object.traits || []).map((t) => t.name);
1627
- if (traitNames.length > 0) {
1628
- const composed = this.compositor.compose(traitNames);
1629
- if (composed.color) color = this.parseColorString(composed.color);
1630
- if (composed.metalness !== void 0) metallic = composed.metalness;
1631
- if (composed.roughness !== void 0) roughness = composed.roughness;
1632
- if (composed.opacity !== void 0) opacity = composed.opacity;
1633
- if (composed.emissive) emissive = this.parseColorString(composed.emissive);
1634
- if (composed.transparent) opacity = Math.min(opacity, 0.9);
1635
- if (composed.transmission !== void 0) transmission = composed.transmission;
1636
- if (composed.ior !== void 0) ior = composed.ior;
1637
- if (composed.thickness !== void 0) thickness = composed.thickness;
1638
- if (composed.clearcoat !== void 0) clearcoat = composed.clearcoat;
1639
- if (composed.clearcoatRoughness !== void 0)
1640
- clearcoatRoughness = composed.clearcoatRoughness;
1641
- if (composed.sheen !== void 0) sheen = composed.sheen;
1642
- if (composed.sheenRoughness !== void 0) sheenRoughness = composed.sheenRoughness;
1643
- if (composed.sheenColor) sheenColor = this.parseColorString(composed.sheenColor);
1644
- if (composed.iridescence !== void 0) iridescence = composed.iridescence;
1645
- if (composed.iridescenceIOR !== void 0) iridescenceIOR = composed.iridescenceIOR;
1646
- if (composed.anisotropy !== void 0) anisotropy = composed.anisotropy;
1647
- if (composed.anisotropyRotation !== void 0)
1648
- anisotropyRotation = composed.anisotropyRotation;
1649
- if (composed.attenuationColor)
1650
- attenuationColor = this.parseColorString(composed.attenuationColor);
1651
- if (composed.attenuationDistance !== void 0)
1652
- attenuationDistance = composed.attenuationDistance;
1653
- }
1654
- const colorProp = this.findProp(object, "color");
1655
- if (typeof colorProp === "string") {
1656
- color = this.parseColorString(colorProp);
1657
- } else if (Array.isArray(colorProp) && colorProp.length >= 3) {
1658
- color = [Number(colorProp[0]), Number(colorProp[1]), Number(colorProp[2])];
1659
- }
1660
- const opacityProp = this.findProp(object, "opacity");
1661
- if (typeof opacityProp === "number") opacity = opacityProp;
1662
- const metallicProp = this.findProp(object, "metallic") ?? this.findProp(object, "metalness");
1663
- if (typeof metallicProp === "number") metallic = metallicProp;
1664
- const roughnessProp = this.findProp(object, "roughness");
1665
- if (typeof roughnessProp === "number") roughness = roughnessProp;
1666
- const emissiveIntensityProp = this.findProp(object, "emissiveIntensity");
1667
- const emissiveIntensity = typeof emissiveIntensityProp === "number" ? emissiveIntensityProp : 1;
1668
- const emissiveProp = this.findProp(object, "emissive");
1669
- if (typeof emissiveProp === "string") emissive = this.parseColorString(emissiveProp);
1670
- const transmissionProp = this.findProp(object, "transmission");
1671
- if (typeof transmissionProp === "number") transmission = transmissionProp;
1672
- const iorProp = this.findProp(object, "ior");
1673
- if (typeof iorProp === "number") ior = iorProp;
1674
- const clearcoatProp = this.findProp(object, "clearcoat");
1675
- if (typeof clearcoatProp === "number") clearcoat = clearcoatProp;
1676
- const texturePaths = {};
1677
- const TEX_PROP_MAP = {
1678
- // HoloScript property name → glTF slot key
1679
- baseColorMap: "baseColor",
1680
- albedo_map: "baseColor",
1681
- colorMap: "baseColor",
1682
- normalMap: "normal",
1683
- normal_map: "normal",
1684
- roughnessMap: "metallicRoughness",
1685
- roughness_map: "metallicRoughness",
1686
- metallicMap: "metallicRoughness",
1687
- metallic_map: "metallicRoughness",
1688
- metalnessMap: "metallicRoughness",
1689
- occlusionMap: "occlusion",
1690
- ao_map: "occlusion",
1691
- ambientOcclusionMap: "occlusion",
1692
- emissiveMap: "emissive",
1693
- emissive_map: "emissive",
1694
- emissionMap: "emissive"
1695
- };
1696
- for (const [propName, slotKey] of Object.entries(TEX_PROP_MAP)) {
1697
- const val = this.findProp(object, propName);
1698
- if (typeof val === "string" && val.length > 0 && !texturePaths[slotKey]) {
1699
- texturePaths[slotKey] = val;
1700
- }
1701
- }
1702
- const key = JSON.stringify({
1703
- color,
1704
- metallic,
1705
- roughness,
1706
- opacity,
1707
- emissive,
1708
- transmission,
1709
- ior,
1710
- thickness,
1711
- clearcoat,
1712
- clearcoatRoughness,
1713
- sheen,
1714
- sheenRoughness,
1715
- sheenColor,
1716
- iridescence,
1717
- iridescenceIOR,
1718
- anisotropy,
1719
- anisotropyRotation,
1720
- attenuationColor,
1721
- attenuationDistance,
1722
- texturePaths
1723
- });
1724
- if (this.materialMap.has(key)) {
1725
- return this.materialMap.get(key);
1726
- }
1727
- const material = {
1728
- name: `material_${this.materials.length}`,
1729
- pbrMetallicRoughness: {
1730
- baseColorFactor: [...color, opacity],
1731
- metallicFactor: metallic,
1732
- roughnessFactor: roughness
1733
- },
1734
- doubleSided: opacity < 1 || transmission > 0
1735
- };
1736
- if (opacity < 1 || transmission > 0) {
1737
- material.alphaMode = "BLEND";
1738
- }
1739
- const emissiveSum = emissive[0] + emissive[1] + emissive[2];
1740
- if (emissiveSum > 0) {
1741
- material.emissiveFactor = emissive.map((c) => c * emissiveIntensity);
1742
- }
1743
- const extensions = {};
1744
- if (transmission > 0) {
1745
- Object.assign(extensions, createTransmissionExtension(transmission));
1746
- }
1747
- if (ior !== 1.5) {
1748
- Object.assign(extensions, createIORExtension(ior));
1749
- }
1750
- if (clearcoat > 0) {
1751
- Object.assign(
1752
- extensions,
1753
- createClearcoatExtension({ factor: clearcoat, roughness: clearcoatRoughness })
1754
- );
1755
- }
1756
- if (sheen > 0) {
1757
- Object.assign(
1758
- extensions,
1759
- createSheenExtension({ color: sheenColor, roughness: sheenRoughness })
1760
- );
1761
- }
1762
- if (iridescence > 0) {
1763
- Object.assign(
1764
- extensions,
1765
- createIridescenceExtension({ factor: iridescence, ior: iridescenceIOR })
1766
- );
1767
- }
1768
- if (anisotropy > 0) {
1769
- Object.assign(
1770
- extensions,
1771
- createAnisotropyExtension({ strength: anisotropy, rotation: anisotropyRotation })
1772
- );
1773
- }
1774
- if (thickness > 0 || attenuationDistance !== void 0) {
1775
- Object.assign(
1776
- extensions,
1777
- createVolumeExtension({
1778
- thickness: thickness || 1,
1779
- attenuationDistance,
1780
- attenuationColor
1781
- })
1782
- );
1783
- }
1784
- if (emissiveIntensity > 1) {
1785
- Object.assign(extensions, createEmissiveStrengthExtension(emissiveIntensity));
1786
- }
1787
- if (Object.keys(extensions).length > 0) {
1788
- material.extensions = extensions;
1789
- }
1790
- const isScaledGeometry = shapeType === "hull" || shapeType === "metaball";
1791
- const isScaledMaterial = typeof namedMat === "string" && (namedMat === "skin_dark" || namedMat === "leather");
1792
- if (isScaledGeometry || isScaledMaterial) {
1793
- const texIdx = this.ensureScaleTexture();
1794
- if (texIdx >= 0) {
1795
- material.pbrMetallicRoughness.baseColorTexture = {
1796
- index: texIdx,
1797
- texCoord: 0
1798
- };
1799
- if (this.scaleNormalTextureIndex >= 0) {
1800
- material.normalTexture = {
1801
- index: this.scaleNormalTextureIndex,
1802
- texCoord: 0,
1803
- scale: 1
1804
- };
1805
- }
1806
- this.stats.textureCount = 2;
1807
- }
1808
- }
1809
- if (Object.keys(texturePaths).length > 0) {
1810
- const pbr = material.pbrMetallicRoughness;
1811
- if (texturePaths.baseColor && !pbr.baseColorTexture) {
1812
- const idx = this.getOrCreateExternalTexture(texturePaths.baseColor);
1813
- if (idx >= 0) pbr.baseColorTexture = { index: idx, texCoord: 0 };
1814
- }
1815
- if (texturePaths.metallicRoughness && !pbr.metallicRoughnessTexture) {
1816
- const idx = this.getOrCreateExternalTexture(texturePaths.metallicRoughness);
1817
- if (idx >= 0) pbr.metallicRoughnessTexture = { index: idx, texCoord: 0 };
1818
- }
1819
- const matRecord = material;
1820
- if (texturePaths.normal && !matRecord.normalTexture) {
1821
- const idx = this.getOrCreateExternalTexture(texturePaths.normal);
1822
- if (idx >= 0) matRecord.normalTexture = { index: idx, texCoord: 0, scale: 1 };
1823
- }
1824
- if (texturePaths.occlusion && !matRecord.occlusionTexture) {
1825
- const idx = this.getOrCreateExternalTexture(texturePaths.occlusion);
1826
- if (idx >= 0) matRecord.occlusionTexture = { index: idx, texCoord: 0, strength: 1 };
1827
- }
1828
- if (texturePaths.emissive && !matRecord.emissiveTexture) {
1829
- const idx = this.getOrCreateExternalTexture(texturePaths.emissive);
1830
- if (idx >= 0) matRecord.emissiveTexture = { index: idx, texCoord: 0 };
1831
- }
1832
- }
1833
- const materialIndex = this.materials.length;
1834
- this.materials.push(material);
1835
- this.materialMap.set(key, materialIndex);
1836
- this.stats.materialCount++;
1837
- return materialIndex;
1838
- }
1839
- /**
1840
- * Embed an external texture image in the glTF buffer. Returns the texture
1841
- * index, or -1 if the image data is not available in options.textureData.
1842
- * Caches by path so the same image is only embedded once.
1843
- */
1844
- getOrCreateExternalTexture(path) {
1845
- if (this.textureIndexMap.has(path)) {
1846
- return this.textureIndexMap.get(path);
1847
- }
1848
- const imageData = this.options.textureData[path];
1849
- if (!imageData || imageData.length === 0) {
1850
- return -1;
1851
- }
1852
- const isPNG = imageData[0] === 137 && imageData[1] === 80;
1853
- const isJPEG = imageData[0] === 255 && imageData[1] === 216;
1854
- const mimeType = isPNG ? "image/png" : isJPEG ? "image/jpeg" : "image/png";
1855
- const byteOffset = this.bufferByteLength;
1856
- this.appendToBuffer(imageData);
1857
- this.padBuffer(4);
1858
- const bufferViewIndex = this.bufferViews.length;
1859
- this.bufferViews.push({
1860
- buffer: 0,
1861
- byteOffset,
1862
- byteLength: imageData.length
1863
- });
1864
- const imageIndex = this.images.length;
1865
- this.images.push({
1866
- bufferView: bufferViewIndex,
1867
- mimeType
1868
- });
1869
- let samplerIndex;
1870
- if (this.samplers.length > 0) {
1871
- samplerIndex = 0;
1872
- } else {
1873
- samplerIndex = this.samplers.length;
1874
- this.samplers.push({
1875
- magFilter: 9729,
1876
- // LINEAR
1877
- minFilter: 9987,
1878
- // LINEAR_MIPMAP_LINEAR
1879
- wrapS: 10497,
1880
- // REPEAT
1881
- wrapT: 10497
1882
- // REPEAT
1883
- });
1884
- }
1885
- const textureIndex = this.textures.length;
1886
- this.textures.push({
1887
- source: imageIndex,
1888
- sampler: samplerIndex
1889
- });
1890
- this.textureIndexMap.set(path, textureIndex);
1891
- this.stats.textureCount++;
1892
- return textureIndex;
1893
- }
1894
- /**
1895
- * Ensure the procedural scale texture is generated and embedded in the glTF.
1896
- * Returns the texture index, or -1 if generation failed.
1897
- */
1898
- ensureScaleTexture() {
1899
- if (this.scaleTextureIndex >= 0) return this.scaleTextureIndex;
1900
- const TEX_SIZE = 256;
1901
- const pixels = generateScaleTexture(TEX_SIZE);
1902
- const pngData = encodePNG(pixels, TEX_SIZE, TEX_SIZE);
1903
- const byteOffset = this.bufferByteLength;
1904
- this.appendToBuffer(pngData);
1905
- this.padBuffer(4);
1906
- const bufferViewIndex = this.bufferViews.length;
1907
- this.bufferViews.push({
1908
- buffer: 0,
1909
- byteOffset,
1910
- byteLength: pngData.length
1911
- });
1912
- const imageIndex = this.images.length;
1913
- this.images.push({
1914
- bufferView: bufferViewIndex,
1915
- mimeType: "image/png"
1916
- });
1917
- const samplerIndex = this.samplers.length;
1918
- this.samplers.push({
1919
- magFilter: 9729,
1920
- // LINEAR
1921
- minFilter: 9987,
1922
- // LINEAR_MIPMAP_LINEAR
1923
- wrapS: 10497,
1924
- // REPEAT
1925
- wrapT: 10497
1926
- // REPEAT
1927
- });
1928
- this.scaleTextureIndex = this.textures.length;
1929
- this.textures.push({
1930
- source: imageIndex,
1931
- sampler: samplerIndex
1932
- });
1933
- const normalPixels = generateScaleNormalMap(TEX_SIZE);
1934
- const normalPng = encodePNG(normalPixels, TEX_SIZE, TEX_SIZE);
1935
- const normalByteOffset = this.bufferByteLength;
1936
- this.appendToBuffer(normalPng);
1937
- this.padBuffer(4);
1938
- const normalBVIndex = this.bufferViews.length;
1939
- this.bufferViews.push({
1940
- buffer: 0,
1941
- byteOffset: normalByteOffset,
1942
- byteLength: normalPng.length
1943
- });
1944
- const normalImageIndex = this.images.length;
1945
- this.images.push({
1946
- bufferView: normalBVIndex,
1947
- mimeType: "image/png"
1948
- });
1949
- this.scaleNormalTextureIndex = this.textures.length;
1950
- this.textures.push({
1951
- source: normalImageIndex,
1952
- sampler: samplerIndex
1953
- // reuse same sampler
1954
- });
1955
- return this.scaleTextureIndex;
1956
- }
1957
- /**
1958
- * Create an accessor with arbitrary type string (e.g., MAT4 for skeleton data, VEC4 for skinning).
1959
- * Supports Uint8Array for UNSIGNED_BYTE (5121), Uint16Array for UNSIGNED_SHORT (5123),
1960
- * Uint32Array for UNSIGNED_INT (5125), and Float32Array for FLOAT (5126).
1961
- */
1962
- createAccessorRaw(data, type, componentType) {
1963
- const byteOffset = this.bufferByteLength;
1964
- const view = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
1965
- this.appendToBuffer(view);
1966
- this.padBuffer(4);
1967
- const bufferViewIndex = this.bufferViews.length;
1968
- this.bufferViews.push({
1969
- buffer: 0,
1970
- byteOffset,
1971
- byteLength: data.byteLength
1972
- });
1973
- const comps = type === "MAT4" ? 16 : type === "VEC4" ? 4 : type === "VEC3" ? 3 : type === "VEC2" ? 2 : 1;
1974
- const count = data.length / comps;
1975
- const accessorIndex = this.accessors.length;
1976
- this.accessors.push({
1977
- bufferView: bufferViewIndex,
1978
- componentType,
1979
- count,
1980
- type
1981
- });
1982
- return accessorIndex;
1983
- }
1984
- /**
1985
- * Create an accessor and buffer view for data
1986
- */
1987
- createAccessor(data, type, computeBounds = false) {
1988
- const byteOffset = this.bufferByteLength;
1989
- const componentType = data instanceof Uint32Array ? 5125 : data instanceof Uint16Array ? 5123 : 5126;
1990
- const view = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
1991
- this.appendToBuffer(view);
1992
- this.padBuffer(4);
1993
- const bufferViewIndex = this.bufferViews.length;
1994
- const bufferView = {
1995
- buffer: 0,
1996
- byteOffset,
1997
- byteLength: data.byteLength
1998
- };
1999
- if (componentType === 5126) {
2000
- bufferView.target = 34962;
2001
- } else {
2002
- bufferView.target = 34963;
2003
- }
2004
- this.bufferViews.push(bufferView);
2005
- const componentsPerElement = type === "SCALAR" ? 1 : type === "VEC2" ? 2 : type === "VEC3" ? 3 : 4;
2006
- const count = data.length / componentsPerElement;
2007
- const accessor = {
2008
- bufferView: bufferViewIndex,
2009
- componentType,
2010
- count,
2011
- type
2012
- };
2013
- if (computeBounds && type === "VEC3" && data instanceof Float32Array) {
2014
- const min = [Infinity, Infinity, Infinity];
2015
- const max = [-Infinity, -Infinity, -Infinity];
2016
- for (let i = 0; i < data.length; i += 3) {
2017
- min[0] = Math.min(min[0], data[i]);
2018
- min[1] = Math.min(min[1], data[i + 1]);
2019
- min[2] = Math.min(min[2], data[i + 2]);
2020
- max[0] = Math.max(max[0], data[i]);
2021
- max[1] = Math.max(max[1], data[i + 1]);
2022
- max[2] = Math.max(max[2], data[i + 2]);
2023
- }
2024
- accessor.min = min;
2025
- accessor.max = max;
2026
- }
2027
- const accessorIndex = this.accessors.length;
2028
- this.accessors.push(accessor);
2029
- return accessorIndex;
2030
- }
2031
- /**
2032
- * Build the glTF JSON document
2033
- */
2034
- buildDocument(name, bufferLength) {
2035
- const gltf = {
2036
- asset: {
2037
- version: "2.0",
2038
- generator: this.options.generator
2039
- },
2040
- scene: 0,
2041
- scenes: this.scenes,
2042
- nodes: this.nodes,
2043
- meshes: this.meshes,
2044
- materials: this.materials,
2045
- accessors: this.accessors,
2046
- bufferViews: this.bufferViews,
2047
- buffers: [
2048
- {
2049
- byteLength: bufferLength
2050
- }
2051
- ]
2052
- };
2053
- if (this.options.copyright) {
2054
- gltf.asset.copyright = this.options.copyright;
2055
- }
2056
- if (this.animations.length > 0) {
2057
- gltf.animations = this.animations;
2058
- }
2059
- if (this.images.length > 0) {
2060
- gltf.images = this.images;
2061
- gltf.textures = this.textures;
2062
- gltf.samplers = this.samplers;
2063
- }
2064
- if (this._skins && this._skins.length > 0) {
2065
- gltf.skins = this._skins;
2066
- }
2067
- declareExtensions(gltf);
2068
- if (this.options.format === "gltf") {
2069
- gltf.buffers[0].uri = `${name}.bin`;
2070
- }
2071
- return gltf;
2072
- }
2073
- /**
2074
- * Create a GLB binary file
2075
- */
2076
- createGLB(gltf, buffer) {
2077
- const jsonString = JSON.stringify(gltf);
2078
- const jsonBuffer = new TextEncoder().encode(jsonString);
2079
- const jsonPadding = (4 - jsonBuffer.byteLength % 4) % 4;
2080
- const paddedJsonLength = jsonBuffer.byteLength + jsonPadding;
2081
- const binPadding = (4 - buffer.byteLength % 4) % 4;
2082
- const paddedBinLength = buffer.byteLength + binPadding;
2083
- const totalSize = 12 + 8 + paddedJsonLength + 8 + paddedBinLength;
2084
- const output = new ArrayBuffer(totalSize);
2085
- const view = new DataView(output);
2086
- const bytes = new Uint8Array(output);
2087
- let offset = 0;
2088
- view.setUint32(offset, 1179937895, true);
2089
- offset += 4;
2090
- view.setUint32(offset, 2, true);
2091
- offset += 4;
2092
- view.setUint32(offset, totalSize, true);
2093
- offset += 4;
2094
- view.setUint32(offset, paddedJsonLength, true);
2095
- offset += 4;
2096
- view.setUint32(offset, 1313821514, true);
2097
- offset += 4;
2098
- bytes.set(jsonBuffer, offset);
2099
- offset += jsonBuffer.byteLength;
2100
- for (let i = 0; i < jsonPadding; i++) {
2101
- bytes[offset++] = 32;
2102
- }
2103
- view.setUint32(offset, paddedBinLength, true);
2104
- offset += 4;
2105
- view.setUint32(offset, 5130562, true);
2106
- offset += 4;
2107
- bytes.set(buffer, offset);
2108
- offset += buffer.byteLength;
2109
- for (let i = 0; i < binPadding; i++) {
2110
- bytes[offset++] = 0;
2111
- }
2112
- return new Uint8Array(output);
2113
- }
2114
- /**
2115
- * Process a light node (stored as extras for universal glTF compatibility)
2116
- */
2117
- processLight(light) {
2118
- const props = light.properties || [];
2119
- const findLightProp = (key) => props.find((p) => p.key === key)?.value;
2120
- const position = findLightProp("position");
2121
- const node = {
2122
- name: light.name || `light_${this.nodes.length}`,
2123
- translation: position ? [position[0], position[1], position[2]] : [0, 0, 0],
2124
- extras: {
2125
- type: "light",
2126
- lightType: light.lightType,
2127
- color: findLightProp("color") || "#ffffff",
2128
- intensity: findLightProp("intensity") ?? 1
2129
- }
2130
- };
2131
- const nodeIndex = this.nodes.length;
2132
- this.nodes.push(node);
2133
- this.stats.nodeCount++;
2134
- return nodeIndex;
2135
- }
2136
- /**
2137
- * Process a camera node
2138
- */
2139
- processCamera(camera) {
2140
- const props = camera.properties || [];
2141
- const findCamProp = (key) => props.find((p) => p.key === key)?.value;
2142
- const position = findCamProp("position");
2143
- const node = {
2144
- name: "Camera",
2145
- translation: position ? [position[0], position[1], position[2]] : [0, 0, 0],
2146
- extras: {
2147
- type: "camera",
2148
- cameraType: camera.cameraType,
2149
- fov: findCamProp("fov") ?? 75,
2150
- near: findCamProp("near") ?? 0.1,
2151
- far: findCamProp("far") ?? 1e3
2152
- }
2153
- };
2154
- const nodeIndex = this.nodes.length;
2155
- this.nodes.push(node);
2156
- this.stats.nodeCount++;
2157
- return nodeIndex;
2158
- }
2159
- // =============================================================================
2160
- // UTILITY METHODS
2161
- // =============================================================================
2162
- /** Find a property by key in a HoloObjectDecl's property array. */
2163
- findProp(object, key) {
2164
- return object.properties?.find((p) => p.key === key)?.value;
2165
- }
2166
- /** Extract a vec3 from an object's properties array. */
2167
- extractVec3Prop(object, key, defaultValue) {
2168
- const val = this.findProp(object, key);
2169
- if (Array.isArray(val) && val.length >= 3) {
2170
- return [Number(val[0]) || 0, Number(val[1]) || 0, Number(val[2]) || 0];
2171
- }
2172
- return defaultValue;
2173
- }
2174
- parseColorString(color) {
2175
- if (color.startsWith("#")) {
2176
- const hex = color.slice(1);
2177
- if (hex.length >= 6) {
2178
- const r = parseInt(hex.slice(0, 2), 16) / 255;
2179
- const g = parseInt(hex.slice(2, 4), 16) / 255;
2180
- const b = parseInt(hex.slice(4, 6), 16) / 255;
2181
- return [r, g, b];
2182
- }
2183
- }
2184
- const colors = {
2185
- red: [1, 0, 0],
2186
- green: [0, 1, 0],
2187
- blue: [0, 0, 1],
2188
- white: [1, 1, 1],
2189
- black: [0, 0, 0],
2190
- yellow: [1, 1, 0],
2191
- cyan: [0, 1, 1],
2192
- magenta: [1, 0, 1],
2193
- orange: [1, 0.5, 0],
2194
- purple: [0.5, 0, 0.5]
2195
- };
2196
- return colors[color.toLowerCase()] || [1, 1, 1];
2197
- }
2198
- eulerToQuaternion(euler) {
2199
- const toRad = Math.PI / 180;
2200
- const x = euler[0] * toRad / 2;
2201
- const y = euler[1] * toRad / 2;
2202
- const z = euler[2] * toRad / 2;
2203
- const cx = Math.cos(x);
2204
- const sx = Math.sin(x);
2205
- const cy = Math.cos(y);
2206
- const sy = Math.sin(y);
2207
- const cz = Math.cos(z);
2208
- const sz = Math.sin(z);
2209
- return [
2210
- sx * cy * cz - cx * sy * sz,
2211
- cx * sy * cz + sx * cy * sz,
2212
- cx * cy * sz - sx * sy * cz,
2213
- cx * cy * cz + sx * sy * sz
2214
- ];
2215
- }
2216
- mapPropertyToGLTFPath(property) {
2217
- const mapping = {
2218
- position: "translation",
2219
- translation: "translation",
2220
- rotation: "rotation",
2221
- scale: "scale"
2222
- };
2223
- return mapping[property.toLowerCase()] || null;
2224
- }
2225
- };
2226
- function createGLTFPipeline(options) {
2227
- return new GLTFPipeline(options);
2228
- }
2229
-
2230
- export { GLTFPipeline, createGLTFPipeline, generateScaleNormalMap, generateScaleTexture };
2231
- //# sourceMappingURL=chunk-3FJCK5XA.js.map
2232
- //# sourceMappingURL=chunk-3FJCK5XA.js.map