@pmndrs/viverse 0.2.0 → 0.2.2

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 (64) hide show
  1. package/dist/action/action.d.ts +40 -0
  2. package/dist/action/action.js +114 -0
  3. package/dist/action/index.d.ts +18 -0
  4. package/dist/action/index.js +25 -0
  5. package/dist/action/keyboard.d.ts +26 -0
  6. package/dist/action/keyboard.js +89 -0
  7. package/dist/action/pointer-capture.d.ts +8 -0
  8. package/dist/{input → action}/pointer-capture.js +17 -42
  9. package/dist/action/pointer-lock.d.ts +6 -0
  10. package/dist/action/pointer-lock.js +49 -0
  11. package/dist/action/pointer.d.ts +18 -0
  12. package/dist/action/pointer.js +90 -0
  13. package/dist/action/screen-joystick.d.ts +16 -0
  14. package/dist/action/screen-joystick.js +120 -0
  15. package/dist/action/screen-jump-button.d.ts +6 -0
  16. package/dist/action/screen-jump-button.js +32 -0
  17. package/dist/animation/bone-map.d.ts +4 -0
  18. package/dist/animation/bone-map.js +11 -0
  19. package/dist/animation/default.d.ts +9 -1
  20. package/dist/animation/default.js +16 -9
  21. package/dist/animation/index.d.ts +8 -13
  22. package/dist/animation/index.js +61 -39
  23. package/dist/animation/mask.d.ts +4 -1
  24. package/dist/animation/mask.js +50 -0
  25. package/dist/camera.d.ts +8 -4
  26. package/dist/camera.js +24 -9
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/model/index.d.ts +1 -2
  30. package/dist/simple-character/apply-input-options.d.ts +2 -0
  31. package/dist/simple-character/apply-input-options.js +34 -0
  32. package/dist/simple-character/index.d.ts +33 -10
  33. package/dist/simple-character/index.js +18 -19
  34. package/dist/simple-character/state/jump-down.js +2 -1
  35. package/dist/simple-character/state/jump-forward.js +4 -2
  36. package/dist/simple-character/state/jump-loop.js +2 -1
  37. package/dist/simple-character/state/jump-start.js +2 -2
  38. package/dist/simple-character/state/jump-up.js +4 -2
  39. package/dist/simple-character/state/movement.js +12 -8
  40. package/dist/simple-character/update-input-velocity.d.ts +1 -2
  41. package/dist/simple-character/update-input-velocity.js +4 -4
  42. package/dist/simple-character/update-rotation.js +1 -1
  43. package/dist/utils.d.ts +5 -5
  44. package/dist/utils.js +15 -6
  45. package/package.json +2 -2
  46. package/dist/animation/bvh.d.ts +0 -4
  47. package/dist/animation/bvh.js +0 -8
  48. package/dist/animation/fbx.d.ts +0 -4
  49. package/dist/animation/fbx.js +0 -8
  50. package/dist/animation/gltf.d.ts +0 -3
  51. package/dist/animation/gltf.js +0 -8
  52. package/dist/animation/vrma.d.ts +0 -3
  53. package/dist/animation/vrma.js +0 -8
  54. package/dist/input/index.d.ts +0 -31
  55. package/dist/input/index.js +0 -77
  56. package/dist/input/keyboard.d.ts +0 -16
  57. package/dist/input/keyboard.js +0 -82
  58. package/dist/input/pointer-capture.d.ts +0 -20
  59. package/dist/input/pointer-lock.d.ts +0 -17
  60. package/dist/input/pointer-lock.js +0 -55
  61. package/dist/input/screen-joystick.d.ts +0 -18
  62. package/dist/input/screen-joystick.js +0 -120
  63. package/dist/input/screen-jump-button.d.ts +0 -8
  64. package/dist/input/screen-jump-button.js +0 -49
@@ -1,26 +1,26 @@
1
1
  import { VRM, VRMUtils } from '@pixiv/three-vrm';
2
2
  import { runTimeline, GraphTimeline } from '@pmndrs/timeline';
3
3
  import { Group, Vector3 } from 'three';
4
+ import { applySimpleCharacterActionBindingOptions } from './apply-input-options.js';
5
+ import { KeyboardLocomotionActionBindings, PointerCaptureRotateZoomActionBindings, ScreenJoystickLocomotionActionBindings, ScreenButtonJumpActionBindings, } from '../action/index.js';
4
6
  import { CharacterCameraBehavior } from '../camera.js';
5
- import { LocomotionKeyboardInput, PointerCaptureInput, ScreenJoystickInput, ScreenJumpButtonInput, InputSystem, } from '../input/index.js';
6
7
  import { loadCharacterModel, flattenCharacterModelOptions, } from '../model/index.js';
