Fix for coverity #29409 - Use char 0 instead of NULL
[supertux.git] / src / supertux / collision.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "supertux/collision.hpp"
18
19 #include <algorithm>
20
21 #include "math/aatriangle.hpp"
22 #include "math/rectf.hpp"
23
24 namespace collision {
25
26 bool intersects(const Rectf& r1, const Rectf& r2)
27 {
28   if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
29     return false;
30   if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
31     return false;
32
33   return true;
34 }
35
36 //---------------------------------------------------------------------------
37
38 namespace {
39 inline void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
40 {
41   n = Vector(p2.y-p1.y, p1.x-p2.x);
42   c = -(p2 * n);
43   float nval = n.norm();
44   n /= nval;
45   c /= nval;
46 }
47
48 }
49
50 bool rectangle_aatriangle(Constraints* constraints, const Rectf& rect,
51                           const AATriangle& triangle, const Vector& addl_ground_movement)
52 {
53   if(!intersects(rect, (const Rectf&) triangle))
54     return false;
55
56   Vector normal;
57   float c = 0.0;
58   Vector p1;
59   Rectf area;
60   switch(triangle.dir & AATriangle::DEFORM_MASK) {
61     case 0:
62       area.p1 = triangle.bbox.p1;
63       area.p2 = triangle.bbox.p2;
64       break;
65     case AATriangle::DEFORM_BOTTOM:
66       area.p1 = Vector(triangle.bbox.p1.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
67       area.p2 = triangle.bbox.p2;
68       break;
69     case AATriangle::DEFORM_TOP:
70       area.p1 = triangle.bbox.p1;
71       area.p2 = Vector(triangle.bbox.p2.x, triangle.bbox.p1.y + triangle.bbox.get_height()/2);
72       break;
73     case AATriangle::DEFORM_LEFT:
74       area.p1 = triangle.bbox.p1;
75       area.p2 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p2.y);
76       break;
77     case AATriangle::DEFORM_RIGHT:
78       area.p1 = Vector(triangle.bbox.p1.x + triangle.bbox.get_width()/2, triangle.bbox.p1.y);
79       area.p2 = triangle.bbox.p2;
80       break;
81     default:
82       assert(false);
83   }
84
85   switch(triangle.dir & AATriangle::DIRECTION_MASK) {
86     case AATriangle::SOUTHWEST:
87       p1 = Vector(rect.p1.x, rect.p2.y);
88       makePlane(area.p1, area.p2, normal, c);
89       break;
90     case AATriangle::NORTHEAST:
91       p1 = Vector(rect.p2.x, rect.p1.y);
92       makePlane(area.p2, area.p1, normal, c);
93       break;
94     case AATriangle::SOUTHEAST:
95       p1 = rect.p2;
96       makePlane(Vector(area.p1.x, area.p2.y),
97                 Vector(area.p2.x, area.p1.y), normal, c);
98       break;
99     case AATriangle::NORTHWEST:
100       p1 = rect.p1;
101       makePlane(Vector(area.p2.x, area.p1.y),
102                 Vector(area.p1.x, area.p2.y), normal, c);
103       break;
104     default:
105       assert(false);
106   }
107
108   float n_p1 = -(normal * p1);
109   float depth = n_p1 - c;
110   if(depth < 0)
111     return false;
112
113 #if 0
114   std::cout << "R: " << rect << " Tri: " << triangle << "\n";
115   std::cout << "Norm: " << normal << " Depth: " << depth << "\n";
116 #endif
117
118   Vector outvec = normal * (depth + 0.2f);
119
120   const float RDELTA = 3;
121   if(p1.x < area.p1.x - RDELTA || p1.x > area.p2.x + RDELTA
122      || p1.y < area.p1.y - RDELTA || p1.y > area.p2.y + RDELTA) {
123     set_rectangle_rectangle_constraints(constraints, rect, area);
124   } else {
125     if(outvec.x < 0) {
126       constraints->constrain_right(rect.get_right() + outvec.x, addl_ground_movement.x);
127       constraints->hit.right = true;
128     } else {
129       constraints->constrain_left(rect.get_left() + outvec.x, addl_ground_movement.x);
130       constraints->hit.left = true;
131     }
132
133     if(outvec.y < 0) {
134       constraints->constrain_bottom(rect.get_bottom() + outvec.y, addl_ground_movement.y);
135       constraints->hit.bottom = true;
136       constraints->ground_movement += addl_ground_movement;
137     } else {
138       constraints->constrain_top(rect.get_top() + outvec.y, addl_ground_movement.y);
139       constraints->hit.top = true;
140     }
141     constraints->hit.slope_normal = normal;
142   }
143
144   return true;
145 }
146
147 void set_rectangle_rectangle_constraints(Constraints* constraints,
148                                          const Rectf& r1, const Rectf& r2, const Vector& addl_ground_movement)
149 {
150   float itop = r1.get_bottom() - r2.get_top();
151   float ibottom = r2.get_bottom() - r1.get_top();
152   float ileft = r1.get_right() - r2.get_left();
153   float iright = r2.get_right() - r1.get_left();
154
155   float vert_penetration = std::min(itop, ibottom);
156   float horiz_penetration = std::min(ileft, iright);
157   if(vert_penetration < horiz_penetration) {
158     if(itop < ibottom) {
159       constraints->constrain_bottom(r2.get_top(), addl_ground_movement.y);
160       constraints->hit.bottom = true;
161       constraints->ground_movement += addl_ground_movement;
162     } else {
163       constraints->constrain_top(r2.get_bottom(), addl_ground_movement.y);
164       constraints->hit.top = true;
165     }
166   } else {
167     if(ileft < iright) {
168       constraints->constrain_right(r2.get_left(), addl_ground_movement.x);
169       constraints->hit.right = true;
170     } else {
171       constraints->constrain_left(r2.get_right(), addl_ground_movement.x);
172       constraints->hit.left = true;
173     }
174   }
175 }
176
177 }
178
179 /* EOF */