BsCapsule.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsCapsule.h"
  4. #include "BsRay.h"
  5. namespace BansheeEngine
  6. {
  7. Capsule::Capsule()
  8. :mRadius(0.0f)
  9. { }
  10. Capsule::Capsule(const LineSegment3& segment, float radius)
  11. :mSegment(segment), mRadius(radius)
  12. { }
  13. std::pair<bool, float> Capsule::intersects(const Ray& ray) const
  14. {
  15. const Vector3& org = ray.getOrigin();
  16. const Vector3& dir = ray.getDirection();
  17. Vector3 segDir = mSegment.getEnd() - mSegment.getStart();
  18. float segExtent = segDir.normalize() * 0.5f;
  19. Vector3 segCenter = mSegment.getStart() + segDir * segExtent;
  20. Vector3 basis[3];
  21. basis[0] = segDir;
  22. basis[0].orthogonalComplement(basis[1], basis[2]);
  23. float rSqr = mRadius * mRadius;
  24. Vector3 diff = org - segCenter;
  25. Vector3 P(basis[1].dot(diff), basis[2].dot(diff), basis[0].dot(diff));
  26. // Get the z-value, in capsule coordinates, of the incoming line's
  27. // unit-length direction.
  28. float dz = basis[0].dot(dir);
  29. if (std::abs(dz) == 1.0f)
  30. {
  31. // The line is parallel to the capsule axis. Determine whether the
  32. // line intersects the capsule hemispheres.
  33. float radialSqrDist = rSqr - P[0] * P[0] - P[1] * P[1];
  34. if (radialSqrDist < 0.0f)
  35. {
  36. // The line is outside the cylinder of the capsule, so there is no
  37. // intersection.
  38. return std::make_pair(false, 0.0f);
  39. }
  40. // The line intersects the hemispherical caps.
  41. float zOffset = std::sqrt(radialSqrDist) + segExtent;
  42. if (dz > 0.0f)
  43. return std::make_pair(true, -P[2] - zOffset);
  44. else
  45. return std::make_pair(true, P[2] - zOffset);
  46. }
  47. // Convert the incoming line unit-length direction to capsule coordinates.
  48. Vector3 D(basis[1].dot(dir), basis[2].dot(dir), dz);
  49. // Test intersection of line with infinite cylinder
  50. float a0 = P[0] * P[0] + P[1] * P[1] - rSqr;
  51. float a1 = P[0] * D[0] + P[1] * D[1];
  52. float a2 = D[0] * D[0] + D[1] * D[1];
  53. float discr = a1*a1 - a0*a2;
  54. if (discr < 0.0f)
  55. {
  56. // The line does not intersect the infinite cylinder.
  57. return std::make_pair(false, 0.0f);
  58. }
  59. float root, inv, tValue, zValue;
  60. float nearestT = std::numeric_limits<float>::max();
  61. bool foundOneIntersection = false;
  62. if (discr > 0.0f)
  63. {
  64. // The line intersects the infinite cylinder in two places.
  65. root = std::sqrt(discr);
  66. inv = 1.0f / a2;
  67. tValue = (-a1 - root)*inv;
  68. zValue = P[2] + tValue*D[2];
  69. if (std::abs(zValue) <= segExtent)
  70. {
  71. nearestT = tValue;
  72. foundOneIntersection = true;
  73. }
  74. tValue = (-a1 + root)*inv;
  75. zValue = P[2] + tValue*D[2];
  76. if (std::abs(zValue) <= segExtent)
  77. {
  78. if (foundOneIntersection)
  79. return std::make_pair(true, nearestT);
  80. else
  81. {
  82. nearestT = tValue;
  83. foundOneIntersection = true;
  84. }
  85. }
  86. }
  87. else
  88. {
  89. // The line is tangent to the infinite cylinder but intersects the
  90. // cylinder in a single point.
  91. tValue = -a1 / a2;
  92. zValue = P[2] + tValue*D[2];
  93. if (std::abs(zValue) <= segExtent)
  94. return std::make_pair(true, tValue);
  95. }
  96. // Test intersection with bottom hemisphere.
  97. float PZpE = P[2] + segExtent;
  98. a1 += PZpE*D[2];
  99. a0 += PZpE*PZpE;
  100. discr = a1*a1 - a0;
  101. if (discr > 0)
  102. {
  103. root = sqrt(discr);
  104. tValue = -a1 - root;
  105. zValue = P[2] + tValue*D[2];
  106. if (zValue <= -segExtent)
  107. {
  108. if (foundOneIntersection)
  109. return std::make_pair(true, nearestT < tValue ? nearestT : tValue);
  110. else
  111. {
  112. nearestT = tValue;
  113. foundOneIntersection = true;
  114. }
  115. }
  116. tValue = -a1 + root;
  117. zValue = P[2] + tValue*D[2];
  118. if (zValue <= -segExtent)
  119. {
  120. if (foundOneIntersection)
  121. return std::make_pair(true, nearestT < tValue ? nearestT : tValue);
  122. else
  123. {
  124. nearestT = tValue;
  125. foundOneIntersection = true;
  126. }
  127. }
  128. }
  129. else if (discr == 0.0f)
  130. {
  131. tValue = -a1;
  132. zValue = P[2] + tValue*D[2];
  133. if (zValue <= -segExtent)
  134. {
  135. if (foundOneIntersection)
  136. return std::make_pair(true, nearestT < tValue ? nearestT : tValue);
  137. else
  138. {
  139. nearestT = tValue;
  140. foundOneIntersection = true;
  141. }
  142. }
  143. }
  144. // Test intersection with top hemisphere
  145. a1 -= 2.0f*segExtent*D[2];
  146. a0 -= 4.0f*segExtent*P[2];
  147. discr = a1*a1 - a0;
  148. if (discr > 0.0f)
  149. {
  150. root = sqrt(discr);
  151. tValue = -a1 - root;
  152. zValue = P[2] + tValue*D[2];
  153. if (zValue >= segExtent)
  154. {
  155. if (foundOneIntersection)
  156. return std::make_pair(true, nearestT < tValue ? nearestT : tValue);
  157. else
  158. {
  159. nearestT = tValue;
  160. foundOneIntersection = true;
  161. }
  162. }
  163. tValue = -a1 + root;
  164. zValue = P[2] + tValue*D[2];
  165. if (zValue >= segExtent)
  166. {
  167. if (foundOneIntersection)
  168. return std::make_pair(true, nearestT < tValue ? nearestT : tValue);
  169. else
  170. {
  171. nearestT = tValue;
  172. foundOneIntersection = true;
  173. }
  174. }
  175. }
  176. else if (discr == 0.0f)
  177. {
  178. tValue = -a1;
  179. zValue = P[2] + tValue*D[2];
  180. if (zValue >= segExtent)
  181. {
  182. if (foundOneIntersection)
  183. return std::make_pair(true, nearestT < tValue ? nearestT : tValue);
  184. else
  185. {
  186. nearestT = tValue;
  187. foundOneIntersection = true;
  188. }
  189. }
  190. }
  191. if (foundOneIntersection)
  192. return std::make_pair(true, nearestT);
  193. return std::make_pair(false, 0.0f);
  194. }
  195. }