@pmndrs/viverse 0.1.20 → 0.2.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.
Files changed (55) hide show
  1. package/dist/animation/bvh.d.ts +2 -2
  2. package/dist/animation/default.d.ts +1 -0
  3. package/dist/animation/default.js +18 -0
  4. package/dist/animation/fbx.d.ts +2 -2
  5. package/dist/animation/gltf.d.ts +2 -2
  6. package/dist/animation/index.d.ts +14 -16
  7. package/dist/animation/index.js +18 -36
  8. package/dist/animation/mask.d.ts +3 -0
  9. package/dist/animation/mask.js +3 -0
  10. package/dist/camera.d.ts +3 -7
  11. package/dist/camera.js +16 -24
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.js +3 -2
  14. package/dist/input/index.d.ts +6 -7
  15. package/dist/input/index.js +5 -4
  16. package/dist/input/keyboard.d.ts +3 -4
  17. package/dist/input/keyboard.js +8 -10
  18. package/dist/input/pointer-capture.d.ts +3 -3
  19. package/dist/input/pointer-capture.js +11 -12
  20. package/dist/input/pointer-lock.d.ts +3 -3
  21. package/dist/input/pointer-lock.js +10 -10
  22. package/dist/input/screen-joystick.d.ts +6 -10
  23. package/dist/input/screen-joystick.js +29 -36
  24. package/dist/input/screen-jump-button.d.ts +1 -1
  25. package/dist/model/index.d.ts +10 -13
  26. package/dist/model/index.js +12 -34
  27. package/dist/physics/index.d.ts +2 -5
  28. package/dist/physics/index.js +7 -16
  29. package/dist/simple-character/defaults.d.ts +2 -0
  30. package/dist/simple-character/defaults.js +2 -0
  31. package/dist/simple-character/index.d.ts +101 -0
  32. package/dist/simple-character/index.js +109 -0
  33. package/dist/simple-character/state/index.d.ts +6 -0
  34. package/dist/simple-character/state/index.js +6 -0
  35. package/dist/simple-character/state/jump-down.d.ts +3 -0
  36. package/dist/simple-character/state/jump-down.js +25 -0
  37. package/dist/simple-character/state/jump-forward.d.ts +5 -0
  38. package/dist/simple-character/state/jump-forward.js +39 -0
  39. package/dist/simple-character/state/jump-loop.d.ts +3 -0
  40. package/dist/simple-character/state/jump-loop.js +23 -0
  41. package/dist/simple-character/state/jump-start.d.ts +4 -0
  42. package/dist/simple-character/state/jump-start.js +30 -0
  43. package/dist/simple-character/state/jump-up.d.ts +5 -0
  44. package/dist/simple-character/state/jump-up.js +38 -0
  45. package/dist/simple-character/state/movement.d.ts +3 -0
  46. package/dist/simple-character/state/movement.js +59 -0
  47. package/dist/simple-character/update-input-velocity.d.ts +5 -0
  48. package/dist/simple-character/update-input-velocity.js +25 -0
  49. package/dist/simple-character/update-rotation.d.ts +6 -0
  50. package/dist/simple-character/update-rotation.js +40 -0
  51. package/dist/utils.d.ts +12 -5
  52. package/dist/utils.js +28 -39
  53. package/package.json +2 -2
  54. package/dist/simple-character.d.ts +0 -107
  55. package/dist/simple-character.js +0 -344
@@ -0,0 +1,18 @@
1
+ export async function loadDefaultCharacterAnimationUrl(type) {
2
+ switch (type) {
3
+ case 'idle':
4
+ return (await import('../assets/idle.js')).url;
5
+ case 'jumpDown':
6
+ return (await import('../assets/jump-down.js')).url;
7
+ case 'jumpForward':
8
+ return (await import('../assets/jump-forward.js')).url;
9
+ case 'jumpLoop':
10
+ return (await import('../assets/jump-loop.js')).url;
11
+ case 'jumpUp':
12
+ return (await import('../assets/jump-up.js')).url;
13
+ case 'run':
14
+ return (await import('../assets/run.js')).url;
15
+ case 'walk':
16
+ return (await import('../assets/walk.js')).url;
17
+ }
18
+ }
@@ -1,4 +1,4 @@
1
1
  import { AnimationClip } from 'three';
2
- import { loadCharacterModel } from '../model/index.js';
2
+ import type { CharacterModel } from '../model/index.js';
3
3
  import type { VRMHumanBoneName } from '@pixiv/three-vrm';
