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