@pmndrs/viverse 0.2.1 → 0.2.3
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/dist/{input → action}/action.d.ts +19 -20
- package/dist/{input → action}/action.js +56 -39
- package/dist/{input → action}/index.d.ts +6 -5
- package/dist/{input → action}/index.js +5 -4
- package/dist/action/keyboard.d.ts +26 -0
- package/dist/action/keyboard.js +89 -0
- package/dist/action/pointer-capture.d.ts +8 -0
- package/dist/{input → action}/pointer-capture.js +16 -22
- package/dist/action/pointer-lock.d.ts +6 -0
- package/dist/action/pointer-lock.js +49 -0
- package/dist/action/pointer.d.ts +18 -0
- package/dist/action/pointer.js +90 -0
- package/dist/action/screen-joystick.d.ts +16 -0
- package/dist/{input → action}/screen-joystick.js +36 -33
- package/dist/action/screen-jump-button.d.ts +6 -0
- package/dist/action/screen-jump-button.js +32 -0
- package/dist/camera.d.ts +3 -3
- package/dist/camera.js +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/simple-character/apply-input-options.d.ts +2 -2
- package/dist/simple-character/apply-input-options.js +28 -22
- package/dist/simple-character/index.d.ts +17 -9
- package/dist/simple-character/index.js +13 -9
- package/dist/simple-character/state/jump-start.js +1 -1
- package/dist/simple-character/state/movement.js +1 -1
- package/dist/simple-character/update-input-velocity.d.ts +1 -1
- package/dist/simple-character/update-input-velocity.js +2 -2
- package/dist/simple-character/update-rotation.js +1 -1
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/dist/input/keyboard.d.ts +0 -39
- package/dist/input/keyboard.js +0 -96
- package/dist/input/pointer-capture.d.ts +0 -15
- package/dist/input/pointer-lock.d.ts +0 -12
- package/dist/input/pointer-lock.js +0 -34
- package/dist/input/screen-joystick.d.ts +0 -23
- package/dist/input/screen-jump-button.d.ts +0 -6
- package/dist/input/screen-jump-button.js +0 -44
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type WriteonlyEventAction<T = void> = {
|
|
2
|
+
emit(value: T): void;
|
|
3
|
+
};
|
|
4
|
+
export declare class EventAction<T = unknown> implements WriteonlyEventAction<T> {
|
|
5
|
+
private readonly combine?;
|
|
6
|
+
private readonly neutral?;
|
|
2
7
|
private latestTime;
|
|
3
8
|
private readonly subscriptionListeners;
|
|
9
|
+
private readonly readers;
|
|
10
|
+
constructor(combine?: ((...values: Array<T>) => T) | undefined, neutral?: T | undefined);
|
|
4
11
|
emit(value: T): void;
|
|
5
12
|
subscribe(callback: (value: T) => void, options?: {
|
|
6
13
|
once?: true;
|
|
@@ -8,6 +15,12 @@ export declare class EventAction<T = void> {
|
|
|
8
15
|
}): void;
|
|
9
16
|
waitFor(signal?: AbortSignal): Promise<T>;
|
|
10
17
|
getLatestTime(): number;
|
|
18
|
+
createReader(abortSignal: AbortSignal): {
|
|
19
|
+
update(): void;
|
|
20
|
+
get(): T;
|
|
21
|
+
};
|
|
22
|
+
mapFrom<S>(fn: (value: S) => T, abortSignal?: AbortSignal): EventAction<S>;
|
|
23
|
+
filterFrom(fn: (value: T) => boolean, abortSignal?: AbortSignal): EventAction<T>;
|
|
11
24
|
}
|
|
12
25
|
export type StateActionWriter<T> = {
|
|
13
26
|
write(value: T): void;
|
|
@@ -16,26 +29,12 @@ export type StateActionWriter<T> = {
|
|
|
16
29
|
* StateAction keeps the latest state per writer and merges them on read.
|
|
17
30
|
* Values persist until the writer is disposed (abortSignal aborts).
|
|
18
31
|
*/
|
|
19
|
-
export declare class StateAction<T> {
|
|
20
|
-
private readonly mergeWriters
|
|
21
|
-
private readonly neutral
|
|
32
|
+
export declare class StateAction<T = unknown> {
|
|
33
|
+
private readonly mergeWriters?;
|
|
34
|
+
private readonly neutral?;
|
|
22
35
|
private readonly absoluteActions;
|
|
23
|
-
constructor(mergeWriters
|
|
36
|
+
constructor(mergeWriters?: ((...values: Array<T>) => T) | undefined, neutral?: T | undefined);
|
|
24
37
|
createWriter(abortSignal: AbortSignal): StateActionWriter<T>;
|
|
25
38
|
get(): T;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* DeltaAction accumulates transient values each frame and clears them on frame advance.
|
|
29
|
-
* Multiple writes per frame from any source are combined using combine().
|
|
30
|
-
*/
|
|
31
|
-
export declare class DeltaAction<T> {
|
|
32
|
-
private readonly combine;
|
|
33
|
-
private readonly neutral;
|
|
34
|
-
constructor(combine: (...values: Array<T>) => T, neutral: T);
|
|
35
|
-
private readonly readers;
|
|
36
|
-
write(value: T): void;
|
|
37
|
-
createReader(abortSignal: AbortSignal): {
|
|
38
|
-
update(): void;
|
|
39
|
-
get(): T;
|
|
40
|
-
};
|
|
39
|
+
mapFrom<S>(fn: (value: S) => T): StateAction<S>;
|
|
41
40
|
}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
// no external imports
|
|
2
1
|
export class EventAction {
|
|
2
|
+
combine;
|
|
3
|
+
neutral;
|
|
3
4
|
latestTime = -Infinity;
|
|
4
5
|
subscriptionListeners = new Set();
|
|
6
|
+
readers = new Set();
|
|
7
|
+
constructor(combine, neutral) {
|
|
8
|
+
this.combine = combine;
|
|
9
|
+
this.neutral = neutral;
|
|
10
|
+
}
|
|
5
11
|
emit(value) {
|
|
6
12
|
this.latestTime = performance.now() / 1000;
|
|
7
13
|
for (const listener of this.subscriptionListeners) {
|
|
8
14
|
listener(value);
|
|
9
15
|
}
|
|
16
|
+
for (const reader of this.readers) {
|
|
17
|
+
reader.next.push(value);
|
|
18
|
+
}
|
|
10
19
|
}
|
|
11
20
|
subscribe(callback, options) {
|
|
12
21
|
const listener = (value) => {
|
|
@@ -26,6 +35,41 @@ export class EventAction {
|
|
|
26
35
|
getLatestTime() {
|
|
27
36
|
return this.latestTime;
|
|
28
37
|
}
|
|
38
|
+
createReader(abortSignal) {
|
|
39
|
+
const reader = { next: new Array(), current: new Array() };
|
|
40
|
+
this.readers.add(reader);
|
|
41
|
+
abortSignal.addEventListener('abort', () => {
|
|
42
|
+
this.readers.delete(reader);
|
|
43
|
+
}, { once: true });
|
|
44
|
+
return {
|
|
45
|
+
update: () => {
|
|
46
|
+
reader.current.length = 0;
|
|
47
|
+
if (reader.next.length) {
|
|
48
|
+
reader.current.push(...reader.next);
|
|
49
|
+
reader.next.length = 0;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
get: () => {
|
|
53
|
+
if (this.combine == null) {
|
|
54
|
+
throw new Error(`unable to use a reader without providing a combine function`);
|
|
55
|
+
}
|
|
56
|
+
if (reader.current.length === 0) {
|
|
57
|
+
return this.neutral;
|
|
58
|
+
}
|
|
59
|
+
return this.combine(...reader.current);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
mapFrom(fn, abortSignal) {
|
|
64
|
+
const action = new EventAction();
|
|
65
|
+
action.subscribe((value) => this.emit(fn(value)), { signal: abortSignal });
|
|
66
|
+
return action;
|
|
67
|
+
}
|
|
68
|
+
filterFrom(fn, abortSignal) {
|
|
69
|
+
const action = new EventAction();
|
|
70
|
+
action.subscribe((value) => void (fn(value) && this.emit(value)), { signal: abortSignal });
|
|
71
|
+
return action;
|
|
72
|
+
}
|
|
29
73
|
}
|
|
30
74
|
/**
|
|
31
75
|
* StateAction keeps the latest state per writer and merges them on read.
|
|
@@ -51,47 +95,20 @@ export class StateAction {
|
|
|
51
95
|
};
|
|
52
96
|
}
|
|
53
97
|
get() {
|
|
98
|
+
if (this.mergeWriters == null) {
|
|
99
|
+
throw new Error(`unable to read from a state action without providing a merge function`);
|
|
100
|
+
}
|
|
54
101
|
const values = [...this.absoluteActions.values()];
|
|
55
102
|
return values.length ? this.mergeWriters(...values) : this.neutral;
|
|
56
103
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
neutral;
|
|
65
|
-
constructor(combine, neutral) {
|
|
66
|
-
this.combine = combine;
|
|
67
|
-
this.neutral = neutral;
|
|
68
|
-
}
|
|
69
|
-
readers = new Set();
|
|
70
|
-
write(value) {
|
|
71
|
-
for (const reader of this.readers) {
|
|
72
|
-
reader.next.push(value);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
createReader(abortSignal) {
|
|
76
|
-
const reader = { next: new Array(), current: new Array() };
|
|
77
|
-
this.readers.add(reader);
|
|
78
|
-
abortSignal.addEventListener('abort', () => {
|
|
79
|
-
this.readers.delete(reader);
|
|
80
|
-
}, { once: true });
|
|
81
|
-
return {
|
|
82
|
-
update: () => {
|
|
83
|
-
reader.current.length = 0;
|
|
84
|
-
if (reader.next.length) {
|
|
85
|
-
reader.current.push(...reader.next);
|
|
86
|
-
reader.next.length = 0;
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
get: () => {
|
|
90
|
-
if (reader.current.length === 0) {
|
|
91
|
-
return this.neutral;
|
|
92
|
-
}
|
|
93
|
-
return this.combine(...reader.current);
|
|
94
|
-
},
|
|
104
|
+
mapFrom(fn) {
|
|
105
|
+
const action = new StateAction();
|
|
106
|
+
action.createWriter = (abortSignal) => {
|
|
107
|
+
const writer = this.createWriter(abortSignal);
|
|
108
|
+
return {
|
|
109
|
+
write: (value) => writer.write(fn(value)),
|
|
110
|
+
};
|
|
95
111
|
};
|
|
112
|
+
return action;
|
|
96
113
|
}
|
|
97
114
|
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { EventAction, StateAction
|
|
1
|
+
import { EventAction, StateAction } from './action.js';
|
|
2
2
|
export declare function BooleanOr(...values: Array<boolean>): boolean;
|
|
3
3
|
export declare const MoveForwardAction: StateAction<number>;
|
|
4
4
|
export declare const MoveBackwardAction: StateAction<number>;
|
|
5
5
|
export declare const MoveLeftAction: StateAction<number>;
|
|
6
6
|
export declare const MoveRightAction: StateAction<number>;
|
|
7
7
|
export declare const RunAction: StateAction<boolean>;
|
|
8
|
-
export declare const JumpAction: EventAction<
|
|
9
|
-
export declare const ZoomAction:
|
|
10
|
-
export declare const RotateYawAction:
|
|
11
|
-
export declare const RotatePitchAction:
|
|
8
|
+
export declare const JumpAction: EventAction<unknown>;
|
|
9
|
+
export declare const ZoomAction: EventAction<number>;
|
|
10
|
+
export declare const RotateYawAction: EventAction<number>;
|
|
11
|
+
export declare const RotatePitchAction: EventAction<number>;
|
|
12
12
|
export * from './pointer-lock.js';
|
|
13
13
|
export * from './pointer-capture.js';
|
|
14
|
+
export * from './pointer.js';
|
|
14
15
|
export * from './keyboard.js';
|
|
15
16
|
export * from './screen-joystick.js';
|
|
16
17
|
export * from './screen-jump-button.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventAction, StateAction
|
|
1
|
+
import { EventAction, StateAction } from './action.js';
|
|
2
2
|
export function BooleanOr(...values) {
|
|
3
3
|
let value = false;
|
|
4
4
|
for (let i = 0; i < values.length; i++) {
|
|
@@ -13,11 +13,12 @@ export const MoveLeftAction = new StateAction(Math.max, 0);
|
|
|
13
13
|
export const MoveRightAction = new StateAction(Math.max, 0);
|
|
14
14
|
export const RunAction = new StateAction(BooleanOr, false);
|
|
15
15
|
export const JumpAction = new EventAction();
|
|
16
|
-
export const ZoomAction = new
|
|
17
|
-
export const RotateYawAction = new
|
|
18
|
-
export const RotatePitchAction = new
|
|
16
|
+
export const ZoomAction = new EventAction(sum, 0);
|
|
17
|
+
export const RotateYawAction = new EventAction(sum, 0);
|
|
18
|
+
export const RotatePitchAction = new EventAction(sum, 0);
|
|
19
19
|
export * from './pointer-lock.js';
|
|
20
20
|
export * from './pointer-capture.js';
|
|
21
|
+
export * from './pointer.js';
|
|
21
22
|
export * from './keyboard.js';
|
|
22
23
|
export * from './screen-joystick.js';
|
|
23
24
|
export * from './screen-jump-button.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { StateAction, WriteonlyEventAction } from './index.js';
|
|
2
|
+
export declare class KeyboardActionBinding {
|
|
3
|
+
private pressedKeys;
|
|
4
|
+
private writer?;
|
|
5
|
+
private _keys;
|
|
6
|
+
get keys(): Array<string>;
|
|
7
|
+
set keys(keys: Array<string>);
|
|
8
|
+
requiresPointerLock?: boolean;
|
|
9
|
+
private updateState;
|
|
10
|
+
constructor(action: WriteonlyEventAction<KeyboardEvent> | StateAction<boolean>, domElement: HTMLElement, abortSignal: AbortSignal);
|
|
11
|
+
}
|
|
12
|
+
export declare const DefaultMoveForwardKeys: string[];
|
|
13
|
+
export declare const DefaultMoveBackwardKeys: string[];
|
|
14
|
+
export declare const DefaultMoveLeftKeys: string[];
|
|
15
|
+
export declare const DefaultMoveRightKeys: string[];
|
|
16
|
+
export declare const DefaultRunKeys: string[];
|
|
17
|
+
export declare const DefaultJumpKeys: string[];
|
|
18
|
+
export declare class KeyboardLocomotionActionBindings {
|
|
19
|
+
moveForwardBinding: KeyboardActionBinding;
|
|
20
|
+
moveLeftBinding: KeyboardActionBinding;
|
|
21
|
+
moveRightBinding: KeyboardActionBinding;
|
|
22
|
+
moveBackwardBinding: KeyboardActionBinding;
|
|
23
|
+
jumpBinding: KeyboardActionBinding;
|
|
24
|
+
runBinding: KeyboardActionBinding;
|
|
25
|
+
constructor(domElement: HTMLElement, abortSignal: AbortSignal);
|
|
26
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { JumpAction, MoveBackwardAction, MoveForwardAction, MoveLeftAction, MoveRightAction, StateAction, RunAction, } from './index.js';
|
|
2
|
+
export class KeyboardActionBinding {
|
|
3
|
+
pressedKeys = new Set();
|
|
4
|
+
writer;
|
|
5
|
+
//options
|
|
6
|
+
_keys = [];
|
|
7
|
+
get keys() {
|
|
8
|
+
return this._keys;
|
|
9
|
+
}
|
|
10
|
+
set keys(keys) {
|
|
11
|
+
this._keys = keys;
|
|
12
|
+
this.updateState();
|
|
13
|
+
}
|
|
14
|
+
requiresPointerLock;
|
|
15
|
+
updateState() {
|
|
16
|
+
this.writer?.write(this.keys.some((key) => this.pressedKeys.has(key)));
|
|
17
|
+
}
|
|
18
|
+
constructor(action, domElement, abortSignal) {
|
|
19
|
+
if (action instanceof StateAction) {
|
|
20
|
+
this.writer = action.createWriter(abortSignal);
|
|
21
|
+
domElement.tabIndex = 0;
|
|
22
|
+
domElement.addEventListener('keydown', (e) => {
|
|
23
|
+
if ((this.requiresPointerLock ?? false) && document.pointerLockElement != domElement) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.pressedKeys.add(e.code);
|
|
27
|
+
this.updateState();
|
|
28
|
+
}, { signal: abortSignal });
|
|
29
|
+
domElement.addEventListener('keyup', (e) => {
|
|
30
|
+
console.log('up');
|
|
31
|
+
this.pressedKeys.delete(e.code);
|
|
32
|
+
this.updateState();
|
|
33
|
+
}, { signal: abortSignal });
|
|
34
|
+
// Handle focus loss to clear pressed keys
|
|
35
|
+
const onBlur = () => {
|
|
36
|
+
console.log('blur');
|
|
37
|
+
this.pressedKeys.clear();
|
|
38
|
+
this.updateState();
|
|
39
|
+
};
|
|
40
|
+
domElement.addEventListener('blur', onBlur, { signal: abortSignal });
|
|
41
|
+
domElement.addEventListener('visibilitychange', () => {
|
|
42
|
+
if (document.visibilityState !== 'visible') {
|
|
43
|
+
onBlur();
|
|
44
|
+
}
|
|
45
|
+
}, { signal: abortSignal });
|
|
46
|
+
window.addEventListener('blur', onBlur, { signal: abortSignal });
|
|
47
|
+
document.addEventListener('pointerlockchange', () => {
|
|
48
|
+
if (document.pointerLockElement !== domElement) {
|
|
49
|
+
onBlur();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
domElement.addEventListener('keydown', (e) => {
|
|
55
|
+
if (!this.keys.includes(e.code) || e.repeat) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
action.emit(e);
|
|
59
|
+
}, { signal: abortSignal });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export const DefaultMoveForwardKeys = ['KeyW'];
|
|
63
|
+
export const DefaultMoveBackwardKeys = ['KeyS'];
|
|
64
|
+
export const DefaultMoveLeftKeys = ['KeyA'];
|
|
65
|
+
export const DefaultMoveRightKeys = ['KeyD'];
|
|
66
|
+
export const DefaultRunKeys = ['ShiftRight', 'ShiftLeft'];
|
|
67
|
+
export const DefaultJumpKeys = ['Space'];
|
|
68
|
+
export class KeyboardLocomotionActionBindings {
|
|
69
|
+
moveForwardBinding;
|
|
70
|
+
moveLeftBinding;
|
|
71
|
+
moveRightBinding;
|
|
72
|
+
moveBackwardBinding;
|
|
73
|
+
jumpBinding;
|
|
74
|
+
runBinding;
|
|
75
|
+
constructor(domElement, abortSignal) {
|
|
76
|
+
this.moveForwardBinding = new KeyboardActionBinding(MoveForwardAction.mapFrom((isPressed) => (isPressed ? 1 : 0)), domElement, abortSignal);
|
|
77
|
+
this.moveForwardBinding.keys = DefaultMoveForwardKeys;
|
|
78
|
+
this.moveLeftBinding = new KeyboardActionBinding(MoveLeftAction.mapFrom((isPressed) => (isPressed ? 1 : 0)), domElement, abortSignal);
|
|
79
|
+
this.moveLeftBinding.keys = DefaultMoveLeftKeys;
|
|
80
|
+
this.moveRightBinding = new KeyboardActionBinding(MoveRightAction.mapFrom((isPressed) => (isPressed ? 1 : 0)), domElement, abortSignal);
|
|
81
|
+
this.moveRightBinding.keys = DefaultMoveRightKeys;
|
|
82
|
+
this.moveBackwardBinding = new KeyboardActionBinding(MoveBackwardAction.mapFrom((isPressed) => (isPressed ? 1 : 0)), domElement, abortSignal);
|
|
83
|
+
this.moveBackwardBinding.keys = DefaultMoveBackwardKeys;
|
|
84
|
+
this.jumpBinding = new KeyboardActionBinding(JumpAction, domElement, abortSignal);
|
|
85
|
+
this.jumpBinding.keys = DefaultJumpKeys;
|
|
86
|
+
this.runBinding = new KeyboardActionBinding(RunAction, domElement, abortSignal);
|
|
87
|
+
this.runBinding.keys = DefaultRunKeys;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { RotatePitchAction, RotateYawAction, ZoomAction } from './index.js';
|
|
2
|
-
|
|
3
|
-
* @requires to manually execute `domElement.setPointerCapture(pointerId)` on pointerdown
|
|
4
|
-
*/
|
|
5
|
-
export class PointerCaptureInput {
|
|
2
|
+
export class PointerCaptureRotateZoomActionBindings {
|
|
6
3
|
domElement;
|
|
7
|
-
abortController = new AbortController();
|
|
8
4
|
activePointers = new Map();
|
|
9
5
|
lastPinchDist = null;
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
rotationSpeed; // default 0.4
|
|
7
|
+
zoomSpeed; // default 0.0001
|
|
8
|
+
constructor(domElement, abortSignal) {
|
|
12
9
|
this.domElement = domElement;
|
|
13
10
|
domElement.addEventListener('pointerdown', (event) => {
|
|
14
11
|
this.domElement.setPointerCapture(event.pointerId);
|
|
@@ -18,7 +15,7 @@ export class PointerCaptureInput {
|
|
|
18
15
|
this.lastPinchDist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
19
16
|
}
|
|
20
17
|
}, {
|
|
21
|
-
signal:
|
|
18
|
+
signal: abortSignal,
|
|
22
19
|
});
|
|
23
20
|
domElement.addEventListener('pointermove', (event) => {
|
|
24
21
|
if (!this.domElement.hasPointerCapture(event.pointerId)) {
|
|
@@ -29,18 +26,18 @@ export class PointerCaptureInput {
|
|
|
29
26
|
const pts = Array.from(this.activePointers.values());
|
|
30
27
|
if (this.lastPinchDist != null) {
|
|
31
28
|
const d = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
32
|
-
const zoomSpeed = this.
|
|
33
|
-
ZoomAction.
|
|
29
|
+
const zoomSpeed = this.zoomSpeed ?? 0.0001;
|
|
30
|
+
ZoomAction.emit((this.lastPinchDist - d) * zoomSpeed);
|
|
34
31
|
this.lastPinchDist = d;
|
|
35
32
|
}
|
|
36
33
|
event.preventDefault();
|
|
37
34
|
return;
|
|
38
35
|
}
|
|
39
|
-
const rotationSpeed = this.
|
|
40
|
-
RotateYawAction.
|
|
41
|
-
RotatePitchAction.
|
|
36
|
+
const rotationSpeed = this.rotationSpeed ?? 0.4;
|
|
37
|
+
RotateYawAction.emit(-(event.movementX / window.innerHeight) * rotationSpeed);
|
|
38
|
+
RotatePitchAction.emit(-(event.movementY / window.innerHeight) * rotationSpeed);
|
|
42
39
|
}, {
|
|
43
|
-
signal:
|
|
40
|
+
signal: abortSignal,
|
|
44
41
|
});
|
|
45
42
|
domElement.addEventListener('pointerup', (event) => {
|
|
46
43
|
this.domElement.releasePointerCapture(event.pointerId);
|
|
@@ -49,7 +46,7 @@ export class PointerCaptureInput {
|
|
|
49
46
|
this.lastPinchDist = null;
|
|
50
47
|
}
|
|
51
48
|
}, {
|
|
52
|
-
signal:
|
|
49
|
+
signal: abortSignal,
|
|
53
50
|
});
|
|
54
51
|
domElement.addEventListener('pointercancel', (event) => {
|
|
55
52
|
this.domElement.releasePointerCapture(event.pointerId);
|
|
@@ -58,17 +55,14 @@ export class PointerCaptureInput {
|
|
|
58
55
|
this.lastPinchDist = null;
|
|
59
56
|
}
|
|
60
57
|
}, {
|
|
61
|
-
signal:
|
|
58
|
+
signal: abortSignal,
|
|
62
59
|
});
|
|
63
60
|
domElement.addEventListener('wheel', (event) => {
|
|
64
61
|
event.preventDefault();
|
|
65
|
-
const zoomSpeed = this.
|
|
66
|
-
ZoomAction.
|
|
62
|
+
const zoomSpeed = this.zoomSpeed ?? 0.0001;
|
|
63
|
+
ZoomAction.emit(event.deltaY * zoomSpeed);
|
|
67
64
|
}, {
|
|
68
|
-
signal:
|
|
65
|
+
signal: abortSignal,
|
|
69
66
|
});
|
|
70
67
|
}
|
|
71
|
-
dispose() {
|
|
72
|
-
this.abortController.abort();
|
|
73
|
-
}
|
|
74
68
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { RotatePitchAction, RotateYawAction, ZoomAction } from './index.js';
|
|
2
|
+
export class PointerLockRotateZoomActionBindings {
|
|
3
|
+
rotationSpeed; // default 0.4
|
|
4
|
+
zoomSpeed; // default 0.0001
|
|
5
|
+
lockOnClick;
|
|
6
|
+
constructor(domElement, abortSignal) {
|
|
7
|
+
// lock on click (only left click)
|
|
8
|
+
domElement.addEventListener('mousedown', (event) => {
|
|
9
|
+
const lockOnClick = this.lockOnClick ?? true;
|
|
10
|
+
if (!lockOnClick) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
// 0 = primary/left button
|
|
14
|
+
if (event.button !== 0) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (document.pointerLockElement === domElement) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// request pointer lock as a user-gesture
|
|
21
|
+
if (domElement.requestPointerLock) {
|
|
22
|
+
domElement.requestPointerLock();
|
|
23
|
+
}
|
|
24
|
+
}, {
|
|
25
|
+
signal: abortSignal,
|
|
26
|
+
});
|
|
27
|
+
domElement.addEventListener('pointermove', (event) => {
|
|
28
|
+
if (document.pointerLockElement != domElement) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const rect = domElement.getBoundingClientRect();
|
|
32
|
+
const rotationSpeed = this.rotationSpeed ?? 0.4;
|
|
33
|
+
RotateYawAction.emit(-(event.movementX / rect.height) * rotationSpeed);
|
|
34
|
+
RotatePitchAction.emit(-(event.movementY / rect.height) * rotationSpeed);
|
|
35
|
+
}, {
|
|
36
|
+
signal: abortSignal,
|
|
37
|
+
});
|
|
38
|
+
domElement.addEventListener('wheel', (event) => {
|
|
39
|
+
if (document.pointerLockElement != domElement) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const zoomSpeed = this.zoomSpeed ?? 0.0001;
|
|
43
|
+
ZoomAction.emit(event.deltaY * zoomSpeed);
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
}, {
|
|
46
|
+
signal: abortSignal,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StateAction, WriteonlyEventAction } from './index.js';
|
|
2
|
+
export declare class PointerButtonActionBinding {
|
|
3
|
+
private readonly domElement;
|
|
4
|
+
private writer?;
|
|
5
|
+
private eventAction?;
|
|
6
|
+
private buttonsMask;
|
|
7
|
+
private wasPressed;
|
|
8
|
+
private _buttons;
|
|
9
|
+
/**
|
|
10
|
+
* When undefined, any pressed button will set the state to true.
|
|
11
|
+
*/
|
|
12
|
+
get buttons(): Array<number> | undefined;
|
|
13
|
+
set buttons(buttons: Array<number> | undefined);
|
|
14
|
+
requiresPointerLock?: boolean;
|
|
15
|
+
private getPressedButtonsFromMask;
|
|
16
|
+
private processPointer;
|
|
17
|
+
constructor(action: WriteonlyEventAction<PointerEvent> | StateAction<boolean>, domElement: HTMLElement, abortSignal: AbortSignal);
|
|
18
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { StateAction } from './index.js';
|
|
2
|
+
export class PointerButtonActionBinding {
|
|
3
|
+
domElement;
|
|
4
|
+
writer;
|
|
5
|
+
eventAction;
|
|
6
|
+
buttonsMask = 0;
|
|
7
|
+
wasPressed = false;
|
|
8
|
+
// options
|
|
9
|
+
_buttons;
|
|
10
|
+
/**
|
|
11
|
+
* When undefined, any pressed button will set the state to true.
|
|
12
|
+
*/
|
|
13
|
+
get buttons() {
|
|
14
|
+
return this._buttons;
|
|
15
|
+
}
|
|
16
|
+
set buttons(buttons) {
|
|
17
|
+
this._buttons = buttons;
|
|
18
|
+
this.processPointer();
|
|
19
|
+
}
|
|
20
|
+
requiresPointerLock;
|
|
21
|
+
getPressedButtonsFromMask(mask) {
|
|
22
|
+
const pressed = [];
|
|
23
|
+
if (mask & 1)
|
|
24
|
+
pressed.push(0);
|
|
25
|
+
if (mask & 2)
|
|
26
|
+
pressed.push(2);
|
|
27
|
+
if (mask & 4)
|
|
28
|
+
pressed.push(1);
|
|
29
|
+
if (mask & 8)
|
|
30
|
+
pressed.push(3);
|
|
31
|
+
if (mask & 16)
|
|
32
|
+
pressed.push(4);
|
|
33
|
+
return pressed;
|
|
34
|
+
}
|
|
35
|
+
processPointer(e) {
|
|
36
|
+
if (e != null) {
|
|
37
|
+
this.buttonsMask = e.buttons;
|
|
38
|
+
}
|
|
39
|
+
if ((this.requiresPointerLock ?? false) && document.pointerLockElement != this.domElement) {
|
|
40
|
+
this.buttonsMask = 0;
|
|
41
|
+
}
|
|
42
|
+
const pressedButtons = this.getPressedButtonsFromMask(this.buttonsMask);
|
|
43
|
+
const isActive = this.buttons == null ? pressedButtons.length > 0 : this.buttons.some((btn) => pressedButtons.includes(btn));
|
|
44
|
+
if (this.writer != null) {
|
|
45
|
+
this.writer.write(isActive);
|
|
46
|
+
}
|
|
47
|
+
if (this.eventAction != null && e != null) {
|
|
48
|
+
if (isActive && !this.wasPressed) {
|
|
49
|
+
this.eventAction.emit(e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
this.wasPressed = isActive;
|
|
53
|
+
}
|
|
54
|
+
constructor(action, domElement, abortSignal) {
|
|
55
|
+
this.domElement = domElement;
|
|
56
|
+
if (action instanceof StateAction) {
|
|
57
|
+
this.writer = action.createWriter(abortSignal);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.eventAction = action;
|
|
61
|
+
}
|
|
62
|
+
domElement.addEventListener('pointerdown', (e) => {
|
|
63
|
+
this.processPointer(e);
|
|
64
|
+
}, { signal: abortSignal });
|
|
65
|
+
domElement.addEventListener('pointermove', (e) => {
|
|
66
|
+
// Moving while multiple buttons are pressed is the only signal we get
|
|
67
|
+
// when a second button is pressed simultaneously.
|
|
68
|
+
this.processPointer(e);
|
|
69
|
+
}, { signal: abortSignal });
|
|
70
|
+
domElement.addEventListener('pointerup', (e) => {
|
|
71
|
+
this.processPointer(e);
|
|
72
|
+
}, { signal: abortSignal });
|
|
73
|
+
domElement.addEventListener('pointercancel', (e) => {
|
|
74
|
+
this.processPointer(e);
|
|
75
|
+
}, { signal: abortSignal });
|
|
76
|
+
domElement.addEventListener('pointerleave', (e) => {
|
|
77
|
+
this.processPointer(e);
|
|
78
|
+
}, { signal: abortSignal });
|
|
79
|
+
domElement.addEventListener('blur', () => {
|
|
80
|
+
// On blur, assume no buttons pressed.
|
|
81
|
+
this.buttonsMask = 0;
|
|
82
|
+
this.processPointer();
|
|
83
|
+
}, { signal: abortSignal });
|
|
84
|
+
document.addEventListener('pointerlockchange', () => {
|
|
85
|
+
if (document.pointerLockElement !== domElement) {
|
|
86
|
+
this.processPointer();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class ScreenJoystickLocomotionActionBindings {
|
|
2
|
+
private abortSignal;
|
|
3
|
+
readonly root: HTMLDivElement;
|
|
4
|
+
private readonly handle;
|
|
5
|
+
private pointerId;
|
|
6
|
+
private readonly forwardWriter;
|
|
7
|
+
private readonly backwardWriter;
|
|
8
|
+
private readonly leftWriter;
|
|
9
|
+
private readonly rightWriter;
|
|
10
|
+
private readonly runWriter;
|
|
11
|
+
runDistancePx?: number;
|
|
12
|
+
deadZonePx?: number;
|
|
13
|
+
constructor(domElement: HTMLElement, abortSignal: AbortSignal);
|
|
14
|
+
private updateHandle;
|
|
15
|
+
private resetHandle;
|
|
16
|
+
}
|