4
- export declare function loadVrmModelFbxAnimations(model: Exclude<Awaited<ReturnType<typeof loadCharacterModel>>, undefined>, url: string, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): Promise<Array<AnimationClip>>;
4
+ export declare function loadVrmModelFbxAnimations(model: CharacterModel, url: string, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): Promise<Array<AnimationClip>>;
@@ -1,3 +1,3 @@
1
1
  import { AnimationClip } from 'three';
2
- import { loadCharacterModel, VRMHumanBoneName } from '../model/index.js';
3
- export declare function loadVrmModelGltfAnimations(model: Exclude<Awaited<ReturnType<typeof loadCharacterModel>>, undefined>, url: string, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): Promise<Array<AnimationClip>>;
2
+ import type { CharacterModel, VRMHumanBoneName } from '../model/index.js';
3
+ export declare function loadVrmModelGltfAnimations(model: CharacterModel, url: string, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): Promise<Array<AnimationClip>>;
@@ -1,14 +1,18 @@
1
1
  import { VRMHumanBoneName } from '@pixiv/three-vrm';
2
2
  import { AnimationClip, Object3D } from 'three';
3
- import { loadCharacterModel } from '../model/index.js';
4
- export declare function fixModelAnimationClip(model: Exclude<Awaited<ReturnType<typeof loadCharacterModel>>, undefined>, clip: AnimationClip, clipScene: Object3D | undefined, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): void;
3
+ import { loadDefaultCharacterAnimationUrl } from './default.js';
4
+ import type { CharacterModel } from '../model/index.js';
5
+ export declare function fixModelAnimationClip(model: CharacterModel, clip: AnimationClip, clipScene: Object3D | undefined, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): void;
5
6
  export * from './gltf.js';
6
7
  export * from './fbx.js';
7
8
  export * from './vrma.js';
8
9
  export * from './utils.js';
