@holoscript/engine 6.0.3 → 6.0.4

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 (192) hide show
  1. package/dist/AutoMesher-CK47F6AV.js +17 -0
  2. package/dist/GPUBuffers-2LHBCD7X.js +9 -0
  3. package/dist/WebGPUContext-TNEUYU2Y.js +11 -0
  4. package/dist/animation/index.cjs +38 -38
  5. package/dist/animation/index.d.cts +1 -1
  6. package/dist/animation/index.d.ts +1 -1
  7. package/dist/animation/index.js +1 -1
  8. package/dist/audio/index.cjs +16 -6
  9. package/dist/audio/index.d.cts +1 -1
  10. package/dist/audio/index.d.ts +1 -1
  11. package/dist/audio/index.js +1 -1
  12. package/dist/camera/index.cjs +23 -23
  13. package/dist/camera/index.d.cts +1 -1
  14. package/dist/camera/index.d.ts +1 -1
  15. package/dist/camera/index.js +1 -1
  16. package/dist/character/index.cjs +6 -4
  17. package/dist/character/index.js +1 -1
  18. package/dist/choreography/index.cjs +1194 -0
  19. package/dist/choreography/index.d.cts +687 -0
  20. package/dist/choreography/index.d.ts +687 -0
  21. package/dist/choreography/index.js +1156 -0
  22. package/dist/chunk-2CSNRI2N.js +217 -0
  23. package/dist/chunk-33T2WINR.js +266 -0
  24. package/dist/chunk-35R73OFM.js +1257 -0
  25. package/dist/chunk-4MMDSUNP.js +1256 -0
  26. package/dist/chunk-5V6HOU72.js +319 -0
  27. package/dist/chunk-6QOP6PYF.js +1038 -0
  28. package/dist/chunk-7KMJVHIL.js +8944 -0
  29. package/dist/chunk-7VPUC62U.js +1106 -0
  30. package/dist/chunk-A2Y6RCAT.js +1878 -0
  31. package/dist/chunk-AHM42MK6.js +8944 -0
  32. package/dist/chunk-BL7IDTHE.js +218 -0
  33. package/dist/chunk-CITOMSWL.js +10462 -0
  34. package/dist/chunk-CXDPKW2K.js +8944 -0
  35. package/dist/chunk-CXZPLD4S.js +223 -0
  36. package/dist/chunk-CZYJE7IH.js +5169 -0
  37. package/dist/chunk-D2OP7YC7.js +6325 -0
  38. package/dist/chunk-EDRVQHUU.js +1544 -0
  39. package/dist/chunk-EJSLOOW2.js +3589 -0
  40. package/dist/chunk-F53SFGW5.js +1878 -0
  41. package/dist/chunk-HCFPELPY.js +919 -0
  42. package/dist/chunk-HNEE36PY.js +93 -0
  43. package/dist/chunk-HYXNV36F.js +1256 -0
  44. package/dist/chunk-IB7KHVFY.js +821 -0
  45. package/dist/chunk-IBBO7YYG.js +690 -0
  46. package/dist/chunk-ILIBGINU.js +5470 -0
  47. package/dist/chunk-IS4MHLKN.js +5479 -0
  48. package/dist/chunk-JT2PFKWD.js +5479 -0
  49. package/dist/chunk-K4CUB4NY.js +1038 -0
  50. package/dist/chunk-KATDQXRJ.js +10462 -0
  51. package/dist/chunk-KBQE6ZFJ.js +8944 -0
  52. package/dist/chunk-KBVD5K7E.js +560 -0
  53. package/dist/chunk-KCDPVQRY.js +4088 -0
  54. package/dist/chunk-KN4QJPKN.js +8944 -0
  55. package/dist/chunk-KWJ3ROSI.js +8944 -0
  56. package/dist/chunk-L45VF6DD.js +919 -0
  57. package/dist/chunk-LY4T37YK.js +307 -0
  58. package/dist/chunk-MDN5WZXA.js +1544 -0
  59. package/dist/chunk-MGCDP6VU.js +928 -0
  60. package/dist/chunk-NCX7X6G2.js +8681 -0
  61. package/dist/chunk-OF54BPVD.js +913 -0
  62. package/dist/chunk-OWSN2Q3Q.js +690 -0
  63. package/dist/chunk-PRRB5TTA.js +406 -0
  64. package/dist/chunk-PXWVQF76.js +4086 -0
  65. package/dist/chunk-PYCOIDT2.js +812 -0
  66. package/dist/chunk-PZCSADOV.js +928 -0
  67. package/dist/chunk-Q2XBVS2K.js +1038 -0
  68. package/dist/chunk-QDZRXWN5.js +1776 -0
  69. package/dist/chunk-RNWOZ6WQ.js +913 -0
  70. package/dist/chunk-ROLFT4CJ.js +1693 -0
  71. package/dist/chunk-SLTJRZ2N.js +266 -0
  72. package/dist/chunk-SRUS5XSU.js +4088 -0
  73. package/dist/chunk-TKCA3WZ5.js +5409 -0
  74. package/dist/chunk-TNRMXYI2.js +1650 -0
  75. package/dist/chunk-TQB3GJGM.js +9763 -0
  76. package/dist/chunk-TUFGXG6K.js +510 -0
  77. package/dist/chunk-U6KMTGQJ.js +632 -0
  78. package/dist/chunk-VMGJQST6.js +8681 -0
  79. package/dist/chunk-X4F4TCG4.js +5470 -0
  80. package/dist/chunk-ZIFROE75.js +1544 -0
  81. package/dist/chunk-ZIJQYHSQ.js +1204 -0
  82. package/dist/combat/index.cjs +4 -4
  83. package/dist/combat/index.d.cts +1 -1
  84. package/dist/combat/index.d.ts +1 -1
  85. package/dist/combat/index.js +1 -1
  86. package/dist/ecs/index.cjs +1 -1
  87. package/dist/ecs/index.js +1 -1
  88. package/dist/environment/index.cjs +14 -14
  89. package/dist/environment/index.d.cts +1 -1
  90. package/dist/environment/index.d.ts +1 -1
  91. package/dist/environment/index.js +1 -1
  92. package/dist/gpu/index.cjs +4810 -0
  93. package/dist/gpu/index.js +3714 -0
  94. package/dist/hologram/index.cjs +27 -1
  95. package/dist/hologram/index.js +1 -1
  96. package/dist/index-B2PIsAmR.d.cts +2180 -0
  97. package/dist/index-B2PIsAmR.d.ts +2180 -0
  98. package/dist/index-BHySEPX7.d.cts +2921 -0
  99. package/dist/index-BJV21zuy.d.cts +341 -0
  100. package/dist/index-BJV21zuy.d.ts +341 -0
  101. package/dist/index-BQutTphC.d.cts +790 -0
  102. package/dist/index-ByIq2XrS.d.cts +3910 -0
  103. package/dist/index-BysHjDSO.d.cts +224 -0
  104. package/dist/index-BysHjDSO.d.ts +224 -0
  105. package/dist/index-CKwAJGck.d.ts +455 -0
  106. package/dist/index-CUl3QstQ.d.cts +3006 -0
  107. package/dist/index-CUl3QstQ.d.ts +3006 -0
  108. package/dist/index-CmYtNiI-.d.cts +953 -0
  109. package/dist/index-CmYtNiI-.d.ts +953 -0
  110. package/dist/index-CnRzWxi_.d.cts +522 -0
  111. package/dist/index-CnRzWxi_.d.ts +522 -0
  112. package/dist/index-CwRWbSC7.d.ts +2921 -0
  113. package/dist/index-CxKIBstO.d.ts +790 -0
  114. package/dist/index-DJ6-R8vh.d.cts +455 -0
  115. package/dist/index-DQKisbcI.d.cts +4968 -0
  116. package/dist/index-DQKisbcI.d.ts +4968 -0
  117. package/dist/index-DRT2zJez.d.ts +3910 -0
  118. package/dist/index-DfNLiAka.d.cts +192 -0
  119. package/dist/index-DfNLiAka.d.ts +192 -0
  120. package/dist/index-nMvkoRm8.d.cts +405 -0
  121. package/dist/index-nMvkoRm8.d.ts +405 -0
  122. package/dist/index-s9yOFU37.d.cts +604 -0
  123. package/dist/index-s9yOFU37.d.ts +604 -0
  124. package/dist/index.cjs +22966 -6960
  125. package/dist/index.d.cts +864 -20
  126. package/dist/index.d.ts +864 -20
  127. package/dist/index.js +3062 -48
  128. package/dist/input/index.cjs +1 -1
  129. package/dist/input/index.js +1 -1
  130. package/dist/orbital/index.cjs +3 -3
  131. package/dist/orbital/index.d.cts +1 -1
  132. package/dist/orbital/index.d.ts +1 -1
  133. package/dist/orbital/index.js +1 -1
  134. package/dist/particles/index.cjs +16 -16
  135. package/dist/particles/index.d.cts +1 -1
  136. package/dist/particles/index.d.ts +1 -1
  137. package/dist/particles/index.js +1 -1
  138. package/dist/physics/index.cjs +2377 -21
  139. package/dist/physics/index.d.cts +1 -1
  140. package/dist/physics/index.d.ts +1 -1
  141. package/dist/physics/index.js +35 -1
  142. package/dist/postfx/index.cjs +3491 -0
  143. package/dist/postfx/index.js +93 -0
  144. package/dist/procedural/index.cjs +1 -1
  145. package/dist/procedural/index.js +1 -1
  146. package/dist/puppeteer-5VF6KDVO.js +52197 -0
  147. package/dist/puppeteer-IZVZ3SG4.js +52197 -0
  148. package/dist/rendering/index.cjs +33 -32
  149. package/dist/rendering/index.d.cts +1 -1
  150. package/dist/rendering/index.d.ts +1 -1
  151. package/dist/rendering/index.js +8 -6
  152. package/dist/runtime/index.cjs +23 -13
  153. package/dist/runtime/index.d.cts +1 -1
  154. package/dist/runtime/index.d.ts +1 -1
  155. package/dist/runtime/index.js +8 -6
  156. package/dist/runtime/protocols/index.cjs +349 -0
  157. package/dist/runtime/protocols/index.js +15 -0
  158. package/dist/scene/index.cjs +8 -8
  159. package/dist/scene/index.d.cts +1 -1
  160. package/dist/scene/index.d.ts +1 -1
  161. package/dist/scene/index.js +1 -1
  162. package/dist/shader/index.cjs +3087 -0
  163. package/dist/shader/index.js +3044 -0
  164. package/dist/simulation/index.cjs +10680 -0
  165. package/dist/simulation/index.d.cts +3 -0
  166. package/dist/simulation/index.d.ts +3 -0
  167. package/dist/simulation/index.js +307 -0
  168. package/dist/spatial/index.cjs +2443 -0
  169. package/dist/spatial/index.d.cts +1545 -0
  170. package/dist/spatial/index.d.ts +1545 -0
  171. package/dist/spatial/index.js +2400 -0
  172. package/dist/terrain/index.cjs +1 -1
  173. package/dist/terrain/index.d.cts +1 -1
  174. package/dist/terrain/index.d.ts +1 -1
  175. package/dist/terrain/index.js +1 -1
  176. package/dist/transformers.node-4NKAPD5U.js +45620 -0
  177. package/dist/vm/index.cjs +7 -8
  178. package/dist/vm/index.d.cts +1 -1
  179. package/dist/vm/index.d.ts +1 -1
  180. package/dist/vm/index.js +1 -1
  181. package/dist/vm-bridge/index.cjs +2 -2
  182. package/dist/vm-bridge/index.d.cts +2 -2
  183. package/dist/vm-bridge/index.d.ts +2 -2
  184. package/dist/vm-bridge/index.js +1 -1
  185. package/dist/vr/index.cjs +6 -6
  186. package/dist/vr/index.js +1 -1
  187. package/dist/world/index.cjs +3 -3
  188. package/dist/world/index.d.cts +1 -1
  189. package/dist/world/index.d.ts +1 -1
  190. package/dist/world/index.js +1 -1
  191. package/package.json +53 -21
  192. package/LICENSE +0 -21
