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