2 * Copyright (c) 2006-2009 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 module b2dlite
.arbiter
;
14 import b2dlite
.mathutils
;
17 VFloat
clamp() (VFloat a
, VFloat low
, VFloat high
) { pragma(inline
, true); import std
.algorithm
: min
, max
; return max(low
, min(a
, high
)); }
37 VFloat separation
= VFloatNum
!(0.0);
38 VFloat Pn
= VFloatNum
!(0.0); // accumulated normal impulse
39 VFloat Pt
= VFloatNum
!(0.0); // accumulated tangent impulse
40 VFloat Pnb
= VFloatNum
!(0.0); // accumulated normal impulse for position bias
41 VFloat massNormal
, massTangent
;
42 VFloat bias
= VFloatNum
!(0.0);
48 private import b2dlite
.bbody
: Body
;
54 Contact
[MAX_POINTS
] contacts
;
65 this (Body b1
, Body b2
) { setup(b1
, b2
); }
67 void setup (Body b1
, Body b2
) {
68 import std
.math
: sqrt
;
69 import b2dlite
.collide
: collide
;
78 numContacts
= collide(contacts
.ptr
, body1
, body2
);
79 friction
= sqrt(body1
.friction
*body2
.friction
);
83 glPointSize(VFloatNum!(4.0));
84 glColor3f(VFloatNum!(1.0), VFloatNum!(0.0), VFloatNum!(0.0));
86 for (int i = 0; i < numContacts; ++i) glVertex2f(contacts[i].position.x, contacts[i].position.y);
88 glPointSize(VFloatNum!(1.0));
92 void update (Contact
* newContacts
, int numNewContacts
) {
93 import b2dlite
.world
: World
;
95 Contact
[2] mergedContacts
;
96 for (int i
= 0; i
< numNewContacts
; ++i
) {
97 Contact
*cNew
= newContacts
+i
;
99 for (int j
= 0; j
< numContacts
; ++j
) {
100 Contact
* cOld
= contacts
.ptr
+j
;
101 if (cNew
.feature
.value
== cOld
.feature
.value
) { k
= j
; break; }
104 Contact
* c
= mergedContacts
.ptr
+i
;
105 Contact
* cOld
= contacts
.ptr
+k
;
107 if (World
.warmStarting
) {
112 c
.Pn
= VFloatNum
!(0.0);
113 c
.Pt
= VFloatNum
!(0.0);
114 c
.Pnb
= VFloatNum
!(0.0);
117 mergedContacts
[i
] = newContacts
[i
];
120 for (int i
= 0; i
< numNewContacts
; ++i
) contacts
[i
] = mergedContacts
[i
];
121 numContacts
= numNewContacts
;
124 void preStep (VFloat inv_dt
) {
125 import b2dlite
.world
: World
;
126 import std
.algorithm
: min
;
128 enum k_allowedPenetration
= VFloatNum
!(0.01);
129 VFloat k_biasFactor
= (World
.positionCorrection ? VFloatNum
!(0.2) : VFloatNum
!(0.0));
130 for (int i
= 0; i
< numContacts
; ++i
) {
131 Contact
*c
= contacts
.ptr
+i
;
132 Vec2 r1
= c
.position
-body1
.position
;
133 Vec2 r2
= c
.position
-body2
.position
;
135 // precompute normal mass, tangent mass, and bias
136 VFloat rn1
= r1
*c
.normal
; //Dot(r1, c.normal);
137 VFloat rn2
= r2
*c
.normal
; //Dot(r2, c.normal);
138 VFloat kNormal
= body1
.invMass
+body2
.invMass
;
139 //kNormal += body1.invI*(Dot(r1, r1)-rn1*rn1)+body2.invI*(Dot(r2, r2)-rn2*rn2);
140 kNormal
+= body1
.invI
*((r1
*r1
)-rn1
*rn1
)+body2
.invI
*((r2
*r2
)-rn2
*rn2
);
141 c
.massNormal
= VFloatNum
!(1.0)/kNormal
;
143 Vec2 tangent
= cross(c
.normal
, VFloatNum
!(1.0));
144 VFloat rt1
= r1
*tangent
; //Dot(r1, tangent);
145 VFloat rt2
= r2
*tangent
; //Dot(r2, tangent);
146 VFloat kTangent
= body1
.invMass
+body2
.invMass
;
147 //kTangent += body1.invI*(Dot(r1, r1)-rt1*rt1)+body2.invI*(Dot(r2, r2)-rt2*rt2);
148 kTangent
+= body1
.invI
*((r1
*r1
)-rt1
*rt1
)+body2
.invI
*((r2
*r2
)-rt2
*rt2
);
149 c
.massTangent
= VFloatNum
!(1.0)/kTangent
;
151 c
.bias
= -k_biasFactor
*inv_dt
*min(VFloatNum
!(0.0), c
.separation
+k_allowedPenetration
);
153 if (World
.accumulateImpulses
) {
154 // apply normal + friction impulse
155 Vec2 P
= c
.Pn
*c
.normal
+c
.Pt
*tangent
;
157 body1
.velocity
-= body1
.invMass
*P
;
158 body1
.angularVelocity
-= body1
.invI
*cross(r1
, P
);
160 body2
.velocity
+= body2
.invMass
*P
;
161 body2
.angularVelocity
+= body2
.invI
*cross(r2
, P
);
166 void applyImpulse () {
167 import b2dlite
.world
: World
;
168 import std
.algorithm
: max
;
173 for (int i
= 0; i
< numContacts
; ++i
) {
174 Contact
*c
= contacts
.ptr
+i
;
175 c
.r1
= c
.position
-b1
.position
;
176 c
.r2
= c
.position
-b2
.position
;
178 // relative velocity at contact
179 Vec2 dv
= b2
.velocity
+cross(b2
.angularVelocity
, c
.r2
)-b1
.velocity
-cross(b1
.angularVelocity
, c
.r1
);
181 // compute normal impulse
182 VFloat vn
= dv
*c
.normal
; //Dot(dv, c.normal);
184 VFloat dPn
= c
.massNormal
*(-vn
+c
.bias
);
186 if (World
.accumulateImpulses
) {
187 // clamp the accumulated impulse
189 c
.Pn
= max(Pn0
+dPn
, VFloatNum
!(0.0));
192 dPn
= max(dPn
, VFloatNum
!(0.0));
195 // apply contact impulse
196 Vec2 Pn
= dPn
*c
.normal
;
198 b1
.velocity
-= b1
.invMass
*Pn
;
199 b1
.angularVelocity
-= b1
.invI
*cross(c
.r1
, Pn
);
201 b2
.velocity
+= b2
.invMass
*Pn
;
202 b2
.angularVelocity
+= b2
.invI
*cross(c
.r2
, Pn
);
204 // relative velocity at contact
205 dv
= b2
.velocity
+cross(b2
.angularVelocity
, c
.r2
)-b1
.velocity
-cross(b1
.angularVelocity
, c
.r1
);
207 Vec2 tangent
= cross(c
.normal
, VFloatNum
!(1.0));
208 VFloat vt
= dv
*tangent
; //Dot(dv, tangent);
209 VFloat dPt
= c
.massTangent
*(-vt
);
211 if (World
.accumulateImpulses
) {
212 // compute friction impulse
213 VFloat maxPt
= friction
*c
.Pn
;
215 VFloat oldTangentImpulse
= c
.Pt
;
216 c
.Pt
= clamp(oldTangentImpulse
+dPt
, -maxPt
, maxPt
);
217 dPt
= c
.Pt
-oldTangentImpulse
;
219 VFloat maxPt
= friction
*dPn
;
220 dPt
= clamp(dPt
, -maxPt
, maxPt
);
223 // apply contact impulse
224 Vec2 Pt
= dPt
*tangent
;
226 b1
.velocity
-= b1
.invMass
*Pt
;
227 b1
.angularVelocity
-= b1
.invI
*cross(c
.r1
, Pt
);
229 b2
.velocity
+= b2
.invMass
*Pt
;
230 b2
.angularVelocity
+= b2
.invI
*cross(c
.r2
, Pt
);