blob: 217cde8dc61c9f87859fb9a7d33dee2233db0524 [file] [log] [blame]
// Copyright 2016 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 "modules/vr/VRFrameData.h"
#include "modules/vr/VREyeParameters.h"
#include "modules/vr/VRPose.h"
#include <cmath>
namespace blink {
// TODO(bajones): All of the matrix math here is temporary. It will be removed
// once the VRService has been updated to allow the underlying VR APIs to
// provide the projection and view matrices directly.
// Build a projection matrix from a field of view and near/far planes.
void ProjectionFromFieldOfView(DOMFloat32Array* out_array,
VRFieldOfView* fov,
float depth_near,
float depth_far) {
float up_tan = tanf(fov->UpDegrees() * M_PI / 180.0);
float down_tan = tanf(fov->DownDegrees() * M_PI / 180.0);
float left_tan = tanf(fov->LeftDegrees() * M_PI / 180.0);
float right_tan = tanf(fov->RightDegrees() * M_PI / 180.0);
float x_scale = 2.0f / (left_tan + right_tan);
float y_scale = 2.0f / (up_tan + down_tan);
float* out = out_array->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] = (depth_near + depth_far) / (depth_near - depth_far);
out[11] = -1.0f;
out[12] = 0.0f;
out[13] = 0.0f;
out[14] = (2 * depth_far * depth_near) / (depth_near - depth_far);
out[15] = 0.0f;
}
// Create a matrix from a rotation and translation.
void MatrixfromRotationTranslation(
DOMFloat32Array* out_array,
const WTF::Optional<WTF::Vector<float>>& rotation,
const WTF::Optional<WTF::Vector<float>>& translation) {
// Quaternion math
float x = !rotation ? 0.0f : rotation.value()[0];
float y = !rotation ? 0.0f : rotation.value()[1];
float z = !rotation ? 0.0f : rotation.value()[2];
float w = !rotation ? 1.0f : rotation.value()[3];
float x2 = x + x;
float y2 = y + y;
float z2 = z + z;
float xx = x * x2;
float xy = x * y2;
float xz = x * z2;
float yy = y * y2;
float yz = y * z2;
float zz = z * z2;
float wx = w * x2;
float wy = w * y2;
float wz = w * z2;
float* out = out_array->Data();
out[0] = 1 - (yy + zz);
out[1] = xy + wz;
out[2] = xz - wy;
out[3] = 0;
out[4] = xy - wz;
out[5] = 1 - (xx + zz);
out[6] = yz + wx;
out[7] = 0;
out[8] = xz + wy;
out[9] = yz - wx;
out[10] = 1 - (xx + yy);
out[11] = 0;
out[12] = !translation ? 0.0f : translation.value()[0];
out[13] = !translation ? 0.0f : translation.value()[1];
out[14] = !translation ? 0.0f : translation.value()[2];
out[15] = 1;
}
// Translate a matrix
void MatrixTranslate(DOMFloat32Array* out_array,
const DOMFloat32Array* translation) {
if (!translation)
return;
float x = translation->Data()[0];
float y = translation->Data()[1];
float z = translation->Data()[2];
float* out = out_array->Data();
out[12] = out[0] * x + out[4] * y + out[8] * z + out[12];
out[13] = out[1] * x + out[5] * y + out[9] * z + out[13];
out[14] = out[2] * x + out[6] * y + out[10] * z + out[14];
out[15] = out[3] * x + out[7] * y + out[11] * z + out[15];
}
bool MatrixInvert(DOMFloat32Array* out_array) {
float* out = out_array->Data();
float a00 = out[0];
float a01 = out[1];
float a02 = out[2];
float a03 = out[3];
float a10 = out[4];
float a11 = out[5];
float a12 = out[6];
float a13 = out[7];
float a20 = out[8];
float a21 = out[9];
float a22 = out[10];
float a23 = out[11];
float a30 = out[12];
float a31 = out[13];
float a32 = out[14];
float a33 = out[15];
float b00 = a00 * a11 - a01 * a10;
float b01 = a00 * a12 - a02 * a10;
float b02 = a00 * a13 - a03 * a10;
float b03 = a01 * a12 - a02 * a11;
float b04 = a01 * a13 - a03 * a11;
float b05 = a02 * a13 - a03 * a12;
float b06 = a20 * a31 - a21 * a30;
float b07 = a20 * a32 - a22 * a30;
float b08 = a20 * a33 - a23 * a30;
float b09 = a21 * a32 - a22 * a31;
float b10 = a21 * a33 - a23 * a31;
float b11 = a22 * a33 - a23 * a32;
// Calculate the determinant
float det =
b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det)
return false;
det = 1.0 / det;
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
return true;
};
VRFrameData::VRFrameData() {
left_projection_matrix_ = DOMFloat32Array::Create(16);
left_view_matrix_ = DOMFloat32Array::Create(16);
right_projection_matrix_ = DOMFloat32Array::Create(16);
right_view_matrix_ = DOMFloat32Array::Create(16);
pose_ = VRPose::Create();
}
bool VRFrameData::Update(const device::mojom::blink::VRPosePtr& pose,
VREyeParameters* left_eye,
VREyeParameters* right_eye,
float depth_near,
float depth_far) {
VRFieldOfView* fov_left;
VRFieldOfView* fov_right;
if (left_eye && right_eye) {
fov_left = left_eye->FieldOfView();
fov_right = right_eye->FieldOfView();
} else {
DCHECK(!left_eye && !right_eye);
// TODO(offenwanger): Look into making the projection matrixes null instead
// of hard coding values. May break some apps.
fov_left = fov_right = new VRFieldOfView(45, 45, 45, 45);
}
// Build the projection matrices
ProjectionFromFieldOfView(left_projection_matrix_, fov_left, depth_near,
depth_far);
ProjectionFromFieldOfView(right_projection_matrix_, fov_right, depth_near,
depth_far);
// Build the view matrices
MatrixfromRotationTranslation(left_view_matrix_, pose->orientation,
pose->position);
MatrixfromRotationTranslation(right_view_matrix_, pose->orientation,
pose->position);
if (left_eye && right_eye) {
MatrixTranslate(left_view_matrix_, left_eye->offset());
MatrixTranslate(right_view_matrix_, right_eye->offset());
}
if (!MatrixInvert(left_view_matrix_) || !MatrixInvert(right_view_matrix_))
return false;
// Set the pose
pose_->SetPose(pose);
return true;
}
void VRFrameData::Trace(blink::Visitor* visitor) {
visitor->Trace(left_projection_matrix_);
visitor->Trace(left_view_matrix_);
visitor->Trace(right_projection_matrix_);
visitor->Trace(right_view_matrix_);
visitor->Trace(pose_);
ScriptWrappable::Trace(visitor);
}
} // namespace blink