updating Nolok contrib templates
[supertux.git] / lib / special / collision.cpp
1 #include <config.h>
2
3 #include "collision.h"
4
5 #include <algorithm>
6 #include <iostream>
7 #include <stdio.h>
8 #include <float.h>
9 #include <math.h>
10 #include "math/vector.h"
11 #include "math/aatriangle.h"
12 #include "math/rectangle.h"
13 #include "collision_hit.h"
14
15 namespace SuperTux
16 {
17
18 static const float DELTA = .0001;
19
20 bool
21 Collision::rectangle_rectangle(CollisionHit& hit, const Rectangle& r1,
22     const Vector& movement, const Rectangle& r2)
23 {
24   if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
25     return false;
26   if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
27     return false;
28
29   if(movement.x > DELTA) {
30     hit.depth = r1.p2.x - r2.p1.x;
31     hit.time = hit.depth / movement.x;
32     hit.normal.x = -1;
33     hit.normal.y = 0;
34   } else if(movement.x < -DELTA) {
35     hit.depth = r2.p2.x - r1.p1.x;
36     hit.time = hit.depth / -movement.x;
37     hit.normal.x = 1;
38     hit.normal.y = 0;
39   } else {
40     if(movement.y > -DELTA && movement.y < DELTA) {
41       hit.time = 0;
42       hit.depth = 0;
43       hit.normal.x = 1;
44       hit.normal.y = 0;
45       return true;
46     }
47     hit.time = FLT_MAX;
48   }
49
50   if(movement.y > DELTA) {
51     float ydepth = r1.p2.y - r2.p1.y;
52     float yt = ydepth / movement.y;
53     if(yt < hit.time) {
54       hit.depth = ydepth;
55       hit.time = yt;
56       hit.normal.x = 0;
57       hit.normal.y = -1;
58     }
59   } else if(movement.y < -DELTA) {
60     float ydepth = r2.p2.y - r1.p1.y;
61     float yt = ydepth / -movement.y;
62     if(yt < hit.time) {
63       hit.depth = ydepth;
64       hit.time = yt;
65       hit.normal.x = 0;
66       hit.normal.y = 1;
67     }
68   }
69
70   return true;
71 }
72
73 //---------------------------------------------------------------------------
74
75 static void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
76 {
77   n = Vector(p2.y-p1.y, p1.x-p2.x);
78   c = -(p2 * n);
79   float nval = n.norm();             
80   n /= nval;
81   c /= nval;
82 }
83
84 bool
85 Collision::rectangle_aatriangle(CollisionHit& hit, const Rectangle& rect,
86     const Vector& movement, const AATriangle& triangle)
87 {
88   if(!rectangle_rectangle(hit, rect, movement, (const Rectangle&) triangle))
89     return false;
90
91   Vector normal;
92   float c;
93   Vector p1;
94   Vector tp1, tp2;
95   switch(triangle.dir & AATriangle::DEFORM_MASK) {
96     case 0:
97       tp1 = triangle.p1;
98       tp2 = triangle.p2;
99       break;
100     case AATriangle::DEFORM1:
101       tp1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
102       tp2 = triangle.p2;
103       break;
104     case AATriangle::DEFORM2:
105       tp1 = triangle.p1;
106       tp2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
107       break;
108     case AATriangle::DEFORM3:
109       tp1 = triangle.p1;
110       tp2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
111       break;
112     case AATriangle::DEFORM4:
113       tp1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
114       tp2 = triangle.p2;
115       break;
116     default:
117       assert(false);
118   } 
119   
120   switch(triangle.dir & AATriangle::DIRECTION_MASK) {
121     case AATriangle::SOUTHWEST:
122       p1 = Vector(rect.p1.x, rect.p2.y);
123       makePlane(tp1, tp2, normal, c);
124       break;
125     case AATriangle::NORTHEAST:
126       p1 = Vector(rect.p2.x, rect.p1.y);
127       makePlane(tp2, tp1, normal, c);
128       break;
129     case AATriangle::SOUTHEAST:
130       p1 = rect.p2;
131       makePlane(Vector(tp1.x, tp2.y),
132           Vector(tp2.x, tp1.y), normal, c);
133       break;
134     case AATriangle::NORTHWEST:
135       p1 = rect.p1;
136       makePlane(Vector(tp2.x, tp1.y),
137           Vector(tp1.x, tp2.y), normal, c);
138       break;
139     default:
140       assert(false);
141   }
142
143   float n_p1 = -(normal * p1);
144   float depth = n_p1 - c;
145   if(depth < 0)
146     return false;
147   float time = depth / -(normal * movement);
148   if(time < hit.time) {
149     hit.depth = depth;
150     hit.time = time;
151     hit.normal = normal;
152   }
153
154   return true;
155 }
156
157 }