@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,928 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-AKLW2MUS.js";
4
+
5
+ // src/camera/index.ts
6
+ var camera_exports = {};
7
+ __export(camera_exports, {
8
+ CameraConstraints: () => CameraConstraints,
9
+ CameraController: () => CameraController,
10
+ CameraEffects: () => CameraEffects,
11
+ CameraPath: () => CameraPath,
12
+ CameraShake: () => CameraShake,
13
+ CinematicTrack: () => CinematicTrack
14
+ });
15
+
16
+ // src/camera/CameraConstraints.ts
17
+ var CameraConstraints = class {
18
+ bounds = null;
19
+ deadZone = null;
20
+ softLimit = null;
21
+ smoothing = 0.1;
22
+ // Lerp factor (0 = no movement, 1 = instant)
23
+ lookAheadDistance = 0;
24
+ currentX = 0;
25
+ currentY = 0;
26
+ // ---------------------------------------------------------------------------
27
+ // Configuration
28
+ // ---------------------------------------------------------------------------
29
+ setBounds(bounds) {
30
+ this.bounds = bounds;
31
+ }
32
+ setDeadZone(dz) {
33
+ this.deadZone = dz;
34
+ }
35
+ setSoftLimit(sl) {
36
+ this.softLimit = sl;
37
+ }
38
+ setSmoothing(factor) {
39
+ this.smoothing = Math.max(0, Math.min(1, factor));
40
+ }
41
+ setLookAhead(distance) {
42
+ this.lookAheadDistance = distance;
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // Update
46
+ // ---------------------------------------------------------------------------
47
+ follow(targetX, targetY, velocityX = 0, velocityY = 0) {
48
+ let desiredX = targetX;
49
+ let desiredY = targetY;
50
+ if (this.lookAheadDistance > 0) {
51
+ const speed = Math.sqrt(velocityX * velocityX + velocityY * velocityY) || 1;
52
+ desiredX += velocityX / speed * this.lookAheadDistance;
53
+ desiredY += velocityY / speed * this.lookAheadDistance;
54
+ }
55
+ if (this.deadZone) {
56
+ const dx = desiredX - this.currentX;
57
+ const dy = desiredY - this.currentY;
58
+ const halfW = this.deadZone.width / 2;
59
+ const halfH = this.deadZone.height / 2;
60
+ if (Math.abs(dx) < halfW) desiredX = this.currentX;
61
+ else desiredX = this.currentX + (dx > 0 ? dx - halfW : dx + halfW);
62
+ if (Math.abs(dy) < halfH) desiredY = this.currentY;
63
+ else desiredY = this.currentY + (dy > 0 ? dy - halfH : dy + halfH);
64
+ }
65
+ this.currentX += (desiredX - this.currentX) * this.smoothing;
66
+ this.currentY += (desiredY - this.currentY) * this.smoothing;
67
+ if (this.softLimit && this.bounds) {
68
+ this.applySoftLimit();
69
+ }
70
+ if (this.bounds) {
71
+ this.currentX = Math.max(this.bounds.minX, Math.min(this.bounds.maxX, this.currentX));
72
+ this.currentY = Math.max(this.bounds.minY, Math.min(this.bounds.maxY, this.currentY));
73
+ }
74
+ return { x: this.currentX, y: this.currentY };
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Soft Limits
78
+ // ---------------------------------------------------------------------------
79
+ applySoftLimit() {
80
+ if (!this.softLimit || !this.bounds) return;
81
+ const sl = this.softLimit;
82
+ if (this.currentX < this.bounds.minX + sl.distance) {
83
+ const over = this.bounds.minX + sl.distance - this.currentX;
84
+ this.currentX += over * sl.stiffness;
85
+ }
86
+ if (this.currentX > this.bounds.maxX - sl.distance) {
87
+ const over = this.currentX - (this.bounds.maxX - sl.distance);
88
+ this.currentX -= over * sl.stiffness;
89
+ }
90
+ if (this.currentY < this.bounds.minY + sl.distance) {
91
+ const over = this.bounds.minY + sl.distance - this.currentY;
92
+ this.currentY += over * sl.stiffness;
93
+ }
94
+ if (this.currentY > this.bounds.maxY - sl.distance) {
95
+ const over = this.currentY - (this.bounds.maxY - sl.distance);
96
+ this.currentY -= over * sl.stiffness;
97
+ }
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // Queries
101
+ // ---------------------------------------------------------------------------
102
+ getPosition() {
103
+ return { x: this.currentX, y: this.currentY };
104
+ }
105
+ setPosition(x, y) {
106
+ this.currentX = x;
107
+ this.currentY = y;
108
+ }
109
+ };
110
+
111
+ // src/camera/CameraController.ts
112
+ var DEFAULT_CAMERA = {
113
+ mode: "follow",
114
+ smoothing: 0.1,
115
+ followOffset: { x: 0, y: 5, z: -10 },
116
+ orbitDistance: 10,
117
+ orbitMinDistance: 2,
118
+ orbitMaxDistance: 50,
119
+ orbitSpeed: 2,
120
+ zoomSpeed: 1,
121
+ minZoom: 0.5,
122
+ maxZoom: 5,
123
+ deadZone: { x: 0, y: 0 },
124
+ bounds: null,
125
+ fov: 60,
126
+ freeSpeed: 10
127
+ };
128
+ var CameraController = class {
129
+ config;
130
+ state;
131
+ target = { x: 0, y: 0, z: 0 };
132
+ orbitAngle = 0;
133
+ orbitPitch = 0.3;
134
+ /**
135
+ * Creates a new camera controller with optional configuration.
136
+ *
137
+ * @param config - Optional camera configuration overrides. Merged with defaults.
138
+ * @example
139
+ * ```typescript
140
+ * const camera = new CameraController({
141
+ * mode: 'follow',
142
+ * smoothing: 0.2,
143
+ * followOffset: { x: 0, y: 3, z: -8 }
144
+ * });
145
+ * ```
146
+ */
147
+ constructor(config) {
148
+ this.config = { ...DEFAULT_CAMERA, ...config };
149
+ this.state = {
150
+ position: [0, 5, -10],
151
+ rotation: { pitch: 0, yaw: 0, roll: 0 },
152
+ zoom: 1,
153
+ fov: this.config.fov
154
+ };
155
+ }
156
+ // ---------------------------------------------------------------------------
157
+ // Update
158
+ // ---------------------------------------------------------------------------
159
+ /**
160
+ * Updates camera position and rotation based on current mode and delta time.
161
+ *
162
+ * Call this every frame to animate camera movement. Different modes handle
163
+ * updates differently:
164
+ * - **follow**: Smoothly interpolates toward target + offset
165
+ * - **orbit**: Updates position based on orbit angle and pitch
166
+ * - **topDown**: Maintains overhead view with smooth target tracking
167
+ * - **free/fixed**: No automatic movement (controlled externally)
168
+ *
169
+ * @param dt - Delta time in seconds since last update
170
+ * @example
171
+ * ```typescript
172
+ * // In game loop
173
+ * const deltaTime = (now - lastTime) / 1000;
174
+ * camera.update(deltaTime);
175
+ * ```
176
+ */
177
+ update(dt) {
178
+ switch (this.config.mode) {
179
+ case "follow":
180
+ this.updateFollow(dt);
181
+ break;
182
+ case "orbit":
183
+ this.updateOrbit(dt);
184
+ break;
185
+ case "free":
186
+ break;
187
+ // Controlled externally via moveCamera
188
+ case "topDown":
189
+ this.updateTopDown(dt);
190
+ break;
191
+ case "fixed":
192
+ break;
193
+ }
194
+ if (this.config.bounds) this.clampToBounds();
195
+ }
196
+ updateFollow(dt) {
197
+ const off = this.config.followOffset;
198
+ const desiredX = this.target.x + off.x;
199
+ const desiredY = this.target.y + off.y;
200
+ const desiredZ = this.target.z + off.z;
201
+ const dx = Math.abs(this.target.x - this.state.position.x + off.x);
202
+ const dy = Math.abs(this.target.y - this.state.position.y + off.y);
203
+ const dz = this.config.deadZone;
204
+ if (dx < dz.x && dy < dz.y) return;
205
+ const s = 1 - Math.pow(1 - this.config.smoothing, dt * 60);
206
+ this.state.position.x = this.lerp(this.state.position.x, desiredX, s);
207
+ this.state.position.y = this.lerp(this.state.position.y, desiredY, s);
208
+ this.state.position.z = this.lerp(this.state.position.z, desiredZ, s);
209
+ }
210
+ updateOrbit(_dt) {
211
+ const d = this.config.orbitDistance * this.state.zoom;
212
+ this.state.position.x = this.target.x + Math.sin(this.orbitAngle) * Math.cos(this.orbitPitch) * d;
213
+ this.state.position.y = this.target.y + Math.sin(this.orbitPitch) * d;
214
+ this.state.position.z = this.target.z + Math.cos(this.orbitAngle) * Math.cos(this.orbitPitch) * d;
215
+ this.state.rotation.yaw = this.orbitAngle;
216
+ this.state.rotation.pitch = -this.orbitPitch;
217
+ }
218
+ updateTopDown(_dt) {
219
+ const height = 20 * this.state.zoom;
220
+ const s = 1 - Math.pow(1 - this.config.smoothing, 1);
221
+ this.state.position.x = this.lerp(this.state.position.x, this.target.x, s);
222
+ this.state.position.y = height;
223
+ this.state.position.z = this.lerp(this.state.position.z, this.target.z, s);
224
+ this.state.rotation.pitch = -Math.PI / 2;
225
+ this.state.rotation.yaw = 0;
226
+ }
227
+ clampToBounds() {
228
+ const b = this.config.bounds;
229
+ this.state.position.x = Math.max(b.min.x, Math.min(b.max.x, this.state.position.x));
230
+ this.state.position.y = Math.max(b.min.y, Math.min(b.max.y, this.state.position.y));
231
+ this.state.position.z = Math.max(b.min.z, Math.min(b.max.z, this.state.position.z));
232
+ }
233
+ // ---------------------------------------------------------------------------
234
+ // Controls
235
+ // ---------------------------------------------------------------------------
236
+ /**
237
+ * Sets the target point that the camera should focus on or track.
238
+ *
239
+ * Used by 'follow', 'orbit', and 'topDown' modes. In 'follow' mode, camera
240
+ * maintains offset from this target. In 'orbit' mode, camera rotates around
241
+ * this point. In 'topDown' mode, camera looks down at this location.
242
+ *
243
+ * @param x - Target X coordinate in world space
244
+ * @param y - Target Y coordinate in world space
245
+ * @param z - Target Z coordinate in world space
246
+ * @example
247
+ * ```typescript
248
+ * // Track a moving player
249
+ * camera.setTarget(player.x, player.y, player.z);
250
+ * ```
251
+ */
252
+ setTarget(x, y, z) {
253
+ this.target = { x, y, z };
254
+ }
255
+ /**
256
+ * Gets the current target position.
257
+ *
258
+ * @returns Copy of the current target coordinates
259
+ */
260
+ getTarget() {
261
+ return { ...this.target };
262
+ }
263
+ /**
264
+ * Rotates the camera in orbit mode by the specified angles.
265
+ *
266
+ * Only affects camera behavior when mode is set to 'orbit'. Angles are
267
+ * clamped to prevent the camera from flipping or going too extreme.
268
+ *
269
+ * @param deltaAngle - Horizontal rotation change (yaw) in radians
270
+ * @param deltaPitch - Vertical rotation change (pitch) in radians, clamped to [-1.4, 1.4]
271
+ * @example
272
+ * ```typescript
273
+ * // Rotate based on mouse movement
274
+ * camera.rotateOrbit(mouseX * 0.01, mouseY * 0.01);
275
+ * ```
276
+ */
277
+ rotateOrbit(deltaAngle, deltaPitch) {
278
+ this.orbitAngle += deltaAngle * this.config.orbitSpeed;
279
+ this.orbitPitch = Math.max(
280
+ -1.4,
281
+ Math.min(1.4, this.orbitPitch + deltaPitch * this.config.orbitSpeed)
282
+ );
283
+ }
284
+ /**
285
+ * Adjusts camera zoom by the specified delta amount.
286
+ *
287
+ * Zoom affects distance in 'orbit' mode, height in 'topDown' mode, and FOV scaling.
288
+ * Value is clamped between configured minZoom and maxZoom limits.
289
+ *
290
+ * @param delta - Amount to change zoom by (positive = zoom in, negative = zoom out)
291
+ * @example
292
+ * ```typescript
293
+ * // Zoom in on mouse wheel
294
+ * camera.zoom(-wheelDelta * 0.1);
295
+ * ```
296
+ */
297
+ zoom(delta) {
298
+ this.state.zoom = Math.max(
299
+ this.config.minZoom,
300
+ Math.min(this.config.maxZoom, this.state.zoom + delta * this.config.zoomSpeed)
301
+ );
302
+ }
303
+ /**
304
+ * Directly moves camera position by specified amounts.
305
+ *
306
+ * Primarily used in 'free' camera mode for manual camera control.
307
+ * Movement is scaled by the configured freeSpeed multiplier.
308
+ *
309
+ * @param dx - Change in X position (world units)
310
+ * @param dy - Change in Y position (world units)
311
+ * @param dz - Change in Z position (world units)
312
+ * @example
313
+ * ```typescript
314
+ * // WASD movement in free camera mode
315
+ * if (wPressed) camera.moveCamera(0, 0, 1);
316
+ * if (sPressed) camera.moveCamera(0, 0, -1);
317
+ * ```
318
+ */
319
+ moveCamera(dx, dy, dz) {
320
+ this.state.position.x += dx * this.config.freeSpeed;
321
+ this.state.position.y += dy * this.config.freeSpeed;
322
+ this.state.position.z += dz * this.config.freeSpeed;
323
+ }
324
+ /**
325
+ * Changes the camera's behavior mode.
326
+ *
327
+ * @param mode - New camera mode ('follow' | 'orbit' | 'free' | 'topDown' | 'fixed')
328
+ * @example
329
+ * ```typescript
330
+ * camera.setMode('orbit'); // Switch to orbital camera
331
+ * ```
332
+ */
333
+ setMode(mode) {
334
+ this.config.mode = mode;
335
+ }
336
+ /**
337
+ * Gets the current camera mode.
338
+ *
339
+ * @returns Current camera mode
340
+ */
341
+ getMode() {
342
+ return this.config.mode;
343
+ }
344
+ // ---------------------------------------------------------------------------
345
+ // State
346
+ // ---------------------------------------------------------------------------
347
+ /**
348
+ * Gets a copy of the current camera state.
349
+ *
350
+ * Returns position, rotation, zoom, and FOV values. All objects are cloned
351
+ * to prevent external modification of internal state.
352
+ *
353
+ * @returns Current camera state with position, rotation, zoom, and FOV
354
+ * @example
355
+ * ```typescript
356
+ * const state = camera.getState();
357
+ * renderer.setCamera(state.position, state.rotation);
358
+ * renderer.setFOV(state.fov);
359
+ * ```
360
+ */
361
+ getState() {
362
+ return {
363
+ position: { ...this.state.position },
364
+ rotation: { ...this.state.rotation },
365
+ zoom: this.state.zoom,
366
+ fov: this.state.fov
367
+ };
368
+ }
369
+ /**
370
+ * Directly sets the camera zoom level.
371
+ *
372
+ * Unlike zoom(), this sets an absolute value rather than a delta.
373
+ * Value is clamped to configured min/max zoom limits.
374
+ *
375
+ * @param z - New zoom level (clamped to minZoom/maxZoom)
376
+ * @example
377
+ * ```typescript
378
+ * camera.setZoom(1.0); // Reset to default zoom
379
+ * ```
380
+ */
381
+ setZoom(z) {
382
+ this.state.zoom = Math.max(this.config.minZoom, Math.min(this.config.maxZoom, z));
383
+ }
384
+ /**
385
+ * Sets the camera's field of view.
386
+ *
387
+ * @param fov - New field of view in degrees
388
+ * @example
389
+ * ```typescript
390
+ * camera.setFOV(90); // Wide angle
391
+ * camera.setFOV(30); // Telephoto
392
+ * ```
393
+ */
394
+ setFOV(fov) {
395
+ this.state.fov = fov;
396
+ }
397
+ /**
398
+ * Adjusts camera smoothing factor for interpolated movement modes.
399
+ *
400
+ * Affects 'follow' and 'topDown' modes. Higher values = more responsive,
401
+ * lower values = smoother but more delayed movement.
402
+ *
403
+ * @param s - Smoothing factor between 0.0 (no movement) and 1.0 (instant)
404
+ * @example
405
+ * ```typescript
406
+ * camera.setSmoothing(0.1); // Very smooth
407
+ * camera.setSmoothing(0.8); // Very responsive
408
+ * ```
409
+ */
410
+ setSmoothing(s) {
411
+ this.config.smoothing = Math.max(0, Math.min(1, s));
412
+ }
413
+ // ---------------------------------------------------------------------------
414
+ // Helpers
415
+ // ---------------------------------------------------------------------------
416
+ lerp(a, b, t) {
417
+ return a + (b - a) * t;
418
+ }
419
+ };
420
+
421
+ // src/camera/CameraEffects.ts
422
+ var _effectId = 0;
423
+ var CameraEffects = class {
424
+ effects = [];
425
+ shakeOffset = { x: 0, y: 0 };
426
+ zoomMultiplier = 1;
427
+ flashAlpha = 0;
428
+ flashColor = { r: 1, g: 1, b: 1 };
429
+ letterboxAmount = 0;
430
+ vignetteIntensity = 0;
431
+ fadeAlpha = 0;
432
+ // ---------------------------------------------------------------------------
433
+ // Effect Triggers
434
+ // ---------------------------------------------------------------------------
435
+ shake(duration, intensity = 5, frequency = 20, decay = 1) {
436
+ return this.addEffect("shake", duration, intensity, { frequency, decay });
437
+ }
438
+ zoomPulse(duration, targetZoom = 1.2, easeBack = true) {
439
+ return this.addEffect("zoom", duration, targetZoom, { targetZoom, easeBack });
440
+ }
441
+ flash(duration = 0.1, color = { r: 1, g: 1, b: 1 }, intensity = 1) {
442
+ return this.addEffect("flash", duration, intensity, { color });
443
+ }
444
+ letterbox(duration, ratio = 2.35) {
445
+ return this.addEffect("letterbox", duration, ratio, { ratio });
446
+ }
447
+ vignette(duration, intensity = 0.5) {
448
+ return this.addEffect("vignette", duration, intensity, {});
449
+ }
450
+ fade(duration, fadeIn = false, color = { r: 0, g: 0, b: 0 }) {
451
+ return this.addEffect("fade", duration, 1, { fadeIn, color });
452
+ }
453
+ addEffect(type, duration, intensity, params) {
454
+ const id = `fx_${_effectId++}`;
455
+ this.effects.push({ id, type, duration, elapsed: 0, intensity, params, active: true });
456
+ return id;
457
+ }
458
+ // ---------------------------------------------------------------------------
459
+ // Update
460
+ // ---------------------------------------------------------------------------
461
+ update(dt) {
462
+ this.shakeOffset = { x: 0, y: 0 };
463
+ this.zoomMultiplier = 1;
464
+ this.flashAlpha = 0;
465
+ this.vignetteIntensity = 0;
466
+ for (const effect of this.effects) {
467
+ if (!effect.active) continue;
468
+ effect.elapsed += dt;
469
+ const t = Math.min(effect.elapsed / effect.duration, 1);
470
+ switch (effect.type) {
471
+ case "shake": {
472
+ const p = effect.params;
473
+ const decay = 1 - t * p.decay;
474
+ if (decay > 0) {
475
+ this.shakeOffset.x += (Math.random() - 0.5) * 2 * effect.intensity * decay;
476
+ this.shakeOffset.y += (Math.random() - 0.5) * 2 * effect.intensity * decay;
477
+ }
478
+ break;
479
+ }
480
+ case "zoom": {
481
+ const p = effect.params;
482
+ if (p.easeBack) {
483
+ const curve = t < 0.5 ? t * 2 : (1 - t) * 2;
484
+ this.zoomMultiplier *= 1 + (p.targetZoom - 1) * curve;
485
+ } else {
486
+ this.zoomMultiplier *= 1 + (p.targetZoom - 1) * (1 - t);
487
+ }
488
+ break;
489
+ }
490
+ case "flash": {
491
+ const p = effect.params;
492
+ this.flashAlpha = Math.max(this.flashAlpha, effect.intensity * (1 - t));
493
+ this.flashColor = p.color;
494
+ break;
495
+ }
496
+ case "letterbox": {
497
+ const p = effect.params;
498
+ this.letterboxAmount = Math.max(this.letterboxAmount, p.ratio * Math.min(t * 4, 1));
499
+ break;
500
+ }
501
+ case "vignette":
502
+ this.vignetteIntensity = Math.max(
503
+ this.vignetteIntensity,
504
+ effect.intensity * Math.min(t * 3, 1)
505
+ );
506
+ break;
507
+ case "fade": {
508
+ const p = effect.params;
509
+ this.fadeAlpha = p.fadeIn ? 1 - t : t;
510
+ break;
511
+ }
512
+ }
513
+ if (t >= 1) effect.active = false;
514
+ }
515
+ this.effects = this.effects.filter((e) => e.active);
516
+ }
517
+ // ---------------------------------------------------------------------------
518
+ // Queries
519
+ // ---------------------------------------------------------------------------
520
+ getShakeOffset() {
521
+ return { ...this.shakeOffset };
522
+ }
523
+ getZoomMultiplier() {
524
+ return this.zoomMultiplier;
525
+ }
526
+ getFlashAlpha() {
527
+ return this.flashAlpha;
528
+ }
529
+ getFlashColor() {
530
+ return { ...this.flashColor };
531
+ }
532
+ getLetterboxAmount() {
533
+ return this.letterboxAmount;
534
+ }
535
+ getVignetteIntensity() {
536
+ return this.vignetteIntensity;
537
+ }
538
+ getFadeAlpha() {
539
+ return this.fadeAlpha;
540
+ }
541
+ getActiveEffectCount() {
542
+ return this.effects.length;
543
+ }
544
+ cancelEffect(id) {
545
+ const idx = this.effects.findIndex((e) => e.id === id);
546
+ if (idx < 0) return false;
547
+ this.effects.splice(idx, 1);
548
+ return true;
549
+ }
550
+ cancelAll() {
551
+ this.effects = [];
552
+ }
553
+ };
554
+
555
+ // src/camera/CameraPath.ts
556
+ var CameraPath = class {
557
+ points = [];
558
+ progress = 0;
559
+ // 0-1
560
+ speed = 1;
561
+ // Units per second
562
+ loop = false;
563
+ playing = false;
564
+ // ---------------------------------------------------------------------------
565
+ // Path Setup
566
+ // ---------------------------------------------------------------------------
567
+ setPoints(points) {
568
+ this.points = [...points];
569
+ }
570
+ addPoint(point) {
571
+ this.points.push(point);
572
+ }
573
+ clearPoints() {
574
+ this.points = [];
575
+ this.progress = 0;
576
+ }
577
+ setSpeed(speed) {
578
+ this.speed = speed;
579
+ }
580
+ setLoop(loop) {
581
+ this.loop = loop;
582
+ }
583
+ // ---------------------------------------------------------------------------
584
+ // Playback
585
+ // ---------------------------------------------------------------------------
586
+ play() {
587
+ this.playing = true;
588
+ this.progress = 0;
589
+ }
590
+ pause() {
591
+ this.playing = false;
592
+ }
593
+ resume() {
594
+ this.playing = true;
595
+ }
596
+ stop() {
597
+ this.playing = false;
598
+ this.progress = 0;
599
+ }
600
+ update(dt) {
601
+ if (!this.playing || this.points.length < 2) return null;
602
+ const segIndex = Math.min(
603
+ Math.floor(this.progress * (this.points.length - 1)),
604
+ this.points.length - 2
605
+ );
606
+ const speedMul = this.points[segIndex]?.speedMultiplier ?? 1;
607
+ const totalLength = this.points.length - 1;
608
+ this.progress += dt * this.speed * speedMul / totalLength;
609
+ if (this.progress >= 1) {
610
+ if (this.loop) {
611
+ this.progress %= 1;
612
+ } else {
613
+ this.progress = 1;
614
+ this.playing = false;
615
+ }
616
+ }
617
+ return this.evaluate(this.progress);
618
+ }
619
+ // ---------------------------------------------------------------------------
620
+ // Catmull-Rom Evaluation
621
+ // ---------------------------------------------------------------------------
622
+ evaluate(t) {
623
+ const n = this.points.length;
624
+ if (n === 0) return { position: [0, 0, 0], lookAt: null };
625
+ if (n === 1) return { position: { ...this.points[0] }, lookAt: null };
626
+ const f = t * (n - 1);
627
+ const i = Math.min(Math.floor(f), n - 2);
628
+ const frac = f - i;
629
+ const p0 = this.points[Math.max(0, i - 1)];
630
+ const p1 = this.points[i];
631
+ const p2 = this.points[Math.min(n - 1, i + 1)];
632
+ const p3 = this.points[Math.min(n - 1, i + 2)];
633
+ const position = {
634
+ x: this.catmullRom(p0.x, p1.x, p2.x, p3.x, frac),
635
+ y: this.catmullRom(p0.y, p1.y, p2.y, p3.y, frac),
636
+ z: this.catmullRom(p0.z, p1.z, p2.z, p3.z, frac)
637
+ };
638
+ let lookAt = null;
639
+ if (p1.lookAtX !== void 0 && p2.lookAtX !== void 0) {
640
+ lookAt = {
641
+ x: p1.lookAtX + (p2.lookAtX - p1.lookAtX) * frac,
642
+ y: (p1.lookAtY ?? 0) + ((p2.lookAtY ?? 0) - (p1.lookAtY ?? 0)) * frac,
643
+ z: (p1.lookAtZ ?? 0) + ((p2.lookAtZ ?? 0) - (p1.lookAtZ ?? 0)) * frac
644
+ };
645
+ }
646
+ return { position, lookAt };
647
+ }
648
+ catmullRom(p0, p1, p2, p3, t) {
649
+ const t2 = t * t, t3 = t2 * t;
650
+ return 0.5 * (2 * p1 + (-p0 + p2) * t + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 + (-p0 + 3 * p1 - 3 * p2 + p3) * t3);
651
+ }
652
+ // ---------------------------------------------------------------------------
653
+ // Queries
654
+ // ---------------------------------------------------------------------------
655
+ getProgress() {
656
+ return this.progress;
657
+ }
658
+ isPlaying() {
659
+ return this.playing;
660
+ }
661
+ getPointCount() {
662
+ return this.points.length;
663
+ }
664
+ };
665
+
666
+ // src/camera/CameraShake.ts
667
+ var CameraShake = class {
668
+ layers = /* @__PURE__ */ new Map();
669
+ time = 0;
670
+ // ---------------------------------------------------------------------------
671
+ // Layer Management
672
+ // ---------------------------------------------------------------------------
673
+ addLayer(id, config) {
674
+ this.layers.set(id, {
675
+ id,
676
+ trauma: 0,
677
+ decay: 1,
678
+ frequency: 15,
679
+ amplitude: 10,
680
+ rotationAmount: 0.05,
681
+ ...config
682
+ });
683
+ }
684
+ removeLayer(id) {
685
+ this.layers.delete(id);
686
+ }
687
+ // ---------------------------------------------------------------------------
688
+ // Trauma
689
+ // ---------------------------------------------------------------------------
690
+ addTrauma(layerId, amount) {
691
+ const layer = this.layers.get(layerId);
692
+ if (layer) layer.trauma = Math.min(1, layer.trauma + amount);
693
+ }
694
+ setTrauma(layerId, amount) {
695
+ const layer = this.layers.get(layerId);
696
+ if (layer) layer.trauma = Math.max(0, Math.min(1, amount));
697
+ }
698
+ // ---------------------------------------------------------------------------
699
+ // Update
700
+ // ---------------------------------------------------------------------------
701
+ update(dt) {
702
+ this.time += dt;
703
+ let totalX = 0, totalY = 0, totalRot = 0;
704
+ for (const layer of this.layers.values()) {
705
+ if (layer.trauma <= 0) continue;
706
+ const shake = layer.trauma * layer.trauma;
707
+ const t = this.time * layer.frequency;
708
+ totalX += shake * layer.amplitude * this.noise(t, 0);
709
+ totalY += shake * layer.amplitude * this.noise(t, 100);
710
+ totalRot += shake * layer.rotationAmount * this.noise(t, 200);
711
+ layer.trauma = Math.max(0, layer.trauma - layer.decay * dt);
712
+ }
713
+ return { offsetX: totalX, offsetY: totalY, rotation: totalRot };
714
+ }
715
+ // ---------------------------------------------------------------------------
716
+ // Noise (simplified)
717
+ // ---------------------------------------------------------------------------
718
+ noise(x, seed) {
719
+ const n = Math.sin((x + seed) * 12.9898) * 43758.5453;
720
+ return (n - Math.floor(n)) * 2 - 1;
721
+ }
722
+ // ---------------------------------------------------------------------------
723
+ // Queries
724
+ // ---------------------------------------------------------------------------
725
+ getTrauma(layerId) {
726
+ return this.layers.get(layerId)?.trauma ?? 0;
727
+ }
728
+ isShaking() {
729
+ return [...this.layers.values()].some((l) => l.trauma > 0);
730
+ }
731
+ };
732
+
733
+ // src/camera/CinematicTrack.ts
734
+ var CinematicTrack = class {
735
+ keyframes = [];
736
+ cues = [];
737
+ currentTime = 0;
738
+ duration = 0;
739
+ playing = false;
740
+ looping = false;
741
+ speed = 1;
742
+ cueListeners = [];
743
+ // ---------------------------------------------------------------------------
744
+ // Keyframe Management
745
+ // ---------------------------------------------------------------------------
746
+ addKeyframe(keyframe) {
747
+ this.keyframes.push(keyframe);
748
+ this.keyframes.sort((a, b) => a.time - b.time);
749
+ this.duration = Math.max(this.duration, keyframe.time);
750
+ }
751
+ addCue(time, event, data = {}) {
752
+ this.cues.push({ time, event, data, fired: false });
753
+ this.cues.sort((a, b) => a.time - b.time);
754
+ }
755
+ removeKeyframesAt(time) {
756
+ const before = this.keyframes.length;
757
+ this.keyframes = this.keyframes.filter((k) => Math.abs(k.time - time) > 1e-3);
758
+ this.recalcDuration();
759
+ return before - this.keyframes.length;
760
+ }
761
+ // ---------------------------------------------------------------------------
762
+ // Playback
763
+ // ---------------------------------------------------------------------------
764
+ play() {
765
+ this.playing = true;
766
+ }
767
+ pause() {
768
+ this.playing = false;
769
+ }
770
+ stop() {
771
+ this.playing = false;
772
+ this.currentTime = 0;
773
+ this.resetCues();
774
+ }
775
+ seek(time) {
776
+ this.currentTime = Math.max(0, Math.min(time, this.duration));
777
+ }
778
+ setSpeed(speed) {
779
+ this.speed = speed;
780
+ }
781
+ setLooping(loop) {
782
+ this.looping = loop;
783
+ }
784
+ isPlaying() {
785
+ return this.playing;
786
+ }
787
+ getCurrentTime() {
788
+ return this.currentTime;
789
+ }
790
+ getDuration() {
791
+ return this.duration;
792
+ }
793
+ getProgress() {
794
+ return this.duration > 0 ? this.currentTime / this.duration : 0;
795
+ }
796
+ // ---------------------------------------------------------------------------
797
+ // Update
798
+ // ---------------------------------------------------------------------------
799
+ update(dt) {
800
+ if (!this.playing || this.keyframes.length === 0) return null;
801
+ this.currentTime += dt * this.speed;
802
+ for (const cue of this.cues) {
803
+ if (!cue.fired && this.currentTime >= cue.time) {
804
+ cue.fired = true;
805
+ for (const listener of this.cueListeners) listener(cue.event, cue.data);
806
+ }
807
+ }
808
+ if (this.currentTime >= this.duration) {
809
+ if (this.looping) {
810
+ this.currentTime %= this.duration;
811
+ this.resetCues();
812
+ } else {
813
+ this.currentTime = this.duration;
814
+ this.playing = false;
815
+ }
816
+ }
817
+ return this.evaluate(this.currentTime);
818
+ }
819
+ // ---------------------------------------------------------------------------
820
+ // Evaluation
821
+ // ---------------------------------------------------------------------------
822
+ evaluate(time) {
823
+ const state = {
824
+ position: [0, 0, 0],
825
+ rotation: { pitch: 0, yaw: 0, roll: 0 },
826
+ fov: 60,
827
+ zoom: 1
828
+ };
829
+ if (this.keyframes.length === 0) return state;
830
+ if (this.keyframes.length === 1) {
831
+ const k = this.keyframes[0];
832
+ if (k.position) state.position = { ...k.position };
833
+ if (k.rotation) state.rotation = { ...k.rotation };
834
+ if (k.fov !== void 0) state.fov = k.fov;
835
+ if (k.zoom !== void 0) state.zoom = k.zoom;
836
+ return state;
837
+ }
838
+ let kA = this.keyframes[0];
839
+ let kB = this.keyframes[this.keyframes.length - 1];
840
+ for (let i = 0; i < this.keyframes.length - 1; i++) {
841
+ if (time >= this.keyframes[i].time && time <= this.keyframes[i + 1].time) {
842
+ kA = this.keyframes[i];
843
+ kB = this.keyframes[i + 1];
844
+ break;
845
+ }
846
+ }
847
+ const range = kB.time - kA.time;
848
+ const rawT = range > 0 ? (time - kA.time) / range : 1;
849
+ const t = this.applyEasing(rawT, kB.easing);
850
+ if (kA.position && kB.position) {
851
+ state.position.x = kA.position.x + (kB.position.x - kA.position.x) * t;
852
+ state.position.y = kA.position.y + (kB.position.y - kA.position.y) * t;
853
+ state.position.z = kA.position.z + (kB.position.z - kA.position.z) * t;
854
+ } else if (kB.position) {
855
+ state.position = { ...kB.position };
856
+ } else if (kA.position) {
857
+ state.position = { ...kA.position };
858
+ }
859
+ if (kA.rotation && kB.rotation) {
860
+ state.rotation.pitch = kA.rotation.pitch + (kB.rotation.pitch - kA.rotation.pitch) * t;
861
+ state.rotation.yaw = kA.rotation.yaw + (kB.rotation.yaw - kA.rotation.yaw) * t;
862
+ state.rotation.roll = kA.rotation.roll + (kB.rotation.roll - kA.rotation.roll) * t;
863
+ }
864
+ if (kA.fov !== void 0 && kB.fov !== void 0) {
865
+ state.fov = kA.fov + (kB.fov - kA.fov) * t;
866
+ }
867
+ if (kA.zoom !== void 0 && kB.zoom !== void 0) {
868
+ state.zoom = kA.zoom + (kB.zoom - kA.zoom) * t;
869
+ }
870
+ return state;
871
+ }
872
+ // ---------------------------------------------------------------------------
873
+ // Easing
874
+ // ---------------------------------------------------------------------------
875
+ applyEasing(t, easing) {
876
+ switch (easing) {
877
+ case "linear":
878
+ return t;
879
+ case "easeIn":
880
+ return t * t;
881
+ case "easeOut":
882
+ return 1 - (1 - t) * (1 - t);
883
+ case "easeInOut":
884
+ return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
885
+ case "smoothStep":
886
+ return t * t * (3 - 2 * t);
887
+ default:
888
+ return t;
889
+ }
890
+ }
891
+ // ---------------------------------------------------------------------------
892
+ // Listeners
893
+ // ---------------------------------------------------------------------------
894
+ onCue(listener) {
895
+ this.cueListeners.push(listener);
896
+ }
897
+ // ---------------------------------------------------------------------------
898
+ // Helpers
899
+ // ---------------------------------------------------------------------------
900
+ resetCues() {
901
+ for (const cue of this.cues) cue.fired = false;
902
+ }
903
+ recalcDuration() {
904
+ this.duration = this.keyframes.reduce((max, k) => Math.max(max, k.time), 0);
905
+ }
906
+ getKeyframeCount() {
907
+ return this.keyframes.length;
908
+ }
909
+ getCueCount() {
910
+ return this.cues.length;
911
+ }
912
+ clear() {
913
+ this.keyframes = [];
914
+ this.cues = [];
915
+ this.duration = 0;
916
+ this.currentTime = 0;
917
+ }
918
+ };
919
+
920
+ export {
921
+ CameraConstraints,
922
+ CameraController,
923
+ CameraEffects,
924
+ CameraPath,
925
+ CameraShake,
926
+ CinematicTrack,
927
+ camera_exports
928
+ };