OPC_TriBoxOverlap.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. //! This macro quickly finds the min & max values among 3 variables
  2. #define FINDMINMAX(x0, x1, x2, min, max) \
  3. min = max = x0; \
  4. if(x1<min) min=x1; \
  5. if(x1>max) max=x1; \
  6. if(x2<min) min=x2; \
  7. if(x2>max) max=x2;
  8. //! TO BE DOCUMENTED
  9. inline_ BOOL planeBoxOverlap(const Point& normal, const float d, const Point& maxbox)
  10. {
  11. Point vmin, vmax;
  12. for(udword q=0;q<=2;q++)
  13. {
  14. if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; }
  15. else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; }
  16. }
  17. if((normal|vmin)+d>0.0f) return FALSE;
  18. if((normal|vmax)+d>=0.0f) return TRUE;
  19. return FALSE;
  20. }
  21. //! TO BE DOCUMENTED
  22. #define AXISTEST_X01(a, b, fa, fb) \
  23. min = a*v0.y - b*v0.z; \
  24. max = a*v2.y - b*v2.z; \
  25. if(min>max) {const float tmp=max; max=min; min=tmp; } \
  26. rad = fa * extents.y + fb * extents.z; \
  27. if(min>rad || max<-rad) return FALSE;
  28. //! TO BE DOCUMENTED
  29. #define AXISTEST_X2(a, b, fa, fb) \
  30. min = a*v0.y - b*v0.z; \
  31. max = a*v1.y - b*v1.z; \
  32. if(min>max) {const float tmp=max; max=min; min=tmp; } \
  33. rad = fa * extents.y + fb * extents.z; \
  34. if(min>rad || max<-rad) return FALSE;
  35. //! TO BE DOCUMENTED
  36. #define AXISTEST_Y02(a, b, fa, fb) \
  37. min = b*v0.z - a*v0.x; \
  38. max = b*v2.z - a*v2.x; \
  39. if(min>max) {const float tmp=max; max=min; min=tmp; } \
  40. rad = fa * extents.x + fb * extents.z; \
  41. if(min>rad || max<-rad) return FALSE;
  42. //! TO BE DOCUMENTED
  43. #define AXISTEST_Y1(a, b, fa, fb) \
  44. min = b*v0.z - a*v0.x; \
  45. max = b*v1.z - a*v1.x; \
  46. if(min>max) {const float tmp=max; max=min; min=tmp; } \
  47. rad = fa * extents.x + fb * extents.z; \
  48. if(min>rad || max<-rad) return FALSE;
  49. //! TO BE DOCUMENTED
  50. #define AXISTEST_Z12(a, b, fa, fb) \
  51. min = a*v1.x - b*v1.y; \
  52. max = a*v2.x - b*v2.y; \
  53. if(min>max) {const float tmp=max; max=min; min=tmp; } \
  54. rad = fa * extents.x + fb * extents.y; \
  55. if(min>rad || max<-rad) return FALSE;
  56. //! TO BE DOCUMENTED
  57. #define AXISTEST_Z0(a, b, fa, fb) \
  58. min = a*v0.x - b*v0.y; \
  59. max = a*v1.x - b*v1.y; \
  60. if(min>max) {const float tmp=max; max=min; min=tmp; } \
  61. rad = fa * extents.x + fb * extents.y; \
  62. if(min>rad || max<-rad) return FALSE;
  63. // compute triangle edges
  64. // - edges lazy evaluated to take advantage of early exits
  65. // - fabs precomputed (half less work, possible since extents are always >0)
  66. // - customized macros to take advantage of the null component
  67. // - axis vector discarded, possibly saves useless movs
  68. #define IMPLEMENT_CLASS3_TESTS \
  69. float rad; \
  70. float min, max; \
  71. \
  72. const float fey0 = fabsf(e0.y); \
  73. const float fez0 = fabsf(e0.z); \
  74. AXISTEST_X01(e0.z, e0.y, fez0, fey0); \
  75. const float fex0 = fabsf(e0.x); \
  76. AXISTEST_Y02(e0.z, e0.x, fez0, fex0); \
  77. AXISTEST_Z12(e0.y, e0.x, fey0, fex0); \
  78. \
  79. const float fey1 = fabsf(e1.y); \
  80. const float fez1 = fabsf(e1.z); \
  81. AXISTEST_X01(e1.z, e1.y, fez1, fey1); \
  82. const float fex1 = fabsf(e1.x); \
  83. AXISTEST_Y02(e1.z, e1.x, fez1, fex1); \
  84. AXISTEST_Z0(e1.y, e1.x, fey1, fex1); \
  85. \
  86. const Point e2 = mLeafVerts[0] - mLeafVerts[2]; \
  87. const float fey2 = fabsf(e2.y); \
  88. const float fez2 = fabsf(e2.z); \
  89. AXISTEST_X2(e2.z, e2.y, fez2, fey2); \
  90. const float fex2 = fabsf(e2.x); \
  91. AXISTEST_Y1(e2.z, e2.x, fez2, fex2); \
  92. AXISTEST_Z12(e2.y, e2.x, fey2, fex2);
  93. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  94. /**
  95. * Triangle-Box overlap test using the separating axis theorem.
  96. * This is the code from Tomas Möller, a bit optimized:
  97. * - with some more lazy evaluation (faster path on PC)
  98. * - with a tiny bit of assembly
  99. * - with "SAT-lite" applied if needed
  100. * - and perhaps with some more minor modifs...
  101. *
  102. * \param center [in] box center
  103. * \param extents [in] box extents
  104. * \return true if triangle & box overlap
  105. */
  106. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  107. inline_ BOOL AABBTreeCollider::TriBoxOverlap(const Point& center, const Point& extents)
  108. {
  109. // Stats
  110. mNbBVPrimTests++;
  111. // use separating axis theorem to test overlap between triangle and box
  112. // need to test for overlap in these directions:
  113. // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
  114. // we do not even need to test these)
  115. // 2) normal of the triangle
  116. // 3) crossproduct(edge from tri, {x,y,z}-directin)
  117. // this gives 3x3=9 more tests
  118. // move everything so that the boxcenter is in (0,0,0)
  119. Point v0, v1, v2;
  120. v0.x = mLeafVerts[0].x - center.x;
  121. v1.x = mLeafVerts[1].x - center.x;
  122. v2.x = mLeafVerts[2].x - center.x;
  123. // First, test overlap in the {x,y,z}-directions
  124. #ifdef OPC_USE_FCOMI
  125. // find min, max of the triangle in x-direction, and test for overlap in X
  126. if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
  127. if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
  128. // same for Y
  129. v0.y = mLeafVerts[0].y - center.y;
  130. v1.y = mLeafVerts[1].y - center.y;
  131. v2.y = mLeafVerts[2].y - center.y;
  132. if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
  133. if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
  134. // same for Z
  135. v0.z = mLeafVerts[0].z - center.z;
  136. v1.z = mLeafVerts[1].z - center.z;
  137. v2.z = mLeafVerts[2].z - center.z;
  138. if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
  139. if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
  140. #else
  141. float min,max;
  142. // Find min, max of the triangle in x-direction, and test for overlap in X
  143. FINDMINMAX(v0.x, v1.x, v2.x, min, max);
  144. if(min>extents.x || max<-extents.x) return FALSE;
  145. // Same for Y
  146. v0.y = mLeafVerts[0].y - center.y;
  147. v1.y = mLeafVerts[1].y - center.y;
  148. v2.y = mLeafVerts[2].y - center.y;
  149. FINDMINMAX(v0.y, v1.y, v2.y, min, max);
  150. if(min>extents.y || max<-extents.y) return FALSE;
  151. // Same for Z
  152. v0.z = mLeafVerts[0].z - center.z;
  153. v1.z = mLeafVerts[1].z - center.z;
  154. v2.z = mLeafVerts[2].z - center.z;
  155. FINDMINMAX(v0.z, v1.z, v2.z, min, max);
  156. if(min>extents.z || max<-extents.z) return FALSE;
  157. #endif
  158. // 2) Test if the box intersects the plane of the triangle
  159. // compute plane equation of triangle: normal*x+d=0
  160. // ### could be precomputed since we use the same leaf triangle several times
  161. const Point e0 = v1 - v0;
  162. const Point e1 = v2 - v1;
  163. const Point normal = e0 ^ e1;
  164. const float d = -normal|v0;
  165. if(!planeBoxOverlap(normal, d, extents)) return FALSE;
  166. // 3) "Class III" tests
  167. if(mFullPrimBoxTest)
  168. {
  169. IMPLEMENT_CLASS3_TESTS
  170. }
  171. return TRUE;
  172. }
  173. //! A dedicated version where the box is constant
  174. inline_ BOOL OBBCollider::TriBoxOverlap()
  175. {
  176. // Stats
  177. mNbVolumePrimTests++;
  178. // Hook
  179. const Point& extents = mBoxExtents;
  180. const Point& v0 = mLeafVerts[0];
  181. const Point& v1 = mLeafVerts[1];
  182. const Point& v2 = mLeafVerts[2];
  183. // use separating axis theorem to test overlap between triangle and box
  184. // need to test for overlap in these directions:
  185. // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
  186. // we do not even need to test these)
  187. // 2) normal of the triangle
  188. // 3) crossproduct(edge from tri, {x,y,z}-directin)
  189. // this gives 3x3=9 more tests
  190. // Box center is already in (0,0,0)
  191. // First, test overlap in the {x,y,z}-directions
  192. #ifdef OPC_USE_FCOMI
  193. // find min, max of the triangle in x-direction, and test for overlap in X
  194. if(FCMin3(v0.x, v1.x, v2.x)>mBoxExtents.x) return FALSE;
  195. if(FCMax3(v0.x, v1.x, v2.x)<-mBoxExtents.x) return FALSE;
  196. if(FCMin3(v0.y, v1.y, v2.y)>mBoxExtents.y) return FALSE;
  197. if(FCMax3(v0.y, v1.y, v2.y)<-mBoxExtents.y) return FALSE;
  198. if(FCMin3(v0.z, v1.z, v2.z)>mBoxExtents.z) return FALSE;
  199. if(FCMax3(v0.z, v1.z, v2.z)<-mBoxExtents.z) return FALSE;
  200. #else
  201. float min,max;
  202. // Find min, max of the triangle in x-direction, and test for overlap in X
  203. FINDMINMAX(v0.x, v1.x, v2.x, min, max);
  204. if(min>mBoxExtents.x || max<-mBoxExtents.x) return FALSE;
  205. FINDMINMAX(v0.y, v1.y, v2.y, min, max);
  206. if(min>mBoxExtents.y || max<-mBoxExtents.y) return FALSE;
  207. FINDMINMAX(v0.z, v1.z, v2.z, min, max);
  208. if(min>mBoxExtents.z || max<-mBoxExtents.z) return FALSE;
  209. #endif
  210. // 2) Test if the box intersects the plane of the triangle
  211. // compute plane equation of triangle: normal*x+d=0
  212. // ### could be precomputed since we use the same leaf triangle several times
  213. const Point e0 = v1 - v0;
  214. const Point e1 = v2 - v1;
  215. const Point normal = e0 ^ e1;
  216. const float d = -normal|v0;
  217. if(!planeBoxOverlap(normal, d, mBoxExtents)) return FALSE;
  218. // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
  219. {
  220. IMPLEMENT_CLASS3_TESTS
  221. }
  222. return TRUE;
  223. }
  224. //! ...and another one, jeez
  225. inline_ BOOL AABBCollider::TriBoxOverlap()
  226. {
  227. // Stats
  228. mNbVolumePrimTests++;
  229. // Hook
  230. const Point& center = mBox.mCenter;
  231. const Point& extents = mBox.mExtents;
  232. // use separating axis theorem to test overlap between triangle and box
  233. // need to test for overlap in these directions:
  234. // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
  235. // we do not even need to test these)
  236. // 2) normal of the triangle
  237. // 3) crossproduct(edge from tri, {x,y,z}-directin)
  238. // this gives 3x3=9 more tests
  239. // move everything so that the boxcenter is in (0,0,0)
  240. Point v0, v1, v2;
  241. v0.x = mLeafVerts[0].x - center.x;
  242. v1.x = mLeafVerts[1].x - center.x;
  243. v2.x = mLeafVerts[2].x - center.x;
  244. // First, test overlap in the {x,y,z}-directions
  245. #ifdef OPC_USE_FCOMI
  246. // find min, max of the triangle in x-direction, and test for overlap in X
  247. if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
  248. if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
  249. // same for Y
  250. v0.y = mLeafVerts[0].y - center.y;
  251. v1.y = mLeafVerts[1].y - center.y;
  252. v2.y = mLeafVerts[2].y - center.y;
  253. if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
  254. if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
  255. // same for Z
  256. v0.z = mLeafVerts[0].z - center.z;
  257. v1.z = mLeafVerts[1].z - center.z;
  258. v2.z = mLeafVerts[2].z - center.z;
  259. if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
  260. if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
  261. #else
  262. float min,max;
  263. // Find min, max of the triangle in x-direction, and test for overlap in X
  264. FINDMINMAX(v0.x, v1.x, v2.x, min, max);
  265. if(min>extents.x || max<-extents.x) return FALSE;
  266. // Same for Y
  267. v0.y = mLeafVerts[0].y - center.y;
  268. v1.y = mLeafVerts[1].y - center.y;
  269. v2.y = mLeafVerts[2].y - center.y;
  270. FINDMINMAX(v0.y, v1.y, v2.y, min, max);
  271. if(min>extents.y || max<-extents.y) return FALSE;
  272. // Same for Z
  273. v0.z = mLeafVerts[0].z - center.z;
  274. v1.z = mLeafVerts[1].z - center.z;
  275. v2.z = mLeafVerts[2].z - center.z;
  276. FINDMINMAX(v0.z, v1.z, v2.z, min, max);
  277. if(min>extents.z || max<-extents.z) return FALSE;
  278. #endif
  279. // 2) Test if the box intersects the plane of the triangle
  280. // compute plane equation of triangle: normal*x+d=0
  281. // ### could be precomputed since we use the same leaf triangle several times
  282. const Point e0 = v1 - v0;
  283. const Point e1 = v2 - v1;
  284. const Point normal = e0 ^ e1;
  285. const float d = -normal|v0;
  286. if(!planeBoxOverlap(normal, d, extents)) return FALSE;
  287. // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
  288. {
  289. IMPLEMENT_CLASS3_TESTS
  290. }
  291. return TRUE;
  292. }