2 * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
4 * Permission to use, copy, modify, distribute and sell this software
5 * and its documentation for any purpose is hereby granted without fee,
6 * provided that the above copyright notice appear in all copies.
7 * Erin Catto makes no representations about the suitability
8 * of this software for any purpose.
9 * It is provided "as is" without express or implied warranty.
11 * Changes by Ketmar // Invisible Vector
12 * basically, i replaced boxes with convex polygon bodies, and added
13 * universal SAT solver, based on Randy Gaul code
18 public import iv
.vmath
;
20 version = b2dlite_use_bvh
;
21 version = b2dlite_bvh_many_asserts
;
24 // ////////////////////////////////////////////////////////////////////////// //
25 public alias Vec2
= VecN
!(2, float);
26 public alias VFloat
= Vec2
.VFloat
;
27 public alias VFloatNum
= Vec2
.VFloatNum
;
30 // ////////////////////////////////////////////////////////////////////////// //
31 public struct BodyContact
{
37 public void delegate (in ref BodyContact ctx
) b2dlDrawContactsCB
;
40 // ////////////////////////////////////////////////////////////////////////// //
49 import core
.stdc
.math
: cosf
, sinf
;
50 immutable VFloat c
= cosf(angle
), s
= sinf(angle
);
51 col1
.x
= c
; col1
.y
= s
;
52 col2
.x
= -s
; col2
.y
= c
;
56 this() (in auto ref Vec2 acol1
, in auto ref Vec2 acol2
) { pragma(inline
, true); col1
= acol1
; col2
= acol2
; }
58 Mat22
transpose () const { pragma(inline
, true); return Mat22(Vec2(col1
.x
, col2
.x
), Vec2(col1
.y
, col2
.y
)); }
60 Mat22
invert () const {
62 immutable VFloat a
= col1
.x
, b
= col2
.x
, c
= col1
.y
, d
= col2
.y
;
65 assert(det
!= VFloatNum
!0);
66 det
= VFloatNum
!1/det
;
74 Vec2
opBinary(string op
:"*") (in auto ref Vec2 v
) const { pragma(inline
, true); return Vec2(col1
.x
*v
.x
+col2
.x
*v
.y
, col1
.y
*v
.x
+col2
.y
*v
.y
); }
76 Mat22
opBinary(string op
:"+") (in auto ref Mat22 bm
) const { pragma(inline
, true); return Mat22(col1
+bm
.col1
, col2
+bm
.col2
); }
77 Mat22
opBinary(string op
:"*") (in auto ref Mat22 bm
) const { pragma(inline
, true); return Mat22(this*bm
.col1
, this*bm
.col2
); }
79 Mat22
abs() () { pragma(inline
, true); return Mat22(col1
.abs
, col2
.abs
); }
83 // ////////////////////////////////////////////////////////////////////////// //
84 private Vec2
fcross() (VFloat s
, in auto ref Vec2 a
) { pragma(inline
, true); return Vec2(-s
*a
.y
, s
*a
.x
); }
87 // ////////////////////////////////////////////////////////////////////////// //
89 public final class World
{
91 static struct ArbiterKey
{
92 BodyBase body0
, body1
;
94 this (BodyBase b0
, BodyBase b1
) { if (b0
< b1
) { body0
= b0
; body1
= b1
; } else { body0
= b1
; body1
= b0
; } }
96 bool opEquals() (in auto ref ArbiterKey b
) const {
98 return (body0
== b
.body0
&& body1
== b
.body1
);
101 int opCmp() (in auto ref ArbiterKey b
) const {
103 if (b
.body0
!is null) return -1;
104 if (body1
is null) return (b
.body1
!is null ?
-1 : 0);
105 return body1
.opCmp(b
.body1
);
107 if (int r
= body0
.opCmp(b
.body0
)) return r
;
108 if (body1
is null) return (b
.body1
!is null ?
-1 : 0);
109 return body1
.opCmp(b
.body1
);
113 return hashu32((body0
!is null ? body0
.midx
: 0))^
hashu32((body1
!is null ? body1
.midx
: 0));
117 uint hashu32 (uint a
) pure nothrow @safe @nogc {
130 version(b2dlite_use_bvh
) {
139 Arbiter
[ArbiterKey
] arbiters
;
143 // the following are world options
144 static bool accumulateImpulses
= true;
145 static bool warmStarting
= true;
146 static bool positionCorrection
= true;
149 this() (in auto ref Vec2 agravity
, int aiterations
) {
151 iterations
= aiterations
;
152 version(b2dlite_use_bvh
) bvh
= new DynamicAABBTree(VFloatNum
!0.2); // gap
155 void add (BodyBase bbody
) {
156 if (bbody
!is null) {
158 version(b2dlite_use_bvh
) bbody
.nodeId
= bvh
.addObject(bbody
);
162 void add (Joint joint
) {
163 if (joint
!is null) joints
~= joint
;
166 void opOpAssign(string op
:"~", T
) (T v
) if (is(T
: BodyBase
) ||
is(T
: Joint
)) {
174 version(b2dlite_use_bvh
) bvh
.reset();
177 void step (VFloat
dt) {
178 if (dt <= VFloatNum
!0) return;
179 ++frameNum
; // new frame (used to track arbiters)
180 VFloat invDt
= (dt > VFloatNum
!0 ? VFloatNum
!1/dt : VFloatNum
!0);
181 // determine overlapping bodies and update contact points
184 foreach (BodyBase b
; bodies
) {
185 if (b
.invMass
== VFloatNum
!0) continue;
186 b
.velocity
+= (gravity
+b
.force
*b
.invMass
)*dt;
187 b
.angularVelocity
+= dt*b
.invI
*b
.torque
;
190 foreach (ref Arbiter arb
; arbiters
.byValue
) {
191 if (arb
.frameNum
== frameNum
&& arb
.active
) arb
.preStep(invDt
);
193 foreach (Joint j
; joints
) j
.preStep(invDt
);
194 // perform iterations
195 foreach (immutable itnum
; 0..iterations
) {
196 foreach (ref Arbiter arb
; arbiters
.byValue
) {
197 if (arb
.frameNum
== frameNum
&& arb
.active
) arb
.applyImpulse();
199 foreach (Joint j
; joints
) j
.applyImpulse();
201 // integrate velocities
202 foreach (BodyBase b
; bodies
) {
203 auto disp
= b
.velocity
*dt;
204 b
.position
+= b
.velocity
*dt;
205 b
.rotation
+= b
.angularVelocity
*dt;
206 b
.force
.set(VFloatNum
!0, VFloatNum
!0);
207 b
.torque
= VFloatNum
!0;
208 version(b2dlite_use_bvh
) bvh
.updateObject(b
.nodeId
, disp
);
213 version(b2dlite_use_bvh
) {
214 foreach (immutable idx
, BodyBase bi
; bodies
) {
215 auto aabb
= bi
.getAABB();
216 bvh
.reportAllShapesOverlappingWithAABB(aabb
, (int nodeId
) {
217 auto bj
= bvh
.getNodeBody(nodeId
);
218 if (bj
== bi
) return;
219 if (bi
.invMass
== VFloatNum
!0 && bj
.invMass
== VFloatNum
!0) return;
220 auto ak
= ArbiterKey(bi
, bj
);
221 if (auto arb
= ak
in arbiters
) {
222 if (arb
.frameNum
!= frameNum
) arb
.setup(bi
, bj
, frameNum
);
224 arbiters
[ak
] = Arbiter(bi
, bj
, frameNum
);
229 // O(n^2) broad-phase
230 foreach (immutable idx
, BodyBase bi
; bodies
) {
231 foreach (BodyBase bj
; bodies
[idx
+1..$]) {
232 if (bi
.invMass
== VFloatNum
!0 && bj
.invMass
== VFloatNum
!0) continue;
233 if (auto arb
= ArbiterKey(bi
, bj
) in arbiters
) {
234 arb
.setup(bi
, bj
, frameNum
);
236 arbiters
[ArbiterKey(bi
, bj
)] = Arbiter(bi
, bj
, frameNum
);
243 void drawBVH (scope void delegate (Vec2 min
, Vec2 max
) dg
) {
244 version(b2dlite_use_bvh
) {
245 bvh
.forEachLeaf((int nodeId
, in ref AABB aabb
) {
246 dg(aabb
.mMin
, aabb
.mMax
);
253 // ////////////////////////////////////////////////////////////////////////// //
255 public abstract class BodyBase
{
257 static shared uint lastidx
;
261 Mat22 rmattmp
; // undefined most of the time; used only in coldet for now
262 int nodeId
; // in bvh
269 VFloat angularVelocity
;
275 VFloat mass
, invMass
;
280 import core
.atomic
: atomicOp
;
281 midx
= atomicOp
!"+="(lastidx
, 1);
283 position
.set(VFloatNum
!0, VFloatNum
!0);
284 rotation
= VFloatNum
!0;
285 velocity
.set(VFloatNum
!0, VFloatNum
!0);
286 angularVelocity
= VFloatNum
!0;
287 force
.set(VFloatNum
!0, VFloatNum
!0);
288 torque
= VFloatNum
!0;
289 friction
= VFloatNum
!(0.2);
291 invMass
= VFloatNum
!0;
296 @property uint id () const pure nothrow @safe @nogc { pragma(inline
, true); return midx
; }
298 void addForce() (in auto ref Vec2 f
) { pragma(inline
, true); force
+= f
; }
300 override bool opEquals (Object b
) const pure nothrow @trusted @nogc {
301 //pragma(inline, true);
302 if (b
is null) return false;
303 if (b
is this) return true;
304 if (auto bb
= cast(BodyBase
)b
) return (bb
.midx
== midx
);
308 override int opCmp (Object b
) const pure nothrow @trusted @nogc {
309 //pragma(inline, true);
310 if (b
is null) return 1;
311 if (b
is this) return 0;
312 if (auto bb
= cast(BodyBase
)b
) return (midx
< bb
.midx ?
-1 : midx
> bb
.midx ?
1 : 0);
316 override size_t
toHash () nothrow @safe @nogc {
317 return hashu32(midx
);
320 abstract AABB
getAABB ();
323 uint hashu32 (uint a
) pure nothrow @safe @nogc {
336 // ////////////////////////////////////////////////////////////////////////// //
337 public final class PolyBody
: BodyBase
{
339 Vec2
[] verts
; // vertices
340 Vec2
[] norms
; // normals
345 computeMass(VFloatNum
!1);
348 static PolyBody
Box() (in auto ref Vec2 width
, VFloat density
) {
349 auto res
= new PolyBody();
351 res
.computeMass(density
);
355 void set() (const(Vec2
)[] averts
, VFloat m
) {
356 position
.set(VFloatNum
!0, VFloatNum
!0);
357 rotation
= VFloatNum
!0;
358 velocity
.set(VFloatNum
!0, VFloatNum
!0);
359 angularVelocity
= VFloatNum
!0;
360 force
.set(VFloatNum
!0, VFloatNum
!0);
361 torque
= VFloatNum
!0;
362 friction
= VFloatNum
!(0.2);
367 void computeMass(bool densityIsMass
=false) (VFloat density
) {
368 // calculate centroid and moment of interia
369 auto c
= Vec2(0, 0); // centroid
372 enum k_inv3
= VFloatNum
!1/VFloatNum
!3;
374 foreach (immutable i1
; 0..verts
.length
) {
375 // triangle vertices, third vertex implied as (0, 0)
377 auto i2
= (i1
+1)%verts
.length
;
380 VFloat D
= p1
.cross(p2
);
381 VFloat triangleArea
= VFloatNum
!(0.5)*D
;
383 area
+= triangleArea
;
385 // use area to weight the centroid average, not just vertex position
386 c
+= triangleArea
*k_inv3
*(p1
+p2
);
388 VFloat intx2
= p1
.x
*p1
.x
+p2
.x
*p1
.x
+p2
.x
*p2
.x
;
389 VFloat inty2
= p1
.y
*p1
.y
+p2
.y
*p1
.y
+p2
.y
*p2
.y
;
390 I
+= (VFloatNum
!(0.25)*k_inv3
*D
)*(intx2
+inty2
);
393 c
*= VFloatNum
!1/area
;
395 // translate vertices to centroid (make the centroid (0, 0) for the polygon in model space)
396 // not really necessary, but I like doing this anyway
397 foreach (ref v
; verts
) v
-= c
;
399 if (area
> 0 && density
< VFloat
.max
) {
401 invMass
= VFloatNum
!1/mass
;
402 //i = mass*(width.x*width.x+width.y*width.y)/VFloatNum!12;
404 invI
= VFloatNum
!1/i
;
407 invMass
= VFloatNum
!0;
414 void setBox (Vec2 size
) {
417 verts
~= Vec2(-size
.x
, -size
.y
);
418 verts
~= Vec2( size
.x
, -size
.y
);
419 verts
~= Vec2( size
.x
, size
.y
);
420 verts
~= Vec2(-size
.x
, size
.y
);
422 norms
~= Vec2(VFloatNum
!( 0), VFloatNum
!(-1));
423 norms
~= Vec2(VFloatNum
!( 1), VFloatNum
!( 0));
424 norms
~= Vec2(VFloatNum
!( 0), VFloatNum
!( 1));
425 norms
~= Vec2(VFloatNum
!(-1), VFloatNum
!( 0));
428 private void setVerts (const(Vec2
)[] averts
) {
429 // no hulls with less than 3 vertices (ensure actual polygon)
430 if (averts
.length
< 3) throw new Exception("degenerate body");
432 // find the right most point on the hull
434 VFloat highestXCoord
= averts
[0].x
;
435 foreach (immutable i
; 1..averts
.length
) {
436 VFloat x
= averts
[i
].x
;
437 if (x
> highestXCoord
) {
439 rightMost
= cast(int)i
;
440 } else if (x
== highestXCoord
) {
441 // if matching x then take farthest negative y
442 if (averts
[i
].y
< averts
[rightMost
].y
) rightMost
= cast(int)i
;
446 auto hull
= new int[](averts
.length
);
448 int indexHull
= rightMost
;
452 hull
[outCount
] = indexHull
;
454 // search for next index that wraps around the hull by computing cross products to
455 // find the most counter-clockwise vertex in the set, given the previos hull index
456 int nextHullIndex
= 0;
457 foreach (immutable i
; 1..averts
.length
) {
458 // skip if same coordinate as we need three unique points in the set to perform a cross product
459 if (nextHullIndex
== indexHull
) {
463 // cross every set of three unique vertices
464 // record each counter clockwise third vertex and add to the output hull
465 // See : http://www.oocities.org/pcgpe/math2d.html
466 auto e1
= averts
[nextHullIndex
]-averts
[hull
[outCount
]];
467 auto e2
= averts
[i
]-averts
[hull
[outCount
]];
468 auto c
= e1
.cross(e2
);
469 if (c
< 0.0f) nextHullIndex
= i
;
471 // cross product is zero then e vectors are on same line
472 // therefore want to record vertex farthest along that line
473 if (c
== 0.0f && e2
.lengthSquared
> e1
.lengthSquared
) nextHullIndex
= i
;
477 indexHull
= nextHullIndex
;
479 // conclude algorithm upon wrap-around
480 if (nextHullIndex
== rightMost
) {
485 if (vcount
< 3) throw new Exception("degenerate body");
487 // copy vertices into shape's vertices
488 foreach (immutable i
; 0..vcount
) verts
~= averts
[hull
[i
]];
489 if (!isConvex()) throw new Exception("non-convex body");
491 // compute face normals
492 norms
.reserve(verts
.length
);
493 foreach (immutable i1
; 0..verts
.length
) {
494 immutable i2
= (i1
+1)%verts
.length
;
495 auto face
= verts
[i2
]-verts
[i1
];
496 // ensure no zero-length edges, because that's bad
497 assert(face
.lengthSquared
> FLTEPS
*FLTEPS
);
498 // calculate normal with 2D cross product between vector and scalar
499 norms
~= Vec2(face
.y
, -face
.x
).normalized
;
503 // The extreme point along a direction within a polygon
504 Vec2
getSupport() (in auto ref Vec2 dir
) {
505 VFloat bestProjection
= -VFloat
.max
;
507 foreach (immutable i
; 0..verts
.length
) {
509 VFloat projection
= v
.dot(dir
);
510 if (projection
> bestProjection
) {
512 bestProjection
= projection
;
519 static int sign() (VFloat v
) { pragma(inline
, true); return (v
< 0 ?
-1 : v
> 0 ?
1 : 0); }
520 if (verts
.length
< 3) return false;
521 if (verts
.length
== 3) return true; // nothing to check here
523 foreach (immutable idx
, const ref v
; verts
) {
524 auto v1
= Vec2(verts
[(idx
+1)%verts
.length
])-v
;
525 auto v2
= Vec2(verts
[(idx
+2)%verts
.length
]);
526 int d
= sign(v2
.x
*v1
.y
-v2
.y
*v1
.x
+v1
.x
*v
.y
-v1
.y
*v
.x
);
527 if (d
== 0) return false;
529 if (dir
!= d
) return false;
537 override AABB
getAABB () {
539 res
.mMin
= Vec2(float.max
, float.max
);
540 res
.mMax
= Vec2(-float.max
, -float.max
);
541 auto rmt
= Mat22(rotation
);
542 foreach (const ref vx
; verts
) {
543 import std
.algorithm
: max
, min
;
544 auto vp
= position
+rmt
*vx
;
545 res
.mMin
.x
= min(res
.mMin
.x
, vp
.x
);
546 res
.mMin
.y
= min(res
.mMin
.y
, vp
.y
);
547 res
.mMax
.x
= max(res
.mMax
.x
, vp
.x
);
548 res
.mMax
.y
= max(res
.mMax
.y
, vp
.y
);
555 // ////////////////////////////////////////////////////////////////////////// //
557 public final class Joint
{
560 Vec2 localAnchor1
, localAnchor2
;
563 Vec2 accimp
= Vec2(0, 0); // accumulated impulse
566 VFloat biasFactor
= VFloatNum
!(0.2);
567 VFloat softness
= VFloatNum
!0;
570 void set() (BodyBase b0
, BodyBase b1
, in auto ref Vec2 anchor
) {
574 auto rot1
= Mat22(body0
.rotation
);
575 auto rot2
= Mat22(body1
.rotation
);
576 Mat22 rot1T
= rot1
.transpose();
577 Mat22 rot2T
= rot2
.transpose();
579 localAnchor1
= rot1T
*(anchor
-body0
.position
);
580 localAnchor2
= rot2T
*(anchor
-body1
.position
);
582 accimp
.set(VFloatNum
!0, VFloatNum
!0);
584 softness
= VFloatNum
!0;
585 biasFactor
= VFloatNum
!(0.2);
588 void preStep (VFloat invDt
) {
589 // pre-compute anchors, mass matrix, and bias
590 auto rot1
= Mat22(body0
.rotation
);
591 auto rot2
= Mat22(body1
.rotation
);
593 r1
= rot1
*localAnchor1
;
594 r2
= rot2
*localAnchor2
;
596 // deltaV = deltaV0+kmt*impulse
597 // invM = [(1/m1+1/m2)*eye(2)-skew(r1)*invI1*skew(r1)-skew(r2)*invI2*skew(r2)]
598 // = [1/m1+1/m2 0 ]+invI1*[r1.y*r1.y -r1.x*r1.y]+invI2*[r1.y*r1.y -r1.x*r1.y]
599 // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
601 k1
.col1
.x
= body0
.invMass
+body1
.invMass
; k1
.col2
.x
= VFloatNum
!0;
602 k1
.col1
.y
= VFloatNum
!0; k1
.col2
.y
= body0
.invMass
+body1
.invMass
;
605 k2
.col1
.x
= body0
.invI
*r1
.y
*r1
.y
; k2
.col2
.x
= -body0
.invI
*r1
.x
*r1
.y
;
606 k2
.col1
.y
= -body0
.invI
*r1
.x
*r1
.y
; k2
.col2
.y
= body0
.invI
*r1
.x
*r1
.x
;
609 k3
.col1
.x
= body1
.invI
*r2
.y
*r2
.y
; k3
.col2
.x
= -body1
.invI
*r2
.x
*r2
.y
;
610 k3
.col1
.y
= -body1
.invI
*r2
.x
*r2
.y
; k3
.col2
.y
= body1
.invI
*r2
.x
*r2
.x
;
612 Mat22 kmt
= k1
+k2
+k3
;
613 kmt
.col1
.x
+= softness
;
614 kmt
.col2
.y
+= softness
;
618 Vec2 p1
= body0
.position
+r1
;
619 Vec2 p2
= body1
.position
+r2
;
622 if (World
.positionCorrection
) {
623 bias
= -biasFactor
*invDt
*dp
;
625 bias
.set(VFloatNum
!0, VFloatNum
!0);
628 if (World
.warmStarting
) {
629 // apply accumulated impulse
630 body0
.velocity
-= body0
.invMass
*accimp
;
631 body0
.angularVelocity
-= body0
.invI
*r1
.cross(accimp
);
633 body1
.velocity
+= body1
.invMass
*accimp
;
634 body1
.angularVelocity
+= body1
.invI
*r2
.cross(accimp
);
636 accimp
.set(VFloatNum
!0, VFloatNum
!0);
640 void applyImpulse () {
641 Vec2 dv
= body1
.velocity
+body1
.angularVelocity
.fcross(r2
)-body0
.velocity
-body0
.angularVelocity
.fcross(r1
);
642 Vec2 impulse
= mt
*(bias
-dv
-softness
*accimp
);
644 body0
.velocity
-= body0
.invMass
*impulse
;
645 body0
.angularVelocity
-= body0
.invI
*r1
.cross(impulse
);
647 body1
.velocity
+= body1
.invMass
*impulse
;
648 body1
.angularVelocity
+= body1
.invI
*r2
.cross(impulse
);
655 // ////////////////////////////////////////////////////////////////////////// //
656 private struct Contact
{
658 Vec2 position
; // updated in collide()
659 Vec2 normal
; // updated in collide()
661 VFloat separation
= VFloatNum
!0; // updated in collide(), negative of penetration
662 VFloat accimpN
= VFloatNum
!0; // accumulated normal impulse
663 VFloat accimpT
= VFloatNum
!0; // accumulated tangent impulse
664 VFloat accimpNb
= VFloatNum
!0; // accumulated normal impulse for position bias
665 VFloat massNormal
, massTangent
;
666 VFloat bias
= VFloatNum
!0;
670 // ////////////////////////////////////////////////////////////////////////// //
671 private struct Arbiter
{
673 enum MaxContactPoints
= 2;
675 static private VFloat
clamp() (VFloat a
, VFloat low
, VFloat high
) { pragma(inline
, true); import std
.algorithm
: min
, max
; return max(low
, min(a
, high
)); }
678 Contact
[MaxContactPoints
] contacts
;
681 BodyBase body0
, body1
;
682 VFloat friction
; // combined friction
683 uint frameNum
; // used to track "frame touch"
686 this (BodyBase b0
, BodyBase b1
, int aFrameNum
) { setup(b0
, b1
, frameNum
); }
688 @disable this (this);
690 void clear () { numContacts
= 0; }
692 @property bool active () { return (numContacts
> 0); }
694 void setup (BodyBase b0
, BodyBase b1
, int aFrameNum
) {
695 frameNum
= aFrameNum
;
696 import core
.stdc
.math
: sqrtf
;
704 numContacts
= collide(contacts
[], body0
, body1
);
705 friction
= sqrtf(body0
.friction
*body1
.friction
);
706 if (b2dlDrawContactsCB
!is null) {
708 foreach (const ref ctx
; contacts
[0..numContacts
]) {
709 bc
.position
= ctx
.position
;
710 bc
.normal
= ctx
.normal
;
711 bc
.separation
= ctx
.separation
;
712 b2dlDrawContactsCB(bc
);
717 void preStep (VFloat invDt
) {
718 import std
.algorithm
: min
;
719 enum AllowedPenetration
= VFloatNum
!(0.01);
720 immutable VFloat biasFactor
= (World
.positionCorrection ? VFloatNum
!(0.2) : VFloatNum
!0);
721 foreach (immutable idx
; 0..numContacts
) {
722 Contact
*c
= contacts
.ptr
+idx
;
723 Vec2 r1
= c
.position
-body0
.position
;
724 Vec2 r2
= c
.position
-body1
.position
;
726 // precompute normal mass, tangent mass, and bias
727 VFloat rn1
= r1
*c
.normal
; //Dot(r1, c.normal);
728 VFloat rn2
= r2
*c
.normal
; //Dot(r2, c.normal);
729 VFloat kNormal
= body0
.invMass
+body1
.invMass
;
730 //kNormal += body0.invI*(Dot(r1, r1)-rn1*rn1)+body1.invI*(Dot(r2, r2)-rn2*rn2);
731 kNormal
+= body0
.invI
*((r1
*r1
)-rn1
*rn1
)+body1
.invI
*((r2
*r2
)-rn2
*rn2
);
732 c
.massNormal
= VFloatNum
!1/kNormal
;
734 //Vec2 tangent = c.normal.cross(VFloatNum!1);
735 Vec2 tangent
= Vec2(VFloatNum
!1*c
.normal
.y
, -VFloatNum
!1*c
.normal
.x
);
736 VFloat rt1
= r1
*tangent
; //Dot(r1, tangent);
737 VFloat rt2
= r2
*tangent
; //Dot(r2, tangent);
738 VFloat kTangent
= body0
.invMass
+body1
.invMass
;
739 //kTangent += body0.invI*(Dot(r1, r1)-rt1*rt1)+body1.invI*(Dot(r2, r2)-rt2*rt2);
740 kTangent
+= body0
.invI
*((r1
*r1
)-rt1
*rt1
)+body1
.invI
*((r2
*r2
)-rt2
*rt2
);
741 c
.massTangent
= VFloatNum
!1/kTangent
;
743 c
.bias
= -biasFactor
*invDt
*min(VFloatNum
!0, c
.separation
+AllowedPenetration
);
745 if (World
.accumulateImpulses
) {
746 // apply normal + friction impulse
747 Vec2 accimp
= c
.accimpN
*c
.normal
+c
.accimpT
*tangent
;
749 body0
.velocity
-= body0
.invMass
*accimp
;
750 body0
.angularVelocity
-= body0
.invI
*r1
.cross(accimp
);
752 body1
.velocity
+= body1
.invMass
*accimp
;
753 body1
.angularVelocity
+= body1
.invI
*r2
.cross(accimp
);
758 void applyImpulse () {
759 import std
.algorithm
: max
;
762 foreach (immutable idx
; 0..numContacts
) {
763 Contact
*c
= contacts
.ptr
+idx
;
764 c
.r1
= c
.position
-b0
.position
;
765 c
.r2
= c
.position
-b1
.position
;
767 // relative velocity at contact
768 Vec2 dv
= b1
.velocity
+b1
.angularVelocity
.fcross(c
.r2
)-b0
.velocity
-b0
.angularVelocity
.fcross(c
.r1
);
770 // compute normal impulse
771 VFloat vn
= dv
*c
.normal
; //Dot(dv, c.normal);
773 VFloat dPn
= c
.massNormal
*(-vn
+c
.bias
);
775 if (World
.accumulateImpulses
) {
776 // clamp the accumulated impulse
777 VFloat accimpN0
= c
.accimpN
;
778 c
.accimpN
= max(accimpN0
+dPn
, VFloatNum
!0);
779 dPn
= c
.accimpN
-accimpN0
;
781 dPn
= max(dPn
, VFloatNum
!0);
784 // apply contact impulse
785 Vec2 accimpN
= dPn
*c
.normal
;
787 b0
.velocity
-= b0
.invMass
*accimpN
;
788 b0
.angularVelocity
-= b0
.invI
*c
.r1
.cross(accimpN
);
790 b1
.velocity
+= b1
.invMass
*accimpN
;
791 b1
.angularVelocity
+= b1
.invI
*c
.r2
.cross(accimpN
);
793 // relative velocity at contact
794 dv
= b1
.velocity
+b1
.angularVelocity
.fcross(c
.r2
)-b0
.velocity
-b0
.angularVelocity
.fcross(c
.r1
);
796 //Vec2 tangent = c.normal.cross(VFloatNum!1);
797 Vec2 tangent
= Vec2(VFloatNum
!1*c
.normal
.y
, -VFloatNum
!1*c
.normal
.x
);
798 VFloat vt
= dv
*tangent
; //Dot(dv, tangent);
799 VFloat dPt
= c
.massTangent
*(-vt
);
801 if (World
.accumulateImpulses
) {
802 // compute friction impulse
803 VFloat maxPt
= friction
*c
.accimpN
;
805 VFloat oldTangentImpulse
= c
.accimpT
;
806 c
.accimpT
= clamp(oldTangentImpulse
+dPt
, -maxPt
, maxPt
);
807 dPt
= c
.accimpT
-oldTangentImpulse
;
809 VFloat maxPt
= friction
*dPn
;
810 dPt
= clamp(dPt
, -maxPt
, maxPt
);
813 // apply contact impulse
814 Vec2 accimpT
= dPt
*tangent
;
816 b0
.velocity
-= b0
.invMass
*accimpT
;
817 b0
.angularVelocity
-= b0
.invI
*c
.r1
.cross(accimpT
);
819 b1
.velocity
+= b1
.invMass
*accimpT
;
820 b1
.angularVelocity
+= b1
.invI
*c
.r2
.cross(accimpT
);
826 // ////////////////////////////////////////////////////////////////////////// //
828 // the normal points from A to B
829 // return number of contact points
831 // position (already moved out of body)
833 // separation (this is negative penetration)
834 // feature (used only in arbiter updates to merge contacts, and has no effect in b2dlite)
835 // return number of contacts
836 private int collide (Contact
[] contacts
, BodyBase bodyAb
, BodyBase bodyBb
) {
837 auto pb0
= cast(PolyBody
)bodyAb
;
838 auto pb1
= cast(PolyBody
)bodyBb
;
839 if (pb0
is null || pb1
is null) assert(0);
842 polygon2Polygon(ci
, pb0
, pb1
);
843 if (!ci
.valid
) return 0; // no contacts
845 foreach (immutable cidx
; 0..ci
.contactCount
) {
846 contacts
[cidx
].normal
= ci
.normal
;
847 contacts
[cidx
].separation
= -ci
.penetration
;
848 contacts
[cidx
].position
= ci
.contacts
[cidx
]+(ci
.normal
*ci
.penetration
);
851 return ci
.contactCount
;
855 // ////////////////////////////////////////////////////////////////////////// //
856 private struct ContactInfo
{
857 VFloat penetration
; // depth of penetration from collision
858 Vec2 normal
; // from A to B
859 Vec2
[2] contacts
; // points of contact during collision
860 uint contactCount
; // number of contacts that occured during collision
862 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (contactCount
> 0); }
866 private bool biasGreaterThan (VFloat a
, VFloat b
) {
867 enum k_biasRelative
= VFloatNum
!(0.95);
868 enum k_biasAbsolute
= VFloatNum
!(0.01);
869 return (a
>= b
*k_biasRelative
+a
*k_biasAbsolute
);
873 private VFloat
findAxisLeastPenetration (uint *faceIndex
, PolyBody A
, PolyBody B
) {
874 VFloat bestDistance
= -VFloat
.max
;
876 foreach (immutable i
; 0..A
.verts
.length
) {
877 // retrieve a face normal from A
879 Vec2 nw
= A
.rmattmp
*n
;
881 // rransform face normal into B's model space
882 Mat22 buT
= B
.rmattmp
.transpose();
885 // retrieve support point from B along -n
886 Vec2 s
= B
.getSupport(-n
);
888 // retrieve vertex on face from A, transform into B's model space
890 v
= A
.rmattmp
*v
+A
.position
;
894 // compute penetration distance (in B's model space)
895 VFloat d
= n
.dot(s
-v
);
897 // store greatest distance
898 if (d
> bestDistance
) {
904 *faceIndex
= bestIndex
;
909 private void findIncidentFace (Vec2
[] v
, PolyBody RefPoly
, PolyBody IncPoly
, uint referenceIndex
) {
910 Vec2 referenceNormal
= RefPoly
.norms
[referenceIndex
];
912 // calculate normal in incident's frame of reference
913 referenceNormal
= RefPoly
.rmattmp
*referenceNormal
; // to world space
914 referenceNormal
= IncPoly
.rmattmp
.transpose()*referenceNormal
; // to incident's model space
916 // find most anti-normal face on incident polygon
917 uint incidentFace
= 0;
918 VFloat minDot
= VFloat
.max
;
919 foreach (immutable i
; 0..IncPoly
.verts
.length
) {
920 VFloat dot
= referenceNormal
.dot(IncPoly
.norms
[i
]);
927 // assign face vertices for incidentFace
928 v
[0] = IncPoly
.rmattmp
*IncPoly
.verts
[incidentFace
]+IncPoly
.position
;
929 incidentFace
= (incidentFace
+1)%IncPoly
.verts
.length
;
930 v
[1] = IncPoly
.rmattmp
*IncPoly
.verts
[incidentFace
]+IncPoly
.position
;
934 private uint clip (Vec2 n
, VFloat c
, Vec2
[] face
) {
936 Vec2
[2] outv
= face
[0..2];
938 // retrieve distances from each endpoint to the line
940 VFloat d1
= n
.dot(face
[0])-c
;
941 VFloat d2
= n
.dot(face
[1])-c
;
943 // if negative (behind plane) clip
944 if (d1
<= VFloatNum
!0) outv
[sp
++] = face
[0];
945 if (d2
<= VFloatNum
!0) outv
[sp
++] = face
[1];
947 // if the points are on different sides of the plane
948 if (d1
*d2
< VFloatNum
!0) { // less than to ignore -0.0f
949 // push interesection point
950 VFloat alpha
= d1
/(d1
-d2
);
951 outv
[sp
] = face
[0]+alpha
*(face
[1]-face
[0]);
955 // assign our new converted values
965 private void polygon2Polygon (ref ContactInfo m
, PolyBody A
, PolyBody B
) {
966 A
.rmattmp
= Mat22(A
.rotation
);
967 B
.rmattmp
= Mat22(B
.rotation
);
971 // check for a separating axis with A's face planes
973 VFloat penetrationA
= findAxisLeastPenetration(&faceA
, A
, B
);
974 if (penetrationA
>= VFloatNum
!0) return;
976 // check for a separating axis with B's face planes
978 VFloat penetrationB
= findAxisLeastPenetration(&faceB
, B
, A
);
979 if (penetrationB
>= VFloatNum
!0) return;
982 bool flip
; // Always point from a to b
984 PolyBody RefPoly
; // Reference
985 PolyBody IncPoly
; // Incident
987 // determine which shape contains reference face
988 if (biasGreaterThan(penetrationA
, penetrationB
)) {
991 referenceIndex
= faceA
;
996 referenceIndex
= faceB
;
1000 // world space incident face
1001 Vec2
[2] incidentFace
;
1002 findIncidentFace(incidentFace
[], RefPoly
, IncPoly
, referenceIndex
);
1006 // +---c ------posPlane--
1008 // +---+ c-----negPlane--
1012 // r : reference face
1013 // i : incident poly
1014 // c : clipped point
1015 // n : incident normal
1017 // setup reference face vertices
1018 Vec2 v1
= RefPoly
.verts
[referenceIndex
];
1019 referenceIndex
= (referenceIndex
+1)%RefPoly
.verts
.length
;
1020 Vec2 v2
= RefPoly
.verts
[referenceIndex
];
1022 // transform vertices to world space
1023 v1
= RefPoly
.rmattmp
*v1
+RefPoly
.position
;
1024 v2
= RefPoly
.rmattmp
*v2
+RefPoly
.position
;
1026 // calculate reference face side normal in world space
1027 Vec2 sidePlaneNormal
= v2
-v1
;
1028 sidePlaneNormal
.normalize
;
1031 auto refFaceNormal
= Vec2(sidePlaneNormal
.y
, -sidePlaneNormal
.x
);
1034 // c is distance from origin
1035 VFloat refC
= refFaceNormal
.dot(v1
);
1036 VFloat negSide
= -sidePlaneNormal
.dot(v1
);
1037 VFloat posSide
= sidePlaneNormal
.dot(v2
);
1039 // clip incident face to reference face side planes
1040 if (clip(-sidePlaneNormal
, negSide
, incidentFace
) < 2) return; // due to floating point error, possible to not have required points
1041 if (clip(sidePlaneNormal
, posSide
, incidentFace
) < 2) return; // due to floating point error, possible to not have required points
1044 m
.normal
= (flip ?
-refFaceNormal
: refFaceNormal
);
1046 // keep points behind reference face
1047 uint cp
= 0; // clipped points behind reference face
1048 VFloat separation
= refFaceNormal
.dot(incidentFace
[0])-refC
;
1049 if (separation
<= VFloatNum
!0) {
1050 m
.contacts
[cp
] = incidentFace
[0];
1051 m
.penetration
= -separation
;
1057 separation
= refFaceNormal
.dot(incidentFace
[1])-refC
;
1058 if (separation
<= VFloatNum
!0) {
1059 m
.contacts
[cp
] = incidentFace
[1];
1060 m
.penetration
+= -separation
;
1062 // average penetration
1063 m
.penetration
/= cast(VFloat
)cp
;
1066 m
.contactCount
= cp
;
1070 // ////////////////////////////////////////////////////////////////////////// //
1071 /* Dynamic AABB tree (bounding volume hierarchy)
1072 * based on the code from ReactPhysics3D physics library, http://www.reactphysics3d.com
1073 * Copyright (c) 2010-2016 Daniel Chappuis
1075 * This software is provided 'as-is', without any express or implied warranty.
1076 * In no event will the authors be held liable for any damages arising from the
1077 * use of this software.
1079 * Permission is granted to anyone to use this software for any purpose,
1080 * including commercial applications, and to alter it and redistribute it
1081 * freely, subject to the following restrictions:
1083 * 1. The origin of this software must not be misrepresented; you must not claim
1084 * that you wrote the original software. If you use this software in a
1085 * product, an acknowledgment in the product documentation would be
1086 * appreciated but is not required.
1088 * 2. Altered source versions must be plainly marked as such, and must not be
1089 * misrepresented as being the original software.
1091 * 3. This notice may not be removed or altered from any source distribution.
1093 private struct AABBBase(VT
) if (Vec2
.isVector
!VT
) {
1100 public pure nothrow @safe @nogc:
1101 /*const ref*/ VT
getMin () const { pragma(inline
, true); return mMin
; }
1102 /*const ref*/ VT
getMax () const { pragma(inline
, true); return mMax
; }
1104 void setMin (VT v
) { pragma(inline
, true); mMin
= v
; }
1105 void setMax (VT v
) { pragma(inline
, true); mMax
= v
; }
1107 void setMin (in ref VT v
) { pragma(inline
, true); mMin
= v
; }
1108 void setMax (in ref VT v
) { pragma(inline
, true); mMax
= v
; }
1110 // return the volume of the AABB
1111 @property VFloat
volume () const {
1112 immutable diff
= mMax
-mMin
;
1113 static if (VT
.isVector3
!VT
) {
1114 return diff
.x
*diff
.y
*diff
.z
;
1116 return diff
.x
*diff
.y
;
1120 static auto mergeAABBs (in ref AABB aabb1
, in ref AABB aabb2
) {
1121 import std
.algorithm
: max
, min
;
1123 res
.mMin
.x
= min(aabb1
.mMin
.x
, aabb2
.mMin
.x
);
1124 res
.mMin
.y
= min(aabb1
.mMin
.y
, aabb2
.mMin
.y
);
1125 res
.mMax
.x
= max(aabb1
.mMax
.x
, aabb2
.mMax
.x
);
1126 res
.mMax
.y
= max(aabb1
.mMax
.y
, aabb2
.mMax
.y
);
1127 static if (VT
.isVector3
!VT
) {
1128 res
.mMin
.z
= min(aabb1
.mMin
.z
, aabb2
.mMin
.z
);
1129 res
.mMax
.z
= max(aabb1
.mMax
.z
, aabb2
.mMax
.z
);
1134 void merge (in ref AABB aabb1
, in ref AABB aabb2
) {
1135 import std
.algorithm
: max
, min
;
1136 mMin
.x
= min(aabb1
.mMin
.x
, aabb2
.mMin
.x
);
1137 mMin
.y
= min(aabb1
.mMin
.y
, aabb2
.mMin
.y
);
1138 mMax
.x
= max(aabb1
.mMax
.x
, aabb2
.mMax
.x
);
1139 mMax
.y
= max(aabb1
.mMax
.y
, aabb2
.mMax
.y
);
1140 static if (VT
.isVector3
!VT
) {
1141 mMin
.z
= min(aabb1
.mMin
.z
, aabb2
.mMin
.z
);
1142 mMax
.z
= max(aabb1
.mMax
.z
, aabb2
.mMax
.z
);
1146 // return true if the current AABB contains the AABB given in parameter
1147 bool contains (in ref AABB aabb
) const {
1148 bool isInside
= true;
1149 isInside
= isInside
&& mMin
.x
<= aabb
.mMin
.x
;
1150 isInside
= isInside
&& mMin
.y
<= aabb
.mMin
.y
;
1151 isInside
= isInside
&& mMax
.x
>= aabb
.mMax
.x
;
1152 isInside
= isInside
&& mMax
.y
>= aabb
.mMax
.y
;
1153 static if (VT
.isVector3
!VT
) {
1154 isInside
= isInside
&& mMin
.z
<= aabb
.mMin
.z
;
1155 isInside
= isInside
&& mMax
.z
>= aabb
.mMax
.z
;
1160 // return true if the current AABB is overlapping with the AABB in argument
1161 // two AABBs overlap if they overlap in the three x, y (and z) axes at the same time
1162 bool testCollision (in ref AABB aabb
) const {
1163 if (mMax
.x
< aabb
.mMin
.x || aabb
.mMax
.x
< mMin
.x
) return false;
1164 if (mMax
.y
< aabb
.mMin
.y || aabb
.mMax
.y
< mMin
.y
) return false;
1165 static if (VT
.isVector3
!VT
) {
1166 if (mMax
.z
< aabb
.mMin
.z || aabb
.mMax
.z
< mMin
.z
) return false;
1172 alias AABB
= AABBBase
!Vec2
;
1175 // ////////////////////////////////////////////////////////////////////////// //
1176 private align(1) struct TreeNode
{
1178 enum NullTreeNode
= -1;
1179 enum { Left
= 0, Right
= 1 }
1180 // a node is either in the tree (has a parent) or in the free nodes list (has a next node)
1185 // a node is either a leaf (has data) or is an internal node (has children)
1187 int[2] children
; /// left and right child of the node (children[0] = left child)
1190 // height of the node in the tree
1192 // fat axis aligned bounding box (AABB) corresponding to the node
1194 // return true if the node is a leaf of the tree
1195 @property bool leaf () const pure nothrow @safe @nogc { pragma(inline
, true); return (height
== 0); }
1199 // ////////////////////////////////////////////////////////////////////////// //
1201 * This class implements a dynamic AABB tree that is used for broad-phase
1202 * collision detection. This data structure is inspired by Nathanael Presson's
1203 * dynamic tree implementation in BulletPhysics. The following implementation is
1204 * based on the one from Erin Catto in Box2D as described in the book
1205 * "Introduction to Game Physics with Box2D" by Ian Parberry.
1207 private final class DynamicAABBTree
{
1208 private import std
.algorithm
: max
, min
;
1210 // in the broad-phase collision detection (dynamic AABB tree), the AABBs are
1211 // also inflated in direction of the linear motion of the body by mutliplying the
1212 // followin constant with the linear velocity and the elapsed time between two frames
1213 enum VFloat DynamicBVHLinGapMult
= VFloatNum
!(1.7);
1216 // called when a overlapping node has been found during the call to reportAllShapesOverlappingWithAABB()
1217 alias OverlapCallback
= void delegate (int nodeId
);
1220 TreeNode
* mNodes
; // pointer to the memory location of the nodes of the tree
1221 int mRootNodeId
; // id of the root node of the tree
1222 int mFreeNodeId
; // id of the first node of the list of free (allocated) nodes in the tree that we can use
1223 int mAllocCount
; // number of allocated nodes in the tree
1224 int mNodeCount
; // number of nodes in the tree
1226 // extra AABB Gap used to allow the collision shape to move a little bit
1227 // without triggering a large modification of the tree which can be costly
1230 // allocate and return a node to use in the tree
1231 int allocateNode () {
1232 // if there is no more allocated node to use
1233 if (mFreeNodeId
== TreeNode
.NullTreeNode
) {
1234 import core
.stdc
.stdlib
: realloc
;
1235 version(b2dlite_bvh_many_asserts
) assert(mNodeCount
== mAllocCount
);
1236 // allocate more nodes in the tree
1237 auto newsz
= mAllocCount
*2;
1238 if (newsz
-mAllocCount
> 4096) newsz
= mAllocCount
+4096;
1239 TreeNode
* nn
= cast(TreeNode
*)realloc(mNodes
, newsz
*TreeNode
.sizeof
);
1240 if (nn
is null) assert(0, "out of memory");
1241 mAllocCount
= newsz
;
1243 // initialize the allocated nodes
1244 foreach (int i
; mNodeCount
..mAllocCount
-1) {
1245 mNodes
[i
].nextNodeId
= i
+1;
1246 mNodes
[i
].height
= -1;
1248 mNodes
[mAllocCount
-1].nextNodeId
= TreeNode
.NullTreeNode
;
1249 mNodes
[mAllocCount
-1].height
= -1;
1250 mFreeNodeId
= mNodeCount
;
1252 // get the next free node
1253 int freeNodeId
= mFreeNodeId
;
1254 mFreeNodeId
= mNodes
[freeNodeId
].nextNodeId
;
1255 mNodes
[freeNodeId
].parentId
= TreeNode
.NullTreeNode
;
1256 mNodes
[freeNodeId
].height
= 0;
1262 void releaseNode (int nodeId
) {
1263 version(b2dlite_bvh_many_asserts
) assert(mNodeCount
> 0);
1264 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
1265 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].height
>= 0);
1266 mNodes
[nodeId
].nextNodeId
= mFreeNodeId
;
1267 mNodes
[nodeId
].height
= -1;
1268 mFreeNodeId
= nodeId
;
1272 // insert a leaf node in the tree
1273 // the process of inserting a new leaf node in the dynamic tree is described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
1274 void insertLeafNode (int nodeId
) {
1275 // if the tree is empty
1276 if (mRootNodeId
== TreeNode
.NullTreeNode
) {
1277 mRootNodeId
= nodeId
;
1278 mNodes
[mRootNodeId
].parentId
= TreeNode
.NullTreeNode
;
1282 version(b2dlite_bvh_many_asserts
) assert(mRootNodeId
!= TreeNode
.NullTreeNode
);
1284 // find the best sibling node for the new node
1285 AABB newNodeAABB
= mNodes
[nodeId
].aabb
;
1286 int currentNodeId
= mRootNodeId
;
1287 while (!mNodes
[currentNodeId
].leaf
) {
1288 int leftChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Left
];
1289 int rightChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Right
];
1291 // compute the merged AABB
1292 VFloat volumeAABB
= mNodes
[currentNodeId
].aabb
.volume
;
1293 AABB mergedAABBs
= AABB
.mergeAABBs(mNodes
[currentNodeId
].aabb
, newNodeAABB
);
1294 VFloat mergedVolume
= mergedAABBs
.volume
;
1296 // compute the cost of making the current node the sibbling of the new node
1297 VFloat costS
= VFloatNum
!(2.0)*mergedVolume
;
1299 // compute the minimum cost of pushing the new node further down the tree (inheritance cost)
1300 VFloat costI
= VFloatNum
!(2.0)*(mergedVolume
-volumeAABB
);
1302 // compute the cost of descending into the left child
1304 AABB currentAndLeftAABB
= AABB
.mergeAABBs(newNodeAABB
, mNodes
[leftChild
].aabb
);
1305 if (mNodes
[leftChild
].leaf
) {
1306 costLeft
= currentAndLeftAABB
.volume
+costI
;
1308 VFloat leftChildVolume
= mNodes
[leftChild
].aabb
.volume
;
1309 costLeft
= costI
+currentAndLeftAABB
.volume
-leftChildVolume
;
1312 // compute the cost of descending into the right child
1314 AABB currentAndRightAABB
= AABB
.mergeAABBs(newNodeAABB
, mNodes
[rightChild
].aabb
);
1315 if (mNodes
[rightChild
].leaf
) {
1316 costRight
= currentAndRightAABB
.volume
+costI
;
1318 VFloat rightChildVolume
= mNodes
[rightChild
].aabb
.volume
;
1319 costRight
= costI
+currentAndRightAABB
.volume
-rightChildVolume
;
1322 // if the cost of making the current node a sibbling of the new node is smaller than the cost of going down into the left or right child
1323 if (costS
< costLeft
&& costS
< costRight
) break;
1325 // it is cheaper to go down into a child of the current node, choose the best child
1326 currentNodeId
= (costLeft
< costRight ? leftChild
: rightChild
);
1329 int siblingNode
= currentNodeId
;
1331 // create a new parent for the new node and the sibling node
1332 int oldParentNode
= mNodes
[siblingNode
].parentId
;
1333 int newParentNode
= allocateNode();
1334 mNodes
[newParentNode
].parentId
= oldParentNode
;
1335 mNodes
[newParentNode
].aabb
.merge(mNodes
[siblingNode
].aabb
, newNodeAABB
);
1336 mNodes
[newParentNode
].height
= cast(short)(mNodes
[siblingNode
].height
+1);
1337 version(b2dlite_bvh_many_asserts
) assert(mNodes
[newParentNode
].height
> 0);
1339 // If the sibling node was not the root node
1340 if (oldParentNode
!= TreeNode
.NullTreeNode
) {
1341 version(b2dlite_bvh_many_asserts
) assert(!mNodes
[oldParentNode
].leaf
);
1342 if (mNodes
[oldParentNode
].children
.ptr
[TreeNode
.Left
] == siblingNode
) {
1343 mNodes
[oldParentNode
].children
.ptr
[TreeNode
.Left
] = newParentNode
;
1345 mNodes
[oldParentNode
].children
.ptr
[TreeNode
.Right
] = newParentNode
;
1347 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Left
] = siblingNode
;
1348 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Right
] = nodeId
;
1349 mNodes
[siblingNode
].parentId
= newParentNode
;
1350 mNodes
[nodeId
].parentId
= newParentNode
;
1352 // if the sibling node was the root node
1353 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Left
] = siblingNode
;
1354 mNodes
[newParentNode
].children
.ptr
[TreeNode
.Right
] = nodeId
;
1355 mNodes
[siblingNode
].parentId
= newParentNode
;
1356 mNodes
[nodeId
].parentId
= newParentNode
;
1357 mRootNodeId
= newParentNode
;
1360 // move up in the tree to change the AABBs that have changed
1361 currentNodeId
= mNodes
[nodeId
].parentId
;
1362 version(b2dlite_bvh_many_asserts
) assert(!mNodes
[currentNodeId
].leaf
);
1363 while (currentNodeId
!= TreeNode
.NullTreeNode
) {
1364 // balance the sub-tree of the current node if it is not balanced
1365 currentNodeId
= balanceSubTreeAtNode(currentNodeId
);
1366 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].leaf
);
1368 version(b2dlite_bvh_many_asserts
) assert(!mNodes
[currentNodeId
].leaf
);
1369 int leftChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Left
];
1370 int rightChild
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Right
];
1371 version(b2dlite_bvh_many_asserts
) assert(leftChild
!= TreeNode
.NullTreeNode
);
1372 version(b2dlite_bvh_many_asserts
) assert(rightChild
!= TreeNode
.NullTreeNode
);
1374 // recompute the height of the node in the tree
1375 mNodes
[currentNodeId
].height
= cast(short)(max(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
)+1);
1376 version(b2dlite_bvh_many_asserts
) assert(mNodes
[currentNodeId
].height
> 0);
1378 // recompute the AABB of the node
1379 mNodes
[currentNodeId
].aabb
.merge(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
1381 currentNodeId
= mNodes
[currentNodeId
].parentId
;
1384 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].leaf
);
1387 // remove a leaf node from the tree
1388 void removeLeafNode (int nodeId
) {
1389 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
1390 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].leaf
);
1392 // If we are removing the root node (root node is a leaf in this case)
1393 if (mRootNodeId
== nodeId
) { mRootNodeId
= TreeNode
.NullTreeNode
; return; }
1395 int parentNodeId
= mNodes
[nodeId
].parentId
;
1396 int grandParentNodeId
= mNodes
[parentNodeId
].parentId
;
1399 if (mNodes
[parentNodeId
].children
.ptr
[TreeNode
.Left
] == nodeId
) {
1400 siblingNodeId
= mNodes
[parentNodeId
].children
.ptr
[TreeNode
.Right
];
1402 siblingNodeId
= mNodes
[parentNodeId
].children
.ptr
[TreeNode
.Left
];
1405 // if the parent of the node to remove is not the root node
1406 if (grandParentNodeId
!= TreeNode
.NullTreeNode
) {
1407 // destroy the parent node
1408 if (mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Left
] == parentNodeId
) {
1409 mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Left
] = siblingNodeId
;
1411 version(b2dlite_bvh_many_asserts
) assert(mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Right
] == parentNodeId
);
1412 mNodes
[grandParentNodeId
].children
.ptr
[TreeNode
.Right
] = siblingNodeId
;
1414 mNodes
[siblingNodeId
].parentId
= grandParentNodeId
;
1415 releaseNode(parentNodeId
);
1417 // now, we need to recompute the AABBs of the node on the path back to the root and make sure that the tree is still balanced
1418 int currentNodeId
= grandParentNodeId
;
1419 while (currentNodeId
!= TreeNode
.NullTreeNode
) {
1420 // balance the current sub-tree if necessary
1421 currentNodeId
= balanceSubTreeAtNode(currentNodeId
);
1423 version(b2dlite_bvh_many_asserts
) assert(!mNodes
[currentNodeId
].leaf
);
1425 // get the two children.ptr of the current node
1426 int leftChildId
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Left
];
1427 int rightChildId
= mNodes
[currentNodeId
].children
.ptr
[TreeNode
.Right
];
1429 // recompute the AABB and the height of the current node
1430 mNodes
[currentNodeId
].aabb
.merge(mNodes
[leftChildId
].aabb
, mNodes
[rightChildId
].aabb
);
1431 mNodes
[currentNodeId
].height
= cast(short)(max(mNodes
[leftChildId
].height
, mNodes
[rightChildId
].height
)+1);
1432 version(b2dlite_bvh_many_asserts
) assert(mNodes
[currentNodeId
].height
> 0);
1434 currentNodeId
= mNodes
[currentNodeId
].parentId
;
1437 // if the parent of the node to remove is the root node, the sibling node becomes the new root node
1438 mRootNodeId
= siblingNodeId
;
1439 mNodes
[siblingNodeId
].parentId
= TreeNode
.NullTreeNode
;
1440 releaseNode(parentNodeId
);
1444 // balance the sub-tree of a given node using left or right rotations
1445 // the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
1446 // this method returns the new root node Id
1447 int balanceSubTreeAtNode (int nodeId
) {
1448 version(b2dlite_bvh_many_asserts
) assert(nodeId
!= TreeNode
.NullTreeNode
);
1450 TreeNode
* nodeA
= mNodes
+nodeId
;
1452 // if the node is a leaf or the height of A's sub-tree is less than 2
1453 if (nodeA
.leaf || nodeA
.height
< 2) return nodeId
; // do not perform any rotation
1455 // get the two children nodes
1456 int nodeBId
= nodeA
.children
.ptr
[TreeNode
.Left
];
1457 int nodeCId
= nodeA
.children
.ptr
[TreeNode
.Right
];
1458 version(b2dlite_bvh_many_asserts
) assert(nodeBId
>= 0 && nodeBId
< mAllocCount
);
1459 version(b2dlite_bvh_many_asserts
) assert(nodeCId
>= 0 && nodeCId
< mAllocCount
);
1460 TreeNode
* nodeB
= mNodes
+nodeBId
;
1461 TreeNode
* nodeC
= mNodes
+nodeCId
;
1463 // compute the factor of the left and right sub-trees
1464 int balanceFactor
= nodeC
.height
-nodeB
.height
;
1466 // if the right node C is 2 higher than left node B
1467 if (balanceFactor
> 1) {
1468 version(b2dlite_bvh_many_asserts
) assert(!nodeC
.leaf
);
1470 int nodeFId
= nodeC
.children
.ptr
[TreeNode
.Left
];
1471 int nodeGId
= nodeC
.children
.ptr
[TreeNode
.Right
];
1472 version(b2dlite_bvh_many_asserts
) assert(nodeFId
>= 0 && nodeFId
< mAllocCount
);
1473 version(b2dlite_bvh_many_asserts
) assert(nodeGId
>= 0 && nodeGId
< mAllocCount
);
1474 TreeNode
* nodeF
= mNodes
+nodeFId
;
1475 TreeNode
* nodeG
= mNodes
+nodeGId
;
1477 nodeC
.children
.ptr
[TreeNode
.Left
] = nodeId
;
1478 nodeC
.parentId
= nodeA
.parentId
;
1479 nodeA
.parentId
= nodeCId
;
1481 if (nodeC
.parentId
!= TreeNode
.NullTreeNode
) {
1482 if (mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Left
] == nodeId
) {
1483 mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Left
] = nodeCId
;
1485 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Right
] == nodeId
);
1486 mNodes
[nodeC
.parentId
].children
.ptr
[TreeNode
.Right
] = nodeCId
;
1489 mRootNodeId
= nodeCId
;
1492 version(b2dlite_bvh_many_asserts
) assert(!nodeC
.leaf
);
1493 version(b2dlite_bvh_many_asserts
) assert(!nodeA
.leaf
);
1495 // if the right node C was higher than left node B because of the F node
1496 if (nodeF
.height
> nodeG
.height
) {
1497 nodeC
.children
.ptr
[TreeNode
.Right
] = nodeFId
;
1498 nodeA
.children
.ptr
[TreeNode
.Right
] = nodeGId
;
1499 nodeG
.parentId
= nodeId
;
1501 // recompute the AABB of node A and C
1502 nodeA
.aabb
.merge(nodeB
.aabb
, nodeG
.aabb
);
1503 nodeC
.aabb
.merge(nodeA
.aabb
, nodeF
.aabb
);
1505 // recompute the height of node A and C
1506 nodeA
.height
= cast(short)(max(nodeB
.height
, nodeG
.height
)+1);
1507 nodeC
.height
= cast(short)(max(nodeA
.height
, nodeF
.height
)+1);
1508 version(b2dlite_bvh_many_asserts
) assert(nodeA
.height
> 0);
1509 version(b2dlite_bvh_many_asserts
) assert(nodeC
.height
> 0);
1511 // if the right node C was higher than left node B because of node G
1512 nodeC
.children
.ptr
[TreeNode
.Right
] = nodeGId
;
1513 nodeA
.children
.ptr
[TreeNode
.Right
] = nodeFId
;
1514 nodeF
.parentId
= nodeId
;
1516 // recompute the AABB of node A and C
1517 nodeA
.aabb
.merge(nodeB
.aabb
, nodeF
.aabb
);
1518 nodeC
.aabb
.merge(nodeA
.aabb
, nodeG
.aabb
);
1520 // recompute the height of node A and C
1521 nodeA
.height
= cast(short)(max(nodeB
.height
, nodeF
.height
)+1);
1522 nodeC
.height
= cast(short)(max(nodeA
.height
, nodeG
.height
)+1);
1523 version(b2dlite_bvh_many_asserts
) assert(nodeA
.height
> 0);
1524 version(b2dlite_bvh_many_asserts
) assert(nodeC
.height
> 0);
1527 // return the new root of the sub-tree
1531 // if the left node B is 2 higher than right node C
1532 if (balanceFactor
< -1) {
1533 version(b2dlite_bvh_many_asserts
) assert(!nodeB
.leaf
);
1535 int nodeFId
= nodeB
.children
.ptr
[TreeNode
.Left
];
1536 int nodeGId
= nodeB
.children
.ptr
[TreeNode
.Right
];
1537 version(b2dlite_bvh_many_asserts
) assert(nodeFId
>= 0 && nodeFId
< mAllocCount
);
1538 version(b2dlite_bvh_many_asserts
) assert(nodeGId
>= 0 && nodeGId
< mAllocCount
);
1539 TreeNode
* nodeF
= mNodes
+nodeFId
;
1540 TreeNode
* nodeG
= mNodes
+nodeGId
;
1542 nodeB
.children
.ptr
[TreeNode
.Left
] = nodeId
;
1543 nodeB
.parentId
= nodeA
.parentId
;
1544 nodeA
.parentId
= nodeBId
;
1546 if (nodeB
.parentId
!= TreeNode
.NullTreeNode
) {
1547 if (mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Left
] == nodeId
) {
1548 mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Left
] = nodeBId
;
1550 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Right
] == nodeId
);
1551 mNodes
[nodeB
.parentId
].children
.ptr
[TreeNode
.Right
] = nodeBId
;
1554 mRootNodeId
= nodeBId
;
1557 version(b2dlite_bvh_many_asserts
) assert(!nodeB
.leaf
);
1558 version(b2dlite_bvh_many_asserts
) assert(!nodeA
.leaf
);
1560 // if the left node B was higher than right node C because of the F node
1561 if (nodeF
.height
> nodeG
.height
) {
1562 nodeB
.children
.ptr
[TreeNode
.Right
] = nodeFId
;
1563 nodeA
.children
.ptr
[TreeNode
.Left
] = nodeGId
;
1564 nodeG
.parentId
= nodeId
;
1566 // recompute the AABB of node A and B
1567 nodeA
.aabb
.merge(nodeC
.aabb
, nodeG
.aabb
);
1568 nodeB
.aabb
.merge(nodeA
.aabb
, nodeF
.aabb
);
1570 // recompute the height of node A and B
1571 nodeA
.height
= cast(short)(max(nodeC
.height
, nodeG
.height
)+1);
1572 nodeB
.height
= cast(short)(max(nodeA
.height
, nodeF
.height
)+1);
1573 version(b2dlite_bvh_many_asserts
) assert(nodeA
.height
> 0);
1574 version(b2dlite_bvh_many_asserts
) assert(nodeB
.height
> 0);
1576 // if the left node B was higher than right node C because of node G
1577 nodeB
.children
.ptr
[TreeNode
.Right
] = nodeGId
;
1578 nodeA
.children
.ptr
[TreeNode
.Left
] = nodeFId
;
1579 nodeF
.parentId
= nodeId
;
1581 // recompute the AABB of node A and B
1582 nodeA
.aabb
.merge(nodeC
.aabb
, nodeF
.aabb
);
1583 nodeB
.aabb
.merge(nodeA
.aabb
, nodeG
.aabb
);
1585 // recompute the height of node A and B
1586 nodeA
.height
= cast(short)(max(nodeC
.height
, nodeF
.height
)+1);
1587 nodeB
.height
= cast(short)(max(nodeA
.height
, nodeG
.height
)+1);
1588 version(b2dlite_bvh_many_asserts
) assert(nodeA
.height
> 0);
1589 version(b2dlite_bvh_many_asserts
) assert(nodeB
.height
> 0);
1592 // return the new root of the sub-tree
1596 // if the sub-tree is balanced, return the current root node
1600 // compute the height of a given node in the tree
1601 int computeHeight (int nodeId
) {
1602 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
1603 TreeNode
* node
= mNodes
+nodeId
;
1605 // If the node is a leaf, its height is zero
1606 if (node
.leaf
) return 0;
1608 // Compute the height of the left and right sub-tree
1609 int leftHeight
= computeHeight(node
.children
.ptr
[TreeNode
.Left
]);
1610 int rightHeight
= computeHeight(node
.children
.ptr
[TreeNode
.Right
]);
1612 // Return the height of the node
1613 return 1+max(leftHeight
, rightHeight
);
1616 // internally add an object into the tree
1617 int addObjectInternal (in ref AABB aabb
) {
1618 // get the next available node (or allocate new ones if necessary)
1619 int nodeId
= allocateNode();
1621 // create the fat aabb to use in the tree
1622 immutable gap
= AABB
.VType(mExtraGap
, mExtraGap
, mExtraGap
);
1623 mNodes
[nodeId
].aabb
.setMin(aabb
.getMin()-gap
);
1624 mNodes
[nodeId
].aabb
.setMax(aabb
.getMax()+gap
);
1626 // set the height of the node in the tree
1627 mNodes
[nodeId
].height
= 0;
1629 // insert the new leaf node in the tree
1630 insertLeafNode(nodeId
);
1631 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].leaf
);
1633 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0);
1635 // return the Id of the node
1639 // initialize the tree
1641 import core
.stdc
.stdlib
: malloc
;
1642 import core
.stdc
.string
: memset
;
1644 mRootNodeId
= TreeNode
.NullTreeNode
;
1648 mNodes
= cast(TreeNode
*)malloc(mAllocCount
*TreeNode
.sizeof
);
1649 if (mNodes
is null) assert(0, "out of memory");
1650 memset(mNodes
, 0, mAllocCount
*TreeNode
.sizeof
);
1652 // initialize the allocated nodes
1653 foreach (int i
; 0..mAllocCount
-1) {
1654 mNodes
[i
].nextNodeId
= i
+1;
1655 mNodes
[i
].height
= -1;
1657 mNodes
[mAllocCount
-1].nextNodeId
= TreeNode
.NullTreeNode
;
1658 mNodes
[mAllocCount
-1].height
= -1;
1662 // also, checks if the tree structure is valid (for debugging purpose)
1663 void forEachLeaf (scope void delegate (int nodeId
, in ref AABB aabb
) dg
) {
1664 void forEachNode (int nodeId
) {
1665 if (nodeId
== TreeNode
.NullTreeNode
) return;
1666 // if it is the root
1667 if (nodeId
== mRootNodeId
) {
1668 assert(mNodes
[nodeId
].parentId
== TreeNode
.NullTreeNode
);
1670 // get the children nodes
1671 TreeNode
* pNode
= mNodes
+nodeId
;
1672 assert(pNode
.height
>= 0);
1673 assert(pNode
.aabb
.volume
> 0);
1674 // if the current node is a leaf
1676 assert(pNode
.height
== 0);
1677 if (dg
!is null) dg(nodeId
, pNode
.aabb
);
1679 int leftChild
= pNode
.children
.ptr
[TreeNode
.Left
];
1680 int rightChild
= pNode
.children
.ptr
[TreeNode
.Right
];
1681 // check that the children node Ids are valid
1682 assert(0 <= leftChild
&& leftChild
< mAllocCount
);
1683 assert(0 <= rightChild
&& rightChild
< mAllocCount
);
1684 // check that the children nodes have the correct parent node
1685 assert(mNodes
[leftChild
].parentId
== nodeId
);
1686 assert(mNodes
[rightChild
].parentId
== nodeId
);
1687 // check the height of node
1688 int height
= 1+max(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
);
1689 assert(mNodes
[nodeId
].height
== height
);
1690 // check the AABB of the node
1691 AABB aabb
= AABB
.mergeAABBs(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
1692 assert(aabb
.getMin() == mNodes
[nodeId
].aabb
.getMin());
1693 assert(aabb
.getMax() == mNodes
[nodeId
].aabb
.getMax());
1694 // recursively check the children nodes
1695 forEachNode(leftChild
);
1696 forEachNode(rightChild
);
1699 // recursively check each node
1700 forEachNode(mRootNodeId
);
1704 this (VFloat extraAABBGap
=VFloatNum
!0) {
1705 mExtraGap
= extraAABBGap
;
1710 import core
.stdc
.stdlib
: free
;
1714 // return the fat AABB corresponding to a given node Id
1715 /*const ref*/ AABB
getFatAABB (int nodeId
) const {
1716 pragma(inline
, true);
1717 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
1718 return mNodes
[nodeId
].aabb
;
1721 // return the pointer to the data array of a given leaf node of the tree
1722 BodyBase
getNodeBody (int nodeId
) {
1723 pragma(inline
, true);
1724 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
1725 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].leaf
);
1726 return mNodes
[nodeId
].flesh
;
1729 // return the root AABB of the tree
1730 AABB
getRootAABB () { pragma(inline
, true); return getFatAABB(mRootNodeId
); }
1732 // add an object into the tree.
1733 // this method creates a new leaf node in the tree and returns the Id of the corresponding node
1734 int addObject (/*in ref AABB aabb,*/ BodyBase flesh
) {
1735 auto aabb
= flesh
.getAABB();
1736 int nodeId
= addObjectInternal(aabb
);
1737 mNodes
[nodeId
].flesh
= flesh
;
1741 // remove an object from the tree
1742 void removeObject (int nodeId
) {
1743 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
1744 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].leaf
);
1745 // remove the node from the tree
1746 removeLeafNode(nodeId
);
1747 releaseNode(nodeId
);
1750 // update the dynamic tree after an object has moved
1751 // if the new AABB of the object that has moved is still inside its fat AABB, then nothing is done.
1752 // otherwise, the corresponding node is removed and reinserted into the tree.
1753 // the method returns true if the object has been reinserted into the tree.
1754 // the "displacement" argument is the linear velocity of the AABB multiplied by the elapsed time between two frames.
1755 // if the "forceReinsert" parameter is true, we force a removal and reinsertion of the node
1756 // (this can be useful if the shape AABB has become much smaller than the previous one for instance).
1757 bool updateObject (int nodeId
/*, in ref AABB newAABB*/, in ref AABB
.VType displacement
, bool forceReinsert
=false) {
1758 version(b2dlite_bvh_many_asserts
) assert(nodeId
>= 0 && nodeId
< mAllocCount
);
1759 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].leaf
);
1760 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].height
>= 0);
1762 auto newAABB
= mNodes
[nodeId
].flesh
.getAABB();
1764 // If the new AABB is still inside the fat AABB of the node
1765 if (!forceReinsert
&& mNodes
[nodeId
].aabb
.contains(newAABB
)) return false;
1767 // if the new AABB is outside the fat AABB, we remove the corresponding node
1768 removeLeafNode(nodeId
);
1770 // compute the fat AABB by inflating the AABB with a constant gap
1771 mNodes
[nodeId
].aabb
= newAABB
;
1772 immutable gap
= AABB
.VType(mExtraGap
, mExtraGap
, mExtraGap
);
1773 mNodes
[nodeId
].aabb
.mMin
-= gap
;
1774 mNodes
[nodeId
].aabb
.mMax
+= gap
;
1776 // inflate the fat AABB in direction of the linear motion of the AABB
1777 if (displacement
.x
< VFloatNum
!0) {
1778 mNodes
[nodeId
].aabb
.mMin
.x
+= DynamicBVHLinGapMult
*displacement
.x
;
1780 mNodes
[nodeId
].aabb
.mMax
.x
+= DynamicBVHLinGapMult
*displacement
.x
;
1782 if (displacement
.y
< VFloatNum
!0) {
1783 mNodes
[nodeId
].aabb
.mMin
.y
+= DynamicBVHLinGapMult
*displacement
.y
;
1785 mNodes
[nodeId
].aabb
.mMax
.y
+= DynamicBVHLinGapMult
*displacement
.y
;
1787 static if (AABB
.VType
.isVector3
!(AABB
.VType
)) {
1788 if (displacement
.z
< VFloatNum
!0) {
1789 mNodes
[nodeId
].aabb
.mMin
.z
+= DynamicBVHLinGapMult
*displacement
.z
;
1791 mNodes
[nodeId
].aabb
.mMax
.z
+= DynamicBVHLinGapMult
*displacement
.z
;
1795 version(b2dlite_bvh_many_asserts
) assert(mNodes
[nodeId
].aabb
.contains(newAABB
));
1797 // reinsert the node into the tree
1798 insertLeafNode(nodeId
);
1803 // report all shapes overlapping with the AABB given in parameter
1804 void reportAllShapesOverlappingWithAABB (in ref AABB aabb
, scope OverlapCallback callback
) {
1805 int[256] stack
= void; // stack with the nodes to visit
1808 void spush (int id
) {
1809 if (sp
>= stack
.length
) throw new Exception("stack overflow");
1810 stack
.ptr
[sp
++] = id
;
1814 if (sp
== 0) throw new Exception("stack underflow");
1815 return stack
.ptr
[--sp
];
1820 // while there are still nodes to visit
1822 // get the next node id to visit
1823 int nodeIdToVisit
= spop();
1824 // skip it if it is a null node
1825 if (nodeIdToVisit
== TreeNode
.NullTreeNode
) continue;
1826 // get the corresponding node
1827 const(TreeNode
)* nodeToVisit
= mNodes
+nodeIdToVisit
;
1828 // if the AABB in parameter overlaps with the AABB of the node to visit
1829 if (aabb
.testCollision(nodeToVisit
.aabb
)) {
1830 // if the node is a leaf
1831 if (nodeToVisit
.leaf
) {
1832 // notify the broad-phase about a new potential overlapping pair
1833 callback(nodeIdToVisit
);
1835 // if the node is not a leaf
1836 // we need to visit its children
1837 spush(nodeToVisit
.children
.ptr
[TreeNode
.Left
]);
1838 spush(nodeToVisit
.children
.ptr
[TreeNode
.Right
]);
1844 // compute the height of the tree
1845 int computeHeight () { pragma(inline
, true); return computeHeight(mRootNodeId
); }
1847 // clear all the nodes and reset the tree
1849 import core
.stdc
.stdlib
: free
;