Commit | Line | Data |
---|---|---|
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 | 19 | |
f32a9b7c PG |
20 | #include <pg/debug.h> |
21 | #include <pg/Vector2.h> | |
22 | #include <pg/mathw.h> | |
54fe85c5 PG |
23 | |
24 | #include "Entities/Ball.h" | |
aa2791cf | 25 | #include "Entities/Polygon.h" |
54fe85c5 PG |
26 | #include "Entities/PhysicsEntity.h" |
27 | ||
28 | #include "CollisionInfo.h" | |
29 | ||
54fe85c5 PG |
30 | /// ***** Private Method Headers ***** |
31 | ||
7d8eb699 PG |
32 | static void clearEntities(); |
33 | static void placeEntity(PhysicsEntity*); | |
34 | static void updateEntities(); | |
54fe85c5 | 35 | |
7d8eb699 PG |
36 | static void applyCollision(PhysicsEntity*, PhysicsEntity*); |
37 | static void applyCollision(Ball*, Ball*); | |
38 | static void applyCollision(Polygon*, Ball*); | |
14597cf6 | 39 | |
7d8eb699 PG |
40 | static bool getInfo(const Ball*, const Ball*, CollisionInfo*); |
41 | static bool getInfo(const Polygon*, const Ball*, CollisionInfo*); | |
54fe85c5 PG |
42 | |
43 | /// ***** Private Variables ***** | |
44 | ||
7d8eb699 PG |
45 | static const int sc_ixDivisions = 20; |
46 | static const int sc_iyDivisions = 16; | |
47 | static const int sc_ixScreenSize = 800; | |
48 | static const int sc_iyScreenSize = 600; | |
14597cf6 | 49 | |
7d8eb699 | 50 | setPhys divisions[sc_ixDivisions][sc_iyDivisions]; |
14597cf6 | 51 | |
54fe85c5 PG |
52 | /// ***** Initializers/Cleaners ***** |
53 | ||
54 | void collision::init() | |
55 | { | |
56 | ||
57 | } | |
58 | void collision::clean() | |
59 | { | |
60 | ||
61 | } | |
62 | ||
63 | /// ***** Public Methods ***** | |
64 | ||
2869e2e8 | 65 | void collision::update(setPhys& sp) |
54fe85c5 | 66 | { |
14597cf6 PG |
67 | clearEntities(); |
68 | ||
69 | for( setPhys::iterator it = sp.begin(); | |
70 | it != sp.end(); | |
71 | it++ ) | |
54fe85c5 | 72 | { |
14597cf6 PG |
73 | placeEntity(*it); |
74 | } | |
75 | ||
76 | updateEntities(); | |
77 | } | |
78 | ||
79 | /// ***** Private Methods ***** | |
80 | ||
81 | void clearEntities() | |
82 | { | |
83 | for( int x = 0; | |
7d8eb699 | 84 | x < sc_ixDivisions; |
14597cf6 PG |
85 | x++ ) |
86 | { | |
87 | for( int y = 0; | |
7d8eb699 | 88 | y < sc_iyDivisions; |
14597cf6 | 89 | y++ ) |
54fe85c5 | 90 | { |
14597cf6 | 91 | divisions[x][y].clear(); |
54fe85c5 PG |
92 | } |
93 | } | |
14597cf6 | 94 | } |
54fe85c5 | 95 | |
7d8eb699 | 96 | void placeEntity(PhysicsEntity* ppe) |
14597cf6 | 97 | { |
7d8eb699 PG |
98 | DASSERT(ppe != NULL); |
99 | ||
100 | Vector2 vecMin; | |
101 | Vector2 vecMax; | |
14597cf6 | 102 | |
aa2791cf | 103 | { // Ball case |
7d8eb699 | 104 | Ball* pBall = dynamic_cast<Ball*>(ppe); |
aa2791cf | 105 | |
7d8eb699 | 106 | if( pBall != NULL ) |
aa2791cf | 107 | { |
7d8eb699 PG |
108 | const float& xb = pBall->positionRaw().m_fX; |
109 | const float& yb = pBall->positionRaw().m_fY; | |
110 | const float& rad = pBall->radius; | |
aa2791cf | 111 | |
7d8eb699 PG |
112 | vecMin.m_fX = xb - rad; |
113 | vecMin.m_fY = yb - rad; | |
114 | vecMax.m_fX = xb + rad; | |
115 | vecMax.m_fY = yb + rad; | |
aa2791cf PG |
116 | |
117 | goto start; | |
118 | } | |
14597cf6 PG |
119 | } |
120 | ||
aa2791cf | 121 | { // Polygon case |
7d8eb699 | 122 | Polygon* pPoly = dynamic_cast<Polygon*>(ppe); |
14597cf6 | 123 | |
aa2791cf PG |
124 | if( pPoly != NULL ) |
125 | { | |
7d8eb699 PG |
126 | vecMin = pPoly->minP; |
127 | vecMax = pPoly->maxP; | |
aa2791cf PG |
128 | |
129 | goto start; | |
130 | } | |
131 | } | |
132 | ||
133 | DPF(0, "ENTITY TYPE NOT SUPPORTED BY placeEntity()!!"); | |
134 | return; | |
135 | ||
136 | start: | |
7d8eb699 PG |
137 | for( int x = static_cast<int>( vecMin.m_fX / (sc_ixScreenSize / sc_ixDivisions) ); |
138 | x <= static_cast<int>( vecMax.m_fX / (sc_ixScreenSize / sc_ixDivisions) ); | |
14597cf6 PG |
139 | x++ ) |
140 | { | |
7d8eb699 | 141 | if(x < 0 || sc_ixDivisions <= x) |
14597cf6 PG |
142 | break; |
143 | ||
7d8eb699 PG |
144 | for( int y = static_cast<int>( vecMin.m_fY / (sc_iyScreenSize / sc_iyDivisions) ); |
145 | y <= static_cast<int>( vecMax.m_fY / (sc_iyScreenSize / sc_iyDivisions) ); | |
14597cf6 PG |
146 | y++ ) |
147 | { | |
7d8eb699 | 148 | if(y < 0 || sc_iyDivisions <= y) |
14597cf6 PG |
149 | break; |
150 | ||
7d8eb699 | 151 | divisions[x][y].insert(ppe); |
14597cf6 PG |
152 | } |
153 | } | |
54fe85c5 PG |
154 | } |
155 | ||
14597cf6 PG |
156 | void updateEntities() |
157 | { | |
158 | for( int x = 0; | |
7d8eb699 | 159 | x < sc_ixDivisions; |
14597cf6 PG |
160 | x++ ) |
161 | { | |
162 | for( int y = 0; | |
7d8eb699 | 163 | y < sc_iyDivisions; |
14597cf6 PG |
164 | y++ ) |
165 | { | |
166 | for( setPhys::iterator it1 = divisions[x][y].begin(); | |
167 | it1 != divisions[x][y].end(); | |
168 | it1++ ) | |
169 | { | |
170 | for( setPhys::iterator it2 = divisions[x][y].begin(); | |
171 | it2 != divisions[x][y].end(); | |
172 | it2++ ) | |
173 | { | |
1a4f2f30 PG |
174 | if( *it1 == *it2 ) |
175 | break; | |
176 | ||
177 | applyCollision(*it1, *it2); | |
14597cf6 PG |
178 | } |
179 | } | |
180 | } | |
181 | } | |
182 | } | |
54fe85c5 | 183 | |
7d8eb699 | 184 | void applyCollision(PhysicsEntity* ppe1, PhysicsEntity* ppe2) |
54fe85c5 | 185 | { |
7d8eb699 | 186 | DASSERT(ppe1 != NULL && ppe2 != NULL); |
aa2791cf | 187 | |
7d8eb699 PG |
188 | {// Ball vs Ball |
189 | Ball* pb1 = dynamic_cast<Ball*>(ppe1); | |
190 | Ball* pb2 = dynamic_cast<Ball*>(ppe2); | |
191 | ||
192 | if( pb1 != NULL && pb2 != NULL ) | |
aa2791cf | 193 | { |
7d8eb699 | 194 | applyCollision(pb1, pb2); |
aa2791cf PG |
195 | return; |
196 | } | |
197 | } | |
54fe85c5 | 198 | |
7d8eb699 PG |
199 | {// Polygon vs Ball |
200 | Polygon* pPoly = dynamic_cast<Polygon*>(ppe1); | |
201 | Ball* pBall = dynamic_cast<Ball*>(ppe2); | |
aa2791cf PG |
202 | |
203 | if( pPoly != NULL && pBall != NULL ) | |
204 | { | |
205 | applyCollision(pPoly, pBall); | |
206 | return; | |
207 | } | |
208 | } | |
209 | ||
7d8eb699 PG |
210 | {// Ball vs Polygon |
211 | Polygon* pPoly = dynamic_cast<Polygon*>(ppe2); | |
212 | Ball* pBall = dynamic_cast<Ball*>(ppe1); | |
aa2791cf PG |
213 | |
214 | if( pPoly != NULL && pBall != NULL ) | |
215 | { | |
216 | applyCollision(pPoly, pBall); | |
217 | return; | |
218 | } | |
54fe85c5 PG |
219 | } |
220 | ||
14597cf6 | 221 | DPF(0, "ENTITY TYPE NOT SUPPORTED BY applyCollision()!!"); |
54fe85c5 PG |
222 | } |
223 | ||
7d8eb699 | 224 | void applyCollision(Ball* pb1, Ball* pb2) |
54fe85c5 | 225 | { |
7d8eb699 PG |
226 | DASSERT(pb1 != NULL && pb2 != NULL); |
227 | ||
39643454 | 228 | CollisionInfo cInfo; |
54fe85c5 | 229 | |
7d8eb699 | 230 | if(!getInfo(pb1, pb2, &cInfo)) |
54fe85c5 PG |
231 | return; |
232 | ||
233 | // a few values to simplify the equations | |
7d8eb699 PG |
234 | const Vector2& vecNormal = cInfo.m_vecNormal; |
235 | const Vector2& vecPoint = cInfo.m_vecPoint; | |
54fe85c5 | 236 | |
7d8eb699 PG |
237 | float m1 = pb1->mass; |
238 | float m2 = pb2->mass; | |
54fe85c5 | 239 | |
7d8eb699 | 240 | float e = (pb1->CoR + pb2->CoR) / 2; |
54fe85c5 | 241 | |
7d8eb699 PG |
242 | Vector2 v1 = pb1->velocityRaw(); |
243 | Vector2 v2 = pb2->velocityRaw(); | |
54fe85c5 PG |
244 | |
245 | ||
7d8eb699 | 246 | float iTop = -(e + 1) * (v1 - v2).dot(vecNormal); |
54fe85c5 PG |
247 | |
248 | // otherwise the collision happened and we do the math the below assumes | |
249 | // collisions have no friction | |
250 | ||
7d8eb699 | 251 | float impulse = iTop / (vecNormal.dot(vecNormal) * (1 / m1 + 1 / m2)); |
54fe85c5 | 252 | |
7d8eb699 PG |
253 | pb1->applyImpulse(impulse / m1 * vecNormal, vecPoint); |
254 | pb2->applyImpulse(-impulse / m2 * vecNormal, vecPoint); | |
54fe85c5 PG |
255 | } |
256 | ||
aa2791cf PG |
257 | void applyCollision(Polygon* pPoly, Ball* pBall) |
258 | { | |
7d8eb699 PG |
259 | DASSERT(pPoly != NULL && pBall != NULL); |
260 | ||
aa2791cf PG |
261 | CollisionInfo cInfo; |
262 | ||
263 | if(!getInfo(pPoly, pBall, &cInfo)) | |
264 | return; | |
265 | ||
7d8eb699 PG |
266 | // a few values to simplify the equations |
267 | const Vector2& vecNorm = cInfo.m_vecNormal; | |
aa2791cf | 268 | |
7d8eb699 | 269 | float fCoR = pBall->CoR; |
aa2791cf PG |
270 | Vector2 vecVelo = pBall->velocityRaw(); |
271 | ||
272 | // impulse divided by mass | |
273 | float idm = (-(fCoR + 1) * vecVelo.dot(vecNorm)) | |
274 | / (vecNorm.dot(vecNorm)); | |
275 | ||
276 | pBall->applyImpulse(idm * vecNorm); | |
277 | ||
278 | // HACK | |
7d8eb699 | 279 | // CoR penetration fix, adds the polygon-ball jitters |
aa2791cf PG |
280 | |
281 | // from center to point | |
19c2da50 | 282 | Vector2 vecCollP = vecNorm / vecNorm.length() * pBall->radius; |
e86c86b3 | 283 | pBall->applyNudge(cInfo.m_vecPoint + vecCollP - pBall->positionRaw()); |
aa2791cf PG |
284 | } |
285 | ||
7d8eb699 | 286 | bool getInfo(const Ball* pb1, const Ball* pb2, CollisionInfo* pcInfo) |
54fe85c5 PG |
287 | { |
288 | // a few values to simplify the equations | |
7d8eb699 PG |
289 | float r1 = pb1->radius; |
290 | float r2 = pb2->radius; | |
54fe85c5 | 291 | |
7d8eb699 PG |
292 | Vector2 p1 = pb1->positionRaw(); |
293 | Vector2 p2 = pb2->positionRaw(); | |
294 | Vector2 v1 = pb1->velocityRaw(); | |
295 | Vector2 v2 = pb2->velocityRaw(); | |
54fe85c5 PG |
296 | |
297 | // quick binding box check | |
9d6dea5f PG |
298 | if (p1.m_fX - r1 > p2.m_fX + r2 |
299 | || p1.m_fX + r1 < p2.m_fX - r2 | |
300 | || p1.m_fY - r1 > p2.m_fY + r2 | |
301 | || p1.m_fY + r1 < p2.m_fY - r2) | |
39643454 | 302 | return false; |
54fe85c5 PG |
303 | |
304 | // test if not touching | |
305 | if ((p1 - p2).sqrLength() >= (r1 + r2)*(r1 + r2)) | |
39643454 | 306 | return false; |
54fe85c5 | 307 | |
7d8eb699 | 308 | // test if they are moving apart in some way if they are it's likely |
54fe85c5 PG |
309 | // that they collided last frame and are still overlapping |
310 | ||
311 | if ((v1 - v2).dot(p1 - p2) >= 0) | |
39643454 PG |
312 | return false; |
313 | ||
e86c86b3 PG |
314 | pcInfo->m_vecPoint = p1 - (p1 - p2) * r1 / (r1 + r2); |
315 | pcInfo->m_vecNormal = p1 - p2; | |
54fe85c5 | 316 | |
39643454 | 317 | return true; |
54fe85c5 | 318 | } |
aa2791cf PG |
319 | |
320 | bool getInfo(const Polygon* pPoly, const Ball* pBall, CollisionInfo* pcInfo) | |
321 | { | |
322 | // a few values to simplify the equations | |
323 | float fRad = pBall->radius; | |
324 | Vector2 vecPos = pBall->positionRaw(); | |
325 | Vector2 vecVelo = pBall->velocityRaw(); | |
326 | ||
9d6dea5f PG |
327 | float fMaxX = pPoly->maxP.m_fX; |
328 | float fMinX = pPoly->minP.m_fX; | |
329 | float fMaxY = pPoly->maxP.m_fY; | |
330 | float fMinY = pPoly->minP.m_fY; | |
aa2791cf PG |
331 | |
332 | // quick binding box check | |
9d6dea5f PG |
333 | if (vecPos.m_fX - fRad > fMaxX || vecPos.m_fX + fRad < fMinX || |
334 | vecPos.m_fY - fRad > fMaxY || vecPos.m_fY + fRad < fMinY) | |
aa2791cf PG |
335 | return false; |
336 | ||
337 | ||
338 | Vector2 vecTotVec; | |
339 | int iTot = 0; | |
340 | ||
341 | { | |
342 | const vector<Vector2>& pts = pPoly->points; | |
343 | unsigned int num = pts.size(); | |
344 | ||
345 | for (unsigned int i = 0; i < num; i++) | |
346 | { | |
347 | Vector2 vec = vectorToLine(vecPos, | |
9d6dea5f PG |
348 | pts[i].m_fX, |
349 | pts[i].m_fY, | |
350 | pts[(i + 1) % num].m_fX, | |
351 | pts[(i + 1) % num].m_fY); | |
aa2791cf PG |
352 | |
353 | if (vec.sqrLength() <= fRad*fRad && 0 < vec.dot(vecVelo)) | |
354 | { | |
355 | vecTotVec += vec; | |
356 | iTot += 1; | |
357 | } | |
358 | } | |
359 | } | |
360 | ||
361 | if (iTot <= 0) | |
362 | return false; | |
363 | ||
364 | ||
e86c86b3 PG |
365 | pcInfo->m_vecPoint = vecTotVec / iTot + vecPos; |
366 | pcInfo->m_vecNormal = vecPos - pcInfo->m_vecPoint; | |
aa2791cf PG |
367 | |
368 | return true; | |
369 | } |