7
8
  import { BvhCharacterPhysics } from '../physics/index.js';
9
+ import { shouldJump } from '../utils.js';
8
10
  import { loadSimpleCharacterJumpDownState } from './state/jump-down.js';
9
11
  import { loadSimpleCharacterJumpForwardAction, loadSimpleCharacterJumpForwardState } from './state/jump-forward.js';
10
12
  import { loadSimpleCharacterJumpLoopState } from './state/jump-loop.js';
11
13
  import { loadSimpleCharacterJumpStartState } from './state/jump-start.js';
12
14
  import { loadSimpleCharacterJumpUpAction, loadSimpleCharacterJumpUpState } from './state/jump-up.js';
13
15
  import { loadSimpleCharacterMovingState } from './state/movement.js';
14
- import { updateSimpleCharacterInputVelocity } from './update-input-velocity.js';
16
+ import { updateSimpleCharacterVelocity } from './update-input-velocity.js';
15
17
  import { updateSimpleCharacterRotation } from './update-rotation.js';
16
- import { shouldJump } from '../utils.js';
17
18
  export class SimpleCharacter extends Group {
18
19
  camera;
19
20
  world;
20
21
  options;
21
22
  cameraBehavior;
22
23
  physics;
23
- inputSystem;
24
24
  currentAnimationRef = {};
25
25
  //loaded asychronously
26
26
  model;
@@ -28,24 +28,22 @@ export class SimpleCharacter extends Group {
28
28
  graph = new GraphTimeline('moving');
29
29
  abortController = new AbortController();
30
30
  lastJump = 0;
31
+ abortSignal = this.abortController.signal;
31
32
  constructor(camera, world, domElement, options = {}) {
32
33
  super();
33
34
  this.camera = camera;
34
35
  this.world = world;
35
36
  this.options = options;
36
- this.inputSystem = new InputSystem();
37
- const inputOptions = options.input ?? [
38
- ScreenJoystickInput,
39
- ScreenJumpButtonInput,
40
- PointerCaptureInput,
41
- LocomotionKeyboardInput,
42
- ];
43
- for (let input of inputOptions) {
44
- this.inputSystem.add(typeof input === 'function' ? new input(domElement) : input);
45
- }
37
+ const actionBindingsList = (options.actionBindings ??
38
+ options.input ?? [
39
+ ScreenJoystickLocomotionActionBindings,
40
+ ScreenButtonJumpActionBindings,
41
+ PointerCaptureRotateZoomActionBindings,
42
+ KeyboardLocomotionActionBindings,
43
+ ]).map((ActionBindings) => new ActionBindings(domElement, this.abortController.signal));
44
+ applySimpleCharacterActionBindingOptions(actionBindingsList, options.actionBindingOptions ?? options.inputOptions);
46
45
  // camera behavior
47
46
  this.cameraBehavior = new CharacterCameraBehavior();
48
- console.log(this.graph);
49
47
  // physics
50
48
  this.physics = new BvhCharacterPhysics(world);
51
49
  // timeline graph
@@ -81,29 +79,30 @@ export class SimpleCharacter extends Group {
81
79
  const jumpOptions = this.options.movement?.jump;
82
80
  if (jumpOptions != false &&
83
81
  this.model == null &&
84
- shouldJump(this.physics, this.inputSystem, this.lastJump, jumpOptions == true ? undefined : jumpOptions?.bufferTime)) {
82
+ shouldJump(this.physics, this.lastJump, jumpOptions == true ? undefined : jumpOptions?.bufferTime)) {
85
83
  this.physics.applyVelocity(new Vector3(0, (typeof this.options.movement?.jump === 'object' ? this.options.movement?.jump.speed : undefined) ?? 8, 0));
86
84
  this.lastJump = performance.now() / 1000;
87
85
  }
88
86
  if (this.model != null) {
89
87
  updateSimpleCharacterRotation(delta, this.physics, this.camera, this.model, this.options.animation);
90
88
  }
91
- updateSimpleCharacterInputVelocity(this.camera, this.inputSystem, this.physics, this.options.movement);
89
+ updateSimpleCharacterVelocity(this.camera, this.physics, this.options.movement);
92
90
  this.updateTimeline?.(undefined, delta);
93
91
  this.model?.mixer.update(delta);
94
92
  if (this.model instanceof VRM) {
95
93
  this.model.update(delta);
96
94
  }
97
95
  this.physics.update(this, delta, this.options.physics);
98
- this.cameraBehavior.update(this.camera, this, this.inputSystem, delta, this.world.raycast.bind(this.world), this.options.cameraBehavior);
96
+ this.cameraBehavior.update(this.camera, this, delta, this.world.raycast.bind(this.world), this.options.cameraBehavior);
99
97
  }
100
98
  dispose() {
101
99
  this.abortController.abort();
102
100
  this.parent?.remove(this);
103
101
  this.model?.scene.dispatchEvent({ type: 'dispose' });
104
- this.inputSystem.dispose();
102
+ this.cameraBehavior.dispose();
105
103
  VRMUtils.deepDispose(this);
106
104
  }
107
105
  }
108
106
  export * from './update-input-velocity.js';
109
107
  export * from './update-rotation.js';
108
+ export * from './apply-input-options.js';
@@ -1,5 +1,6 @@
1
1
  import { action, timePassed } from '@pmndrs/timeline';
2
2
  import { LoopOnce } from 'three';
3
+ import { JumpDownAnimationUrl } from '../../animation/default.js';
3
4
  import { flattenCharacterAnimationOptions, loadCharacterAnimation } from '../../animation/index.js';
4
5
  import { startAnimation } from '../../utils.js';
5
6
  import { DefaultCrossFadeDuration } from '../defaults.js';
@@ -8,7 +9,7 @@ export async function loadSimpleCharacterJumpDownState(state, options) {
8
9
  if (model == null) {
9
10
  throw new Error(`Unable to load animation without existing model`);
10
11
  }
11
- const jumpDown = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions(options.animation?.jumpDown ?? { url: { default: 'jumpDown' } })));
12
+ const jumpDown = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions({ url: JumpDownAnimationUrl, ...options.animation?.jumpDown })));
12
13
  jumpDown.loop = LoopOnce;
