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 | |