gim_tri_collision.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. #ifndef GIM_TRI_COLLISION_H_INCLUDED
  2. #define GIM_TRI_COLLISION_H_INCLUDED
  3. /*! \file gim_tri_collision.h
  4. \author Francisco Leon Najera
  5. */
  6. /*
  7. -----------------------------------------------------------------------------
  8. This source file is part of GIMPACT Library.
  9. For the latest info, see http://gimpact.sourceforge.net/
  10. Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371.
  11. email: [email protected]
  12. This library is free software; you can redistribute it and/or
  13. modify it under the terms of EITHER:
  14. (1) The GNU Lesser General Public License as published by the Free
  15. Software Foundation; either version 2.1 of the License, or (at
  16. your option) any later version. The text of the GNU Lesser
  17. General Public License is included with this library in the
  18. file GIMPACT-LICENSE-LGPL.TXT.
  19. (2) The BSD-style license that is included with this library in
  20. the file GIMPACT-LICENSE-BSD.TXT.
  21. (3) The zlib/libpng license that is included with this library in
  22. the file GIMPACT-LICENSE-ZLIB.TXT.
  23. This library is distributed in the hope that it will be useful,
  24. but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
  26. GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details.
  27. -----------------------------------------------------------------------------
  28. */
  29. #include "gim_box_collision.h"
  30. #include "gim_clip_polygon.h"
  31. #define MAX_TRI_CLIPPING 16
  32. //! Structure for collision
  33. struct GIM_TRIANGLE_CONTACT_DATA
  34. {
  35. GREAL m_penetration_depth;
  36. GUINT m_point_count;
  37. btVector4 m_separating_normal;
  38. btVector3 m_points[MAX_TRI_CLIPPING];
  39. SIMD_FORCE_INLINE void copy_from(const GIM_TRIANGLE_CONTACT_DATA& other)
  40. {
  41. m_penetration_depth = other.m_penetration_depth;
  42. m_separating_normal = other.m_separating_normal;
  43. m_point_count = other.m_point_count;
  44. GUINT i = m_point_count;
  45. while(i--)
  46. {
  47. m_points[i] = other.m_points[i];
  48. }
  49. }
  50. GIM_TRIANGLE_CONTACT_DATA()
  51. {
  52. }
  53. GIM_TRIANGLE_CONTACT_DATA(const GIM_TRIANGLE_CONTACT_DATA& other)
  54. {
  55. copy_from(other);
  56. }
  57. //! classify points that are closer
  58. template<typename DISTANCE_FUNC,typename CLASS_PLANE>
  59. SIMD_FORCE_INLINE void mergepoints_generic(const CLASS_PLANE & plane,
  60. GREAL margin, const btVector3 * points, GUINT point_count, DISTANCE_FUNC distance_func)
  61. {
  62. m_point_count = 0;
  63. m_penetration_depth= -1000.0f;
  64. GUINT point_indices[MAX_TRI_CLIPPING];
  65. GUINT _k;
  66. for(_k=0;_k<point_count;_k++)
  67. {
  68. GREAL _dist = -distance_func(plane,points[_k]) + margin;
  69. if(_dist>=0.0f)
  70. {
  71. if(_dist>m_penetration_depth)
  72. {
  73. m_penetration_depth = _dist;
  74. point_indices[0] = _k;
  75. m_point_count=1;
  76. }
  77. else if((_dist+G_EPSILON)>=m_penetration_depth)
  78. {
  79. point_indices[m_point_count] = _k;
  80. m_point_count++;
  81. }
  82. }
  83. }
  84. for( _k=0;_k<m_point_count;_k++)
  85. {
  86. m_points[_k] = points[point_indices[_k]];
  87. }
  88. }
  89. //! classify points that are closer
  90. SIMD_FORCE_INLINE void merge_points(const btVector4 & plane, GREAL margin,
  91. const btVector3 * points, GUINT point_count)
  92. {
  93. m_separating_normal = plane;
  94. mergepoints_generic(plane, margin, points, point_count, DISTANCE_PLANE_3D_FUNC());
  95. }
  96. };
  97. //! Class for colliding triangles
  98. class GIM_TRIANGLE
  99. {
  100. public:
  101. btScalar m_margin;
  102. btVector3 m_vertices[3];
  103. GIM_TRIANGLE():m_margin(0.1f)
  104. {
  105. }
  106. SIMD_FORCE_INLINE GIM_AABB get_box() const
  107. {
  108. return GIM_AABB(m_vertices[0],m_vertices[1],m_vertices[2],m_margin);
  109. }
  110. SIMD_FORCE_INLINE void get_normal(btVector3 &normal) const
  111. {
  112. TRIANGLE_NORMAL(m_vertices[0],m_vertices[1],m_vertices[2],normal);
  113. }
  114. SIMD_FORCE_INLINE void get_plane(btVector4 &plane) const
  115. {
  116. TRIANGLE_PLANE(m_vertices[0],m_vertices[1],m_vertices[2],plane);;
  117. }
  118. SIMD_FORCE_INLINE void apply_transform(const btTransform & trans)
  119. {
  120. m_vertices[0] = trans(m_vertices[0]);
  121. m_vertices[1] = trans(m_vertices[1]);
  122. m_vertices[2] = trans(m_vertices[2]);
  123. }
  124. SIMD_FORCE_INLINE void get_edge_plane(GUINT edge_index,const btVector3 &triangle_normal,btVector4 &plane) const
  125. {
  126. const btVector3 & e0 = m_vertices[edge_index];
  127. const btVector3 & e1 = m_vertices[(edge_index+1)%3];
  128. EDGE_PLANE(e0,e1,triangle_normal,plane);
  129. }
  130. //! Gets the relative transformation of this triangle
  131. /*!
  132. The transformation is oriented to the triangle normal , and aligned to the 1st edge of this triangle. The position corresponds to vertice 0:
  133. - triangle normal corresponds to Z axis.
  134. - 1st normalized edge corresponds to X axis,
  135. */
  136. SIMD_FORCE_INLINE void get_triangle_transform(btTransform & triangle_transform) const
  137. {
  138. btMatrix3x3 & matrix = triangle_transform.getBasis();
  139. btVector3 zaxis;
  140. get_normal(zaxis);
  141. MAT_SET_Z(matrix,zaxis);
  142. btVector3 xaxis = m_vertices[1] - m_vertices[0];
  143. VEC_NORMALIZE(xaxis);
  144. MAT_SET_X(matrix,xaxis);
  145. //y axis
  146. xaxis = zaxis.cross(xaxis);
  147. MAT_SET_Y(matrix,xaxis);
  148. triangle_transform.setOrigin(m_vertices[0]);
  149. }
  150. //! Test triangles by finding separating axis
  151. /*!
  152. \param other Triangle for collide
  153. \param contact_data Structure for holding contact points, normal and penetration depth; The normal is pointing toward this triangle from the other triangle
  154. */
  155. bool collide_triangle_hard_test(
  156. const GIM_TRIANGLE & other,
  157. GIM_TRIANGLE_CONTACT_DATA & contact_data) const;
  158. //! Test boxes before doing hard test
  159. /*!
  160. \param other Triangle for collide
  161. \param contact_data Structure for holding contact points, normal and penetration depth; The normal is pointing toward this triangle from the other triangle
  162. \
  163. */
  164. SIMD_FORCE_INLINE bool collide_triangle(
  165. const GIM_TRIANGLE & other,
  166. GIM_TRIANGLE_CONTACT_DATA & contact_data) const
  167. {
  168. //test box collisioin
  169. GIM_AABB boxu(m_vertices[0],m_vertices[1],m_vertices[2],m_margin);
  170. GIM_AABB boxv(other.m_vertices[0],other.m_vertices[1],other.m_vertices[2],other.m_margin);
  171. if(!boxu.has_collision(boxv)) return false;
  172. //do hard test
  173. return collide_triangle_hard_test(other,contact_data);
  174. }
  175. /*!
  176. Solve the System for u,v parameters:
  177. u*axe1[i1] + v*axe2[i1] = vecproj[i1]
  178. u*axe1[i2] + v*axe2[i2] = vecproj[i2]
  179. sustitute:
  180. v = (vecproj[i2] - u*axe1[i2])/axe2[i2]
  181. then the first equation in terms of 'u':
  182. --> u*axe1[i1] + ((vecproj[i2] - u*axe1[i2])/axe2[i2])*axe2[i1] = vecproj[i1]
  183. --> u*axe1[i1] + vecproj[i2]*axe2[i1]/axe2[i2] - u*axe1[i2]*axe2[i1]/axe2[i2] = vecproj[i1]
  184. --> u*(axe1[i1] - axe1[i2]*axe2[i1]/axe2[i2]) = vecproj[i1] - vecproj[i2]*axe2[i1]/axe2[i2]
  185. --> u*((axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1])/axe2[i2]) = (vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1])/axe2[i2]
  186. --> u*(axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1]) = vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1]
  187. --> u = (vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1]) /(axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1])
  188. if 0.0<= u+v <=1.0 then they are inside of triangle
  189. \return false if the point is outside of triangle.This function doesn't take the margin
  190. */
  191. SIMD_FORCE_INLINE bool get_uv_parameters(
  192. const btVector3 & point,
  193. const btVector3 & tri_plane,
  194. GREAL & u, GREAL & v) const
  195. {
  196. btVector3 _axe1 = m_vertices[1]-m_vertices[0];
  197. btVector3 _axe2 = m_vertices[2]-m_vertices[0];
  198. btVector3 _vecproj = point - m_vertices[0];
  199. GUINT _i1 = (tri_plane.closestAxis()+1)%3;
  200. GUINT _i2 = (_i1+1)%3;
  201. if(btFabs(_axe2[_i2])<G_EPSILON)
  202. {
  203. u = (_vecproj[_i2]*_axe2[_i1] - _vecproj[_i1]*_axe2[_i2]) /(_axe1[_i2]*_axe2[_i1] - _axe1[_i1]*_axe2[_i2]);
  204. v = (_vecproj[_i1] - u*_axe1[_i1])/_axe2[_i1];
  205. }
  206. else
  207. {
  208. u = (_vecproj[_i1]*_axe2[_i2] - _vecproj[_i2]*_axe2[_i1]) /(_axe1[_i1]*_axe2[_i2] - _axe1[_i2]*_axe2[_i1]);
  209. v = (_vecproj[_i2] - u*_axe1[_i2])/_axe2[_i2];
  210. }
  211. if(u<-G_EPSILON)
  212. {
  213. return false;
  214. }
  215. else if(v<-G_EPSILON)
  216. {
  217. return false;
  218. }
  219. else
  220. {
  221. btScalar sumuv;
  222. sumuv = u+v;
  223. if(sumuv<-G_EPSILON)
  224. {
  225. return false;
  226. }
  227. else if(sumuv-1.0f>G_EPSILON)
  228. {
  229. return false;
  230. }
  231. }
  232. return true;
  233. }
  234. //! is point in triangle beam?
  235. /*!
  236. Test if point is in triangle, with m_margin tolerance
  237. */
  238. SIMD_FORCE_INLINE bool is_point_inside(const btVector3 & point, const btVector3 & tri_normal) const
  239. {
  240. //Test with edge 0
  241. btVector4 edge_plane;
  242. this->get_edge_plane(0,tri_normal,edge_plane);
  243. GREAL dist = DISTANCE_PLANE_POINT(edge_plane,point);
  244. if(dist-m_margin>0.0f) return false; // outside plane
  245. this->get_edge_plane(1,tri_normal,edge_plane);
  246. dist = DISTANCE_PLANE_POINT(edge_plane,point);
  247. if(dist-m_margin>0.0f) return false; // outside plane
  248. this->get_edge_plane(2,tri_normal,edge_plane);
  249. dist = DISTANCE_PLANE_POINT(edge_plane,point);
  250. if(dist-m_margin>0.0f) return false; // outside plane
  251. return true;
  252. }
  253. //! Bidireccional ray collision
  254. SIMD_FORCE_INLINE bool ray_collision(
  255. const btVector3 & vPoint,
  256. const btVector3 & vDir, btVector3 & pout, btVector3 & triangle_normal,
  257. GREAL & tparam, GREAL tmax = G_REAL_INFINITY)
  258. {
  259. btVector4 faceplane;
  260. {
  261. btVector3 dif1 = m_vertices[1] - m_vertices[0];
  262. btVector3 dif2 = m_vertices[2] - m_vertices[0];
  263. VEC_CROSS(faceplane,dif1,dif2);
  264. faceplane[3] = m_vertices[0].dot(faceplane);
  265. }
  266. GUINT res = LINE_PLANE_COLLISION(faceplane,vDir,vPoint,pout,tparam, btScalar(0), tmax);
  267. if(res == 0) return false;
  268. if(! is_point_inside(pout,faceplane)) return false;
  269. if(res==2) //invert normal
  270. {
  271. triangle_normal.setValue(-faceplane[0],-faceplane[1],-faceplane[2]);
  272. }
  273. else
  274. {
  275. triangle_normal.setValue(faceplane[0],faceplane[1],faceplane[2]);
  276. }
  277. VEC_NORMALIZE(triangle_normal);
  278. return true;
  279. }
  280. //! one direccion ray collision
  281. SIMD_FORCE_INLINE bool ray_collision_front_side(
  282. const btVector3 & vPoint,
  283. const btVector3 & vDir, btVector3 & pout, btVector3 & triangle_normal,
  284. GREAL & tparam, GREAL tmax = G_REAL_INFINITY)
  285. {
  286. btVector4 faceplane;
  287. {
  288. btVector3 dif1 = m_vertices[1] - m_vertices[0];
  289. btVector3 dif2 = m_vertices[2] - m_vertices[0];
  290. VEC_CROSS(faceplane,dif1,dif2);
  291. faceplane[3] = m_vertices[0].dot(faceplane);
  292. }
  293. GUINT res = LINE_PLANE_COLLISION(faceplane,vDir,vPoint,pout,tparam, btScalar(0), tmax);
  294. if(res != 1) return false;
  295. if(!is_point_inside(pout,faceplane)) return false;
  296. triangle_normal.setValue(faceplane[0],faceplane[1],faceplane[2]);
  297. VEC_NORMALIZE(triangle_normal);
  298. return true;
  299. }
  300. };
  301. #endif // GIM_TRI_COLLISION_H_INCLUDED