/* Copyright (C) 2007, 2010 - Bit-Blot This file is part of Aquaria. Aquaria is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Collision.h" CollisionShape::CollisionShape() { xw = yw = 0; setType(CIRCLE); active = true; project = true; radius = 32; corners.resize(4); layer = 0; } void CollisionShape::setType(Type type) { this->type = type; } CollisionShape::Type CollisionShape::getType() { return type; } /* bool CollisionShape::compareMask(CollisionShape &c) { for (CollisionLayerMask::iterator i = c.colliderMask.begin(); i != c.colliderMask.end(); i++) { for (CollisionLayerMask::iterator j = c.collideeMask.begin(); j != c.collideeMask.end(); j++) { if ((*i)) && (*j)) { return true; } } } return false; } */ bool CollisionShape::compareLayer(CollisionShape &c) { return (this->layer <= c.layer); } void CollisionShape::updatePosition(const Vector &position) { this->position = position + offsetPosition; /* switch (getType()) { case AABB: { corners[0] = position + Vector( - xw, - yw); corners[1] = position + Vector(xw, - yw); corners[2] = position + Vector(xw, yw); corners[3] = position + Vector(-xw ,yw); } break; } */ } CollisionResult CollisionShape::findOverlap(CollisionShape &collisionShape) { CollisionResult c; switch (getType()) { case CIRCLE: { switch(collisionShape.getType()) { case CIRCLE: c = collideCircleWithCircle(collisionShape); break; case AABB: { float txw = collisionShape.xw; float tyw = collisionShape.yw; Vector d = position - collisionShape.position;//tile->obj delta int px = (txw + radius) - fabsf(d.x);//penetration depth in x if(0 < px) { int py = (tyw + radius) - fabsf(d.y);//pen depth in y if(0 < py) { //object may be colliding with tile //determine grid/voronoi region of circle center float oH = 0; float oV = 0; if(d.x < -txw) { //circle is on left side of tile oH = -1; } else if(txw < d.x) { //circle is on right side of tile oH = 1; } if(d.y < -tyw) { //circle is on top side of tile oV = -1; } else if(tyw < d.y) { //circle is on bottom side of tile oV = 1; } c = collideCircleWithAABB(collisionShape, px, py, oH, oV); //ResolveCircleTile(px,py,oH,oV,this,c); } } //return collideCircleWithAABB(collisionShape); } break; case TOP_HALF_CIRCLE: c = collideCircleWithTopHalfCircle(collisionShape); break; } } break; } return c; } CollisionResult CollisionShape::collideCircleWithCircle(CollisionShape &collisionShape) { CollisionResult c; Vector dist = position - collisionShape.position;// - position; float fastLen = dist.getSquaredLength2D(); float totalDist = (radius + collisionShape.radius); if (fastLen < (totalDist*totalDist)) { /* std::ostringstream os; os << "len " << len << " totalDist " << totalDist; msg(os.str()); */ float len = dist.getLength2D(); c.collided = true; dist.setLength2D(totalDist - len); //dist |= totalDist; c.overlap = dist; } else { c.collided = false; } return c; } float CollisionShape::getY1() { return position.y - yw; } float CollisionShape::getY2() { return position.y + yw; } float CollisionShape::getX1() { return position.x - xw; } float CollisionShape::getX2() { return position.x + xw; } void CollisionShape::render() { #ifdef BBGE_BUILD_OPENGL glTranslatef(offsetPosition.x, offsetPosition.y,0); switch(getType()) { case CIRCLE: drawCircle(radius); break; case AABB: //case CIRCLE: //glColor3f(1,1,1); //glLineWidth(2); glBegin(GL_QUADS); { glVertex2f(-xw,yw); glVertex2f(xw,yw); glVertex2f(xw,-yw); glVertex2f(-xw,-yw); /* glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glVertex2f(topLeft.x, topLeft.y); glVertex2f(bottomRight.x, topLeft.y); glVertex2f(bottomRight.x, topLeft.y); glVertex2f(bottomRight.x, bottomRight.y); glVertex2f(bottomRight.x, bottomRight.y); glVertex2f(topLeft.x, bottomRight.y); glVertex2f(topLeft.x, bottomRight.y); glVertex2f(topLeft.x, topLeft.y); */ } glEnd(); break; } glTranslatef(-offsetPosition.x, -offsetPosition.y,0); #endif //glDisable(GL_BLEND); } /* // FOR EDGES if (position.y > collisionShape.getY1() - radius && position.y < collisionShape.getY2() + radius) { float dist = collisionShape.getX1() - position.x; if (dist > 0 && dist < radius) { c.collided = true; c.overlap = Vector(radius - dist,0); } else if (dist < 0 && -dist < radius) { c.collided = true; c.overlap = -Vector(radius - (-dist),0); } } if (!c.collided) { if (position.y > collisionShape.getY1() - radius && position.y < collisionShape.getY2() + radius) { float dist = collisionShape.getX2() - position.x; if (dist > 0 && dist < radius) { c.collided = true; c.overlap += Vector(radius - dist,0); } else if (dist < 0 && -dist < radius) { c.collided = true; c.overlap += -Vector(radius - (-dist),0); } } } */ CollisionResult CollisionShape::collideCircleWithAABB(CollisionShape &collisionShape, float x, float y, int oH, int oV) { CollisionResult c; if(oH == 0) { if(oV == 0) { //collision with current cell if(x < y) { //penetration in x is smaller; project in x float dx = position.x - collisionShape.position.x;//get sign for projection along x-axis // msg("oH==0, oV ==0, x = y"); //penetration in y is smaller; project in y float dy = position.y - collisionShape.position.y;//get sign for projection along y-axis //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) if(dy < 0) { c.reportCollision(Vector(0, -y)); return c; } else { c.reportCollision(Vector(0, y)); return c; } } } else { // msg ("oH == 0, oV != 0"); c.reportCollision(Vector(0, y*oV)); return c; } } else if(oV == 0) { // msg ("oV == 0"); c.reportCollision(Vector(x*oH,0)); return c; } else { //diagonal collision //get diag vertex position float vx = collisionShape.position.x + (oH*collisionShape.xw); float vy = collisionShape.position.y + (oV*collisionShape.yw); float dx = position.x - vx - 1;//calc vert->circle vector float dy = position.y - vy - 1; float len = sqrtf(dx*dx + dy*dy); float pen = radius - len; if(0 < pen) { //vertex is in the circle; project outward if(len == 0) { //project out by 45deg dx = oH / SQRT2; dy = oV / SQRT2; } else { dx /= len; dy /= len; } c.reportCollision(Vector(dx*pen, dy*pen)); //obj.ReportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); return c; } } return c; } bool CollisionShape::isPointWithin(Vector point) { switch (this->getType()) { case CIRCLE: { Vector dist = point - this->position; return (dist.getSquaredLength2D() < sqr(this->radius)); } break; case AABB: { if (point.x < position.x + xw && point.y < position.y + yw) { if (point.x > position.x - xw && point.y > position.y - yw) { return true; } } } break; } return false; } CollisionResult CollisionShape::collideCircleWithTopHalfCircle(CollisionShape &collisionShape) { CollisionResult c; Vector dist = collisionShape.position - position; float len = dist.getLength2D(); float totalDist = (radius + collisionShape.radius); //which edge did we hit? if (collisionShape.position.y > (position.y - (radius/2))) { if (len < collisionShape.radius) { c.collided = true; c.overlap = Vector(0, position.y - (collisionShape.position.y - collisionShape.radius)); } } else if (len < totalDist && dist.y > 0) { c.collided = true; dist.setLength2D(totalDist - len); c.overlap = dist; } else { c.collided = false; } return c; }