@pmndrs/viverse 0.1.20 → 0.2.1

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 (67) hide show
  1. package/dist/animation/bone-map.d.ts +4 -0
  2. package/dist/animation/bone-map.js +11 -0
  3. package/dist/animation/default.d.ts +9 -0
  4. package/dist/animation/default.js +25 -0
  5. package/dist/animation/index.d.ts +12 -19
  6. package/dist/animation/index.js +76 -72
  7. package/dist/animation/mask.d.ts +6 -0
  8. package/dist/animation/mask.js +53 -0
  9. package/dist/camera.d.ts +7 -7
  10. package/dist/camera.js +33 -26
  11. package/dist/index.d.ts +3 -2
  12. package/dist/index.js +3 -2
  13. package/dist/input/action.d.ts +41 -0
  14. package/dist/input/action.js +97 -0
  15. package/dist/input/index.d.ts +12 -27
  16. package/dist/input/index.js +17 -69
  17. package/dist/input/keyboard.d.ts +29 -7
  18. package/dist/input/keyboard.js +84 -72
  19. package/dist/input/pointer-capture.d.ts +6 -11
  20. package/dist/input/pointer-capture.js +10 -30
  21. package/dist/input/pointer-lock.d.ts +6 -11
  22. package/dist/input/pointer-lock.js +8 -29
  23. package/dist/input/screen-joystick.d.ts +12 -11
  24. package/dist/input/screen-joystick.js +39 -49
  25. package/dist/input/screen-jump-button.d.ts +2 -4
  26. package/dist/input/screen-jump-button.js +7 -12
  27. package/dist/model/index.d.ts +9 -13
  28. package/dist/model/index.js +12 -34
  29. package/dist/physics/index.d.ts +2 -5
  30. package/dist/physics/index.js +7 -16
  31. package/dist/simple-character/apply-input-options.d.ts +2 -0
  32. package/dist/simple-character/apply-input-options.js +28 -0
  33. package/dist/simple-character/defaults.d.ts +2 -0
  34. package/dist/simple-character/defaults.js +2 -0
  35. package/dist/simple-character/index.d.ts +116 -0
  36. package/dist/simple-character/index.js +104 -0
  37. package/dist/simple-character/state/index.d.ts +6 -0
  38. package/dist/simple-character/state/index.js +6 -0
  39. package/dist/simple-character/state/jump-down.d.ts +3 -0
  40. package/dist/simple-character/state/jump-down.js +26 -0
  41. package/dist/simple-character/state/jump-forward.d.ts +5 -0
  42. package/dist/simple-character/state/jump-forward.js +41 -0
  43. package/dist/simple-character/state/jump-loop.d.ts +3 -0
  44. package/dist/simple-character/state/jump-loop.js +24 -0
  45. package/dist/simple-character/state/jump-start.d.ts +4 -0
  46. package/dist/simple-character/state/jump-start.js +30 -0
  47. package/dist/simple-character/state/jump-up.d.ts +5 -0
  48. package/dist/simple-character/state/jump-up.js +40 -0
  49. package/dist/simple-character/state/movement.d.ts +3 -0
  50. package/dist/simple-character/state/movement.js +63 -0
  51. package/dist/simple-character/update-input-velocity.d.ts +4 -0
  52. package/dist/simple-character/update-input-velocity.js +25 -0
  53. package/dist/simple-character/update-rotation.d.ts +6 -0
  54. package/dist/simple-character/update-rotation.js +40 -0
  55. package/dist/utils.d.ts +12 -5
  56. package/dist/utils.js +37 -39
  57. package/package.json +2 -2
  58. package/dist/animation/bvh.d.ts +0 -4
  59. package/dist/animation/bvh.js +0 -8
  60. package/dist/animation/fbx.d.ts +0 -4
  61. package/dist/animation/fbx.js +0 -8
  62. package/dist/animation/gltf.d.ts +0 -3
  63. package/dist/animation/gltf.js +0 -8
  64. package/dist/animation/vrma.d.ts +0 -3
  65. package/dist/animation/vrma.js +0 -8
  66. package/dist/simple-character.d.ts +0 -107
  67. package/dist/simple-character.js +0 -344
