@holoscript/core 8.0.13 → 8.0.15

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 (191) hide show
  1. package/README.md +20 -13
  2. package/bin/holo-decision.mjs +133 -0
  3. package/bin/holoscript.cjs +0 -0
  4. package/dist/{GLTFPipeline-L76YTXKA.js → GLTFPipeline-FRBZIATS.js} +7 -7
  5. package/dist/{GLTFPipeline-ZWJN7GI6.cjs → GLTFPipeline-MTPP444P.cjs} +11 -11
  6. package/dist/{HoloCompositionParser-P4Y3CXKQ.js → HoloCompositionParser-2GVQAZ2X.js} +7 -7
  7. package/dist/HoloCompositionParser-3NDF6BMX.cjs +45 -0
  8. package/dist/HoloScriptPlusParser-URSIBEAW.cjs +30 -0
  9. package/dist/HoloScriptPlusParser-YCV6L7GI.js +13 -0
  10. package/dist/{HoloScriptRuntime-YBYXJHJN.cjs → HoloScriptRuntime-CRWI6C3P.cjs} +5 -5
  11. package/dist/{HoloScriptRuntime-UTRLJI5L.js → HoloScriptRuntime-IAZGHPFP.js} +4 -4
  12. package/dist/{SceneIRCompiler-63KA3M2F.js → SceneIRCompiler-BJQMKLWL.js} +7 -7
  13. package/dist/{SceneIRCompiler-KWUFHZ4O.cjs → SceneIRCompiler-RTPPDQAV.cjs} +12 -12
  14. package/dist/{chunk-FWCHPCD4.js → chunk-22AULOAL.js} +5 -5
  15. package/dist/{chunk-G35CAQ7I.cjs → chunk-2DJIS7TH.cjs} +5 -5
  16. package/dist/{chunk-LHUDAJQT.cjs → chunk-2PVEXEOL.cjs} +5 -5
  17. package/dist/{chunk-WWNCM473.js → chunk-2UP2J3OK.js} +4 -4
  18. package/dist/{chunk-HVX7QQYK.js → chunk-3AUYBQ5I.js} +3 -3
  19. package/dist/{chunk-TSTFYGGX.js → chunk-3MQAOMKD.js} +3 -3
  20. package/dist/{chunk-FWKEY4LD.js → chunk-4AGNARX6.js} +3 -3
  21. package/dist/{chunk-WNYXT4TE.cjs → chunk-4CQ7ULXZ.cjs} +3 -3
  22. package/dist/{chunk-U3D4SYQ5.js → chunk-5LCJDX5A.js} +3 -3
  23. package/dist/{chunk-IK6Z2HNC.js → chunk-6OQHLNTT.js} +2 -2
  24. package/dist/{chunk-EI3J6LTW.cjs → chunk-6RDHWSZM.cjs} +217 -217
  25. package/dist/{chunk-77QI552M.js → chunk-7AY66PZP.js} +3 -3
  26. package/dist/{chunk-7IDJDMPU.cjs → chunk-7FYSSI3C.cjs} +27 -2
  27. package/dist/{chunk-BP4KTMLY.js → chunk-7HDRN5BW.js} +4 -4
  28. package/dist/{chunk-SQAAWO4V.cjs → chunk-7YKNV2BE.cjs} +5 -5
  29. package/dist/{chunk-WTH23ISI.js → chunk-AAUQTR7I.js} +4 -4
  30. package/dist/{chunk-OC6GV5QH.cjs → chunk-AN3ZH7M7.cjs} +38 -38
  31. package/dist/{chunk-YW3I2FQL.js → chunk-ANMME7XT.js} +4 -2
  32. package/dist/{chunk-A4OOLBZD.js → chunk-ATRAK4RC.js} +5 -5
  33. package/dist/{chunk-BQJ3YBF3.js → chunk-AUF7HFOO.js} +724 -647
  34. package/dist/{chunk-2X3IPCBK.cjs → chunk-AUMTQV64.cjs} +7 -7
  35. package/dist/chunk-AUU4ACJP.cjs +1605 -0
  36. package/dist/{chunk-QQFB4RT2.cjs → chunk-BTVHY3UB.cjs} +2 -2
  37. package/dist/{chunk-ZK7KX5CD.cjs → chunk-CMEINO5I.cjs} +190 -74
  38. package/dist/{chunk-BCP4E7MD.cjs → chunk-CRUIGXF7.cjs} +26 -10
  39. package/dist/{chunk-2E6PP5UE.cjs → chunk-E2SNPMBO.cjs} +11 -11
  40. package/dist/{chunk-2BYAUHBO.js → chunk-E4YS5SIZ.js} +90 -25
  41. package/dist/{chunk-KFVNCW4Q.js → chunk-EKNWUSGW.js} +3 -3
  42. package/dist/{chunk-JEJ7TT54.js → chunk-ETIJOUIO.js} +4 -4
  43. package/dist/{chunk-FRSKLX6I.js → chunk-F33KLYOS.js} +12 -9
  44. package/dist/{chunk-GGR7QUKK.cjs → chunk-F47FEV4X.cjs} +40 -40
  45. package/dist/{chunk-J3UHV5T4.cjs → chunk-FCGTI7FO.cjs} +4 -4
  46. package/dist/{chunk-RRQIAJLQ.cjs → chunk-FGTXAVKG.cjs} +5 -5
  47. package/dist/{chunk-2KKOYZYI.cjs → chunk-FPGYWNNA.cjs} +5 -5
  48. package/dist/{chunk-65S62L4D.cjs → chunk-FQ4MYZYU.cjs} +20 -20
  49. package/dist/{chunk-UGQ3QNV6.js → chunk-FT4FGTR6.js} +24 -8
  50. package/dist/{chunk-Q4K2GEMW.cjs → chunk-FVNV4WHQ.cjs} +20 -17
  51. package/dist/{chunk-SJIRCTIS.cjs → chunk-HSGJFCVJ.cjs} +2 -2
  52. package/dist/{chunk-B3CWTCK2.js → chunk-IA7FCSRY.js} +27 -2
  53. package/dist/{chunk-OIBRWLPY.cjs → chunk-ILO2KMER.cjs} +55 -4
  54. package/dist/{chunk-ZRPALBQB.cjs → chunk-IPBSNGAN.cjs} +92 -27
  55. package/dist/{chunk-FHVPN7BN.js → chunk-IX2U3RQT.js} +3 -3
  56. package/dist/{chunk-QEDMOGF3.cjs → chunk-IYK6LMQ7.cjs} +7 -7
  57. package/dist/{chunk-G6EKZHXY.js → chunk-J3VVM6GV.js} +4 -4
  58. package/dist/{chunk-LMB555QB.cjs → chunk-J74S4ZMF.cjs} +18 -18
  59. package/dist/{chunk-36NPLOI4.cjs → chunk-JERWCCEJ.cjs} +4 -2
  60. package/dist/{chunk-RVNXBF2M.js → chunk-JTDDLCSP.js} +4 -4
  61. package/dist/{chunk-VTEXJOSD.cjs → chunk-K53L47NH.cjs} +30 -30
  62. package/dist/{chunk-IJU6AS67.cjs → chunk-LJTM5ONX.cjs} +23 -23
  63. package/dist/{chunk-G6KACNMO.js → chunk-LJVVOVA6.js} +4 -4
  64. package/dist/{chunk-PDXX5UI4.cjs → chunk-LNJKBQ2B.cjs} +38 -38
  65. package/dist/{chunk-EM2OGPYE.js → chunk-M2GDE3NV.js} +3 -3
  66. package/dist/{chunk-CP7V3ATL.js → chunk-MSZNGI5P.js} +52 -5
  67. package/dist/{chunk-P7JEKJTZ.cjs → chunk-N2BOTBPG.cjs} +27 -16
  68. package/dist/{chunk-LB3ZZMKT.cjs → chunk-NFALJJCH.cjs} +12 -12
  69. package/dist/{chunk-XF63CZ7E.cjs → chunk-NRZRBK25.cjs} +6 -6
  70. package/dist/{chunk-GZPUA2TL.cjs → chunk-NTSLNIY7.cjs} +10 -10
  71. package/dist/{chunk-Y57AVOM2.cjs → chunk-NZJOAWJM.cjs} +4 -4
  72. package/dist/{chunk-RXQNRZ5F.js → chunk-OG7RKWWX.js} +3 -3
  73. package/dist/{chunk-VVAAZITA.cjs → chunk-OQVHCYY4.cjs} +5 -5
  74. package/dist/{chunk-PJFTTSKE.js → chunk-PFZ5WLIP.js} +4 -4
  75. package/dist/{chunk-PXVAPGTR.cjs → chunk-PHLNABJD.cjs} +3788 -1138
  76. package/dist/{chunk-SQXAZHDG.js → chunk-PLK3DLTF.js} +3 -3
  77. package/dist/{chunk-QBY7LRN2.js → chunk-PMACVMHL.js} +3 -3
  78. package/dist/{chunk-DJ5BNRIS.js → chunk-PV2OXNP5.js} +3684 -1041
  79. package/dist/{chunk-F3YT6GUF.cjs → chunk-Q6VW7MGX.cjs} +14 -14
  80. package/dist/{chunk-DUG3OQAS.js → chunk-QC3CAVFN.js} +187 -70
  81. package/dist/{chunk-NQN5LFN4.cjs → chunk-QKLEDRHN.cjs} +19 -19
  82. package/dist/{chunk-2EGGAZT3.js → chunk-QS27OYBU.js} +3 -3
  83. package/dist/{chunk-4376VHNJ.js → chunk-RUB7WL6C.js} +3 -3
  84. package/dist/{chunk-5LVFTGKH.js → chunk-TW2AH2ZS.js} +3 -3
  85. package/dist/{chunk-AATYYLYK.js → chunk-TZM6DKSJ.js} +18 -7
  86. package/dist/{chunk-ESSEF6U2.js → chunk-U227RWXB.js} +3 -3
  87. package/dist/{chunk-TC7JEO6R.cjs → chunk-U6CQOM5Q.cjs} +711 -52
  88. package/dist/{chunk-Z557DVYN.js → chunk-ULT2QAV5.js} +4 -4
  89. package/dist/{chunk-S4O7CFHQ.cjs → chunk-VSO6HPDZ.cjs} +7 -7
  90. package/dist/{chunk-ONPVM4IV.js → chunk-WQJMSSJT.js} +698 -45
  91. package/dist/{chunk-GILYQ5VH.cjs → chunk-WXSDBWGM.cjs} +4 -4
  92. package/dist/{chunk-UTUD6GJI.js → chunk-X5PMMK7H.js} +3 -3
  93. package/dist/{chunk-IFW5IW4L.cjs → chunk-X5ZLSPU2.cjs} +742 -657
  94. package/dist/{chunk-QM7FJB5W.js → chunk-X727CQ7X.js} +5 -5
  95. package/dist/{chunk-566ZB4LZ.cjs → chunk-XHZYS4QO.cjs} +5 -5
  96. package/dist/{chunk-RZ2EPXO3.cjs → chunk-XRQCTTDC.cjs} +8 -8
  97. package/dist/{chunk-5FYMCVBY.js → chunk-YBIHSN55.js} +4 -4
  98. package/dist/{chunk-FG6PYGIZ.js → chunk-Z574GVI3.js} +4 -4
  99. package/dist/{chunk-ZVQMKUGJ.js → chunk-Z5V3KA7L.js} +2 -2
  100. package/dist/chunk-ZGDEOXGU.js +1571 -0
  101. package/dist/{chunk-IS7NTKY5.cjs → chunk-ZHW6TDZ6.cjs} +14 -14
  102. package/dist/{chunk-CKV5NYAE.js → chunk-ZPHCTPR6.js} +4 -4
  103. package/dist/{chunk-3IUHTGDD.cjs → chunk-ZXXC2GXB.cjs} +38 -38
  104. package/dist/cli/holoscript-runner.cjs +169 -20
  105. package/dist/cli/holoscript-runner.js +163 -14
  106. package/dist/compiler/agent-inference.cjs +8 -8
  107. package/dist/compiler/agent-inference.js +4 -4
  108. package/dist/compiler/android-xr.cjs +7 -7
  109. package/dist/compiler/android-xr.js +5 -5
  110. package/dist/compiler/android.cjs +6 -6
  111. package/dist/compiler/android.js +4 -4
  112. package/dist/compiler/context.cjs +6 -6
  113. package/dist/compiler/context.js +3 -3
  114. package/dist/compiler/daimon-seed.cjs +9 -9
  115. package/dist/compiler/daimon-seed.js +4 -4
  116. package/dist/compiler/domain-block-utils.cjs +165 -165
  117. package/dist/compiler/domain-block-utils.js +4 -4
  118. package/dist/compiler/dtdl.cjs +7 -7
  119. package/dist/compiler/dtdl.js +4 -4
  120. package/dist/compiler/gltf-pipeline.cjs +8 -8
  121. package/dist/compiler/gltf-pipeline.js +1 -1
  122. package/dist/compiler/godot.cjs +6 -6
  123. package/dist/compiler/godot.js +5 -5
  124. package/dist/compiler/incremental.cjs +8 -8
  125. package/dist/compiler/incremental.js +3 -3
  126. package/dist/compiler/index.cjs +2275 -212
  127. package/dist/compiler/index.d.ts +29 -1
  128. package/dist/compiler/index.js +2078 -66
  129. package/dist/compiler/ios.cjs +6 -6
  130. package/dist/compiler/ios.js +4 -4
  131. package/dist/compiler/llm-provider-capabilities.cjs +6 -6
  132. package/dist/compiler/llm-provider-capabilities.js +3 -3
  133. package/dist/compiler/openxr.cjs +6 -6
  134. package/dist/compiler/openxr.js +5 -5
  135. package/dist/compiler/sdf.cjs +7 -7
  136. package/dist/compiler/sdf.js +5 -5
  137. package/dist/compiler/state.cjs +6 -6
  138. package/dist/compiler/state.js +4 -4
  139. package/dist/compiler/trait-composition.cjs +7 -7
  140. package/dist/compiler/trait-composition.js +3 -3
  141. package/dist/compiler/unity.cjs +6 -6
  142. package/dist/compiler/unity.js +5 -5
  143. package/dist/compiler/unreal.cjs +7 -7
  144. package/dist/compiler/unreal.js +5 -5
  145. package/dist/compiler/urdf.cjs +14 -14
  146. package/dist/compiler/urdf.js +5 -5
  147. package/dist/compiler/usd-physics.cjs +7 -7
  148. package/dist/compiler/usd-physics.js +4 -4
  149. package/dist/compiler/visionos.cjs +6 -6
  150. package/dist/compiler/visionos.js +5 -5
  151. package/dist/compiler/vrchat.cjs +7 -7
  152. package/dist/compiler/vrchat.js +5 -5
  153. package/dist/compiler/wasm.cjs +10 -10
  154. package/dist/compiler/wasm.js +5 -5
  155. package/dist/compiler/webgpu.cjs +6 -6
  156. package/dist/compiler/webgpu.js +5 -5
  157. package/dist/constants.cjs +99 -99
  158. package/dist/constants.js +3 -3
  159. package/dist/debugger.cjs +5 -5
  160. package/dist/debugger.js +3 -3
  161. package/dist/{dist-GRMOP2UL.cjs → dist-YGATNURH.cjs} +705 -9
  162. package/dist/{dist-ZBUKU2JG.js → dist-ZEZXRCBG.js} +701 -10
  163. package/dist/entries/scripting.cjs +8 -8
  164. package/dist/entries/scripting.js +4 -4
  165. package/dist/evolution/index.cjs +20 -20
  166. package/dist/evolution/index.js +8 -8
  167. package/dist/index.cjs +1099 -826
  168. package/dist/index.d.ts +78 -5
  169. package/dist/index.js +265 -120
  170. package/dist/parser/HoloCompositionTypes.d.ts +1 -1
  171. package/dist/parser.cjs +21 -21
  172. package/dist/parser.js +7 -7
  173. package/dist/reconstruction/index.cjs +127 -38
  174. package/dist/reconstruction/index.d.ts +159 -0
  175. package/dist/reconstruction/index.js +11 -2
  176. package/dist/runtime.cjs +9 -9
  177. package/dist/runtime.d.ts +1 -0
  178. package/dist/runtime.js +3 -3
  179. package/dist/traitDocs/traitDocs.cjs +8 -8
  180. package/dist/traitDocs/traitDocs.js +2 -2
  181. package/dist/traits/botanical-lotus.d.ts +61 -0
  182. package/dist/traits/index.cjs +761 -673
  183. package/dist/traits/index.d.ts +65 -0
  184. package/dist/traits/index.js +102 -27
  185. package/dist/types/HoloScriptPlus.d.ts +1 -1
  186. package/package.json +71 -65
  187. package/dist/HoloCompositionParser-6GW2BZID.cjs +0 -45
  188. package/dist/HoloScriptPlusParser-HQV6IJAH.js +0 -13
  189. package/dist/HoloScriptPlusParser-RFNHRMPE.cjs +0 -30
  190. package/dist/chunk-DVHZD5HV.cjs +0 -639
  191. package/dist/chunk-G72754QB.js +0 -625
