three-gamepad-controls 0.10.5 → 0.11.0
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.
- package/README.md +1 -0
- package/dist/gamepad-arcball-controls.js +66 -1
- package/dist/gamepad-controls.d.ts +9 -0
- package/dist/gamepad-controls.js +55 -24
- package/dist/gamepad-drag-controls.js +79 -1
- package/dist/gamepad-first-person-controls.js +28 -1
- package/dist/gamepad-fly-controls.js +1 -1
- package/dist/gamepad-input.d.ts +156 -0
- package/dist/gamepad-input.js +270 -0
- package/dist/gamepad-manager.js +104 -0
- package/dist/gamepad-orbit-controls.js +1 -1
- package/dist/gamepad-pointer-lock-controls.js +1 -1
- package/dist/gamepad-trackball-controls.js +33 -1
- package/dist/gamepad-transform-controls.js +299 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/utils.js +1 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ bun add three-gamepad-controls
|
|
|
37
37
|
## 📖 Documentation
|
|
38
38
|
|
|
39
39
|
- [Core](./docs/core.md) — The fundamental building blocks.
|
|
40
|
+
- [GamepadInput](./docs/gamepad-input.md) - Gamepad input state reader for gameplay and menus.
|
|
40
41
|
- [GamepadControls](./docs/gamepad-controls.md) — Abstract base class for custom gamepad controls.
|
|
41
42
|
- [GamepadArcballControls](./docs/gamepad-arcball-controls.md) - Gamepad support for `ArcballControls`.
|
|
42
43
|
- [GamepadDragControls](./docs/gamepad-drag-controls.md) - Gamepad support for `DragControls`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
|
|
3
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
+
import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
|
|
4
4
|
import { Vector2, Vector3 } from "three";
|
|
5
5
|
//#region src/gamepad-arcball-controls.ts
|
|
6
6
|
/**
|
|
@@ -103,6 +103,15 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
103
103
|
this.#wasInteracting = activeInput;
|
|
104
104
|
if (!activeInput) this.#endInteraction();
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Applies gamepad stick rotation through Arcball's runtime rotation helper.
|
|
108
|
+
*
|
|
109
|
+
* @param deltaTime - Seconds since the last frame.
|
|
110
|
+
* @param rotateX - Horizontal rotation input after dead zone processing.
|
|
111
|
+
* @param rotateY - Vertical rotation input after dead zone processing.
|
|
112
|
+
* @param rotateSpeed - User-configured rotation speed multiplier.
|
|
113
|
+
* @returns `true` when a rotation was applied.
|
|
114
|
+
*/
|
|
106
115
|
#applyRotation(deltaTime, rotateX, rotateY, rotateSpeed) {
|
|
107
116
|
if (rotateX === 0 && rotateY === 0) return false;
|
|
108
117
|
const controls = this.#controls;
|
|
@@ -119,6 +128,13 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
119
128
|
}
|
|
120
129
|
return changed;
|
|
121
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Applies an Arcball rotation around a specific world axis.
|
|
133
|
+
*
|
|
134
|
+
* @param axis - World axis to rotate around.
|
|
135
|
+
* @param angle - Rotation amount in radians.
|
|
136
|
+
* @returns `true` when ArcballControls produced and applied a transform.
|
|
137
|
+
*/
|
|
122
138
|
#applyRotationAroundAxis(axis, angle) {
|
|
123
139
|
if (axis.lengthSq() === 0 || angle === 0) return false;
|
|
124
140
|
const controls = this.#controls;
|
|
@@ -128,6 +144,15 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
128
144
|
if (changed) controls.object.up.copy(this.#previousUp).applyAxisAngle(axis, -angle);
|
|
129
145
|
return changed;
|
|
130
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Applies gamepad pan by converting stick input to Arcball trackball points.
|
|
149
|
+
*
|
|
150
|
+
* @param deltaTime - Seconds since the last frame.
|
|
151
|
+
* @param panX - Horizontal pan input after dead zone processing.
|
|
152
|
+
* @param panY - Vertical pan input after dead zone processing.
|
|
153
|
+
* @param panSpeed - User-configured pan speed multiplier.
|
|
154
|
+
* @returns `true` when a pan transform was applied.
|
|
155
|
+
*/
|
|
131
156
|
#applyPan(deltaTime, panX, panY, panSpeed) {
|
|
132
157
|
if (panX === 0 && panY === 0) return false;
|
|
133
158
|
const controls = this.#controls;
|
|
@@ -137,6 +162,15 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
137
162
|
this.#panEnd.set(panX * distance, panY * distance, 0);
|
|
138
163
|
return this.#applyTransform(controls.pan(this.#panStart, this.#panEnd));
|
|
139
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Applies trigger-driven zoom around Arcball's gizmo center.
|
|
167
|
+
*
|
|
168
|
+
* @param deltaTime - Seconds since the last frame.
|
|
169
|
+
* @param zoom - Signed zoom input from the configured trigger pair.
|
|
170
|
+
* @param zoomSpeed - User-configured zoom speed multiplier.
|
|
171
|
+
* @param deadzone - Trigger dead zone threshold.
|
|
172
|
+
* @returns `true` when a zoom transform was applied.
|
|
173
|
+
*/
|
|
140
174
|
#applyZoom(deltaTime, zoom, zoomSpeed, deadzone) {
|
|
141
175
|
if (Math.abs(zoom) <= deadzone || this.#controls.scaleFactor <= 0) return false;
|
|
142
176
|
const controls = this.#controls;
|
|
@@ -145,6 +179,15 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
145
179
|
controls.updateMatrixState();
|
|
146
180
|
return this.#applyTransform(controls.scale(size, controls._gizmos.position));
|
|
147
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Applies shoulder-button rotation around the current camera view axis.
|
|
184
|
+
*
|
|
185
|
+
* @param deltaTime - Seconds since the last frame.
|
|
186
|
+
* @param zRotation - Signed z-rotation input from the configured buttons.
|
|
187
|
+
* @param zRotateSpeed - User-configured z-rotation speed multiplier.
|
|
188
|
+
* @param deadzone - Button value dead zone threshold.
|
|
189
|
+
* @returns `true` when a z-rotation transform was applied.
|
|
190
|
+
*/
|
|
148
191
|
#applyZRotation(deltaTime, zRotation, zRotateSpeed, deadzone) {
|
|
149
192
|
if (Math.abs(zRotation) <= deadzone) return false;
|
|
150
193
|
const controls = this.#controls;
|
|
@@ -156,6 +199,12 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
156
199
|
if (changed) controls.object.up.copy(this.#previousUp).applyAxisAngle(controls._rotationAxis, angle);
|
|
157
200
|
return changed;
|
|
158
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Focuses ArcballControls on the given point when one was consumed.
|
|
204
|
+
*
|
|
205
|
+
* @param point - World-space focus point, or `null` when no focus is pending.
|
|
206
|
+
* @returns `true` when focus was applied.
|
|
207
|
+
*/
|
|
159
208
|
#applyFocus(point) {
|
|
160
209
|
if (point === null) return false;
|
|
161
210
|
const controls = this.#controls;
|
|
@@ -164,12 +213,25 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
164
213
|
controls.updateMatrixState();
|
|
165
214
|
return true;
|
|
166
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Applies a transformation returned by an Arcball runtime helper.
|
|
218
|
+
*
|
|
219
|
+
* @param transformation - Arcball transformation matrices, if any.
|
|
220
|
+
* @returns `true` when a transformation was applied.
|
|
221
|
+
*/
|
|
167
222
|
#applyTransform(transformation) {
|
|
168
223
|
if (transformation === void 0) return false;
|
|
169
224
|
this.#controls.applyTransformMatrix(transformation);
|
|
170
225
|
this.#controls.updateMatrixState();
|
|
171
226
|
return true;
|
|
172
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Consumes a focus-button press and resolves the viewport center hit point.
|
|
230
|
+
*
|
|
231
|
+
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
232
|
+
* @param buttonFocus - Button index configured for focus.
|
|
233
|
+
* @returns The center hit point, or `null` when focus should not run.
|
|
234
|
+
*/
|
|
173
235
|
#consumeFocusPoint(gamepad, buttonFocus) {
|
|
174
236
|
const controls = this.#controls;
|
|
175
237
|
const focusPressed = getGamepadButtonPressed(gamepad, buttonFocus);
|
|
@@ -178,6 +240,9 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
178
240
|
if (!shouldFocus || !controls.enabled || !controls.enablePan || !controls.enableFocus || controls.scene === null) return null;
|
|
179
241
|
return controls.unprojectOnObj(this.#centerNdc, controls.object);
|
|
180
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Dispatches Arcball's `end` event when an active gamepad interaction stops.
|
|
245
|
+
*/
|
|
181
246
|
#endInteraction() {
|
|
182
247
|
if (!this.#wasInteracting) return;
|
|
183
248
|
this.#controls.dispatchEvent({ type: "end" });
|
|
@@ -12,12 +12,18 @@ type GamepadControlsEventMap = {
|
|
|
12
12
|
* Fired when a gamepad is connected and set as the active gamepad.
|
|
13
13
|
*/
|
|
14
14
|
connected: {
|
|
15
|
+
/**
|
|
16
|
+
* Gamepad snapshot that became active.
|
|
17
|
+
*/
|
|
15
18
|
gamepad: Gamepad;
|
|
16
19
|
};
|
|
17
20
|
/**
|
|
18
21
|
* Fired when the active gamepad is disconnected.
|
|
19
22
|
*/
|
|
20
23
|
disconnected: {
|
|
24
|
+
/**
|
|
25
|
+
* Gamepad snapshot that was active before disconnection.
|
|
26
|
+
*/
|
|
21
27
|
gamepad: Gamepad;
|
|
22
28
|
};
|
|
23
29
|
};
|
|
@@ -38,6 +44,9 @@ declare abstract class GamepadControls extends EventDispatcher<GamepadControlsEv
|
|
|
38
44
|
* The currently active gamepad, or `null` if no gamepad is connected.
|
|
39
45
|
*/
|
|
40
46
|
gamepad: Gamepad | null;
|
|
47
|
+
/**
|
|
48
|
+
* Creates the base gamepad lifecycle manager and attaches browser listeners.
|
|
49
|
+
*/
|
|
41
50
|
constructor();
|
|
42
51
|
/**
|
|
43
52
|
* Advances the controller by one frame. Call this inside your render loop.
|
package/dist/gamepad-controls.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GamepadManager } from "./gamepad-manager.js";
|
|
2
2
|
import { EventDispatcher } from "three";
|
|
3
3
|
//#region src/gamepad-controls.ts
|
|
4
4
|
/**
|
|
@@ -17,37 +17,63 @@ var GamepadControls = class extends EventDispatcher {
|
|
|
17
17
|
* The currently active gamepad, or `null` if no gamepad is connected.
|
|
18
18
|
*/
|
|
19
19
|
gamepad = null;
|
|
20
|
+
#manager;
|
|
21
|
+
/**
|
|
22
|
+
* Bound browser connection listener kept so it can be removed in {@link dispose}.
|
|
23
|
+
*/
|
|
20
24
|
#onGamepadConnected;
|
|
25
|
+
/**
|
|
26
|
+
* Bound browser disconnection listener kept so it can be removed in {@link dispose}.
|
|
27
|
+
*/
|
|
21
28
|
#onGamepadDisconnected;
|
|
29
|
+
/**
|
|
30
|
+
* Creates the base gamepad lifecycle manager and attaches browser listeners.
|
|
31
|
+
*/
|
|
22
32
|
constructor() {
|
|
23
33
|
super();
|
|
24
|
-
this.#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.#onGamepadDisconnected = (event) => {
|
|
28
|
-
this.onGamepadDisconnected(event.gamepad);
|
|
29
|
-
};
|
|
34
|
+
this.#manager = new GamepadManager();
|
|
35
|
+
this.#onGamepadConnected = this.#handleGamepadConnectedEvent.bind(this);
|
|
36
|
+
this.#onGamepadDisconnected = this.#handleGamepadDisconnectedEvent.bind(this);
|
|
30
37
|
window.addEventListener("gamepadconnected", this.#onGamepadConnected);
|
|
31
38
|
window.addEventListener("gamepaddisconnected", this.#onGamepadDisconnected);
|
|
32
39
|
}
|
|
33
40
|
/**
|
|
41
|
+
* Forwards a browser connection event to the overridable lifecycle hook.
|
|
42
|
+
*
|
|
43
|
+
* @param event - Browser event containing the connected gamepad snapshot.
|
|
44
|
+
*/
|
|
45
|
+
#handleGamepadConnectedEvent(event) {
|
|
46
|
+
this.onGamepadConnected(event.gamepad);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Forwards a browser disconnection event to the overridable lifecycle hook.
|
|
50
|
+
*
|
|
51
|
+
* @param event - Browser event containing the disconnected gamepad snapshot.
|
|
52
|
+
*/
|
|
53
|
+
#handleGamepadDisconnectedEvent(event) {
|
|
54
|
+
this.onGamepadDisconnected(event.gamepad);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
34
57
|
* Advances the controller by one frame. Call this inside your render loop.
|
|
35
58
|
*
|
|
36
59
|
* @param deltaTime - Seconds since the last frame.
|
|
37
60
|
*/
|
|
38
61
|
update(deltaTime) {
|
|
39
62
|
if (!this.enabled) return;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
64
|
+
const { gamepad, connected, disconnected } = this.#manager.update();
|
|
65
|
+
if (connected !== null) {
|
|
66
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
67
|
+
this.onGamepadConnected(connected);
|
|
68
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
69
|
+
} else if (disconnected !== null) {
|
|
70
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
71
|
+
this.onGamepadDisconnected(disconnected);
|
|
72
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
73
|
+
return;
|
|
43
74
|
} else {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (nextGamepad === null) {
|
|
47
|
-
this.onGamepadDisconnected(previousGamepad);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
this.gamepad = nextGamepad;
|
|
75
|
+
this.gamepad = gamepad;
|
|
76
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
51
77
|
}
|
|
52
78
|
if (this.gamepad === null) return;
|
|
53
79
|
this.onUpdate(deltaTime, this.gamepad);
|
|
@@ -59,6 +85,7 @@ var GamepadControls = class extends EventDispatcher {
|
|
|
59
85
|
window.removeEventListener("gamepadconnected", this.#onGamepadConnected);
|
|
60
86
|
window.removeEventListener("gamepaddisconnected", this.#onGamepadDisconnected);
|
|
61
87
|
this.gamepad = null;
|
|
88
|
+
this.#manager.activeGamepad = null;
|
|
62
89
|
this.enabled = false;
|
|
63
90
|
}
|
|
64
91
|
/**
|
|
@@ -70,11 +97,14 @@ var GamepadControls = class extends EventDispatcher {
|
|
|
70
97
|
* @param gamepad - The gamepad that just connected.
|
|
71
98
|
*/
|
|
72
99
|
onGamepadConnected(gamepad) {
|
|
73
|
-
|
|
74
|
-
this.gamepad
|
|
100
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
101
|
+
if (!this.#manager.connect(gamepad)) return;
|
|
102
|
+
const connectedGamepad = this.#manager.activeGamepad;
|
|
103
|
+
if (connectedGamepad === null) return;
|
|
104
|
+
this.gamepad = connectedGamepad;
|
|
75
105
|
this.dispatchEvent({
|
|
76
106
|
type: "connected",
|
|
77
|
-
gamepad
|
|
107
|
+
gamepad: connectedGamepad
|
|
78
108
|
});
|
|
79
109
|
}
|
|
80
110
|
/**
|
|
@@ -86,12 +116,13 @@ var GamepadControls = class extends EventDispatcher {
|
|
|
86
116
|
* @param gamepad - The gamepad that just disconnected.
|
|
87
117
|
*/
|
|
88
118
|
onGamepadDisconnected(gamepad) {
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
119
|
+
this.#manager.activeGamepad = this.gamepad;
|
|
120
|
+
const disconnectedGamepad = this.#manager.disconnect(gamepad);
|
|
121
|
+
if (disconnectedGamepad === null) return;
|
|
122
|
+
this.gamepad = this.#manager.activeGamepad;
|
|
92
123
|
this.dispatchEvent({
|
|
93
124
|
type: "disconnected",
|
|
94
|
-
gamepad:
|
|
125
|
+
gamepad: disconnectedGamepad
|
|
95
126
|
});
|
|
96
127
|
}
|
|
97
128
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed } from "./utils.js";
|
|
3
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
+
import { applyGamepadDeadzone, getGamepadButtonPressed } from "./utils.js";
|
|
4
4
|
import { Matrix4, Vector2, Vector3 } from "three";
|
|
5
5
|
//#region src/gamepad-drag-controls.ts
|
|
6
6
|
/**
|
|
@@ -111,6 +111,12 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
111
111
|
this.#clearHover();
|
|
112
112
|
super.onGamepadDisconnected(gamepad);
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Updates the selected object from gamepad drag and rotation input.
|
|
116
|
+
*
|
|
117
|
+
* @param deltaTime - Seconds since the last frame.
|
|
118
|
+
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
119
|
+
*/
|
|
114
120
|
#updateSelected(deltaTime, gamepad) {
|
|
115
121
|
const selected = this.#selected;
|
|
116
122
|
if (selected === null) return;
|
|
@@ -126,6 +132,15 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
126
132
|
object: selected
|
|
127
133
|
});
|
|
128
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Moves the selected object in the camera-facing plane.
|
|
137
|
+
*
|
|
138
|
+
* @param deltaTime - Seconds since the last frame.
|
|
139
|
+
* @param dragX - Horizontal drag input after dead zone processing.
|
|
140
|
+
* @param dragY - Vertical drag input after dead zone processing.
|
|
141
|
+
* @param dragSpeed - User-configured drag speed multiplier.
|
|
142
|
+
* @returns `true` when the selected object moved.
|
|
143
|
+
*/
|
|
129
144
|
#applyDrag(deltaTime, dragX, dragY, dragSpeed) {
|
|
130
145
|
if (this.#selected === null || dragX === 0 && dragY === 0) return false;
|
|
131
146
|
this.#updateCameraAxes();
|
|
@@ -136,6 +151,15 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
136
151
|
this.#applySelectedWorldPosition();
|
|
137
152
|
return true;
|
|
138
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Rotates the selected object around camera-relative world axes.
|
|
156
|
+
*
|
|
157
|
+
* @param deltaTime - Seconds since the last frame.
|
|
158
|
+
* @param rotateX - Horizontal rotation input after dead zone processing.
|
|
159
|
+
* @param rotateY - Vertical rotation input after dead zone processing.
|
|
160
|
+
* @param rotateSpeed - User-configured rotation speed multiplier.
|
|
161
|
+
* @returns `true` when the selected object rotated.
|
|
162
|
+
*/
|
|
139
163
|
#applyRotation(deltaTime, rotateX, rotateY, rotateSpeed) {
|
|
140
164
|
const selected = this.#selected;
|
|
141
165
|
if (selected === null || rotateX === 0 && rotateY === 0) return false;
|
|
@@ -145,6 +169,11 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
145
169
|
if (rotateY !== 0) selected.rotateOnWorldAxis(this.#cameraRight, rotateY * scale);
|
|
146
170
|
return true;
|
|
147
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Raycasts from the center of the viewport into DragControls objects.
|
|
174
|
+
*
|
|
175
|
+
* @returns The closest center hit, or `undefined` when nothing is hit.
|
|
176
|
+
*/
|
|
148
177
|
#intersectCenter() {
|
|
149
178
|
const controls = this.#controls;
|
|
150
179
|
this.#intersections.length = 0;
|
|
@@ -152,6 +181,11 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
152
181
|
controls.raycaster.intersectObjects(controls.objects, controls.recursive, this.#intersections);
|
|
153
182
|
return this.#intersections[0];
|
|
154
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Updates DragControls hover state for the object under the center reticle.
|
|
186
|
+
*
|
|
187
|
+
* @param object - Object currently under the reticle, or `null`.
|
|
188
|
+
*/
|
|
155
189
|
#updateHover(object) {
|
|
156
190
|
if (this.#hovered === object) return;
|
|
157
191
|
this.#clearHover();
|
|
@@ -162,6 +196,9 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
162
196
|
object
|
|
163
197
|
});
|
|
164
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Clears the current hover object and dispatches `hoveroff` when needed.
|
|
201
|
+
*/
|
|
165
202
|
#clearHover() {
|
|
166
203
|
if (this.#hovered === null) return;
|
|
167
204
|
const object = this.#hovered;
|
|
@@ -171,6 +208,11 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
171
208
|
object
|
|
172
209
|
});
|
|
173
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Selects an object and dispatches DragControls `dragstart`.
|
|
213
|
+
*
|
|
214
|
+
* @param object - Object hit by the center reticle.
|
|
215
|
+
*/
|
|
174
216
|
#grabObject(object) {
|
|
175
217
|
const selected = this.#getSelectedObject(object);
|
|
176
218
|
selected.updateWorldMatrix(true, false);
|
|
@@ -181,6 +223,9 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
181
223
|
object: selected
|
|
182
224
|
});
|
|
183
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Releases the selected object and dispatches DragControls `dragend`.
|
|
228
|
+
*/
|
|
184
229
|
#releaseSelected() {
|
|
185
230
|
if (this.#selected === null) return;
|
|
186
231
|
const selected = this.#selected;
|
|
@@ -190,10 +235,22 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
190
235
|
object: selected
|
|
191
236
|
});
|
|
192
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Resolves which object should be dragged for a reticle hit.
|
|
240
|
+
*
|
|
241
|
+
* @param object - Object hit by the center reticle.
|
|
242
|
+
* @returns The hit object, or its outermost group when group dragging is enabled.
|
|
243
|
+
*/
|
|
193
244
|
#getSelectedObject(object) {
|
|
194
245
|
if (!this.#controls.transformGroup) return object;
|
|
195
246
|
return this.#findOutermostGroup(object) ?? object;
|
|
196
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Finds the highest ancestor that is a Three.js `Group`.
|
|
250
|
+
*
|
|
251
|
+
* @param object - Object where the ancestor search starts.
|
|
252
|
+
* @returns The outermost group ancestor, or `null` when none exists.
|
|
253
|
+
*/
|
|
197
254
|
#findOutermostGroup(object) {
|
|
198
255
|
let group = null;
|
|
199
256
|
let current = object;
|
|
@@ -203,6 +260,9 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
203
260
|
}
|
|
204
261
|
return group;
|
|
205
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Writes the accumulated world-space selected position back to the object.
|
|
265
|
+
*/
|
|
206
266
|
#applySelectedWorldPosition() {
|
|
207
267
|
const selected = this.#selected;
|
|
208
268
|
if (selected === null) return;
|
|
@@ -217,12 +277,18 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
217
277
|
selected.position.copy(this.#selectedLocalPosition);
|
|
218
278
|
selected.updateMatrixWorld();
|
|
219
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Refreshes camera-relative axes used for dragging and rotation.
|
|
282
|
+
*/
|
|
220
283
|
#updateCameraAxes() {
|
|
221
284
|
const camera = this.#controls.object;
|
|
222
285
|
this.#cameraRight.set(1, 0, 0).applyQuaternion(camera.quaternion).normalize();
|
|
223
286
|
this.#cameraUp.set(0, 1, 0).applyQuaternion(camera.quaternion).normalize();
|
|
224
287
|
camera.getWorldDirection(this.#cameraForward).normalize();
|
|
225
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Computes the world-space viewport size at the selected object's depth.
|
|
291
|
+
*/
|
|
226
292
|
#updateViewSizeAtSelectedDepth() {
|
|
227
293
|
const camera = this.#controls.object;
|
|
228
294
|
if (this.#isOrthographicCamera(camera)) {
|
|
@@ -238,9 +304,21 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
238
304
|
}
|
|
239
305
|
this.#viewSize.set(1, 1);
|
|
240
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Narrows a Three.js camera to `PerspectiveCamera`.
|
|
309
|
+
*
|
|
310
|
+
* @param camera - Camera to inspect.
|
|
311
|
+
* @returns `true` when the camera is perspective.
|
|
312
|
+
*/
|
|
241
313
|
#isPerspectiveCamera(camera) {
|
|
242
314
|
return camera.isPerspectiveCamera === true;
|
|
243
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Narrows a Three.js camera to `OrthographicCamera`.
|
|
318
|
+
*
|
|
319
|
+
* @param camera - Camera to inspect.
|
|
320
|
+
* @returns `true` when the camera is orthographic.
|
|
321
|
+
*/
|
|
244
322
|
#isOrthographicCamera(camera) {
|
|
245
323
|
return camera.isOrthographicCamera === true;
|
|
246
324
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
|
-
import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
|
|
3
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
+
import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
|
|
4
4
|
import { MathUtils, Spherical, Vector3 } from "three";
|
|
5
5
|
//#region src/gamepad-first-person-controls.ts
|
|
6
6
|
/**
|
|
@@ -58,6 +58,18 @@ var GamepadFirstPersonControls = class extends GamepadControls {
|
|
|
58
58
|
this.#applyMovement(deltaTime, gamepad, moveSpeed, deadzone, axisMoveForward, axisMoveRight, buttonMoveUp, buttonMoveDown);
|
|
59
59
|
this.#applyLook(deltaTime, gamepad, lookSpeed, deadzone, axisLookX, axisLookY);
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Applies local translation input to FirstPersonControls' object.
|
|
63
|
+
*
|
|
64
|
+
* @param deltaTime - Seconds since the last frame.
|
|
65
|
+
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
66
|
+
* @param moveSpeed - User-configured movement speed multiplier.
|
|
67
|
+
* @param deadzone - Axis and trigger dead zone threshold.
|
|
68
|
+
* @param axisMoveForward - Axis index for forward and backward movement.
|
|
69
|
+
* @param axisMoveRight - Axis index for right and left strafe movement.
|
|
70
|
+
* @param buttonMoveUp - Button index for upward movement.
|
|
71
|
+
* @param buttonMoveDown - Button index for downward movement.
|
|
72
|
+
*/
|
|
61
73
|
#applyMovement(deltaTime, gamepad, moveSpeed, deadzone, axisMoveForward, axisMoveRight, buttonMoveUp, buttonMoveDown) {
|
|
62
74
|
const controls = this.#controls;
|
|
63
75
|
const moveMult = deltaTime * controls.movementSpeed * moveSpeed;
|
|
@@ -77,6 +89,16 @@ var GamepadFirstPersonControls = class extends GamepadControls {
|
|
|
77
89
|
if (up > deadzone) controls.object.translateY(up * moveMult);
|
|
78
90
|
if (down > deadzone) controls.object.translateY(-down * moveMult);
|
|
79
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Applies camera look input while keeping FirstPersonControls state in sync.
|
|
94
|
+
*
|
|
95
|
+
* @param deltaTime - Seconds since the last frame.
|
|
96
|
+
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
97
|
+
* @param lookSpeed - User-configured look speed multiplier.
|
|
98
|
+
* @param deadzone - Axis dead zone threshold.
|
|
99
|
+
* @param axisLookX - Axis index for yaw input.
|
|
100
|
+
* @param axisLookY - Axis index for pitch input.
|
|
101
|
+
*/
|
|
80
102
|
#applyLook(deltaTime, gamepad, lookSpeed, deadzone, axisLookX, axisLookY) {
|
|
81
103
|
const lookX = applyGamepadDeadzone(gamepad.axes[axisLookX] ?? 0, deadzone);
|
|
82
104
|
const lookY = applyGamepadDeadzone(gamepad.axes[axisLookY] ?? 0, deadzone);
|
|
@@ -98,6 +120,11 @@ var GamepadFirstPersonControls = class extends GamepadControls {
|
|
|
98
120
|
controls._lat = lat;
|
|
99
121
|
controls._lon = lon;
|
|
100
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Reads the current FirstPersonControls orientation, deriving it if needed.
|
|
125
|
+
*
|
|
126
|
+
* @returns Current latitude and longitude in degrees.
|
|
127
|
+
*/
|
|
101
128
|
#getOrientation() {
|
|
102
129
|
const { _lat, _lon } = this.#controls;
|
|
103
130
|
if (Number.isFinite(_lat) && Number.isFinite(_lon)) return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
|
|
3
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
+
import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
|
|
4
4
|
import { Quaternion } from "three";
|
|
5
5
|
//#region src/gamepad-fly-controls.ts
|
|
6
6
|
/**
|