blob: 9e6953823453ea7a9a4e6b47d8db626924a7d260 [file] [log] [blame]
// 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/core/geometry/dom_point_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/transforms/transformation_matrix.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 blink {
XRRay::XRRay(const TransformationMatrix& matrix,
ExceptionState& exception_state) {
Set(matrix, exception_state);
}
XRRay::XRRay(XRRigidTransform* transform, ExceptionState& exception_state) {
DOMFloat32Array* m = transform->matrix();
Set(DOMFloat32ArrayToTransformationMatrix(m), exception_state);
}
XRRay::XRRay(DOMPointInit* origin,
DOMPointInit* direction,
ExceptionState& exception_state) {
FloatPoint3D o;
if (origin) {
o = FloatPoint3D(origin->x(), origin->y(), origin->z());
} else {
o = FloatPoint3D(0.f, 0.f, 0.f);
}
FloatPoint3D d;
if (direction) {
d = FloatPoint3D(direction->x(), direction->y(), direction->z());
} else {
d = FloatPoint3D(0.f, 0.f, -1.f);
}
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 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();
if (direction.length() == 0.0f) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
kUnableToNormalizeZeroLength);
return;
} else {
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(ExceptionState& exception_state) {
auto* result = MakeGarbageCollected<XRRay>(nullptr, nullptr, exception_state);
if (exception_state.HadException()) {
return nullptr;
}
return result;
}
XRRay* XRRay::Create(DOMPointInit* origin, ExceptionState& exception_state) {
auto* result = MakeGarbageCollected<XRRay>(origin, nullptr, exception_state);
if (exception_state.HadException()) {
return nullptr;
}
return result;
}
XRRay* XRRay::Create(DOMPointInit* origin,
DOMPointInit* 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_->View() || !matrix_->View()->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);
}
// Step 9: Return matrix
return matrix_;
}
void XRRay::Trace(blink::Visitor* visitor) {
visitor->Trace(origin_);
visitor->Trace(direction_);
visitor->Trace(matrix_);
ScriptWrappable::Trace(visitor);
}
} // namespace blink