// $Id$
//
// SuperTux
-// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2005 Matthias Braun <matze@braunis.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms 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 <config.h>
-#include "defines.h"
-#include "collision.h"
-#include "bitmask.h"
-#include "scene.h"
-#include "sector.h"
-#include "tilemap.h"
-#include "tile.h"
+#include "collision.hpp"
-bool rectcollision(const base_type& one, const base_type& two)
-{
- return (one.x >= two.x - one.width + 1 &&
- one.x <= two.x + two.width - 1 &&
- one.y >= two.y - one.height + 1 &&
- one.y <= two.y + two.height - 1);
-}
-
-bool rectcollision_offset(const base_type& one, const base_type& two, float off_x, float off_y)
-{
- return (one.x >= two.x - one.width + off_x + 1 &&
- one.x <= two.x + two.width + off_x - 1 &&
- one.y >= two.y - one.height + off_y + 1 &&
- one.y <= two.y + two.height + off_y - 1);
-}
-
-bool collision_object_map(const base_type& base)
-{
- const TileMap& tilemap = *Sector::current()->solids;
+#include <algorithm>
+#include <iostream>
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include "math/vector.hpp"
+#include "math/aatriangle.hpp"
+#include "math/rect.hpp"
+#include "collision_hit.hpp"
- // we make the collision rectangle 1 pixel smaller
- int starttilex = int(base.x+1) / 32;
- int starttiley = int(base.y+1) / 32;
- int max_x = int(base.x + base.width);
- int max_y = int(base.y + base.height);
+static const float DELTA = .0001;
- for(int x = starttilex; x*32 < max_x; ++x) {
- for(int y = starttiley; y*32 < max_y; ++y) {
- Tile* tile = tilemap.get_tile(x, y);
- if(tile->attributes & Tile::SOLID)
- return true;
- }
- }
-
- return false;
-}
-
-void* collision_func(const base_type& base, tiletestfunction function)
+bool
+Collision::rectangle_rectangle(CollisionHit& hit, const Rect& r1,
+ const Vector& movement, const Rect& r2)
{
- const TileMap& tilemap = *Sector::current()->solids;
-
- int starttilex = int(base.x) / 32;
- int starttiley = int(base.y) / 32;
- int max_x = int(base.x + base.width);
- int max_y = int(base.y + base.height);
-
- for(int x = starttilex; x*32 < max_x; ++x) {
- for(int y = starttiley; y*32 < max_y; ++y) {
- Tile* tile = tilemap.get_tile(x, y);
- void* result = function(tile);
- if(result != 0)
- return result;
+ if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
+ return false;
+ if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
+ return false;
+
+ if(movement.x > DELTA) {
+ hit.depth = r1.p2.x - r2.p1.x;
+ hit.time = hit.depth / movement.x;
+ hit.normal.x = -1;
+ hit.normal.y = 0;
+ } else if(movement.x < -DELTA) {
+ hit.depth = r2.p2.x - r1.p1.x;
+ hit.time = hit.depth / -movement.x;
+ hit.normal.x = 1;
+ hit.normal.y = 0;
+ } else {
+ if(movement.y > -DELTA && movement.y < DELTA) {
+ hit.time = 0;
+ hit.depth = 0;
+ hit.normal.x = 1;
+ hit.normal.y = 0;
+ return true;
}
+ hit.time = FLT_MAX;
}
- return 0;
-}
-
-static void* test_goal_tile_function(Tile* tile)
-{
- if(tile && (tile->attributes & Tile::GOAL))
- return tile;
- return 0;
-}
-
-Tile* collision_goal(const base_type& base)
-{
- return (Tile*) collision_func(base, test_goal_tile_function);
-}
-
-void collision_swept_object_map(base_type* old, base_type* current)
-{
- int steps; /* Used to speed up the collision tests, by stepping every 16pixels in the path. */
- int h;
- float lpath; /* Holds the longest path, which is either in X or Y direction. */
- float xd,yd; /* Hold the smallest steps in X and Y directions. */
- float temp, xt, yt; /* Temporary variable. */
-
- lpath = 0;
- xd = 0;
- yd = 0;
-
- if(old->x == current->x && old->y == current->y)
- {
- return;
- }
- else if(old->x == current->x && old->y != current->y)
- {
- lpath = current->y - old->y;
- if(lpath < 0)
- {
- yd = -1;
- lpath = -lpath;
- }
- else
- {
- yd = 1;
- }
-
- h = 1;
- xd = 0;
- }
- else if(old->x != current->x && old->y == current->y)
- {
- lpath = current->x - old->x;
- if(lpath < 0)
- {
- xd = -1;
- lpath = -lpath;
- }
- else
- {
- xd = 1;
- }
- h = 2;
- yd = 0;
+ if(movement.y > DELTA) {
+ float ydepth = r1.p2.y - r2.p1.y;
+ float yt = ydepth / movement.y;
+ if(yt < hit.time) {
+ hit.depth = ydepth;
+ hit.time = yt;
+ hit.normal.x = 0;
+ hit.normal.y = -1;
}
- else
- {
- lpath = current->x - old->x;
- if(lpath < 0)
- lpath = -lpath;
- if(current->y - old->y > lpath || old->y - current->y > lpath)
- lpath = current->y - old->y;
- if(lpath < 0)
- lpath = -lpath;
- h = 3;
- xd = (current->x - old->x) / lpath;
- yd = (current->y - old->y) / lpath;
+ } else if(movement.y < -DELTA) {
+ float ydepth = r2.p2.y - r1.p1.y;
+ float yt = ydepth / -movement.y;
+ if(yt < hit.time) {
+ hit.depth = ydepth;
+ hit.time = yt;
+ hit.normal.x = 0;
+ hit.normal.y = 1;
}
+ }
- steps = (int)(lpath / (float)16);
-
- float orig_x = old->x;
- float orig_y = old->y;
- old->x += xd;
- old->y += yd;
-
- for(float i = 0; i <= lpath; old->x += xd, old->y += yd, ++i)
- {
- if(steps > 0)
- {
- old->y += yd*16.;
- old->x += xd*16.;
- steps--;
- }
-
- if(collision_object_map(*old))
- {
- switch(h)
- {
- case 1:
- current->y = old->y - yd;
- while(collision_object_map(*current))
- current->y -= yd;
- break;
- case 2:
- current->x = old->x - xd;
- while(collision_object_map(*current))
- current->x -= xd;
- break;
- case 3:
- xt = current->x;
- yt = current->y;
- current->x = old->x - xd;
- current->y = old->y - yd;
- while(collision_object_map(*current))
- {
- current->x -= xd;
- current->y -= yd;
- }
-
- temp = current->x;
- current->x = xt;
- if(!collision_object_map(*current))
- break;
- current->x = temp;
- temp = current->y;
- current->y = yt;
-
- if(!collision_object_map(*current))
- {
- break;
- }
- else
- {
- current->y = temp;
- while(!collision_object_map(*current))
- current->y += yd;
- current->y -= yd;
- break;
- }
-
- break;
- default:
- break;
- }
- break;
- }
- }
-
- if((xd > 0 && current->x < orig_x) || (xd < 0 && current->x > orig_x))
- current->x = orig_x;
- if((yd > 0 && current->y < orig_y) || (yd < 0 && current->y > orig_y))
- current->y = orig_y;
-
- *old = *current;
-}
-
-Tile* gettile(float x, float y)
-{
- const TileMap& tilemap = *Sector::current()->solids;
- return tilemap.get_tile_at(Vector(x, y));
-}
-
-bool issolid(float x, float y)
-{
- Tile* tile = gettile(x,y);
- return tile && (tile->attributes & Tile::SOLID);
+ return true;
}
-bool isbrick(float x, float y)
-{
- Tile* tile = gettile(x,y);
- return tile && (tile->attributes & Tile::BRICK);
-}
+//---------------------------------------------------------------------------
-bool isice(float x, float y)
+static void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
{
- Tile* tile = gettile(x,y);
- return tile && (tile->attributes & Tile::ICE);
+ n = Vector(p2.y-p1.y, p1.x-p2.x);
+ c = -(p2 * n);
+ float nval = n.norm();
+ n /= nval;
+ c /= nval;
}
-bool isspike(float x, float y)
+bool
+Collision::rectangle_aatriangle(CollisionHit& hit, const Rect& rect,
+ const Vector& movement, const AATriangle& triangle)
{
- Tile* tile = gettile(x,y);
- return tile && (tile->attributes & Tile::SPIKE);
-}
+ if(!rectangle_rectangle(hit, rect, movement, (const Rect&) triangle))
+ return false;
+
+ Vector normal;
+ float c;
+ Vector p1;
+ Vector tp1, tp2;
+ switch(triangle.dir & AATriangle::DEFORM_MASK) {
+ case 0:
+ tp1 = triangle.p1;
+ tp2 = triangle.p2;
+ break;
+ case AATriangle::DEFORM1:
+ tp1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
+ tp2 = triangle.p2;
+ break;
+ case AATriangle::DEFORM2:
+ tp1 = triangle.p1;
+ tp2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
+ break;
+ case AATriangle::DEFORM3:
+ tp1 = triangle.p1;
+ tp2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
+ break;
+ case AATriangle::DEFORM4:
+ tp1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
+ tp2 = triangle.p2;
+ break;
+ default:
+ assert(false);
+ }
+
+ switch(triangle.dir & AATriangle::DIRECTION_MASK) {
+ case AATriangle::SOUTHWEST:
+ p1 = Vector(rect.p1.x, rect.p2.y);
+ makePlane(tp1, tp2, normal, c);
+ break;
+ case AATriangle::NORTHEAST:
+ p1 = Vector(rect.p2.x, rect.p1.y);
+ makePlane(tp2, tp1, normal, c);
+ break;
+ case AATriangle::SOUTHEAST:
+ p1 = rect.p2;
+ makePlane(Vector(tp1.x, tp2.y),
+ Vector(tp2.x, tp1.y), normal, c);
+ break;
+ case AATriangle::NORTHWEST:
+ p1 = rect.p1;
+ makePlane(Vector(tp2.x, tp1.y),
+ Vector(tp1.x, tp2.y), normal, c);
+ break;
+ default:
+ assert(false);
+ }
-bool isfullbox(float x, float y)
-{
- Tile* tile = gettile(x,y);
- return tile && (tile->attributes & Tile::FULLBOX);
-}
+ float n_p1 = -(normal * p1);
+ float depth = n_p1 - c;
+ if(depth < 0)
+ return false;
+ float time = depth / -(normal * movement);
+ if(time < hit.time) {
+ hit.depth = depth;
+ hit.time = time;
+ hit.normal = normal;
+ }
-bool iscoin(float x, float y)
-{
- Tile* tile = gettile(x,y);
- return tile && (tile->attributes & Tile::COIN);
+ return true;
}
-/* EOF */
-
-