bilca commited on
Commit
1486770
·
verified ·
1 Parent(s): 7f0d05c

Upload 85 files

Browse files
dist/controls/OrbitControls.d.ts CHANGED
@@ -11,12 +11,18 @@ declare class OrbitControls {
11
  panSpeed: number;
12
  zoomSpeed: number;
13
  dampening: number;
14
- alphaMika: number;
15
- betaMika: number;
16
- radiusMika: number;
17
  setCameraTarget: (newTarget: Vector3) => void;
18
  update: () => void;
19
  dispose: () => void;
20
- constructor(camera: Camera, canvas: HTMLElement, alpha?: number, beta?: number, radius?: number, enableKeyboardControls?: boolean, inputTarget?: Vector3);
 
 
 
 
 
 
 
 
 
21
  }
22
  export { OrbitControls };
 
11
  panSpeed: number;
12
  zoomSpeed: number;
13
  dampening: number;
 
 
 
14
  setCameraTarget: (newTarget: Vector3) => void;
15
  update: () => void;
16
  dispose: () => void;
17
+ /**
18
+ * @param camera the camera to control.
19
+ * @param canvas the element to attach events.
20
+ * @param alpha initial horizontal angle (in radians).
21
+ * @param beta initial vertical angle (in radians).
22
+ * @param radius initial distance from the target.
23
+ * @param enableKeyboardControls whether to listen to keyboard events.
24
+ * @param inputTarget the point to orbit around.
25
+ */
26
+ constructor(camera: Camera, canvas: HTMLElement, alpha?: number, beta?: number, radius?: number, enableKeyboardControls?: boolean, inputTarget?: Vector3, initAlpha?: number, initBeta?: number, initRadius?: number);
27
  }
28
  export { OrbitControls };
dist/index.js CHANGED
The diff for this file is too large to render. See raw diff
 
dist/index.js.map CHANGED
The diff for this file is too large to render. See raw diff
 
src/controls/OrbitControls.ts CHANGED
@@ -4,323 +4,325 @@ import { Quaternion } from "../math/Quaternion";
4
  import { Vector3 } from "../math/Vector3";
5
 