@@ -0,0 +1,560 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-AKLW2MUS.js";
4
+
5
+ // src/vr/index.ts
6
+ var vr_exports = {};
7
+ __export(vr_exports, {
8
+ HandTracker: () => HandTracker,
9
+ HapticFeedback: () => HapticFeedback,
10
+ VRLocomotion: () => VRLocomotion,
11
+ WebXRManager: () => WebXRManager,
12
+ WebXRSystem: () => WebXRSystem
13
+ });
14
+
15
+ // src/vr/HandTracker.ts
16
+ var HandTracker = class {
17
+ hands = /* @__PURE__ */ new Map();
18
+ gestureHistory = [];
19
+ maxHistory = 100;
20
+ constructor() {
21
+ this.hands.set("left", this.createDefaultState("left"));
22
+ this.hands.set("right", this.createDefaultState("right"));
23
+ }
24
+ createDefaultState(side) {
25
+ return {
26
+ side,
27
+ tracked: false,
28
+ joints: /* @__PURE__ */ new Map(),
29
+ gesture: "none",
30
+ confidence: 0,
31
+ pinchStrength: 0,
32
+ gripStrength: 0
33
+ };
34
+ }
35
+ /**
36
+ * Update joint positions for a hand
37
+ */
38
+ updateJoints(side, joints) {
39
+ const state = this.hands.get(side);
40
+ state.tracked = true;
41
+ state.joints.clear();
42
+ for (const [name, pos] of Object.entries(joints)) {
43
+ state.joints.set(name, pos);
44
+ }
45
+ state.gesture = this.detectGesture(state);
46
+ this.gestureHistory.push({ side, gesture: state.gesture, timestamp: Date.now() });
47
+ if (this.gestureHistory.length > this.maxHistory) this.gestureHistory.shift();
48
+ }
49
+ /**
50
+ * Update pinch/grip strength
51
+ */
52
+ updateStrength(side, pinch, grip) {
53
+ const state = this.hands.get(side);
54
+ state.pinchStrength = pinch;
55
+ state.gripStrength = grip;
56
+ }
57
+ /**
58
+ * Detect gesture from joint positions
59
+ */
60
+ detectGesture(state) {
61
+ if (state.pinchStrength > 0.8) return "pinch";
62
+ if (state.gripStrength > 0.8) return "grab";
63
+ const thumb = state.joints.get("thumb_tip");
64
+ const index = state.joints.get("index_tip");
65
+ const middle = state.joints.get("middle_tip");
66
+ if (thumb && index && middle) {
67
+ const indexDist = Math.sqrt(
68
+ (thumb.x - index.x) ** 2 + (thumb.y - index.y) ** 2 + (thumb.z - index.z) ** 2
69
+ );
70
+ if (indexDist > 0.1 && state.gripStrength < 0.3) return "point";
71
+ }
72
+ if (state.gripStrength > 0.6) return "fist";
73
+ if (state.gripStrength < 0.2 && state.pinchStrength < 0.2) return "open";
74
+ return "none";
75
+ }
76
+ getHand(side) {
77
+ return this.hands.get(side);
78
+ }
79
+ isTracked(side) {
80
+ return this.hands.get(side).tracked;
81
+ }
82
+ getGesture(side) {
83
+ return this.hands.get(side).gesture;
84
+ }
85
+ getGestureHistory() {
86
+ return [...this.gestureHistory];
87
+ }
88
+ getJoint(side, joint) {
89
+ return this.hands.get(side).joints.get(joint);
90
+ }
91
+ };
92
+
93
+ // src/vr/HapticFeedback.ts
94
+ var HapticFeedback = class {
95
+ patterns = /* @__PURE__ */ new Map();
96
+ activePulses = [];
97
+ enabled = true;
98
+ globalIntensity = 1;
99
+ constructor() {
100
+ this.registerPresets();
101
+ }
102
+ registerPresets() {
103
+ this.registerPattern({
104
+ name: "tap",
105
+ loop: false,
106
+ pulses: [{ hand: "right", intensity: 0.5, durationMs: 50, frequency: 200 }]
107
+ });
108
+ this.registerPattern({
109
+ name: "grab",
110
+ loop: false,
111
+ pulses: [
112
+ { hand: "right", intensity: 0.3, durationMs: 30, frequency: 150 },
113
+ { hand: "right", intensity: 0.7, durationMs: 100, frequency: 250 }
114
+ ]
115
+ });
116
+ this.registerPattern({
117
+ name: "impact",
118
+ loop: false,
119
+ pulses: [{ hand: "both", intensity: 1, durationMs: 150, frequency: 300 }]
120
+ });
121
+ this.registerPattern({
122
+ name: "heartbeat",
123
+ loop: true,
124
+ pulses: [
125
+ { hand: "both", intensity: 0.6, durationMs: 80, frequency: 100 },
126
+ { hand: "both", intensity: 0.3, durationMs: 60, frequency: 100 }
127
+ ]
128
+ });
129
+ }
130
+ /**
131
+ * Register a haptic pattern
132
+ */
133
+ registerPattern(pattern) {
134
+ this.patterns.set(pattern.name, pattern);
135
+ }
136
+ /**
137
+ * Play a named pattern
138
+ */
139
+ play(patternName, hand) {
140
+ if (!this.enabled) return false;
141
+ const pattern = this.patterns.get(patternName);
142
+ if (!pattern) return false;
143
+ const now = Date.now();
144
+ for (const pulse of pattern.pulses) {
145
+ const effective = {
146
+ ...pulse,
147
+ hand: hand ?? pulse.hand,
148
+ intensity: pulse.intensity * this.globalIntensity
149
+ };
150
+ this.activePulses.push({ hand: effective.hand, pulse: effective, startTime: now });
151
+ }
152
+ return true;
153
+ }
154
+ /**
155
+ * Fire a single custom pulse
156
+ */
157
+ pulse(hand, intensity, durationMs, frequency = 200) {
158
+ if (!this.enabled) return;
159
+ const p = {
160
+ hand,
161
+ intensity: intensity * this.globalIntensity,
162
+ durationMs,
163
+ frequency
164
+ };
165
+ this.activePulses.push({ hand, pulse: p, startTime: Date.now() });
166
+ }
167
+ /**
168
+ * Stop all active haptics
169
+ */
170
+ stopAll() {
171
+ this.activePulses = [];
172
+ }
173
+ /**
174
+ * Get active pulse count
175
+ */
176
+ getActivePulseCount() {
177
+ return this.activePulses.length;
178
+ }
179
+ getPatternCount() {
180
+ return this.patterns.size;
181
+ }
182
+ getPattern(name) {
183
+ return this.patterns.get(name);
184
+ }
185
+ setEnabled(enabled) {
186
+ this.enabled = enabled;
187
+ }
188
+ isEnabled() {
189
+ return this.enabled;
190
+ }
191
+ setGlobalIntensity(v) {
192
+ this.globalIntensity = Math.max(0, Math.min(1, v));
193
+ }
194
+ getGlobalIntensity() {
195
+ return this.globalIntensity;
196
+ }
197
+ };
198
+
199
+ // src/vr/VRLocomotion.ts
200
+ var VRLocomotion = class {
201
+ config;
202
+ position = [0, 0, 0];
203
+ rotation = 0;
204
+ // yaw in degrees
205
+ teleportHistory = [];
206
+ boundaryDistance = Infinity;
207
+ constructor(config) {
208
+ this.config = {
209
+ mode: config?.mode ?? "teleport",
210
+ moveSpeed: config?.moveSpeed ?? 2,
211
+ snapAngle: config?.snapAngle ?? 45,
212
+ teleportRange: config?.teleportRange ?? 10,
213
+ boundaryFadeDistance: config?.boundaryFadeDistance ?? 0.5,
214
+ comfortVignette: config?.comfortVignette ?? true
215
+ };
216
+ }
217
+ /**
218
+ * Teleport to a target position
219
+ */
220
+ teleport(target) {
221
+ if (!target.valid) return false;
222
+ const dist = Math.sqrt((target.x - this.position[0]) ** 2 + (target.z - this.position[2]) ** 2);
223
+ if (dist > this.config.teleportRange) return false;
224
+ this.position = [target.x, target.y, target.z];
225
+ this.teleportHistory.push(target);
226
+ return true;
227
+ }
228
+ /**
229
+ * Smooth move
230
+ */
231
+ move(dx, dz, deltaTime) {
232
+ const speed = this.config.moveSpeed * deltaTime;
233
+ const rad = this.rotation * Math.PI / 180;
234
+ this.position[0] += (dx * Math.cos(rad) - dz * Math.sin(rad)) * speed;
235
+ this.position[2] += (dx * Math.sin(rad) + dz * Math.cos(rad)) * speed;
236
+ }
237
+ /**
238
+ * Snap turn
239
+ */
240
+ snapTurn(direction) {
241
+ this.rotation += direction === "right" ? this.config.snapAngle : -this.config.snapAngle;
242
+ this.rotation = (this.rotation % 360 + 360) % 360;
243
+ }
244
+ /**
245
+ * Update boundary distance
246
+ */
247
+ updateBoundary(distance) {
248
+ this.boundaryDistance = distance;
249
+ }
250
+ /**
251
+ * Get boundary fade factor (0 = fully faded, 1 = clear)
252
+ */
253
+ getBoundaryFade() {
254
+ if (this.boundaryDistance >= this.config.boundaryFadeDistance) return 1;
255
+ return Math.max(0, this.boundaryDistance / this.config.boundaryFadeDistance);
256
+ }
257
+ /**
258
+ * Should show comfort vignette
259
+ */
260
+ shouldShowVignette() {
261
+ return this.config.comfortVignette && this.config.mode === "smooth";
262
+ }
263
+ getPosition() {
264
+ return { x: this.position[0], y: this.position[1], z: this.position[2] };
265
+ }
266
+ getRotation() {
267
+ return this.rotation;
268
+ }
269
+ getConfig() {
270
+ return { ...this.config };
271
+ }
272
+ setMode(mode) {
273
+ this.config.mode = mode;
274
+ }
275
+ getTeleportHistory() {
276
+ return [...this.teleportHistory];
277
+ }
278
+ };
279
+
280
+ // src/vr/WebXRManager.ts
281
+ var WebXRManager = class {
282
+ session = null;
283
+ referenceSpace = null;
284
+ localReferenceSpace = null;
285
+ glContext = null;
286
+ // Callback for the render loop
287
+ onFrameCallback = null;
288
+ // Callbacks for session state
289
+ onSessionStart = null;
290
+ onSessionEnd = null;
291
+ constructor(context) {
292
+ if (context) {
293
+ this.glContext = context;
294
+ }
295
+ }
296
+ /**
297
+ * Check if VR is supported
298
+ */
299
+ async isSessionSupported() {
300
+ if (typeof navigator !== "undefined" && "xr" in navigator) {
301
+ return navigator.xr.isSessionSupported("immersive-vr");
302
+ }
303
+ return false;
304
+ }
305
+ /**
306
+ * Request an immersive VR session
307
+ */
308
+ async requestSession() {
309
+ if (!this.session) {
310
+ const sessionInit = { optionalFeatures: ["local-floor", "bounded-floor", "hand-tracking"] };
311
+ this.session = await navigator.xr.requestSession("immersive-vr", sessionInit);
312
+ this.session.addEventListener("end", this.onSessionEnded);
313
+ this.referenceSpace = await this.session.requestReferenceSpace("local-floor");
314
+ this.localReferenceSpace = await this.session.requestReferenceSpace("local");
315
+ if (this.onSessionStart) {
316
+ this.onSessionStart(this.session);
317
+ }
318
+ return this.session;
319
+ }
320
+ return this.session;
321
+ }
322
+ /**
323
+ * End the current session
324
+ */
325
+ async endSession() {
326
+ if (this.session) {
327
+ await this.session.end();
328
+ }
329
+ }
330
+ onSessionEnded = () => {
331
+ this.session = null;
332
+ this.referenceSpace = null;
333
+ this.localReferenceSpace = null;
334
+ if (this.onSessionEnd) {
335
+ this.onSessionEnd();
336
+ }
337
+ };
338
+ /**
339
+ * Start the XR render loop
340
+ */
341
+ setAnimationLoop(callback) {
342
+ this.onFrameCallback = callback;
343
+ if (this.session) {
344
+ this.session.requestAnimationFrame(this.onXRFrame);
345
+ }
346
+ }
347
+ onXRFrame = (time, frame) => {
348
+ if (!this.session) return;
349
+ const session = frame.session;
350
+ session.requestAnimationFrame(this.onXRFrame);
351
+ if (this.onFrameCallback) {
352
+ this.onFrameCallback(time, frame);
353
+ }
354
+ };
355
+ /**
356
+ * Get the current reference space (prefer local-floor)
357
+ */
358
+ getReferenceSpace() {
359
+ return this.referenceSpace || this.localReferenceSpace;
360
+ }
361
+ getSession() {
362
+ return this.session;
363
+ }
364
+ };
365
+
366
+ // src/vr/WebXRSystem.ts
367
+ var WebXRSystem = class {
368
+ name = "WebXR";
369
+ priority = 50;
370
+ // Early — before physics needs controller input
371
+ mode = "inactive";
372
+ frameData = null;
373
+ eventListeners = /* @__PURE__ */ new Map();
374
+ supported = false;
375
+ // Stereo camera matrices
376
+ leftEye = null;
377
+ rightEye = null;
378
+ // Controller tracking
379
+ controllers = /* @__PURE__ */ new Map();
380
+ handJoints = /* @__PURE__ */ new Map();
381
+ // Physics integration callbacks
382
+ grabCallbacks = [];
383
+ releaseCallbacks = [];
384
+ // Locomotion
385
+ locomotionSpeed = 3;
386
+ // m/s
387
+ teleportTarget = null;
388
+ playerPosition = { x: 0, y: 0, z: 0 };
389
+ playerRotation = 0;
390
+ // Y-axis radians
391
+ // ---------------------------------------------------------------------------
392
+ // Lifecycle (EngineSystem)
393
+ // ---------------------------------------------------------------------------
394
+ async init() {
395
+ if (typeof navigator !== "undefined" && "xr" in navigator) {
396
+ try {
397
+ this.supported = await navigator.xr.isSessionSupported("immersive-vr");
398
+ } catch {
399
+ this.supported = false;
400
+ }
401
+ }
402
+ }
403
+ update(dt) {
404
+ if (this.mode === "inactive" || !this.frameData) return;
405
+ for (const [id, input] of this.controllers) {
406
+ if (input.grip > 0.8) {
407
+ for (const cb of this.grabCallbacks) cb(id, input.position);
408
+ }
409
+ if (id.includes("left") && (Math.abs(input.thumbstick.x) > 0.1 || Math.abs(input.thumbstick.y) > 0.1)) {
410
+ const sinR = Math.sin(this.playerRotation);
411
+ const cosR = Math.cos(this.playerRotation);
412
+ this.playerPosition.x += (input.thumbstick.x * cosR - input.thumbstick.y * sinR) * this.locomotionSpeed * dt;
413
+ this.playerPosition.z += (input.thumbstick.x * sinR + input.thumbstick.y * cosR) * this.locomotionSpeed * dt;
414
+ }
415
+ if (id.includes("right") && Math.abs(input.thumbstick.x) > 0.6) {
416
+ this.playerRotation += Math.sign(input.thumbstick.x) * (Math.PI / 6);
417
+ }
418
+ }
419
+ if (this.teleportTarget) {
420
+ this.playerPosition = { ...this.teleportTarget };
421
+ this.teleportTarget = null;
422
+ }
423
+ }
424
+ destroy() {
425
+ this.mode = "inactive";
426
+ this.frameData = null;
427
+ this.controllers.clear();
428
+ this.handJoints.clear();
429
+ this.eventListeners.clear();
430
+ }
431
+ // ---------------------------------------------------------------------------
432
+ // Session Management
433
+ // ---------------------------------------------------------------------------
434
+ isSupported() {
435
+ return this.supported;
436
+ }
437
+ getMode() {
438
+ return this.mode;
439
+ }
440
+ /** Simulate entering VR mode (for testing without real WebXR). */
441
+ enterVR() {
442
+ this.mode = "immersive-vr";
443
+ this.emit("sessionstart");
444
+ }
445
+ /** Simulate entering AR mode. */
446
+ enterAR() {
447
+ this.mode = "immersive-ar";
448
+ this.emit("sessionstart");
449
+ }
450
+ /** Exit immersive mode. */
451
+ exit() {
452
+ const wasActive = this.mode !== "inactive";
453
+ this.mode = "inactive";
454
+ this.frameData = null;
455
+ if (wasActive) this.emit("sessionend");
456
+ }
457
+ // ---------------------------------------------------------------------------
458
+ // Frame Data Injection (from actual WebXR or mock)
459
+ // ---------------------------------------------------------------------------
460
+ /**
461
+ * Submit frame data from the XR runtime.
462
+ * In production, this is called from the XR animation frame callback.
463
+ * In tests, this can be called manually with mock data.
464
+ */
465
+ submitFrame(data) {
466
+ this.frameData = data;
467
+ this.controllers = data.controllers;
468
+ this.handJoints = data.hands;
469
+ this.leftEye = data.views.find((v) => v.eye === "left") ?? null;
470
+ this.rightEye = data.views.find((v) => v.eye === "right") ?? null;
471
+ }
472
+ // ---------------------------------------------------------------------------
473
+ // Controller / Hand Queries
474
+ // ---------------------------------------------------------------------------
475
+ getController(id) {
476
+ return this.controllers.get(id);
477
+ }
478
+ getAllControllers() {
479
+ return new Map(this.controllers);
480
+ }
481
+ getHandJoints(side) {
482
+ return this.handJoints.get(side);
483
+ }
484
+ getHeadPosition() {
485
+ return this.frameData?.headPosition ?? { x: 0, y: 0, z: 0 };
486
+ }
487
+ getHeadRotation() {
488
+ return this.frameData?.headRotation ?? { x: 0, y: 0, z: 0 };
489
+ }
490
+ // ---------------------------------------------------------------------------
491
+ // Stereo Rendering
492
+ // ---------------------------------------------------------------------------
493
+ getLeftEye() {
494
+ return this.leftEye;
495
+ }
496
+ getRightEye() {
497
+ return this.rightEye;
498
+ }
499
+ getStereoViews() {
500
+ const views = [];
501
+ if (this.leftEye) views.push(this.leftEye);
502
+ if (this.rightEye) views.push(this.rightEye);
503
+ return views;
504
+ }
505
+ // ---------------------------------------------------------------------------
506
+ // Locomotion
507
+ // ---------------------------------------------------------------------------
508
+ getPlayerPosition() {
509
+ return { ...this.playerPosition };
510
+ }
511
+ getPlayerRotation() {
512
+ return this.playerRotation;
513
+ }
514
+ setLocomotionSpeed(speed) {
515
+ this.locomotionSpeed = speed;
516
+ }
517
+ /** Queue a teleport to a world position. Executes next frame. */
518
+ teleportTo(target) {
519
+ this.teleportTarget = { ...target };
520
+ }
521
+ // ---------------------------------------------------------------------------
522
+ // Physics Integration Callbacks
523
+ // ---------------------------------------------------------------------------
524
+ onGrab(cb) {
525
+ this.grabCallbacks.push(cb);
526
+ }
527
+ onRelease(cb) {
528
+ this.releaseCallbacks.push(cb);
529
+ }
530
+ // ---------------------------------------------------------------------------
531
+ // Events
532
+ // ---------------------------------------------------------------------------
533
+ on(type, callback) {
534
+ if (!this.eventListeners.has(type)) this.eventListeners.set(type, []);
535
+ this.eventListeners.get(type).push(callback);
536
+ }
537
+ off(type, callback) {
538
+ const list = this.eventListeners.get(type);
539
+ if (list) {
540
+ const idx = list.indexOf(callback);
541
+ if (idx >= 0) list.splice(idx, 1);
542
+ }
543
+ }
544
+ emit(type, inputSource) {
545
+ const listeners = this.eventListeners.get(type);
546
+ if (listeners) {
547
+ const event = { type, inputSource, time: performance.now() };
548
+ for (const cb of listeners) cb(event);
549
+ }
550
+ }
551
+ };
552
+
553
+ export {
554
+ HandTracker,
555
+ HapticFeedback,
556
+ VRLocomotion,
557
+ WebXRManager,
558
+ WebXRSystem,
559
+ vr_exports
560
+ };