bilca commited on
Commit
7bf3f06
·
verified ·
1 Parent(s): 66bfa9c

Upload 85 files

Browse files
dist/controls/OrbitControls.d.ts CHANGED
@@ -14,6 +14,7 @@ declare class OrbitControls {
14
  setCameraTarget: (newTarget: Vector3) => void;
15
  update: () => void;
16
  dispose: () => void;
17
- constructor(camera: Camera, canvas: HTMLElement, alpha?: number, beta?: number, radius?: number, enableKeyboardControls?: boolean, inputTarget?: Vector3);
 
18
  }
19
  export { OrbitControls };
 
14
  setCameraTarget: (newTarget: Vector3) => void;
15
  update: () => void;
16
  dispose: () => void;
17
+ constructor(camera: Camera, canvas: HTMLElement, alpha?: number, // <-- These parameters will be overridden
18
+ beta?: number, radius?: number, enableKeyboardControls?: boolean, inputTarget?: Vector3);
19
  }
20
  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,333 +4,319 @@ 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
- setCameraTarget: (newTarget: Vector3) => void = () => {};
20
- update: () => void;
21
- dispose: () => void;
22
-
23
- constructor(
24
- camera: Camera,
25
- canvas: HTMLElement,
26
- // The following defaults will be overridden by the computed values.
27
- alpha: number = 0.5,
28
- beta: number = 0.5,
29
- radius: number = 5,
30
- enableKeyboardControls: boolean = true,
31
- inputTarget: Vector3 = new Vector3(),
32
- ) {
33
- // 'target' is the point the camera orbits around.
34
- let target = inputTarget.clone();
35
-
36
- // Compute the initial values from the cameras position relative to the target.
37
- // Let v = camera.position - target.
38
- const v = camera.position.subtract(target);
39
- const computedRadius = v.magnitude();
40
- // From the update formulas, camera.position is computed as:
41
- // x = target.x + radius * sin(alpha)*cos(beta)
42
- // y = target.y - radius * sin(beta)
43
- // z = target.z - radius * cos(alpha)*cos(beta)
44
- // Thus, to recover alpha and beta:
45
- const computedBeta = Math.asin(-v.y / computedRadius);
46
- const computedAlpha = Math.atan2(v.x, -v.z);
47
-
48
- // Initialize our internal state using the computed values.
49
- let alphaVal = computedAlpha;
50
- let betaVal = computedBeta;
51
- let radiusVal = computedRadius;
52
- let desiredAlpha = computedAlpha;
53
- let desiredBeta = computedBeta;
54
- let desiredRadius = computedRadius;
55
- let desiredTarget = target.clone();
56
-
57
- const keys: { [key: string]: boolean } = {};
58
-
59
- let dragging = false;
60
- let panning = false;
61
- let lastDist = 0;
62
- let lastX = 0;
63
- let lastY = 0;
64
- let isUpdatingCamera = false;
65
-
66
- const onCameraChange = () => {
67
- if (isUpdatingCamera) return;
68
-
69
- // When the camera itself changes (e.g. via a reset),
70
- // update the desired angles from its rotation.
71
- const eulerRotation = camera.rotation.toEuler();
72
- // (Note: depending on your math conventions you might need to adjust signs.)
73
- desiredAlpha = -eulerRotation.y;
74
- desiredBeta = -eulerRotation.x;
75
-
76
- const x = camera.position.x - desiredRadius * Math.sin(desiredAlpha) * Math.cos(desiredBeta);
77
- const y = camera.position.y + desiredRadius * Math.sin(desiredBeta);
78
- const z = camera.position.z + desiredRadius * Math.cos(desiredAlpha) * Math.cos(desiredBeta);
79
- desiredTarget = new Vector3(x, y, z);
80
- };
81
-
82
- camera.addEventListener("objectChanged", onCameraChange);
83
-
84
- this.setCameraTarget = (newTarget: Vector3) => {
85
- const dx = newTarget.x - camera.position.x;
86
- const dy = newTarget.y - camera.position.y;
87
- const dz = newTarget.z - camera.position.z;
88
- desiredRadius = Math.sqrt(dx * dx + dy * dy + dz * dz);
89
- desiredBeta = Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
90
- desiredAlpha = -Math.atan2(dx, dz);
91
- desiredTarget = new Vector3(newTarget.x, newTarget.y, newTarget.z);
92
- };
93
-
94
- const computeZoomNorm = () => {
95
- return 0.1 + (0.9 * (desiredRadius - this.minZoom)) / (this.maxZoom - this.minZoom);
96
- };
97
-
98
- const onKeyDown = (e: KeyboardEvent) => {
99
- keys[e.code] = true;
100
- // Map arrow keys to WASD keys
101
- if (e.code === "ArrowUp") keys["KeyW"] = true;
102
- if (e.code === "ArrowDown") keys["KeyS"] = true;
103
- if (e.code === "ArrowLeft") keys["KeyA"] = true;
104
- if (e.code === "ArrowRight") keys["KeyD"] = true;
105
- };
106
-
107
- const onKeyUp = (e: KeyboardEvent) => {
108
- keys[e.code] = false;
109
- if (e.code === "ArrowUp") keys["KeyW"] = false;
110
- if (e.code === "ArrowDown") keys["KeyS"] = false;
111
- if (e.code === "ArrowLeft") keys["KeyA"] = false;
112
- if (e.code === "ArrowRight") keys["KeyD"] = false;
113
- };
114
-
115
- const onMouseDown = (e: MouseEvent) => {
116
- preventDefault(e);
117
- dragging = true;
118
- panning = e.button === 2;
119
- lastX = e.clientX;
120
- lastY = e.clientY;
121
- window.addEventListener("mouseup", onMouseUp);
122
- };
123
-
124
- const onMouseUp = (e: MouseEvent) => {
125
- preventDefault(e);
126
- dragging = false;
127
- panning = false;
128
- window.removeEventListener("mouseup", onMouseUp);
129
- };
130
-
131
- const onMouseMove = (e: MouseEvent) => {
132
- preventDefault(e);
133
- if (!dragging || !camera) return;
134
-
135
- const dx = e.clientX - lastX;
136
- const dy = e.clientY - lastY;
137
-
138
- if (panning) {
139
- const zoomNorm = computeZoomNorm();
140
- const panX = -dx * this.panSpeed * 0.01 * zoomNorm;
141
- const panY = -dy * this.panSpeed * 0.01 * zoomNorm;
142
- const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
143
- const right = new Vector3(R[0], R[3], R[6]);
144
- const up = new Vector3(R[1], R[4], R[7]);
145
- desiredTarget = desiredTarget.add(right.multiply(panX));
146
- desiredTarget = desiredTarget.add(up.multiply(panY));
147
- } else {
148
- desiredAlpha -= dx * this.orbitSpeed * 0.003;
149
- desiredBeta += dy * this.orbitSpeed * 0.003;
150
- desiredBeta = Math.min(
151
- Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
152
- (this.maxAngle * Math.PI) / 180,
153
- );
154
- }
155
-
156
- lastX = e.clientX;
157
- lastY = e.clientY;
158
- };
159
-
160
- const onWheel = (e: WheelEvent) => {
161
- preventDefault(e);
162
- const zoomNorm = computeZoomNorm();
163
- desiredRadius += e.deltaY * this.zoomSpeed * 0.025 * zoomNorm;
164
- desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
165
- };
166
-
167
- const onTouchStart = (e: TouchEvent) => {
168
- preventDefault(e);
169
- if (e.touches.length === 1) {
170
- dragging = true;
171
- panning = false;
172
- lastX = e.touches[0].clientX;
173
- lastY = e.touches[0].clientY;
174
- lastDist = 0;
175
- } else if (e.touches.length === 2) {
176
- dragging = true;
177
- panning = true;
178
- lastX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
179
- lastY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
180
- const distX = e.touches[0].clientX - e.touches[1].clientX;
181
- const distY = e.touches[0].clientY - e.touches[1].clientY;
182
- lastDist = Math.sqrt(distX * distX + distY * distY);
183
- }
184
- };
185
-
186
- const onTouchEnd = (e: TouchEvent) => {
187
- preventDefault(e);
188
- dragging = false;
189
- panning = false;
190
- };
191
-
192
- const onTouchMove = (e: TouchEvent) => {
193
- preventDefault(e);
194
- if (!dragging || !camera) return;
195
-
196
- if (panning) {
197
- const zoomNorm = computeZoomNorm();
198
- const distX = e.touches[0].clientX - e.touches[1].clientX;
199
- const distY = e.touches[0].clientY - e.touches[1].clientY;
200
- const dist = Math.sqrt(distX * distX + distY * distY);
201
- const delta = lastDist - dist;
202
- desiredRadius += delta * this.zoomSpeed * 0.1 * zoomNorm;
203
- desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
204
- lastDist = dist;
205
-
206
- const touchX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
207
- const touchY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
208
- const dx = touchX - lastX;
209
- const dy = touchY - lastY;
210
- const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
211
- const right = new Vector3(R[0], R[3], R[6]);
212
- const up = new Vector3(R[1], R[4], R[7]);
213
- desiredTarget = desiredTarget.add(right.multiply(-dx * this.panSpeed * 0.025 * zoomNorm));
214
- desiredTarget = desiredTarget.add(up.multiply(-dy * this.panSpeed * 0.025 * zoomNorm));
215
- lastX = touchX;
216
- lastY = touchY;
217
- } else {
218
- const dx = e.touches[0].clientX - lastX;
219
- const dy = e.touches[0].clientY - lastY;
220
-
221
- desiredAlpha -= dx * this.orbitSpeed * 0.003;
222
- desiredBeta += dy * this.orbitSpeed * 0.003;
223
- desiredBeta = Math.min(
224
- Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
225
- (this.maxAngle * Math.PI) / 180,
226
- );
227
-
228
- lastX = e.touches[0].clientX;
229
- lastY = e.touches[0].clientY;
230
- }
231
- };
232
-
233
- const lerp = (a: number, b: number, t: number) => {
234
- return (1 - t) * a + t * b;
235
- };
236
-
237
- this.update = () => {
238
- isUpdatingCamera = true;
239
-
240
- // Update the current values toward the desired values.
241
- alphaVal = lerp(alphaVal, desiredAlpha, this.dampening);
242
- betaVal = lerp(betaVal, desiredBeta, this.dampening);
243
- radiusVal = lerp(radiusVal, desiredRadius, this.dampening);
244
- target = target.lerp(desiredTarget, this.dampening);
245
-
246
- // Recompute camera.position from the current angles.
247
- const x = target.x + radiusVal * Math.sin(alphaVal) * Math.cos(betaVal);
248
- const y = target.y - radiusVal * Math.sin(betaVal);
249
- const z = target.z - radiusVal * Math.cos(alphaVal) * Math.cos(betaVal);
250
- camera.position = new Vector3(x, y, z);
251
-
252
- // Compute new rotation so that camera looks at the target.
253
- const direction = target.subtract(camera.position).normalize();
254
- const rx = Math.asin(-direction.y);
255
- const ry = Math.atan2(direction.x, direction.z);
256
- camera.rotation = Quaternion.FromEuler(new Vector3(rx, ry, 0));
257
-
258
- // Process keyboard input for panning/orbiting.
259
- const moveSpeed = 0.025;
260
- const rotateSpeed = 0.01;
261
- const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
262
- const forward = new Vector3(-R[2], -R[5], -R[8]);
263
- const right = new Vector3(R[0], R[3], R[6]);
264
-
265
- if (keys["KeyS"]) desiredTarget = desiredTarget.add(forward.multiply(moveSpeed));
266
- if (keys["KeyW"]) desiredTarget = desiredTarget.subtract(forward.multiply(moveSpeed));
267
- if (keys["KeyA"]) desiredTarget = desiredTarget.subtract(right.multiply(moveSpeed));
268
- if (keys["KeyD"]) desiredTarget = desiredTarget.add(right.multiply(moveSpeed));
269
-
270
- // Horizontal (azimuth) rotation with 'e' and 'q'
271
- if (keys["KeyE"]) desiredAlpha += rotateSpeed;
272
- if (keys["KeyQ"]) desiredAlpha -= rotateSpeed;
273
- desiredAlpha = Math.min(
274
- Math.max(desiredAlpha, (this.minAzimuth * Math.PI) / 180),
275
- (this.maxAzimuth * Math.PI) / 180,
276
- );
277
-
278
- // Vertical (polar) rotation with 'r' and 'f'
279
- if (keys["KeyR"]) desiredBeta += rotateSpeed;
280
- if (keys["KeyF"]) desiredBeta -= rotateSpeed;
281
- desiredBeta = Math.min(
282
- Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
283
- (this.maxAngle * Math.PI) / 180,
284
- );
285
-
286
- isUpdatingCamera = false;
287
- };
288
-
289
- const preventDefault = (e: Event) => {
290
- e.preventDefault();
291
- e.stopPropagation();
292
- };
293
-
294
- this.dispose = () => {
295
- canvas.removeEventListener("dragenter", preventDefault);
296
- canvas.removeEventListener("dragover", preventDefault);
297
- canvas.removeEventListener("dragleave", preventDefault);
298
- canvas.removeEventListener("contextmenu", preventDefault);
299
-
300
- canvas.removeEventListener("mousedown", onMouseDown);
301
- canvas.removeEventListener("mousemove", onMouseMove);
302
- canvas.removeEventListener("wheel", onWheel);
303
-
304
- canvas.removeEventListener("touchstart", onTouchStart);
305
- canvas.removeEventListener("touchend", onTouchEnd);
306
- canvas.removeEventListener("touchmove", onTouchMove);
307
-
308
- if (enableKeyboardControls) {
309
- window.removeEventListener("keydown", onKeyDown);
310
- window.removeEventListener("keyup", onKeyUp);
311
- }
312
- };
313
-
314
- if (enableKeyboardControls) {
315
- window.addEventListener("keydown", onKeyDown);
316
- window.addEventListener("keyup", onKeyUp);
 
 
317
  }
318
-
319
- canvas.addEventListener("dragenter", preventDefault);
320
- canvas.addEventListener("dragover", preventDefault);
321
- canvas.addEventListener("dragleave", preventDefault);
322
- canvas.addEventListener("contextmenu", preventDefault);
323
-
324
- canvas.addEventListener("mousedown", onMouseDown);
325
- canvas.addEventListener("mousemove", onMouseMove);
326
- canvas.addEventListener("wheel", onWheel);
327
-
328
- canvas.addEventListener("touchstart", onTouchStart);
329
- canvas.addEventListener("touchend", onTouchEnd);
330
- canvas.addEventListener("touchmove", onTouchMove);
331
-
332
- this.update();
333
- }
334
  }
