fix cr/lfs and remove trailing whitespaces...
[supertux.git] / src / collision.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program 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.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include "collision.hpp"
23
24 #include <algorithm>
25 #include <iostream>
26 #include <stdio.h>
27 #include <float.h>
28 #include <math.h>
29 #include "math/vector.hpp"
30 #include "math/aatriangle.hpp"
31 #include "math/rect.hpp"
32 #include "collision_hit.hpp"
33 #include "log.hpp"
34
35 namespace collision
36 {
37
38 bool intersects(const Rect& r1, const Rect& r2)
39 {
40   if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
41     return false;
42   if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
43     return false;
44
45   return true;
46 }
47
48 //---------------------------------------------------------------------------
49
50 namespace {
51   inline void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
52   {
53     n = Vector(p2.y-p1.y, p1.x-p2.x);
54     c = -(p2 * n);
55     float nval = n.norm();
56     n /= nval;
57     c /= nval;
58   }
59
60   static const float DELTA = .0001;
61 }
62
63 bool rectangle_aatriangle(Constraints* constraints, const Rect& rect,
64     const AATriangle& triangle)
65 {
66   if(!intersects(rect, (const Rect&) triangle))
67     return false;
68
69   Vector normal;
70   float c;
71   Vector p1;
72   Rect area;
73   switch(triangle.dir & AATriangle::DEFORM_MASK) {
74     case 0:
75       area.p1 = triangle.p1;
76       area.p2 = triangle.p2;
77       break;
78     case AATriangle::DEFORM1:
79       area.p1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
80       area.p2 = triangle.p2;
81       break;
82     case AATriangle::DEFORM2:
83       area.p1 = triangle.p1;
84       area.p2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
85       break;
86     case AATriangle::DEFORM3:
87       area.p1 = triangle.p1;
88       area.p2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
89       break;
90     case AATriangle::DEFORM4:
91       area.p1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
92       area.p2 = triangle.p2;
93       break;
94     default:
95       assert(false);
96   }
97
98   switch(triangle.dir & AATriangle::DIRECTION_MASK) {
99     case AATriangle::SOUTHWEST:
100       p1 = Vector(rect.p1.x, rect.p2.y);
101       makePlane(area.p1, area.p2, normal, c);
102       break;
103     case AATriangle::NORTHEAST:
104       p1 = Vector(rect.p2.x, rect.p1.y);
105       makePlane(area.p2, area.p1, normal, c);
106       break;
107     case AATriangle::SOUTHEAST:
108       p1 = rect.p2;
109       makePlane(Vector(area.p1.x, area.p2.y),
110           Vector(area.p2.x, area.p1.y), normal, c);
111       break;
112     case AATriangle::NORTHWEST:
113       p1 = rect.p1;
114       makePlane(Vector(area.p2.x, area.p1.y),
115           Vector(area.p1.x, area.p2.y), normal, c);
116       break;
117     default:
118       assert(false);
119   }
120
121   float n_p1 = -(normal * p1);
122   float depth = n_p1 - c;
123   if(depth < 0)
124     return false;
125
126 #if 0
127   std::cout << "R: " << rect << " Tri: " << triangle << "\n";
128   std::cout << "Norm: " << normal << " Depth: " << depth << "\n";
129 #endif
130
131   Vector outvec = normal * (depth + 0.2);
132
133   const float RDELTA = 3;
134   if(p1.x < area.p1.x - RDELTA || p1.x > area.p2.x + RDELTA
135         || p1.y < area.p1.y - RDELTA || p1.y > area.p2.y + RDELTA) {
136     set_rectangle_rectangle_constraints(constraints, rect, area);
137     constraints->hit.left = false;
138     constraints->hit.right = false;
139   } else {
140     if(outvec.x < 0) {
141       constraints->right = rect.get_right() + outvec.x;
142     } else {
143       constraints->left = rect.get_left() + outvec.x;
144     }
145
146     if(outvec.y < 0) {
147       constraints->bottom = rect.get_bottom() + outvec.y;
148       constraints->hit.bottom = true;
149     } else {
150       constraints->top = rect.get_top() + outvec.y;
151       constraints->hit.top = true;
152     }
153     constraints->hit.slope_normal = normal;
154   }
155
156   return true;
157 }
158
159 void set_rectangle_rectangle_constraints(Constraints* constraints,
160         const Rect& r1, const Rect& r2)
161 {
162   float itop = r1.get_bottom() - r2.get_top();
163   float ibottom = r2.get_bottom() - r1.get_top();
164   float ileft = r1.get_right() - r2.get_left();
165   float iright = r2.get_right() - r1.get_left();
166
167   float vert_penetration = std::min(itop, ibottom);
168   float horiz_penetration = std::min(ileft, iright);
169   if(vert_penetration < horiz_penetration) {
170     if(itop < ibottom) {
171       constraints->bottom = std::min(constraints->bottom, r2.get_top());
172       constraints->hit.bottom = true;
173     } else {
174       constraints->top = std::max(constraints->top, r2.get_bottom());
175       constraints->hit.top = true;
176     }
177   } else {
178     if(ileft < iright) {
179       constraints->right = std::min(constraints->right, r2.get_left());
180       constraints->hit.right = true;
181     } else {
182       constraints->left = std::max(constraints->left, r2.get_right());
183       constraints->hit.left = true;
184     }
185   }
186 }
187
188 }