| // Copyright 2017 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_view.h" |
| |
| #include "third_party/blink/renderer/modules/xr/xr_frame.h" |
| #include "third_party/blink/renderer/modules/xr/xr_session.h" |
| #include "third_party/blink/renderer/modules/xr/xr_utils.h" |
| #include "third_party/blink/renderer/platform/geometry/float_point_3d.h" |
| |
| namespace blink { |
| |
| XRView::XRView(XRSession* session, XREye eye) |
| : eye_(eye), |
| session_(session), |
| projection_matrix_(DOMFloat32Array::Create(16)), |
| view_matrix_(DOMFloat32Array::Create(16)) { |
| eye_string_ = (eye_ == kEyeLeft ? "left" : "right"); |
| } |
| |
| XRView::XRView() |
| : eye_(XREye::kEyeLeft), |
| eye_string_("left"), |
| session_(nullptr), |
| projection_matrix_(DOMFloat32Array::Create(16)), |
| view_matrix_(DOMFloat32Array::Create(16)) {} |
| |
| // deep copy |
| XRView::XRView(const XRView& other) |
| : projection_matrix_(DOMFloat32Array::Create(16)), |
| view_matrix_(DOMFloat32Array::Create(16)) { |
| *this = other; |
| } |
| |
| // deep copy |
| XRView& XRView::operator=(const XRView& other) { |
| if (&other == this) |
| return *this; |
| |
| eye_ = other.eye_; |
| eye_string_ = other.eye_string_; |
| session_ = other.session_; |
| AssignMatrices(other); |
| offset_ = other.offset_; |
| |
| transform_ = |
| MakeGarbageCollected<XRRigidTransform>(*(other.transform_.Get())); |
| |
| // Don't copy the inverse projection matrix because it is rarely used. |
| // Just set this flag so that if UnprojectPointer is called, this matrix |
| // gets computed. |
| inv_projection_dirty_ = true; |
| |
| return *this; |
| } |
| |
| void XRView::AssignMatrices(const XRView& other) { |
| const float* src_projection_data = other.projection_matrix_->Data(); |
| const float* src_view_data = other.view_matrix_->Data(); |
| |
| float* dst_projection_data = projection_matrix_->Data(); |
| float* dst_view_data = view_matrix_->Data(); |
| |
| for (int i = 0; i < 16; ++i) { |
| dst_projection_data[i] = src_projection_data[i]; |
| dst_view_data[i] = src_view_data[i]; |
| } |
| } |
| |
| XRSession* XRView::session() const { |
| return session_; |
| } |
| |
| // TODO(http://crbug.com/836496): This method only supports |
| // straight-ahead projection matrices. In order to support |
| // multiple sessions embedded with projection matrices that act |
| // like views into the shared camera space, this math needs to |
| // be updated. |
| void XRView::UpdateProjectionMatrixFromRawValues( |
| const WTF::Vector<float>& projection_matrix, |
| float near_depth, |
| float far_depth) { |
| DCHECK_EQ(projection_matrix.size(), 16lu); |
| float* out = projection_matrix_->Data(); |
| for (int i = 0; i < 16; i++) { |
| out[i] = projection_matrix[i]; |
| } |
| |
| // Recalculate elements that depend on near/far depth. The input matrix used |
| // arbitrary values, need to adjust to what the client uses. |
| float inverse_near_far = 1.0f / (near_depth - far_depth); |
| out[10] = (near_depth + far_depth) * inverse_near_far; |
| out[14] = (2.0f * far_depth * near_depth) * inverse_near_far; |
| |
| inv_projection_dirty_ = true; |
| } |
| |
| void XRView::UpdateProjectionMatrixFromFoV(float up_rad, |
| float down_rad, |
| float left_rad, |
| float right_rad, |
| float near_depth, |
| float far_depth) { |
| float up_tan = tanf(up_rad); |
| float down_tan = tanf(down_rad); |
| float left_tan = tanf(left_rad); |
| float right_tan = tanf(right_rad); |
| float x_scale = 2.0f / (left_tan + right_tan); |
| float y_scale = 2.0f / (up_tan + down_tan); |
| float inv_nf = 1.0f / (near_depth - far_depth); |
| |
| float* out = projection_matrix_->Data(); |
| out[0] = x_scale; |
| out[1] = 0.0f; |
| out[2] = 0.0f; |
| out[3] = 0.0f; |
| out[4] = 0.0f; |
| out[5] = y_scale; |
| out[6] = 0.0f; |
| out[7] = 0.0f; |
| out[8] = -((left_tan - right_tan) * x_scale * 0.5); |
| out[9] = ((up_tan - down_tan) * y_scale * 0.5); |
| out[10] = (near_depth + far_depth) * inv_nf; |
| out[11] = -1.0f; |
| out[12] = 0.0f; |
| out[13] = 0.0f; |
| out[14] = (2.0f * far_depth * near_depth) * inv_nf; |
| out[15] = 0.0f; |
| |
| inv_projection_dirty_ = true; |
| } |
| |
| void XRView::UpdateProjectionMatrixFromAspect(float fovy, |
| float aspect, |
| float near_depth, |
| float far_depth) { |
| float f = 1.0f / tanf(fovy / 2); |
| float inv_nf = 1.0f / (near_depth - far_depth); |
| |
| float* out = projection_matrix_->Data(); |
| out[0] = f / aspect; |
| out[1] = 0.0f; |
| out[2] = 0.0f; |
| out[3] = 0.0f; |
| out[4] = 0.0f; |
| out[5] = f; |
| out[6] = 0.0f; |
| out[7] = 0.0f; |
| out[8] = 0.0f; |
| out[9] = 0.0f; |
| out[10] = (far_depth + near_depth) * inv_nf; |
| out[11] = -1.0f; |
| out[12] = 0.0f; |
| out[13] = 0.0f; |
| out[14] = (2.0f * far_depth * near_depth) * inv_nf; |
| out[15] = 0.0f; |
| |
| inv_projection_dirty_ = true; |
| } |
| |
| void XRView::UpdateOffset(float x, float y, float z) { |
| offset_.Set(x, y, z); |
| } |
| |
| std::unique_ptr<TransformationMatrix> XRView::UnprojectPointer( |
| double x, |
| double y, |
| double canvas_width, |
| double canvas_height) { |
| // Recompute the inverse projection matrix if needed. |
| if (inv_projection_dirty_) { |
| float* m = projection_matrix_->Data(); |
| std::unique_ptr<TransformationMatrix> projection = |
| TransformationMatrix::Create(m[0], m[1], m[2], m[3], m[4], m[5], m[6], |
| m[7], m[8], m[9], m[10], m[11], m[12], |
| m[13], m[14], m[15]); |
| inv_projection_ = TransformationMatrix::Create(projection->Inverse()); |
| inv_projection_dirty_ = false; |
| } |
| |
| // Transform the x/y coordinate into WebGL normalized device coordinates. |
| // Z coordinate of -1 means the point will be projected onto the projection |
| // matrix near plane. |
| FloatPoint3D point_in_projection_space( |
| x / canvas_width * 2.0 - 1.0, |
| (canvas_height - y) / canvas_height * 2.0 - 1.0, -1.0); |
| |
| FloatPoint3D point_in_view_space = |
| inv_projection_->MapPoint(point_in_projection_space); |
| |
| const FloatPoint3D kOrigin(0.0, 0.0, 0.0); |
| const FloatPoint3D kUp(0.0, 1.0, 0.0); |
| |
| // Generate a "Look At" matrix |
| FloatPoint3D z_axis = kOrigin - point_in_view_space; |
| z_axis.Normalize(); |
| |
| FloatPoint3D x_axis = kUp.Cross(z_axis); |
| x_axis.Normalize(); |
| |
| FloatPoint3D y_axis = z_axis.Cross(x_axis); |
| y_axis.Normalize(); |
| |
| // TODO(bajones): There's probably a more efficent way to do this? |
| TransformationMatrix inv_pointer(x_axis.X(), y_axis.X(), z_axis.X(), 0.0, |
| x_axis.Y(), y_axis.Y(), z_axis.Y(), 0.0, |
| x_axis.Z(), y_axis.Z(), z_axis.Z(), 0.0, 0.0, |
| 0.0, 0.0, 1.0); |
| inv_pointer.Translate3d(-point_in_view_space.X(), -point_in_view_space.Y(), |
| -point_in_view_space.Z()); |
| |
| // LookAt matrices are view matrices (inverted), so invert before returning. |
| std::unique_ptr<TransformationMatrix> pointer = |
| TransformationMatrix::Create(inv_pointer.Inverse()); |
| |
| return pointer; |
| } |
| |
| // Pass inv_pose_matrix by value because this method modifies its offset, but |
| // the calling code doesn't want it changed. |
| void XRView::UpdateViewMatrix(TransformationMatrix inv_pose_matrix) { |
| // Transform by the negative offset, since we're operating on the inverted |
| // matrix |
| inv_pose_matrix.PostTranslate3d(-offset_.X(), -offset_.Y(), -offset_.Z()); |
| view_matrix_ = transformationMatrixToDOMFloat32Array(inv_pose_matrix); |
| |
| // transform's matrix is the inverse of the view matrix |
| // can't use the original pose matrix because it has translation applied |
| // after taking the inverse |
| // compute the inverse lazily |
| DCHECK(inv_pose_matrix.IsInvertible()); |
| inv_pose_ = TransformationMatrix::Create(inv_pose_matrix); |
| } |
| |
| XRRigidTransform* XRView::transform() { |
| if (!transform_) { |
| transform_ = MakeGarbageCollected<XRRigidTransform>(inv_pose_->Inverse()); |
| } |
| return transform_; |
| } |
| |
| void XRView::Trace(blink::Visitor* visitor) { |
| visitor->Trace(session_); |
| visitor->Trace(projection_matrix_); |
| visitor->Trace(view_matrix_); |
| visitor->Trace(transform_); |
| ScriptWrappable::Trace(visitor); |
| } |
| |
| } // namespace blink |