335
 
336
  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
+ constructor(
24
+ camera: Camera,
25
+ canvas: HTMLElement,
26
+ alpha: number = 0.5, // <-- These parameters will be overridden
27
+ beta: number = 0.5,
28
+ radius: number = 5,
29
+ enableKeyboardControls: boolean = true,
30
+ inputTarget: Vector3 = new Vector3(),
31
+ ) {
32
+ // 'target' is the point the camera orbits around.
33
+ let target = inputTarget.clone();
34
+
35
+ // Instead of using the passed alpha, beta, radius values,
36
+ // compute them from the camera's initial position.
37
+ // (This assumes that the camera.position is already set
38
+ // to the desired initial position.)
39
+ const dx = target.x - camera.position.x;
40
+ const dy = target.y - camera.position.y;
41
+ const dz = target.z - camera.position.z;
42
+ let desiredRadius = Math.sqrt(dx * dx + dy * dy + dz * dz);
43
+ let desiredBeta = Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
44
+ let desiredAlpha = -Math.atan2(dx, dz);
45
+ let desiredTarget = target.clone();
46
+
47
+ let dragging = false;
48
+ let panning = false;
49
+ let lastDist = 0;
50
+ let lastX = 0;
51
+ let lastY = 0;
52
+
53
+ const keys: { [key: string]: boolean } = {};
54
+
55
+ let isUpdatingCamera = false;
56
+
57
+ const onCameraChange = () => {
58
+ if (isUpdatingCamera) return;
59
+
60
+ const eulerRotation = camera.rotation.toEuler();
61
+ desiredAlpha = -eulerRotation.y;
62
+ desiredBeta = -eulerRotation.x;
63
+
64
+ const x = camera.position.x - desiredRadius * Math.sin(desiredAlpha) * Math.cos(desiredBeta);
65
+ const y = camera.position.y + desiredRadius * Math.sin(desiredBeta);
66
+ const z = camera.position.z + desiredRadius * Math.cos(desiredAlpha) * Math.cos(desiredBeta);
67
+
68
+ desiredTarget = new Vector3(x, y, z);
69
+ };
70
+
71
+ camera.addEventListener("objectChanged", onCameraChange);
72
+
73
+ this.setCameraTarget = (newTarget: Vector3) => {
74
+ const dx = newTarget.x - camera.position.x;
75
+ const dy = newTarget.y - camera.position.y;
76
+ const dz = newTarget.z - camera.position.z;
77
+ desiredRadius = Math.sqrt(dx * dx + dy * dy + dz * dz);
78
+ desiredBeta = Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
79
+ desiredAlpha = -Math.atan2(dx, dz);
80
+ desiredTarget = new Vector3(newTarget.x, newTarget.y, newTarget.z);
81
+ };
82
+
83
+ const computeZoomNorm = () => {
84
+ return 0.1 + (0.9 * (desiredRadius - this.minZoom)) / (this.maxZoom - this.minZoom);
85
+ };
86
+
87
+ const onKeyDown = (e: KeyboardEvent) => {
88
+ keys[e.code] = true;
89
+ // Map arrow keys to WASD keys
90
+ if (e.code === "ArrowUp") keys["KeyW"] = true;
91
+ if (e.code === "ArrowDown") keys["KeyS"] = true;
92
+ if (e.code === "ArrowLeft") keys["KeyA"] = true;
93
+ if (e.code === "ArrowRight") keys["KeyD"] = true;
94
+ };
95
+
96
+ const onKeyUp = (e: KeyboardEvent) => {
97
+ keys[e.code] = false;
98
+ if (e.code === "ArrowUp") keys["KeyW"] = false;
99
+ if (e.code === "ArrowDown") keys["KeyS"] = false;
100
+ if (e.code === "ArrowLeft") keys["KeyA"] = false;
101
+ if (e.code === "ArrowRight") keys["KeyD"] = false;
102
+ };
103
+
104
+ const onMouseDown = (e: MouseEvent) => {
105
+ preventDefault(e);
106
+ dragging = true;
107
+ panning = e.button === 2;
108
+ lastX = e.clientX;
109
+ lastY = e.clientY;
110
+ window.addEventListener("mouseup", onMouseUp);
111
+ };
112
+
113
+ const onMouseUp = (e: MouseEvent) => {
114
+ preventDefault(e);
115
+ dragging = false;
116
+ panning = false;
117
+ window.removeEventListener("mouseup", onMouseUp);
118
+ };
119
+
120
+ const onMouseMove = (e: MouseEvent) => {
121
+ preventDefault(e);
122
+ if (!dragging || !camera) return;
123
+
124
+ const dx = e.clientX - lastX;
125
+ const dy = e.clientY - lastY;
126
+
127
+ if (panning) {
128
+ const zoomNorm = computeZoomNorm();
129
+ const panX = -dx * this.panSpeed * 0.01 * zoomNorm;
130
+ const panY = -dy * this.panSpeed * 0.01 * zoomNorm;
131
+ const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
132
+ const right = new Vector3(R[0], R[3], R[6]);
133
+ const up = new Vector3(R[1], R[4], R[7]);
134
+ desiredTarget = desiredTarget.add(right.multiply(panX));
135
+ desiredTarget = desiredTarget.add(up.multiply(panY));
136
+ } else {
137
+ desiredAlpha -= dx * this.orbitSpeed * 0.003;
138
+ desiredBeta += dy * this.orbitSpeed * 0.003;
139
+ desiredBeta = Math.min(
140
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
141
+ (this.maxAngle * Math.PI) / 180,
142
+ );
143
+ }
144
+
145
+ lastX = e.clientX;
146
+ lastY = e.clientY;
147
+ };
148
+
149
+ const onWheel = (e: WheelEvent) => {
150
+ preventDefault(e);
151
+ const zoomNorm = computeZoomNorm();
152
+ desiredRadius += e.deltaY * this.zoomSpeed * 0.025 * zoomNorm;
153
+ desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
154
+ };
155
+
156
+ const onTouchStart = (e: TouchEvent) => {
157
+ preventDefault(e);
158
+ if (e.touches.length === 1) {
159
+ dragging = true;
160
+ panning = false;
161
+ lastX = e.touches[0].clientX;
162
+ lastY = e.touches[0].clientY;
163
+ lastDist = 0;
164
+ } else if (e.touches.length === 2) {
165
+ dragging = true;
166
+ panning = true;
167
+ lastX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
168
+ lastY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
169
+ const distX = e.touches[0].clientX - e.touches[1].clientX;
170
+ const distY = e.touches[0].clientY - e.touches[1].clientY;
171
+ lastDist = Math.sqrt(distX * distX + distY * distY);
172
+ }
173
+ };
174
+
175
+ const onTouchEnd = (e: TouchEvent) => {
176
+ preventDefault(e);
177
+ dragging = false;
178
+ panning = false;
179
+ };
180
+
181
+ const onTouchMove = (e: TouchEvent) => {
182
+ preventDefault(e);
183
+ if (!dragging || !camera) return;
184
+
185
+ if (panning) {
186
+ const zoomNorm = computeZoomNorm();
187
+ const distX = e.touches[0].clientX - e.touches[1].clientX;
188
+ const distY = e.touches[0].clientY - e.touches[1].clientY;
189
+ const dist = Math.sqrt(distX * distX + distY * distY);
190
+ const delta = lastDist - dist;
191
+ desiredRadius += delta * this.zoomSpeed * 0.1 * zoomNorm;
192
+ desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
193
+ lastDist = dist;
194
+
195
+ const touchX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
196
+ const touchY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
197
+ const dx = touchX - lastX;
198
+ const dy = touchY - lastY;
199
+ const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
200
+ const right = new Vector3(R[0], R[3], R[6]);
201
+ const up = new Vector3(R[1], R[4], R[7]);
202
+ desiredTarget = desiredTarget.add(right.multiply(-dx * this.panSpeed * 0.025 * zoomNorm));
203
+ desiredTarget = desiredTarget.add(up.multiply(-dy * this.panSpeed * 0.025 * zoomNorm));
204
+ lastX = touchX;
205
+ lastY = touchY;
206
+ } else {
207
+ const dx = e.touches[0].clientX - lastX;
208
+ const dy = e.touches[0].clientY - lastY;
209
+
210
+ desiredAlpha -= dx * this.orbitSpeed * 0.003;
211
+ desiredBeta += dy * this.orbitSpeed * 0.003;
212
+ desiredBeta = Math.min(
213
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
214
+ (this.maxAngle * Math.PI) / 180,
215
+ );
216
+
217
+ lastX = e.touches[0].clientX;
218
+ lastY = e.touches[0].clientY;
219
+ }
220
+ };
221
+
222
+ const lerp = (a: number, b: number, t: number) => {
223
+ return (1 - t) * a + t * b;
224
+ };
225
+
226
+ this.update = () => {
227
+ isUpdatingCamera = true;
228
+
229
+ alpha = lerp(alpha, desiredAlpha, this.dampening);
230
+ beta = lerp(beta, desiredBeta, this.dampening);
231
+ radius = lerp(radius, desiredRadius, this.dampening);
232
+ target = target.lerp(desiredTarget, this.dampening);
233
+
234
+ const x = target.x + radius * Math.sin(alpha) * Math.cos(beta);
235
+ const y = target.y - radius * Math.sin(beta);
236
+ const z = target.z - radius * Math.cos(alpha) * Math.cos(beta);
237
+ camera.position = new Vector3(x, y, z);
238
+
239
+ const direction = target.subtract(camera.position).normalize();
240
+ const rx = Math.asin(-direction.y);
241
+ const ry = Math.atan2(direction.x, direction.z);
242
+ camera.rotation = Quaternion.FromEuler(new Vector3(rx, ry, 0));
243
+
244
+ const moveSpeed = 0.025;
245
+ const rotateSpeed = 0.01;
246
+
247
+ const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer;
248
+ const forward = new Vector3(-R[2], -R[5], -R[8]);
249
+ const right = new Vector3(R[0], R[3], R[6]);
250
+
251
+ if (keys["KeyS"]) desiredTarget = desiredTarget.add(forward.multiply(moveSpeed));
252
+ if (keys["KeyW"]) desiredTarget = desiredTarget.subtract(forward.multiply(moveSpeed));
253
+ if (keys["KeyA"]) desiredTarget = desiredTarget.subtract(right.multiply(moveSpeed));
254
+ if (keys["KeyD"]) desiredTarget = desiredTarget.add(right.multiply(moveSpeed));
255
+
256
+ // Horizontal (azimuth) rotation with 'e' and 'q'
257
+ if (keys["KeyE"]) desiredAlpha += rotateSpeed;
258
+ if (keys["KeyQ"]) desiredAlpha -= rotateSpeed;
259
+ desiredAlpha = Math.min(
260
+ Math.max(desiredAlpha, (this.minAzimuth * Math.PI) / 180),
261
+ (this.maxAzimuth * Math.PI) / 180
262
+ );
263
+
264
+ // Vertical (polar) rotation with 'r' and 'f'
265
+ if (keys["KeyR"]) desiredBeta += rotateSpeed;
266
+ if (keys["KeyF"]) desiredBeta -= rotateSpeed;
267
+ desiredBeta = Math.min(
268
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
269
+ (this.maxAngle * Math.PI) / 180,
270
+ );
271
+
272
+ isUpdatingCamera = false;
273
+ };
274
+
275
+ const preventDefault = (e: Event) => {
276
+ e.preventDefault();
277
+ e.stopPropagation();
278
+ };
279
+
280
+ this.dispose = () => {
281
+ canvas.removeEventListener("dragenter", preventDefault);
282
+ canvas.removeEventListener("dragover", preventDefault);
283
+ canvas.removeEventListener("dragleave", preventDefault);
284
+ canvas.removeEventListener("contextmenu", preventDefault);
285
+
286
+ canvas.removeEventListener("mousedown", onMouseDown);
287
+ canvas.removeEventListener("mousemove", onMouseMove);
288
+ canvas.removeEventListener("wheel", onWheel);
289
+
290
+ canvas.removeEventListener("touchstart", onTouchStart);
291
+ canvas.removeEventListener("touchend", onTouchEnd);
292
+ canvas.removeEventListener("touchmove", onTouchMove);
293
+
294
+ if (enableKeyboardControls) {
295
+ window.removeEventListener("keydown", onKeyDown);
296
+ window.removeEventListener("keyup", onKeyUp);
297
+ }
298
+ };
299
+
300
+ if (enableKeyboardControls) {
301
+ window.addEventListener("keydown", onKeyDown);
302
+ window.addEventListener("keyup", onKeyUp);
303
+ }
304
+
305
+ canvas.addEventListener("dragenter", preventDefault);
306
+ canvas.addEventListener("dragover", preventDefault);
307
+ canvas.addEventListener("dragleave", preventDefault);
308
+ canvas.addEventListener("contextmenu", preventDefault);
309
+
310
+ canvas.addEventListener("mousedown", onMouseDown);
311
+ canvas.addEventListener("mousemove", onMouseMove);
312
+ canvas.addEventListener("wheel", onWheel);
313
+
314
+ canvas.addEventListener("touchstart", onTouchStart);
315
+ canvas.addEventListener("touchend", onTouchEnd);
316
+ canvas.addEventListener("touchmove", onTouchMove);
317
+
318
+ this.update();
319
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  }
321
 
322
  export { OrbitControls };