6
  class OrbitControls {
7
- // Vertical (polar) limits: by default, free rotation.
8
- minAngle: number = -Infinity;
9
- maxAngle: number = Infinity;
10
- // Horizontal (azimuth) limits: by default, free rotation.
11
- minAzimuth: number = -Infinity;
12
- maxAzimuth: number = Infinity;
13
- minZoom: number = 0.1;
14
- maxZoom: number = 30;
15
- orbitSpeed: number = 1;
16
- panSpeed: number = 1;
17
- zoomSpeed: number = 1;
18
- dampening: number = 0.12;
19
- alphaMika: number = 0.5;
20
- betaMika: number = 0.5;
21
- radiusMika: number = 5;
22
- setCameraTarget: (newTarget: Vector3) => void = () => {};
23
- update: () => void;
24
- dispose: () => void;
25
-
26
- constructor(
27
- camera: Camera,
28
- canvas: HTMLElement,
29
- alpha: number = 0.5,
30
- beta: number = 0.5,
31
- radius: number = 5,
32
-
33
- enableKeyboardControls: boolean = true,
34
- inputTarget: Vector3 = new Vector3(),
35
- ) {
36
-
37
- alpha = this.alphaMika;
38
- beta = this.betaMika;
39
- radius = this.radiusMika;
40
-
41
- let target = inputTarget.clone();
42
-
43
- let desiredTarget = target.clone();
44
- let desiredAlpha = alpha;
45
- let desiredBeta = beta;
46
- let desiredRadius = radius;
47
-
48
- let dragging = false;
49
- let panning = false;
50
- let lastDist = 0;
51
- let lastX = 0;
52
- let lastY = 0;
53
-
54
- const keys: { [key: string]: boolean } = {};
55
-
56
- let isUpdatingCamera = false;
57
-
58
- const onCameraChange = () => {
59
- if (isUpdatingCamera) return;
60
-
61
- const eulerRotation = camera.rotation.toEuler();
62
- desiredAlpha = -eulerRotation.y;
63
- desiredBeta = -eulerRotation.x;
64
-
65
- const x = camera.position.x - desiredRadius * Math.sin(desiredAlpha) * Math.cos(desiredBeta);
66
- const y = camera.position.y + desiredRadius * Math.sin(desiredBeta);
67
- const z = camera.position.z + desiredRadius * Math.cos(desiredAlpha) * Math.cos(desiredBeta);
68
-
69
- desiredTarget = new Vector3(x, y, z);
70
- };
71
-
72
- camera.addEventListener("objectChanged", onCameraChange);
73
-
74
- this.setCameraTarget = (newTarget: Vector3) => {
75
- const dx = newTarget.x - camera.position.x;
76
- const dy = newTarget.y - camera.position.y;
77
- const dz = newTarget.z - camera.position.z;
78
- desiredRadius = Math.sqrt(dx * dx + dy * dy + dz * dz);
79
- desiredBeta = Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
80
- desiredAlpha = -Math.atan2(dx, dz);
81
- desiredTarget = new Vector3(newTarget.x, newTarget.y, newTarget.z);
82
- };
83
-
84
- const computeZoomNorm = () => {
85
- return 0.1 + (0.9 * (desiredRadius - this.minZoom)) / (this.maxZoom - this.minZoom);
86
- };
87
-
88
- const onKeyDown = (e: KeyboardEvent) => {
89
- keys[e.code] = true;
90
- // Map arrow keys to WASD keys
91
- if (e.code === "ArrowUp") keys["KeyW"] = true;
92
- if (e.code === "ArrowDown") keys["KeyS"] = true;
93
- if (e.code === "ArrowLeft") keys["KeyA"] = true;
94
- if (e.code === "ArrowRight") keys["KeyD"] = true;
95
- };
96
-
97
- const onKeyUp = (e: KeyboardEvent) => {
98
- keys[e.code] = false;
99
- if (e.code === "ArrowUp") keys["KeyW"] = false;
100
- if (e.code === "ArrowDown") keys["KeyS"] = false;
101
- if (e.code === "ArrowLeft") keys["KeyA"] = false;
102
- if (e.code === "ArrowRight") keys["KeyD"] = false;
103
- };
104
-
105
- const onMouseDown = (e: MouseEvent) => {
106
- preventDefault(e);
107
- dragging = true;
108
- panning = e.button === 2;
109
- lastX = e.clientX;
110
- lastY = e.clientY;
111
- window.addEventListener("mouseup", onMouseUp);
112
- };
113
-
114
- const onMouseUp = (e: MouseEvent) => {
115
- preventDefault(e);
116
- dragging = false;
117
- panning = false;
118
- window.removeEventListener("mouseup", onMouseUp);
119
- };
120
-
121
- const onMouseMove = (e: MouseEvent) => {
122
- preventDefault(e);
123
- if (!dragging || !camera) return;
124
-
125
- const dx = e.clientX - lastX;
126
- const dy = e.clientY - lastY;
127
-
128
- if (panning) {
129
- const zoomNorm = computeZoomNorm();
130
- const panX = -dx * this.panSpeed * 0.01 * zoomNorm;
131
- const panY = -dy * this.panSpeed * 0.01 * zoomNorm;
132
- const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
133
- const right = new Vector3(R[0], R[3], R[6]);
134
- const up = new Vector3(R[1], R[4], R[7]);
135
- desiredTarget = desiredTarget.add(right.multiply(panX));
136
- desiredTarget = desiredTarget.add(up.multiply(panY));
137
- } else {
138
- desiredAlpha -= dx * this.orbitSpeed * 0.003;
139
- desiredBeta += dy * this.orbitSpeed * 0.003;
140
- // Clamp vertical angle (beta) only if limits are finite
141
- desiredBeta = Math.min(
142
- Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
143
- (this.maxAngle * Math.PI) / 180,
144
- );
145
- }
146
-
147
- lastX = e.clientX;
148
- lastY = e.clientY;
149
- };
150
-
151
- const onWheel = (e: WheelEvent) => {
152
- preventDefault(e);
153
- const zoomNorm = computeZoomNorm();
154
- desiredRadius += e.deltaY * this.zoomSpeed * 0.025 * zoomNorm;
155
- desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
156
- };
157
-
158
- const onTouchStart = (e: TouchEvent) => {
159
- preventDefault(e);
160
- if (e.touches.length === 1) {
161
- dragging = true;
162
- panning = false;
163
- lastX = e.touches[0].clientX;
164
- lastY = e.touches[0].clientY;
165
- lastDist = 0;
166
- } else if (e.touches.length === 2) {
167
- dragging = true;
168
- panning = true;
169
- lastX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
170
- lastY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
171
- const distX = e.touches[0].clientX - e.touches[1].clientX;
172
- const distY = e.touches[0].clientY - e.touches[1].clientY;
173
- lastDist = Math.sqrt(distX * distX + distY * distY);
174
- }
175
- };
176
-
177
- const onTouchEnd = (e: TouchEvent) => {
178
- preventDefault(e);
179
- dragging = false;
180
- panning = false;
181
- };
182
-
183
- const onTouchMove = (e: TouchEvent) => {
184
- preventDefault(e);
185
- if (!dragging || !camera) return;
186
-
187
- if (panning) {
188
- const zoomNorm = computeZoomNorm();
189
- const distX = e.touches[0].clientX - e.touches[1].clientX;
190
- const distY = e.touches[0].clientY - e.touches[1].clientY;
191
- const dist = Math.sqrt(distX * distX + distY * distY);
192
- const delta = lastDist - dist;
193
- desiredRadius += delta * this.zoomSpeed * 0.1 * zoomNorm;
194
- desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
195
- lastDist = dist;
196
-
197
- const touchX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
198
- const touchY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
199
- const dx = touchX - lastX;
200
- const dy = touchY - lastY;
201
- const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
202
- const right = new Vector3(R[0], R[3], R[6]);
203
- const up = new Vector3(R[1], R[4], R[7]);
204
- desiredTarget = desiredTarget.add(right.multiply(-dx * this.panSpeed * 0.025 * zoomNorm));
205
- desiredTarget = desiredTarget.add(up.multiply(-dy * this.panSpeed * 0.025 * zoomNorm));
206
- lastX = touchX;
207
- lastY = touchY;
208
- } else {
209
- const dx = e.touches[0].clientX - lastX;
210
- const dy = e.touches[0].clientY - lastY;
211
-
212
- desiredAlpha -= dx * this.orbitSpeed * 0.003;
213
- desiredBeta += dy * this.orbitSpeed * 0.003;
214
- // Clamp vertical (polar) angle (beta) if limits are finite
215
- desiredBeta = Math.min(
216
- Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
217
- (this.maxAngle * Math.PI) / 180,
218
- );
219
-
220
- lastX = e.touches[0].clientX;
221
- lastY = e.touches[0].clientY;
222
- }
223
- };
224
-
225
- const lerp = (a: number, b: number, t: number) => {
226
- return (1 - t) * a + t * b;
227
- };
228
-
229
- this.update = () => {
230
- isUpdatingCamera = true;
231
-
232
- alpha = lerp(alpha, desiredAlpha, this.dampening);
233
- beta = lerp(beta, desiredBeta, this.dampening);
234
- radius = lerp(radius, desiredRadius, this.dampening);
235
- target = target.lerp(desiredTarget, this.dampening);
236
-
237
- const x = target.x + radius * Math.sin(alpha) * Math.cos(beta);
238
- const y = target.y - radius * Math.sin(beta);
239
- const z = target.z - radius * Math.cos(alpha) * Math.cos(beta);
240
- camera.position = new Vector3(x, y, z);
241
-
242
- const direction = target.subtract(camera.position).normalize();
243
- const rx = Math.asin(-direction.y);
244
- const ry = Math.atan2(direction.x, direction.z);
245
- camera.rotation = Quaternion.FromEuler(new Vector3(rx, ry, 0));
246
-
247
- const moveSpeed = 0.025;
248
- const rotateSpeed = 0.01;
249
-
250
- const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
251
- const forward = new Vector3(-R[2], -R[5], -R[8]);
252
- const right = new Vector3(R[0], R[3], R[6]);
253
-
254
- if (keys["KeyS"]) desiredTarget = desiredTarget.add(forward.multiply(moveSpeed));
255
- if (keys["KeyW"]) desiredTarget = desiredTarget.subtract(forward.multiply(moveSpeed));
256
- if (keys["KeyA"]) desiredTarget = desiredTarget.subtract(right.multiply(moveSpeed));
257
- if (keys["KeyD"]) desiredTarget = desiredTarget.add(right.multiply(moveSpeed));
258
-
259
- // Horizontal (azimuth) rotation with 'e' and 'q'
260
- if (keys["KeyE"]) desiredAlpha += rotateSpeed;
261
- if (keys["KeyQ"]) desiredAlpha -= rotateSpeed;
262
- // Clamp horizontal angle (azimuth) only if limits are finite
263
- desiredAlpha = Math.min(
264
- Math.max(desiredAlpha, (this.minAzimuth * Math.PI) / 180),
265
- (this.maxAzimuth * Math.PI) / 180
266
- );
267
-
268
- // Vertical (polar) rotation with 'r' and 'f'
269
- if (keys["KeyR"]) desiredBeta += rotateSpeed;
270
- if (keys["KeyF"]) desiredBeta -= rotateSpeed;
271
- desiredBeta = Math.min(
272
- Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
273
- (this.maxAngle * Math.PI) / 180,
274
- );
275
-
276
- isUpdatingCamera = false;
277
- };
278
-
279
- const preventDefault = (e: Event) => {
280
- e.preventDefault();
281
- e.stopPropagation();
282
- };
283
-
284
- this.dispose = () => {
285
- canvas.removeEventListener("dragenter", preventDefault);
286
- canvas.removeEventListener("dragover", preventDefault);
287
- canvas.removeEventListener("dragleave", preventDefault);
288
- canvas.removeEventListener("contextmenu", preventDefault);
289
-
290
- canvas.removeEventListener("mousedown", onMouseDown);
291
- canvas.removeEventListener("mousemove", onMouseMove);
292
- canvas.removeEventListener("wheel", onWheel);
293
-
294
- canvas.removeEventListener("touchstart", onTouchStart);
295
- canvas.removeEventListener("touchend", onTouchEnd);
296
- canvas.removeEventListener("touchmove", onTouchMove);
297
-
298
- if (enableKeyboardControls) {
299
- window.removeEventListener("keydown", onKeyDown);
300
- window.removeEventListener("keyup", onKeyUp);
301
- }
302
- };
303
-
304
- if (enableKeyboardControls) {
305
- window.addEventListener("keydown", onKeyDown);
306
- window.addEventListener("keyup", onKeyUp);
307
- }
308
-
309
- canvas.addEventListener("dragenter", preventDefault);
310
- canvas.addEventListener("dragover", preventDefault);
311
- canvas.addEventListener("dragleave", preventDefault);
312
- canvas.addEventListener("contextmenu", preventDefault);
313
-
314
- canvas.addEventListener("mousedown", onMouseDown);
315
- canvas.addEventListener("mousemove", onMouseMove);
316
- canvas.addEventListener("wheel", onWheel);
317
-
318
- canvas.addEventListener("touchstart", onTouchStart);
319
- canvas.addEventListener("touchend", onTouchEnd);
320
- canvas.addEventListener("touchmove", onTouchMove);
321
-
322
- this.update();
323
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  }
325
 
326
  export { OrbitControls };
 
4
  import { Vector3 } from "../math/Vector3";
5
 
6
  class OrbitControls {
7
+ // Vertical (polar) limits: by default, free rotation.
8
+ minAngle: number = -Infinity;
9
+ maxAngle: number = Infinity;
10
+ // Horizontal (azimuth) limits: by default, free rotation.
11
+ minAzimuth: number = -Infinity;
12
+ maxAzimuth: number = Infinity;
13
+ minZoom: number = 0.1;
14
+ maxZoom: number = 30;
15
+ orbitSpeed: number = 1;
16
+ panSpeed: number = 1;
17
+ zoomSpeed: number = 1;
18
+ dampening: number = 0.12;
19
+ setCameraTarget: (newTarget: Vector3) => void = () => {};
20
+ update: () => void;
21
+ dispose: () => void;
22
+
23
+ /**
24
+ * @param camera the camera to control.
25
+ * @param canvas the element to attach events.
26
+ * @param alpha initial horizontal angle (in radians).
27
+ * @param beta initial vertical angle (in radians).
28
+ * @param radius initial distance from the target.
29
+ * @param enableKeyboardControls whether to listen to keyboard events.
30
+ * @param inputTarget the point to orbit around.
31
+ */
32
+ constructor(
33
+ camera: Camera,
34
+ canvas: HTMLElement,
35
+ alpha: number = 0.5,
36
+ beta: number = 0.5,
37
+ radius: number = 5,
38
+ enableKeyboardControls: boolean = true,
39
+ inputTarget: Vector3 = new Vector3(),
40
+ // New optional parameters to be set from JSON:
41
+ initAlpha?: number,
42
+ initBeta?: number,
43
+ initRadius?: number,
44
+ ) {
45
+ // Use the JSON-provided initial values if given.
46
+ const defaultAlpha = initAlpha !== undefined ? initAlpha : alpha;
47
+ const defaultBeta = initBeta !== undefined ? initBeta : beta;
48
+ const defaultRadius = initRadius !== undefined ? initRadius : radius;
49
+
50
+ // The target (the point the camera orbits around)
51
+ let target = inputTarget.clone();
52
+
53
+ // Set both the current and desired orbit parameters to the defaults.
54
+ let desiredTarget = target.clone();
55
+ let desiredAlpha = defaultAlpha;
56
+ let desiredBeta = defaultBeta;
57
+ let desiredRadius = defaultRadius;
58
+
59
+ // We now use separate “current” state variables so that update() can lerp toward desired values.
60
+ let currentAlpha = defaultAlpha;
61
+ let currentBeta = defaultBeta;
62
+ let currentRadius = defaultRadius;
63
+
64
+ let dragging = false;
65
+ let panning = false;
66
+ let lastDist = 0;
67
+ let lastX = 0;
68
+ let lastY = 0;
69
+
70
+ const keys: { [key: string]: boolean } = {};
71
+
72
+ let isUpdatingCamera = false;
73
+
74
+ // (Optionally you could keep an onCameraChange listener to update the current state when the camera changes,
75
+ // but that may override your JSON settings. In this example, we remove it.)
76
+ // camera.addEventListener("objectChanged", onCameraChange);
77
+
78
+ this.setCameraTarget = (newTarget: Vector3) => {
79
+ const dx = newTarget.x - camera.position.x;
80
+ const dy = newTarget.y - camera.position.y;
81
+ const dz = newTarget.z - camera.position.z;
82
+ desiredRadius = Math.sqrt(dx * dx + dy * dy + dz * dz);
83
+ desiredBeta = Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
84
+ desiredAlpha = -Math.atan2(dx, dz);
85
+ desiredTarget = new Vector3(newTarget.x, newTarget.y, newTarget.z);
86
+ };
87
+
88
+ const computeZoomNorm = () => {
89
+ return 0.1 + (0.9 * (desiredRadius - this.minZoom)) / (this.maxZoom - this.minZoom);
90
+ };
91
+
92
+ const onKeyDown = (e: KeyboardEvent) => {
93
+ keys[e.code] = true;
94
+ // Map arrow keys to WASD keys
95
+ if (e.code === "ArrowUp") keys["KeyW"] = true;
96
+ if (e.code === "ArrowDown") keys["KeyS"] = true;
97
+ if (e.code === "ArrowLeft") keys["KeyA"] = true;
98
+ if (e.code === "ArrowRight") keys["KeyD"] = true;
99
+ };
100
+
101
+ const onKeyUp = (e: KeyboardEvent) => {
102
+ keys[e.code] = false;
103
+ if (e.code === "ArrowUp") keys["KeyW"] = false;
104
+ if (e.code === "ArrowDown") keys["KeyS"] = false;
105
+ if (e.code === "ArrowLeft") keys["KeyA"] = false;
106
+ if (e.code === "ArrowRight") keys["KeyD"] = false;
107
+ };
108
+
109
+ const onMouseDown = (e: MouseEvent) => {
110
+ preventDefault(e);
111
+ dragging = true;
112
+ panning = e.button === 2;
113
+ lastX = e.clientX;
114
+ lastY = e.clientY;
115
+ window.addEventListener("mouseup", onMouseUp);
116
+ };
117
+
118
+ const onMouseUp = (e: MouseEvent) => {
119
+ preventDefault(e);
120
+ dragging = false;
121
+ panning = false;
122
+ window.removeEventListener("mouseup", onMouseUp);
123
+ };
124
+
125
+ const onMouseMove = (e: MouseEvent) => {
126
+ preventDefault(e);
127
+ if (!dragging || !camera) return;
128
+
129
+ const dx = e.clientX - lastX;
130
+ const dy = e.clientY - lastY;
131
+
132
+ if (panning) {
133
+ const zoomNorm = computeZoomNorm();
134
+ const panX = -dx * this.panSpeed * 0.01 * zoomNorm;
135
+ const panY = -dy * this.panSpeed * 0.01 * zoomNorm;
136
+ const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
137
+ const right = new Vector3(R[0], R[3], R[6]);
138
+ const up = new Vector3(R[1], R[4], R[7]);
139
+ desiredTarget = desiredTarget.add(right.multiply(panX));
140
+ desiredTarget = desiredTarget.add(up.multiply(panY));
141
+ } else {
142
+ desiredAlpha -= dx * this.orbitSpeed * 0.003;
143
+ desiredBeta += dy * this.orbitSpeed * 0.003;
144
+ // Clamp vertical angle (beta) if limits are finite
145
+ desiredBeta = Math.min(
146
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
147
+ (this.maxAngle * Math.PI) / 180,
148
+ );
149
+ }
150
+
151
+ lastX = e.clientX;
152
+ lastY = e.clientY;
153
+ };
154
+
155
+ const onWheel = (e: WheelEvent) => {
156
+ preventDefault(e);
157
+ const zoomNorm = computeZoomNorm();
158
+ desiredRadius += e.deltaY * this.zoomSpeed * 0.025 * zoomNorm;
159
+ desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
160
+ };
161
+
162
+ const onTouchStart = (e: TouchEvent) => {
163
+ preventDefault(e);
164
+ if (e.touches.length === 1) {
165
+ dragging = true;
166
+ panning = false;
167
+ lastX = e.touches[0].clientX;
168
+ lastY = e.touches[0].clientY;
169
+ lastDist = 0;
170
+ } else if (e.touches.length === 2) {
171
+ dragging = true;
172
+ panning = true;
173
+ lastX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
174
+ lastY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
175
+ const distX = e.touches[0].clientX - e.touches[1].clientX;
176
+ const distY = e.touches[0].clientY - e.touches[1].clientY;
177
+ lastDist = Math.sqrt(distX * distX + distY * distY);
178
+ }
179
+ };
180
+
181
+ const onTouchEnd = (e: TouchEvent) => {
182
+ preventDefault(e);
183
+ dragging = false;
184
+ panning = false;
185
+ };
186
+
187
+ const onTouchMove = (e: TouchEvent) => {
188
+ preventDefault(e);
189
+ if (!dragging || !camera) return;
190
+
191
+ if (panning) {
192
+ const zoomNorm = computeZoomNorm();
193
+ const distX = e.touches[0].clientX - e.touches[1].clientX;
194
+ const distY = e.touches[0].clientY - e.touches[1].clientY;
195
+ const dist = Math.sqrt(distX * distX + distY * distY);
196
+ const delta = lastDist - dist;
197
+ desiredRadius += delta * this.zoomSpeed * 0.1 * zoomNorm;
198
+ desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
199
+ lastDist = dist;
200
+
201
+ const touchX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
202
+ const touchY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
203
+ const dx = touchX - lastX;
204
+ const dy = touchY - lastY;
205
+ const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
206
+ const right = new Vector3(R[0], R[3], R[6]);
207
+ const up = new Vector3(R[1], R[4], R[7]);
208
+ desiredTarget = desiredTarget.add(right.multiply(-dx * this.panSpeed * 0.025 * zoomNorm));
209
+ desiredTarget = desiredTarget.add(up.multiply(-dy * this.panSpeed * 0.025 * zoomNorm));
210
+ lastX = touchX;
211
+ lastY = touchY;
212
+ } else {
213
+ const dx = e.touches[0].clientX - lastX;
214
+ const dy = e.touches[0].clientY - lastY;
215
+
216
+ desiredAlpha -= dx * this.orbitSpeed * 0.003;
217
+ desiredBeta += dy * this.orbitSpeed * 0.003;
218
+ desiredBeta = Math.min(
219
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
220
+ (this.maxAngle * Math.PI) / 180,
221
+ );
222
+
223
+ lastX = e.touches[0].clientX;
224
+ lastY = e.touches[0].clientY;
225
+ }
226
+ };
227
+
228
+ const lerp = (a: number, b: number, t: number) => (1 - t) * a + t * b;
229
+
230
+ this.update = () => {
231
+ isUpdatingCamera = true;
232
+
233
+ // Lerp the current state toward the desired state.
234
+ currentAlpha = lerp(currentAlpha, desiredAlpha, this.dampening);
235
+ currentBeta = lerp(currentBeta, desiredBeta, this.dampening);
236
+ currentRadius = lerp(currentRadius, desiredRadius, this.dampening);
237
+ target = target.lerp(desiredTarget, this.dampening);
238
+
239
+ // Recompute the camera position from currentAlpha/currentBeta/currentRadius.
240
+ const x = target.x + currentRadius * Math.sin(currentAlpha) * Math.cos(currentBeta);
241
+ const y = target.y - currentRadius * Math.sin(currentBeta);
242
+ const z = target.z - currentRadius * Math.cos(currentAlpha) * Math.cos(currentBeta);
243
+ camera.position = new Vector3(x, y, z);
244
+
245
+ // Compute new rotation so that the camera looks at the target.
246
+ const direction = target.subtract(camera.position).normalize();
247
+ const rx = Math.asin(-direction.y);
248
+ const ry = Math.atan2(direction.x, direction.z);
249
+ camera.rotation = Quaternion.FromEuler(new Vector3(rx, ry, 0));
250
+
251
+ const moveSpeed = 0.025;
252
+ const rotateSpeed = 0.01;
253
+ const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
254
+ const forward = new Vector3(-R[2], -R[5], -R[8]);
255
+ const right = new Vector3(R[0], R[3], R[6]);
256
+
257
+ if (keys["KeyS"]) desiredTarget = desiredTarget.add(forward.multiply(moveSpeed));
258
+ if (keys["KeyW"]) desiredTarget = desiredTarget.subtract(forward.multiply(moveSpeed));
259
+ if (keys["KeyA"]) desiredTarget = desiredTarget.subtract(right.multiply(moveSpeed));
260
+ if (keys["KeyD"]) desiredTarget = desiredTarget.add(right.multiply(moveSpeed));
261
+
262
+ // Horizontal (azimuth) rotation with 'e' and 'q'
263
+ if (keys["KeyE"]) desiredAlpha += rotateSpeed;
264
+ if (keys["KeyQ"]) desiredAlpha -= rotateSpeed;
265
+ desiredAlpha = Math.min(
266
+ Math.max(desiredAlpha, (this.minAzimuth * Math.PI) / 180),
267
+ (this.maxAzimuth * Math.PI) / 180,
268
+ );
269
+
270
+ // Vertical (polar) rotation with 'r' and 'f'
271
+ if (keys["KeyR"]) desiredBeta += rotateSpeed;
272
+ if (keys["KeyF"]) desiredBeta -= rotateSpeed;
273
+ desiredBeta = Math.min(
274
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
275
+ (this.maxAngle * Math.PI) / 180,
276
+ );
277
+
278
+ isUpdatingCamera = false;
279
+ };
280
+
281
+ const preventDefault = (e: Event) => {
282
+ e.preventDefault();
283
+ e.stopPropagation();
284
+ };
285
+
286
+ this.dispose = () => {
287
+ canvas.removeEventListener("dragenter", preventDefault);
288
+ canvas.removeEventListener("dragover", preventDefault);
289
+ canvas.removeEventListener("dragleave", preventDefault);
290
+ canvas.removeEventListener("contextmenu", preventDefault);
291
+
292
+ canvas.removeEventListener("mousedown", onMouseDown);
293
+ canvas.removeEventListener("mousemove", onMouseMove);
294
+ canvas.removeEventListener("wheel", onWheel);
295
+
296
+ canvas.removeEventListener("touchstart", onTouchStart);
297
+ canvas.removeEventListener("touchend", onTouchEnd);
298
+ canvas.removeEventListener("touchmove", onTouchMove);
299
+
300
+ if (enableKeyboardControls) {
301
+ window.removeEventListener("keydown", onKeyDown);
302
+ window.removeEventListener("keyup", onKeyUp);
303
+ }
304
+ };
305
+
306
+ if (enableKeyboardControls) {
307
+ window.addEventListener("keydown", onKeyDown);
308
+ window.addEventListener("keyup", onKeyUp);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  }
310
+
311
+ canvas.addEventListener("dragenter", preventDefault);
312
+ canvas.addEventListener("dragover", preventDefault);
313
+ canvas.addEventListener("dragleave", preventDefault);
314
+ canvas.addEventListener("contextmenu", preventDefault);
315
+
316
+ canvas.addEventListener("mousedown", onMouseDown);
317
+ canvas.addEventListener("mousemove", onMouseMove);
318
+ canvas.addEventListener("wheel", onWheel);
319
+
320
+ canvas.addEventListener("touchstart", onTouchStart);
321
+ canvas.addEventListener("touchend", onTouchEnd);
322
+ canvas.addEventListener("touchmove", onTouchMove);
323
+
324
+ this.update();
325
+ }
326
  }
327
 
328
  export { OrbitControls };