@@ -1,10 +1,202 @@
1
1
  'use strict';
2
2
 
3
- var chunkQEDMOGF3_cjs = require('./chunk-QEDMOGF3.cjs');
4
- var chunkZK7KX5CD_cjs = require('./chunk-ZK7KX5CD.cjs');
3
+ var chunkIYK6LMQ7_cjs = require('./chunk-IYK6LMQ7.cjs');
4
+ var chunkCMEINO5I_cjs = require('./chunk-CMEINO5I.cjs');
5
5
  var chunkZHPMP447_cjs = require('./chunk-ZHPMP447.cjs');
6
6
  var ans = require('@holoscript/core-types/ans');
7
7
 
8
+ // src/compiler/render-modules/geometry-registry.ts
9
+ function resolveGeometry(meshType) {
10
+ const key = String(meshType ?? "").trim().toLowerCase();
11
+ const kind = ALIAS[key] ?? "box";
12
+ return PRIMITIVE[kind];
13
+ }
14
+ var PRIMITIVE, ALIAS;
15
+ var init_geometry_registry = chunkZHPMP447_cjs.__esm({
16
+ "src/compiler/render-modules/geometry-registry.ts"() {
17
+ PRIMITIVE = {
18
+ box: { kind: "box", webgpuVertexFn: "generateCubeVertices(1.0)" },
19
+ sphere: { kind: "sphere", webgpuVertexFn: "generateSphereVertices(0.5, 16, 16)" },
20
+ plane: { kind: "plane", webgpuVertexFn: "generatePlaneVertices(1.0, 1.0)" },
21
+ cylinder: { kind: "cylinder", webgpuVertexFn: "generateCylinderVertices(0.5, 1.0, 16)" },
22
+ cone: { kind: "cone", webgpuVertexFn: "generateConeVertices(0.5, 1.0, 16)" },
23
+ torus: { kind: "torus", webgpuVertexFn: "generateTorusVertices(0.5, 0.15, 16, 32)" }
24
+ };
25
+ ALIAS = {
26
+ // box family (panels, rounded boxes read as boxes)
27
+ cube: "box",
28
+ box: "box",
29
+ rounded_panel: "box",
30
+ rounded_box: "box",
31
+ panel: "box",
32
+ card: "box",
33
+ // sphere family
34
+ sphere: "sphere",
35
+ orb: "sphere",
36
+ ball: "sphere",
37
+ bubble: "sphere",
38
+ procedural_upper_body: "sphere",
39
+ avatar: "sphere",
40
+ // plane / surface family
41
+ plane: "plane",
42
+ quad: "plane",
43
+ surface: "plane",
44
+ full_screen_fluid_plane: "plane",
45
+ fluid_plane: "plane",
46
+ water: "plane",
47
+ // cylinder family
48
+ cylinder: "cylinder",
49
+ tube: "cylinder",
50
+ pillar: "cylinder",
51
+ capsule: "cylinder",
52
+ // cone family
53
+ cone: "cone",
54
+ pyramid: "cone",
55
+ spike: "cone",
56
+ crystal: "cone",
57
+ diamond: "cone",
58
+ gem: "cone",
59
+ // torus family
60
+ torus: "torus",
61
+ ring: "torus",
62
+ donut: "torus"
63
+ };
64
+ }
65
+ });
66
+
67
+ // src/compiler/render-modules/skybox-registry.ts
68
+ function hexToRgb(hex) {
69
+ const h = hex.replace("#", "");
70
+ return [
71
+ Number((parseInt(h.substring(0, 2), 16) / 255).toFixed(4)),
72
+ Number((parseInt(h.substring(2, 4), 16) / 255).toFixed(4)),
73
+ Number((parseInt(h.substring(4, 6), 16) / 255).toFixed(4))
74
+ ];
75
+ }
76
+ function resolveSkyboxColor(name) {
77
+ const key = String(name ?? "").trim().toLowerCase();
78
+ const hex = SKYBOX_HEX[key];
79
+ return hex ? hexToRgb(hex) : FALLBACK;
80
+ }
81
+ var SKYBOX_HEX, FALLBACK;
82
+ var init_skybox_registry = chunkZHPMP447_cjs.__esm({
83
+ "src/compiler/render-modules/skybox-registry.ts"() {
84
+ SKYBOX_HEX = {
85
+ // ── deep / space / void ─────────────────────────────────────────────
86
+ deep_machine_ocean: "#052430",
87
+ deep_space: "#05060d",
88
+ space: "#05060d",
89
+ nebula: "#0b0618",
90
+ night_sky: "#0b1020",
91
+ night_overcast: "#14161c",
92
+ void: "#050507",
93
+ midnight: "#070a12",
94
+ // ── water / underwater ──────────────────────────────────────────────
95
+ ocean: "#063240",
96
+ underwater: "#062a3a",
97
+ // ── studio / neutral (light backdrops kept light, just not pure white) ─
98
+ studio: "#c4c8cf",
99
+ neutral_studio: "#c4c8cf",
100
+ studio_soft: "#d2d5da",
101
+ hdr_studio: "#c9ccd2",
102
+ gallery: "#20222a",
103
+ indoor_museum: "#1c1e24",
104
+ interior_dark: "#14151a",
105
+ classroom: "#d2cfc6",
106
+ warehouse: "#3a3c40",
107
+ asset_pipeline_warehouse: "#3a3c40",
108
+ // ── sky / daylight ──────────────────────────────────────────────────
109
+ procedural_sky: "#8fbce6",
110
+ clear_sky: "#8fbce6",
111
+ daylight: "#9ec6ea",
112
+ fantasy_sky: "#2a3a6a",
113
+ sunset: "#46283a",
114
+ dusk: "#2e2740",
115
+ desert_dawn: "#caa079",
116
+ // ── tech / abstract ─────────────────────────────────────────────────
117
+ digital_grid: "#0a0f1e",
118
+ gradient: "#1a2030",
119
+ // ── environments / scenes ───────────────────────────────────────────
120
+ industrial: "#2a2d30",
121
+ urban_courtyard: "#3a3d42",
122
+ ancient_ruins: "#3a3428",
123
+ concert_venue: "#0e0a14",
124
+ lounge: "#1a1220",
125
+ farm: "#7fae5a",
126
+ vault_archive: "#1a140a",
127
+ passthrough: "#0c0c0e",
128
+ default: "#0a0e14"
129
+ };
130
+ FALLBACK = hexToRgb("#0a0e14");
131
+ }
132
+ });
133
+
134
+ // src/compiler/render-modules/geometry-purpose.ts
135
+ function coerceBool(v) {
136
+ if (v === true || v === "true") return true;
137
+ if (v === false || v === "false") return false;
138
+ return void 0;
139
+ }
140
+ function resolveGeometryRole(opts) {
141
+ let purpose = "render";
142
+ const p = String(opts.purpose ?? "").trim().toLowerCase();
143
+ if (p && ALL_PURPOSES.has(p)) {
144
+ purpose = p;
145
+ } else {
146
+ for (const t of opts.traitNames ?? []) {
147
+ const mapped = PURPOSE_TRAITS[String(t).trim().toLowerCase()];
148
+ if (mapped) {
149
+ purpose = mapped;
150
+ break;
151
+ }
152
+ }
153
+ }
154
+ const explicit = coerceBool(opts.visible);
155
+ const visible = explicit !== void 0 ? explicit : purpose === "render";
156
+ return { purpose, visible };
157
+ }
158
+ var INVISIBLE_PURPOSES, PURPOSE_TRAITS, ALL_PURPOSES;
159
+ var init_geometry_purpose = chunkZHPMP447_cjs.__esm({
160
+ "src/compiler/render-modules/geometry-purpose.ts"() {
161
+ INVISIBLE_PURPOSES = /* @__PURE__ */ new Set([
162
+ "collision",
163
+ "trigger",
164
+ "vfx_emitter",
165
+ "sim_domain",
166
+ "navmesh",
167
+ "occluder",
168
+ "audio_zone"
169
+ ]);
170
+ PURPOSE_TRAITS = {
171
+ collider: "collision",
172
+ collision: "collision",
173
+ physics_collider: "collision",
174
+ rigidbody_collider: "collision",
175
+ trigger: "trigger",
176
+ trigger_volume: "trigger",
177
+ interaction_volume: "trigger",
178
+ emitter: "vfx_emitter",
179
+ vfx_emitter: "vfx_emitter",
180
+ particle_emitter: "vfx_emitter",
181
+ spawn_volume: "vfx_emitter",
182
+ sim_domain: "sim_domain",
183
+ sim_volume: "sim_domain",
184
+ fluid_domain: "sim_domain",
185
+ field_region: "sim_domain",
186
+ navmesh: "navmesh",
187
+ nav_mesh: "navmesh",
188
+ occluder: "occluder",
189
+ occlusion: "occluder",
190
+ audio_zone: "audio_zone",
191
+ audio_volume: "audio_zone"
192
+ };
193
+ ALL_PURPOSES = /* @__PURE__ */ new Set([
194
+ "render",
195
+ ...Array.from(INVISIBLE_PURPOSES)
196
+ ]);
197
+ }
198
+ });
199
+
8
200
  // src/compiler/WebGPUCompiler.ts
