@pmndrs/viverse 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <h1 align="center">@pmndrs/viverse</h1>
2
- <h3 align="center">Toolkit for building Three.js Apps for Viverse and beyond.</h3>
2
+ <h3 align="center">Toolkit for building Three.js Apps for VIVERSE and beyond.</h3>
3
3
  <br/>
4
4
 
5
5
  <p align="center">
@@ -24,9 +24,9 @@ export type BvhCharacterPhysicsOptions = {
24
24
  */
25
25
  linearDamping?: number;
26
26
  /**
27
- * @default 0.25;
27
+ * @default 0.5;
28
28
  */
29
- slopeGroundingThreshold?: number;
29
+ maxGroundSlope?: number;
30
30
  } | boolean;
31
31
  /**
32
32
  * assumes the target object origin is at its bottom
@@ -37,7 +37,8 @@ export declare class BvhCharacterPhysics {
37
37
  private destroyed;
38
38
  private readonly stateVelocity;
39
39
  readonly inputVelocity: Vector3;
40
- isGrounded: boolean;
40
+ private notGroundedSeconds;
41
+ get isGrounded(): boolean;
41
42
  constructor(character: Object3D, world: BvhPhysicsWorld);
42
43
  applyVelocity(velocity: Vector3): void;
43
44
  /**
@@ -45,6 +46,6 @@ export declare class BvhCharacterPhysics {
45
46
  */
46
47
  update(fullDelta: number, options?: BvhCharacterPhysicsOptions): void;
47
48
  destroy(): void;
48
- shapecastCapsule(position: Vector3, options: Exclude<BvhCharacterPhysicsOptions, boolean>): void;
49
+ shapecastCapsule(position: Vector3, maxGroundSlope: number, options: Exclude<BvhCharacterPhysicsOptions, boolean>): boolean;
49
50
  }
50
51
  export * from './world.js';
@@ -7,8 +7,8 @@ const triPoint = new Vector3();
7
7
  const capsulePoint = new Vector3();
8
8
  const collisionFreePosition = new Vector3();
9
9
  const position = new Vector3();
10
- const collisionDelta = new Vector3();
11
10
  const invertedParentMatrix = new Matrix4();
11
+ const YAxis = new Vector3(0, 1, 0);
12
12
  /**
13
13
  * assumes the target object origin is at its bottom
14
14
  */
