blob: af6fb1253552dcce56d7ec832d6538ba33d4f3b5 [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/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/math_extras.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);
gfx::Point3F o(origin->x(), origin->y(), origin->z());
gfx::Vector3dF d(direction->x(), direction->y(), direction->z());
if (d.LengthSquared() == 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) {
gfx::Point3F origin = matrix.MapPoint(gfx::Point3F(0, 0, 0));
gfx::Point3F direction_point = matrix.MapPoint(gfx::Point3F(0, 0, -1));
Set(origin, direction_point - origin, 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(gfx::Point3F origin,
gfx::Vector3dF direction,
ExceptionState& exception_state) {
DVLOG(3) << __FUNCTION__ << ": origin=" << origin.ToString()
<< ", direction=" << direction.ToString();
gfx::Vector3dF normalized_direction;
if (!direction.GetNormalized(&normalized_direction))
normalized_direction = gfx::Vector3dF(0, 0, -1);
origin_ = DOMPointReadOnly::Create(origin.x(), origin.y(), origin.z(), 1.0);
direction_ = DOMPointReadOnly::Create(normalized_direction.x(),
normalized_direction.y(),
normalized_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 gfx::Vector3dF desired_ray_direction(
static_cast<float>(direction_->x()),
static_cast<float>(direction_->y()),
static_cast<float>(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 gfx::Vector3dF initial_ray_direction(0.f, 0.f, -1.f);
// Step 3: Let axis be the vector cross product of z and ray’s direction,
// z × direction
gfx::Vector3dF axis =
gfx::CrossProduct(initial_ray_direction, desired_ray_direction);
// Step 4: Let cos_angle be the scalar dot product of z and ray’s direction,
// z · direction
float cos_angle =
gfx::DotProduct(initial_ray_direction, desired_ray_direction);
// 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