Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

geometry.cpp 10 KiB

10 anos atrás
10 anos atrás
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 */