9
- export type ModelAnimationOptions = {
10
+ export type CharacterAnimationMask = (boneName: VRMHumanBoneName) => boolean;
11
+ export type CharacterAnimationOptions = {
12
+ url: string | {
13
+ default: Parameters<typeof loadDefaultCharacterAnimationUrl>[0];
14
+ };
10
15
  type?: 'mixamo' | 'gltf' | 'vrma' | 'fbx' | 'bvh';
11
- url: string;
12
16
  removeXZMovement?: boolean;
13
17
  trimTime?: {
14
18
  start?: number;
@@ -16,18 +20,12 @@ export type ModelAnimationOptions = {
16
20
  };
17
21
  boneMap?: Record<string, VRMHumanBoneName>;
18
22
  scaleTime?: number;
23
+ mask?: CharacterAnimationMask;
19
24
  };
20
- export declare function loadCharacterModelAnimation(model: Exclude<Awaited<ReturnType<typeof loadCharacterModel>>, undefined>, options: ModelAnimationOptions): Promise<AnimationClip>;
21
- declare const simpleCharacterAnimationUrls: {
22
- walk: () => Promise<typeof import("../assets/walk.js")>;
23
- run: () => Promise<typeof import("../assets/run.js")>;
24
- idle: () => Promise<typeof import("../assets/idle.js")>;
25
- jumpUp: () => Promise<typeof import("../assets/jump-up.js")>;
26
- jumpLoop: () => Promise<typeof import("../assets/jump-loop.js")>;
27
- jumpDown: () => Promise<typeof import("../assets/jump-down.js")>;
28
- jumpForward: () => Promise<typeof import("../assets/jump-forward.js")>;
29
- };
30
- export declare const simpleCharacterAnimationNames: Array<keyof typeof simpleCharacterAnimationUrls>;
31
- export declare function getSimpleCharacterModelAnimationOptions(animationName: keyof typeof simpleCharacterAnimationUrls): Promise<ModelAnimationOptions>;
25
+ export type Tail<T extends any[]> = T extends [any, ...infer Rest] ? Rest : never;
26
+ export declare function flattenCharacterAnimationOptions(options: Exclude<CharacterAnimationOptions, false>): Tail<Parameters<typeof loadCharacterAnimation>>;
27
+ export declare function loadCharacterAnimation(model: CharacterModel, url: string | {
28
+ default: Parameters<typeof loadDefaultCharacterAnimationUrl>[0];
29
+ }, type?: CharacterAnimationOptions['type'], removeXZMovement?: boolean, trimStartTime?: number | undefined, trimEndTime?: number | undefined, boneMap?: Record<string, VRMHumanBoneName> | undefined, scaleTime?: number | undefined, mask?: CharacterAnimationMask): Promise<AnimationClip>;
32
30
  export declare const mixamoBoneMap: Record<string, VRMHumanBoneName>;
33
31
  export declare const bvhBoneMap: Record<string, VRMHumanBoneName>;
@@ -2,12 +2,12 @@ import { VRM } from '@pixiv/three-vrm';
2
2
  import { Euler, Quaternion, QuaternionKeyframeTrack, Vector3, VectorKeyframeTrack, } from 'three';
3
3
  import _bvhBoneMap from './bvh-bone-map.json';
4
4
  import { loadVrmModelBvhAnimations } from './bvh.js';
5
+ import { loadDefaultCharacterAnimationUrl } from './default.js';
5
6
  import { loadVrmModelFbxAnimations } from './fbx.js';
6
7
  import { loadVrmModelGltfAnimations } from './gltf.js';
7
8
  import _mixamoBoneMap from './mixamo-bone-map.json';
8
9
  import { scaleAnimationClipTime, trimAnimationClip } from './utils.js';
9
10
  import { loadVrmModelVrmaAnimations } from './vrma.js';
10
- import { cached } from '../utils.js';
11
11
  //helper variables for the quaternion retargeting
12
12
  const baseThisLocalRestRotation_inverse = new Quaternion();
13
13
  const baseThisLocalCurrentRotation = new Quaternion();
@@ -174,7 +174,23 @@ export * from './gltf.js';
174
174
  export * from './fbx.js';
175
175
  export * from './vrma.js';
176
176
  export * from './utils.js';
177
- async function uncachedLoadModelAnimation(model, type, url, removeXZMovement, trimStartTime, trimEndTime, boneMap, scaleTime) {
177
+ export function flattenCharacterAnimationOptions(options) {
178
+ return [
179
+ options.url,
180
+ options.type,
181
+ options.removeXZMovement,
182
+ options.trimTime?.start,
183
+ options.trimTime?.end,
184
+ options.boneMap,
185
+ options.scaleTime,
186
+ options.mask,
187
+ ];
188
+ }
189
+ export async function loadCharacterAnimation(model, url, type, removeXZMovement = false, trimStartTime, trimEndTime, boneMap, scaleTime, mask) {
190
+ if (typeof url === 'object') {
191
+ url = await loadDefaultCharacterAnimationUrl(url.default);
192
+ type = 'gltf';
193
+ }
178
194
  let clips;
179
195
  if (type == null) {
180
196
  const lowerCaseUrl = url.toLocaleLowerCase();
@@ -226,39 +242,5 @@ async function uncachedLoadModelAnimation(model, type, url, removeXZMovement, tr
226
242
  }
227
243
  return clip;
228
244
  }
229
- export function loadCharacterModelAnimation(model, options) {
230
- return cached(uncachedLoadModelAnimation, [
231
- model,
232
- options.type,
233
- options.url,
234
- options.removeXZMovement ?? false,
235
- options.trimTime?.start,
236
- options.trimTime?.end,
237
- options.boneMap,
238
- options.scaleTime,
239
- ]);
240
- }
241
- const extraOptions = {
242
- walk: { scaleTime: 0.5 },
243
- run: { scaleTime: 0.8 },
244
- jumpForward: { scaleTime: 0.9 },
245
- };
246
- const simpleCharacterAnimationUrls = {
247
- walk: () => import('../assets/walk.js'),
248
- run: () => import('../assets/run.js'),
249
- idle: () => import('../assets/idle.js'),
250
- jumpUp: () => import('../assets/jump-up.js'),
251
- jumpLoop: () => import('../assets/jump-loop.js'),
252
- jumpDown: () => import('../assets/jump-down.js'),
253
- jumpForward: () => import('../assets/jump-forward.js'),
254
- };
255
- export const simpleCharacterAnimationNames = Object.keys(simpleCharacterAnimationUrls);
256
- export async function getSimpleCharacterModelAnimationOptions(animationName) {
257
- return {
258
- type: 'gltf',
259
- ...extraOptions[animationName],
260
- url: (await simpleCharacterAnimationUrls[animationName]()).url,
261
- };
262
- }
263
245
  export const mixamoBoneMap = _mixamoBoneMap;
264
246
  export const bvhBoneMap = _bvhBoneMap;
@@ -0,0 +1,3 @@
1
+ import type { CharacterAnimationMask } from './index.js';
2
+ import type { AnimationClip } from 'three';
3
+ export declare function applyMask(clip: AnimationClip, mask: CharacterAnimationMask): void;
@@ -0,0 +1,3 @@
1
+ export function applyMask(clip, mask) {
2
+ clip.tracks = clip.tracks.filter((track) => mask(track.name.split('.')[0]));
3
+ }
package/dist/camera.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Object3D, Vector3, Vector3Tuple, Ray } from 'three';
2
- import { SimpleCharacter } from './simple-character.js';
2
+ import { InputSystem } from './input/index.js';
3
3
  export declare const FirstPersonCharacterCameraBehavior: SimpleCharacterCameraBehaviorOptions;
4
4
  export type SimpleCharacterCameraBehaviorOptions = {
5
5
  /**
@@ -55,16 +55,12 @@ export type SimpleCharacterCameraBehaviorOptions = {
55
55
  maxDistance?: number;
56
56
  } | boolean;
57
57
  } | boolean;
58
- export declare class SimpleCharacterCameraBehavior {
59
- getCamera: () => Object3D;
60
- character: SimpleCharacter;
61
- private readonly raycast?;
58
+ export declare class CharacterCameraBehavior {
62
59
  rotationPitch: number;
63
60
  rotationYaw: number;
64
61
  zoomDistance: number;
65
62
  private collisionFreeZoomDistance;
66
63
  private firstUpdate;
67
- constructor(getCamera: () => Object3D, character: SimpleCharacter, raycast?: ((ray: Ray, far: number) => number | undefined) | undefined);
68
64
  private setRotationFromDelta;
69
65
  private setDistanceFromDelta;
70
66
  private computeCharacterBaseOffset;
@@ -74,5 +70,5 @@ export declare class SimpleCharacterCameraBehavior {
74
70
  /**
75
71
  * @param delta in seconds
76
72
  */
77
- update(deltaTime: number, options?: SimpleCharacterCameraBehaviorOptions): void;
73
+ update(camera: Object3D, target: Object3D, inputSystem: InputSystem, deltaTime: number, raycast?: (ray: Ray, far: number) => number | undefined, options?: SimpleCharacterCameraBehaviorOptions): void;
78
74
  }
package/dist/camera.js CHANGED
@@ -11,25 +11,17 @@ const sphericalOffset = new Vector3();
11
11
  const characterWorldPosition = new Vector3();
12
12
  const euler = new Euler();
13
13
  const rayHelper = new Ray();
14
- export class SimpleCharacterCameraBehavior {
15
- getCamera;
16
- character;
17
- raycast;
14
+ export class CharacterCameraBehavior {
18
15
  rotationPitch = (-20 * Math.PI) / 180;
19
16
  rotationYaw = 0;
20
17
  zoomDistance = 4; // Changed from zoom to distance for clearer semantics
21
18
  //internal state
22
19
  collisionFreeZoomDistance = this.zoomDistance;
23
20
  firstUpdate = true;
24
- constructor(getCamera, character, raycast) {
25
- this.getCamera = getCamera;
26
- this.character = character;
27
- this.raycast = raycast;
28
- }
29
- setRotationFromDelta(delta, rotationOptions) {
21
+ setRotationFromDelta(camera, delta, rotationOptions) {
30
22
  if (delta.lengthSq() < 0.0001) {
31
23
  // use current camera rotation if very close to target
32
- euler.setFromQuaternion(this.getCamera().quaternion, 'YXZ');
24
+ euler.setFromQuaternion(camera.quaternion, 'YXZ');
33
25
  this.rotationPitch = euler.x;
34
26
  this.rotationYaw = euler.y;
35
27
  return;
@@ -63,7 +55,7 @@ export class SimpleCharacterCameraBehavior {
63
55
  /**
64
56
  * @param delta in seconds
65
57
  */
66
- update(deltaTime, options = true) {
58
+ update(camera, target, inputSystem, deltaTime, raycast, options = true) {
67
59
  if (options === false) {
68
60
  this.firstUpdate = true;
69
61
  return;
@@ -74,33 +66,33 @@ export class SimpleCharacterCameraBehavior {
74
66
  }
75
67
  //compute character->camera delta through offset
76
68
  this.computeCharacterBaseOffset(chracterBaseOffsetHelper, options.characterBaseOffset);
77
- this.character.getWorldPosition(characterWorldPosition);
69
+ target.getWorldPosition(characterWorldPosition);
78
70
  characterWorldPosition.add(chracterBaseOffsetHelper);
79
- this.getCamera().getWorldPosition(deltaHelper);
71
+ camera.getWorldPosition(deltaHelper);
80
72
  deltaHelper.sub(characterWorldPosition);
81
73
  // apply rotation input to rotationYaw and rotationPitch if not disabled or first update
82
74
  let rotationOptions = options.rotation ?? true;
83
75
  if (!this.firstUpdate && rotationOptions !== false) {
84
76
  rotationOptions = rotationOptions === true ? {} : rotationOptions;
85
77
  const rotationSpeed = rotationOptions.speed ?? 1000.0;
86
- const deltaYaw = this.character.inputSystem.get(DeltaYawField);
87
- const deltaPitch = this.character.inputSystem.get(DeltaPitchField);
78
+ const deltaYaw = inputSystem.get(DeltaYawField);
79
+ const deltaPitch = inputSystem.get(DeltaPitchField);
88
80
  this.rotationYaw = this.clampYaw(this.rotationYaw + deltaYaw * rotationSpeed * deltaTime, rotationOptions);
89
81
  this.rotationPitch = this.clampPitch(this.rotationPitch + deltaPitch * rotationSpeed * deltaTime, rotationOptions);
90
82
  }
91
83
  else {
92
- this.setRotationFromDelta(deltaHelper, typeof rotationOptions === 'boolean' ? {} : rotationOptions);
84
+ this.setRotationFromDelta(camera, deltaHelper, typeof rotationOptions === 'boolean' ? {} : rotationOptions);
93
85
  }
94
86
  // apply yaw and pitch to camera rotation
95
- this.getCamera().rotation.set(this.rotationPitch, this.rotationYaw, 0, 'YXZ');
96
- rayHelper.direction.set(0, 0, 1).applyEuler(this.getCamera().rotation);
87
+ camera.rotation.set(this.rotationPitch, this.rotationYaw, 0, 'YXZ');
88
+ rayHelper.direction.set(0, 0, 1).applyEuler(camera.rotation);
97
89
  rayHelper.origin.copy(characterWorldPosition);
98
90
  // apply zoom input to zoomDistance if not disabled or first update
99
91
  let zoomOptions = options.zoom ?? true;
100
92
  if (!this.firstUpdate && zoomOptions !== false) {
101
93
  zoomOptions = zoomOptions === true ? {} : zoomOptions;
102
94
  const zoomSpeed = zoomOptions.speed ?? 1000.0;
103
- const deltaZoom = this.character.inputSystem.get(DeltaZoomField);
95
+ const deltaZoom = inputSystem.get(DeltaZoomField);
104
96
  const zoomFactor = 1 + deltaZoom * zoomSpeed * deltaTime;
105
97
  if (deltaZoom >= 0) {
106
98
  this.zoomDistance *= zoomFactor;
@@ -119,19 +111,19 @@ export class SimpleCharacterCameraBehavior {
119
111
  if (collisionOptions === true) {
120
112
  collisionOptions = {};
121
113
  }
122
- let distance = this.raycast?.(rayHelper, this.zoomDistance);
114
+ let distance = raycast?.(rayHelper, this.zoomDistance);
123
115
  if (distance != null) {
124
116
  this.collisionFreeZoomDistance = distance - (collisionOptions?.offset ?? 0.2);
125
117
  }
126
118
  }
127
119
  // Calculate camera position using spherical coordinates from euler
128
120
  sphericalOffset.set(0, 0, this.collisionFreeZoomDistance);
129
- sphericalOffset.applyEuler(this.getCamera().rotation);
121
+ sphericalOffset.applyEuler(camera.rotation);
130
122
  // Get target position with offset (reuse helper vector)
131
- this.character.getWorldPosition(characterWorldPosition);
123
+ target.getWorldPosition(characterWorldPosition);
132
124
  this.computeCharacterBaseOffset(chracterBaseOffsetHelper, options.characterBaseOffset);
133
125
  characterWorldPosition.add(chracterBaseOffsetHelper);
134
126
  // Set camera position relative to target
135
- this.getCamera().position.copy(characterWorldPosition).add(sphericalOffset);
127
+ camera.position.copy(characterWorldPosition).add(sphericalOffset);
136
128
  }
137
129
  }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- export { extractProxy, getIsMobileMediaQuery, isMobile } from './utils.js';
1
+ export * from './utils.js';
2
2
  export * from './input/index.js';
3
+ export * from './utils.js';
3
4
  export * from './camera.js';
4
5
  export * from './physics/index.js';
5
6
  export * from './animation/index.js';
6
7
  export * from './material.js';
7
- export * from './simple-character.js';
8
+ export * from './simple-character/index.js';
8
9
  export * from './model/index.js';
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
- export { extractProxy, getIsMobileMediaQuery, isMobile } from './utils.js';
1
+ export * from './utils.js';
2
2
  export * from './input/index.js';
3
+ export * from './utils.js';
3
4
  export * from './camera.js';
4
5
  export * from './physics/index.js';
5
6
  export * from './animation/index.js';
6
7
  export * from './material.js';
7
- export * from './simple-character.js';
8
+ export * from './simple-character/index.js';
8
9
  export * from './model/index.js';
9
10
  (function injectMobileClassStyle() {
10
11
  if (typeof document === 'undefined') {
@@ -1,8 +1,7 @@
1
- export declare class InputSystem {
2
- private readonly inputs;
3
- constructor(domElement: HTMLElement, inputs: ReadonlyArray<Input | {
4
- new (element: HTMLElement, options?: {}): Input;
5
- }>, options?: {});
1
+ export declare class InputSystem<T extends {} = {}> {
2
+ options: T;
3
+ readonly inputs: Array<Input>;
4
+ constructor(options?: T);
6
5
  add(input: Input): void;
7
6
  remove(input: Input): void;
8
7
  dispose(): void;
@@ -21,8 +20,8 @@ export declare const RunField: InputField<boolean>;
21
20
  export declare const DeltaZoomField: InputField<number>;
22
21
  export declare const DeltaYawField: InputField<number>;
23
22
  export declare const DeltaPitchField: InputField<number>;
24
- export interface Input {
25
- get<T>(field: InputField<T>): T | undefined;
23
+ export interface Input<O = {}> {
24
+ get<T>(field: InputField<T>, options: O): T | undefined;
26
25
  dispose?(): void;
27
26
  }
28
27
  export * from './pointer-lock.js';
@@ -1,7 +1,8 @@
1
1
  export class InputSystem {
2
- inputs;
3
- constructor(domElement, inputs, options) {
4
- this.inputs = inputs.map((input) => (typeof input === 'function' ? new input(domElement, options) : input));
2
+ options;
3
+ inputs = [];
4
+ constructor(options = {}) {
5
+ this.options = options;
5
6
  }
6
7
  add(input) {
7
8
  this.inputs.push(input);
@@ -20,7 +21,7 @@ export class InputSystem {
20
21
  get(field) {
21
22
  let current;
22
23
  for (const input of this.inputs) {
23
- const result = input.get(field);
24
+ const result = input.get(field, this.options);
24
25
  if (result == null) {
25
26
  continue;
26
27
  }
@@ -7,11 +7,10 @@ export type LocomotionKeyboardInputOptions = {
7
7
  keyboardRunKeys?: Array<string>;
8
8
  keyboardJumpKeys?: Array<string>;
9
9
  };
10
- export declare class LocomotionKeyboardInput implements Input {
11
- private readonly options;
10
+ export declare class LocomotionKeyboardInput implements Input<LocomotionKeyboardInputOptions> {
12
11
  private readonly abortController;
13
12
  private readonly keyState;
14
- constructor(domElement: HTMLElement, options?: LocomotionKeyboardInputOptions);
15
- get<T>(field: InputField<T>): T | undefined;
13
+ constructor(domElement: HTMLElement);
14
+ get<T>(field: InputField<T>, options: LocomotionKeyboardInputOptions): T | undefined;
16
15
  dispose(): void;
17
16
  }
@@ -6,11 +6,9 @@ const DefaultMoveRightKeys = ['KeyD'];
6
6
  const DefaultRunKeys = ['ShiftRight', 'ShiftLeft'];
7
7
  const DefaultJumpKeys = ['Space'];
8
8
  export class LocomotionKeyboardInput {
9
- options;
10
9
  abortController = new AbortController();
11
10
  keyState = new Map();
12
- constructor(domElement, options = {}) {
13
- this.options = options;
11
+ constructor(domElement) {
14
12
  domElement.tabIndex = 0;
15
13
  domElement.addEventListener('keydown', (event) => {
16
14
  let state = this.keyState.get(event.code);
@@ -41,9 +39,9 @@ export class LocomotionKeyboardInput {
41
39
  signal: this.abortController.signal,
42
40
  });
43
41
  }
44
- get(field) {
42
+ get(field, options) {
45
43
  if (field === LastTimeJumpPressedField) {
46
- const jumpKeys = this.options.keyboardJumpKeys ?? DefaultJumpKeys;
44
+ const jumpKeys = options.keyboardJumpKeys ?? DefaultJumpKeys;
47
45
  const pressed = jumpKeys
48
46
  .map((key) => this.keyState.get(key)?.pressTime ?? null)
49
47
  .filter((t) => t != null);
@@ -52,19 +50,19 @@ export class LocomotionKeyboardInput {
52
50
  let keys;
53
51
  switch (field) {
54
52
  case MoveForwardField:
55
- keys = this.options.keyboardMoveForwardKeys ?? DefaultMoveForwardKeys;
53
+ keys = options.keyboardMoveForwardKeys ?? DefaultMoveForwardKeys;
56
54
  break;
57
55
  case MoveBackwardField:
58
- keys = this.options.keyboardMoveBackwardKeys ?? DefaultMoveBackwardKeys;
56
+ keys = options.keyboardMoveBackwardKeys ?? DefaultMoveBackwardKeys;
59
57
  break;
60
58
  case MoveLeftField:
61
- keys = this.options.keyboardMoveLeftKeys ?? DefaultMoveLeftKeys;
59
+ keys = options.keyboardMoveLeftKeys ?? DefaultMoveLeftKeys;
62
60
  break;
63
61
  case MoveRightField:
64
- keys = this.options.keyboardMoveRightKeys ?? DefaultMoveRightKeys;
62
+ keys = options.keyboardMoveRightKeys ?? DefaultMoveRightKeys;
65
63
  break;
66
64
  case RunField:
67
- keys = this.options.keyboardRunKeys ?? DefaultRunKeys;
65
+ keys = options.keyboardRunKeys ?? DefaultRunKeys;
68
66
  break;
69
67
  }
70
68
  if (keys == null) {
@@ -6,7 +6,7 @@ export type PointerCaptureInputOptions = {
6
6
  /**
7
7
  * @requires to manually execute `domElement.setPointerCapture(pointerId)` on pointerdown
8
8
  */
9
- export declare class PointerCaptureInput implements Input {
9
+ export declare class PointerCaptureInput implements Input<PointerCaptureInputOptions> {
10
10
  private readonly domElement;
11
11
  private readonly abortController;
12
12
  private deltaZoom;
@@ -14,7 +14,7 @@ export declare class PointerCaptureInput implements Input {
14
14
  private deltaPitch;
15
15
  private activePointers;
16
16
  private lastPinchDist;
17
- constructor(domElement: HTMLElement, options?: PointerCaptureInputOptions);
18
- get<T>(field: InputField<T>): T | undefined;
17
+ constructor(domElement: HTMLElement);
18
+ get<T>(field: InputField<T>, options: PointerCaptureInputOptions): T | undefined;
19
19
  dispose(): void;
20
20
  }
@@ -10,7 +10,7 @@ export class PointerCaptureInput {
10
10
  deltaPitch = 0;
11
11
  activePointers = new Map();
12
12
  lastPinchDist = null;
13
- constructor(domElement, options = {}) {
13
+ constructor(domElement) {
14
14
  this.domElement = domElement;
15
15
  domElement.addEventListener('pointerdown', (event) => {
16
16
  this.domElement.setPointerCapture(event.pointerId);
@@ -31,16 +31,14 @@ export class PointerCaptureInput {
31
31
  const pts = Array.from(this.activePointers.values());
32
32
  if (this.lastPinchDist != null) {
33
33
  const d = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
34
- const zoomSpeed = options.pointerCaptureZoomSpeed ?? 0.0001;
35
- this.deltaZoom += (this.lastPinchDist - d) * zoomSpeed;
34
+ this.deltaZoom += this.lastPinchDist - d;
36
35
  this.lastPinchDist = d;
37
36
  }
38
37
  event.preventDefault();
39
38
  return;
40
39
  }
41
- const rotationSpeed = options.pointerCaptureRotationSpeed ?? 0.4;
42
- this.deltaYaw -= (rotationSpeed * event.movementX) / window.innerHeight;
43
- this.deltaPitch -= (rotationSpeed * event.movementY) / window.innerHeight;
40
+ this.deltaYaw -= event.movementX / window.innerHeight;
41
+ this.deltaPitch -= event.movementY / window.innerHeight;
44
42
  }, {
45
43
  signal: this.abortController.signal,
46
44
  });
@@ -64,25 +62,26 @@ export class PointerCaptureInput {
64
62
  });
65
63
  domElement.addEventListener('wheel', (event) => {
66
64
  event.preventDefault();
67
- const zoomSpeed = options.pointerCaptureZoomSpeed ?? 0.0001;
68
- this.deltaZoom += event.deltaY * zoomSpeed;
65
+ this.deltaZoom += event.deltaY;
69
66
  }, {
70
67
  signal: this.abortController.signal,
71
68
  });
72
69
  }
73
- get(field) {
70
+ get(field, options) {
71
+ const rotationSpeed = options.pointerCaptureRotationSpeed ?? 0.4;
72
+ const zoomSpeed = options.pointerCaptureZoomSpeed ?? 0.0001;
74
73
  let result;
75
74
  switch (field) {
76
75
  case DeltaPitchField:
77
- result = this.deltaPitch;
76
+ result = (this.deltaPitch * rotationSpeed);
78
77
  this.deltaPitch = 0;
79
78
  break;
80
79
  case DeltaYawField:
81
- result = this.deltaYaw;
80
+ result = (this.deltaYaw * rotationSpeed);
82
81
  this.deltaYaw = 0;
83
82
  break;
84
83
  case DeltaZoomField:
85
- result = this.deltaZoom;
84
+ result = (this.deltaZoom * zoomSpeed);
86
85
  this.deltaZoom = 0;
87
86
  break;
88
87
  }
@@ -6,12 +6,12 @@ export type PointerLockInputOptions = {
6
6
  /**
7
7
  * @requires to manually execute `domElement.requestPointerLock()`
8
8
  */
9
- export declare class PointerLockInput implements Input {
9
+ export declare class PointerLockInput implements Input<PointerLockInputOptions> {
10
10
  private readonly abortController;
11
11
  private deltaZoom;
12
12
  private deltaYaw;
13
13
  private deltaPitch;
14
- constructor(domElement: HTMLElement, options?: PointerLockInputOptions);
15
- get<T>(field: InputField<T>): T | undefined;
14
+ constructor(domElement: HTMLElement);
15
+ get<T>(field: InputField<T>, options: PointerLockInputOptions): T | undefined;
16
16
  dispose(): void;
17
17
  }
@@ -7,16 +7,15 @@ export class PointerLockInput {
7
7
  deltaZoom = 0;
8
8
  deltaYaw = 0;
9
9
  deltaPitch = 0;
10
- constructor(domElement, options = {}) {
10
+ constructor(domElement) {
11
11
  domElement.addEventListener('pointermove', (event) => {
12
12
  if (document.pointerLockElement != domElement) {
13
13
  return;
14
14
  }
15
- const rotationSpeed = options.pointerLockRotationSpeed ?? 0.4;
16
15
  // Compute based on domElement bounds instead of window.innerHeight
17
16
  const rect = domElement.getBoundingClientRect();
18
- this.deltaYaw -= (rotationSpeed * event.movementX) / rect.height;
19
- this.deltaPitch -= (rotationSpeed * event.movementY) / rect.height;
17
+ this.deltaYaw -= event.movementX / rect.height;
18
+ this.deltaPitch -= event.movementY / rect.height;
20
19
  }, {
21
20
  signal: this.abortController.signal,
22
21
  });
@@ -24,26 +23,27 @@ export class PointerLockInput {
24
23
  if (document.pointerLockElement != domElement) {
25
24
  return;
26
25
  }
27
- const zoomSpeed = options.pointerLockZoomSpeed ?? 0.0001;
28
- this.deltaZoom += event.deltaY * zoomSpeed;
26
+ this.deltaZoom += event.deltaY;
29
27
  event.preventDefault();
30
28
  }, {
31
29
  signal: this.abortController.signal,
32
30
  });
33
31
  }
34
- get(field) {
32
+ get(field, options) {
33
+ const rotationSpeed = options.pointerLockRotationSpeed ?? 0.4;
34
+ const zoomSpeed = options.pointerLockZoomSpeed ?? 0.0001;
35
35
  let result;
36
36
  switch (field) {
37
37
  case DeltaPitchField:
38
- result = this.deltaPitch;
38
+ result = (this.deltaPitch * rotationSpeed);
39
39
  this.deltaPitch = 0;
40
40
  break;
41
41
  case DeltaYawField:
42
- result = this.deltaYaw;
42
+ result = (this.deltaYaw * rotationSpeed);
43
43
  this.deltaYaw = 0;
44
44
  break;
45
45
  case DeltaZoomField:
46
- result = this.deltaZoom;
46
+ result = (this.deltaZoom * zoomSpeed);
47
47
  this.deltaZoom = 0;
48
48
  break;
49
49
  }
@@ -3,19 +3,15 @@ export type ScreenJoystickInputOptions = {
3
3
  screenJoystickRunDistancePx?: number;
4
4
  screenJoystickDeadZonePx?: number;
5
5
  };
6
- export declare class ScreenJoystickInput implements Input {
7
- private readonly options;
6
+ export declare class ScreenJoystickInput implements Input<ScreenJoystickInputOptions> {
8
7
  readonly root: HTMLDivElement;
9
8
  private readonly handle;
10
- private moveX;
11
- private moveY;
12
- private running;
13
- private readonly joystickRadius;
14
- private joyCenterX;
15
- private joyCenterY;
16
9
  private pointerId;
17
- constructor(domElement: HTMLElement, options?: ScreenJoystickInputOptions);
18
- get<T>(field: InputField<T>): T | undefined;
10
+ private distanceToCenter;
11
+ private clampedX;
12
+ private clampedY;
13
+ constructor(domElement: HTMLElement);
14
+ get<T>(field: InputField<T>, options: ScreenJoystickInputOptions): T | undefined;
19
15
  dispose(): void;
20
16
  private updateHandle;
21
17
  private resetHandle;