@@ -1,107 +0,0 @@
1
- import { VRM } from '@pixiv/three-vrm';
2
- import { AnimationAction, AnimationClip, AnimationMixer, Group, Object3D, Object3DEventMap, Quaternion } from 'three';
3
- import { simpleCharacterAnimationNames, ModelAnimationOptions } from './animation/index.js';
4
- import { SimpleCharacterCameraBehavior, SimpleCharacterCameraBehaviorOptions } from './camera.js';
5
- import { Input, InputSystem, ScreenJoystickInputOptions, LocomotionKeyboardInputOptions, PointerCaptureInputOptions, PointerLockInputOptions } from './input/index.js';
6
- import { CharacterModelOptions, loadCharacterModel } from './model/index.js';
7
- import { BvhCharacterPhysicsOptions, BvhCharacterPhysics, BvhPhysicsWorld } from './physics/index.js';
8
- export type SimpleCharacterMovementOptions = {
9
- /**
10
- * @default true
11
- */
12
- jump?: {
13
- /**
14
- * @default 0.2
15
- */
16
- delay?: number;
17
- /**
18
- * @default 0.1
19
- */
20
- bufferTime?: number;
21
- /**
22
- * @default 8
23
- */
24
- speed?: number;
25
- } | boolean;
26
- /**
27
- * @default true
28
- */
29
- walk?: {
30
- speed?: number;
31
- } | boolean;
32
- /**
33
- * @default true
34
- */
35
- run?: {
36
- speed?: number;
37
- } | boolean;
38
- };
39
- export type SimpleCharacterAnimationOptions = {
40
- readonly walk?: ModelAnimationOptions;
41
- readonly run?: ModelAnimationOptions;
42
- readonly idle?: ModelAnimationOptions;
43
- readonly jumpUp?: ModelAnimationOptions;
44
- readonly jumpLoop?: ModelAnimationOptions;
45
- readonly jumpDown?: ModelAnimationOptions;
46
- readonly jumpForward?: ModelAnimationOptions;
47
- /**
48
- * @default "movement"
49
- */
50
- yawRotationBasdOn?: 'camera' | 'movement';
51
- /**
52
- * @default 10
53
- */
54
- maxYawRotationSpeed?: number;
55
- /**
56
- * @default 0.3
57
- */
58
- crossFadeDuration?: number;
59
- };
60
- export type SimpleCharacterInputOptions = ScreenJoystickInputOptions & PointerCaptureInputOptions & PointerLockInputOptions & LocomotionKeyboardInputOptions;
61
- export type SimpleCharacterOptions = {
62
- readonly input?: ReadonlyArray<Input | {
63
- new (domElement: HTMLElement): Input;
64
- }> | InputSystem;
65
- inputOptions?: SimpleCharacterInputOptions;
66
- movement?: SimpleCharacterMovementOptions;
67
- readonly model?: CharacterModelOptions;
68
- physics?: BvhCharacterPhysicsOptions;
69
- cameraBehavior?: SimpleCharacterCameraBehaviorOptions;
70
- readonly animation?: SimpleCharacterAnimationOptions;
71
- };
72
- export declare function preloadSimpleCharacterAssets(options: Pick<SimpleCharacterOptions, 'animation' | 'model'>): Promise<{
73
- model?: undefined;
74
- animations?: undefined;
75
- } | {
76
- model: ((VRM & {
77
- scene: Object3D<Object3DEventMap & {
78
- dispose: {};
79
- }>;
80
- }) | (import("three/examples/jsm/Addons.js").GLTF & {
81
- scene: Object3D<Object3DEventMap & {
82
- dispose: {};
83
- }>;
84
- })) & {
85
- boneRotationOffset?: Quaternion;
86
- };
87
- animations: Record<"walk" | "run" | "jumpForward" | "idle" | "jumpUp" | "jumpLoop" | "jumpDown", AnimationClip>;
88
- }>;
89
- export declare class SimpleCharacter extends Group<Object3DEventMap & {
90
- loaded: {};
91
- }> {
92
- private readonly camera;
93
- readonly options: SimpleCharacterOptions;
94
- readonly cameraBehavior: SimpleCharacterCameraBehavior;
95
- readonly physics: BvhCharacterPhysics;
96
- readonly mixer: AnimationMixer;
97
- inputSystem: InputSystem;
98
- actions?: Record<(typeof simpleCharacterAnimationNames)[number], AnimationAction> | undefined;
99
- model?: Awaited<Exclude<ReturnType<typeof loadCharacterModel>, undefined>>;
100
- private updateTimeline?;
101
- private readonly abortController;
102
- constructor(camera: Object3D | (() => Object3D), world: BvhPhysicsWorld, domElement: HTMLElement, options?: SimpleCharacterOptions);
103
- getCamera(): Object3D<Object3DEventMap>;
104
- private init;
105
- update(delta: number): void;
106
- dispose(): void;
107
- }
@@ -1,344 +0,0 @@
1
- import { VRM, VRMUtils } from '@pixiv/three-vrm';
2
- import { action, animationFinished, start, timePassed, forever, parallel, graph, } from '@pmndrs/timeline';
3
- import { AnimationMixer, Euler, Group, LoopOnce, Quaternion, Vector3, } from 'three';
4
- import { simpleCharacterAnimationNames, getSimpleCharacterModelAnimationOptions as getSimpleCharacterModelAnimationOptions, loadCharacterModelAnimation as loadCharacterModelAnimation, } from './animation/index.js';
5
- import { SimpleCharacterCameraBehavior } from './camera.js';
6
- import { InputSystem, LocomotionKeyboardInput, MoveBackwardField, MoveForwardField, MoveLeftField, MoveRightField, PointerCaptureInput, RunField, LastTimeJumpPressedField, ScreenJoystickInput, ScreenJumpButtonInput, } from './input/index.js';
7
- import { clearCharacterModelCache, loadCharacterModel } from './model/index.js';
8
- import { BvhCharacterPhysics } from './physics/index.js';
9
- import { extractProxy } from './utils.js';
10
- const DefaultCrossFadeDuration = 0.1;
11
- const DefaultJumDelay = 0.2;
12
- //constants
13
- const NegZAxis = new Vector3(0, 0, -1);
14
- const _2MathPI = 2 * Math.PI;
15
- //helper objects
16
- const cameraEuler = new Euler();
17
- const cameraRotation = new Quaternion();
18
- const vector = new Vector3();
19
- const characterTargetEuler = new Euler();
20
- const goalTargetEuler = new Euler();
21
- const inputDirection = new Vector3();
22
- const quaternion = new Quaternion();
23
- export async function preloadSimpleCharacterAssets(options) {
24
- // load model
25
- const model = await loadCharacterModel(options.model);
26
- if (model == null) {
27
- return {};
28
- }
29
- model.scene.addEventListener('dispose', () => clearCharacterModelCache(options.model));
30
- // load animations
31
- return {
32
- model,
33
- animations: (await Promise.all(simpleCharacterAnimationNames.map(async (name) => loadCharacterModelAnimation(model, options.animation?.[name] ?? (await getSimpleCharacterModelAnimationOptions(name)))))).reduce((prev, animation, i) => {
34
- prev[simpleCharacterAnimationNames[i]] = animation;
35
- return prev;
36
- }, {}),
37
- };
38
- }
39
- async function* SimpleCharacterTimeline(character) {
40
- let lastJump = 0;
41
- function shouldJump() {
42
- let jumpOptions = character.options.movement?.jump;
43
- if (jumpOptions === false) {
44
- return false;
45
- }
46
- if (jumpOptions === true) {
47
- jumpOptions = {};
48
- }
49
- if (!character.physics.isGrounded) {
50
- return false;
51
- }
52
- const lastTimePressed = character.inputSystem.get(LastTimeJumpPressedField);
53
- if (lastTimePressed == null) {
54
- return false;
55
- }
56
- if (lastJump > lastTimePressed) {
57
- return false;
58
- }
59
- //last jump must be more then 0.3 second ago, if not, we dont jump, this is to give the character time to get off the ground
60
- if (lastJump > performance.now() / 1000 - 0.3) {
61
- return false;
62
- }
63
- return performance.now() / 1000 - lastTimePressed < (jumpOptions?.bufferTime ?? 0.1);
64
- }
65
- function applyJumpForce() {
66
- lastJump = performance.now() / 1000;
67
- character.physics.applyVelocity(vector.set(0, (typeof character.options.movement?.jump === 'object' ? character.options.movement?.jump.speed : undefined) ??
68
- 8, 0));
69
- }
70
- const model = character.model;
71
- const actions = character.actions;
72
- //run character
73
- yield* parallel('all',
74
- // character movement
75
- action({
76
- update() {
77
- cameraEuler.setFromQuaternion(character.getCamera().getWorldQuaternion(cameraRotation), 'YXZ');
78
- cameraEuler.x = 0;
79
- cameraEuler.z = 0;
80
- let inputSpeed = 0;
81
- let runOptions = character.options.movement?.run ?? true;
82
- if (character.inputSystem.get(RunField) && runOptions !== false) {
83
- runOptions = runOptions === true ? {} : runOptions;
84
- inputSpeed = runOptions.speed ?? 6;
85
- }
86
- let walkOptions = character.options.movement?.walk ?? true;
87
- if (inputSpeed === 0 && walkOptions !== false) {
88
- walkOptions = walkOptions === true ? {} : walkOptions;
89
- inputSpeed = walkOptions.speed ?? 3;
90
- }
91
- character.physics.inputVelocity
92
- .set(-character.inputSystem.get(MoveLeftField) + character.inputSystem.get(MoveRightField), 0, -character.inputSystem.get(MoveForwardField) + character.inputSystem.get(MoveBackwardField))
93
- .normalize()
94
- .applyEuler(cameraEuler)
95
- .multiplyScalar(inputSpeed);
96
- //run forever
97
- return true;
98
- },
99
- }),
100
- // rotation animations
101
- model != null &&
102
- action({
103
- update(_, clock) {
104
- // Character yaw rotation logic
105
- const basedOn = character.options.animation?.yawRotationBasdOn ?? 'movement';
106
- // compute goalTargetEuler
107
- if (basedOn === 'camera') {
108
- goalTargetEuler.setFromQuaternion(character.getCamera().getWorldQuaternion(quaternion), 'YXZ');
109
- }
110
- else {
111
- //don't rotate if not moving
112
- if (character.physics.inputVelocity.lengthSq() === 0) {
113
- // run forever
114
- return true;
115
- }
116
- inputDirection.copy(character.physics.inputVelocity).normalize();
117
- quaternion.setFromUnitVectors(NegZAxis, inputDirection);
118
- goalTargetEuler.setFromQuaternion(quaternion, 'YXZ');
119
- }
120
- // compute currentTargetEuler
121
- model.scene.getWorldQuaternion(quaternion);
122
- characterTargetEuler.setFromQuaternion(quaternion, 'YXZ');
123
- // apply delta yaw rotation
124
- let deltaYaw = (goalTargetEuler.y - characterTargetEuler.y + _2MathPI) % _2MathPI;
125
- if (deltaYaw > Math.PI) {
126
- deltaYaw = deltaYaw - _2MathPI;
127
- }
128
- const absDeltaYaw = Math.abs(deltaYaw);
129
- if (absDeltaYaw < 0.001) {
130
- // run forever
131
- return true;
132
- }
133
- const yawRotationDirection = deltaYaw / absDeltaYaw;
134
- const maxYawRotationSpeed = (typeof character.options.animation === 'object'
135
- ? character.options.animation.maxYawRotationSpeed
136
- : undefined) ?? 10;
137
- model.scene.rotation.y += Math.min(maxYawRotationSpeed * clock.delta, absDeltaYaw) * yawRotationDirection;
138
- // run forever
139
- return true;
140
- },
141
- }),
142
- // jump and walk animations
143
- actions == null
144
- ? action({
145
- update: () => void (shouldJump() && applyJumpForce()),
146
- })
147
- : graph('moving', {
148
- jumpStart: {
149
- timeline: async function* () {
150
- yield* action({
151
- init() {
152
- actions.jumpUp.reset();
153
- actions.jumpUp.play();
154
- actions.jumpUp.paused = true;
155
- actions.jumpUp.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
156
- actions.jumpForward.reset();
157
- actions.jumpForward.play();
158
- actions.jumpForward.paused = true;
159
- actions.jumpForward.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
160
- },
161
- update: () => void character.physics.inputVelocity.multiplyScalar(0.3),
162
- until: timePassed((typeof character.options.movement?.jump === 'object'
163
- ? character.options.movement?.jump.delay
164
- : undefined) ?? DefaultJumDelay, 'seconds'),
165
- });
166
- if (character.inputSystem.get(RunField)) {
167
- actions.jumpUp.fadeOut(0.1);
168
- return 'jumpForward';
169
- }
170
- else {
171
- actions.jumpForward.fadeOut(0.1);
172
- return 'jumpUp';
173
- }
174
- },
175
- transitionTo: {
176
- jumpDown: { when: () => !character.physics.isGrounded },
177
- },
178
- },
179
- jumpForward: {
180
- timeline: async function* () {
181
- yield* action({
182
- init: () => {
183
- actions.jumpForward.paused = false;
184
- applyJumpForce();
185
- return () => actions.jumpForward.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
186
- },
187
- until: animationFinished(actions.jumpForward),
188
- });
189
- if (character.physics.isGrounded) {
190
- return 'moving';
191
- }
192
- return 'jumpLoop';
193
- },
194
- },
195
- jumpUp: {
196
- timeline: () => action({
197
- init: () => {
198
- actions.jumpUp.paused = false;
199
- applyJumpForce();
200
- return () => void actions.jumpUp.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
201
- },
202
- until: animationFinished(actions.jumpUp),
203
- }),
204
- transitionTo: {
205
- jumpDown: {
206
- when: (_, _clock, actionTime) => actionTime > 0.3 && character.physics.isGrounded,
207
- },
208
- finally: 'jumpLoop',
209
- },
210
- },
211
- jumpLoop: {
212
- timeline: () => action({
213
- init: () => {
214
- actions.jumpLoop.reset();
215
- actions.jumpLoop.play();
216
- actions.jumpLoop.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
217
- return () => actions.jumpLoop.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
218
- },
219
- until: forever(),
220
- }),
221
- transitionTo: {
222
- jumpDown: { when: () => character.physics.isGrounded },
223
- },
224
- },
225
- jumpDown: {
226
- timeline: () => action({
227
- init: () => {
228
- actions.jumpUp.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
229
- actions.jumpForward.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
230
- actions.jumpDown.reset();
231
- actions.jumpDown.play();
232
- actions.jumpDown.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
233
- return () => actions.jumpDown.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
234
- },
235
- until: timePassed(150, 'milliseconds'),
236
- }),
237
- transitionTo: { finally: 'moving' },
238
- },
239
- moving: {
240
- timeline: () => {
241
- let currentAnimation;
242
- return action({
243
- update() {
244
- let nextAnimation;
245
- if (character.physics.inputVelocity.lengthSq() === 0) {
246
- nextAnimation = actions.idle;
247
- }
248
- else if (character.inputSystem.get(RunField) && character.options.movement?.run != false) {
249
- nextAnimation = actions.run;
250
- }
251
- else if (character.options.movement?.walk != false) {
252
- nextAnimation = actions.walk;
253
- }
254
- else {
255
- nextAnimation = actions.idle;
256
- }
257
- if (nextAnimation === currentAnimation) {
258
- return;
259
- }
260
- currentAnimation?.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
261
- nextAnimation.reset();
262
- nextAnimation.play();
263
- nextAnimation.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
264
- currentAnimation = nextAnimation;
265
- },
266
- init: () => () => currentAnimation?.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration),
267
- });
268
- },
269
- transitionTo: {
270
- jumpStart: { when: () => shouldJump() },
271
- jumpLoop: { when: () => !character.physics.isGrounded },
272
- },
273
- },
274
- }));
275
- }
276
- export class SimpleCharacter extends Group {
277
- camera;
278
- options;
279
- cameraBehavior;
280
- physics;
281
- mixer = new AnimationMixer(this);
282
- //can be changed from the outside
283
- inputSystem;
284
- //loaded asychronously
285
- actions;
286
- model;
287
- updateTimeline;
288
- abortController = new AbortController();
289
- constructor(camera, world, domElement, options = {}) {
290
- super();
291
- this.camera = camera;
292
- this.options = options;
293
- // input system
294
- this.inputSystem =
295
- options.input instanceof InputSystem
296
- ? options.input
297
- : new InputSystem(domElement, options.input ?? [ScreenJoystickInput, ScreenJumpButtonInput, PointerCaptureInput, LocomotionKeyboardInput], extractProxy(options, 'inputOptions'));
298
- options.physics ??= {};
299
- // camera behavior
300
- this.cameraBehavior = new SimpleCharacterCameraBehavior(typeof camera === 'function' ? camera : () => camera, this, world.raycast.bind(world));
301
- // physics
302
- this.physics = new BvhCharacterPhysics(this, world);
303
- this.init(options).catch(console.error);
304
- }
305
- getCamera() {
306
- return typeof this.camera === 'function' ? this.camera() : this.camera;
307
- }
308
- async init(options) {
309
- const { model, animations } = await preloadSimpleCharacterAssets(options);
310
- this.model = model;
311
- if (model != null && animations != null) {
312
- this.add(model.scene);
313
- this.actions = {};
314
- for (const name of simpleCharacterAnimationNames) {
315
- this.actions[name] = this.mixer.clipAction(animations[name]);
316
- }
317
- this.actions.jumpDown.loop = LoopOnce;
318
- this.actions.jumpDown.clampWhenFinished = true;
319
- this.actions.jumpUp.loop = LoopOnce;
320
- this.actions.jumpUp.clampWhenFinished = true;
321
- this.actions.jumpForward.loop = LoopOnce;
322
- this.actions.jumpForward.clampWhenFinished = true;
323
- }
324
- this.updateTimeline = start(SimpleCharacterTimeline(this), this.abortController.signal);
325
- this.dispatchEvent({ type: 'loaded' });
326
- }
327
- update(delta) {
328
- this.updateTimeline?.(undefined, delta);
329
- this.mixer?.update(delta);
330
- if (this.model instanceof VRM) {
331
- this.model.update(delta);
332
- }
333
- this.physics.update(delta, this.options.physics);
334
- this.cameraBehavior.update(delta, this.options.cameraBehavior);
335
- }
336
- dispose() {
337
- this.abortController.abort();
338
- this.parent?.remove(this);
339
- this.model?.scene.dispatchEvent({ type: 'dispose' });
340
- this.inputSystem.dispose();
341
- this.physics.dispose();
342
- VRMUtils.deepDispose(this);
343
- }
344
- }