| 1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
| 2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
| 3 | #include "Math/BsRect3.h" |
| 4 | #include "Math/BsRay.h" |
| 5 | #include "Math/BsLineSegment3.h" |
| 6 | #include "Debug/BsDebug.h" |
| 7 | |
| 8 | namespace bs |
| 9 | { |
| 10 | std::pair<std::array<Vector3, 2>, float> Rect3::getNearestPoint(const Ray& ray) const |
| 11 | { |
| 12 | const Vector3& org = ray.getOrigin(); |
| 13 | const Vector3& dir = ray.getDirection(); |
| 14 | |
| 15 | bool foundNearest = false; |
| 16 | float t = 0.0f; |
| 17 | std::array<Vector3, 2> nearestPoints {{ Vector3::ZERO, Vector3::ZERO }}; |
| 18 | float distance = 0.0f; |
| 19 | |
| 20 | // Check if Ray intersects the rectangle |
| 21 | auto intersectResult = intersects(ray); |
| 22 | if (intersectResult.first) |
| 23 | { |
| 24 | t = intersectResult.second; |
| 25 | |
| 26 | nearestPoints[0] = org + dir * t; |
| 27 | nearestPoints[1] = nearestPoints[0]; // Just one point of intersection |
| 28 | foundNearest = true; |
| 29 | } |
| 30 | |
| 31 | // Ray is either passing next to the rectangle or parallel to it, |
| 32 | // compare ray to 4 edges of the rectangle |
| 33 | if (!foundNearest) |
| 34 | { |
| 35 | Vector3 scaledAxes[2]; |
| 36 | scaledAxes[0] = mExtentHorz * mAxisHorz; |
| 37 | scaledAxes[1] = mExtentVert * mAxisVert;; |
| 38 | |
| 39 | distance = std::numeric_limits<float>::max(); |
| 40 | for (UINT32 i = 0; i < 2; i++) |
| 41 | { |
| 42 | for (UINT32 j = 0; j < 2; j++) |
| 43 | { |
| 44 | float sign = (float)(2 * j - 1); |
| 45 | Vector3 segCenter = mCenter + sign * scaledAxes[i]; |
| 46 | Vector3 segStart = segCenter - scaledAxes[1 - i]; |
| 47 | Vector3 segEnd = segCenter + scaledAxes[1 - i]; |
| 48 | |
| 49 | LineSegment3 segment(segStart, segEnd); |
| 50 | auto segResult = segment.getNearestPoint(ray); |
| 51 | |
| 52 | if (segResult.second < distance) |
| 53 | { |
| 54 | nearestPoints = segResult.first; |
| 55 | distance = segResult.second; |
| 56 | } |
| 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 | |
| 70 | nearestPoints[0] = org; |
| 71 | nearestPoints[1] = nearestPointToOrg.first; |
| 72 | distance = nearestPointToOrg.second; |
| 73 | } |
| 74 | |
| 75 | return std::make_pair(nearestPoints, distance); |
| 76 | } |
| 77 | |
| 78 | std::pair<Vector3, float> Rect3::getNearestPoint(const Vector3& point) const |
| 79 | { |
| 80 | Vector3 diff = mCenter - point; |
| 81 | float b0 = diff.dot(mAxisHorz); |
| 82 | float b1 = diff.dot(mAxisVert); |
| 83 | float s0 = -b0, s1 = -b1; |
| 84 | float sqrDistance = diff.dot(diff); |
| 85 | |
| 86 | if (s0 < -mExtentHorz) |
| 87 | s0 = -mExtentHorz; |
| 88 | else if (s0 > mExtentHorz) |
| 89 | s0 = mExtentHorz; |
| 90 | |
| 91 | sqrDistance += s0*(s0 + 2.0f*b0); |
| 92 | |
| 93 | if (s1 < -mExtentVert) |
| 94 | s1 = -mExtentVert; |
| 95 | else if (s1 > mExtentVert) |
| 96 | s1 = mExtentVert; |
| 97 | |
| 98 | sqrDistance += s1*(s1 + 2.0f*b1); |
| 99 | |
| 100 | if (sqrDistance < 0.0f) |
| 101 | sqrDistance = 0.0f; |
| 102 | |
| 103 | float dist = std::sqrt(sqrDistance); |
| 104 | Vector3 nearestPoint = mCenter + s0 * mAxisHorz + s1 * mAxisVert; |
| 105 | |
| 106 | return std::make_pair(nearestPoint, dist); |
| 107 | } |
| 108 | |
| 109 | std::pair<bool, float> Rect3::intersects(const Ray& ray) const |
| 110 | { |
| 111 | const Vector3& org = ray.getOrigin(); |
| 112 | const Vector3& dir = ray.getDirection(); |
| 113 | |
| 114 | Vector3 normal = mAxisHorz.cross(mAxisVert); |
| 115 | float NdotD = normal.dot(dir); |
| 116 | if (fabs(NdotD) > 0.0f) |
| 117 | { |
| 118 | Vector3 diff = org - mCenter; |
| 119 | Vector3 basis[3]; |
| 120 | |
| 121 | basis[0] = dir; |
| 122 | basis[0].orthogonalComplement(basis[1], basis[2]); |
| 123 | |
| 124 | float UdD0 = basis[1].dot(mAxisHorz); |
| 125 | float UdD1 = basis[1].dot(mAxisVert); |
| 126 | float UdPmC = basis[1].dot(diff); |
| 127 | float VdD0 = basis[2].dot(mAxisHorz); |
| 128 | float VdD1 = basis[2].dot(mAxisVert); |
| 129 | float VdPmC = basis[2].dot(diff); |
| 130 | float invDet = 1.0f / (UdD0*VdD1 - UdD1*VdD0); |
| 131 | |
| 132 | float s0 = (VdD1*UdPmC - UdD1*VdPmC)*invDet; |
| 133 | float s1 = (UdD0*VdPmC - VdD0*UdPmC)*invDet; |
| 134 | |
| 135 | if (fabs(s0) <= mExtentHorz && fabs(s1) <= mExtentVert) |
| 136 | { |
| 137 | float DdD0 = dir.dot(mAxisHorz); |
| 138 | float DdD1 = dir.dot(mAxisVert); |
| 139 | float DdDiff = dir.dot(diff); |
| 140 | |
| 141 | float t = s0 * DdD0 + s1 * DdD1 - DdDiff; |
| 142 | |
| 143 | return std::make_pair(true, t); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | return std::make_pair(false, 0.0f); |
| 148 | } |
| 149 | } |
| 150 | |