BsCapsule.cpp 5.0 KB

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