summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/xr/xr_ray.cc
blob: 8be9ce9e129e9e967cb13feccebfd1d5c04d23bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/modules/xr/xr_ray.h"

#include <algorithm>
#include <cmath>
#include <utility>

#include "third_party/blink/renderer/bindings/core/v8/v8_dom_point_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_xr_ray_direction_init.h"
#include "third_party/blink/renderer/core/geometry/dom_point_read_only.h"
#include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
#include "third_party/blink/renderer/modules/xr/xr_utils.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "ui/gfx/geometry/quaternion.h"
#include "ui/gfx/geometry/vector3d_f.h"

namespace {

constexpr char kInvalidWComponentInOrigin[] =
    "Origin's `w` component must be set to 1.0f!";
constexpr char kInvalidWComponentInDirection[] =
    "Direction's `w` component must be set to 0.0f!";

}  // namespace

namespace blink {

XRRay::XRRay() {
  origin_ = DOMPointReadOnly::Create(0.0, 0.0, 0.0, 1.0);
  direction_ = DOMPointReadOnly::Create(0.0, 0.0, -1.0, 0.0);
}

XRRay::XRRay(XRRigidTransform* transform, ExceptionState& exception_state) {
  DOMFloat32Array* m = transform->matrix();
  Set(DOMFloat32ArrayToTransformationMatrix(m), exception_state);
}

XRRay::XRRay(DOMPointInit* origin,
             XRRayDirectionInit* direction,
             ExceptionState& exception_state) {
  DCHECK(origin);
  DCHECK(direction);

  FloatPoint3D o(origin->x(), origin->y(), origin->z());
  FloatPoint3D d(direction->x(), direction->y(), direction->z());

  if (d.length() == 0.0f) {
    exception_state.ThrowTypeError(kUnableToNormalizeZeroLength);
    return;
  }

  if (direction->w() != 0.0f) {
    exception_state.ThrowTypeError(kInvalidWComponentInDirection);
    return;
  }

  if (origin->w() != 1.0f) {
    exception_state.ThrowTypeError(kInvalidWComponentInOrigin);
    return;
  }

  Set(o, d, exception_state);
}

void XRRay::Set(const TransformationMatrix& matrix,
                ExceptionState& exception_state) {
  FloatPoint3D origin = matrix.MapPoint(FloatPoint3D(0, 0, 0));
  FloatPoint3D direction = matrix.MapPoint(FloatPoint3D(0, 0, -1));
  direction.Move(-origin.X(), -origin.Y(), -origin.Z());

  Set(origin, direction, exception_state);
}

// Sets member variables from passed in |origin| and |direction|.
// All constructors with the exception of default constructor eventually invoke
// this method.
// If the |direction|'s length is 0, this method will initialize direction to
// default vector (0, 0, -1).
void XRRay::Set(FloatPoint3D origin,
                FloatPoint3D direction,
                ExceptionState& exception_state) {
  DVLOG(3) << __FUNCTION__ << ": origin=" << origin.ToString()
           << ", direction=" << direction.ToString();

  direction.Normalize();

  origin_ = DOMPointReadOnly::Create(origin.X(), origin.Y(), origin.Z(), 1.0);
  direction_ = DOMPointReadOnly::Create(direction.X(), direction.Y(),
                                        direction.Z(), 0.0);
}

XRRay* XRRay::Create(XRRigidTransform* transform,
                     ExceptionState& exception_state) {
  auto* result = MakeGarbageCollected<XRRay>(transform, exception_state);

  if (exception_state.HadException()) {
    return nullptr;
  }

  return result;
}

XRRay* XRRay::Create(DOMPointInit* origin,
                     XRRayDirectionInit* direction,
                     ExceptionState& exception_state) {
  auto* result =
      MakeGarbageCollected<XRRay>(origin, direction, exception_state);

  if (exception_state.HadException()) {
    return nullptr;
  }

  return result;
}

XRRay::~XRRay() {}

DOMFloat32Array* XRRay::matrix() {
  DVLOG(3) << __FUNCTION__;

  // A page may take the matrix value and detach it so matrix_ is a detached
  // array buffer.  If that's the case, recompute the matrix.
  // Step 1. If transform’s internal matrix is not null, perform the following
  // steps:
  //    Step 1. If the operation IsDetachedBuffer on internal matrix is false,
  //    return transform’s internal matrix.
  if (!matrix_ || !matrix_->Data()) {
    // Returned matrix should represent transformation from ray originating at
    // (0,0,0) with direction (0,0,-1) into ray originating at |origin_| with
    // direction |direction_|.

    TransformationMatrix matrix;

    const blink::FloatPoint3D desiredRayDirection = {
        direction_->x(), direction_->y(), direction_->z()};

    // Translation from 0 to |origin_| is simply translation by |origin_|.
    // (implicit) Step 6: Let translation be the translation matrix with
    // components corresponding to ray’s origin
    matrix.Translate3d(origin_->x(), origin_->y(), origin_->z());

    // Step 2: Let z be the vector [0, 0, -1]
    const blink::FloatPoint3D initialRayDirection =
        blink::FloatPoint3D{0.f, 0.f, -1.f};

    // Step 3: Let axis be the vector cross product of z and ray’s direction,
    // z × direction
    blink::FloatPoint3D axis = initialRayDirection.Cross(desiredRayDirection);

    // Step 4: Let cos_angle be the scalar dot product of z and ray’s direction,
    // z · direction
    float cos_angle = initialRayDirection.Dot(desiredRayDirection);

    // Step 5: Set rotation based on the following:
    if (cos_angle > 0.9999) {
      // Vectors are co-linear or almost co-linear & face the same direction,
      // no rotation is needed.

    } else if (cos_angle < -0.9999) {
      // Vectors are co-linear or almost co-linear & face the opposite
      // direction, rotation by 180 degrees is needed & can be around any vector
      // perpendicular to (0,0,-1) so let's rotate by (1, 0, 0).
      matrix.Rotate3d(1, 0, 0, 180);
    } else {
      // Rotation needed - create it from axis-angle.
      matrix.Rotate3d(axis.X(), axis.Y(), axis.Z(),
                      rad2deg(std::acos(cos_angle)));
    }

    // Step 7: Let matrix be the result of premultiplying rotation from the left
    // onto translation (i.e. translation * rotation) in column-vector notation.
    // Step 8: Set ray’s internal matrix to matrix
    matrix_ = transformationMatrixToDOMFloat32Array(matrix);
    if (!raw_matrix_) {
      raw_matrix_ = std::make_unique<TransformationMatrix>(matrix);
    } else {
      *raw_matrix_ = matrix;
    }
  }

  // Step 9: Return matrix
  return matrix_;
}

TransformationMatrix XRRay::RawMatrix() {
  matrix();

  DCHECK(raw_matrix_);

  return *raw_matrix_;
}

void XRRay::Trace(Visitor* visitor) const {
  visitor->Trace(origin_);
  visitor->Trace(direction_);
  visitor->Trace(matrix_);
  ScriptWrappable::Trace(visitor);
}

}  // namespace blink