dacha 0.17.2 → 0.18.0-alpha.0
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/build/contrib/components/collider/index.d.ts +6 -2
- package/build/contrib/components/collider/index.js +7 -2
- package/build/contrib/components/rigid-body/index.d.ts +29 -17
- package/build/contrib/components/rigid-body/index.js +64 -21
- package/build/contrib/events/index.d.ts +10 -75
- package/build/contrib/events/index.js +0 -36
- package/build/contrib/systems/physics-system/index.d.ts +0 -1
- package/build/contrib/systems/physics-system/index.js +6 -11
- package/build/contrib/systems/physics-system/subsystems/collision-broadcast/collision.d.ts +5 -3
- package/build/contrib/systems/physics-system/subsystems/collision-broadcast/collision.js +7 -5
- package/build/contrib/systems/physics-system/subsystems/collision-broadcast/index.d.ts +4 -6
- package/build/contrib/systems/physics-system/subsystems/collision-broadcast/index.js +20 -17
- package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +12 -9
- package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +107 -93
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/check-boxes-intersection.d.ts +10 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/check-boxes-intersection.js +36 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.d.ts +23 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.js +143 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-circle/check-box-and-circle-intersection.d.ts +9 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-circle/check-box-and-circle-intersection.js +47 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/circle-circle/check-circles-intersection.d.ts +12 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/circle-circle/check-circles-intersection.js +49 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/index.d.ts +2 -2
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/index.js +3 -3
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/tests/helpers.d.ts +17 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/tests/helpers.js +66 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/utils.d.ts +4 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/utils.js +9 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/reorientation-checkers/check-collider.js +3 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +14 -5
- package/build/contrib/systems/physics-system/subsystems/collision-detection/types.js +0 -3
- package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.d.ts +7 -10
- package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.js +117 -79
- package/build/contrib/systems/physics-system/subsystems/index.d.ts +0 -1
- package/build/contrib/systems/physics-system/subsystems/index.js +0 -1
- package/build/contrib/systems/physics-system/subsystems/physics/index.d.ts +3 -9
- package/build/contrib/systems/physics-system/subsystems/physics/index.js +57 -93
- package/build/contrib/systems/physics-system/types.d.ts +9 -0
- package/build/engine/math-lib/vector/ops.d.ts +3 -3
- package/build/engine/math-lib/vector/ops.js +8 -5
- package/build/engine/math-lib/vector/vector2.d.ts +22 -7
- package/build/engine/math-lib/vector/vector2.js +29 -5
- package/build/events/index.d.ts +2 -2
- package/build/events/index.js +1 -1
- package/package.json +1 -1
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-box-and-circle-intersection.d.ts +0 -16
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-box-and-circle-intersection.js +0 -80
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-boxes-intersection.d.ts +0 -6
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-boxes-intersection.js +0 -72
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-circles-intersection.d.ts +0 -11
- package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-circles-intersection.js +0 -39
- package/build/contrib/systems/physics-system/subsystems/collision-solver/index.d.ts +0 -10
- package/build/contrib/systems/physics-system/subsystems/collision-solver/index.js +0 -50
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { checkBoxAndCircleIntersection } from './check-box-and-circle-intersection';
|
|
2
|
-
import { checkBoxesIntersection } from './check-boxes-intersection';
|
|
3
|
-
import { checkCirclesIntersection } from './check-circles-intersection';
|
|
1
|
+
import { checkBoxAndCircleIntersection } from './box-circle/check-box-and-circle-intersection';
|
|
2
|
+
import { checkBoxesIntersection } from './box-box/check-boxes-intersection';
|
|
3
|
+
import { checkCirclesIntersection } from './circle-circle/check-circles-intersection';
|
|
4
4
|
export const intersectionCheckers = {
|
|
5
5
|
box: {
|
|
6
6
|
box: checkBoxesIntersection,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BoxGeometry, CircleGeometry, Intersection, Proxy } from '../../types';
|
|
2
|
+
export declare const createBoxGeometry: (centerX: number, centerY: number, sizeX: number, sizeY: number) => BoxGeometry;
|
|
3
|
+
export declare const createRotatedBoxGeometry: (centerX: number, centerY: number, sizeX: number, sizeY: number, rotation: number) => BoxGeometry;
|
|
4
|
+
export declare const createCircleGeometry: (centerX: number, centerY: number, radius: number) => CircleGeometry;
|
|
5
|
+
export declare const createProxy: (type: 'box' | 'circle', geometry: BoxGeometry | CircleGeometry) => Proxy;
|
|
6
|
+
export declare const expectToBeClose: (point: {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}, x: number, y: number, digits?: number) => void;
|
|
10
|
+
export declare const expectIntersection: (intersection: Intersection | false) => Intersection;
|
|
11
|
+
export declare const sortPoints: (points: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}[]) => {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
}[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { VectorOps } from '../../../../../../../engine/math-lib';
|
|
2
|
+
export const createBoxGeometry = (centerX, centerY, sizeX, sizeY) => {
|
|
3
|
+
return createRotatedBoxGeometry(centerX, centerY, sizeX, sizeY, 0);
|
|
4
|
+
};
|
|
5
|
+
export const createRotatedBoxGeometry = (centerX, centerY, sizeX, sizeY, rotation) => {
|
|
6
|
+
const halfX = sizeX / 2;
|
|
7
|
+
const halfY = sizeY / 2;
|
|
8
|
+
const cos = Math.cos(rotation);
|
|
9
|
+
const sin = Math.sin(rotation);
|
|
10
|
+
const points = [
|
|
11
|
+
{ x: -halfX, y: -halfY },
|
|
12
|
+
{ x: -halfX, y: halfY },
|
|
13
|
+
{ x: halfX, y: halfY },
|
|
14
|
+
{ x: halfX, y: -halfY },
|
|
15
|
+
];
|
|
16
|
+
for (const point of points) {
|
|
17
|
+
const rotatedX = point.x * cos - point.y * sin;
|
|
18
|
+
const rotatedY = point.x * sin + point.y * cos;
|
|
19
|
+
point.x = rotatedX + centerX;
|
|
20
|
+
point.y = rotatedY + centerY;
|
|
21
|
+
}
|
|
22
|
+
const edges = points.map((point1, index, array) => {
|
|
23
|
+
const point2 = array[(index + 1) % array.length];
|
|
24
|
+
return {
|
|
25
|
+
point1,
|
|
26
|
+
point2,
|
|
27
|
+
normal: VectorOps.getNormal(point1.x, point2.x, point1.y, point2.y),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
center: {
|
|
32
|
+
x: centerX,
|
|
33
|
+
y: centerY,
|
|
34
|
+
},
|
|
35
|
+
points,
|
|
36
|
+
edges,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export const createCircleGeometry = (centerX, centerY, radius) => ({
|
|
40
|
+
center: { x: centerX, y: centerY },
|
|
41
|
+
radius,
|
|
42
|
+
});
|
|
43
|
+
export const createProxy = (type, geometry) => ({
|
|
44
|
+
actor: {
|
|
45
|
+
id: `${type}-${Math.random()}`,
|
|
46
|
+
getComponent: () => ({ type }),
|
|
47
|
+
},
|
|
48
|
+
geometry,
|
|
49
|
+
});
|
|
50
|
+
export const expectToBeClose = (point, x, y, digits = 6) => {
|
|
51
|
+
expect(point.x).toBeCloseTo(x, digits);
|
|
52
|
+
expect(point.y).toBeCloseTo(y, digits);
|
|
53
|
+
};
|
|
54
|
+
export const expectIntersection = (intersection) => {
|
|
55
|
+
expect(intersection).not.toBe(false);
|
|
56
|
+
if (intersection === false) {
|
|
57
|
+
throw new Error('Expected intersection, received false');
|
|
58
|
+
}
|
|
59
|
+
return intersection;
|
|
60
|
+
};
|
|
61
|
+
export const sortPoints = (points) => [...points].sort((point1, point2) => {
|
|
62
|
+
if (point1.x !== point2.x) {
|
|
63
|
+
return point1.x - point2.x;
|
|
64
|
+
}
|
|
65
|
+
return point1.y - point2.y;
|
|
66
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Vector2, VectorOps } from '../../../../../../engine/math-lib';
|
|
2
|
+
export const INTERSECTION_EPSILON = 1e-6;
|
|
3
|
+
export const orientNormal = (normal, from, to) => {
|
|
4
|
+
const direction = new Vector2(to.x - from.x, to.y - from.y);
|
|
5
|
+
if (VectorOps.dotProduct(direction, normal) < 0) {
|
|
6
|
+
return normal.clone().multiplyNumber(-1);
|
|
7
|
+
}
|
|
8
|
+
return normal;
|
|
9
|
+
};
|
|
@@ -2,6 +2,9 @@ export const checkCollider = (collider, colliderOld) => {
|
|
|
2
2
|
if (collider.type !== colliderOld.type) {
|
|
3
3
|
return true;
|
|
4
4
|
}
|
|
5
|
+
if (collider.layer !== colliderOld.layer) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
5
8
|
if (collider.type === 'box') {
|
|
6
9
|
return collider.centerX !== colliderOld.centerX
|
|
7
10
|
|| collider.centerY !== colliderOld.centerY
|
|
@@ -41,9 +41,10 @@ export interface OrientationData {
|
|
|
41
41
|
radius?: number;
|
|
42
42
|
sizeX?: number;
|
|
43
43
|
sizeY?: number;
|
|
44
|
+
layer: string;
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
|
-
export interface
|
|
47
|
+
export interface Proxy {
|
|
47
48
|
actor: Actor;
|
|
48
49
|
aabb: AABB;
|
|
49
50
|
geometry: Geometry;
|
|
@@ -51,7 +52,7 @@ export interface CollisionEntry {
|
|
|
51
52
|
edges: Record<Axis, [SortedItem, SortedItem]>;
|
|
52
53
|
}
|
|
53
54
|
export interface SortedItem {
|
|
54
|
-
|
|
55
|
+
proxy: Proxy;
|
|
55
56
|
value: number;
|
|
56
57
|
}
|
|
57
58
|
export interface AxisEntry {
|
|
@@ -63,8 +64,16 @@ export interface Axes {
|
|
|
63
64
|
x: AxisEntry;
|
|
64
65
|
y: AxisEntry;
|
|
65
66
|
}
|
|
66
|
-
export type
|
|
67
|
+
export type ProxyPair = [Proxy, Proxy];
|
|
68
|
+
export interface Contact {
|
|
69
|
+
actor1: Actor;
|
|
70
|
+
actor2: Actor;
|
|
71
|
+
normal: Vector2;
|
|
72
|
+
penetration: number;
|
|
73
|
+
contactPoints: Point[];
|
|
74
|
+
}
|
|
67
75
|
export interface Intersection {
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
normal: Vector2;
|
|
77
|
+
penetration: number;
|
|
78
|
+
contactPoints: Point[];
|
|
70
79
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Contact } from '../collision-detection/types';
|
|
2
2
|
export declare class ConstraintSolver {
|
|
3
|
-
private
|
|
4
|
-
private processedPairs;
|
|
5
|
-
private mtvMap;
|
|
6
|
-
constructor(options: SceneSystemOptions);
|
|
7
|
-
destroy(): void;
|
|
8
|
-
private handleCollision;
|
|
3
|
+
private validContacts;
|
|
9
4
|
private validateCollision;
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
|
|
5
|
+
private getInverseMass;
|
|
6
|
+
private applyNormalImpulse;
|
|
7
|
+
private applyFrictionImpulse;
|
|
8
|
+
private applyPositionCorrection;
|
|
9
|
+
update(contacts: Contact[]): void;
|
|
13
10
|
}
|
|
@@ -1,36 +1,12 @@
|
|
|
1
|
-
import { Vector2 } from '../../../../../engine/math-lib';
|
|
2
1
|
import { RigidBody } from '../../../../components/rigid-body';
|
|
3
2
|
import { Transform } from '../../../../components/transform';
|
|
4
|
-
import { Collision } from '../../../../events';
|
|
5
3
|
import { RIGID_BODY_TYPE } from '../../consts';
|
|
4
|
+
const SOLVER_ITERATIONS = 8;
|
|
5
|
+
const POSITION_CORRECTION_PERCENT = 0.8;
|
|
6
|
+
const PENETRATION_SLOP = 0.01;
|
|
7
|
+
const DEFAULT_CONTACT_FRICTION = 0.6;
|
|
6
8
|
export class ConstraintSolver {
|
|
7
|
-
|
|
8
|
-
processedPairs;
|
|
9
|
-
mtvMap;
|
|
10
|
-
constructor(options) {
|
|
11
|
-
this.scene = options.scene;
|
|
12
|
-
this.processedPairs = new Map();
|
|
13
|
-
this.mtvMap = new Map();
|
|
14
|
-
this.scene.addEventListener(Collision, this.handleCollision);
|
|
15
|
-
}
|
|
16
|
-
destroy() {
|
|
17
|
-
this.scene.removeEventListener(Collision, this.handleCollision);
|
|
18
|
-
}
|
|
19
|
-
handleCollision = (event) => {
|
|
20
|
-
const { actor1, actor2, mtv1, mtv2 } = event;
|
|
21
|
-
if (this.processedPairs.has(actor2) &&
|
|
22
|
-
this.processedPairs.get(actor2).has(actor1)) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (!this.processedPairs.has(actor1)) {
|
|
26
|
-
this.processedPairs.set(actor1, new Set());
|
|
27
|
-
}
|
|
28
|
-
this.processedPairs.get(actor1).add(actor2);
|
|
29
|
-
if (!this.validateCollision(actor1, actor2)) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
this.resolveCollision(actor1, actor2, mtv1, mtv2);
|
|
33
|
-
};
|
|
9
|
+
validContacts = [];
|
|
34
10
|
validateCollision(actor1, actor2) {
|
|
35
11
|
const rigidBody1 = actor1.getComponent(RigidBody);
|
|
36
12
|
const rigidBody2 = actor2.getComponent(RigidBody);
|
|
@@ -41,68 +17,130 @@ export class ConstraintSolver {
|
|
|
41
17
|
rigidBody2.type === RIGID_BODY_TYPE.STATIC) {
|
|
42
18
|
return false;
|
|
43
19
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
getInverseMass(rigidBody) {
|
|
23
|
+
if (rigidBody.disabled || rigidBody.type === RIGID_BODY_TYPE.STATIC) {
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
return rigidBody.inverseMass;
|
|
27
|
+
}
|
|
28
|
+
applyNormalImpulse(contact) {
|
|
29
|
+
const { actor1, actor2, normal } = contact;
|
|
30
|
+
const rigidBody1 = actor1.getComponent(RigidBody);
|
|
31
|
+
const rigidBody2 = actor2.getComponent(RigidBody);
|
|
32
|
+
const inverseMass1 = this.getInverseMass(rigidBody1);
|
|
33
|
+
const inverseMass2 = this.getInverseMass(rigidBody2);
|
|
34
|
+
const inverseMassSum = inverseMass1 + inverseMass2;
|
|
35
|
+
if (inverseMassSum === 0) {
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
const relativeVelocityX = rigidBody2.linearVelocity.x - rigidBody1.linearVelocity.x;
|
|
39
|
+
const relativeVelocityY = rigidBody2.linearVelocity.y - rigidBody1.linearVelocity.y;
|
|
40
|
+
const velocityAlongNormal = relativeVelocityX * normal.x + relativeVelocityY * normal.y;
|
|
41
|
+
if (velocityAlongNormal >= 0) {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
const impulseMagnitude = -velocityAlongNormal / inverseMassSum;
|
|
45
|
+
const impulseX = normal.x * impulseMagnitude;
|
|
46
|
+
const impulseY = normal.y * impulseMagnitude;
|
|
47
|
+
if (inverseMass1 > 0) {
|
|
48
|
+
rigidBody1.linearVelocity.x -= impulseX * inverseMass1;
|
|
49
|
+
rigidBody1.linearVelocity.y -= impulseY * inverseMass1;
|
|
47
50
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
if (inverseMass2 > 0) {
|
|
52
|
+
rigidBody2.linearVelocity.x += impulseX * inverseMass2;
|
|
53
|
+
rigidBody2.linearVelocity.y += impulseY * inverseMass2;
|
|
54
|
+
}
|
|
55
|
+
return impulseMagnitude;
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
57
|
+
applyFrictionImpulse(contact, normalImpulseMagnitude) {
|
|
58
|
+
if (normalImpulseMagnitude <= 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const { normal } = contact;
|
|
62
|
+
const rigidBody1 = contact.actor1.getComponent(RigidBody);
|
|
63
|
+
const rigidBody2 = contact.actor2.getComponent(RigidBody);
|
|
64
|
+
const inverseMass1 = this.getInverseMass(rigidBody1);
|
|
65
|
+
const inverseMass2 = this.getInverseMass(rigidBody2);
|
|
66
|
+
const inverseMassSum = inverseMass1 + inverseMass2;
|
|
67
|
+
if (inverseMassSum === 0) {
|
|
68
|
+
return;
|
|
56
69
|
}
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
const relativeVelocityX = rigidBody2.linearVelocity.x - rigidBody1.linearVelocity.x;
|
|
71
|
+
const relativeVelocityY = rigidBody2.linearVelocity.y - rigidBody1.linearVelocity.y;
|
|
72
|
+
const velocityAlongNormal = relativeVelocityX * normal.x + relativeVelocityY * normal.y;
|
|
73
|
+
const tangentX = relativeVelocityX - normal.x * velocityAlongNormal;
|
|
74
|
+
const tangentY = relativeVelocityY - normal.y * velocityAlongNormal;
|
|
75
|
+
const tangentMagnitude = Math.sqrt(tangentX ** 2 + tangentY ** 2);
|
|
76
|
+
if (tangentMagnitude === 0) {
|
|
60
77
|
return;
|
|
61
78
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
const normalizedTangentX = tangentX / tangentMagnitude;
|
|
80
|
+
const normalizedTangentY = tangentY / tangentMagnitude;
|
|
81
|
+
const velocityAlongTangent = relativeVelocityX * normalizedTangentX +
|
|
82
|
+
relativeVelocityY * normalizedTangentY;
|
|
83
|
+
const unclampedImpulseMagnitude = -velocityAlongTangent / inverseMassSum;
|
|
84
|
+
const maxFrictionImpulseMagnitude = DEFAULT_CONTACT_FRICTION * normalImpulseMagnitude;
|
|
85
|
+
const impulseMagnitude = Math.max(-maxFrictionImpulseMagnitude, Math.min(unclampedImpulseMagnitude, maxFrictionImpulseMagnitude));
|
|
86
|
+
const impulseX = normalizedTangentX * impulseMagnitude;
|
|
87
|
+
const impulseY = normalizedTangentY * impulseMagnitude;
|
|
88
|
+
if (inverseMass1 > 0) {
|
|
89
|
+
rigidBody1.linearVelocity.x -= impulseX * inverseMass1;
|
|
90
|
+
rigidBody1.linearVelocity.y -= impulseY * inverseMass1;
|
|
91
|
+
}
|
|
92
|
+
if (inverseMass2 > 0) {
|
|
93
|
+
rigidBody2.linearVelocity.x += impulseX * inverseMass2;
|
|
94
|
+
rigidBody2.linearVelocity.y += impulseY * inverseMass2;
|
|
72
95
|
}
|
|
73
96
|
}
|
|
74
|
-
|
|
97
|
+
applyPositionCorrection(contact) {
|
|
98
|
+
const { actor1, actor2, normal, penetration } = contact;
|
|
75
99
|
const rigidBody1 = actor1.getComponent(RigidBody);
|
|
76
100
|
const rigidBody2 = actor2.getComponent(RigidBody);
|
|
77
|
-
|
|
78
|
-
|
|
101
|
+
const inverseMass1 = this.getInverseMass(rigidBody1);
|
|
102
|
+
const inverseMass2 = this.getInverseMass(rigidBody2);
|
|
103
|
+
const inverseMassSum = inverseMass1 + inverseMass2;
|
|
104
|
+
if (inverseMassSum === 0) {
|
|
105
|
+
return;
|
|
79
106
|
}
|
|
80
|
-
|
|
81
|
-
|
|
107
|
+
const correctionMagnitude = (Math.max(penetration - PENETRATION_SLOP, 0) *
|
|
108
|
+
POSITION_CORRECTION_PERCENT) /
|
|
109
|
+
inverseMassSum;
|
|
110
|
+
if (correctionMagnitude === 0) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const correctionX = normal.x * correctionMagnitude;
|
|
114
|
+
const correctionY = normal.y * correctionMagnitude;
|
|
115
|
+
const transform1 = actor1.getComponent(Transform);
|
|
116
|
+
const transform2 = actor2.getComponent(Transform);
|
|
117
|
+
if (inverseMass1 > 0) {
|
|
118
|
+
transform1.world.position.x -= correctionX * inverseMass1;
|
|
119
|
+
transform1.world.position.y -= correctionY * inverseMass1;
|
|
82
120
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
121
|
+
if (inverseMass2 > 0) {
|
|
122
|
+
transform2.world.position.x += correctionX * inverseMass2;
|
|
123
|
+
transform2.world.position.y += correctionY * inverseMass2;
|
|
86
124
|
}
|
|
87
125
|
}
|
|
88
|
-
update() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
update(contacts) {
|
|
127
|
+
let validContactsCount = 0;
|
|
128
|
+
contacts.forEach((contact) => {
|
|
129
|
+
if (!this.validateCollision(contact.actor1, contact.actor2)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.validContacts[validContactsCount] = contact;
|
|
133
|
+
validContactsCount += 1;
|
|
134
|
+
});
|
|
135
|
+
this.validContacts.length = validContactsCount;
|
|
136
|
+
for (let iteration = 0; iteration < SOLVER_ITERATIONS; iteration += 1) {
|
|
137
|
+
this.validContacts.forEach((contact) => {
|
|
138
|
+
const normalImpulseMagnitude = this.applyNormalImpulse(contact);
|
|
139
|
+
this.applyFrictionImpulse(contact, normalImpulseMagnitude);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
this.validContacts.forEach((contact) => {
|
|
143
|
+
this.applyPositionCorrection(contact);
|
|
144
|
+
});
|
|
107
145
|
}
|
|
108
146
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { PhysicsSubsystem } from './physics';
|
|
2
2
|
export { CollisionDetectionSubsystem } from './collision-detection';
|
|
3
3
|
export { CollisionBroadcastSubsystem } from './collision-broadcast';
|
|
4
|
-
export { CollisionSolver } from './collision-solver';
|
|
5
4
|
export { ConstraintSolver } from './constraint-solver';
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { PhysicsSubsystem } from './physics';
|
|
2
2
|
export { CollisionDetectionSubsystem } from './collision-detection';
|
|
3
3
|
export { CollisionBroadcastSubsystem } from './collision-broadcast';
|
|
4
|
-
export { CollisionSolver } from './collision-solver';
|
|
5
4
|
export { ConstraintSolver } from './constraint-solver';
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import type { SceneSystemOptions, UpdateOptions } from '../../../../../engine/system';
|
|
2
2
|
export declare class PhysicsSubsystem {
|
|
3
|
-
private scene;
|
|
4
3
|
private actorQuery;
|
|
5
4
|
private gravity;
|
|
6
|
-
private actorVectors;
|
|
7
5
|
constructor(options: SceneSystemOptions);
|
|
8
6
|
destroy(): void;
|
|
9
|
-
private
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
private handleAddImpulse;
|
|
13
|
-
private setUpVectors;
|
|
14
|
-
private applyDragForce;
|
|
15
|
-
private getGravityForce;
|
|
7
|
+
private applyLinearDamping;
|
|
8
|
+
private integrateVelocities;
|
|
9
|
+
private integratePositions;
|
|
16
10
|
update(options: UpdateOptions): void;
|
|
17
11
|
}
|
|
@@ -1,125 +1,89 @@
|
|
|
1
|
-
import { Vector2 } from '../../../../../engine/math-lib';
|
|
2
1
|
import { ActorQuery } from '../../../../../engine/actor';
|
|
3
2
|
import { RigidBody } from '../../../../components/rigid-body';
|
|
4
3
|
import { Transform } from '../../../../components/transform';
|
|
5
|
-
import { AddForce, AddImpulse, StopMovement } from '../../../../events';
|
|
6
|
-
import { RemoveActor } from '../../../../../engine/events';
|
|
7
|
-
const DIRECTION_VECTOR = {
|
|
8
|
-
UP: new Vector2(0, -1),
|
|
9
|
-
LEFT: new Vector2(-1, 0),
|
|
10
|
-
RIGHT: new Vector2(1, 0),
|
|
11
|
-
DOWN: new Vector2(0, 1),
|
|
12
|
-
};
|
|
13
4
|
export class PhysicsSubsystem {
|
|
14
|
-
scene;
|
|
15
5
|
actorQuery;
|
|
16
6
|
gravity;
|
|
17
|
-
actorVectors;
|
|
18
7
|
constructor(options) {
|
|
19
8
|
const { gravity, scene } = options;
|
|
20
|
-
this.scene = scene;
|
|
21
9
|
this.actorQuery = new ActorQuery({ scene, filter: [RigidBody, Transform] });
|
|
22
10
|
this.gravity = gravity;
|
|
23
|
-
this.actorVectors = {};
|
|
24
|
-
this.actorQuery.addEventListener(RemoveActor, this.handleActorRemove);
|
|
25
|
-
this.scene.addEventListener(StopMovement, this.handleStopMovement);
|
|
26
|
-
this.scene.addEventListener(AddForce, this.handleAddForce);
|
|
27
|
-
this.scene.addEventListener(AddImpulse, this.handleAddImpulse);
|
|
28
11
|
}
|
|
29
12
|
destroy() {
|
|
30
|
-
this.actorQuery.
|
|
31
|
-
this.scene.removeEventListener(StopMovement, this.handleStopMovement);
|
|
32
|
-
this.scene.removeEventListener(AddForce, this.handleAddForce);
|
|
33
|
-
this.scene.removeEventListener(AddImpulse, this.handleAddImpulse);
|
|
13
|
+
this.actorQuery.destroy();
|
|
34
14
|
}
|
|
35
|
-
|
|
36
|
-
const {
|
|
37
|
-
|
|
38
|
-
};
|
|
39
|
-
handleStopMovement = (event) => {
|
|
40
|
-
const { target } = event;
|
|
41
|
-
if (!this.actorVectors[target.id]) {
|
|
42
|
-
this.setUpVectors(target);
|
|
43
|
-
}
|
|
44
|
-
this.actorVectors[target.id].velocity.multiplyNumber(0);
|
|
45
|
-
};
|
|
46
|
-
handleAddForce = (event) => {
|
|
47
|
-
const { target, value } = event;
|
|
48
|
-
if (!this.actorVectors[target.id]) {
|
|
49
|
-
this.setUpVectors(target);
|
|
50
|
-
}
|
|
51
|
-
this.actorVectors[target.id].force.add(value);
|
|
52
|
-
};
|
|
53
|
-
handleAddImpulse = (event) => {
|
|
54
|
-
const { target, value } = event;
|
|
55
|
-
if (!this.actorVectors[target.id]) {
|
|
56
|
-
this.setUpVectors(target);
|
|
57
|
-
}
|
|
58
|
-
this.actorVectors[target.id].impulse.add(value);
|
|
59
|
-
};
|
|
60
|
-
setUpVectors(actor) {
|
|
61
|
-
this.actorVectors[actor.id] = {
|
|
62
|
-
velocity: new Vector2(0, 0),
|
|
63
|
-
force: new Vector2(0, 0),
|
|
64
|
-
impulse: new Vector2(0, 0),
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
applyDragForce(actor, deltaTime) {
|
|
68
|
-
const { mass, drag } = actor.getComponent(RigidBody);
|
|
69
|
-
const velocity = this.actorVectors[actor.id]?.velocity;
|
|
70
|
-
if (!drag || !velocity || (!velocity.x && !velocity.y)) {
|
|
15
|
+
applyLinearDamping(rigidBody, deltaTime) {
|
|
16
|
+
const { mass, linearDamping, linearVelocity } = rigidBody;
|
|
17
|
+
if (!linearDamping || (!linearVelocity.x && !linearVelocity.y)) {
|
|
71
18
|
return;
|
|
72
19
|
}
|
|
73
|
-
const velocitySignX = Math.sign(
|
|
74
|
-
const velocitySignY = Math.sign(
|
|
20
|
+
const velocitySignX = Math.sign(linearVelocity.x);
|
|
21
|
+
const velocitySignY = Math.sign(linearVelocity.y);
|
|
75
22
|
const reactionForceValue = mass * this.gravity;
|
|
76
|
-
const dragForceValue = -1 *
|
|
23
|
+
const dragForceValue = -1 * linearDamping * reactionForceValue;
|
|
77
24
|
const forceToVelocityMultiplier = deltaTime / mass;
|
|
78
25
|
const slowdownValue = dragForceValue * forceToVelocityMultiplier;
|
|
79
|
-
const normalizationMultiplier = 1 /
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (Math.sign(
|
|
84
|
-
Math.sign(
|
|
85
|
-
|
|
26
|
+
const normalizationMultiplier = 1 / linearVelocity.magnitude;
|
|
27
|
+
const slowdownMultiplier = slowdownValue * normalizationMultiplier;
|
|
28
|
+
linearVelocity.x += linearVelocity.x * slowdownMultiplier;
|
|
29
|
+
linearVelocity.y += linearVelocity.y * slowdownMultiplier;
|
|
30
|
+
if (Math.sign(linearVelocity.x) !== velocitySignX &&
|
|
31
|
+
Math.sign(linearVelocity.y) !== velocitySignY) {
|
|
32
|
+
linearVelocity.multiplyNumber(0);
|
|
86
33
|
}
|
|
87
34
|
}
|
|
88
|
-
|
|
89
|
-
const { mass, useGravity } = rigidBody;
|
|
90
|
-
const gravityVector = new Vector2(0, 0);
|
|
91
|
-
if (useGravity) {
|
|
92
|
-
gravityVector.add(DIRECTION_VECTOR.DOWN);
|
|
93
|
-
gravityVector.multiplyNumber(mass * this.gravity);
|
|
94
|
-
}
|
|
95
|
-
return gravityVector;
|
|
96
|
-
}
|
|
97
|
-
update(options) {
|
|
98
|
-
const { deltaTime } = options;
|
|
99
|
-
const deltaTimeInMsec = deltaTime;
|
|
100
|
-
const deltaTimeInSeconds = deltaTimeInMsec / 1000;
|
|
35
|
+
integrateVelocities(deltaTimeInSeconds) {
|
|
101
36
|
this.actorQuery.getActors().forEach((actor) => {
|
|
102
37
|
const rigidBody = actor.getComponent(RigidBody);
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
38
|
+
const { mass, inverseMass } = rigidBody;
|
|
39
|
+
if (rigidBody.disabled || rigidBody.type === 'static' || mass <= 0) {
|
|
40
|
+
rigidBody.clearForces();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const { force, impulse } = rigidBody;
|
|
44
|
+
const velocity = rigidBody.linearVelocity;
|
|
45
|
+
if (rigidBody.sleeping) {
|
|
46
|
+
if (force.x || force.y || impulse.x || impulse.y) {
|
|
47
|
+
rigidBody.wakeUp();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (rigidBody.gravityScale) {
|
|
54
|
+
force.y += mass * this.gravity * rigidBody.gravityScale;
|
|
107
55
|
}
|
|
108
|
-
const { velocity, force, impulse } = this.actorVectors[actor.id];
|
|
109
|
-
force.add(this.getGravityForce(rigidBody));
|
|
110
56
|
if (force.x || force.y) {
|
|
111
|
-
force.multiplyNumber(deltaTimeInSeconds
|
|
57
|
+
force.multiplyNumber(deltaTimeInSeconds * inverseMass);
|
|
112
58
|
velocity.add(force);
|
|
113
59
|
}
|
|
114
60
|
if (impulse.x || impulse.y) {
|
|
115
|
-
impulse.multiplyNumber(
|
|
61
|
+
impulse.multiplyNumber(inverseMass);
|
|
116
62
|
velocity.add(impulse);
|
|
117
63
|
}
|
|
118
|
-
this.
|
|
119
|
-
|
|
120
|
-
transform.world.position.y += velocity.y * deltaTimeInSeconds;
|
|
121
|
-
force.multiplyNumber(0);
|
|
122
|
-
impulse.multiplyNumber(0);
|
|
64
|
+
this.applyLinearDamping(rigidBody, deltaTimeInSeconds);
|
|
65
|
+
rigidBody.clearForces();
|
|
123
66
|
});
|
|
124
67
|
}
|
|
68
|
+
integratePositions(deltaTimeInSeconds) {
|
|
69
|
+
this.actorQuery.getActors().forEach((actor) => {
|
|
70
|
+
const rigidBody = actor.getComponent(RigidBody);
|
|
71
|
+
const transform = actor.getComponent(Transform);
|
|
72
|
+
if (rigidBody.disabled ||
|
|
73
|
+
rigidBody.type === 'static' ||
|
|
74
|
+
rigidBody.mass <= 0 ||
|
|
75
|
+
rigidBody.sleeping) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
transform.world.position.x +=
|
|
79
|
+
rigidBody.linearVelocity.x * deltaTimeInSeconds;
|
|
80
|
+
transform.world.position.y +=
|
|
81
|
+
rigidBody.linearVelocity.y * deltaTimeInSeconds;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
update(options) {
|
|
85
|
+
const deltaTimeInSeconds = options.deltaTime / 1000;
|
|
86
|
+
this.integrateVelocities(deltaTimeInSeconds);
|
|
87
|
+
this.integratePositions(deltaTimeInSeconds);
|
|
88
|
+
}
|
|
125
89
|
}
|
|
@@ -2,3 +2,12 @@ import type { SceneSystemOptions } from '../../../engine/system';
|
|
|
2
2
|
export interface PhysicsSystemOptions extends SceneSystemOptions {
|
|
3
3
|
gravity: number;
|
|
4
4
|
}
|
|
5
|
+
export interface CollisionLayer {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export type CollisionMatrix = Record<string, Record<string, boolean>>;
|
|
10
|
+
export interface PhysicsSettings {
|
|
11
|
+
collisionLayers: CollisionLayer[];
|
|
12
|
+
collisionMatrix: CollisionMatrix;
|
|
13
|
+
}
|