fixed polygon penitration due to CoR
[physics.git] / src / collisionManager.cpp
CommitLineData
54fe85c5
PG
1/*
2 * Copyright (C) 2008 Patrik Gornicz, Gornicz_P (at) hotmail (dot) com.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
0ac1dc80 18#include "collisionManager.h"
54fe85c5
PG
19#include "debug.h"
20
21#include "Vector2.h"
22
23#include "Entities/Ball.h"
aa2791cf 24#include "Entities/Polygon.h"
54fe85c5
PG
25#include "Entities/PhysicsEntity.h"
26
27#include "CollisionInfo.h"
28
aa2791cf
PG
29#include "mathw.h"
30
54fe85c5
PG
31/// ***** Private Class Header *****
32
33/// ***** Private Method Headers *****
34
14597cf6
PG
35void clearEntities();
36void placeEntity(PhysicsEntity* p);
37void updateEntities();
54fe85c5 38
14597cf6
PG
39void applyCollision(PhysicsEntity* p1, PhysicsEntity* p2);
40void applyCollision(Ball* b1, Ball* b2);
aa2791cf 41void applyCollision(Polygon* pPoly, Ball* pBall);
14597cf6 42
aa2791cf
PG
43bool getInfo(const Ball* b1, const Ball* b2, CollisionInfo* cInfo);
44bool getInfo(const Polygon* pPoly, const Ball* pBall, CollisionInfo* cInfo);
54fe85c5
PG
45
46/// ***** Private Variables *****
47
14597cf6
PG
48const int xDivisions = 20;
49const int yDivisions = 16;
719d202f
PG
50const int screenX = 800;
51const int screenY = 600;
14597cf6
PG
52
53setPhys divisions[xDivisions][yDivisions];
54
54fe85c5
PG
55/// ***** Initializers/Cleaners *****
56
57void collision::init()
58{
59
60}
61void collision::clean()
62{
63
64}
65
66/// ***** Public Methods *****
67
2869e2e8 68void collision::update(setPhys& sp)
54fe85c5 69{
14597cf6
PG
70 clearEntities();
71
72 for( setPhys::iterator it = sp.begin();
73 it != sp.end();
74 it++ )
54fe85c5 75 {
14597cf6
PG
76 placeEntity(*it);
77 }
78
79 updateEntities();
80}
81
82/// ***** Private Methods *****
83
84void clearEntities()
85{
86 for( int x = 0;
87 x < xDivisions;
88 x++ )
89 {
90 for( int y = 0;
91 y < yDivisions;
92 y++ )
54fe85c5 93 {
14597cf6 94 divisions[x][y].clear();
54fe85c5
PG
95 }
96 }
14597cf6 97}
54fe85c5 98
14597cf6
PG
99void placeEntity(PhysicsEntity* p)
100{
aa2791cf
PG
101 Vector2 minP;
102 Vector2 maxP;
14597cf6 103
aa2791cf
PG
104 { // Ball case
105 Ball* b = dynamic_cast<Ball*>(p);
106
107 if( b != NULL )
108 {
109 const float& xb = b->positionRaw().x;
110 const float& yb = b->positionRaw().y;
111 const float& rad = b->radius;
112
113 minP.x = xb - rad;
114 minP.y = yb - rad;
115 maxP.x = xb + rad;
116 maxP.y = yb + rad;
117
118 goto start;
119 }
14597cf6
PG
120 }
121
aa2791cf
PG
122 { // Polygon case
123 Polygon* pPoly = dynamic_cast<Polygon*>(p);
14597cf6 124
aa2791cf
PG
125 if( pPoly != NULL )
126 {
127 minP = pPoly->minP;
128 maxP = pPoly->maxP;
129
130 goto start;
131 }
132 }
133
134 DPF(0, "ENTITY TYPE NOT SUPPORTED BY placeEntity()!!");
135 return;
136
137start:
138 for( int x = static_cast<int>( minP.x / (screenX / xDivisions) );
139 x <= static_cast<int>( maxP.x / (screenX / xDivisions) );
14597cf6
PG
140 x++ )
141 {
142 if(x < 0 || xDivisions <= x)
143 break;
144
aa2791cf
PG
145 for( int y = static_cast<int>( minP.y / (screenY / yDivisions) );
146 y <= static_cast<int>( maxP.y / (screenY / yDivisions) );
14597cf6
PG
147 y++ )
148 {
149 if(y < 0 || yDivisions <= y)
150 break;
151
152 divisions[x][y].insert(p);
153 }
154 }
54fe85c5
PG
155}
156
14597cf6
PG
157void updateEntities()
158{
159 for( int x = 0;
160 x < xDivisions;
161 x++ )
162 {
163 for( int y = 0;
164 y < yDivisions;
165 y++ )
166 {
167 for( setPhys::iterator it1 = divisions[x][y].begin();
168 it1 != divisions[x][y].end();
169 it1++ )
170 {
171 for( setPhys::iterator it2 = divisions[x][y].begin();
172 it2 != divisions[x][y].end();
173 it2++ )
174 {
1a4f2f30
PG
175 if( *it1 == *it2 )
176 break;
177
178 applyCollision(*it1, *it2);
14597cf6
PG
179 }
180 }
181 }
182 }
183}
54fe85c5 184
14597cf6 185void applyCollision(PhysicsEntity* p1, PhysicsEntity* p2)
54fe85c5 186{
aa2791cf
PG
187 {
188 Ball* b1 = dynamic_cast<Ball*>(p1);
189 Ball* b2 = dynamic_cast<Ball*>(p2);
190
191 if( b1 != NULL && b2 != NULL )
192 {
193 applyCollision(b1, b2);
194 return;
195 }
196 }
54fe85c5 197
54fe85c5 198 {
aa2791cf
PG
199 Polygon* pPoly = dynamic_cast<Polygon*>(p1);
200 Ball* pBall = dynamic_cast<Ball*>(p2);
201
202 if( pPoly != NULL && pBall != NULL )
203 {
204 applyCollision(pPoly, pBall);
205 return;
206 }
207 }
208
209 {
210 Polygon* pPoly = dynamic_cast<Polygon*>(p2);
211 Ball* pBall = dynamic_cast<Ball*>(p1);
212
213 if( pPoly != NULL && pBall != NULL )
214 {
215 applyCollision(pPoly, pBall);
216 return;
217 }
54fe85c5
PG
218 }
219
14597cf6 220 DPF(0, "ENTITY TYPE NOT SUPPORTED BY applyCollision()!!");
54fe85c5
PG
221}
222
14597cf6 223void applyCollision(Ball* b1, Ball* b2)
54fe85c5 224{
39643454 225 CollisionInfo cInfo;
54fe85c5 226
14597cf6 227 if(!getInfo(b1, b2, &cInfo))
54fe85c5
PG
228 return;
229
230 // a few values to simplify the equations
39643454
PG
231 const Vector2& normal = cInfo.normal;
232 const Vector2& point = cInfo.point;
54fe85c5
PG
233
234 float m1 = b1->mass;
235 float m2 = b2->mass;
236
237 float e = (b1->CoR + b2->CoR) / 2;
238
2869e2e8
PG
239 Vector2 v1 = b1->velocityRaw();
240 Vector2 v2 = b2->velocityRaw();
54fe85c5
PG
241
242
243 float iTop = -(e + 1) * (v1 - v2).dot(normal);
244
245 // otherwise the collision happened and we do the math the below assumes
246 // collisions have no friction
247
248 float impulse = iTop / (normal.dot(normal) * (1 / m1 + 1 / m2));
249
250 b1->applyImpulse(impulse / m1 * normal, point);
251 b2->applyImpulse(-impulse / m2 * normal, point);
54fe85c5
PG
252}
253
aa2791cf
PG
254void applyCollision(Polygon* pPoly, Ball* pBall)
255{
256 CollisionInfo cInfo;
257
258 if(!getInfo(pPoly, pBall, &cInfo))
259 return;
260
261 float fCoR = pBall->CoR;
262 Vector2 vecNorm = cInfo.normal;
263
264 Vector2 vecVelo = pBall->velocityRaw();
265
266 // impulse divided by mass
267 float idm = (-(fCoR + 1) * vecVelo.dot(vecNorm))
268 / (vecNorm.dot(vecNorm));
269
270 pBall->applyImpulse(idm * vecNorm);
271
272 // HACK
273 // CoR penetration fix, adds the jitters
274
275 // from center to point
19c2da50
PG
276 Vector2 vecCollP = vecNorm / vecNorm.length() * pBall->radius;
277 pBall->applyNudge(cInfo.point + vecCollP - pBall->positionRaw());
aa2791cf
PG
278}
279
280bool getInfo(const Ball* b1, const Ball* b2, CollisionInfo* pcInfo)
54fe85c5
PG
281{
282 // a few values to simplify the equations
283 float r1 = b1->radius;
284 float r2 = b2->radius;
285
2869e2e8
PG
286 Vector2 p1 = b1->positionRaw();
287 Vector2 p2 = b2->positionRaw();
288 Vector2 v1 = b1->velocityRaw();
289 Vector2 v2 = b2->velocityRaw();
54fe85c5
PG
290
291 // quick binding box check
292 if (p1.x - r1 > p2.x + r2
293 || p1.x + r1 < p2.x - r2
294 || p1.y - r1 > p2.y + r2
295 || p1.y + r1 < p2.y - r2)
39643454 296 return false;
54fe85c5
PG
297
298 // test if not touching
299 if ((p1 - p2).sqrLength() >= (r1 + r2)*(r1 + r2))
39643454 300 return false;
54fe85c5
PG
301
302 // test if they are moving apart in some way if they aren't it's likely
303 // that they collided last frame and are still overlapping
304
305 if ((v1 - v2).dot(p1 - p2) >= 0)
39643454
PG
306 return false;
307
308 pcInfo->point = p1 - (p1 - p2) * r1 / (r1 + r2);
309 pcInfo->normal = p1 - p2;
54fe85c5 310
39643454 311 return true;
54fe85c5 312}
aa2791cf
PG
313
314bool getInfo(const Polygon* pPoly, const Ball* pBall, CollisionInfo* pcInfo)
315{
316 // a few values to simplify the equations
317 float fRad = pBall->radius;
318 Vector2 vecPos = pBall->positionRaw();
319 Vector2 vecVelo = pBall->velocityRaw();
320
321 float fMaxX = pPoly->maxP.x;
322 float fMinX = pPoly->minP.x;
323 float fMaxY = pPoly->maxP.y;
324 float fMinY = pPoly->minP.y;
325
326 // quick binding box check
327 if (vecPos.x - fRad > fMaxX || vecPos.x + fRad < fMinX ||
328 vecPos.y - fRad > fMaxY || vecPos.y + fRad < fMinY)
329 return false;
330
331
332 Vector2 vecTotVec;
333 int iTot = 0;
334
335 {
336 const vector<Vector2>& pts = pPoly->points;
337 unsigned int num = pts.size();
338
339 for (unsigned int i = 0; i < num; i++)
340 {
341 Vector2 vec = vectorToLine(vecPos,
342 pts[i].x,
343 pts[i].y,
344 pts[(i + 1) % num].x,
345 pts[(i + 1) % num].y);
346
347 if (vec.sqrLength() <= fRad*fRad && 0 < vec.dot(vecVelo))
348 {
349 vecTotVec += vec;
350 iTot += 1;
351 }
352 }
353 }
354
355 if (iTot <= 0)
356 return false;
357
358
359 pcInfo->point = vecTotVec / iTot + vecPos;
360 pcInfo->normal = vecPos - pcInfo->point;
361
362 return true;
363}