@dice-o-rolla/dice-physics-rapier 0.1.0 → 0.2.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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Vector3Like } from '@dice-o-rolla/dice-core';
|
|
2
|
-
import type { CreatePhysicsDieOptions, PhysicsDieHandle, PhysicsWorld, TrayOptions } from '@dice-o-rolla/dice-physics';
|
|
2
|
+
import type { CreatePhysicsDieOptions, PhysicsCollisionEvent, PhysicsImpactEvent, PhysicsDieHandle, PhysicsWorld, TrayOptions } from '@dice-o-rolla/dice-physics';
|
|
3
3
|
export interface RapierPhysicsWorldOptions {
|
|
4
4
|
readonly gravity?: Vector3Like;
|
|
5
5
|
}
|
|
@@ -10,6 +10,9 @@ export declare class RapierPhysicsWorld implements PhysicsWorld {
|
|
|
10
10
|
createDie(options: CreatePhysicsDieOptions): PhysicsDieHandle;
|
|
11
11
|
configureTray(options: TrayOptions): void;
|
|
12
12
|
setGravity(gravity: Vector3Like): void;
|
|
13
|
+
setCollisionEventsEnabled(enabled: boolean): void;
|
|
14
|
+
drainCollisionEvents(): readonly PhysicsCollisionEvent[];
|
|
15
|
+
drainImpactEvents(): readonly PhysicsImpactEvent[];
|
|
13
16
|
step(dtSeconds: number): void;
|
|
14
17
|
removeDie(id: string): void;
|
|
15
18
|
clear(): void;
|
|
@@ -6,13 +6,18 @@ const DEFAULT_GRAVITY = { x: 0, y: -9.81, z: 0 };
|
|
|
6
6
|
export class RapierPhysicsWorld {
|
|
7
7
|
#rapier;
|
|
8
8
|
#world;
|
|
9
|
+
#eventQueue;
|
|
9
10
|
#dice = new Map();
|
|
11
|
+
#colliderDice = new Map();
|
|
12
|
+
#dieColliders = new Map();
|
|
10
13
|
#trayBody;
|
|
14
|
+
#collisionEventsEnabled = false;
|
|
11
15
|
#destroyed = false;
|
|
12
16
|
constructor(rapier, gravity) {
|
|
13
17
|
assertVector(gravity, 'gravity');
|
|
14
18
|
this.#rapier = rapier;
|
|
15
19
|
this.#world = new rapier.World(gravity);
|
|
20
|
+
this.#eventQueue = new rapier.EventQueue(true);
|
|
16
21
|
}
|
|
17
22
|
static async create(options = {}) {
|
|
18
23
|
const rapier = await initializeRapier();
|
|
@@ -37,9 +42,15 @@ export class RapierPhysicsWorld {
|
|
|
37
42
|
.setCanSleep(true)
|
|
38
43
|
.setCcdEnabled(true);
|
|
39
44
|
const colliderDescriptor = createConvexHullCollider(this.#rapier, options.collider, options.scale, options.mass, options.material);
|
|
45
|
+
colliderDescriptor.setActiveEvents(this.#collisionEventsEnabled
|
|
46
|
+
? this.#rapier.ActiveEvents.COLLISION_EVENTS |
|
|
47
|
+
this.#rapier.ActiveEvents.CONTACT_FORCE_EVENTS
|
|
48
|
+
: this.#rapier.ActiveEvents.NONE);
|
|
40
49
|
const body = this.#world.createRigidBody(bodyDescriptor);
|
|
41
50
|
try {
|
|
42
|
-
this.#world.createCollider(colliderDescriptor, body);
|
|
51
|
+
const collider = this.#world.createCollider(colliderDescriptor, body);
|
|
52
|
+
this.#colliderDice.set(collider.handle, options.id);
|
|
53
|
+
this.#dieColliders.set(options.id, collider.handle);
|
|
43
54
|
}
|
|
44
55
|
catch (error) {
|
|
45
56
|
this.#world.removeRigidBody(body);
|
|
@@ -82,17 +93,70 @@ export class RapierPhysicsWorld {
|
|
|
82
93
|
assertVector(gravity, 'gravity');
|
|
83
94
|
this.#world.gravity = { x: gravity.x, y: gravity.y, z: gravity.z };
|
|
84
95
|
}
|
|
96
|
+
setCollisionEventsEnabled(enabled) {
|
|
97
|
+
this.#assertAlive();
|
|
98
|
+
this.#collisionEventsEnabled = enabled;
|
|
99
|
+
const activeEvents = enabled
|
|
100
|
+
? this.#rapier.ActiveEvents.COLLISION_EVENTS | this.#rapier.ActiveEvents.CONTACT_FORCE_EVENTS
|
|
101
|
+
: this.#rapier.ActiveEvents.NONE;
|
|
102
|
+
for (const die of this.#dice.values()) {
|
|
103
|
+
for (let index = 0; index < die.body.numColliders(); index += 1) {
|
|
104
|
+
die.body.collider(index).setActiveEvents(activeEvents);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
this.#eventQueue.clear();
|
|
108
|
+
}
|
|
109
|
+
drainCollisionEvents() {
|
|
110
|
+
this.#assertAlive();
|
|
111
|
+
const events = [];
|
|
112
|
+
this.#eventQueue.drainCollisionEvents((firstHandle, secondHandle, started) => {
|
|
113
|
+
const first = this.#colliderDice.get(firstHandle);
|
|
114
|
+
const second = this.#colliderDice.get(secondHandle);
|
|
115
|
+
const dieId = first ?? second;
|
|
116
|
+
if (dieId === undefined)
|
|
117
|
+
return;
|
|
118
|
+
const otherDieId = first === undefined || second === undefined ? undefined : second;
|
|
119
|
+
events.push(Object.freeze({
|
|
120
|
+
dieId,
|
|
121
|
+
...(otherDieId === undefined ? {} : { otherDieId }),
|
|
122
|
+
started,
|
|
123
|
+
}));
|
|
124
|
+
});
|
|
125
|
+
return Object.freeze(events);
|
|
126
|
+
}
|
|
127
|
+
drainImpactEvents() {
|
|
128
|
+
this.#assertAlive();
|
|
129
|
+
const events = [];
|
|
130
|
+
this.#eventQueue.drainContactForceEvents((contact) => {
|
|
131
|
+
const first = this.#colliderDice.get(contact.collider1());
|
|
132
|
+
const second = this.#colliderDice.get(contact.collider2());
|
|
133
|
+
const dieId = first ?? second;
|
|
134
|
+
if (dieId === undefined)
|
|
135
|
+
return;
|
|
136
|
+
const otherDieId = first === undefined || second === undefined ? undefined : second;
|
|
137
|
+
events.push(Object.freeze({
|
|
138
|
+
dieId,
|
|
139
|
+
...(otherDieId === undefined ? {} : { otherDieId }),
|
|
140
|
+
force: contact.totalForceMagnitude(),
|
|
141
|
+
}));
|
|
142
|
+
});
|
|
143
|
+
return Object.freeze(events);
|
|
144
|
+
}
|
|
85
145
|
step(dtSeconds) {
|
|
86
146
|
this.#assertAlive();
|
|
87
147
|
assertPositive(dtSeconds, 'dtSeconds');
|
|
88
148
|
this.#world.timestep = dtSeconds;
|
|
89
|
-
this.#world.step();
|
|
149
|
+
this.#world.step(this.#collisionEventsEnabled ? this.#eventQueue : undefined);
|
|
90
150
|
}
|
|
91
151
|
removeDie(id) {
|
|
92
152
|
this.#assertAlive();
|
|
93
153
|
const die = this.#dice.get(id);
|
|
94
154
|
if (die === undefined)
|
|
95
155
|
return;
|
|
156
|
+
const colliderHandle = this.#dieColliders.get(id);
|
|
157
|
+
if (colliderHandle !== undefined)
|
|
158
|
+
this.#colliderDice.delete(colliderHandle);
|
|
159
|
+
this.#dieColliders.delete(id);
|
|
96
160
|
this.#world.removeRigidBody(die.body);
|
|
97
161
|
die.invalidate();
|
|
98
162
|
this.#dice.delete(id);
|
|
@@ -104,12 +168,16 @@ export class RapierPhysicsWorld {
|
|
|
104
168
|
die.invalidate();
|
|
105
169
|
}
|
|
106
170
|
this.#dice.clear();
|
|
171
|
+
this.#colliderDice.clear();
|
|
172
|
+
this.#dieColliders.clear();
|
|
173
|
+
this.#eventQueue.clear();
|
|
107
174
|
}
|
|
108
175
|
destroy() {
|
|
109
176
|
if (this.#destroyed)
|
|
110
177
|
return;
|
|
111
178
|
this.clear();
|
|
112
179
|
this.#removeTray();
|
|
180
|
+
this.#eventQueue.free();
|
|
113
181
|
this.#world.free();
|
|
114
182
|
this.#destroyed = true;
|
|
115
183
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dice-o-rolla/dice-physics-rapier",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Rapier 3D physics backend for physical dice simulation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dice",
|
|
@@ -8,7 +8,16 @@
|
|
|
8
8
|
"rapier",
|
|
9
9
|
"wasm"
|
|
10
10
|
],
|
|
11
|
+
"homepage": "https://github.com/creepiest-space/dice-o-rolla#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/creepiest-space/dice-o-rolla/issues"
|
|
14
|
+
},
|
|
11
15
|
"license": "Apache-2.0",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/creepiest-space/dice-o-rolla.git",
|
|
19
|
+
"directory": "packages/dice-physics-rapier"
|
|
20
|
+
},
|
|
12
21
|
"files": [
|
|
13
22
|
"dist",
|
|
14
23
|
"README.md",
|
|
@@ -31,8 +40,8 @@
|
|
|
31
40
|
"registry": "https://registry.npmjs.org/"
|
|
32
41
|
},
|
|
33
42
|
"dependencies": {
|
|
34
|
-
"@dice-o-rolla/dice-core": "0.
|
|
35
|
-
"@dice-o-rolla/dice-physics": "0.
|
|
43
|
+
"@dice-o-rolla/dice-core": "0.2.0",
|
|
44
|
+
"@dice-o-rolla/dice-physics": "0.2.0",
|
|
36
45
|
"@dimforge/rapier3d-compat": "^0.20.0"
|
|
37
46
|
},
|
|
38
47
|
"engines": {
|