Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

11 роки тому
10 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
  5. // (c) 2010-2013 Benjamin "Touky" Huet <huet.benjamin@gmail.com>
  6. // This program is free software; you can redistribute it and/or
  7. // modify it under the terms of the Do What The Fuck You Want To
  8. // Public License, Version 2, as published by Sam Hocevar. See
  9. // http://www.wtfpl.net/ for more details.
  10. //
  11. #include <lol/engine-internal.h>
  12. #include <cstdlib> /* free() */
  13. #include <cstring> /* strdup() */
  14. #include <ostream> /* std::ostream */
  15. namespace lol
  16. {
  17. //Test epsilon stuff
  18. TestEpsilon g_test_epsilon;
  19. float TestEpsilon::Get() { return g_test_epsilon.m_epsilon; }
  20. void TestEpsilon::Set(float epsilon) { g_test_epsilon.m_epsilon = lol::max(epsilon, .0f); }
  21. const TestEpsilon& TestEpsilon::F(float value) { g_test_epsilon.m_value = value; return g_test_epsilon; }
  22. float TestEpsilon::Minus()const { return m_value - m_epsilon; }
  23. float TestEpsilon::Plus() const { return m_value + m_epsilon; }
  24. bool TestEpsilon::operator==(float value)const { return (Minus() <= value && value <= Plus()); }
  25. bool TestEpsilon::operator!=(float value)const { return (value < Minus() || Plus() < value); }
  26. bool TestEpsilon::operator<(float value) const { return (Plus() < value); }
  27. bool TestEpsilon::operator<=(float value)const { return (Minus() <= value); }
  28. bool TestEpsilon::operator>(float value) const { return (Minus() > value); }
  29. bool TestEpsilon::operator>=(float value)const { return (Plus() >= value); }
  30. bool operator==(float value, const TestEpsilon& epsilon) { return epsilon == value; }
  31. bool operator!=(float value, const TestEpsilon& epsilon) { return epsilon != value; }
  32. bool operator<(float value, const TestEpsilon& epsilon) { return epsilon > value; }
  33. bool operator<=(float value, const TestEpsilon& epsilon) { return epsilon >= value; }
  34. bool operator>(float value, const TestEpsilon& epsilon) { return epsilon < value; }
  35. bool operator>=(float value, const TestEpsilon& epsilon) { return epsilon <= value; }
  36. // Line/triangle : sets isec_p as the intersection point & return true if ok.
  37. bool TestRayVsTriangle(vec3 const &ray_point, vec3 const &ray_dir,
  38. vec3 const &tri_p0, vec3 const &tri_p1, vec3 const &tri_p2,
  39. vec3 &isec_p)
  40. {
  41. vec3 v01, v02, h, v0P, q;
  42. float a, f, triU, triV;
  43. //
  44. v01 = tri_p1 - tri_p0;
  45. v02 = tri_p2 - tri_p0;
  46. h = cross(ray_dir, v02);
  47. a = dot(v01, h);
  48. //rayDir is coplanar to the triangle, exit.
  49. if (a > -TestEpsilon::Get() && a < TestEpsilon::Get())
  50. return false;
  51. f = 1 / a;
  52. v0P = ray_point - tri_p0;
  53. triU = f * (dot(v0P, h));
  54. //point is supposed to have an U on the segment v01
  55. if (triU < -TestEpsilon::Get() || triU > 1.0)
  56. return false;
  57. q = cross(v0P, v01);
  58. triV = f * dot(ray_dir, q);
  59. //point is not in the triangle
  60. if (triV < -TestEpsilon::Get() || triU + triV > 1.0)
  61. return false;
  62. // at this stage we can compute t to find out where
  63. // the intersection point is on the line
  64. float t = f * dot(v02, q);
  65. if (t > TestEpsilon::Get()) // ray intersection
  66. {
  67. isec_p = tri_p0 + v01 * triU + v02 * triV;
  68. return true;
  69. }
  70. else // this means that there is a line intersection
  71. // but not a ray intersection
  72. return false;
  73. }
  74. // Triangle/Triangle
  75. bool TestTriangleVsTriangle(vec3 const &v00, vec3 const &v01, vec3 const &v02, //triangle 0
  76. vec3 const &v10, vec3 const &v11, vec3 const &v12, //triangle 1
  77. vec3 &ip00, vec3 &ip10) //triangle intersection, iPx means gives the actual intersection points.
  78. {
  79. vec3 isec[2] = { vec3(0, 0, 0), vec3(0, 0, 0) };
  80. vec3 triV[6] = { v00, v01, v02,
  81. v10, v11, v12 };
  82. vec3 triD[6] = { v01 - v00, v02 - v01, v00 - v02,
  83. v11 - v10, v12 - v11, v10 - v12 };
  84. int isecIdx = 0;
  85. vec3 isec_p(0);
  86. //Check the normal before doing any other calculations
  87. vec3 plane_norm[2] = { cross(normalize(triD[0]), normalize(triD[1])),
  88. cross(normalize(triD[3]), normalize(triD[4])) };
  89. if (abs(dot(plane_norm[0], plane_norm[1])) == 1.0f)
  90. return false;
  91. #if 0
  92. //Precheck to verify if two point of one of the tri-A are on tri-B, and vice versa.
  93. int zero_dist[2] = { 0, 0 };
  94. for (int i = 0; i < 3; i++)
  95. {
  96. if (PointDistToPlane(triV[i], triV[3], plane_norm[1]) < TestEpsilon::Get())
  97. zero_dist[0]++;
  98. if (PointDistToPlane(triV[i + 3], triV[0], plane_norm[0]) < TestEpsilon::Get())
  99. zero_dist[1]++;
  100. }
  101. if (zero_dist[0] >= 2 || zero_dist[0] >= 2)
  102. return false;
  103. #endif
  104. //We first need to find two points intersecting, no matter on which triangle.
  105. for (int i = 0; i < 2 && isecIdx < 2; i++)
  106. {
  107. /*int found_isec = -1;*/
  108. for (int j = 0; j < 3 && isecIdx < 2; j++)
  109. {
  110. int pIdx = j + i * 3;
  111. int tIdx = (1 - i) * 3;
  112. if (TestRayVsTriangle(triV[pIdx], triD[pIdx],
  113. triV[tIdx + 0], triV[tIdx + 1], triV[tIdx + 2],
  114. isec[isecIdx]))
  115. {
  116. #if 1 //if the point is near one the given entry, consider it as being the same point.
  117. for (int k = 0; k < 6; k++)
  118. {
  119. if (length(isec[isecIdx] - triV[k]) < TestEpsilon::Get())
  120. {
  121. isec[isecIdx] = triV[k];
  122. break;
  123. }
  124. }
  125. #endif
  126. //Automatic pass on the first intersection
  127. if (isecIdx == 0 ||
  128. //If we have already found an intersection, pass if it's on this triangle.
  129. /*found_isec == i ||*/
  130. //if it's the second intersection, we need it to be different from the first one.
  131. (isecIdx == 1 && length(isec[0] - isec[1]) > TestEpsilon::Get()))
  132. {
  133. /*found_isec = i;*/
  134. isecIdx++;
  135. }
  136. }
  137. }
  138. }
  139. if (isecIdx >= 2)
  140. {
  141. ip00 = isec[0];
  142. ip10 = isec[1];
  143. return true;
  144. }
  145. return false;
  146. }
  147. //Ray/Line : returns one of the RAY_ISECT_* defines.
  148. int TestRayVsRay(vec3 const &ray_p00, vec3 const &ray_p01,
  149. vec3 const &ray_p10, vec3 const &ray_p11,
  150. vec3 &isec_p)
  151. {
  152. vec3 rayD0 = ray_p01 - ray_p00;
  153. vec3 rayD0N = normalize(rayD0);
  154. vec3 rayD1 = ray_p11 - ray_p10;
  155. vec3 rayD1N = normalize(rayD1);
  156. vec3 rayP0P1 = ray_p10 - ray_p00;
  157. vec3 c01 = cross(rayD0N, rayD1N);
  158. float crs01S = length(c01);
  159. if (crs01S > -TestEpsilon::Get() && crs01S < TestEpsilon::Get())
  160. return 0;
  161. mat3 m0 = mat3(rayP0P1, rayD1N, c01);
  162. float t0 = determinant(m0) / (crs01S * crs01S);
  163. vec3 isec0 = ray_p00 + rayD0N * t0;
  164. mat3 m1 = mat3(rayP0P1, rayD0N, c01);
  165. float t1 = determinant(m1) / (crs01S * crs01S);
  166. vec3 isec1 = ray_p10 + rayD1N * t1;
  167. if (sqlength(isec0 - isec1) < TestEpsilon::Get()) //ray intersection
  168. {
  169. isec_p = (isec0 + isec0) * .5f;
  170. float d0 = (length(ray_p01 - isec_p) < TestEpsilon::Get() || length(ray_p00 - isec_p) < TestEpsilon::Get())?
  171. (-1.0f):
  172. (dot(ray_p00 - isec_p, ray_p01 - isec_p));
  173. float d1 = (length(ray_p10 - isec_p) < TestEpsilon::Get() || length(ray_p11 - isec_p) < TestEpsilon::Get())?
  174. (-1.0f):
  175. (dot(ray_p10 - isec_p, ray_p11 - isec_p));
  176. //if the dot is negative, your point is in each ray, so say OK.
  177. if (d0 < .0f && d1 < .0f)
  178. return RAY_ISECT_ALL;
  179. if (d0 < .0f)
  180. return RAY_ISECT_P0;
  181. if (d1 < .0f)
  182. return RAY_ISECT_P1;
  183. return RAY_ISECT_NONE;
  184. }
  185. else // this means that there is a line intersection
  186. // but not a ray intersection
  187. return RAY_ISECT_NOTHING;
  188. }
  189. //used to find the intersecting points between a triangle side and a coplanar line.
  190. bool TestRayVsTriangleSide(vec3 const &v0, vec3 const &v1, vec3 const &v2,
  191. vec3 const &ip0, vec3 const &ip1,
  192. vec3 &iV0, int &iIdx0, vec3 &iV1, int &iIdx1)
  193. {
  194. vec3 isecV[2] = { vec3(.0f), vec3(.0f) };
  195. int isecI[2] = { -1, -1 };
  196. vec3 triV[3] = { v0, v1, v2 };
  197. int isecIdx = 0;
  198. vec3 isec_p(0);
  199. //Two points given, so we test each triangle side to find the intersect
  200. isecIdx = 0;
  201. for (int j = 0; j < 3 && isecIdx < 2; j++)
  202. {
  203. int Result = TestRayVsRay(triV[j], triV[(j + 1) % 3], ip0, ip1, isec_p);
  204. if (Result == RAY_ISECT_P0 || Result == RAY_ISECT_ALL)
  205. {
  206. isecV[isecIdx] = isec_p;
  207. isecI[isecIdx] = j;
  208. isecIdx++;
  209. }
  210. }
  211. if (isecIdx < 2)
  212. return false;
  213. //We send the infos back to the parent.
  214. //TODO : that can be better than this horrible thing.
  215. iV0 = isecV[0];
  216. iV1 = isecV[1];
  217. iIdx0 = isecI[0];
  218. iIdx1 = isecI[1];
  219. return true;
  220. }
  221. //--
  222. bool TestPointVsFrustum(const vec3& point, const mat4& frustum, vec3* result_point)
  223. {
  224. vec4 proj_point = frustum * vec4(point, 1.f);
  225. proj_point /= proj_point.w;
  226. if (result_point)
  227. *result_point = proj_point.xyz;
  228. for (int i = 0; i < 3; i++)
  229. if (lol::abs(proj_point[i]) > 1.f)
  230. return false;
  231. return true;
  232. }
  233. } /* namespace lol */