BsRect3.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Math/BsRect3.h"
  4. #include "Math/BsRay.h"
  5. #include "Math/BsLineSegment3.h"
  6. #include "Debug/BsDebug.h"
  7. namespace bs
  8. {
  9. Rect3::Rect3()
  10. :mCenter(BsZero), mAxisHorz(BsZero), mAxisVert(BsZero), mExtentHorz(0.0f), mExtentVert(0.0f)
  11. { }
  12. Rect3::Rect3(const Vector3& center, const std::array<Vector3, 2>& axes,
  13. const std::array<float, 2>& extents)
  14. :mCenter(center), mAxisHorz(axes[0]), mAxisVert(axes[1]),
  15. mExtentHorz(extents[0]), mExtentVert(extents[1])
  16. {
  17. }
  18. std::pair<std::array<Vector3, 2>, float> Rect3::getNearestPoint(const Ray& ray) const
  19. {
  20. const Vector3& org = ray.getOrigin();
  21. const Vector3& dir = ray.getDirection();
  22. bool foundNearest = false;
  23. float t = 0.0f;
  24. std::array<Vector3, 2> nearestPoints {{ Vector3::ZERO, Vector3::ZERO }};
  25. float distance = 0.0f;
  26. // Check if Ray intersects the rectangle
  27. auto intersectResult = intersects(ray);
  28. if (intersectResult.first)
  29. {
  30. t = intersectResult.second;
  31. nearestPoints[0] = org + dir * t;
  32. nearestPoints[1] = nearestPoints[0]; // Just one point of intersection
  33. foundNearest = true;
  34. }
  35. // Ray is either passing next to the rectangle or parallel to it,
  36. // compare ray to 4 edges of the rectangle
  37. if (!foundNearest)
  38. {
  39. Vector3 scaledAxes[2];
  40. scaledAxes[0] = mExtentHorz * mAxisHorz;
  41. scaledAxes[1] = mExtentVert * mAxisVert;;
  42. distance = std::numeric_limits<float>::max();
  43. for (UINT32 i = 0; i < 2; i++)
  44. {
  45. for (UINT32 j = 0; j < 2; j++)
  46. {
  47. float sign = (float)(2 * j - 1);
  48. Vector3 segCenter = mCenter + sign * scaledAxes[i];
  49. Vector3 segStart = segCenter - scaledAxes[1 - i];
  50. Vector3 segEnd = segCenter + scaledAxes[1 - i];
  51. LineSegment3 segment(segStart, segEnd);
  52. auto segResult = segment.getNearestPoint(ray);
  53. if (segResult.second < distance)
  54. {
  55. nearestPoints = segResult.first;
  56. distance = segResult.second;
  57. }
  58. }
  59. }
  60. }
  61. // Front of the ray is nearest, use found points
  62. if (t >= 0.0f)
  63. {
  64. // Do nothing, we already have the points
  65. }
  66. else // Rectangle is behind the ray origin, find nearest point to origin
  67. {
  68. auto nearestPointToOrg = getNearestPoint(org);
  69. nearestPoints[0] = org;
  70. nearestPoints[1] = nearestPointToOrg.first;
  71. distance = nearestPointToOrg.second;
  72. }
  73. return std::make_pair(nearestPoints, distance);
  74. }
  75. std::pair<Vector3, float> Rect3::getNearestPoint(const Vector3& point) const
  76. {
  77. Vector3 diff = mCenter - point;
  78. float b0 = diff.dot(mAxisHorz);
  79. float b1 = diff.dot(mAxisVert);
  80. float s0 = -b0, s1 = -b1;
  81. float sqrDistance = diff.dot(diff);
  82. if (s0 < -mExtentHorz)
  83. s0 = -mExtentHorz;
  84. else if (s0 > mExtentHorz)
  85. s0 = mExtentHorz;
  86. sqrDistance += s0*(s0 + 2.0f*b0);
  87. if (s1 < -mExtentVert)
  88. s1 = -mExtentVert;
  89. else if (s1 > mExtentVert)
  90. s1 = mExtentVert;
  91. sqrDistance += s1*(s1 + 2.0f*b1);
  92. if (sqrDistance < 0.0f)
  93. sqrDistance = 0.0f;
  94. float dist = std::sqrt(sqrDistance);
  95. Vector3 nearestPoint = mCenter + s0 * mAxisHorz + s1 * mAxisVert;
  96. return std::make_pair(nearestPoint, dist);
  97. }
  98. std::pair<bool, float> Rect3::intersects(const Ray& ray) const
  99. {
  100. const Vector3& org = ray.getOrigin();
  101. const Vector3& dir = ray.getDirection();
  102. Vector3 normal = mAxisHorz.cross(mAxisVert);
  103. float NdotD = normal.dot(dir);
  104. if (fabs(NdotD) > 0.0f)
  105. {
  106. Vector3 diff = org - mCenter;
  107. Vector3 basis[3];
  108. basis[0] = dir;
  109. basis[0].orthogonalComplement(basis[1], basis[2]);
  110. float UdD0 = basis[1].dot(mAxisHorz);
  111. float UdD1 = basis[1].dot(mAxisVert);
  112. float UdPmC = basis[1].dot(diff);
  113. float VdD0 = basis[2].dot(mAxisHorz);
  114. float VdD1 = basis[2].dot(mAxisVert);
  115. float VdPmC = basis[2].dot(diff);
  116. float invDet = 1.0f / (UdD0*VdD1 - UdD1*VdD0);
  117. float s0 = (VdD1*UdPmC - UdD1*VdPmC)*invDet;
  118. float s1 = (UdD0*VdPmC - VdD0*UdPmC)*invDet;
  119. if (fabs(s0) <= mExtentHorz && fabs(s1) <= mExtentVert)
  120. {
  121. float DdD0 = dir.dot(mAxisHorz);
  122. float DdD1 = dir.dot(mAxisVert);
  123. float DdDiff = dir.dot(diff);
  124. float t = s0 * DdD0 + s1 * DdD1 - DdDiff;
  125. return std::make_pair(true, t);
  126. }
  127. }
  128. return std::make_pair(false, 0.0f);
  129. }
  130. }