@pmndrs/viverse 0.1.6 → 0.1.9

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';
@@ -152,7 +152,7 @@ async function* SimpleCharacterTimeline(camera, character) {
152
152
  actions.jumpForward.paused = true;
153
153
  actions.jumpForward.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
154
154
  },
155
- update: () => void character.physics.inputVelocity.multiplyScalar(DefaultCrossFadeDuration),
155
+ update: () => void character.physics.inputVelocity.multiplyScalar(0.3),
156
156
  until: timePassed((typeof character.options.movement?.jump === 'object'
157
157
  ? character.options.movement?.jump.delay
158
158
  : undefined) ?? DefaultJumDelay, 'seconds'),
@@ -197,7 +197,7 @@ async function* SimpleCharacterTimeline(camera, character) {
197
197
  }),
198
198
  transitionTo: {
199
199
  jumpDown: {
200
- when: (_, clock) => clock.actionTime > 0.1 && character.physics.isGrounded,
200
+ when: (_, clock) => clock.actionTime > 0.3 && character.physics.isGrounded,
201
201
  },
202
202
  finally: 'jumpLoop',
203
203
  },
@@ -226,7 +226,7 @@ async function* SimpleCharacterTimeline(camera, character) {
226
226
  actions.jumpDown.fadeIn(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration);
227
227
  },
228
228
  cleanup: () => actions.jumpDown.fadeOut(character.options.animation?.crossFadeDuration ?? DefaultCrossFadeDuration),
229
- until: timePassed(50, 'milliseconds'),
229
+ until: timePassed(150, 'milliseconds'),
230
230
  }),
231
231
  transitionTo: { finally: 'moving' },
232
232
  },
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.6",
24
+ "version": "0.1.9",
25
25
  "type": "module",
26
26
  "dependencies": {
27
27
  "@pixiv/three-vrm": "^3.4.2",