Skip to content

Commit f699e1c

Browse files
committed
perf: optimize broadphase pair management
1 parent 2b29751 commit f699e1c

File tree

5 files changed

+87
-32
lines changed

5 files changed

+87
-32
lines changed

src/Settings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class Settings {
5757
* future position based on the current displacement. This is a dimensionless
5858
* multiplier.
5959
*/
60-
static aabbMultiplier: number = 2.0;
60+
static aabbMultiplier: number = 4.0;
6161

6262
/**
6363
* A small length used as a collision and constraint tolerance. Usually it is

src/collision/BroadPhase.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ export class BroadPhase {
184184
this.m_callback = addPairCallback;
185185

186186
// Perform tree queries for all moving proxies.
187-
while (this.m_moveBuffer.length > 0) {
188-
this.m_queryProxyId = this.m_moveBuffer.pop();
187+
for (let i = 0; i < this.m_moveBuffer.length; ++i) {
188+
this.m_queryProxyId = this.m_moveBuffer[i];
189189
if (this.m_queryProxyId === null) {
190190
continue;
191191
}
@@ -198,8 +198,18 @@ export class BroadPhase {
198198
this.m_tree.query(fatAABB, this.queryCallback);
199199
}
200200

201-
// Try to keep the tree balanced.
202-
// this.m_tree.rebalance(4);
201+
// Clear move flags
202+
for (let i = 0; i < this.m_moveBuffer.length; ++i) {
203+
this.m_queryProxyId = this.m_moveBuffer[i];
204+
if (this.m_queryProxyId === null) {
205+
continue;
206+
}
207+
208+
this.m_tree.clearMoved(this.m_queryProxyId);
209+
}
210+
211+
// Reset move buffer
212+
this.m_moveBuffer.length = 0;
203213
}
204214

205215
queryCallback = (proxyId: number): boolean => {
@@ -208,11 +218,15 @@ export class BroadPhase {
208218
return true;
209219
}
210220

221+
const moved = this.m_tree.wasMoved(proxyId);
222+
if (moved && proxyId > this.m_queryProxyId) {
223+
// Both proxies are moving. Avoid duplicate pairs.
224+
return true;
225+
}
226+
211227
const proxyIdA = Math.min(proxyId, this.m_queryProxyId);
212228
const proxyIdB = Math.max(proxyId, this.m_queryProxyId);
213229

214-
// TODO: Skip any duplicate pairs.
215-
216230
const userDataA = this.m_tree.getUserData(proxyIdA);
217231
const userDataB = this.m_tree.getUserData(proxyIdB);
218232

src/collision/DynamicTree.ts

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export class TreeNode<T> {
4747
/** 0: leaf, -1: free node */
4848
height: number = -1;
4949

50+
moved: boolean = false;
51+
5052
constructor(id?: number) {
5153
this.id = id;
5254
}
@@ -110,6 +112,18 @@ export class DynamicTree<T> {
110112
return node.userData;
111113
}
112114

115+
wasMoved(proxyId: number): boolean {
116+
const node = this.m_nodes[proxyId];
117+
_ASSERT && console.assert(!!node);
118+
return node.moved;
119+
}
120+
121+
clearMoved(proxyId: number): void {
122+
const node = this.m_nodes[proxyId];
123+
_ASSERT && console.assert(!!node);
124+
node.moved = false;
125+
}
126+
113127
/**
114128
* Get the fat AABB for a node id.
115129
*
@@ -152,6 +166,7 @@ export class DynamicTree<T> {
152166

153167
node.userData = userData;
154168
node.height = 0;
169+
node.moved = true;
155170

156171
this.insertLeaf(node);
157172

@@ -176,48 +191,65 @@ export class DynamicTree<T> {
176191
* fattened AABB, then the proxy is removed from the tree and re-inserted.
177192
* Otherwise the function returns immediately.
178193
*
179-
* @param d Displacement
194+
* @param displacement Displacement
180195
*
181196
* @return true if the proxy was re-inserted.
182197
*/
183-
moveProxy(id: number, aabb: AABB, d: Vec2Value): boolean {
198+
moveProxy(id: number, aabb: AABB, displacement: Vec2Value): boolean {
184199
_ASSERT && console.assert(AABB.isValid(aabb));
185-
_ASSERT && console.assert(!d || Vec2.isValid(d));
200+
_ASSERT && console.assert(!displacement || Vec2.isValid(displacement));
186201

187202
const node = this.m_nodes[id];
188203

189204
_ASSERT && console.assert(!!node);
190205
_ASSERT && console.assert(node.isLeaf());
191206

192-
if (node.aabb.contains(aabb)) {
193-
return false;
194-
}
195-
196-
this.removeLeaf(node);
197-
198-
node.aabb.set(aabb);
199-
200207
// Extend AABB.
201-
aabb = node.aabb;
202-
AABB.extend(aabb, Settings.aabbExtension);
208+
const fatAABB = new AABB()
209+
fatAABB.set(aabb);
210+
AABB.extend(fatAABB, Settings.aabbExtension);
203211

204-
// Predict AABB displacement.
212+
// Predict AABB movement
205213
// const d = Vec2.mul(Settings.aabbMultiplier, displacement);
206214

207-
if (d.x < 0.0) {
208-
aabb.lowerBound.x += d.x * Settings.aabbMultiplier;
215+
if (displacement.x < 0.0) {
216+
fatAABB.lowerBound.x += displacement.x * Settings.aabbMultiplier;
209217
} else {
210-
aabb.upperBound.x += d.x * Settings.aabbMultiplier;
218+
fatAABB.upperBound.x += displacement.x * Settings.aabbMultiplier;
211219
}
212220

213-
if (d.y < 0.0) {
214-
aabb.lowerBound.y += d.y * Settings.aabbMultiplier;
221+
if (displacement.y < 0.0) {
222+
fatAABB.lowerBound.y += displacement.y * Settings.aabbMultiplier;
215223
} else {
216-
aabb.upperBound.y += d.y * Settings.aabbMultiplier;
224+
fatAABB.upperBound.y += displacement.y * Settings.aabbMultiplier;
225+
}
226+
227+
const treeAABB = node.aabb;
228+
if (treeAABB.contains(aabb)) {
229+
// The tree AABB still contains the object, but it might be too large.
230+
// Perhaps the object was moving fast but has since gone to sleep.
231+
// The huge AABB is larger than the new fat AABB.
232+
const hugeAABB = new AABB();
233+
hugeAABB.set(fatAABB);
234+
AABB.extend(hugeAABB, 4.0 * Settings.aabbExtension);
235+
236+
if (hugeAABB.contains(treeAABB)) {
237+
// The tree AABB contains the object AABB and the tree AABB is
238+
// not too large. No tree update needed.
239+
return false;
240+
}
241+
242+
// Otherwise the tree AABB is huge and needs to be shrunk
217243
}
218244

245+
this.removeLeaf(node);
246+
247+
node.aabb = fatAABB;
248+
219249
this.insertLeaf(node);
220250

251+
node.moved = true;
252+
221253
return true;
222254
}
223255

@@ -285,6 +317,7 @@ export class DynamicTree<T> {
285317
newParent.userData = null;
286318
newParent.aabb.combine(leafAABB, sibling.aabb);
287319
newParent.height = sibling.height + 1;
320+
newParent.moved = false;
288321

289322
if (oldParent != null) {
290323
// The sibling was not the root.
@@ -693,6 +726,7 @@ export class DynamicTree<T> {
693726
parent.height = 1 + Math.max(child1.height, child2.height);
694727
parent.aabb.combine(child1.aabb, child2.aabb);
695728
parent.parent = null;
729+
parent.moved = false;
696730

697731
child1.parent = parent;
698732
child2.parent = parent;

src/dynamics/Body.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -595,11 +595,18 @@ export class Body {
595595
* Update fixtures in broad-phase.
596596
*/
597597
synchronizeFixtures(): void {
598-
this.m_sweep.getTransform(xf, 0);
599-
600598
const broadPhase = this.m_world.m_broadPhase;
601-
for (let f = this.m_fixtureList; f; f = f.m_next) {
602-
f.synchronize(broadPhase, xf, this.m_xf);
599+
600+
if (this.m_awakeFlag) {
601+
this.m_sweep.getTransform(xf, 0);
602+
603+
for (let f = this.m_fixtureList; f; f = f.m_next) {
604+
f.synchronize(broadPhase, xf, this.m_xf);
605+
}
606+
} else {
607+
for (let f = this.m_fixtureList; f; f = f.m_next) {
608+
f.synchronize(broadPhase, this.m_xf, this.m_xf);
609+
}
603610
}
604611
}
605612

src/dynamics/Fixture.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ export class Fixture {
405405

406406
proxy.aabb.combine(synchronize_aabb1, synchronize_aabb2);
407407

408-
matrix.diffVec2(displacement, xf2.p, xf1.p);
408+
matrix.diffVec2(displacement, synchronize_aabb2.getCenter(), synchronize_aabb1.getCenter());
409409

410410
broadPhase.moveProxy(proxy.proxyId, proxy.aabb, displacement);
411411
}

0 commit comments

Comments
 (0)