@pmndrs/viverse 0.1.14 → 0.1.15
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/dist/physics/index.d.ts +4 -0
- package/dist/physics/index.js +33 -27
- package/dist/physics/world.d.ts +7 -1
- package/dist/physics/world.js +65 -36
- package/package.json +1 -1
package/dist/physics/index.d.ts
CHANGED
|
@@ -38,6 +38,9 @@ export declare class BvhCharacterPhysics {
|
|
|
38
38
|
private readonly stateVelocity;
|
|
39
39
|
readonly inputVelocity: Vector3;
|
|
40
40
|
private notGroundedSeconds;
|
|
41
|
+
private readonly segment;
|
|
42
|
+
private readonly aabbox;
|
|
43
|
+
private radius;
|
|
41
44
|
get isGrounded(): boolean;
|
|
42
45
|
constructor(character: Object3D, world: BvhPhysicsWorld);
|
|
43
46
|
applyVelocity(velocity: Vector3): void;
|
|
@@ -45,6 +48,7 @@ export declare class BvhCharacterPhysics {
|
|
|
45
48
|
* @param delta in seconds
|
|
46
49
|
*/
|
|
47
50
|
update(fullDelta: number, options?: BvhCharacterPhysicsOptions): void;
|
|
51
|
+
private updateBoundingShapes;
|
|
48
52
|
destroy(): void;
|
|
49
53
|
shapecastCapsule(position: Vector3, maxGroundSlope: number, options: Exclude<BvhCharacterPhysicsOptions, boolean>): boolean;
|
|
50
54
|
}
|
package/dist/physics/index.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { Box3, Line3, Matrix4, Vector3 } from 'three';
|
|
2
2
|
//for this is a kinematic character controller
|
|
3
|
-
//helper variables
|
|
4
|
-
const aabbox = new Box3();
|
|
5
|
-
const segment = new Line3();
|
|
6
3
|
const triPoint = new Vector3();
|
|
7
4
|
const capsulePoint = new Vector3();
|
|
8
5
|
const collisionFreePosition = new Vector3();
|
|
@@ -19,6 +16,9 @@ export class BvhCharacterPhysics {
|
|
|
19
16
|
stateVelocity = new Vector3();
|
|
20
17
|
inputVelocity = new Vector3();
|
|
21
18
|
notGroundedSeconds = 0;
|
|
19
|
+
segment = new Line3();
|
|
20
|
+
aabbox = new Box3();
|
|
21
|
+
radius = 0;
|
|
22
22
|
get isGrounded() {
|
|
23
23
|
return this.notGroundedSeconds < 0.2;
|
|
24
24
|
}
|
|
@@ -82,49 +82,55 @@ export class BvhCharacterPhysics {
|
|
|
82
82
|
this.stateVelocity.set(0, (options.gravity ?? -20) * 0.01, 0);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
+
this.updateBoundingShapes(options);
|
|
86
|
+
this.world.updateSensors((bounds) => bounds.intersectsBox(this.aabbox), (triangle) => triangle.closestPointToSegment(this.segment) < this.radius);
|
|
87
|
+
}
|
|
88
|
+
updateBoundingShapes(options) {
|
|
89
|
+
//compute the bounding capsule and bounding box
|
|
90
|
+
this.radius = options.capsuleRadius ?? 0.4;
|
|
91
|
+
const height = options.capsuleHeight ?? 1.7;
|
|
92
|
+
this.segment.start.copy(position);
|
|
93
|
+
this.segment.start.y += this.radius;
|
|
94
|
+
this.segment.end.copy(position);
|
|
95
|
+
this.segment.end.y += height - this.radius;
|
|
96
|
+
this.aabbox.makeEmpty();
|
|
97
|
+
this.aabbox.expandByPoint(this.segment.start);
|
|
98
|
+
this.aabbox.expandByPoint(this.segment.end);
|
|
99
|
+
this.aabbox.min.addScalar(-this.radius);
|
|
100
|
+
this.aabbox.max.addScalar(this.radius);
|
|
85
101
|
}
|
|
86
102
|
destroy() {
|
|
87
103
|
this.destroyed = true;
|
|
88
104
|
}
|
|
89
105
|
shapecastCapsule(position, maxGroundSlope, options) {
|
|
90
|
-
|
|
91
|
-
const height = options.capsuleHeight ?? 1.7;
|
|
92
|
-
segment.start.copy(position);
|
|
93
|
-
segment.start.y += radius;
|
|
94
|
-
segment.end.copy(position);
|
|
95
|
-
segment.end.y += height - radius;
|
|
96
|
-
aabbox.makeEmpty();
|
|
97
|
-
aabbox.expandByPoint(segment.start);
|
|
98
|
-
aabbox.expandByPoint(segment.end);
|
|
99
|
-
aabbox.min.addScalar(-radius);
|
|
100
|
-
aabbox.max.addScalar(radius);
|
|
106
|
+
this.updateBoundingShapes(options);
|
|
101
107
|
let grounded = false;
|
|
102
|
-
this.world.shapecast((bounds) => bounds.intersectsBox(aabbox), (tri) => {
|
|
108
|
+
this.world.shapecast((bounds) => bounds.intersectsBox(this.aabbox), (tri) => {
|
|
103
109
|
// Use your existing triangle vs segment closestPointToSegment
|
|
104
|
-
const distance = tri.closestPointToSegment(segment, triPoint, capsulePoint);
|
|
110
|
+
const distance = tri.closestPointToSegment(this.segment, triPoint, capsulePoint);
|
|
105
111
|
if (distance === 0) {
|
|
106
|
-
const isCloserToSegmentStart = capsulePoint.distanceTo(segment.start) < capsulePoint.distanceTo(segment.end);
|
|
112
|
+
const isCloserToSegmentStart = capsulePoint.distanceTo(this.segment.start) < capsulePoint.distanceTo(this.segment.end);
|
|
107
113
|
if (isCloserToSegmentStart) {
|
|
108
114
|
grounded = true;
|
|
109
115
|
}
|
|
110
|
-
const scaledDirection = capsulePoint.sub(isCloserToSegmentStart ? segment.start : segment.end);
|
|
111
|
-
scaledDirection.y += radius;
|
|
112
|
-
segment.start.add(scaledDirection);
|
|
113
|
-
segment.end.add(scaledDirection);
|
|
116
|
+
const scaledDirection = capsulePoint.sub(isCloserToSegmentStart ? this.segment.start : this.segment.end);
|
|
117
|
+
scaledDirection.y += this.radius;
|
|
118
|
+
this.segment.start.add(scaledDirection);
|
|
119
|
+
this.segment.end.add(scaledDirection);
|
|
114
120
|
}
|
|
115
|
-
else if (distance < radius) {
|
|
116
|
-
const depthInsideCapsule = radius - distance;
|
|
121
|
+
else if (distance < this.radius) {
|
|
122
|
+
const depthInsideCapsule = this.radius - distance;
|
|
117
123
|
const direction = capsulePoint.sub(triPoint).divideScalar(distance);
|
|
118
124
|
const slope = Math.tan(Math.acos(direction.dot(YAxis)));
|
|
119
125
|
if (direction.y > 0 && slope <= maxGroundSlope) {
|
|
120
126
|
grounded = true;
|
|
121
127
|
}
|
|
122
|
-
segment.start.addScaledVector(direction, depthInsideCapsule);
|
|
123
|
-
segment.end.addScaledVector(direction, depthInsideCapsule);
|
|
128
|
+
this.segment.start.addScaledVector(direction, depthInsideCapsule);
|
|
129
|
+
this.segment.end.addScaledVector(direction, depthInsideCapsule);
|
|
124
130
|
}
|
|
125
131
|
});
|
|
126
|
-
position.copy(segment.start);
|
|
127
|
-
position.y -= radius;
|
|
132
|
+
position.copy(this.segment.start);
|
|
133
|
+
position.y -= this.radius;
|
|
128
134
|
return grounded;
|
|
129
135
|
}
|
|
130
136
|
}
|
package/dist/physics/world.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { Box3, Object3D, Ray } from 'three';
|
|
2
2
|
import { ExtendedTriangle } from 'three-mesh-bvh';
|
|
3
3
|
export declare class BvhPhysicsWorld {
|
|
4
|
-
private
|
|
4
|
+
private bodies;
|
|
5
|
+
private sensors;
|
|
5
6
|
/**
|
|
6
7
|
* @deprecated use addBody(object, false) instead
|
|
7
8
|
*/
|
|
8
9
|
addFixedBody(object: Object3D): void;
|
|
10
|
+
addSensor(object: Object3D, isStatic: boolean, onIntersectedChanged: (intersected: boolean) => void): void;
|
|
11
|
+
removeSensor(object: Object3D): void;
|
|
9
12
|
addBody(object: Object3D, kinematic: boolean): void;
|
|
13
|
+
private computeBvhEntries;
|
|
10
14
|
removeBody(object: Object3D): void;
|
|
11
15
|
private computeMatrix;
|
|
16
|
+
updateSensors(intersectsBounds: (box: Box3) => boolean, intersectsTriangle: (triangle: ExtendedTriangle) => boolean): void;
|
|
12
17
|
shapecast(intersectsBounds: (box: Box3) => boolean, intersectsTriangle: (triangle: ExtendedTriangle) => void): void;
|
|
18
|
+
private shapecastEntry;
|
|
13
19
|
raycast(ray: Ray, far: number): number | undefined;
|
|
14
20
|
}
|
package/dist/physics/world.js
CHANGED
|
@@ -6,47 +6,59 @@ const boxHelper = new Box3();
|
|
|
6
6
|
const triangleHelper = new ExtendedTriangle();
|
|
7
7
|
const matrixHelper = new Matrix4();
|
|
8
8
|
export class BvhPhysicsWorld {
|
|
9
|
-
|
|
9
|
+
bodies = [];
|
|
10
|
+
sensors = [];
|
|
10
11
|
/**
|
|
11
12
|
* @deprecated use addBody(object, false) instead
|
|
12
13
|
*/
|
|
13
14
|
addFixedBody(object) {
|
|
14
15
|
this.addBody(object, false);
|
|
15
16
|
}
|
|
17
|
+
addSensor(object, isStatic, onIntersectedChanged) {
|
|
18
|
+
this.sensors.push(...this.computeBvhEntries(object, isStatic).map((entry) => ({
|
|
19
|
+
...entry,
|
|
20
|
+
onIntersectedChanged,
|
|
21
|
+
intersected: false,
|
|
22
|
+
})));
|
|
23
|
+
}
|
|
24
|
+
removeSensor(object) {
|
|
25
|
+
this.sensors = this.sensors.filter((entry) => entry.object != object);
|
|
26
|
+
}
|
|
16
27
|
addBody(object, kinematic) {
|
|
28
|
+
this.bodies.push(...this.computeBvhEntries(object, !kinematic));
|
|
29
|
+
}
|
|
30
|
+
computeBvhEntries(object, isStatic) {
|
|
17
31
|
object.updateWorldMatrix(true, true);
|
|
18
32
|
if (!(object instanceof InstancedMesh)) {
|
|
19
33
|
const parent = object.parent;
|
|
20
|
-
if (
|
|
34
|
+
if (!isStatic) {
|
|
21
35
|
object.parent = null;
|
|
22
36
|
object.updateMatrixWorld(true);
|
|
23
37
|
}
|
|
24
38
|
const geometry = new StaticGeometryGenerator(object).generate();
|
|
25
|
-
|
|
26
|
-
if (
|
|
39
|
+
const bvh = computeBoundsTree.apply(geometry);
|
|
40
|
+
if (!isStatic) {
|
|
27
41
|
object.parent = parent;
|
|
28
42
|
object.updateMatrixWorld(true);
|
|
29
43
|
}
|
|
30
|
-
return;
|
|
44
|
+
return [{ object, bvh, isStatic }];
|
|
31
45
|
}
|
|
32
46
|
if (object.children.length > 0) {
|
|
33
47
|
throw new Error(`cannot add InstancedMesh with children`);
|
|
34
48
|
}
|
|
35
49
|
const bvh = computeBoundsTree.apply(object.geometry);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
}
|
|
50
|
+
return new Array(object.instanceMatrix).fill(undefined).map((_, i) => ({
|
|
51
|
+
object,
|
|
52
|
+
bvh,
|
|
53
|
+
instanceIndex: i,
|
|
54
|
+
isStatic,
|
|
55
|
+
}));
|
|
44
56
|
}
|
|
45
57
|
removeBody(object) {
|
|
46
|
-
this.
|
|
58
|
+
this.bodies = this.bodies.filter((entry) => entry.object != object);
|
|
47
59
|
}
|
|
48
|
-
computeMatrix({
|
|
49
|
-
if (
|
|
60
|
+
computeMatrix({ isStatic, object, instanceIndex }, target) {
|
|
61
|
+
if (isStatic && instanceIndex == null) {
|
|
50
62
|
return false;
|
|
51
63
|
}
|
|
52
64
|
if (instanceIndex == null) {
|
|
@@ -58,31 +70,48 @@ export class BvhPhysicsWorld {
|
|
|
58
70
|
target.premultiply(object.matrixWorld);
|
|
59
71
|
return true;
|
|
60
72
|
}
|
|
73
|
+
updateSensors(intersectsBounds, intersectsTriangle) {
|
|
74
|
+
console.log('update sensors', this.sensors.length);
|
|
75
|
+
for (const entry of this.sensors) {
|
|
76
|
+
const intersected = this.shapecastEntry(entry, intersectsBounds, intersectsTriangle);
|
|
77
|
+
console.log(intersected);
|
|
78
|
+
if (entry.intersected === intersected) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
entry.onIntersectedChanged(intersected);
|
|
82
|
+
entry.intersected = intersected;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
61
85
|
shapecast(intersectsBounds, intersectsTriangle) {
|
|
62
|
-
for (const entry of this.
|
|
63
|
-
|
|
64
|
-
intersectsBounds: (box) => {
|
|
65
|
-
boxHelper.copy(box);
|
|
66
|
-
if (this.computeMatrix(entry, matrixHelper)) {
|
|
67
|
-
boxHelper.applyMatrix4(matrixHelper);
|
|
68
|
-
}
|
|
69
|
-
return intersectsBounds(boxHelper);
|
|
70
|
-
},
|
|
71
|
-
intersectsTriangle: (triangle) => {
|
|
72
|
-
triangleHelper.copy(triangle);
|
|
73
|
-
if (this.computeMatrix(entry, matrixHelper)) {
|
|
74
|
-
triangleHelper.a.applyMatrix4(matrixHelper);
|
|
75
|
-
triangleHelper.b.applyMatrix4(matrixHelper);
|
|
76
|
-
triangleHelper.c.applyMatrix4(matrixHelper);
|
|
77
|
-
}
|
|
78
|
-
intersectsTriangle(triangleHelper);
|
|
79
|
-
},
|
|
80
|
-
});
|
|
86
|
+
for (const entry of this.bodies) {
|
|
87
|
+
this.shapecastEntry(entry, intersectsBounds, intersectsTriangle);
|
|
81
88
|
}
|
|
82
89
|
}
|
|
90
|
+
shapecastEntry(entry, intersectsBounds, intersectsTriangle) {
|
|
91
|
+
return entry.bvh.shapecast({
|
|
92
|
+
intersectsBounds: (box) => {
|
|
93
|
+
boxHelper.copy(box);
|
|
94
|
+
if (this.computeMatrix(entry, matrixHelper)) {
|
|
95
|
+
boxHelper.applyMatrix4(matrixHelper);
|
|
96
|
+
}
|
|
97
|
+
return intersectsBounds(boxHelper);
|
|
98
|
+
},
|
|
99
|
+
intersectsTriangle: (triangle) => {
|
|
100
|
+
triangleHelper.copy(triangle);
|
|
101
|
+
if (this.computeMatrix(entry, matrixHelper)) {
|
|
102
|
+
triangleHelper.a.applyMatrix4(matrixHelper);
|
|
103
|
+
triangleHelper.b.applyMatrix4(matrixHelper);
|
|
104
|
+
triangleHelper.c.applyMatrix4(matrixHelper);
|
|
105
|
+
}
|
|
106
|
+
if (intersectsTriangle(triangleHelper) === true) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
83
112
|
raycast(ray, far) {
|
|
84
113
|
let result;
|
|
85
|
-
for (const entry of this.
|
|
114
|
+
for (const entry of this.bodies.values()) {
|
|
86
115
|
rayHelper.copy(ray);
|
|
87
116
|
let farHelper = far;
|
|
88
117
|
if (this.computeMatrix(entry, matrixHelper)) {
|