@@ -18,7 +18,10 @@ export class BvhCharacterPhysics {
18
18
  destroyed = false;
19
19
  stateVelocity = new Vector3();
20
20
  inputVelocity = new Vector3();
21
- isGrounded = false;
21
+ notGroundedSeconds = 0;
22
+ get isGrounded() {
23
+ return this.notGroundedSeconds < 0.2;
24
+ }
22
25
  constructor(character, world) {
23
26
  this.character = character;
24
27
  this.world = world;
@@ -57,13 +60,17 @@ export class BvhCharacterPhysics {
57
60
  invertedParentMatrix.identity();
58
61
  }
59
62
  //compute new position based on the state velocity, the input velocity, and the delta
60
- const yMovement = this.stateVelocity.y * partialDelta;
61
- position.addScaledVector(this.stateVelocity, partialDelta);
62
63
  position.addScaledVector(this.inputVelocity, partialDelta);
63
- //compute collision and write the corrected position to the target
64
- //TODO: rework - when are we on the ground and how to correct the shapecast
65
- this.shapecastCapsule(collisionFreePosition.copy(position), options);
66
- this.character.position.copy(collisionFreePosition).applyMatrix4(invertedParentMatrix);
64
+ position.addScaledVector(this.stateVelocity, partialDelta);
65
+ const isGrounded = this.shapecastCapsule(collisionFreePosition.copy(position), options.maxGroundSlope ?? 1, options) &&
66
+ this.stateVelocity.y < 0;
67
+ this.notGroundedSeconds += partialDelta;
68
+ if (isGrounded) {
69
+ this.notGroundedSeconds = 0;
70
+ }
71
+ if (!isGrounded || this.inputVelocity.lengthSq() > 0) {
72
+ this.character.position.copy(collisionFreePosition).applyMatrix4(invertedParentMatrix);
73
+ }
67
74
  //compute new velocity
68
75
  // apply gravity
69
76
  this.stateVelocity.y += (options.gravity ?? -20) * partialDelta;
@@ -71,21 +78,15 @@ export class BvhCharacterPhysics {
71
78
  const dampingFactor = 1.0 / (1.0 + partialDelta * (options.linearDamping ?? 0.1));
72
79
  this.stateVelocity.multiplyScalar(dampingFactor);
73
80
  // apply collision to velocity
74
- collisionDelta.copy(collisionFreePosition).sub(position);
75
- this.isGrounded = collisionDelta.y >= Math.abs(yMovement * (options.slopeGroundingThreshold ?? 0.6));
76
- if (this.isGrounded) {
77
- this.stateVelocity.set(0, (options.gravity ?? -20) * partialDelta, 0);
78
- }
79
- else if (collisionDelta.length() > 1e-5) {
80
- collisionDelta.normalize();
81
- this.stateVelocity.addScaledVector(collisionDelta, -collisionDelta.dot(this.stateVelocity));
81
+ if (isGrounded) {
82
+ this.stateVelocity.set(0, (options.gravity ?? -20) * 0.01, 0);
82
83
  }
83
84
  }
84
85
  }
85
86
  destroy() {
86
87
  this.destroyed = true;
87
88
  }
88
- shapecastCapsule(position, options) {
89
+ shapecastCapsule(position, maxGroundSlope, options) {
89
90
  const radius = options.capsuleRadius ?? 0.4;
90
91
  const height = options.capsuleHeight ?? 1.7;
91
92
  segment.start.copy(position);
@@ -97,6 +98,7 @@ export class BvhCharacterPhysics {
97
98
  aabbox.expandByPoint(segment.end);
98
99
  aabbox.min.addScalar(-radius);
99
100
  aabbox.max.addScalar(radius);
101
+ let grounded = false;
100
102
  for (const bvh of this.world.getBodies()) {
101
103
  bvh.shapecast({
102
104
  intersectsBounds: (bounds) => bounds.intersectsBox(aabbox),
@@ -104,9 +106,11 @@ export class BvhCharacterPhysics {
104
106
  // Use your existing triangle vs segment closestPointToSegment
105
107
  const distance = tri.closestPointToSegment(segment, triPoint, capsulePoint);
106
108
  if (distance === 0) {
107
- const scaledDirection = capsulePoint.sub(capsulePoint.distanceTo(segment.start) < capsulePoint.distanceTo(segment.end)
108
- ? segment.start
109
- : segment.end);
109
+ const isCloserToSegmentStart = capsulePoint.distanceTo(segment.start) < capsulePoint.distanceTo(segment.end);
110
+ if (isCloserToSegmentStart) {
111
+ grounded = true;
112
+ }
113
+ const scaledDirection = capsulePoint.sub(isCloserToSegmentStart ? segment.start : segment.end);
110
114
  scaledDirection.y += radius;
111
115
  segment.start.add(scaledDirection);
112
116
  segment.end.add(scaledDirection);
@@ -114,6 +118,10 @@ export class BvhCharacterPhysics {
114
118
  else if (distance < radius) {
115
119
  const depthInsideCapsule = radius - distance;
116
120
  const direction = capsulePoint.sub(triPoint).divideScalar(distance);
121
+ const slope = Math.tan(Math.acos(direction.dot(YAxis)));
122
+ if (direction.y > 0 && slope <= maxGroundSlope) {
123
+ grounded = true;
124
+ }
117
125
  segment.start.addScaledVector(direction, depthInsideCapsule);
118
126
  segment.end.addScaledVector(direction, depthInsideCapsule);
119
127
  }
@@ -122,6 +130,7 @@ export class BvhCharacterPhysics {
122
130
  }
123
131
  position.copy(segment.start);
124
132
  position.y -= radius;
133
+ return grounded;
125
134
  }
126
135
  }
127
136
  export * from './world.js';
@@ -58,6 +58,7 @@ async function* SimpleCharacterTimeline(camera, character) {
58
58
  return performance.now() / 1000 - lastTimePressed < (jumpOptions?.bufferTime ?? 0.1);
59
59
  }
60
60
  function applyJumpForce() {
61
+ lastJump = performance.now() / 1000;
61
62
  character.physics.applyVelocity(vector.set(0, (typeof character.options.movement?.jump === 'object' ? character.options.movement?.jump.speed : undefined) ??
62
63
  8, 0));
63
64
  }
@@ -152,7 +153,7 @@ async function* SimpleCharacterTimeline(camera, character) {
152
153
  actions.jumpForward.paused = true;
153
154
  actions.jumpForward.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
154
155
  },
155
- update: () => void character.physics.inputVelocity.multiplyScalar(DefaultCrossFadeDuration),
156
+ update: () => void character.physics.inputVelocity.multiplyScalar(0.3),
156
157
  until: timePassed((typeof character.options.movement?.jump === 'object'
157
158
  ? character.options.movement?.jump.delay
158
159
  : undefined) ?? DefaultJumDelay, 'seconds'),
@@ -197,7 +198,7 @@ async function* SimpleCharacterTimeline(camera, character) {
197
198
  }),
198
199
  transitionTo: {
199
200
  jumpDown: {
200
- when: (_, clock) => clock.actionTime > 0.1 && character.physics.isGrounded,
201
+ when: (_, clock) => clock.actionTime > 0.3 && character.physics.isGrounded,
201
202
  },
202
203
  finally: 'jumpLoop',
203
204
  },
@@ -226,7 +227,7 @@ async function* SimpleCharacterTimeline(camera, character) {
226
227
  actions.jumpDown.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
227
228
  },
228
229
  cleanup: () => actions.jumpDown.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration),
229
- until: timePassed(50, 'milliseconds'),
230
+ until: timePassed(150, 'milliseconds'),
230
231
  }),
231
232
  transitionTo: { finally: 'moving' },
232
233
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmndrs/viverse",
3
- "description": "Toolkit for building Three.js Apps for Viverse and beyond.",
3
+ "description": "Toolkit for building Three.js Apps for VIVERSE and beyond.",
4
4
  "author": "Bela Bohlender",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://github.com/pmndrs/viverse",
@@ -21,7 +21,7 @@
21
21
  "peerDependencies": {
22
22
  "three": "*"
23
23
  },
24
- "version": "0.1.8",
24
+ "version": "0.1.10",
25
25
  "type": "module",
26
26
  "dependencies": {
27
27
  "@pixiv/three-vrm": "^3.4.2",