three-gamepad-controls 0.10.6 → 0.12.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 +15 -0
- package/dist/gamepad-arcball-controls.d.ts +1 -2
- package/dist/gamepad-arcball-controls.js +75 -16
- package/dist/gamepad-controls.d.ts +25 -12
- package/dist/gamepad-controls.js +58 -51
- package/dist/gamepad-drag-controls.d.ts +1 -2
- package/dist/gamepad-drag-controls.js +86 -13
- package/dist/gamepad-first-person-controls.d.ts +1 -2
- package/dist/gamepad-first-person-controls.js +38 -13
- package/dist/gamepad-fly-controls.d.ts +1 -2
- package/dist/gamepad-fly-controls.js +9 -10
- package/dist/gamepad-input.d.ts +156 -0
- package/dist/gamepad-input.js +310 -0
- package/dist/gamepad-orbit-controls.d.ts +1 -2
- package/dist/gamepad-orbit-controls.js +8 -9
- package/dist/gamepad-pointer-lock-controls.d.ts +1 -2
- package/dist/gamepad-pointer-lock-controls.js +6 -7
- package/dist/gamepad-trackball-controls.d.ts +1 -2
- package/dist/gamepad-trackball-controls.js +45 -15
- package/dist/gamepad-transform-controls.d.ts +1 -2
- package/dist/gamepad-transform-controls.js +317 -21
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +3 -3
- package/dist/utils.js +0 -41
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { GamepadManager } from "./gamepad-manager.js";
|
|
2
|
+
import { EventDispatcher } from "three";
|
|
3
|
+
//#region src/gamepad-input.ts
|
|
4
|
+
const DEFAULT_GAMEPAD_INPUT_OPTIONS = { deadzone: .1 };
|
|
5
|
+
/**
|
|
6
|
+
* Returns `value` unchanged, or `0` if below the dead zone `threshold`.
|
|
7
|
+
*
|
|
8
|
+
* Kept private to this module because `GamepadInput` is the only public API
|
|
9
|
+
* that currently exposes processed axis values.
|
|
10
|
+
*
|
|
11
|
+
* @param value - Raw axis or trigger value, typically in `[-1, 1]`.
|
|
12
|
+
* @param threshold - Dead zone size; values below this magnitude are zeroed.
|
|
13
|
+
* @returns The original value when outside the dead zone, otherwise `0`.
|
|
14
|
+
*/
|
|
15
|
+
const applyGamepadDeadzone = (value, threshold) => {
|
|
16
|
+
return Math.abs(value) < threshold ? 0 : value;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Returns whether a gamepad button is currently pressed.
|
|
20
|
+
*
|
|
21
|
+
* Missing buttons are treated as not pressed.
|
|
22
|
+
*
|
|
23
|
+
* @param gamepad - Gamepad snapshot to read from.
|
|
24
|
+
* @param button - Button index to inspect.
|
|
25
|
+
* @returns `true` when the button exists and is pressed, otherwise `false`.
|
|
26
|
+
*/
|
|
27
|
+
const getGamepadButtonPressed = (gamepad, button) => {
|
|
28
|
+
return gamepad.buttons[button]?.pressed ?? false;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Returns the analog value for a gamepad button.
|
|
32
|
+
*
|
|
33
|
+
* Some digital buttons may report `pressed` without a meaningful non-zero
|
|
34
|
+
* `value`, so pressed buttons fall back to `1`.
|
|
35
|
+
*
|
|
36
|
+
* @param gamepad - Gamepad snapshot to read from.
|
|
37
|
+
* @param button - Button index to inspect.
|
|
38
|
+
* @returns The button value, `1` for pressed digital buttons, or `0` when unavailable.
|
|
39
|
+
*/
|
|
40
|
+
const getGamepadButtonValue = (gamepad, button) => {
|
|
41
|
+
const gamepadButton = gamepad.buttons[button];
|
|
42
|
+
if (gamepadButton === void 0) return 0;
|
|
43
|
+
if (gamepadButton.value !== 0) return gamepadButton.value;
|
|
44
|
+
return gamepadButton.pressed ? 1 : 0;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Gamepad input state reader for gameplay, menus, and custom actions.
|
|
48
|
+
*
|
|
49
|
+
* Call {@link update} once per frame before reading button transitions or axes.
|
|
50
|
+
*/
|
|
51
|
+
var GamepadInput = class extends EventDispatcher {
|
|
52
|
+
/**
|
|
53
|
+
* When `false`, input polling is paused.
|
|
54
|
+
* @default true
|
|
55
|
+
*/
|
|
56
|
+
enabled = true;
|
|
57
|
+
#manager;
|
|
58
|
+
#options;
|
|
59
|
+
#pressedButtons;
|
|
60
|
+
#previousPressedButtons;
|
|
61
|
+
/**
|
|
62
|
+
* Bound browser connection listener kept so it can be removed in {@link dispose}.
|
|
63
|
+
*/
|
|
64
|
+
#onGamepadConnected;
|
|
65
|
+
/**
|
|
66
|
+
* Bound browser disconnection listener kept so it can be removed in {@link dispose}.
|
|
67
|
+
*/
|
|
68
|
+
#onGamepadDisconnected;
|
|
69
|
+
#gamepad = null;
|
|
70
|
+
/**
|
|
71
|
+
* Creates a gamepad input reader.
|
|
72
|
+
*
|
|
73
|
+
* @param options - Optional overrides for the default input behavior.
|
|
74
|
+
*/
|
|
75
|
+
constructor(options) {
|
|
76
|
+
super();
|
|
77
|
+
this.#manager = new GamepadManager();
|
|
78
|
+
this.#options = {
|
|
79
|
+
...DEFAULT_GAMEPAD_INPUT_OPTIONS,
|
|
80
|
+
...options
|
|
81
|
+
};
|
|
82
|
+
this.#pressedButtons = /* @__PURE__ */ new Set();
|
|
83
|
+
this.#previousPressedButtons = /* @__PURE__ */ new Set();
|
|
84
|
+
this.#onGamepadConnected = this.#handleGamepadConnectedEvent.bind(this);
|
|
85
|
+
this.#onGamepadDisconnected = this.#handleGamepadDisconnectedEvent.bind(this);
|
|
86
|
+
window.addEventListener("gamepadconnected", this.#onGamepadConnected);
|
|
87
|
+
window.addEventListener("gamepaddisconnected", this.#onGamepadDisconnected);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Forwards a browser connection event to the active-gamepad adoption logic.
|
|
91
|
+
*
|
|
92
|
+
* @param event - Browser event containing the connected gamepad snapshot.
|
|
93
|
+
*/
|
|
94
|
+
#handleGamepadConnectedEvent(event) {
|
|
95
|
+
this.#handleGamepadConnected(event.gamepad);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Forwards a browser disconnection event to active-gamepad cleanup.
|
|
99
|
+
*
|
|
100
|
+
* @param event - Browser event containing the disconnected gamepad snapshot.
|
|
101
|
+
*/
|
|
102
|
+
#handleGamepadDisconnectedEvent(event) {
|
|
103
|
+
this.#handleGamepadDisconnected(event.gamepad);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The currently active gamepad, or `null` if no gamepad is connected.
|
|
107
|
+
*
|
|
108
|
+
* @returns The active gamepad snapshot, or `null`.
|
|
109
|
+
*/
|
|
110
|
+
get gamepad() {
|
|
111
|
+
return this.#gamepad;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Whether a gamepad is currently active.
|
|
115
|
+
*
|
|
116
|
+
* @returns `true` when a gamepad is active, otherwise `false`.
|
|
117
|
+
*/
|
|
118
|
+
get connected() {
|
|
119
|
+
return this.#gamepad !== null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Mapping reported by the active gamepad, or `null` when none is active.
|
|
123
|
+
*
|
|
124
|
+
* @returns The active gamepad mapping, or `null`.
|
|
125
|
+
*/
|
|
126
|
+
get mapping() {
|
|
127
|
+
return this.#gamepad?.mapping ?? null;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Raw active gamepad snapshot, or `null` if no gamepad is connected.
|
|
131
|
+
*
|
|
132
|
+
* @returns The raw active gamepad snapshot, or `null`.
|
|
133
|
+
*/
|
|
134
|
+
get rawGamepad() {
|
|
135
|
+
return this.#gamepad;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Polls the gamepad and refreshes current and previous button state.
|
|
139
|
+
*/
|
|
140
|
+
update() {
|
|
141
|
+
if (!this.enabled) return;
|
|
142
|
+
const { gamepad, connected, disconnected } = this.#manager.update();
|
|
143
|
+
if (connected !== null) {
|
|
144
|
+
this.#gamepad = gamepad;
|
|
145
|
+
this.#syncButtonState({ seedPrevious: true });
|
|
146
|
+
this.dispatchEvent({
|
|
147
|
+
type: "connected",
|
|
148
|
+
gamepad: connected
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (disconnected !== null) {
|
|
153
|
+
this.#gamepad = null;
|
|
154
|
+
this.#clearButtonState();
|
|
155
|
+
this.dispatchEvent({
|
|
156
|
+
type: "disconnected",
|
|
157
|
+
gamepad: disconnected
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.#gamepad = gamepad;
|
|
162
|
+
this.#syncButtonState({ seedPrevious: false });
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Removes all window-level event listeners attached by this input reader.
|
|
166
|
+
*/
|
|
167
|
+
dispose() {
|
|
168
|
+
window.removeEventListener("gamepadconnected", this.#onGamepadConnected);
|
|
169
|
+
window.removeEventListener("gamepaddisconnected", this.#onGamepadDisconnected);
|
|
170
|
+
this.#manager.activeGamepad = null;
|
|
171
|
+
this.#gamepad = null;
|
|
172
|
+
this.#clearButtonState();
|
|
173
|
+
this.enabled = false;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Returns whether a button is currently pressed.
|
|
177
|
+
*
|
|
178
|
+
* @param button - Button index to inspect.
|
|
179
|
+
* @returns `true` when the button is currently pressed, otherwise `false`.
|
|
180
|
+
*/
|
|
181
|
+
isPressed(button) {
|
|
182
|
+
return this.#pressedButtons.has(button);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Returns whether a button was pressed during the latest update.
|
|
186
|
+
*
|
|
187
|
+
* @param button - Button index to inspect.
|
|
188
|
+
* @returns `true` only on the frame where the button transitions to pressed.
|
|
189
|
+
*/
|
|
190
|
+
wasPressed(button) {
|
|
191
|
+
return this.#pressedButtons.has(button) && !this.#previousPressedButtons.has(button);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Returns whether a button was released during the latest update.
|
|
195
|
+
*
|
|
196
|
+
* @param button - Button index to inspect.
|
|
197
|
+
* @returns `true` only on the frame where the button transitions to released.
|
|
198
|
+
*/
|
|
199
|
+
wasReleased(button) {
|
|
200
|
+
return !this.#pressedButtons.has(button) && this.#previousPressedButtons.has(button);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Returns the current analog value for a button.
|
|
204
|
+
*
|
|
205
|
+
* @param button - Button index to inspect.
|
|
206
|
+
* @returns The button value, `1` for pressed digital buttons, or `0`.
|
|
207
|
+
*/
|
|
208
|
+
buttonValue(button) {
|
|
209
|
+
if (this.#gamepad === null) return 0;
|
|
210
|
+
return getGamepadButtonValue(this.#gamepad, button);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Returns the current value of an axis after dead zone processing.
|
|
214
|
+
*
|
|
215
|
+
* @param axis - Axis index to inspect.
|
|
216
|
+
* @param options - Optional per-read axis options.
|
|
217
|
+
* @returns Axis value after dead zone processing, or `0` when unavailable.
|
|
218
|
+
*/
|
|
219
|
+
axis(axis, options) {
|
|
220
|
+
return applyGamepadDeadzone(this.#gamepad?.axes[axis] ?? 0, this.#getDeadzone(options));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Returns a two-axis stick after dead zone processing.
|
|
224
|
+
*
|
|
225
|
+
* @param xAxis - Horizontal axis index.
|
|
226
|
+
* @param yAxis - Vertical axis index.
|
|
227
|
+
* @param options - Optional per-read axis options.
|
|
228
|
+
* @returns Object containing processed `x` and `y` values.
|
|
229
|
+
*/
|
|
230
|
+
stick(xAxis, yAxis, options) {
|
|
231
|
+
return {
|
|
232
|
+
x: this.axis(xAxis, options),
|
|
233
|
+
y: this.axis(yAxis, options)
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Handles a browser connection event and adopts the gamepad when possible.
|
|
238
|
+
*
|
|
239
|
+
* Button state is seeded as both current and previous so an already-held
|
|
240
|
+
* button does not produce a synthetic `wasPressed` transition on connect.
|
|
241
|
+
*
|
|
242
|
+
* @param gamepad - Browser-provided connected gamepad snapshot.
|
|
243
|
+
*/
|
|
244
|
+
#handleGamepadConnected(gamepad) {
|
|
245
|
+
if (!this.#manager.connect(gamepad)) return;
|
|
246
|
+
this.#gamepad = this.#manager.activeGamepad;
|
|
247
|
+
this.#syncButtonState({ seedPrevious: true });
|
|
248
|
+
this.dispatchEvent({
|
|
249
|
+
type: "connected",
|
|
250
|
+
gamepad
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Handles a browser disconnection event for the active gamepad.
|
|
255
|
+
*
|
|
256
|
+
* Button state is cleared instead of diffed so disconnecting a controller
|
|
257
|
+
* does not produce synthetic `wasReleased` transitions.
|
|
258
|
+
*
|
|
259
|
+
* @param gamepad - Browser-provided disconnected gamepad snapshot.
|
|
260
|
+
*/
|
|
261
|
+
#handleGamepadDisconnected(gamepad) {
|
|
262
|
+
const disconnectedGamepad = this.#manager.disconnect(gamepad);
|
|
263
|
+
if (disconnectedGamepad === null) return;
|
|
264
|
+
this.#gamepad = null;
|
|
265
|
+
this.#clearButtonState();
|
|
266
|
+
this.dispatchEvent({
|
|
267
|
+
type: "disconnected",
|
|
268
|
+
gamepad: disconnectedGamepad
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Refreshes current and previous pressed-button sets from the active snapshot.
|
|
273
|
+
*
|
|
274
|
+
* When `seedPrevious` is `true`, the refreshed current state is copied into
|
|
275
|
+
* the previous state. This intentionally suppresses transition events on the
|
|
276
|
+
* first frame after adopting a gamepad.
|
|
277
|
+
*
|
|
278
|
+
* @param options - Button state synchronization options.
|
|
279
|
+
* @param options.seedPrevious - Whether to seed previous state from current state.
|
|
280
|
+
*/
|
|
281
|
+
#syncButtonState({ seedPrevious }) {
|
|
282
|
+
this.#previousPressedButtons.clear();
|
|
283
|
+
for (const button of this.#pressedButtons) this.#previousPressedButtons.add(button);
|
|
284
|
+
this.#pressedButtons.clear();
|
|
285
|
+
if (this.#gamepad !== null) {
|
|
286
|
+
for (let index = 0; index < this.#gamepad.buttons.length; index += 1) if (getGamepadButtonPressed(this.#gamepad, index)) this.#pressedButtons.add(index);
|
|
287
|
+
}
|
|
288
|
+
if (!seedPrevious) return;
|
|
289
|
+
this.#previousPressedButtons.clear();
|
|
290
|
+
for (const button of this.#pressedButtons) this.#previousPressedButtons.add(button);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Clears all stored button state.
|
|
294
|
+
*/
|
|
295
|
+
#clearButtonState() {
|
|
296
|
+
this.#pressedButtons.clear();
|
|
297
|
+
this.#previousPressedButtons.clear();
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Resolves the dead zone for a single axis or stick read.
|
|
301
|
+
*
|
|
302
|
+
* @param options - Optional per-read axis options.
|
|
303
|
+
* @returns The per-read dead zone when provided, otherwise the instance default.
|
|
304
|
+
*/
|
|
305
|
+
#getDeadzone(options) {
|
|
306
|
+
return options?.deadzone ?? this.#options.deadzone;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
//#endregion
|
|
310
|
+
export { GamepadInput };
|
|
@@ -77,9 +77,8 @@ declare class GamepadOrbitControls extends GamepadControls {
|
|
|
77
77
|
* Maps the current gamepad state to `OrbitControls` rotation, pan, and dolly.
|
|
78
78
|
*
|
|
79
79
|
* @param deltaTime - Seconds since the last frame.
|
|
80
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
81
80
|
*/
|
|
82
|
-
protected onUpdate(deltaTime: number
|
|
81
|
+
protected onUpdate(deltaTime: number): void;
|
|
83
82
|
}
|
|
84
83
|
//#endregion
|
|
85
84
|
export { GamepadOrbitControls, GamepadOrbitControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
|
|
4
3
|
//#region src/gamepad-orbit-controls.ts
|
|
5
4
|
/**
|
|
6
5
|
* Default options merged in the constructor when no explicit configuration is provided.
|
|
@@ -43,19 +42,19 @@ var GamepadOrbitControls = class extends GamepadControls {
|
|
|
43
42
|
* Maps the current gamepad state to `OrbitControls` rotation, pan, and dolly.
|
|
44
43
|
*
|
|
45
44
|
* @param deltaTime - Seconds since the last frame.
|
|
46
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
47
45
|
*/
|
|
48
|
-
onUpdate(deltaTime
|
|
46
|
+
onUpdate(deltaTime) {
|
|
49
47
|
const { rotateSpeed, panSpeed, zoomSpeed, deadzone, axisRotateX, axisRotateY, axisPanX, axisPanY, buttonDollyIn, buttonDollyOut } = this.#options;
|
|
50
|
-
const
|
|
51
|
-
const
|
|
48
|
+
const input = this.gamepadInput;
|
|
49
|
+
const rotX = input.axis(axisRotateX, { deadzone });
|
|
50
|
+
const rotY = input.axis(axisRotateY, { deadzone });
|
|
52
51
|
if (rotX !== 0) this.#controls.rotateLeft(rotX * rotateSpeed * deltaTime * Math.PI);
|
|
53
52
|
if (rotY !== 0) this.#controls.rotateUp(rotY * rotateSpeed * deltaTime * Math.PI);
|
|
54
|
-
const panX =
|
|
55
|
-
const panY =
|
|
53
|
+
const panX = input.axis(axisPanX, { deadzone });
|
|
54
|
+
const panY = input.axis(axisPanY, { deadzone });
|
|
56
55
|
if (panX !== 0 || panY !== 0) this.#controls.pan(panX * panSpeed * deltaTime * 500, panY * panSpeed * deltaTime * 500);
|
|
57
|
-
const triggerIn =
|
|
58
|
-
const triggerOut =
|
|
56
|
+
const triggerIn = input.buttonValue(buttonDollyIn);
|
|
57
|
+
const triggerOut = input.buttonValue(buttonDollyOut);
|
|
59
58
|
if (triggerIn > deadzone) this.#controls.dollyIn(1 / (1 + zoomSpeed * triggerIn * deltaTime));
|
|
60
59
|
if (triggerOut > deadzone) this.#controls.dollyOut(1 / (1 + zoomSpeed * triggerOut * deltaTime));
|
|
61
60
|
}
|
|
@@ -63,9 +63,8 @@ declare class GamepadPointerLockControls extends GamepadControls {
|
|
|
63
63
|
* Maps the current gamepad state to `PointerLockControls` movement and look.
|
|
64
64
|
*
|
|
65
65
|
* @param deltaTime - Seconds since the last frame.
|
|
66
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
67
66
|
*/
|
|
68
|
-
protected onUpdate(deltaTime: number
|
|
67
|
+
protected onUpdate(deltaTime: number): void;
|
|
69
68
|
}
|
|
70
69
|
//#endregion
|
|
71
70
|
export { GamepadPointerLockControls, GamepadPointerLockControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone } from "./utils.js";
|
|
4
3
|
import { Euler } from "three";
|
|
5
4
|
//#region src/gamepad-pointer-lock-controls.ts
|
|
6
5
|
/**
|
|
@@ -44,16 +43,16 @@ var GamepadPointerLockControls = class extends GamepadControls {
|
|
|
44
43
|
* Maps the current gamepad state to `PointerLockControls` movement and look.
|
|
45
44
|
*
|
|
46
45
|
* @param deltaTime - Seconds since the last frame.
|
|
47
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
48
46
|
*/
|
|
49
|
-
onUpdate(deltaTime
|
|
47
|
+
onUpdate(deltaTime) {
|
|
50
48
|
const { moveSpeed, lookSpeed, deadzone, axisMoveForward, axisMoveRight, axisLookX, axisLookY } = this.#options;
|
|
51
|
-
const
|
|
52
|
-
const
|
|
49
|
+
const input = this.gamepadInput;
|
|
50
|
+
const fwd = input.axis(axisMoveForward, { deadzone });
|
|
51
|
+
const strafe = input.axis(axisMoveRight, { deadzone });
|
|
53
52
|
if (fwd !== 0) this.#controls.moveForward(-fwd * moveSpeed * deltaTime);
|
|
54
53
|
if (strafe !== 0) this.#controls.moveRight(strafe * moveSpeed * deltaTime);
|
|
55
|
-
const lookX =
|
|
56
|
-
const lookY =
|
|
54
|
+
const lookX = input.axis(axisLookX, { deadzone });
|
|
55
|
+
const lookY = input.axis(axisLookY, { deadzone });
|
|
57
56
|
if (lookX !== 0 || lookY !== 0) {
|
|
58
57
|
const camera = this.#controls.object;
|
|
59
58
|
const scale = lookSpeed * this.#controls.pointerSpeed * deltaTime * Math.PI;
|
|
@@ -77,9 +77,8 @@ declare class GamepadTrackballControls extends GamepadControls {
|
|
|
77
77
|
* Maps the current gamepad state to `TrackballControls` rotation, pan, and zoom.
|
|
78
78
|
*
|
|
79
79
|
* @param deltaTime - Seconds since the last frame.
|
|
80
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
81
80
|
*/
|
|
82
|
-
protected onUpdate(deltaTime: number
|
|
81
|
+
protected onUpdate(deltaTime: number): void;
|
|
83
82
|
}
|
|
84
83
|
//#endregion
|
|
85
84
|
export { GamepadTrackballControls, GamepadTrackballControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
|
|
4
3
|
//#region src/gamepad-trackball-controls.ts
|
|
5
4
|
/**
|
|
6
5
|
* Default options merged in the constructor when no explicit configuration is provided.
|
|
@@ -43,55 +42,86 @@ var GamepadTrackballControls = class extends GamepadControls {
|
|
|
43
42
|
* Maps the current gamepad state to `TrackballControls` rotation, pan, and zoom.
|
|
44
43
|
*
|
|
45
44
|
* @param deltaTime - Seconds since the last frame.
|
|
46
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
47
45
|
*/
|
|
48
|
-
onUpdate(deltaTime
|
|
46
|
+
onUpdate(deltaTime) {
|
|
49
47
|
const { rotateSpeed, panSpeed, zoomSpeed, deadzone, axisRotateX, axisRotateY, axisPanX, axisPanY, buttonZoomIn, buttonZoomOut } = this.#options;
|
|
50
|
-
this.#queueRotation(deltaTime,
|
|
51
|
-
this.#queuePan(deltaTime,
|
|
52
|
-
this.#queueZoom(deltaTime,
|
|
48
|
+
this.#queueRotation(deltaTime, rotateSpeed, deadzone, axisRotateX, axisRotateY);
|
|
49
|
+
this.#queuePan(deltaTime, panSpeed, deadzone, axisPanX, axisPanY);
|
|
50
|
+
this.#queueZoom(deltaTime, zoomSpeed, deadzone, buttonZoomIn, buttonZoomOut);
|
|
53
51
|
}
|
|
54
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Queues rotation input into TrackballControls' normalized move state.
|
|
54
|
+
*
|
|
55
|
+
* @param deltaTime - Seconds since the last frame.
|
|
56
|
+
* @param rotateSpeed - User-configured rotation speed multiplier.
|
|
57
|
+
* @param deadzone - Axis dead zone threshold.
|
|
58
|
+
* @param axisRotateX - Axis index for horizontal rotation.
|
|
59
|
+
* @param axisRotateY - Axis index for vertical rotation.
|
|
60
|
+
*/
|
|
61
|
+
#queueRotation(deltaTime, rotateSpeed, deadzone, axisRotateX, axisRotateY) {
|
|
55
62
|
const controls = this.#controls;
|
|
56
63
|
if (controls.noRotate) {
|
|
57
64
|
controls._movePrev.copy(controls._moveCurr);
|
|
58
65
|
controls._lastAngle = 0;
|
|
59
66
|
return;
|
|
60
67
|
}
|
|
61
|
-
const
|
|
62
|
-
const
|
|
68
|
+
const input = this.gamepadInput;
|
|
69
|
+
const rotX = input.axis(axisRotateX, { deadzone });
|
|
70
|
+
const rotY = input.axis(axisRotateY, { deadzone });
|
|
63
71
|
if (rotX === 0 && rotY === 0) return;
|
|
64
72
|
const scale = rotateSpeed * deltaTime * Math.PI;
|
|
65
73
|
controls._moveCurr.x += rotX * scale;
|
|
66
74
|
controls._moveCurr.y += -rotY * scale;
|
|
67
75
|
}
|
|
68
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Queues pan input into TrackballControls' normalized pan state.
|
|
78
|
+
*
|
|
79
|
+
* @param deltaTime - Seconds since the last frame.
|
|
80
|
+
* @param panSpeed - User-configured pan speed multiplier.
|
|
81
|
+
* @param deadzone - Axis dead zone threshold.
|
|
82
|
+
* @param axisPanX - Axis index for horizontal panning.
|
|
83
|
+
* @param axisPanY - Axis index for vertical panning.
|
|
84
|
+
*/
|
|
85
|
+
#queuePan(deltaTime, panSpeed, deadzone, axisPanX, axisPanY) {
|
|
69
86
|
const controls = this.#controls;
|
|
70
87
|
if (controls.noPan) {
|
|
71
88
|
controls._panStart.copy(controls._panEnd);
|
|
72
89
|
return;
|
|
73
90
|
}
|
|
74
|
-
const
|
|
75
|
-
const
|
|
91
|
+
const input = this.gamepadInput;
|
|
92
|
+
const panX = input.axis(axisPanX, { deadzone });
|
|
93
|
+
const panY = input.axis(axisPanY, { deadzone });
|
|
76
94
|
if (panX === 0 && panY === 0) return;
|
|
77
95
|
const scale = panSpeed * deltaTime * this.#getInputDampingFactor();
|
|
78
96
|
controls._panEnd.x += panX * scale;
|
|
79
97
|
controls._panEnd.y += panY * scale;
|
|
80
98
|
}
|
|
81
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Queues trigger zoom input into TrackballControls' normalized zoom state.
|
|
101
|
+
*
|
|
102
|
+
* @param deltaTime - Seconds since the last frame.
|
|
103
|
+
* @param zoomSpeed - User-configured zoom speed multiplier.
|
|
104
|
+
* @param deadzone - Trigger dead zone threshold.
|
|
105
|
+
* @param buttonZoomIn - Button index for zooming in.
|
|
106
|
+
* @param buttonZoomOut - Button index for zooming out.
|
|
107
|
+
*/
|
|
108
|
+
#queueZoom(deltaTime, zoomSpeed, deadzone, buttonZoomIn, buttonZoomOut) {
|
|
82
109
|
const controls = this.#controls;
|
|
83
110
|
if (controls.noZoom) {
|
|
84
111
|
controls._zoomStart.copy(controls._zoomEnd);
|
|
85
112
|
return;
|
|
86
113
|
}
|
|
87
|
-
const
|
|
88
|
-
const
|
|
114
|
+
const input = this.gamepadInput;
|
|
115
|
+
const triggerIn = input.buttonValue(buttonZoomIn);
|
|
116
|
+
const triggerOut = input.buttonValue(buttonZoomOut);
|
|
89
117
|
if (triggerIn <= deadzone && triggerOut <= deadzone) return;
|
|
90
118
|
controls._zoomEnd.y += (triggerOut - triggerIn) * zoomSpeed * deltaTime * this.#getInputDampingFactor();
|
|
91
119
|
}
|
|
92
120
|
/**
|
|
93
121
|
* Compensates for TrackballControls reapplying queued pan and zoom deltas
|
|
94
122
|
* while their input state catches up through damping.
|
|
123
|
+
*
|
|
124
|
+
* @returns Multiplier that matches TrackballControls' damping mode.
|
|
95
125
|
*/
|
|
96
126
|
#getInputDampingFactor() {
|
|
97
127
|
const controls = this.#controls;
|
|
@@ -114,9 +114,8 @@ declare class GamepadTransformControls extends GamepadControls {
|
|
|
114
114
|
* translate, rotate, scale, and reset behavior.
|
|
115
115
|
*
|
|
116
116
|
* @param deltaTime - Seconds since the last frame.
|
|
117
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
118
117
|
*/
|
|
119
|
-
protected onUpdate(deltaTime: number
|
|
118
|
+
protected onUpdate(deltaTime: number): void;
|
|
120
119
|
/**
|
|
121
120
|
* Ends any active transform before disposing the gamepad lifecycle listeners.
|
|
122
121
|
*/
|