collision.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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. #include "collision/collision.hpp"
  17. #include <algorithm>
  18. #include "math/aatriangle.hpp"
  19. #include "math/rectf.hpp"
  20. namespace collision {
  21. void Constraints::merge_constraints(const Constraints& other)
  22. {
  23. constrain_left(other.position_left);
  24. constrain_right(other.position_right);
  25. constrain_top(other.position_top);
  26. constrain_bottom(other.position_bottom);
  27. hit.left |= other.hit.left;
  28. hit.right |= other.hit.right;
  29. hit.top |= other.hit.top;
  30. hit.bottom |= other.hit.bottom;
  31. hit.crush |= other.hit.crush;
  32. }
  33. //---------------------------------------------------------------------------
  34. namespace {
  35. inline void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
  36. {
  37. n = Vector(p2.y - p1.y, p1.x - p2.x);
  38. c = -glm::dot(p2, n);
  39. float nval = glm::length(n);
  40. n /= nval;
  41. c /= nval;
  42. }
  43. }
  44. bool rectangle_aatriangle(Constraints* constraints, const Rectf& rect,
  45. const AATriangle& triangle)
  46. {
  47. bool dummy;
  48. return rectangle_aatriangle(constraints, rect, triangle, dummy);
  49. }
  50. bool rectangle_aatriangle(Constraints* constraints, const Rectf& rect,
  51. const AATriangle& triangle,
  52. bool& hits_rectangle_bottom)
  53. {
  54. if (!rect.overlaps(triangle.bbox))
  55. return false;
  56. Vector normal(0.0f, 0.0f);
  57. float c = 0.0;
  58. Vector p1(0.0f, 0.0f);
  59. Rectf area;
  60. switch (triangle.dir & AATriangle::DEFORM_MASK) {
  61. case 0:
  62. area.set_p1(triangle.bbox.p1());
  63. area.set_p2(triangle.bbox.p2());
  64. break;
  65. case AATriangle::DEFORM_BOTTOM:
  66. area.set_p1(Vector(triangle.bbox.get_left(), triangle.bbox.get_top() + triangle.bbox.get_height()/2));
  67. area.set_p2(triangle.bbox.p2());
  68. break;
  69. case AATriangle::DEFORM_TOP:
  70. area.set_p1(triangle.bbox.p1());
  71. area.set_p2(Vector(triangle.bbox.get_right(), triangle.bbox.get_top() + triangle.bbox.get_height()/2));
  72. break;
  73. case AATriangle::DEFORM_LEFT:
  74. area.set_p1(triangle.bbox.p1());
  75. area.set_p2(Vector(triangle.bbox.get_left() + triangle.bbox.get_width()/2, triangle.bbox.get_bottom()));
  76. break;
  77. case AATriangle::DEFORM_RIGHT:
  78. area.set_p1(Vector(triangle.bbox.get_left() + triangle.bbox.get_width()/2, triangle.bbox.get_top()));
  79. area.set_p2(triangle.bbox.p2());
  80. break;
  81. default:
  82. assert(false);
  83. }
  84. switch (triangle.dir & AATriangle::DIRECTION_MASK) {
  85. case AATriangle::SOUTHWEST:
  86. p1 = Vector(rect.get_left(), rect.get_bottom());
  87. makePlane(area.p1(), area.p2(), normal, c);
  88. break;
  89. case AATriangle::NORTHEAST:
  90. p1 = Vector(rect.get_right(), rect.get_top());
  91. makePlane(area.p2(), area.p1(), normal, c);
  92. break;
  93. case AATriangle::SOUTHEAST:
  94. p1 = rect.p2();
  95. makePlane(Vector(area.get_left(), area.get_bottom()),
  96. Vector(area.get_right(), area.get_top()), normal, c);
  97. break;
  98. case AATriangle::NORTHWEST:
  99. p1 = rect.p1();
  100. makePlane(Vector(area.get_right(), area.get_top()),
  101. Vector(area.get_left(), area.get_bottom()), normal, c);
  102. break;
  103. default:
  104. assert(false);
  105. }
  106. float n_p1 = -glm::dot(normal, p1);
  107. float depth = n_p1 - c;
  108. if (depth < 0)
  109. return false;
  110. #if 0
  111. std::cout << "R: " << rect << " Tri: " << triangle << "\n";
  112. std::cout << "Norm: " << normal << " Depth: " << depth << "\n";
  113. #endif
  114. Vector outvec = normal * (depth + 0.2f);
  115. const float RDELTA = 3;
  116. if (p1.x < area.get_left() - RDELTA || p1.x > area.get_right() + RDELTA
  117. || p1.y < area.get_top() - RDELTA || p1.y > area.get_bottom() + RDELTA) {
  118. set_rectangle_rectangle_constraints(constraints, rect, area);
  119. } else {
  120. if (outvec.x < 0) {
  121. constraints->constrain_right(rect.get_right() + outvec.x);
  122. constraints->hit.right = true;
  123. } else {
  124. constraints->constrain_left(rect.get_left() + outvec.x);
  125. constraints->hit.left = true;
  126. }
  127. if (outvec.y < 0) {
  128. constraints->constrain_bottom(rect.get_bottom() + outvec.y);
  129. constraints->hit.bottom = true;
  130. hits_rectangle_bottom = true;
  131. } else {
  132. constraints->constrain_top(rect.get_top() + outvec.y);
  133. constraints->hit.top = true;
  134. }
  135. constraints->hit.slope_normal = normal;
  136. }
  137. return true;
  138. }
  139. void set_rectangle_rectangle_constraints(Constraints* constraints, const Rectf& r1, const Rectf& r2)
  140. {
  141. float itop = r1.get_bottom() - r2.get_top();
  142. float ibottom = r2.get_bottom() - r1.get_top();
  143. float ileft = r1.get_right() - r2.get_left();
  144. float iright = r2.get_right() - r1.get_left();
  145. float vert_penetration = std::min(itop, ibottom);
  146. float horiz_penetration = std::min(ileft, iright);
  147. if (vert_penetration < horiz_penetration) {
  148. if (itop < ibottom) {
  149. constraints->constrain_bottom(r2.get_top());
  150. constraints->hit.bottom = true;
  151. } else {
  152. constraints->constrain_top(r2.get_bottom());
  153. constraints->hit.top = true;
  154. }
  155. } else {
  156. if (ileft < iright) {
  157. constraints->constrain_right(r2.get_left());
  158. constraints->hit.right = true;
  159. } else {
  160. constraints->constrain_left(r2.get_right());
  161. constraints->hit.left = true;
  162. }
  163. }
  164. }
  165. bool line_intersects_line(const Vector& line1_start, const Vector& line1_end, const Vector& line2_start, const Vector& line2_end)
  166. {
  167. // Adapted from Striker, (C) 1999 Joris van der Hoeven, GPL
  168. float a1 = line1_start.x, b1 = line1_start.y, a2 = line1_end.x, b2 = line1_end.y;
  169. float c1 = line2_start.x, d1 = line2_start.y, c2 = line2_end.x, d2 = line2_end.y;
  170. float num = (b2-b1)*(c2-c1) - (a2-a1)*(d2-d1);
  171. float den1 = (d2-b2)*(c1-c2) + (a2-c2)*(d1-d2);
  172. float den2 = (d2-b2)*(a1-a2) + (a2-c2)*(b1-b2);
  173. // Normalize to positive numerator.
  174. if (num < 0) {
  175. num = -num;
  176. den1 = -den1;
  177. den2 = -den2;
  178. }
  179. // Numerator is zero -> Check for parallel or coinciding lines.
  180. if (num == 0) {
  181. if ((b1-b2)*(c1-a2) != (a1-a2)*(d1-b2)) return false;
  182. if (a1 == a2) {
  183. std::swap(a1, b1);
  184. std::swap(a2, b2);
  185. std::swap(c1, d1);
  186. std::swap(c2, d2);
  187. }
  188. if (a1 > a2) std::swap(a1, a2);
  189. if (c1 > c2) std::swap(c1, c2);
  190. return ((a1 <= c2) && (a2 >= c1));
  191. }
  192. // Standard check.
  193. return (den1>=0) && (den1<=num) && (den2>=0) && (den2<=num);
  194. }
  195. bool intersects_line(const Rectf& r, const Vector& line_start, const Vector& line_end)
  196. {
  197. Vector p1 = r.p1();
  198. Vector p2 = Vector(r.get_right(), r.get_top());
  199. Vector p3 = r.p2();
  200. Vector p4 = Vector(r.get_left(), r.get_bottom());
  201. if (line_intersects_line(p1, p2, line_start, line_end)) return true;
  202. if (line_intersects_line(p2, p3, line_start, line_end)) return true;
  203. if (line_intersects_line(p3, p4, line_start, line_end)) return true;
  204. if (line_intersects_line(p4, p1, line_start, line_end)) return true;
  205. return false;
  206. }
  207. }
  208. /* EOF */