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