BsCapsule.cpp 5.0 KB

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