1 /*
2 Copyright (C) 2007, 2010 - Bit-Blot
3
4 This file is part of Aquaria.
5
6 Aquaria is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21 #include "Collision.h"
22
CollisionShape()23 CollisionShape::CollisionShape()
24 {
25 xw = yw = 0;
26 setType(CIRCLE);
27 active = true;
28 project = true;
29 radius = 32;
30 corners.resize(4);
31 layer = 0;
32 }
33
setType(Type type)34 void CollisionShape::setType(Type type)
35 {
36 this->type = type;
37 }
38
getType()39 CollisionShape::Type CollisionShape::getType()
40 {
41 return type;
42 }
43
44 /*
45 bool CollisionShape::compareMask(CollisionShape &c)
46 {
47 for (CollisionLayerMask::iterator i = c.colliderMask.begin(); i != c.colliderMask.end(); i++)
48 {
49 for (CollisionLayerMask::iterator j = c.collideeMask.begin(); j != c.collideeMask.end(); j++)
50 {
51 if ((*i)) && (*j))
52 {
53 return true;
54 }
55 }
56 }
57 return false;
58 }
59 */
compareLayer(CollisionShape & c)60 bool CollisionShape::compareLayer(CollisionShape &c)
61 {
62 return (this->layer <= c.layer);
63 }
64
updatePosition(const Vector & position)65 void CollisionShape::updatePosition(const Vector &position)
66 {
67 this->position = position + offsetPosition;
68 /*
69 switch (getType())
70 {
71 case AABB:
72 {
73 corners[0] = position + Vector( - xw, - yw);
74 corners[1] = position + Vector(xw, - yw);
75 corners[2] = position + Vector(xw, yw);
76 corners[3] = position + Vector(-xw ,yw);
77 }
78 break;
79 }
80 */
81
82 }
83
findOverlap(CollisionShape & collisionShape)84 CollisionResult CollisionShape::findOverlap(CollisionShape &collisionShape)
85 {
86 CollisionResult c;
87
88
89 switch (getType())
90 {
91 case CIRCLE:
92 {
93 switch(collisionShape.getType())
94 {
95 case CIRCLE:
96 c = collideCircleWithCircle(collisionShape);
97 break;
98 case AABB:
99 {
100
101 float txw = collisionShape.xw;
102 float tyw = collisionShape.yw;
103
104 Vector d = position - collisionShape.position;//tile->obj delta
105 int px = (txw + radius) - fabsf(d.x);//penetration depth in x
106
107 if(0 < px)
108 {
109 int py = (tyw + radius) - fabsf(d.y);//pen depth in y
110
111 if(0 < py)
112 {
113 //object may be colliding with tile
114
115 //determine grid/voronoi region of circle center
116 float oH = 0;
117 float oV = 0;
118 if(d.x < -txw)
119 {
120 //circle is on left side of tile
121 oH = -1;
122 }
123 else if(txw < d.x)
124 {
125 //circle is on right side of tile
126 oH = 1;
127 }
128
129 if(d.y < -tyw)
130 {
131 //circle is on top side of tile
132 oV = -1;
133 }
134 else if(tyw < d.y)
135 {
136 //circle is on bottom side of tile
137 oV = 1;
138 }
139
140 c = collideCircleWithAABB(collisionShape, px, py, oH, oV);
141 //ResolveCircleTile(px,py,oH,oV,this,c);
142
143 }
144 }
145 //return collideCircleWithAABB(collisionShape);
146 }
147 break;
148 case TOP_HALF_CIRCLE:
149 c = collideCircleWithTopHalfCircle(collisionShape);
150 break;
151 }
152 }
153 break;
154 }
155 return c;
156 }
157
collideCircleWithCircle(CollisionShape & collisionShape)158 CollisionResult CollisionShape::collideCircleWithCircle(CollisionShape &collisionShape)
159 {
160 CollisionResult c;
161 Vector dist = position - collisionShape.position;// - position;
162 float fastLen = dist.getSquaredLength2D();
163 float totalDist = (radius + collisionShape.radius);
164 if (fastLen < (totalDist*totalDist))
165 {
166 /*
167 std::ostringstream os;
168 os << "len " << len << " totalDist " << totalDist;
169 msg(os.str());
170 */
171 float len = dist.getLength2D();
172 c.collided = true;
173 dist.setLength2D(totalDist - len);
174 //dist |= totalDist;
175 c.overlap = dist;
176 }
177 else
178 {
179 c.collided = false;
180 }
181 return c;
182 }
183
getY1()184 float CollisionShape::getY1()
185 {
186 return position.y - yw;
187 }
188
getY2()189 float CollisionShape::getY2()
190 {
191 return position.y + yw;
192 }
193
getX1()194 float CollisionShape::getX1()
195 {
196 return position.x - xw;
197 }
198
getX2()199 float CollisionShape::getX2()
200 {
201 return position.x + xw;
202 }
203
render()204 void CollisionShape::render()
205 {
206 #ifdef BBGE_BUILD_OPENGL
207 glTranslatef(offsetPosition.x, offsetPosition.y,0);
208 switch(getType())
209 {
210 case CIRCLE:
211 drawCircle(radius);
212 break;
213 case AABB:
214 //case CIRCLE:
215 //glColor3f(1,1,1);
216
217 //glLineWidth(2);
218
219 glBegin(GL_QUADS);
220 {
221 glVertex2f(-xw,yw);
222 glVertex2f(xw,yw);
223 glVertex2f(xw,-yw);
224 glVertex2f(-xw,-yw);
225
226 /*
227 glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
228 glVertex2f(topLeft.x, topLeft.y);
229 glVertex2f(bottomRight.x, topLeft.y);
230
231 glVertex2f(bottomRight.x, topLeft.y);
232 glVertex2f(bottomRight.x, bottomRight.y);
233
234 glVertex2f(bottomRight.x, bottomRight.y);
235 glVertex2f(topLeft.x, bottomRight.y);
236
237 glVertex2f(topLeft.x, bottomRight.y);
238 glVertex2f(topLeft.x, topLeft.y);
239
240 */
241 }
242 glEnd();
243
244 break;
245 }
246
247 glTranslatef(-offsetPosition.x, -offsetPosition.y,0);
248 #endif
249 //glDisable(GL_BLEND);
250
251 }
252
253 /*
254
255 // FOR EDGES
256
257 if (position.y > collisionShape.getY1() - radius && position.y < collisionShape.getY2() + radius)
258 {
259 float dist = collisionShape.getX1() - position.x;
260 if (dist > 0 && dist < radius)
261 {
262 c.collided = true;
263 c.overlap = Vector(radius - dist,0);
264 }
265 else if (dist < 0 && -dist < radius)
266 {
267 c.collided = true;
268 c.overlap = -Vector(radius - (-dist),0);
269 }
270 }
271
272 if (!c.collided)
273 {
274 if (position.y > collisionShape.getY1() - radius && position.y < collisionShape.getY2() + radius)
275 {
276 float dist = collisionShape.getX2() - position.x;
277 if (dist > 0 && dist < radius)
278 {
279 c.collided = true;
280 c.overlap += Vector(radius - dist,0);
281 }
282 else if (dist < 0 && -dist < radius)
283 {
284 c.collided = true;
285 c.overlap += -Vector(radius - (-dist),0);
286 }
287 }
288 }
289 */
290
collideCircleWithAABB(CollisionShape & collisionShape,float x,float y,int oH,int oV)291 CollisionResult CollisionShape::collideCircleWithAABB(CollisionShape &collisionShape, float x, float y, int oH, int oV)
292 {
293 CollisionResult c;
294 if(oH == 0)
295 {
296 if(oV == 0)
297 {
298
299 //collision with current cell
300 if(x < y)
301 {
302 //penetration in x is smaller; project in x
303 float dx = position.x - collisionShape.position.x;//get sign for projection along x-axis
304
305
306
307 // msg("oH==0, oV ==0, x <y");
308 //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?)
309 if(dx < 0)
310 {
311 c.reportCollision(Vector(-x, 0));
312 return c;
313 }
314 else
315 {
316 c.reportCollision(Vector(x,0));
317 return c;
318 }
319 }
320 else
321 {
322 // msg("oH==0, oV ==0, x >= y");
323 //penetration in y is smaller; project in y
324 float dy = position.y - collisionShape.position.y;//get sign for projection along y-axis
325
326 //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?)
327 if(dy < 0)
328 {
329 c.reportCollision(Vector(0, -y));
330 return c;
331 }
332 else
333 {
334 c.reportCollision(Vector(0, y));
335 return c;
336 }
337 }
338 }
339 else
340 {
341 // msg ("oH == 0, oV != 0");
342 c.reportCollision(Vector(0, y*oV));
343 return c;
344 }
345 }
346 else if(oV == 0)
347 {
348 // msg ("oV == 0");
349 c.reportCollision(Vector(x*oH,0));
350 return c;
351 }
352 else
353 {
354 //diagonal collision
355
356 //get diag vertex position
357 float vx = collisionShape.position.x + (oH*collisionShape.xw);
358 float vy = collisionShape.position.y + (oV*collisionShape.yw);
359
360 float dx = position.x - vx - 1;//calc vert->circle vector
361 float dy = position.y - vy - 1;
362
363 float len = sqrtf(dx*dx + dy*dy);
364 float pen = radius - len;
365 if(0 < pen)
366 {
367 //vertex is in the circle; project outward
368 if(len == 0)
369 {
370 //project out by 45deg
371 dx = oH / SQRT2;
372 dy = oV / SQRT2;
373 }
374 else
375 {
376 dx /= len;
377 dy /= len;
378 }
379
380 c.reportCollision(Vector(dx*pen, dy*pen));
381 //obj.ReportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t);
382
383 return c;
384 }
385 }
386
387 return c;
388
389 }
390
isPointWithin(Vector point)391 bool CollisionShape::isPointWithin(Vector point)
392 {
393 switch (this->getType())
394 {
395 case CIRCLE:
396 {
397 Vector dist = point - this->position;
398 return (dist.getSquaredLength2D() < sqr(this->radius));
399 }
400 break;
401 case AABB:
402 {
403 if (point.x < position.x + xw && point.y < position.y + yw)
404 {
405 if (point.x > position.x - xw && point.y > position.y - yw)
406 {
407 return true;
408 }
409 }
410 }
411 break;
412 }
413 return false;
414 }
415
collideCircleWithTopHalfCircle(CollisionShape & collisionShape)416 CollisionResult CollisionShape::collideCircleWithTopHalfCircle(CollisionShape &collisionShape)
417 {
418 CollisionResult c;
419 Vector dist = collisionShape.position - position;
420 float len = dist.getLength2D();
421 float totalDist = (radius + collisionShape.radius);
422
423 //which edge did we hit?
424 if (collisionShape.position.y > (position.y - (radius/2)))
425 {
426 if (len < collisionShape.radius)
427 {
428 c.collided = true;
429 c.overlap = Vector(0, position.y - (collisionShape.position.y - collisionShape.radius));
430 }
431 }
432 else if (len < totalDist && dist.y > 0)
433 {
434 c.collided = true;
435 dist.setLength2D(totalDist - len);
436 c.overlap = dist;
437 }
438 else
439 {
440 c.collided = false;
441 }
442 return c;
443 }
444