9
201
  var WebGPUCompiler_exports = {};
10
202
  chunkZHPMP447_cjs.__export(WebGPUCompiler_exports, {
@@ -19,15 +211,35 @@ function findGpuParticleTrait(traits) {
19
211
  exports.WebGPUCompiler = void 0;
20
212
  var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
21
213
  "src/compiler/WebGPUCompiler.ts"() {
22
- chunkZK7KX5CD_cjs.init_CompilerBase();
23
- chunkQEDMOGF3_cjs.init_DomainBlockCompilerMixin();
24
- exports.WebGPUCompiler = class extends chunkZK7KX5CD_cjs.CompilerBase {
214
+ chunkCMEINO5I_cjs.init_CompilerBase();
215
+ init_geometry_registry();
216
+ init_skybox_registry();
217
+ init_geometry_purpose();
218
+ chunkIYK6LMQ7_cjs.init_DomainBlockCompilerMixin();
219
+ exports.WebGPUCompiler = class extends chunkCMEINO5I_cjs.CompilerBase {
25
220
  constructor(options = {}) {
26
221
  super();
27
222
  this.compilerName = "WebGPUCompiler";
28
223
  this.lines = [];
29
224
  this.indentLevel = 0;
30
225
  this.objectIndex = 0;
226
+ // Resolved once per compile() so findObjProp can fall back to template defaults.
227
+ this.templatesByName = /* @__PURE__ */ new Map();
228
+ // True when any object is a water/fluid surface — gates the animated water
229
+ // pipeline, its WGSL, the tessellated-plane helper, and the shared time uniform.
230
+ this.hasWater = false;
231
+ /** World facts collected at emission time, serialized as the HoloScene Manifest. */
232
+ this.manifestObjects = [];
233
+ /** Accumulated spatial-group translation for the objects currently being
234
+ * emitted — manifest positions are WORLD-space (render applies the same
235
+ * offsets via GroupXform; local coords would be a false fact for grouped
236
+ * scenes). */
237
+ this.manifestGroupOffset = [0, 0, 0];
238
+ // The named channels the water surface's clarity/ripple are bound to (from the
239
+ // .holo's water_clarity_binding / ripple_intensity_binding props). Host state
240
+ // (e.g. Brittney receipts) drives these BY NAME via hsSetChannel.
241
+ this.waterClarityChannel = "environmentWaterClarity";
242
+ this.waterRippleChannel = "environmentRippleIntensity";
31
243
  this.options = {
32
244
  entryPoint: options.entryPoint || "main",
33
245
  enableCompute: options.enableCompute ?? true,
@@ -44,6 +256,13 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
44
256
  this.lines = [];
45
257
  this.indentLevel = 0;
46
258
  this.objectIndex = 0;
259
+ this.manifestObjects = [];
260
+ this.manifestGroupOffset = [0, 0, 0];
261
+ this.templatesByName = new Map(
262
+ (composition.templates ?? []).map((t) => [t.name, t])
263
+ );
264
+ this.hasWater = this.compositionHasWater(composition);
265
+ if (this.hasWater) this.resolveWaterChannels(composition);
47
266
  this.emit("// Auto-generated by HoloScript WebGPUCompiler");
48
267
  this.emit(
49
268
  `// Source: composition "${this.escapeStringValue(composition.name, "TypeScript")}"`
@@ -57,7 +276,27 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
57
276
  this.emitGeometryHelpers();
58
277
  this.emitShaderSources();
59
278
  this.emitEnvironment(composition.environment ?? { type: "Environment", properties: [] });
60
- this.emitCamera(composition.camera);
279
+ this.emitCamera(composition);
280
+ this.emitSceneLights(composition);
281
+ if (this.hasWater) {
282
+ const cc = JSON.stringify(this.waterClarityChannel);
283
+ const rc = JSON.stringify(this.waterRippleChannel);
284
+ this.emit(`const hsChannels = { [${cc}]: 1.0, [${rc}]: 0.0 };`);
285
+ this.emit("let hsWaterAmp = 1.0;");
286
+ this.emit(
287
+ "function hsSetChannel(name, value) { if (name in hsChannels) hsChannels[name] = Math.max(0, Math.min(1, value)); return hsChannels[name]; }"
288
+ );
289
+ this.emit(
290
+ `function hsSetWater(o) { if (o) { if (o.clarity !== undefined) hsSetChannel(${cc}, o.clarity); if (o.ripple !== undefined) hsSetChannel(${rc}, o.ripple); if (o.amplitude !== undefined) hsWaterAmp = Math.max(0, o.amplitude); } return { clarity: hsChannels[${cc}], ripple: hsChannels[${rc}], amplitude: hsWaterAmp }; }`
291
+ );
292
+ this.emit(
293
+ 'if (typeof window !== "undefined") { window.hsSetChannel = hsSetChannel; window.hsSetWater = hsSetWater; }'
294
+ );
295
+ this.emit(
296
+ `const waterFrameU = createBuffer(device, new Float32Array([0,0,0,0, hsChannels[${cc}], hsChannels[${rc}], hsWaterAmp, 0]), GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST);`
297
+ );
298
+ this.emit("");
299
+ }
61
300
  this.emit("const holoGraphObjects = [];");
62
301
  this.emit("");
63
302
  if (composition.objects) {
@@ -66,9 +305,7 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
66
305
  if (composition.spatialGroups) {
67
306
  for (const group of composition.spatialGroups) this.emitGroup(group);
68
307
  }
69
- if (composition.lights) {
70
- for (const light of composition.lights) this.emitLight(light);
71
- }
308
+ this.emitSceneManifest(composition);
72
309
  if (this.options.enableCompute) this.emitComputeShaders(composition);
73
310
  this.emitWebGPUDomainBlocks(composition);
74
311
  this.emitViewportRuntime();
@@ -88,7 +325,7 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
88
325
  this.emitMaterialDomainBlock(block, prefix);
89
326
  break;
90
327
  case "dataviz":
91
- this.emitMultiline(chunkQEDMOGF3_cjs.datavizToWebGPU(chunkQEDMOGF3_cjs.compileDataVizBlock(block), prefix));
328
+ this.emitMultiline(chunkIYK6LMQ7_cjs.datavizToWebGPU(chunkIYK6LMQ7_cjs.compileDataVizBlock(block), prefix));
92
329
  break;
93
330
  case "vfx":
94
331
  this.emitParticleDomainBlock(block, prefix);
@@ -107,8 +344,8 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
107
344
  }
108
345
  }
109
346
  emitMaterialDomainBlock(block, prefix) {
110
- const mat = chunkQEDMOGF3_cjs.compileMaterialBlock(block);
111
- this.emitMultiline(chunkQEDMOGF3_cjs.materialToWebGPU(mat, prefix));
347
+ const mat = chunkIYK6LMQ7_cjs.compileMaterialBlock(block);
348
+ this.emitMultiline(chunkIYK6LMQ7_cjs.materialToWebGPU(mat, prefix));
112
349
  if (Object.keys(mat.textureMaps).length > 0) {
113
350
  this.emit(`const ${prefix}TextureMaps = ${this.json(mat.textureMaps)};`);
114
351
  this.emit(`const ${prefix}Textures = await loadMaterialTextureSet(${prefix}TextureMaps);`);
@@ -118,7 +355,7 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
118
355
  }
119
356
  }
120
357
  emitParticleDomainBlock(block, prefix) {
121
- const ps = chunkQEDMOGF3_cjs.compileParticleBlock(block);
358
+ const ps = chunkIYK6LMQ7_cjs.compileParticleBlock(block);
122
359
  const count = Math.max(
123
360
  1,
124
361
  Math.floor(
@@ -161,7 +398,7 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
161
398
  this.emit("});");
162
399
  }
163
400
  emitPostFXDomainBlock(block, prefix) {
164
- const pp = chunkQEDMOGF3_cjs.compilePostProcessingBlock(block);
401
+ const pp = chunkIYK6LMQ7_cjs.compilePostProcessingBlock(block);
165
402
  this.emit(`// Domain post-processing: ${this.escapeStringValue(pp.name ?? block.name, "TypeScript")}`);
166
403
  this.emit(`const ${prefix}PostFXConfig = ${this.json({
167
404
  name: pp.name,
@@ -182,7 +419,7 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
182
419
  this.emit("});");
183
420
  }
184
421
  emitProceduralDomainBlock(block, prefix) {
185
- const proc = chunkQEDMOGF3_cjs.compileProceduralBlock(block);
422
+ const proc = chunkIYK6LMQ7_cjs.compileProceduralBlock(block);
186
423
  this.emit(`// Domain procedural: ${this.escapeStringValue(proc.name, "TypeScript")}`);
187
424
  this.emit(`const ${prefix}ProceduralConfig = ${this.json({
188
425
  name: proc.name,
@@ -352,6 +589,18 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
352
589
  this.emit("return new Float32Array([-x,0,-z,0,1,0, x,0,-z,0,1,0, x,0,z,0,1,0, -x,0,-z,0,1,0, x,0,z,0,1,0, -x,0,z,0,1,0]);");
353
590
  this.dedent();
354
591
  this.emit("}");
592
+ if (this.hasWater) {
593
+ this.emit("function generateWaterPlaneVertices(w, h, n) {");
594
+ this.indent();
595
+ this.emit("const out = [], hw = w/2, hh = h/2, N = Math.max(2, n|0);");
596
+ this.emit("for (let iy=0; iy<N; iy++) { for (let ix=0; ix<N; ix++) {");
597
+ this.emit(" const x0=-hw+w*ix/N, x1=-hw+w*(ix+1)/N, y0=-hh+h*iy/N, y1=-hh+h*(iy+1)/N;");
598
+ this.emit(" const a=[x0,y0,0,0,0,1], b=[x1,y0,0,0,0,1], c=[x1,y1,0,0,0,1], d=[x0,y1,0,0,0,1];");
599
+ this.emit(" out.push(...a,...b,...c, ...a,...c,...d);");
600
+ this.emit("} } return new Float32Array(out);");
601
+ this.dedent();
602
+ this.emit("}");
603
+ }
355
604
  this.emit("function generateSphereVertices(radius, lat, lon) {");
356
605
  this.indent();
357
606
  this.emit("const rings = Math.max(3, lat | 0), segs = Math.max(3, lon | 0); const out = [];");
@@ -417,7 +666,7 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
417
666
  const envData = {};
418
667
  for (const prop of env.properties) envData[prop.key] = prop.value;
419
668
  const bg = envData.background || envData.skybox || "#000000";
420
- const bgColor = this.parseColor(bg);
669
+ const bgColor = typeof bg === "string" && bg.startsWith("#") || Array.isArray(bg) ? this.parseColor(bg) : resolveSkyboxColor(bg);
421
670
  this.emit(
422
671
  `const clearColor: GPUColor = { r: ${bgColor[0]}, g: ${bgColor[1]}, b: ${bgColor[2]}, a: 1.0 };`
423
672
  );
@@ -444,6 +693,41 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
444
693
  const isModel = meshType === "model" || !!modelSrc;
445
694
  const traits = obj.traits || [];
446
695
  this.emit(`// Object: ${this.escapeStringValue(obj.name, "TypeScript")}`);
696
+ const role = resolveGeometryRole({
697
+ purpose: this.findObjProp(obj, "purpose"),
698
+ visible: this.findObjProp(obj, "visible"),
699
+ traitNames: traits.map((t) => t.name)
700
+ });
701
+ if (!role.visible) {
702
+ const prim = resolveGeometry(meshType).kind;
703
+ const pos = this.findObjProp(obj, "position");
704
+ const [px, py, pz] = Array.isArray(pos) ? pos : [0, 0, 0];
705
+ this.emit(
706
+ `// Functional geometry \u2014 purpose="${role.purpose}", not rendered; primitive="${prim}" is shared with physics/vfx/sim consumers.`
707
+ );
708
+ this.emit(
709
+ `holoGraphObjects.push({ id: "${this.escapeStringValue(obj.name, "TypeScript")}", name: "${this.escapeStringValue(obj.name, "TypeScript")}", geometry: "${this.escapeStringValue(meshType, "TypeScript")}", primitive: "${prim}", purpose: "${role.purpose}", visible: false, traits: ${this.json(traits.map((t) => t.name))}, basePosition: [${px},${py},${pz}], position: [${px},${py},${pz}] });`
710
+ );
711
+ this.manifestObjects.push({
712
+ id: String(obj.name ?? ""),
713
+ geometry: meshType,
714
+ primitive: prim,
715
+ purpose: role.purpose,
716
+ visible: false,
717
+ traits: traits.map((t) => t.name),
718
+ affordances: this.extractAffordanceNames(obj),
719
+ position: [
720
+ px + this.manifestGroupOffset[0],
721
+ py + this.manifestGroupOffset[1],
722
+ pz + this.manifestGroupOffset[2]
723
+ ]
724
+ });
725
+ this.emit("");
726
+ if (obj.children) {
727
+ for (const child of obj.children) this.emitObject(child);
728
+ }
729
+ return;
730
+ }
447
731
  const isGaussianSplat = traits.some((t) => t.name === "gaussian_splat");
448
732
  const isPointCloud = traits.some((t) => t.name === "point_cloud");
449
733
  const isGpuParticle = traits.some((t) => isGpuParticleTraitName(t.name));
@@ -462,6 +746,8 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
462
746
  this.emitPointCloudObject(v, obj);
463
747
  } else if (isGpuParticle) {
464
748
  this.emitGpuParticleObject(v, obj);
749
+ } else if (this.isWaterObject(obj)) {
750
+ this.emitWaterObject(v, obj);
465
751
  } else {
466
752
  this.emitMeshObject(v, obj, meshType);
467
753
  }
@@ -479,6 +765,203 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
479
765
  for (const child of obj.children) this.emitObject(child);
480
766
  }
481
767
  }
768
+ // ─── Water / fluid surfaces ────────────────────────────────────────────────
769
+ /**
770
+ * Resolve which named channels the water surface's clarity/ripple are bound to,
771
+ * from the first water object's `water_clarity_binding` / `ripple_intensity_binding`
772
+ * props. These names match the .holo's environment state fields, so host state
773
+ * (Brittney receipts) driving those fields drives the water by name.
774
+ */
775
+ resolveWaterChannels(composition) {
776
+ const all = [...composition.objects ?? []];
777
+ for (const g of composition.spatialGroups ?? []) all.push(...g.objects ?? []);
778
+ const water = all.find((o) => this.isWaterObject(o));
779
+ if (!water) return;
780
+ const clarity = this.findObjProp(water, "water_clarity_binding");
781
+ const ripple = this.findObjProp(water, "ripple_intensity_binding");
782
+ if (typeof clarity === "string" && clarity.trim()) this.waterClarityChannel = clarity.trim();
783
+ if (typeof ripple === "string" && ripple.trim()) this.waterRippleChannel = ripple.trim();
784
+ }
785
+ /** True if any object (incl. nested groups/children) is a water/fluid surface. */
786
+ compositionHasWater(composition) {
787
+ const scan = (objs) => (objs ?? []).some((o) => this.isWaterObject(o) || scan(o.children));
788
+ if (scan(composition.objects)) return true;
789
+ const walk = (g) => scan(g.objects) || (g.groups ?? []).some(walk);
790
+ return (composition.spatialGroups ?? []).some(walk);
791
+ }
792
+ /**
793
+ * A water surface is a fluid-plane mesh (full_screen_fluid_plane / water / ...),
794
+ * or a water-ish MATERIAL on a plane-like geometry. It is NOT a water material on
795
+ * a sphere — emitWaterObject always renders a tessellated plane.
796
+ */
797
+ isWaterObject(obj) {
798
+ const mesh = String(this.findObjProp(obj, "mesh") ?? this.findObjProp(obj, "geometry") ?? "").trim().toLowerCase();
799
+ const material = String(this.findObjProp(obj, "material") ?? "").trim().toLowerCase();
800
+ const WATER_MESH = ["full_screen_fluid_plane", "fluid_plane", "water", "water_plane", "ocean"];
801
+ const WATER_MAT = ["liquid_glass", "water", "liquid", "fluid"];
802
+ if (WATER_MESH.includes(mesh)) return true;
803
+ return WATER_MAT.includes(material) && resolveGeometry(mesh).kind === "plane";
804
+ }
805
+ /**
806
+ * Emit an animated water surface: a tessellated plane driven by the shared
807
+ * per-frame time uniform through the WGSL_WATER shader (Gerstner-lite height
808
+ * waves + analytic normals + texture-free caustics + fresnel). Binding layout
809
+ * mirrors the mesh path (0=Model, 2=vpUniform) plus 1=WaterStatic, 3=waterFrameU.
810
+ */
811
+ emitWaterObject(v, obj) {
812
+ const pos = this.findObjProp(obj, "position");
813
+ const scale = this.findObjProp(obj, "scale") || this.findObjProp(obj, "size");
814
+ const [px, py, pz] = Array.isArray(pos) ? pos : [0, 0, 0];
815
+ const [sx, sy, sz] = Array.isArray(scale) ? scale : typeof scale === "number" ? [scale, scale, scale] : [1, 1, 1];
816
+ const deep = this.parseColor(this.findObjProp(obj, "color") ?? "#06131f");
817
+ const shallow = [
818
+ Math.min(1, deep[0] * 2.2 + 0.05),
819
+ Math.min(1, deep[1] * 2.2 + 0.2),
820
+ Math.min(1, deep[2] * 2 + 0.24)
821
+ ];
822
+ const amplitude = 0.18, choppiness = 0.4, clarity = 1, ripple = 0;
823
+ const L = [0.3714, 0.7428, 0.5571];
824
+ this.emit(`// Water surface: ${this.escapeStringValue(obj.name, "TypeScript")}`);
825
+ this.emit(`const ${v}Vertices = generateWaterPlaneVertices(1.0, 1.0, 48);`);
826
+ this.emit(`const ${v}VBO = createBuffer(device, ${v}Vertices, GPUBufferUsage.VERTEX);`);
827
+ this.emit(`const ${v}VertexCount = ${v}Vertices.length / 6;`);
828
+ this.emit(
829
+ `const ${v}Model = createBuffer(device, new Float32Array([${sx},0,0,0, 0,${sy},0,0, 0,0,${sz},0, ${px},${py},${pz},1]), GPUBufferUsage.UNIFORM);`
830
+ );
831
+ this.emit(
832
+ `const ${v}WaterStatic = createBuffer(device, new Float32Array([${amplitude},${choppiness},${clarity},${ripple}, ${deep[0]},${deep[1]},${deep[2]},0, ${shallow[0]},${shallow[1]},${shallow[2]},0, ${L[0]},${L[1]},${L[2]},0]), GPUBufferUsage.UNIFORM);`
833
+ );
834
+ this.emit(`const ${v}WaterPipeline = device.createRenderPipeline({`);
835
+ this.indent();
836
+ this.emit('layout: "auto",');
837
+ this.emit(
838
+ 'vertex: { module: device.createShaderModule({ code: WGSL_WATER }), entryPoint: "vs_water",'
839
+ );
840
+ this.emit(
841
+ ' buffers: [{ arrayStride: 24, attributes: [{ shaderLocation: 0, offset: 0, format: "float32x3" }, { shaderLocation: 1, offset: 12, format: "float32x3" }] }] },'
842
+ );
843
+ this.emit(
844
+ 'fragment: { module: device.createShaderModule({ code: WGSL_WATER }), entryPoint: "fs_water", targets: [{ format }] },'
845
+ );
846
+ this.emit('primitive: { topology: "triangle-list", cullMode: "none" },');
847
+ this.emit(
848
+ `depthStencil: { format: "depth24plus", depthWriteEnabled: true, depthCompare: "less" }, multisample: { count: MSAA_COUNT },`
849
+ );
850
+ this.dedent();
851
+ this.emit("});");
852
+ this.emit(
853
+ `const ${v}WaterBind = device.createBindGroup({ layout: ${v}WaterPipeline.getBindGroupLayout(0), entries: [`
854
+ );
855
+ this.emit(
856
+ ` { binding: 0, resource: { buffer: ${v}Model } }, { binding: 1, resource: { buffer: ${v}WaterStatic } }, { binding: 2, resource: { buffer: vpUniform } }, { binding: 3, resource: { buffer: waterFrameU } }] });`
857
+ );
858
+ }
859
+ /** Emit the shared WGSL_WATER module (vs_water + fs_water + noise helpers). */
860
+ emitWaterShaderSource() {
861
+ this.emit("const WGSL_WATER = /* wgsl */`");
862
+ this.emit("struct Model { m: mat4x4<f32> };");
863
+ this.emit("struct WStatic { p: vec4<f32>, deep: vec4<f32>, shallow: vec4<f32>, lightDir: vec4<f32> };");
864
+ this.emit("struct Cam { viewProj: mat4x4<f32> };");
865
+ this.emit("struct WFrame { tc: vec4<f32>, dyn: vec4<f32> };");
866
+ this.emit("@group(0) @binding(0) var<uniform> model: Model;");
867
+ this.emit("@group(0) @binding(1) var<uniform> ws: WStatic;");
868
+ this.emit("@group(0) @binding(2) var<uniform> cam: Cam;");
869
+ this.emit("@group(0) @binding(3) var<uniform> wf: WFrame;");
870
+ this.emit("struct VIn { @location(0) pos: vec3<f32>, @location(1) norm: vec3<f32> };");
871
+ this.emit("struct VOut { @builtin(position) clip: vec4<f32>, @location(0) wNorm: vec3<f32>, @location(1) wPos: vec3<f32> };");
872
+ this.emit("@vertex fn vs_water(in: VIn) -> VOut {");
873
+ this.emit(" var o: VOut;");
874
+ this.emit(" let sx = length(model.m[0].xyz); let sy = length(model.m[1].xyz); let sz = max(length(model.m[2].xyz), 1e-4);");
875
+ this.emit(" let wp = in.pos.xy * vec2<f32>(sx, sy);");
876
+ this.emit(" let t = wf.tc.x; let amp = ws.p.x * wf.dyn.z * (0.4 + wf.dyn.y);");
877
+ this.emit(" var h = 0.0; var dhdx = 0.0; var dhdy = 0.0;");
878
+ this.emit(" var dx = 0.0; var dy = 0.0; var k = 0.0; var spd = 0.0; var a = 0.0;");
879
+ this.emit(" for (var i = 0; i < 4; i = i + 1) {");
880
+ this.emit(" if (i == 0) { dx = 1.0; dy = 0.0; k = 0.7; spd = 0.6; a = 1.0; }");
881
+ this.emit(" else if (i == 1) { dx = 0.5; dy = 0.86; k = 1.2; spd = 0.9; a = 0.55; }");
882
+ this.emit(" else if (i == 2) { dx = -0.8; dy = 0.6; k = 1.9; spd = 1.15; a = 0.3; }");
883
+ this.emit(" else { dx = 0.2; dy = -0.98; k = 2.7; spd = 1.4; a = 0.18; }");
884
+ this.emit(" let d = normalize(vec2<f32>(dx, dy));");
885
+ this.emit(" let ph = k * dot(d, wp) + spd * t;");
886
+ this.emit(" h = h + a * amp * sin(ph);");
887
+ this.emit(" dhdx = dhdx + a * amp * k * d.x * cos(ph);");
888
+ this.emit(" dhdy = dhdy + a * amp * k * d.y * cos(ph);");
889
+ this.emit(" }");
890
+ this.emit(" var lp = in.pos; lp.z = lp.z + h / sz;");
891
+ this.emit(" let world = model.m * vec4<f32>(lp, 1.0);");
892
+ this.emit(" o.wPos = world.xyz;");
893
+ this.emit(" o.wNorm = normalize(vec3<f32>(-dhdx, -dhdy, 1.0));");
894
+ this.emit(" o.clip = cam.viewProj * world;");
895
+ this.emit(" return o;");
896
+ this.emit("}");
897
+ this.emit("fn hash21(p: vec2<f32>) -> f32 { let h = dot(p, vec2<f32>(127.1, 311.7)); return fract(sin(h) * 43758.5453); }");
898
+ this.emit("fn vnoise(p: vec2<f32>) -> f32 {");
899
+ this.emit(" let i = floor(p); let f = fract(p); let u = f * f * (3.0 - 2.0 * f);");
900
+ this.emit(" let a = hash21(i); let b = hash21(i + vec2<f32>(1.0, 0.0));");
901
+ this.emit(" let c = hash21(i + vec2<f32>(0.0, 1.0)); let dd = hash21(i + vec2<f32>(1.0, 1.0));");
902
+ this.emit(" return mix(mix(a, b, u.x), mix(c, dd, u.x), u.y);");
903
+ this.emit("}");
904
+ this.emit("struct FIn { @location(0) wNorm: vec3<f32>, @location(1) wPos: vec3<f32> };");
905
+ this.emit("@fragment fn fs_water(in: FIn) -> @location(0) vec4<f32> {");
906
+ this.emit(" let N = normalize(in.wNorm);");
907
+ this.emit(" let V = normalize(wf.tc.yzw - in.wPos);");
908
+ this.emit(" let L = normalize(ws.lightDir.xyz);");
909
+ this.emit(" let t = wf.tc.x;");
910
+ this.emit(" let ndv = max(dot(N, V), 0.0);");
911
+ this.emit(" let fres = 0.02 + 0.98 * pow(1.0 - ndv, 5.0);");
912
+ this.emit(" let tilt = clamp(1.0 - ndv, 0.0, 1.0);");
913
+ this.emit(" var col = mix(ws.deep.rgb, ws.shallow.rgb, clamp(tilt * 1.3, 0.0, 1.0));");
914
+ this.emit(" let p = in.wPos.xy;");
915
+ this.emit(" let n1 = vnoise(p * 0.9 + vec2<f32>(t * 0.15, t * 0.08));");
916
+ this.emit(" let n2 = vnoise(p * 1.7 - vec2<f32>(t * 0.11, t * 0.19));");
917
+ this.emit(" let caustic = pow(n1 * n2, 3.0);");
918
+ this.emit(" col = col + vec3<f32>(0.10, 0.18, 0.20) * caustic * wf.dyn.x;");
919
+ this.emit(" let H = normalize(L + V);");
920
+ this.emit(" let spec = pow(max(dot(N, H), 0.0), 200.0);");
921
+ this.emit(" col = col + vec3<f32>(0.85, 0.92, 1.0) * spec;");
922
+ this.emit(" let sky = vec3<f32>(0.10, 0.26, 0.36);");
923
+ this.emit(" col = mix(col, sky, fres * 0.3);");
924
+ this.emit(" return vec4<f32>(col, 1.0);");
925
+ this.emit("}`;");
926
+ this.emit("");
927
+ }
928
+ /** Tool-affordance names declared on an object's @tool traits (both trait
929
+ * config formats), for the scene manifest — the artifact-level answer to
930
+ * "which named actions does this entity offer" that the numeric registry
931
+ * cannot express. */
932
+ extractAffordanceNames(obj) {
933
+ const names = [];
934
+ for (const trait of obj.traits ?? []) {
935
+ const t = trait;
936
+ if (t.name !== "tool") continue;
937
+ const config = "config" in t && typeof t.config === "object" && t.config !== null ? t.config : t;
938
+ if (typeof config["name"] === "string" && config["name"] !== "tool") {
939
+ names.push(config["name"]);
940
+ } else {
941
+ names.push("unnamed_tool");
942
+ }
943
+ }
944
+ return names;
945
+ }
946
+ /**
947
+ * HoloScene Manifest — a machine-readable world-facts block INSIDE the
948
+ * delivered artifact (g9tk). Collected at EMISSION time (each emitObject/
949
+ * emitMeshObject records what it actually emitted), so the manifest describes
950
+ * the artifact, not the input. Strict JSON payload: independent consumers
951
+ * (perceiver derivations, tooling) JSON.parse it instead of regexing the
952
+ * imperative holoGraphObjects push literals.
953
+ */
954
+ emitSceneManifest(composition) {
955
+ const manifest = {
956
+ version: "holo-scene-manifest-v1",
957
+ source: String(composition.name ?? ""),
958
+ objects: this.manifestObjects
959
+ };
960
+ this.emit("// === HoloScene Manifest (machine-readable world facts; part of the delivered artifact) ===");
961
+ this.emit(`const holoSceneManifest = ${this.json(manifest)};`);
962
+ this.emit('if (typeof globalThis !== "undefined") { globalThis.holoSceneManifest = holoSceneManifest; }');
963
+ this.emit("");
964
+ }
482
965
  emitMeshObject(v, obj, meshType) {
483
966
  this.emit(`const ${v}Vertices = ${this.geometryVertexDataFn(meshType)};`);
484
967
  this.emit(`const ${v}VBO = createBuffer(device, ${v}Vertices, GPUBufferUsage.VERTEX);`);
@@ -495,14 +978,35 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
495
978
  const [cr, cg, cb] = this.extractMaterialColor(material, color);
496
979
  const rough = this.extractMaterialProp(material, "roughness", 0.5);
497
980
  const metal = this.extractMaterialProp(material, "metalness", 0);
498
- const [er, eg, eb] = this.extractEmissive(material, obj);
499
- const emissiveStrength = this.extractMaterialProp(material, "emissive_intensity", 1);
981
+ let [er, eg, eb] = this.extractEmissive(material, obj);
982
+ let emissiveStrength = this.extractMaterialProp(material, "emissive_intensity", 1);
983
+ const glow = this.materialGlow(material);
984
+ if (glow > 0 && er === 0 && eg === 0 && eb === 0) {
985
+ er = cr;
986
+ eg = cg;
987
+ eb = cb;
988
+ emissiveStrength = glow;
989
+ }
500
990
  this.emit(
501
991
  `const ${v}Mat = createBuffer(device, new Float32Array([${cr},${cg},${cb},1.0, ${rough},${metal},${emissiveStrength},0, ${er},${eg},${eb},0]), GPUBufferUsage.UNIFORM);`
502
992
  );
503
993
  this.emit(
504
994
  `holoGraphObjects.push({ id: "${this.escapeStringValue(obj.name, "TypeScript")}", name: "${this.escapeStringValue(obj.name, "TypeScript")}", geometry: "${this.escapeStringValue(meshType, "TypeScript")}", traits: ${this.json((obj.traits || []).map((t) => t.name))}, properties: ${this.json(this.extractObjectProperties(obj))}, basePosition: [${px},${py},${pz}], position: [${px},${py},${pz}], baseScale: [${sx},${sy},${sz}], scale: [${sx},${sy},${sz}], baseColor: [${cr},${cg},${cb}], color: [${cr},${cg},${cb}], material: { roughness: ${rough}, metalness: ${metal}, emissiveStrength: ${emissiveStrength}, emissive: [${er},${eg},${eb}] }, modelBuffer: ${v}Model, matBuffer: ${v}Mat, visible: true });`
505
995
  );
996
+ this.manifestObjects.push({
997
+ id: String(obj.name ?? ""),
998
+ geometry: meshType,
999
+ visible: true,
1000
+ traits: (obj.traits || []).map((t) => t.name),
1001
+ affordances: this.extractAffordanceNames(obj),
1002
+ position: [
1003
+ px + this.manifestGroupOffset[0],
1004
+ py + this.manifestGroupOffset[1],
1005
+ pz + this.manifestGroupOffset[2]
1006
+ ],
1007
+ scale: [sx, sy, sz],
1008
+ color: [cr, cg, cb]
1009
+ });
506
1010
  this.emit(`const ${v}Pipeline = device.createRenderPipeline({`);
507
1011
  this.indent();
508
1012
  this.emit('layout: "auto",');
@@ -525,7 +1029,7 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
525
1029
  `const ${v}Bind = device.createBindGroup({ layout: ${v}Pipeline.getBindGroupLayout(0), entries: [`
526
1030
  );
527
1031
  this.emit(
528
- ` { binding: 0, resource: { buffer: ${v}Model } }, { binding: 1, resource: { buffer: ${v}Mat } }, { binding: 2, resource: { buffer: vpUniform } }] });`
1032
+ ` { binding: 0, resource: { buffer: ${v}Model } }, { binding: 1, resource: { buffer: ${v}Mat } }, { binding: 2, resource: { buffer: vpUniform } }, { binding: 3, resource: { buffer: sceneLights } }] });`
529
1033
  );
530
1034
  }
531
1035
  emitGaussianSplatObject(v, obj) {
@@ -632,43 +1136,140 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
632
1136
  this.emit(
633
1137
  `const ${v}GroupXform = createBuffer(device, new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, ${px},${py},${pz},1]), GPUBufferUsage.UNIFORM);`
634
1138
  );
1139
+ const saved = this.manifestGroupOffset;
1140
+ this.manifestGroupOffset = [saved[0] + px, saved[1] + py, saved[2] + pz];
635
1141
  for (const obj of group.objects) this.emitObject(obj);
636
1142
  if (group.groups) {
637
1143
  for (const sub of group.groups) this.emitGroup(sub);
638
1144
  }
1145
+ this.manifestGroupOffset = saved;
639
1146
  this.emit("");
640
1147
  }
641
1148
  // ─── Light ────────────────────────────────────────────────────────────────
642
- emitLight(light) {
643
- const v = this.sanitizeName(light.name);
644
- let color = [1, 1, 1], intensity = 1, pos = [0, 10, 0], dir = [0, -1, 0];
645
- for (const prop of light.properties) {
646
- if (prop.key === "color") color = this.parseColor(prop.value);
647
- else if (prop.key === "intensity") intensity = prop.value;
648
- else if (prop.key === "position" && Array.isArray(prop.value)) pos = prop.value;
649
- else if (prop.key === "direction" && Array.isArray(prop.value)) dir = prop.value;
1149
+ // Aggregate the scene's declared lights into ONE shared uniform buffer that every
1150
+ // mesh binds (binding 3). Shape math — color*intensity, type tag, world position,
1151
+ // travel direction is resolved here; the fragment shader consumes up to
1152
+ // MAX_LIGHTS. A light-less scene gets a synthesized key light so it stays lit.
1153
+ emitSceneLights(composition) {
1154
+ const MAX_LIGHTS = 4;
1155
+ const typeIndex = {
1156
+ directional: 0,
1157
+ point: 1,
1158
+ spot: 2,
1159
+ hemisphere: 3,
1160
+ ambient: 4,
1161
+ area: 5
1162
+ };
1163
+ const lights = (composition.lights ?? []).slice(0, MAX_LIGHTS).map((light) => {
1164
+ let color = [1, 1, 1], intensity = 1, pos = [0, 10, 0], dir = [0, -1, 0], hasDir = false;
1165
+ for (const prop of light.properties) {
1166
+ if (prop.key === "color") color = this.parseColor(prop.value);
1167
+ else if (prop.key === "intensity") intensity = prop.value;
1168
+ else if (prop.key === "position" && Array.isArray(prop.value)) pos = prop.value;
1169
+ else if (prop.key === "direction" && Array.isArray(prop.value)) {
1170
+ dir = prop.value;
1171
+ hasDir = true;
1172
+ }
1173
+ }
1174
+ const ti = typeIndex[light.lightType] ?? 0;
1175
+ if (ti === 0 && !hasDir && Array.isArray(pos)) dir = [-pos[0], -pos[1], -pos[2]];
1176
+ return { name: String(light.name ?? ""), kind: String(light.lightType ?? ""), color, intensity, pos, dir, ti };
1177
+ });
1178
+ if (lights.length === 0) {
1179
+ lights.push({ name: "(synthesized key light)", kind: "directional", color: [1, 1, 1], intensity: 1, pos: [1, 2, 1.5], dir: [-1, -2, -1.5], ti: 0 });
1180
+ }
1181
+ const segs = [`${lights.length},0,0,0`];
1182
+ for (let i = 0; i < MAX_LIGHTS; i++) {
1183
+ const l = lights[i];
1184
+ if (l) {
1185
+ segs.push(`${l.color[0] * l.intensity},${l.color[1] * l.intensity},${l.color[2] * l.intensity},${l.ti}`);
1186
+ segs.push(`${l.pos[0]},${l.pos[1]},${l.pos[2]},0`);
1187
+ segs.push(`${l.dir[0]},${l.dir[1]},${l.dir[2]},0`);
1188
+ } else {
1189
+ segs.push("0,0,0,0", "0,0,0,0", "0,0,0,0");
1190
+ }
1191
+ }
1192
+ this.emit("// === Scene Lights (shared uniform, bound by every mesh at binding 3) ===");
1193
+ for (let i = 0; i < lights.length; i++) {
1194
+ const safeName = lights[i].name.replace(/[\r\n*]+/g, " ");
1195
+ this.emit(`// Light[${i}]: "${safeName}" (${lights[i].kind}, intensity ${lights[i].intensity})`);
650
1196
  }
651
- const ti = { directional: 0, point: 1, spot: 2, hemisphere: 3, ambient: 4, area: 5 }[light.lightType] ?? 0;
652
- this.emit(
653
- `// Light: ${this.escapeStringValue(light.name, "TypeScript")} (${light.lightType})`
654
- );
655
1197
  this.emit(
656
- `const ${v}Light = createBuffer(device, new Float32Array([${color[0] * intensity},${color[1] * intensity},${color[2] * intensity},${ti}, ${pos[0]},${pos[1]},${pos[2]},0, ${dir[0]},${dir[1]},${dir[2]},0]), GPUBufferUsage.UNIFORM);`
1198
+ `const sceneLights = createBuffer(device, new Float32Array([${segs.join(", ")}]), GPUBufferUsage.UNIFORM);`
657
1199
  );
658
1200
  this.emit("");
659
1201
  }
660
1202
  // ─── Camera ───────────────────────────────────────────────────────────────
661
- emitCamera(cam) {
1203
+ // Compute a bounding sphere (center + radius) over every object's world position,
1204
+ // including spatial-group offsets and per-object scale. Used to auto-frame the
1205
+ // camera when the author declared no explicit position. Returns null for an
1206
+ // object-less scene (nothing to frame → keep the neutral default).
1207
+ sceneBoundsForCamera(composition) {
1208
+ const pts = [];
1209
+ const R_CAP = 3;
1210
+ const addObj = (obj, offset) => {
1211
+ const pos = this.findObjProp(obj, "position");
1212
+ const scale = this.findObjProp(obj, "scale") || this.findObjProp(obj, "size");
1213
+ const [px, py, pz] = Array.isArray(pos) ? pos : [0, 0, 0];
1214
+ const rawR = Array.isArray(scale) ? Math.max(...scale.map((n) => Math.abs(n))) : typeof scale === "number" ? Math.abs(scale) : 1;
1215
+ pts.push({ p: [px + offset[0], py + offset[1], pz + offset[2]], r: Math.min(rawR, R_CAP) });
1216
+ };
1217
+ for (const obj of composition.objects ?? []) addObj(obj, [0, 0, 0]);
1218
+ const walkGroup = (g, parent) => {
1219
+ const gp = g.properties?.find((p) => p.key === "position")?.value;
1220
+ const [gx, gy, gz] = Array.isArray(gp) ? gp : [0, 0, 0];
1221
+ const off = [parent[0] + gx, parent[1] + gy, parent[2] + gz];
1222
+ for (const obj of g.objects ?? []) addObj(obj, off);
1223
+ for (const sub of g.groups ?? []) walkGroup(sub, off);
1224
+ };
1225
+ for (const g of composition.spatialGroups ?? []) walkGroup(g, [0, 0, 0]);
1226
+ if (pts.length === 0) return null;
1227
+ const min = [Infinity, Infinity, Infinity];
1228
+ const max = [-Infinity, -Infinity, -Infinity];
1229
+ for (const { p, r } of pts) {
1230
+ for (let i = 0; i < 3; i++) {
1231
+ min[i] = Math.min(min[i], p[i] - r);
1232
+ max[i] = Math.max(max[i], p[i] + r);
1233
+ }
1234
+ }
1235
+ const center = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2];
1236
+ let radius = 0;
1237
+ for (const { p, r } of pts) {
1238
+ radius = Math.max(radius, Math.hypot(p[0] - center[0], p[1] - center[1], p[2] - center[2]) + r);
1239
+ }
1240
+ return { center, radius: radius || 1 };
1241
+ }
1242
+ emitCamera(composition) {
1243
+ const cam = composition.camera;
662
1244
  this.emit("// === Camera ===");
663
- let fov = 60, near = 0.1, far = 1e3, pos = [4, 3, 6], lookAt = [0, 0, 0];
1245
+ let fov = 60, near = 0.1, far = 1e3, pos = [4, 3, 6], lookAt = [0, 0, 0], hasPos = false;
664
1246
  for (const prop of cam?.properties ?? []) {
665
1247
  if (prop.key === "fov" || prop.key === "field_of_view") fov = prop.value;
666
1248
  else if (prop.key === "near") near = prop.value;
667
1249
  else if (prop.key === "far") far = prop.value;
668
- else if (prop.key === "position" && Array.isArray(prop.value)) pos = prop.value;
669
- else if ((prop.key === "look_at" || prop.key === "lookAt") && Array.isArray(prop.value))
1250
+ else if (prop.key === "position" && Array.isArray(prop.value)) {
1251
+ pos = prop.value;
1252
+ hasPos = true;
1253
+ } else if ((prop.key === "look_at" || prop.key === "lookAt") && Array.isArray(prop.value))
670
1254
  lookAt = prop.value;
671
1255
  }
1256
+ if (!hasPos) {
1257
+ const b = this.sceneBoundsForCamera(composition);
1258
+ if (b) {
1259
+ const r4 = (n) => Number(n.toFixed(4));
1260
+ const fovRad = fov * Math.PI / 180;
1261
+ const dist = b.radius / Math.sin(fovRad / 2) * 1.1;
1262
+ const dl = Math.hypot(0.35, 0.45, 1);
1263
+ const dir = [0.35 / dl, 0.45 / dl, 1 / dl];
1264
+ pos = [
1265
+ r4(b.center[0] + dir[0] * dist),
1266
+ r4(b.center[1] + dir[1] * dist),
1267
+ r4(b.center[2] + dir[2] * dist)
1268
+ ];
1269
+ lookAt = [r4(b.center[0]), r4(b.center[1]), r4(b.center[2])];
1270
+ far = Math.max(far, r4(dist + b.radius * 2 + 10));
1271
+ }
1272
+ }
672
1273
  this.emit(`const cameraFov = ${fov} * Math.PI / 180;`);
673
1274
  this.emit(`const cameraNear = ${near}, cameraFar = ${far};`);
674
1275
  this.emit(`const cameraPos = new Float32Array([${pos.join(",")}]);`);
@@ -768,15 +1369,27 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
768
1369
  this.emit("");
769
1370
  this.emit("const WGSL_FRAGMENT = /* wgsl */`");
770
1371
  this.emit("struct Mat { color: vec4<f32>, rm: vec4<f32>, emissive: vec4<f32> };");
1372
+ this.emit("struct Light { ct: vec4<f32>, posW: vec4<f32>, dirW: vec4<f32> };");
1373
+ this.emit("struct Lights { count: vec4<f32>, items: array<Light, 4> };");
771
1374
  this.emit("@group(0) @binding(1) var<uniform> mat: Mat;");
1375
+ this.emit("@group(0) @binding(3) var<uniform> lights: Lights;");
772
1376
  this.emit("struct FIn { @location(0) wNorm: vec3<f32>, @location(1) wPos: vec3<f32> };");
773
1377
  this.emit("@fragment fn fs_main(i: FIn) -> @location(0) vec4<f32> {");
774
- this.emit(" let N = normalize(i.wNorm); let L = normalize(vec3<f32>(1.0, 2.0, 1.5));");
775
- this.emit(" let d = max(dot(N, L), 0.0) * (1.0 - mat.rm[0] * 0.5);");
776
- this.emit(" let lit = mat.color.rgb * (0.15 + d);");
1378
+ this.emit(" let N = normalize(i.wNorm);");
1379
+ this.emit(" var acc = vec3<f32>(0.0, 0.0, 0.0);");
1380
+ this.emit(" let n = i32(lights.count.x);");
1381
+ this.emit(" for (var k = 0; k < n; k = k + 1) {");
1382
+ this.emit(" let lt = lights.items[k];");
1383
+ this.emit(" var L = normalize(-lt.dirW.xyz);");
1384
+ this.emit(" if (lt.ct.w > 0.5) { L = normalize(lt.posW.xyz - i.wPos); }");
1385
+ this.emit(" let d = max(dot(N, L), 0.0) * (1.0 - mat.rm[0] * 0.5);");
1386
+ this.emit(" acc = acc + lt.ct.rgb * d;");
1387
+ this.emit(" }");
1388
+ this.emit(" let lit = mat.color.rgb * (0.15 + acc);");
777
1389
  this.emit(" let emit = mat.emissive.rgb * mat.rm[2];");
778
1390
  this.emit(" return vec4<f32>(lit + emit, mat.color.a); }`;");
779
1391
  this.emit("");
1392
+ if (this.hasWater) this.emitWaterShaderSource();
780
1393
  this.emit("const WGSL_SPLAT = /* wgsl */`");
781
1394
  this.emit(
782
1395
  "struct Splat { center: vec3<f32>, opacity: f32, cov: array<f32,6>, sh: vec3<f32> };"
@@ -1011,6 +1624,11 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
1011
1624
  this.emit("frameCount++;");
1012
1625
  this.emit("const time = (performance.now() - t0) / 1000.0;");
1013
1626
  this.emit("device.queue.writeBuffer(vpUniform, 0, buildViewProjection());");
1627
+ if (this.hasWater) {
1628
+ this.emit(
1629
+ `device.queue.writeBuffer(waterFrameU, 0, new Float32Array([time, cameraPos[0], cameraPos[1], cameraPos[2], hsChannels[${JSON.stringify(this.waterClarityChannel)}], hsChannels[${JSON.stringify(this.waterRippleChannel)}], hsWaterAmp, 0]));`
1630
+ );
1631
+ }
1014
1632
  this.emit("hsRenderLabels();");
1015
1633
  this.emit("const enc = device.createCommandEncoder();");
1016
1634
  const gpuObjs = (composition.objects || []).filter(
@@ -1067,6 +1685,17 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
1067
1685
  emitDrawCall(obj) {
1068
1686
  const v = this.sanitizeName(obj.name);
1069
1687
  const traits = obj.traits || [];
1688
+ const role = resolveGeometryRole({
1689
+ purpose: this.findObjProp(obj, "purpose"),
1690
+ visible: this.findObjProp(obj, "visible"),
1691
+ traitNames: traits.map((t) => t.name)
1692
+ });
1693
+ if (!role.visible) {
1694
+ if (obj.children) {
1695
+ for (const child of obj.children) this.emitDrawCall(child);
1696
+ }
1697
+ return;
1698
+ }
1070
1699
  if (traits.some((t) => t.name === "gaussian_splat")) {
1071
1700
  this.emit(`rp.setPipeline(${v}SplatPipeline); rp.draw(4, ${v}SplatCount.value);`);
1072
1701
  } else if (traits.some((t) => t.name === "point_cloud")) {
@@ -1075,6 +1704,10 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
1075
1704
  this.emit(
1076
1705
  `rp.setPipeline(${v}ParticlePipeline); rp.setVertexBuffer(0, ${v}ParticleBufA); rp.draw(${v}ParticleCount);`
1077
1706
  );
1707
+ } else if (this.isWaterObject(obj)) {
1708
+ this.emit(
1709
+ `rp.setPipeline(${v}WaterPipeline); rp.setBindGroup(0, ${v}WaterBind); rp.setVertexBuffer(0, ${v}VBO); rp.draw(${v}VertexCount);`
1710
+ );
1078
1711
  } else {
1079
1712
  this.emit(
1080
1713
  `rp.setPipeline(${v}Pipeline); rp.setBindGroup(0, ${v}Bind); rp.setVertexBuffer(0, ${v}VBO); rp.draw(${v}VertexCount);`
@@ -1152,7 +1785,14 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
1152
1785
  return h >>> 0;
1153
1786
  }
1154
1787
  findObjProp(obj, key) {
1155
- return obj.properties?.find((p) => p.key === key)?.value;
1788
+ const own = obj.properties?.find((p) => p.key === key)?.value;
1789
+ if (own !== void 0) return own;
1790
+ if (obj.template) {
1791
+ const tpl = this.templatesByName.get(obj.template);
1792
+ const fromTemplate = tpl?.properties?.find((p) => p.key === key)?.value;
1793
+ if (fromTemplate !== void 0) return fromTemplate;
1794
+ }
1795
+ return void 0;
1156
1796
  }
1157
1797
  extractObjectProperties(obj) {
1158
1798
  const properties = {};
@@ -1204,16 +1844,29 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
1204
1844
  return [0, 0, 0];
1205
1845
  }
1206
1846
  geometryVertexDataFn(meshType) {
1207
- const map = {
1208
- sphere: "generateSphereVertices(0.5, 16, 16)",
1209
- orb: "generateSphereVertices(0.5, 16, 16)",
1210
- plane: "generatePlaneVertices(1.0, 1.0)",
1211
- cylinder: "generateCylinderVertices(0.5, 1.0, 16)",
1212
- cone: "generateConeVertices(0.5, 1.0, 16)",
1213
- pyramid: "generateConeVertices(0.5, 1.0, 16)",
1214
- torus: "generateTorusVertices(0.5, 0.15, 16, 32)"
1215
- };
1216
- return map[meshType] || "generateCubeVertices(1.0)";
1847
+ return resolveGeometry(meshType).webgpuVertexFn;
1848
+ }
1849
+ materialName(material) {
1850
+ if (typeof material === "string") return material;
1851
+ if (material && typeof material === "object") {
1852
+ const m = material;
1853
+ if (typeof m.type === "string") return m.type;
1854
+ if (typeof m.name === "string") return m.name;
1855
+ }
1856
+ return void 0;
1857
+ }
1858
+ // Self-illumination strength for a named material when no explicit emissive is
1859
+ // authored. Holographic/neon/emissive surfaces glow in their own color; glass/gel
1860
+ // gets a soft sheen; everything else stays purely lit (0).
1861
+ materialGlow(material) {
1862
+ const name = this.materialName(material);
1863
+ if (!name) return 0;
1864
+ const n = name.toLowerCase();
1865
+ if (/hologram|holo|liquid_glass|liquid_hologram|neon|glow|emissive|aura|plasma|energy/.test(n)) {
1866
+ return 0.65;
1867
+ }
1868
+ if (/glass|crystal|gel|water/.test(n)) return 0.28;
1869
+ return 0;
1217
1870
  }
1218
1871
  };
1219
1872
  }
@@ -1221,5 +1874,11 @@ var init_WebGPUCompiler = chunkZHPMP447_cjs.__esm({
1221
1874
 
1222
1875
  exports.WebGPUCompiler_exports = WebGPUCompiler_exports;
1223
1876
  exports.init_WebGPUCompiler = init_WebGPUCompiler;
1224
- //# sourceMappingURL=chunk-TC7JEO6R.cjs.map
1225
- //# sourceMappingURL=chunk-TC7JEO6R.cjs.map
1877
+ exports.init_geometry_purpose = init_geometry_purpose;
1878
+ exports.init_geometry_registry = init_geometry_registry;
1879
+ exports.init_skybox_registry = init_skybox_registry;
1880
+ exports.resolveGeometry = resolveGeometry;
1881
+ exports.resolveGeometryRole = resolveGeometryRole;
1882
+ exports.resolveSkyboxColor = resolveSkyboxColor;
1883
+ //# sourceMappingURL=chunk-U6CQOM5Q.cjs.map
1884
+ //# sourceMappingURL=chunk-U6CQOM5Q.cjs.map