df83319bd534a199997bac1492bc88a82eedcfa9
[physics.git] / src / collisionManager.cpp
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
18 #include "collisionManager.h"
19 #include "debug.h"
20
21 #include "Vector2.h"
22
23 #include "Entities/Ball.h"
24 #include "Entities/PhysicsEntity.h"
25
26 #include "CollisionInfo.h"
27
28 /// ***** Private Class Header *****
29
30 /// ***** Private Method Headers *****
31
32 void clearEntities();
33 void placeEntity(PhysicsEntity* p);
34 void updateEntities();
35
36 void applyCollision(PhysicsEntity* p1, PhysicsEntity* p2);
37 void applyCollision(Ball* b1, Ball* b2);
38
39 bool getInfo(Ball* b1, Ball* b2, CollisionInfo* cInfo);
40
41 /// ***** Private Variables *****
42
43 const int xDivisions = 20;
44 const int yDivisions = 16;
45
46 setPhys divisions[xDivisions][yDivisions];
47
48 /// ***** Initializers/Cleaners *****
49
50 void collision::init()
51 {
52
53 }
54 void collision::clean()
55 {
56
57 }
58
59 /// ***** Public Methods *****
60
61 void collision::update(setPhys& sp)
62 {
63     clearEntities();
64
65     for( setPhys::iterator it = sp.begin();
66          it != sp.end();
67          it++ )
68     {
69         placeEntity(*it);
70     }
71
72     updateEntities();
73 }
74
75 /// ***** Private Methods *****
76
77 void clearEntities()
78 {
79     for( int x = 0;
80              x < xDivisions;
81              x++ )
82     {
83         for( int y = 0;
84                  y < yDivisions;
85                  y++ )
86         {
87             divisions[x][y].clear();
88         }
89     }
90 }
91
92 void placeEntity(PhysicsEntity* p)
93 {
94     Ball* b = dynamic_cast<Ball*>(p);
95
96     if( b == NULL )
97     {
98         DPF(0, "ENTITY TYPE NOT SUPPORTED BY placeEntity()!!");
99         return;
100     }
101
102     const float& xb     = b->positionRaw().x;
103     const float& yb     = b->positionRaw().y;
104     const float& rad    = b->radius;
105
106     for( int x = (int)(xb - rad);
107              x < (int)(xb + rad);
108              x++ )
109     {
110         if(x < 0 || xDivisions <= x)
111             break;
112
113         for( int y = (int)(yb - rad);
114                  y < (int)(yb + rad);
115                  y++ )
116         {
117             if(y < 0 || yDivisions <= y)
118                 break;
119
120             divisions[x][y].insert(p);
121         }
122     }
123 }
124
125 void updateEntities()
126 {
127     for( int x = 0;
128              x < xDivisions;
129              x++ )
130     {
131         for( int y = 0;
132                  y < yDivisions;
133                  y++ )
134         {
135             for( setPhys::iterator it1 = divisions[x][y].begin();
136                  it1 != divisions[x][y].end();
137                  it1++ )
138             {
139                 for( setPhys::iterator it2 = divisions[x][y].begin();
140                      it2 != divisions[x][y].end();
141                      it2++ )
142                 {
143                     if( *it1 != *it2 )
144                     {
145                         applyCollision(*it1, *it2);
146                     }
147                 }
148             }
149         }
150     }
151 }
152
153 void applyCollision(PhysicsEntity* p1, PhysicsEntity* p2)
154 {
155     Ball* b1 = dynamic_cast<Ball*>(p1);
156     Ball* b2 = dynamic_cast<Ball*>(p2);
157
158     if( b1 != NULL && b2 != NULL )
159     {
160         applyCollision(b1, b2);
161         return;
162     }
163
164     DPF(0, "ENTITY TYPE NOT SUPPORTED BY applyCollision()!!");
165 }
166
167 void applyCollision(Ball* b1, Ball* b2)
168 {
169     CollisionInfo   cInfo;
170
171     if(!getInfo(b1, b2, &cInfo))
172         return;
173
174     // a few values to simplify the equations
175     const Vector2& normal = cInfo.normal;
176     const Vector2& point  = cInfo.point;
177
178     float m1 = b1->mass;
179     float m2 = b2->mass;
180
181     float e = (b1->CoR + b2->CoR) / 2;
182
183     Vector2 v1 = b1->velocityRaw();
184     Vector2 v2 = b2->velocityRaw();
185
186
187     float iTop = -(e + 1) * (v1 - v2).dot(normal);
188
189     // otherwise the collision happened and we do the math the below assumes
190     // collisions have no friction
191
192     float impulse = iTop / (normal.dot(normal) * (1 / m1 + 1 / m2));
193
194     b1->applyImpulse(impulse / m1 * normal, point);
195     b2->applyImpulse(-impulse / m2 * normal, point);
196 }
197
198 bool getInfo(Ball* b1, Ball* b2, CollisionInfo* pcInfo)
199 {
200     // a few values to simplify the equations
201     float r1 = b1->radius;
202     float r2 = b2->radius;
203
204     Vector2 p1 = b1->positionRaw();
205     Vector2 p2 = b2->positionRaw();
206     Vector2 v1 = b1->velocityRaw();
207     Vector2 v2 = b2->velocityRaw();
208
209     // quick binding box check
210     if (p1.x - r1 > p2.x + r2
211      || p1.x + r1 < p2.x - r2
212      || p1.y - r1 > p2.y + r2
213      || p1.y + r1 < p2.y - r2)
214         return false;
215
216     // test if not touching
217     if ((p1 - p2).sqrLength() >= (r1 + r2)*(r1 + r2))
218         return false;
219
220     // test if they are moving apart in some way if they aren't it's likely
221     // that they collided last frame and are still overlapping
222
223     if ((v1 - v2).dot(p1 - p2) >= 0)
224         return false;
225
226     pcInfo->point   = p1 - (p1 - p2) * r1 / (r1 + r2);
227     pcInfo->normal  = p1 - p2;
228
229     return true;
230 }