@holoscript/core 6.0.3 → 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 (244) hide show
  1. package/README.md +40 -40
  2. package/dist/codebase/index.d.ts +25 -0
  3. package/dist/compiler/index.d.ts +82 -0
  4. package/dist/constants.d.ts +4 -0
  5. package/dist/coordinators/index.d.ts +189 -0
  6. package/dist/entries/interop.d.ts +9 -0
  7. package/dist/entries/scripting.d.ts +7 -0
  8. package/dist/index.d.ts +520 -19
  9. package/dist/paper-0c-spike/index.d.ts +29 -0
  10. package/dist/reconstruction/index.d.ts +78 -0
  11. package/dist/self-improvement/index.d.ts +0 -48
  12. package/dist/storage/index.d.ts +24 -0
  13. package/dist/tools/index.d.ts +33 -0
  14. package/dist/traitDocs/traitDocs.d.ts +69 -0
  15. package/dist/traits/index.d.ts +61 -0
  16. package/package.json +102 -19
  17. package/dist/GLTFPipeline-3KLWWUQO.cjs +0 -34
  18. package/dist/GLTFPipeline-LYII2ZVQ.js +0 -13
  19. package/dist/HoloScriptPlusParser-BZR5DELQ.js +0 -692
  20. package/dist/HoloScriptPlusParser-SBMYDNXO.cjs +0 -697
  21. package/dist/USDZExporter-MHEHXZB4.cjs +0 -709
  22. package/dist/USDZExporter-YA55ZS35.js +0 -707
  23. package/dist/chunk-26TLYBFD.js +0 -309
  24. package/dist/chunk-27LWW5FU.cjs +0 -410
  25. package/dist/chunk-2UX5LRAP.cjs +0 -833
  26. package/dist/chunk-32TWR3HE.js +0 -70
  27. package/dist/chunk-3FXV5S25.cjs +0 -633
  28. package/dist/chunk-3FYZOR7S.cjs +0 -69
  29. package/dist/chunk-3KEU5QYY.cjs +0 -175
  30. package/dist/chunk-4FCZDTD5.js +0 -610
  31. package/dist/chunk-4KJ2R7VP.cjs +0 -507
  32. package/dist/chunk-4SMUJFHL.js +0 -1213
  33. package/dist/chunk-54FHG2MX.js +0 -655
  34. package/dist/chunk-56IF4NTA.js +0 -56
  35. package/dist/chunk-5GLCTO44.js +0 -161
  36. package/dist/chunk-5TOFIR6L.js +0 -407
  37. package/dist/chunk-65RFOWZI.js +0 -765
  38. package/dist/chunk-6OQBLABR.js +0 -5240
  39. package/dist/chunk-6WNCRE6F.js +0 -126
  40. package/dist/chunk-74YCHHTE.js +0 -1872
  41. package/dist/chunk-7H5UNJZD.cjs +0 -1973
  42. package/dist/chunk-7KPI4EKH.cjs +0 -701
  43. package/dist/chunk-7LCYLPW2.cjs +0 -663
  44. package/dist/chunk-7N3JIJMT.js +0 -1395
  45. package/dist/chunk-7X2IEJIE.cjs +0 -2526
  46. package/dist/chunk-A6GO3DPZ.cjs +0 -1002
  47. package/dist/chunk-AC4BSHFV.js +0 -765
  48. package/dist/chunk-AIUXRS74.cjs +0 -3681
  49. package/dist/chunk-ARNKA274.cjs +0 -130
  50. package/dist/chunk-AW7WAELW.js +0 -975
  51. package/dist/chunk-BKIGPPVM.js +0 -629
  52. package/dist/chunk-BO2OKHIY.js +0 -6055
  53. package/dist/chunk-BU5ZAFMC.js +0 -115
  54. package/dist/chunk-BY3B7ZYV.cjs +0 -457
  55. package/dist/chunk-C2QHVHZF.js +0 -4066
  56. package/dist/chunk-C7BNX4XJ.js +0 -1043
  57. package/dist/chunk-CLYEIWA5.cjs +0 -408
  58. package/dist/chunk-CMYAWUX3.js +0 -4455
  59. package/dist/chunk-CN4NOESF.cjs +0 -416
  60. package/dist/chunk-CNVM7J3M.js +0 -883
  61. package/dist/chunk-CO2VM2DK.js +0 -1965
  62. package/dist/chunk-CQDOF3G7.cjs +0 -1404
  63. package/dist/chunk-DH5G2JUA.cjs +0 -614
  64. package/dist/chunk-DIEDKX5B.cjs +0 -660
  65. package/dist/chunk-DN7UFU63.cjs +0 -172
  66. package/dist/chunk-DXVCEFZB.js +0 -2027
  67. package/dist/chunk-EGIZHYJP.cjs +0 -85
  68. package/dist/chunk-EJA7G2C4.cjs +0 -428
  69. package/dist/chunk-ELLQPFAF.js +0 -830
  70. package/dist/chunk-EMO7HAKJ.cjs +0 -1875
  71. package/dist/chunk-ENV7K6XA.js +0 -282
  72. package/dist/chunk-EPWRXL6S.js +0 -2522
  73. package/dist/chunk-EUFLX2PI.js +0 -56956
  74. package/dist/chunk-F5LVGHNT.js +0 -449
  75. package/dist/chunk-FEFHPUEM.cjs +0 -6122
  76. package/dist/chunk-FF3Q7ADR.js +0 -380
  77. package/dist/chunk-GC3YU46J.js +0 -762
  78. package/dist/chunk-H6WMMLQK.cjs +0 -4492
  79. package/dist/chunk-H7XMORZI.js +0 -2731
  80. package/dist/chunk-HAN4V3PF.cjs +0 -2037
  81. package/dist/chunk-HBZYCASG.js +0 -4208
  82. package/dist/chunk-HF4OFY25.cjs +0 -4097
  83. package/dist/chunk-HHS6FMOU.cjs +0 -3813
  84. package/dist/chunk-HJJLD366.js +0 -1169
  85. package/dist/chunk-HKCVM6OK.cjs +0 -2237
  86. package/dist/chunk-HUFNKFA6.js +0 -698
  87. package/dist/chunk-I3XGTIHM.cjs +0 -1505
  88. package/dist/chunk-I6XZ5DZF.cjs +0 -488
  89. package/dist/chunk-IHOUYQIA.js +0 -406
  90. package/dist/chunk-ILRBGFVD.cjs +0 -72
  91. package/dist/chunk-KDB6BUMB.js +0 -3666
  92. package/dist/chunk-KUJRR4FJ.js +0 -657
  93. package/dist/chunk-KYM4XRFG.js +0 -421
  94. package/dist/chunk-L3Z2HIWJ.cjs +0 -793
  95. package/dist/chunk-LBPEZQAF.js +0 -2232
  96. package/dist/chunk-LER4WXW5.cjs +0 -286
  97. package/dist/chunk-LK5IAVH6.cjs +0 -1173
  98. package/dist/chunk-M6FU6S22.js +0 -2911
  99. package/dist/chunk-MWEFR6YQ.js +0 -1857
  100. package/dist/chunk-N5FTS757.cjs +0 -415
  101. package/dist/chunk-NCUHGRTZ.js +0 -752
  102. package/dist/chunk-NKRKT6V2.js +0 -69
  103. package/dist/chunk-NLPSZT4C.js +0 -630
  104. package/dist/chunk-NRUB55IT.cjs +0 -771
  105. package/dist/chunk-NTMZSDXM.cjs +0 -633
  106. package/dist/chunk-ODGMVILH.js +0 -9662
  107. package/dist/chunk-PBA6NXCT.cjs +0 -1216
  108. package/dist/chunk-PHENW4MQ.js +0 -65
  109. package/dist/chunk-PJMOXFPR.cjs +0 -1861
  110. package/dist/chunk-PLMYCCA4.js +0 -504
  111. package/dist/chunk-PPULB4GG.cjs +0 -9728
  112. package/dist/chunk-PQLGZKMC.cjs +0 -755
  113. package/dist/chunk-Q56TT7IL.js +0 -483
  114. package/dist/chunk-QHVVVN47.cjs +0 -2033
  115. package/dist/chunk-QWKUKVRE.js +0 -2026
  116. package/dist/chunk-RE3OKSYF.cjs +0 -424
  117. package/dist/chunk-RT7LJRSF.cjs +0 -57378
  118. package/dist/chunk-SPDELRRV.cjs +0 -319
  119. package/dist/chunk-SUCBB66F.js +0 -229
  120. package/dist/chunk-TSWTWZ42.cjs +0 -4214
  121. package/dist/chunk-UITWA6DV.cjs +0 -2829
  122. package/dist/chunk-UQW6SLM5.js +0 -172
  123. package/dist/chunk-V2ILLPHK.cjs +0 -237
  124. package/dist/chunk-V42NTCFH.js +0 -3645
  125. package/dist/chunk-VJ3L7CHT.cjs +0 -169
  126. package/dist/chunk-VJVCD5T5.cjs +0 -769
  127. package/dist/chunk-VK5AXKO3.js +0 -746
  128. package/dist/chunk-VSXOIUCF.cjs +0 -473
  129. package/dist/chunk-VZNKJZTT.cjs +0 -749
  130. package/dist/chunk-W76ETJTI.js +0 -678
  131. package/dist/chunk-WFJIDI2N.cjs +0 -424
  132. package/dist/chunk-WN3YF33G.cjs +0 -118
  133. package/dist/chunk-WUXIRGZP.cjs +0 -2915
  134. package/dist/chunk-WYH4GVZ5.js +0 -1488
  135. package/dist/chunk-X4YVN7H3.cjs +0 -888
  136. package/dist/chunk-X67XRI2T.js +0 -410
  137. package/dist/chunk-XDXZM3ZP.cjs +0 -1046
  138. package/dist/chunk-XH7SE4HH.cjs +0 -72
  139. package/dist/chunk-XNMEH2BI.js +0 -126
  140. package/dist/chunk-XSF76QRU.js +0 -468
  141. package/dist/chunk-XSUZMPVQ.cjs +0 -682
  142. package/dist/chunk-XUILI4KB.cjs +0 -430
  143. package/dist/chunk-Y5MUAYTO.js +0 -167
  144. package/dist/chunk-Y7VK5TH3.cjs +0 -129
  145. package/dist/chunk-YCMQQQ5U.cjs +0 -5244
  146. package/dist/chunk-YYXH45E2.js +0 -426
  147. package/dist/chunk-ZGTGVSTZ.js +0 -422
  148. package/dist/chunk-ZL6VJ6SN.js +0 -421
  149. package/dist/cli/holoscript-runner.cjs +0 -2258
  150. package/dist/cli/holoscript-runner.js +0 -2233
  151. package/dist/codebase/index.cjs +0 -336
  152. package/dist/codebase/index.js +0 -331
  153. package/dist/compiler/agent-inference.cjs +0 -29
  154. package/dist/compiler/agent-inference.js +0 -6
  155. package/dist/compiler/android-xr.cjs +0 -20
  156. package/dist/compiler/android-xr.js +0 -10
  157. package/dist/compiler/android.cjs +0 -21
  158. package/dist/compiler/android.js +0 -11
  159. package/dist/compiler/ar.cjs +0 -21
  160. package/dist/compiler/ar.js +0 -9
  161. package/dist/compiler/babylon.cjs +0 -18
  162. package/dist/compiler/babylon.js +0 -12
  163. package/dist/compiler/coco.cjs +0 -27
  164. package/dist/compiler/coco.js +0 -4
  165. package/dist/compiler/domain-block-utils.cjs +0 -595
  166. package/dist/compiler/domain-block-utils.js +0 -9
  167. package/dist/compiler/dtdl.cjs +0 -25
  168. package/dist/compiler/dtdl.js +0 -9
  169. package/dist/compiler/gltf-pipeline.cjs +0 -39
  170. package/dist/compiler/gltf-pipeline.js +0 -4
  171. package/dist/compiler/godot.cjs +0 -16
  172. package/dist/compiler/godot.js +0 -10
  173. package/dist/compiler/incremental.cjs +0 -30
  174. package/dist/compiler/incremental.js +0 -5
  175. package/dist/compiler/index.cjs +0 -1522
  176. package/dist/compiler/index.js +0 -1321
  177. package/dist/compiler/ios.cjs +0 -21
  178. package/dist/compiler/ios.js +0 -11
  179. package/dist/compiler/multi-layer.cjs +0 -27
  180. package/dist/compiler/multi-layer.js +0 -12
  181. package/dist/compiler/nodetoy.cjs +0 -23
  182. package/dist/compiler/nodetoy.js +0 -4
  183. package/dist/compiler/openxr.cjs +0 -16
  184. package/dist/compiler/openxr.js +0 -10
  185. package/dist/compiler/playcanvas.cjs +0 -17
  186. package/dist/compiler/playcanvas.js +0 -11
  187. package/dist/compiler/r3f.cjs +0 -35
  188. package/dist/compiler/r3f.js +0 -13
  189. package/dist/compiler/remotion.cjs +0 -23
  190. package/dist/compiler/remotion.js +0 -4
  191. package/dist/compiler/reproducibility.cjs +0 -39
  192. package/dist/compiler/reproducibility.js +0 -4
  193. package/dist/compiler/sdf.cjs +0 -22
  194. package/dist/compiler/sdf.js +0 -10
  195. package/dist/compiler/semantic-scene.cjs +0 -31
  196. package/dist/compiler/semantic-scene.js +0 -4
  197. package/dist/compiler/state.cjs +0 -18
  198. package/dist/compiler/state.js +0 -8
  199. package/dist/compiler/trait-composition.cjs +0 -27
  200. package/dist/compiler/trait-composition.js +0 -6
  201. package/dist/compiler/unity.cjs +0 -16
  202. package/dist/compiler/unity.js +0 -10
  203. package/dist/compiler/unreal.cjs +0 -20
  204. package/dist/compiler/unreal.js +0 -10
  205. package/dist/compiler/urdf.cjs +0 -46
  206. package/dist/compiler/urdf.js +0 -10
  207. package/dist/compiler/usd-physics.cjs +0 -24
  208. package/dist/compiler/usd-physics.js +0 -7
  209. package/dist/compiler/visionos.cjs +0 -16
  210. package/dist/compiler/visionos.js +0 -10
  211. package/dist/compiler/vrchat.cjs +0 -20
  212. package/dist/compiler/vrchat.js +0 -10
  213. package/dist/compiler/vrr.cjs +0 -22
  214. package/dist/compiler/vrr.js +0 -10
  215. package/dist/compiler/wasm.cjs +0 -34
  216. package/dist/compiler/wasm.js +0 -10
  217. package/dist/compiler/webgpu.cjs +0 -16
  218. package/dist/compiler/webgpu.js +0 -10
  219. package/dist/debugger.cjs +0 -21
  220. package/dist/debugger.js +0 -8
  221. package/dist/entries/interop.cjs +0 -62
  222. package/dist/entries/interop.js +0 -5
  223. package/dist/entries/scripting.cjs +0 -67
  224. package/dist/entries/scripting.js +0 -10
  225. package/dist/index.cjs +0 -61703
  226. package/dist/index.js +0 -58652
  227. package/dist/math/vec3.cjs +0 -57
  228. package/dist/math/vec3.js +0 -4
  229. package/dist/parser.cjs +0 -26
  230. package/dist/parser.js +0 -9
  231. package/dist/playwright-7DTEQCBD.cjs +0 -98673
  232. package/dist/playwright-BIZXMLD2.js +0 -98660
  233. package/dist/post-quantum-JTTAAGO3.cjs +0 -6
  234. package/dist/post-quantum-RVPVDEPI.js +0 -4
  235. package/dist/runtime.cjs +0 -16
  236. package/dist/runtime.js +0 -7
  237. package/dist/storage/index.cjs +0 -37
  238. package/dist/storage/index.js +0 -4
  239. package/dist/traits/index.cjs +0 -14331
  240. package/dist/traits/index.js +0 -12126
  241. package/dist/type-checker.cjs +0 -18
  242. package/dist/type-checker.js +0 -5
  243. package/dist/wot/index.cjs +0 -29
  244. package/dist/wot/index.js +0 -4
@@ -1,2232 +0,0 @@
1
- import { generateHullGeometry, generateMembraneGeometry, generateSplineGeometry } from './chunk-HJJLD366.js';
2
- import { init_R3FCompiler, init_ObjectPool, ASTNodePool, MATERIAL_PRESETS } from './chunk-HBZYCASG.js';
3
- import { init_TraitCompositor, TraitCompositor } from './chunk-XSF76QRU.js';
4
- import { init_DomainBlockCompilerMixin, compileDomainBlocks, compileMaterialBlock, materialToGLTF } from './chunk-KDB6BUMB.js';
5
- import { init_CompilerBase, init_ANSNamespace, CompilerBase, ANSCapabilityPath } from './chunk-QWKUKVRE.js';
6
-
7
- // src/compiler/GLTFPipeline.ts
8
- init_TraitCompositor();
9
- init_R3FCompiler();
10
- init_CompilerBase();
11
- init_ANSNamespace();
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-LBPEZQAF.js.map
2232
- //# sourceMappingURL=chunk-LBPEZQAF.js.map