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.
13 public import iv
.vmath
;
16 // ////////////////////////////////////////////////////////////////////////// //
18 public alias Vec2
= VecN
!(2, float);
19 public alias VFloat
= Vec2
.VFloat
;
20 public alias VFloatNum
= Vec2
.VFloatNum
;
23 // ////////////////////////////////////////////////////////////////////////// //
32 import core
.stdc
.math
: cosf
, sinf
;
33 immutable VFloat c
= cosf(angle
), s
= sinf(angle
);
34 col1
.x
= c
; col1
.y
= s
;
35 col2
.x
= -s
; col2
.y
= c
;
39 this() (in auto ref Vec2 acol1
, in auto ref Vec2 acol2
) { pragma(inline
, true); col1
= acol1
; col2
= acol2
; }
41 Mat22
transpose () const { pragma(inline
, true); return Mat22(Vec2(col1
.x
, col2
.x
), Vec2(col1
.y
, col2
.y
)); }
43 Mat22
invert () const {
45 immutable VFloat a
= col1
.x
, b
= col2
.x
, c
= col1
.y
, d
= col2
.y
;
48 assert(det
!= VFloatNum
!(0.0));
49 det
= VFloatNum
!(1.0)/det
;
57 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
); }
59 Mat22
opBinary(string op
:"+") (in auto ref Mat22 B
) const { pragma(inline
, true); return Mat22(col1
+B
.col1
, col2
+B
.col2
); }
60 Mat22
opBinary(string op
:"*") (in auto ref Mat22 B
) const { pragma(inline
, true); return Mat22(this*B
.col1
, this*B
.col2
); }
62 Mat22
abs() () { pragma(inline
, true); return Mat22(col1
.abs
, col2
.abs
); }
66 // ////////////////////////////////////////////////////////////////////////// //
67 private Vec2
fcross() (VFloat s
, in auto ref Vec2 a
) { pragma(inline
, true); return Vec2(-s
*a
.y
, s
*a
.x
); }
70 // ////////////////////////////////////////////////////////////////////////// //
74 static shared uint lastidx
;
84 VFloat angularVelocity
;
97 import core
.atomic
: atomicOp
;
98 midx
= atomicOp
!"+="(lastidx
, 1);
99 position
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
100 rotation
= VFloatNum
!(0.0);
101 velocity
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
102 angularVelocity
= VFloatNum
!(0.0);
103 force
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
104 torque
= VFloatNum
!(0.0);
105 friction
= VFloatNum
!(0.2);
107 width
.set(VFloatNum
!(1.0), VFloatNum
!(1.0));
109 invMass
= VFloatNum
!(0.0);
111 invI
= VFloatNum
!(0.0);
114 @property uint id () const pure nothrow @safe @nogc { pragma(inline
, true); return midx
; }
116 void set() (in auto ref Vec2 w
, VFloat m
) {
117 position
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
118 rotation
= VFloatNum
!(0.0);
119 velocity
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
120 angularVelocity
= VFloatNum
!(0.0);
121 force
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
122 torque
= VFloatNum
!(0.0);
123 friction
= VFloatNum
!(0.2);
126 if (mass
< VFloat
.max
) {
127 invMass
= VFloatNum
!(1.0)/mass
;
128 I
= mass
*(width
.x
*width
.x
+width
.y
*width
.y
)/VFloatNum
!(12.0);
129 invI
= VFloatNum
!(1.0)/I
;
131 invMass
= VFloatNum
!(0.0);
133 invI
= VFloatNum
!(0.0);
137 void addForce() (in auto ref Vec2 f
) { pragma(inline
, true); force
+= f
; }
139 int opCmp() (Body b
) pure const nothrow @trusted @nogc {
140 pragma(inline
, true);
141 return (b
!is null ? b
.midx
== midx
: 1);
146 // ////////////////////////////////////////////////////////////////////////// //
151 Vec2 localAnchor1
, localAnchor2
;
154 Vec2 P
= Vec2(0, 0); // accumulated impulse
157 VFloat biasFactor
= VFloatNum
!(0.2);
158 VFloat softness
= VFloatNum
!(0.0);
161 void set() (Body b1
, Body b2
, in auto ref Vec2 anchor
) {
165 auto Rot1
= Mat22(body1
.rotation
);
166 auto Rot2
= Mat22(body2
.rotation
);
167 Mat22 Rot1T
= Rot1
.transpose();
168 Mat22 Rot2T
= Rot2
.transpose();
170 localAnchor1
= Rot1T
*(anchor
-body1
.position
);
171 localAnchor2
= Rot2T
*(anchor
-body2
.position
);
173 P
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
175 softness
= VFloatNum
!(0.0);
176 biasFactor
= VFloatNum
!(0.2);
179 void preStep (VFloat inv_dt
) {
180 // pre-compute anchors, mass matrix, and bias
181 auto Rot1
= Mat22 (body1
.rotation
);
182 auto Rot2
= Mat22 (body2
.rotation
);
184 r1
= Rot1
*localAnchor1
;
185 r2
= Rot2
*localAnchor2
;
187 // deltaV = deltaV0 + K * impulse
188 // invM = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)]
189 // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y]
190 // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
192 K1
.col1
.x
= body1
.invMass
+body2
.invMass
; K1
.col2
.x
= VFloatNum
!(0.0);
193 K1
.col1
.y
= VFloatNum
!(0.0); K1
.col2
.y
= body1
.invMass
+body2
.invMass
;
196 K2
.col1
.x
= body1
.invI
*r1
.y
*r1
.y
; K2
.col2
.x
= -body1
.invI
*r1
.x
*r1
.y
;
197 K2
.col1
.y
= -body1
.invI
*r1
.x
*r1
.y
; K2
.col2
.y
= body1
.invI
*r1
.x
*r1
.x
;
200 K3
.col1
.x
= body2
.invI
*r2
.y
*r2
.y
; K3
.col2
.x
= -body2
.invI
*r2
.x
*r2
.y
;
201 K3
.col1
.y
= -body2
.invI
*r2
.x
*r2
.y
; K3
.col2
.y
= body2
.invI
*r2
.x
*r2
.x
;
204 K
.col1
.x
+= softness
;
205 K
.col2
.y
+= softness
;
209 Vec2 p1
= body1
.position
+r1
;
210 Vec2 p2
= body2
.position
+r2
;
213 if (World
.positionCorrection
) {
214 bias
= -biasFactor
*inv_dt
*dp
;
216 bias
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
219 if (World
.warmStarting
) {
220 // apply accumulated impulse
221 body1
.velocity
-= body1
.invMass
*P
;
222 body1
.angularVelocity
-= body1
.invI
*r1
.cross(P
);
224 body2
.velocity
+= body2
.invMass
*P
;
225 body2
.angularVelocity
+= body2
.invI
*r2
.cross(P
);
227 P
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
231 void applyImpulse () {
232 Vec2 dv
= body2
.velocity
+body2
.angularVelocity
.fcross(r2
)-body1
.velocity
-body1
.angularVelocity
.fcross(r1
);
233 Vec2 impulse
= M
*(bias
-dv
-softness
*P
);
235 body1
.velocity
-= body1
.invMass
*impulse
;
236 body1
.angularVelocity
-= body1
.invI
*r1
.cross(impulse
);
238 body2
.velocity
+= body2
.invMass
*impulse
;
239 body2
.angularVelocity
+= body2
.invI
*r2
.cross(impulse
);
246 // ////////////////////////////////////////////////////////////////////////// //
249 VFloat
clamp() (VFloat a
, VFloat low
, VFloat high
) { pragma(inline
, true); import std
.algorithm
: min
, max
; return max(low
, min(a
, high
)); }
252 // ////////////////////////////////////////////////////////////////////////// //
254 static struct Edges
{
265 // ////////////////////////////////////////////////////////////////////////// //
271 VFloat separation
= VFloatNum
!(0.0);
272 VFloat Pn
= VFloatNum
!(0.0); // accumulated normal impulse
273 VFloat Pt
= VFloatNum
!(0.0); // accumulated tangent impulse
274 VFloat Pnb
= VFloatNum
!(0.0); // accumulated normal impulse for position bias
275 VFloat massNormal
, massTangent
;
276 VFloat bias
= VFloatNum
!(0.0);
281 // ////////////////////////////////////////////////////////////////////////// //
287 Contact
[MAX_POINTS
] contacts
;
298 this (Body b1
, Body b2
) { setup(b1
, b2
); }
300 void setup (Body b1
, Body b2
) {
301 import std
.math
: sqrt
;
310 numContacts
= collide(contacts
.ptr
, body1
, body2
);
311 friction
= sqrt(body1
.friction
*body2
.friction
);
315 glPointSize(VFloatNum!(4.0));
316 glColor3f(VFloatNum!(1.0), VFloatNum!(0.0), VFloatNum!(0.0));
318 for (int i = 0; i < numContacts; ++i) glVertex2f(contacts[i].position.x, contacts[i].position.y);
320 glPointSize(VFloatNum!(1.0));
324 void update (Contact
* newContacts
, int numNewContacts
) {
325 Contact
[2] mergedContacts
;
326 for (int i
= 0; i
< numNewContacts
; ++i
) {
327 Contact
*cNew
= newContacts
+i
;
329 for (int j
= 0; j
< numContacts
; ++j
) {
330 Contact
* cOld
= contacts
.ptr
+j
;
331 if (cNew
.feature
.value
== cOld
.feature
.value
) { k
= j
; break; }
334 Contact
* c
= mergedContacts
.ptr
+i
;
335 Contact
* cOld
= contacts
.ptr
+k
;
337 if (World
.warmStarting
) {
342 c
.Pn
= VFloatNum
!(0.0);
343 c
.Pt
= VFloatNum
!(0.0);
344 c
.Pnb
= VFloatNum
!(0.0);
347 mergedContacts
[i
] = newContacts
[i
];
350 for (int i
= 0; i
< numNewContacts
; ++i
) contacts
[i
] = mergedContacts
[i
];
351 numContacts
= numNewContacts
;
354 void preStep (VFloat inv_dt
) {
355 import std
.algorithm
: min
;
357 enum k_allowedPenetration
= VFloatNum
!(0.01);
358 VFloat k_biasFactor
= (World
.positionCorrection ? VFloatNum
!(0.2) : VFloatNum
!(0.0));
359 for (int i
= 0; i
< numContacts
; ++i
) {
360 Contact
*c
= contacts
.ptr
+i
;
361 Vec2 r1
= c
.position
-body1
.position
;
362 Vec2 r2
= c
.position
-body2
.position
;
364 // precompute normal mass, tangent mass, and bias
365 VFloat rn1
= r1
*c
.normal
; //Dot(r1, c.normal);
366 VFloat rn2
= r2
*c
.normal
; //Dot(r2, c.normal);
367 VFloat kNormal
= body1
.invMass
+body2
.invMass
;
368 //kNormal += body1.invI*(Dot(r1, r1)-rn1*rn1)+body2.invI*(Dot(r2, r2)-rn2*rn2);
369 kNormal
+= body1
.invI
*((r1
*r1
)-rn1
*rn1
)+body2
.invI
*((r2
*r2
)-rn2
*rn2
);
370 c
.massNormal
= VFloatNum
!(1.0)/kNormal
;
372 //Vec2 tangent = c.normal.cross(VFloatNum!(1.0));
373 Vec2 tangent
= Vec2(VFloatNum
!(1.0)*c
.normal
.y
, -VFloatNum
!(1.0)*c
.normal
.x
);
374 VFloat rt1
= r1
*tangent
; //Dot(r1, tangent);
375 VFloat rt2
= r2
*tangent
; //Dot(r2, tangent);
376 VFloat kTangent
= body1
.invMass
+body2
.invMass
;
377 //kTangent += body1.invI*(Dot(r1, r1)-rt1*rt1)+body2.invI*(Dot(r2, r2)-rt2*rt2);
378 kTangent
+= body1
.invI
*((r1
*r1
)-rt1
*rt1
)+body2
.invI
*((r2
*r2
)-rt2
*rt2
);
379 c
.massTangent
= VFloatNum
!(1.0)/kTangent
;
381 c
.bias
= -k_biasFactor
*inv_dt
*min(VFloatNum
!(0.0), c
.separation
+k_allowedPenetration
);
383 if (World
.accumulateImpulses
) {
384 // apply normal + friction impulse
385 Vec2 P
= c
.Pn
*c
.normal
+c
.Pt
*tangent
;
387 body1
.velocity
-= body1
.invMass
*P
;
388 body1
.angularVelocity
-= body1
.invI
*r1
.cross(P
);
390 body2
.velocity
+= body2
.invMass
*P
;
391 body2
.angularVelocity
+= body2
.invI
*r2
.cross(P
);
396 void applyImpulse () {
397 import std
.algorithm
: max
;
402 for (int i
= 0; i
< numContacts
; ++i
) {
403 Contact
*c
= contacts
.ptr
+i
;
404 c
.r1
= c
.position
-b1
.position
;
405 c
.r2
= c
.position
-b2
.position
;
407 // relative velocity at contact
408 Vec2 dv
= b2
.velocity
+b2
.angularVelocity
.fcross(c
.r2
)-b1
.velocity
-b1
.angularVelocity
.fcross(c
.r1
);
410 // compute normal impulse
411 VFloat vn
= dv
*c
.normal
; //Dot(dv, c.normal);
413 VFloat dPn
= c
.massNormal
*(-vn
+c
.bias
);
415 if (World
.accumulateImpulses
) {
416 // clamp the accumulated impulse
418 c
.Pn
= max(Pn0
+dPn
, VFloatNum
!(0.0));
421 dPn
= max(dPn
, VFloatNum
!(0.0));
424 // apply contact impulse
425 Vec2 Pn
= dPn
*c
.normal
;
427 b1
.velocity
-= b1
.invMass
*Pn
;
428 b1
.angularVelocity
-= b1
.invI
*c
.r1
.cross(Pn
);
430 b2
.velocity
+= b2
.invMass
*Pn
;
431 b2
.angularVelocity
+= b2
.invI
*c
.r2
.cross(Pn
);
433 // relative velocity at contact
434 dv
= b2
.velocity
+b2
.angularVelocity
.fcross(c
.r2
)-b1
.velocity
-b1
.angularVelocity
.fcross(c
.r1
);
436 //Vec2 tangent = c.normal.cross(VFloatNum!(1.0));
437 Vec2 tangent
= Vec2(VFloatNum
!(1.0)*c
.normal
.y
, -VFloatNum
!(1.0)*c
.normal
.x
);
438 VFloat vt
= dv
*tangent
; //Dot(dv, tangent);
439 VFloat dPt
= c
.massTangent
*(-vt
);
441 if (World
.accumulateImpulses
) {
442 // compute friction impulse
443 VFloat maxPt
= friction
*c
.Pn
;
445 VFloat oldTangentImpulse
= c
.Pt
;
446 c
.Pt
= clamp(oldTangentImpulse
+dPt
, -maxPt
, maxPt
);
447 dPt
= c
.Pt
-oldTangentImpulse
;
449 VFloat maxPt
= friction
*dPn
;
450 dPt
= clamp(dPt
, -maxPt
, maxPt
);
453 // apply contact impulse
454 Vec2 Pt
= dPt
*tangent
;
456 b1
.velocity
-= b1
.invMass
*Pt
;
457 b1
.angularVelocity
-= b1
.invI
*c
.r1
.cross(Pt
);
459 b2
.velocity
+= b2
.invMass
*Pt
;
460 b2
.angularVelocity
+= b2
.invI
*c
.r2
.cross(Pt
);
466 // ////////////////////////////////////////////////////////////////////////// //
469 VFloat
sign() (VFloat x
) { pragma(inline
, true); return (x
< VFloatNum
!(0.0) ?
-VFloatNum
!(1.0) : VFloatNum
!(1.0)); }
471 void swap(T
) (ref T a
, ref T b
) {
472 pragma(inline
, true);
478 private void flip (ref FeaturePair fp
) {
479 pragma(inline
, true);
480 swap(fp
.e
.inEdge1
, fp
.e
.inEdge2
);
481 swap(fp
.e
.outEdge1
, fp
.e
.outEdge2
);
487 // Box vertex and edge numbering:
500 // ////////////////////////////////////////////////////////////////////////// //
518 // ////////////////////////////////////////////////////////////////////////// //
525 // ////////////////////////////////////////////////////////////////////////// //
526 private int clipSegmentToLine() (ClipVertex
[/*2*/] vOut
, ClipVertex
[/*2*/] vIn
, in auto ref Vec2 normal
, VFloat offset
, char clipEdge
) {
527 // start with no output points
529 // calculate the distance of end points to the line
530 VFloat distance0
= /*Dot*/(normal
*vIn
[0].v
)-offset
;
531 VFloat distance1
= /*Dot*/(normal
*vIn
[1].v
)-offset
;
532 // if the points are behind the plane
533 if (distance0
<= VFloatNum
!(0.0)) vOut
[numOut
++] = vIn
[0];
534 if (distance1
<= VFloatNum
!(0.0)) vOut
[numOut
++] = vIn
[1];
535 // if the points are on different sides of the plane
536 if (distance0
*distance1
< VFloatNum
!(0.0)) {
537 // find intersection point of edge and plane
538 VFloat interp
= distance0
/(distance0
-distance1
);
539 vOut
[numOut
].v
= vIn
[0].v
+interp
*(vIn
[1].v
-vIn
[0].v
);
540 if (distance0
> VFloatNum
!(0.0)) {
541 vOut
[numOut
].fp
= vIn
[0].fp
;
542 vOut
[numOut
].fp
.e
.inEdge1
= clipEdge
;
543 vOut
[numOut
].fp
.e
.inEdge2
= EdgeNumbers
.NO_EDGE
;
545 vOut
[numOut
].fp
= vIn
[1].fp
;
546 vOut
[numOut
].fp
.e
.outEdge1
= clipEdge
;
547 vOut
[numOut
].fp
.e
.outEdge2
= EdgeNumbers
.NO_EDGE
;
555 // ////////////////////////////////////////////////////////////////////////// //
556 private void computeIncidentEdge() (ClipVertex
[/*2*/] c
, in auto ref Vec2 h
, in auto ref Vec2 pos
, in auto ref Mat22 Rot
, in auto ref Vec2 normal
) {
557 // the normal is from the reference box; convert it to the incident boxe's frame and flip sign
558 Mat22 RotT
= Rot
.transpose();
559 Vec2 n
= -(RotT
*normal
);
561 if (nAbs
.x
> nAbs
.y
) {
562 if (sign(n
.x
) > VFloatNum
!(0.0)) {
563 c
[0].v
.set(h
.x
, -h
.y
);
564 c
[0].fp
.e
.inEdge2
= EdgeNumbers
.EDGE3
;
565 c
[0].fp
.e
.outEdge2
= EdgeNumbers
.EDGE4
;
567 c
[1].v
.set(h
.x
, h
.y
);
568 c
[1].fp
.e
.inEdge2
= EdgeNumbers
.EDGE4
;
569 c
[1].fp
.e
.outEdge2
= EdgeNumbers
.EDGE1
;
571 c
[0].v
.set(-h
.x
, h
.y
);
572 c
[0].fp
.e
.inEdge2
= EdgeNumbers
.EDGE1
;
573 c
[0].fp
.e
.outEdge2
= EdgeNumbers
.EDGE2
;
575 c
[1].v
.set(-h
.x
, -h
.y
);
576 c
[1].fp
.e
.inEdge2
= EdgeNumbers
.EDGE2
;
577 c
[1].fp
.e
.outEdge2
= EdgeNumbers
.EDGE3
;
580 if (sign(n
.y
) > VFloatNum
!(0.0)) {
581 c
[0].v
.set(h
.x
, h
.y
);
582 c
[0].fp
.e
.inEdge2
= EdgeNumbers
.EDGE4
;
583 c
[0].fp
.e
.outEdge2
= EdgeNumbers
.EDGE1
;
585 c
[1].v
.set(-h
.x
, h
.y
);
586 c
[1].fp
.e
.inEdge2
= EdgeNumbers
.EDGE1
;
587 c
[1].fp
.e
.outEdge2
= EdgeNumbers
.EDGE2
;
589 c
[0].v
.set(-h
.x
, -h
.y
);
590 c
[0].fp
.e
.inEdge2
= EdgeNumbers
.EDGE2
;
591 c
[0].fp
.e
.outEdge2
= EdgeNumbers
.EDGE3
;
593 c
[1].v
.set(h
.x
, -h
.y
);
594 c
[1].fp
.e
.inEdge2
= EdgeNumbers
.EDGE3
;
595 c
[1].fp
.e
.outEdge2
= EdgeNumbers
.EDGE4
;
599 c
[0].v
= pos
+Rot
*c
[0].v
;
600 c
[1].v
= pos
+Rot
*c
[1].v
;
604 // ////////////////////////////////////////////////////////////////////////// //
605 // the normal points from A to B
606 int collide (Contact
* contacts
, Body bodyA
, Body bodyB
) {
608 Vec2 hA
= VFloatNum
!(0.5)*bodyA
.width
;
609 Vec2 hB
= VFloatNum
!(0.5)*bodyB
.width
;
611 Vec2 posA
= bodyA
.position
;
612 Vec2 posB
= bodyB
.position
;
614 auto RotA
= Mat22 (bodyA
.rotation
);
615 auto RotB
= Mat22 (bodyB
.rotation
);
617 Mat22 RotAT
= RotA
.transpose();
618 Mat22 RotBT
= RotB
.transpose();
620 //k8:Vec2 a1 = RotA.col1, a2 = RotA.col2;
621 //k8:Vec2 b1 = RotB.col1, b2 = RotB.col2;
627 Mat22 C
= RotAT
*RotB
;
629 Mat22 absCT
= absC
.transpose();
632 Vec2 faceA
= dA
.abs
-hA
-absC
*hB
;
633 if (faceA
.x
> VFloatNum
!(0.0) || faceA
.y
> VFloatNum
!(0.0)) return 0;
636 Vec2 faceB
= dB
.abs
-absCT
*hA
-hB
;
637 if (faceB
.x
> VFloatNum
!(0.0) || faceB
.y
> VFloatNum
!(0.0)) return 0;
645 axis
= Axis
.FACE_A_X
;
646 separation
= faceA
.x
;
647 normal
= dA
.x
> VFloatNum
!(0.0) ? RotA
.col1
: -RotA
.col1
;
649 const VFloat relativeTol
= VFloatNum
!(0.95);
650 const VFloat absoluteTol
= VFloatNum
!(0.01);
652 if (faceA
.y
> relativeTol
*separation
+absoluteTol
*hA
.y
) {
653 axis
= Axis
.FACE_A_Y
;
654 separation
= faceA
.y
;
655 normal
= dA
.y
> VFloatNum
!(0.0) ? RotA
.col2
: -RotA
.col2
;
659 if (faceB
.x
> relativeTol
*separation
+absoluteTol
*hB
.x
) {
660 axis
= Axis
.FACE_B_X
;
661 separation
= faceB
.x
;
662 normal
= dB
.x
> VFloatNum
!(0.0) ? RotB
.col1
: -RotB
.col1
;
665 if (faceB
.y
> relativeTol
*separation
+absoluteTol
*hB
.y
) {
666 axis
= Axis
.FACE_B_Y
;
667 separation
= faceB
.y
;
668 normal
= dB
.y
> VFloatNum
!(0.0) ? RotB
.col2
: -RotB
.col2
;
671 // Setup clipping plane data based on the separating axis
672 Vec2 frontNormal
, sideNormal
;
673 ClipVertex
[2] incidentEdge
;
674 VFloat front
, negSide
, posSide
;
675 char negEdge
, posEdge
;
677 // Compute the clipping lines and the line segment to be clipped.
680 frontNormal
= normal
;
681 front
= /*Dot*/(posA
*frontNormal
)+hA
.x
;
682 sideNormal
= RotA
.col2
;
683 VFloat side
= /*Dot*/(posA
*sideNormal
);
684 negSide
= -side
+hA
.y
;
686 negEdge
= EdgeNumbers
.EDGE3
;
687 posEdge
= EdgeNumbers
.EDGE1
;
688 computeIncidentEdge(incidentEdge
, hB
, posB
, RotB
, frontNormal
);
691 frontNormal
= normal
;
692 front
= /*Dot*/(posA
*frontNormal
)+hA
.y
;
693 sideNormal
= RotA
.col1
;
694 VFloat side
= /*Dot*/(posA
*sideNormal
);
695 negSide
= -side
+hA
.x
;
697 negEdge
= EdgeNumbers
.EDGE2
;
698 posEdge
= EdgeNumbers
.EDGE4
;
699 computeIncidentEdge(incidentEdge
, hB
, posB
, RotB
, frontNormal
);
702 frontNormal
= -normal
;
703 front
= /*Dot*/(posB
*frontNormal
)+hB
.x
;
704 sideNormal
= RotB
.col2
;
705 VFloat side
= /*Dot*/(posB
*sideNormal
);
706 negSide
= -side
+hB
.y
;
708 negEdge
= EdgeNumbers
.EDGE3
;
709 posEdge
= EdgeNumbers
.EDGE1
;
710 computeIncidentEdge(incidentEdge
, hA
, posA
, RotA
, frontNormal
);
713 frontNormal
= -normal
;
714 front
= /*Dot*/(posB
*frontNormal
)+hB
.y
;
715 sideNormal
= RotB
.col1
;
716 VFloat side
= /*Dot*/(posB
*sideNormal
);
717 negSide
= -side
+hB
.x
;
719 negEdge
= EdgeNumbers
.EDGE2
;
720 posEdge
= EdgeNumbers
.EDGE4
;
721 computeIncidentEdge(incidentEdge
, hA
, posA
, RotA
, frontNormal
);
726 // clip other face with 5 box planes (1 face plane, 4 edge planes)
727 ClipVertex
[2] clipPoints1
;
728 ClipVertex
[2] clipPoints2
;
731 // clip to box side 1
732 np
= clipSegmentToLine(clipPoints1
, incidentEdge
, -sideNormal
, negSide
, negEdge
);
733 if (np
< 2) return 0;
735 // clip to negative box side 1
736 np
= clipSegmentToLine(clipPoints2
, clipPoints1
, sideNormal
, posSide
, posEdge
);
737 if (np
< 2) return 0;
739 // Now clipPoints2 contains the clipping points.
740 // Due to roundoff, it is possible that clipping removes all points.
743 for (int i
= 0; i
< 2; ++i
) {
744 separation
= /*Dot*/(frontNormal
*clipPoints2
[i
].v
)-front
;
745 if (separation
<= 0) {
746 contacts
[numContacts
].separation
= separation
;
747 contacts
[numContacts
].normal
= normal
;
748 // slide contact point onto reference face (easy to cull)
749 contacts
[numContacts
].position
= clipPoints2
[i
].v
-separation
*frontNormal
;
750 contacts
[numContacts
].feature
= clipPoints2
[i
].fp
;
751 if (axis
== Axis
.FACE_B_X || axis
== Axis
.FACE_B_Y
) flip(contacts
[numContacts
].feature
);
760 // ////////////////////////////////////////////////////////////////////////// //
764 static struct ArbiterKey
{
765 // pointers, actually
768 this (Body b1
, Body b2
) { if (b1
< b2
) { body1
= b1
; body2
= b2
; } else { body1
= b2
; body2
= b1
; } }
774 Arbiter
[ArbiterKey
] arbiters
;
777 Arbiter xarb
; // temporary
779 static bool accumulateImpulses
= true;
780 static bool warmStarting
= true;
781 static bool positionCorrection
= true;
784 this() (in auto ref Vec2 agravity
, int aiterations
) {
786 iterations
= aiterations
;
787 xarb
= new Arbiter();
790 void add (Body bbody
) {
791 if (bbody
!is null) bodies
~= bbody
;
794 void add (Joint joint
) {
795 if (joint
!is null) joints
~= joint
;
798 void opOpAssign(string op
:"~", T
) (T v
) if (is(T
: Body
) ||
is(T
: Joint
)) {
808 void step (VFloat
dt) {
809 VFloat inv_dt
= (dt > VFloatNum
!(0.0) ? VFloatNum
!(1.0)/dt : VFloatNum
!(0.0));
810 // determine overlapping bodies and update contact points
813 for (int i
= 0; i
< bodies
.length
; ++i
) {
815 if (b
.invMass
== VFloatNum
!(0.0)) continue;
816 b
.velocity
+= (gravity
+b
.force
*b
.invMass
)*dt;
817 b
.angularVelocity
+= dt*b
.invI
*b
.torque
;
820 foreach (Arbiter arb
; arbiters
.byValue
) arb
.preStep(inv_dt
);
821 for (int i
= 0; i
< joints
.length
; ++i
) joints
[i
].preStep(inv_dt
);
822 // perform iterations
823 for (int i
= 0; i
< iterations
; ++i
) {
824 foreach (Arbiter arb
; arbiters
.byValue
) arb
.applyImpulse();
825 for (int j
= 0; j
< joints
.length
; ++j
) joints
[j
].applyImpulse();
827 // integrate velocities
828 for (int i
= 0; i
< bodies
.length
; ++i
) {
831 b
.position
+= b
.velocity
*dt;
832 b
.rotation
+= b
.angularVelocity
*dt;
834 b
.force
.set(VFloatNum
!(0.0), VFloatNum
!(0.0));
835 b
.torque
= VFloatNum
!(0.0);
840 // O(n^2) broad-phase
841 for (int i
= 0; i
< bodies
.length
; ++i
) {
843 for (int j
= i
+1; j
< bodies
.length
; ++j
) {
845 if (bi
.invMass
== VFloatNum
!(0.0) && bj
.invMass
== VFloatNum
!(0.0)) continue;
846 if (auto arb
= ArbiterKey(bi
, bj
) in arbiters
) {
848 arb
.update(xarb
.contacts
.ptr
, xarb
.numContacts
);
850 arbiters
[ArbiterKey(bi
, bj
)] = new Arbiter(bi
, bj
);