13
14
  jumpDown.clampWhenFinished = true;
14
15
  return {
@@ -1,5 +1,6 @@
1
1
  import { action, animationFinished } from '@pmndrs/timeline';
2
2
  import { LoopOnce, Vector3 } from 'three';
3
+ import { JumpForwardAnimationUrl } from '../../animation/default.js';
3
4
  import { flattenCharacterAnimationOptions, loadCharacterAnimation } from '../../animation/index.js';
4
5
  import { startAnimation } from '../../utils.js';
5
6
  import { DefaultCrossFadeDuration } from '../defaults.js';
@@ -8,9 +9,10 @@ export async function loadSimpleCharacterJumpForwardAction(state, options) {
8
9
  if (model == null) {
9
10
  throw new Error(`Unable to load animation without existing model`);
10
11
  }
11
- const jumpForward = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions(options.animation?.jumpForward ?? {
12
- url: { default: 'jumpForward' },
12
+ const jumpForward = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions({
13
+ url: JumpForwardAnimationUrl,
13
14
  scaleTime: 0.9,
15
+ ...options.animation?.jumpForward,
14
16
  })));
15
17
  jumpForward.loop = LoopOnce;
16
18
  jumpForward.clampWhenFinished = true;
@@ -1,4 +1,5 @@
1
1
  import { action } from '@pmndrs/timeline';
2
+ import { JumpLoopAnimationUrl } from '../../animation/default.js';
2
3
  import { flattenCharacterAnimationOptions, loadCharacterAnimation } from '../../animation/index.js';
3
4
  import { startAnimation } from '../../utils.js';
4
5
  import { DefaultCrossFadeDuration } from '../defaults.js';
@@ -7,7 +8,7 @@ export async function loadSimpleCharacterJumpLoopState(state, options) {
7
8
  if (model == null) {
8
9
  throw new Error(`Unable to load animation without existing model`);
9
10
  }
10
- const jumpLoop = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions(options.animation?.jumpLoop ?? { url: { default: 'jumpLoop' } })));
11
+ const jumpLoop = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions({ url: JumpLoopAnimationUrl, ...options.animation?.jumpLoop })));
11
12
  return {
12
13
  timeline: () => action({
13
14
  init: () => {
@@ -1,5 +1,5 @@
1
1
  import { action, timePassed } from '@pmndrs/timeline';
2
- import { RunField } from '../../input/index.js';
2
+ import { RunAction } from '../../action/index.js';
3
3
  import { startAnimation } from '../../utils.js';
4
4
  import { DefaultCrossFadeDuration, DefaultJumDelay } from '../defaults.js';
5
5
  export async function loadSimpleCharacterJumpStartState(jumpUp, jumpForward, state, options) {
@@ -24,7 +24,7 @@ export async function loadSimpleCharacterJumpStartState(jumpUp, jumpForward, sta
24
24
  }),
25
25
  transitionTo: {
26
26
  jumpDown: { whenUpdate: () => !state.physics.isGrounded },
27
- finally: () => (state.inputSystem.get(RunField) ? 'jumpForward' : 'jumpUp'),
27
+ finally: () => (RunAction.get() ? 'jumpForward' : 'jumpUp'),
28
28
  },
29
29
  };
30
30
  }
@@ -1,5 +1,6 @@
1
1
  import { action, animationFinished } from '@pmndrs/timeline';
2
2
  import { LoopOnce, Vector3 } from 'three';
3
+ import { JumpUpAnimationUrl } from '../../animation/default.js';
3
4
  import { flattenCharacterAnimationOptions, loadCharacterAnimation } from '../../animation/index.js';
4
5
  import { startAnimation } from '../../utils.js';
5
6
  export async function loadSimpleCharacterJumpUpAction(state, options) {
@@ -7,8 +8,9 @@ export async function loadSimpleCharacterJumpUpAction(state, options) {
7
8
  if (model == null) {
8
9
  throw new Error(`Unable to load animation without existing model`);
9
10
  }
10
- const jumpUp = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions(options.animation?.jumpUp ?? {
11
- url: { default: 'jumpUp' },
11
+ const jumpUp = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions({
12
+ url: JumpUpAnimationUrl,
13
+ ...options.animation?.jumpUp,
12
14
  })));
13
15
  jumpUp.loop = LoopOnce;
14
16
  jumpUp.clampWhenFinished = true;
@@ -1,6 +1,7 @@
1
1
  import { action } from '@pmndrs/timeline';
2
+ import { RunAction } from '../../action/index.js';
3
+ import { IdleAnimationUrl, RunAnimationUrl, WalkAnimationUrl } from '../../animation/default.js';
2
4
  import { flattenCharacterAnimationOptions, loadCharacterAnimation } from '../../animation/index.js';
3
- import { RunField } from '../../input/index.js';
4
5
  import { shouldJump, startAnimation } from '../../utils.js';
5
6
  import { DefaultCrossFadeDuration } from '../defaults.js';
6
7
  export async function loadSimpleCharacterMovingState(state, options) {
@@ -8,16 +9,19 @@ export async function loadSimpleCharacterMovingState(state, options) {
8
9
  if (model == null) {
9
10
  throw new Error(`Unable to load animation without existing model`);
10
11
  }
11
- const idle = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions(options.animation?.idle ?? {
12
- url: { default: 'idle' },
12
+ const idle = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions({
13
+ url: IdleAnimationUrl,
14
+ ...options.animation?.idle,
13
15
  })));
14
16
  const run = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions({
15
- url: { default: 'run' },
17
+ url: RunAnimationUrl,
16
18
  scaleTime: 0.8,
19
+ ...options.animation?.run,
17
20
  })));
18
- const walk = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions(options.animation?.walk ?? {
21
+ const walk = model.mixer.clipAction(await loadCharacterAnimation(model, ...flattenCharacterAnimationOptions({
19
22
  scaleTime: 0.5,
20
- url: { default: 'walk' },
23
+ url: WalkAnimationUrl,
24
+ ...options.animation?.walk,
21
25
  })));
22
26
  return {
23
27
  timeline: () => {
@@ -28,7 +32,7 @@ export async function loadSimpleCharacterMovingState(state, options) {
28
32
  if (state.physics.inputVelocity.lengthSq() === 0) {
29
33
  nextAnimation = idle;
30
34
  }
31
- else if (state.inputSystem.get(RunField) && options.movement?.run != false) {
35
+ else if (RunAction.get() && options.movement?.run != false) {
32
36
  nextAnimation = run;
33
37
  }
34
38
  else if (options.movement?.walk != false) {
@@ -51,7 +55,7 @@ export async function loadSimpleCharacterMovingState(state, options) {
51
55
  transitionTo: {
52
56
  jumpStart: {
53
57
  whenUpdate: () => options.movement?.jump !== false &&
54
- shouldJump(state.physics, state.inputSystem, state.lastJump, options.movement?.jump === true ? undefined : options.movement?.jump?.bufferTime),
58
+ shouldJump(state.physics, state.lastJump, options.movement?.jump === true ? undefined : options.movement?.jump?.bufferTime),
55
59
  },
56
60
  jumpLoop: { whenUpdate: () => !state.physics.isGrounded },
57
61
  },
@@ -1,5 +1,4 @@
1
1
  import { Object3D } from 'three';
2
- import { InputSystem } from '../input/index.js';
3
2
  import type { SimpleCharacterMovementOptions } from './index.js';
4
3
  import type { BvhCharacterPhysics } from '../physics/index.js';
5
- export declare function updateSimpleCharacterInputVelocity(camera: Object3D, inputSystem: InputSystem, physics: BvhCharacterPhysics, options?: SimpleCharacterMovementOptions): void;
4
+ export declare function updateSimpleCharacterVelocity(camera: Object3D, physics: BvhCharacterPhysics, options?: SimpleCharacterMovementOptions): void;
@@ -1,14 +1,14 @@
1
1
  import { Euler, Quaternion } from 'three';
2
- import { RunField, MoveLeftField, MoveRightField, MoveForwardField, MoveBackwardField, } from '../input/index.js';
2
+ import { RunAction, MoveLeftAction, MoveRightAction, MoveForwardAction, MoveBackwardAction } from '../action/index.js';
3
3
  const cameraEuler = new Euler();
4
4
  const cameraRotation = new Quaternion();
5
- export function updateSimpleCharacterInputVelocity(camera, inputSystem, physics, options) {
5
+ export function updateSimpleCharacterVelocity(camera, physics, options) {
6
6
  cameraEuler.setFromQuaternion(camera.getWorldQuaternion(cameraRotation), 'YXZ');
7
7
  cameraEuler.x = 0;
8
8
  cameraEuler.z = 0;
9
9
  let inputSpeed = 0;
10
10
  let runOptions = options?.run ?? true;
11
- if (inputSystem.get(RunField) && runOptions !== false) {
11
+ if (RunAction.get() && runOptions !== false) {
12
12
  runOptions = runOptions === true ? {} : runOptions;
13
13
  inputSpeed = runOptions.speed ?? 6;
14
14
  }
@@ -18,7 +18,7 @@ export function updateSimpleCharacterInputVelocity(camera, inputSystem, physics,
18
18
  inputSpeed = walkOptions.speed ?? 3;
19
19
  }
20
20
  physics.inputVelocity
21
- .set(-inputSystem.get(MoveLeftField) + inputSystem.get(MoveRightField), 0, -inputSystem.get(MoveForwardField) + inputSystem.get(MoveBackwardField))
21
+ .set(-MoveLeftAction.get() + MoveRightAction.get(), 0, -MoveForwardAction.get() + MoveBackwardAction.get())
22
22
  .normalize()
23
23
  .applyEuler(cameraEuler)
24
24
  .multiplyScalar(inputSpeed);
@@ -7,7 +7,7 @@ const inputDirection = new Vector3();
7
7
  const quaternion = new Quaternion();
8
8
  export function updateSimpleCharacterRotation(delta, physics, camera, model, options) {
9
9
  // Character yaw rotation logic
10
- const basedOn = options?.yawRotationBasdOn ?? 'movement';
10
+ const basedOn = options?.yawRotationBasedOn ?? 'movement';
11
11
  // compute goalTargetEuler
12
12
  if (basedOn === 'camera') {
13
13
  goalTargetEuler.setFromQuaternion(camera.getWorldQuaternion(quaternion), 'YXZ');
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { type InputSystem } from './input/index.js';
2
- import type { CharacterAnimationMask } from './animation/index.js';
3
1
  import type { CharacterModel } from './model/index.js';
4
2
  import type { BvhCharacterPhysics } from './physics/index.js';
5
3
  import type { AnimationAction } from 'three';
@@ -7,8 +5,10 @@ export declare function getIsMobileMediaQuery(): MediaQueryList | undefined;
7
5
  export declare function isMobile(): boolean;
8
6
  export type StartAnimationOptions = {
9
7
  fadeDuration?: number;
8
+ sync?: boolean;
10
9
  paused?: boolean;
11
- mask?: CharacterAnimationMask;
10
+ crossFade?: boolean;
11
+ layer?: string;
12
12
  };
13
- export declare function startAnimation(animation: AnimationAction, currentAnimations: CharacterModel['currentAnimations'], { fadeDuration, paused, mask }: StartAnimationOptions): void;
14
- export declare function shouldJump(physics: BvhCharacterPhysics, inputSystem: InputSystem, lastJump: number, bufferTime?: number): boolean;
13
+ export declare function startAnimation(animation: AnimationAction, currentAnimations: CharacterModel['currentAnimations'], { crossFade, layer, fadeDuration, paused, sync }: StartAnimationOptions): (() => void) | undefined;
14
+ export declare function shouldJump(physics: BvhCharacterPhysics, lastJump: number, bufferTime?: number): boolean;
package/dist/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { LastTimeJumpPressedField } from './input/index.js';
1
+ import { JumpAction } from './action/index.js';
2
2
  export function getIsMobileMediaQuery() {
3
3
  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
4
4
  return undefined;
@@ -8,24 +8,33 @@ export function getIsMobileMediaQuery() {
8
8
  export function isMobile() {
9
9
  return getIsMobileMediaQuery()?.matches ?? false;
10
10
  }
11
- export function startAnimation(animation, currentAnimations, { fadeDuration = 0.1, paused = false, mask }) {
11
+ export function startAnimation(animation, currentAnimations, { crossFade = true, layer, fadeDuration = 0.1, paused = false, sync = false }) {
12
12
  animation.reset();
13
13
  animation.play();
14
14
  animation.paused = paused;
15
- const currentAnimation = currentAnimations.get(mask);
15
+ if (!crossFade) {
16
+ animation.fadeIn(fadeDuration);
17
+ return () => {
18
+ animation.fadeOut(fadeDuration);
19
+ };
20
+ }
21
+ const currentAnimation = currentAnimations.get(layer);
22
+ if (currentAnimation != null && sync) {
23
+ animation.syncWith(currentAnimation);
24
+ }
16
25
  if (currentAnimation != null) {
17
26
  animation.crossFadeFrom(currentAnimation, fadeDuration);
18
27
  }
19
28
  else {
20
29
  animation.fadeIn(fadeDuration);
21
30
  }
22
- currentAnimations.set(mask, animation);
31
+ currentAnimations.set(layer, animation);
23
32
  }
24
- export function shouldJump(physics, inputSystem, lastJump, bufferTime = 0.1) {
33
+ export function shouldJump(physics, lastJump, bufferTime = 0.1) {
25
34
  if (!physics.isGrounded) {
26
35
  return false;
27
36
  }
28
- const lastTimePressed = inputSystem.get(LastTimeJumpPressedField);
37
+ const lastTimePressed = JumpAction.getLatestTime();
29
38
  if (lastTimePressed == null || lastJump > lastTimePressed) {
30
39
  return false;
31
40
  }
package/package.json CHANGED
@@ -21,12 +21,12 @@
21
21
  "peerDependencies": {
22
22
  "three": "*"
23
23
  },
24
- "version": "0.2.0",
24
+ "version": "0.2.2",
25
25
  "type": "module",
26
26
  "dependencies": {
27
27
  "@pixiv/three-vrm": "^3.4.2",
28
28
  "@pixiv/three-vrm-animation": "^3.4.2",
29
- "@pmndrs/timeline": "^0.3.5",
29
+ "@pmndrs/timeline": "^0.3.7",
30
30
  "@viverse/sdk": "1.2.10-alpha.0",
31
31
  "three-mesh-bvh": "^0.9.1"
32
32
  },
@@ -1,4 +0,0 @@
1
- import { AnimationClip } from 'three';
2
- import type { CharacterModel } from '../model/index.js';
3
- import type { VRMHumanBoneName } from '@pixiv/three-vrm';
4
- export declare function loadVrmModelBvhAnimations(model: CharacterModel, url: string, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): Promise<Array<AnimationClip>>;
@@ -1,8 +0,0 @@
1
- import { BVHLoader } from 'three/examples/jsm/Addons.js';
2
- import { bvhBoneMap, fixModelAnimationClip } from './index.js';
3
- const loader = new BVHLoader();
4
- export async function loadVrmModelBvhAnimations(model, url, removeXZMovement, boneMap) {
5
- const clipScene = await loader.loadAsync(url);
6
- fixModelAnimationClip(model, clipScene.clip, undefined, removeXZMovement, boneMap ?? bvhBoneMap);
7
- return [clipScene.clip];
8
- }
@@ -1,4 +0,0 @@
1
- import { AnimationClip } from 'three';
2
- import type { CharacterModel } from '../model/index.js';
3
- import type { VRMHumanBoneName } from '@pixiv/three-vrm';
4
- export declare function loadVrmModelFbxAnimations(model: CharacterModel, url: string, removeXZMovement: boolean, boneMap?: Record<string, VRMHumanBoneName>): Promise<Array<AnimationClip>>;
@@ -1,8 +0,0 @@
1
- import { FBXLoader } from 'three/examples/jsm/Addons.js';
2
- import { fixModelAnimationClip } from './index.js';
3
- const loader = new FBXLoader();
4
- export async function loadVrmModelFbxAnimations(model, url, removeXZMovement, boneMap) {
5
- const clipScene = await loader.loadAsync(url);
6
- clipScene.animations.forEach((clip) => fixModelAnimationClip(model, clip, clipScene, removeXZMovement, boneMap));
7
- return clipScene.animations;
8
- }
@@ -1,3 +0,0 @@
1
- import { AnimationClip } from 'three';
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,8 +0,0 @@
1
- import { GLTFLoader } from 'three/examples/jsm/Addons.js';
2
- import { fixModelAnimationClip } from './index.js';
3
- const loader = new GLTFLoader();
4
- export async function loadVrmModelGltfAnimations(model, url, removeXZMovement, boneMap) {
5
- const { animations, scene: clipScene } = await loader.loadAsync(url);
6
- animations.forEach((clip) => fixModelAnimationClip(model, clip, clipScene, removeXZMovement, boneMap));
7
- return animations;
8
- }
@@ -1,3 +0,0 @@
1
- import { VRM } from '@pixiv/three-vrm';
2
- import { AnimationClip } from 'three';
3
- export declare function loadVrmModelVrmaAnimations(vrm: VRM, url: string, removeXZMovement: boolean): Promise<Array<AnimationClip>>;
@@ -1,8 +0,0 @@
1
- import { createVRMAnimationClip } from '@pixiv/three-vrm-animation';
2
- import { vrmaLoader } from '../index.js';
3
- export async function loadVrmModelVrmaAnimations(vrm, url, removeXZMovement) {
4
- const animations = await vrmaLoader.loadAsync(url);
5
- const vrmAnimations = animations.userData.vrmAnimations;
6
- const clips = vrmAnimations.map((vrmAnimation) => createVRMAnimationClip(vrmAnimation, vrm));
7
- return clips;
8
- }
@@ -1,31 +0,0 @@
1
- export declare class InputSystem<T extends {} = {}> {
2
- options: T;
3
- readonly inputs: Array<Input>;
4
- constructor(options?: T);
5
- add(input: Input): void;
6
- remove(input: Input): void;
7
- dispose(): void;
8
- get<T>(field: InputField<T>): T;
9
- }
10
- export type InputField<T> = {
11
- default: T;
12
- combine: (v1: any, v2: any) => T;
13
- };
14
- export declare const MoveForwardField: InputField<number>;
15
- export declare const MoveBackwardField: InputField<number>;
16
- export declare const MoveLeftField: InputField<number>;
17
- export declare const MoveRightField: InputField<number>;
18
- export declare const LastTimeJumpPressedField: InputField<number | null>;
19
- export declare const RunField: InputField<boolean>;
20
- export declare const DeltaZoomField: InputField<number>;
21
- export declare const DeltaYawField: InputField<number>;
22
- export declare const DeltaPitchField: InputField<number>;
23
- export interface Input<O = {}> {
24
- get<T>(field: InputField<T>, options: O): T | undefined;
25
- dispose?(): void;
26
- }
27
- export * from './pointer-lock.js';
28
- export * from './pointer-capture.js';
29
- export * from './keyboard.js';
30
- export * from './screen-joystick.js';
31
- export * from './screen-jump-button.js';
@@ -1,77 +0,0 @@
1
- export class InputSystem {
2
- options;
3
- inputs = [];
4
- constructor(options = {}) {
5
- this.options = options;
6
- }
7
- add(input) {
8
- this.inputs.push(input);
9
- }
10
- remove(input) {
11
- const index = this.inputs.indexOf(input);
12
- if (index === -1) {
13
- return;
14
- }
15
- this.inputs.splice(index, 1);
16
- }
17
- dispose() {
18
- this.inputs.forEach((input) => input.dispose?.());
19
- this.inputs.length = 0;
20
- }
21
- get(field) {
22
- let current;
23
- for (const input of this.inputs) {
24
- const result = input.get(field, this.options);
25
- if (result == null) {
26
- continue;
27
- }
28
- if (current == undefined) {
29
- current = result;
30
- continue;
31
- }
32
- current = field.combine(current, result);
33
- }
34
- return current ?? field.default;
35
- }
36
- }
37
- export const MoveForwardField = {
38
- default: 0,
39
- combine: Math.max,
40
- };
41
- export const MoveBackwardField = {
42
- default: 0,
43
- combine: Math.max,
44
- };
45
- export const MoveLeftField = {
46
- default: 0,
47
- combine: Math.max,
48
- };
49
- export const MoveRightField = {
50
- default: 0,
51
- combine: Math.max,
52
- };
53
- export const LastTimeJumpPressedField = {
54
- default: null,
55
- combine: (v1, v2) => (v1 == null && v2 == null ? null : Math.min(v1 ?? Infinity, v2 ?? Infinity)),
56
- };
57
- export const RunField = {
58
- default: false,
59
- combine: (v1, v2) => v1 || v2,
60
- };
61
- export const DeltaZoomField = {
62
- default: 0,
63
- combine: Math.max,
64
- };
65
- export const DeltaYawField = {
66
- default: 0,
67
- combine: Math.max,
68
- };
69
- export const DeltaPitchField = {
70
- default: 0,
71
- combine: Math.max,
72
- };
73
- export * from './pointer-lock.js';
74
- export * from './pointer-capture.js';
75
- export * from './keyboard.js';
76
- export * from './screen-joystick.js';
77
- export * from './screen-jump-button.js';
@@ -1,16 +0,0 @@
1
- import { Input, InputField } from './index.js';
2
- export type LocomotionKeyboardInputOptions = {
3
- keyboardMoveForwardKeys?: Array<string>;
4
- keyboardMoveBackwardKeys?: Array<string>;
5
- keyboardMoveLeftKeys?: Array<string>;
6
- keyboardMoveRightKeys?: Array<string>;
7
- keyboardRunKeys?: Array<string>;
8
- keyboardJumpKeys?: Array<string>;
9
- };
10
- export declare class LocomotionKeyboardInput implements Input<LocomotionKeyboardInputOptions> {
11
- private readonly abortController;
12
- private readonly keyState;
13
- constructor(domElement: HTMLElement);
14
- get<T>(field: InputField<T>, options: LocomotionKeyboardInputOptions): T | undefined;
15
- dispose(): void;
16
- }
@@ -1,82 +0,0 @@
1
- import { MoveForwardField, MoveBackwardField, MoveLeftField, MoveRightField, LastTimeJumpPressedField, RunField, } from './index.js';
2
- const DefaultMoveForwardKeys = ['KeyW'];
3
- const DefaultMoveBackwardKeys = ['KeyS'];
4
- const DefaultMoveLeftKeys = ['KeyA'];
5
- const DefaultMoveRightKeys = ['KeyD'];
6
- const DefaultRunKeys = ['ShiftRight', 'ShiftLeft'];
7
- const DefaultJumpKeys = ['Space'];
8
- export class LocomotionKeyboardInput {
9
- abortController = new AbortController();
10
- keyState = new Map();
11
- constructor(domElement) {
12
- domElement.tabIndex = 0;
13
- domElement.addEventListener('keydown', (event) => {
14
- let state = this.keyState.get(event.code);
15
- const now = performance.now() / 1000;
16
- if (state == null) {
17
- this.keyState.set(event.code, (state = { pressTime: now }));
18
- }
19
- else {
20
- state.pressTime = now;
21
- }
22
- }, {
23
- signal: this.abortController.signal,
24
- });
25
- domElement.addEventListener('keyup', (event) => {
26
- let state = this.keyState.get(event.code);
27
- const now = performance.now() / 1000;
28
- if (state == null) {
29
- this.keyState.set(event.code, (state = { releaseTime: now }));
30
- }
31
- else {
32
- state.releaseTime = now;
33
- }
34
- }, {
35
- signal: this.abortController.signal,
36
- });
37
- // Handle focus loss to clear pressed keys
38
- domElement.addEventListener('blur', () => this.keyState.clear(), {
39
- signal: this.abortController.signal,
40
- });
41
- }
42
- get(field, options) {
43
- if (field === LastTimeJumpPressedField) {
44
- const jumpKeys = options.keyboardJumpKeys ?? DefaultJumpKeys;
45
- const pressed = jumpKeys
46
- .map((key) => this.keyState.get(key)?.pressTime ?? null)
47
- .filter((t) => t != null);
48
- return (pressed.length > 0 ? Math.max(...pressed) : null);
49
- }
50
- let keys;
51
- switch (field) {
52
- case MoveForwardField:
53
- keys = options.keyboardMoveForwardKeys ?? DefaultMoveForwardKeys;
54
- break;
55
- case MoveBackwardField:
56
- keys = options.keyboardMoveBackwardKeys ?? DefaultMoveBackwardKeys;
57
- break;
58
- case MoveLeftField:
59
- keys = options.keyboardMoveLeftKeys ?? DefaultMoveLeftKeys;
60
- break;
61
- case MoveRightField:
62
- keys = options.keyboardMoveRightKeys ?? DefaultMoveRightKeys;
63
- break;
64
- case RunField:
65
- keys = options.keyboardRunKeys ?? DefaultRunKeys;
66
- break;
67
- }
68
- if (keys == null) {
69
- return undefined;
70
- }
71
- return keys.some((key) => {
72
- const state = this.keyState.get(key);
73
- if (state?.pressTime == null) {
74
- return false;
75
- }
76
- return state.releaseTime == null || state.pressTime > state.releaseTime;
77
- });
78
- }
79
- dispose() {
80
- this.abortController.abort();
81
- }
82
- }