1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/xr/xr_ray.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <utility>
10
11 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_point_init.h"
12 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_ray_direction_init.h"
13 #include "third_party/blink/renderer/core/geometry/dom_point_read_only.h"
14 #include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
15 #include "third_party/blink/renderer/modules/xr/xr_utils.h"
16 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
17 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
18 #include "ui/gfx/geometry/quaternion.h"
19 #include "ui/gfx/geometry/vector3d_f.h"
20
21 namespace {
22
23 constexpr char kInvalidWComponentInOrigin[] =
24 "Origin's `w` component must be set to 1.0f!";
25 constexpr char kInvalidWComponentInDirection[] =
26 "Direction's `w` component must be set to 0.0f!";
27
28 } // namespace
29
30 namespace blink {
31
XRRay()32 XRRay::XRRay() {
33 origin_ = DOMPointReadOnly::Create(0.0, 0.0, 0.0, 1.0);
34 direction_ = DOMPointReadOnly::Create(0.0, 0.0, -1.0, 0.0);
35 }
36
XRRay(XRRigidTransform * transform,ExceptionState & exception_state)37 XRRay::XRRay(XRRigidTransform* transform, ExceptionState& exception_state) {
38 DOMFloat32Array* m = transform->matrix();
39 Set(DOMFloat32ArrayToTransformationMatrix(m), exception_state);
40 }
41
XRRay(DOMPointInit * origin,XRRayDirectionInit * direction,ExceptionState & exception_state)42 XRRay::XRRay(DOMPointInit* origin,
43 XRRayDirectionInit* direction,
44 ExceptionState& exception_state) {
45 DCHECK(origin);
46 DCHECK(direction);
47
48 FloatPoint3D o(origin->x(), origin->y(), origin->z());
49 FloatPoint3D d(direction->x(), direction->y(), direction->z());
50
51 if (d.length() == 0.0f) {
52 exception_state.ThrowTypeError(kUnableToNormalizeZeroLength);
53 return;
54 }
55
56 if (direction->w() != 0.0f) {
57 exception_state.ThrowTypeError(kInvalidWComponentInDirection);
58 return;
59 }
60
61 if (origin->w() != 1.0f) {
62 exception_state.ThrowTypeError(kInvalidWComponentInOrigin);
63 return;
64 }
65
66 Set(o, d, exception_state);
67 }
68
Set(const TransformationMatrix & matrix,ExceptionState & exception_state)69 void XRRay::Set(const TransformationMatrix& matrix,
70 ExceptionState& exception_state) {
71 FloatPoint3D origin = matrix.MapPoint(FloatPoint3D(0, 0, 0));
72 FloatPoint3D direction = matrix.MapPoint(FloatPoint3D(0, 0, -1));
73 direction.Move(-origin.X(), -origin.Y(), -origin.Z());
74
75 Set(origin, direction, exception_state);
76 }
77
78 // Sets member variables from passed in |origin| and |direction|.
79 // All constructors with the exception of default constructor eventually invoke
80 // this method.
81 // If the |direction|'s length is 0, this method will initialize direction to
82 // default vector (0, 0, -1).
Set(FloatPoint3D origin,FloatPoint3D direction,ExceptionState & exception_state)83 void XRRay::Set(FloatPoint3D origin,
84 FloatPoint3D direction,
85 ExceptionState& exception_state) {
86 DVLOG(3) << __FUNCTION__ << ": origin=" << origin.ToString()
87 << ", direction=" << direction.ToString();
88
89 direction.Normalize();
90
91 origin_ = DOMPointReadOnly::Create(origin.X(), origin.Y(), origin.Z(), 1.0);
92 direction_ = DOMPointReadOnly::Create(direction.X(), direction.Y(),
93 direction.Z(), 0.0);
94 }
95
Create(XRRigidTransform * transform,ExceptionState & exception_state)96 XRRay* XRRay::Create(XRRigidTransform* transform,
97 ExceptionState& exception_state) {
98 auto* result = MakeGarbageCollected<XRRay>(transform, exception_state);
99
100 if (exception_state.HadException()) {
101 return nullptr;
102 }
103
104 return result;
105 }
106
Create(DOMPointInit * origin,XRRayDirectionInit * direction,ExceptionState & exception_state)107 XRRay* XRRay::Create(DOMPointInit* origin,
108 XRRayDirectionInit* direction,
109 ExceptionState& exception_state) {
110 auto* result =
111 MakeGarbageCollected<XRRay>(origin, direction, exception_state);
112
113 if (exception_state.HadException()) {
114 return nullptr;
115 }
116
117 return result;
118 }
119
~XRRay()120 XRRay::~XRRay() {}
121
matrix()122 DOMFloat32Array* XRRay::matrix() {
123 DVLOG(3) << __FUNCTION__;
124
125 // A page may take the matrix value and detach it so matrix_ is a detached
126 // array buffer. If that's the case, recompute the matrix.
127 // Step 1. If transform’s internal matrix is not null, perform the following
128 // steps:
129 // Step 1. If the operation IsDetachedBuffer on internal matrix is false,
130 // return transform’s internal matrix.
131 if (!matrix_ || !matrix_->Data()) {
132 // Returned matrix should represent transformation from ray originating at
133 // (0,0,0) with direction (0,0,-1) into ray originating at |origin_| with
134 // direction |direction_|.
135
136 TransformationMatrix matrix;
137
138 const blink::FloatPoint3D desiredRayDirection = {
139 direction_->x(), direction_->y(), direction_->z()};
140
141 // Translation from 0 to |origin_| is simply translation by |origin_|.
142 // (implicit) Step 6: Let translation be the translation matrix with
143 // components corresponding to ray’s origin
144 matrix.Translate3d(origin_->x(), origin_->y(), origin_->z());
145
146 // Step 2: Let z be the vector [0, 0, -1]
147 const blink::FloatPoint3D initialRayDirection =
148 blink::FloatPoint3D{0.f, 0.f, -1.f};
149
150 // Step 3: Let axis be the vector cross product of z and ray’s direction,
151 // z × direction
152 blink::FloatPoint3D axis = initialRayDirection.Cross(desiredRayDirection);
153
154 // Step 4: Let cos_angle be the scalar dot product of z and ray’s direction,
155 // z · direction
156 float cos_angle = initialRayDirection.Dot(desiredRayDirection);
157
158 // Step 5: Set rotation based on the following:
159 if (cos_angle > 0.9999) {
160 // Vectors are co-linear or almost co-linear & face the same direction,
161 // no rotation is needed.
162
163 } else if (cos_angle < -0.9999) {
164 // Vectors are co-linear or almost co-linear & face the opposite
165 // direction, rotation by 180 degrees is needed & can be around any vector
166 // perpendicular to (0,0,-1) so let's rotate by (1, 0, 0).
167 matrix.Rotate3d(1, 0, 0, 180);
168 } else {
169 // Rotation needed - create it from axis-angle.
170 matrix.Rotate3d(axis.X(), axis.Y(), axis.Z(),
171 rad2deg(std::acos(cos_angle)));
172 }
173
174 // Step 7: Let matrix be the result of premultiplying rotation from the left
175 // onto translation (i.e. translation * rotation) in column-vector notation.
176 // Step 8: Set ray’s internal matrix to matrix
177 matrix_ = transformationMatrixToDOMFloat32Array(matrix);
178 if (!raw_matrix_) {
179 raw_matrix_ = std::make_unique<TransformationMatrix>(matrix);
180 } else {
181 *raw_matrix_ = matrix;
182 }
183 }
184
185 // Step 9: Return matrix
186 return matrix_;
187 }
188
RawMatrix()189 TransformationMatrix XRRay::RawMatrix() {
190 matrix();
191
192 DCHECK(raw_matrix_);
193
194 return *raw_matrix_;
195 }
196
Trace(Visitor * visitor) const197 void XRRay::Trace(Visitor* visitor) const {
198 visitor->Trace(origin_);
199 visitor->Trace(direction_);
200 visitor->Trace(matrix_);
201 ScriptWrappable::Trace(visitor);
202 }
203
204 } // namespace blink
205