You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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