| Index: chrome/browser/android/vr_shell/vr_shell.cc
|
| diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
|
| index a670541121c710c55c52368723a92dd927c202df..6ee80a971117ccac7869df163ec0e59de4c6e91a 100644
|
| --- a/chrome/browser/android/vr_shell/vr_shell.cc
|
| +++ b/chrome/browser/android/vr_shell/vr_shell.cc
|
| @@ -4,66 +4,57 @@
|
|
|
| #include "chrome/browser/android/vr_shell/vr_shell.h"
|
|
|
| +#include <thread>
|
| +
|
| #include "chrome/browser/android/vr_shell/vr_shell_renderer.h"
|
| #include "chrome/browser/android/vr_shell/vr_util.h"
|
| #include "jni/VrShell_jni.h"
|
| #include "ui/gl/gl_bindings.h"
|
| #include "ui/gl/init/gl_factory.h"
|
|
|
| -namespace vr_shell {
|
| +using base::android::JavaParamRef;
|
|
|
| namespace {
|
| // Constant taken from treasure_hunt demo.
|
| -const long kPredictionTimeWithoutVsyncNanos = 50000000;
|
| -
|
| -const float kZNear = 0.1f;
|
| -const float kZFar = 1000.0f;
|
| -
|
| -// Content rect in world coordinates. Height and width are currently supplied
|
| -// as DrawFrame arguments.
|
| -const gvr::Vec3f kContentRectPositionDefault = {0.0f, 0.0f, -1.0f};
|
| +static constexpr long kPredictionTimeWithoutVsyncNanos = 50000000;
|
|
|
| -} // namespace
|
| +static constexpr float kZNear = 0.1f;
|
| +static constexpr float kZFar = 1000.0f;
|
|
|
| -ContentRect::ContentRect() {
|
| - SetIdentity();
|
| -}
|
| +static constexpr gvr::Vec3f kDesktopPositionDefault = {0.0f, 0.0f, -2.0f};
|
| +static constexpr float kDesktopHeightDefault = 1.6f;
|
|
|
| -ContentRect::~ContentRect() {}
|
| +// Screen angle in degrees. 0 = vertical, positive = top closer.
|
| +static constexpr float kDesktopScreenTiltDefault = 0;
|
|
|
| -void ContentRect::SetIdentity() {
|
| - transfrom_to_world.m[0][0] = 1;
|
| - transfrom_to_world.m[0][1] = 0;
|
| - transfrom_to_world.m[0][2] = 0;
|
| - transfrom_to_world.m[0][3] = 0;
|
| - transfrom_to_world.m[1][0] = 0;
|
| - transfrom_to_world.m[1][1] = 1;
|
| - transfrom_to_world.m[1][2] = 0;
|
| - transfrom_to_world.m[1][3] = 0;
|
| - transfrom_to_world.m[2][0] = 0;
|
| - transfrom_to_world.m[2][1] = 0;
|
| - transfrom_to_world.m[2][2] = 1;
|
| - transfrom_to_world.m[2][3] = 0;
|
| - transfrom_to_world.m[3][0] = 0;
|
| - transfrom_to_world.m[3][1] = 0;
|
| - transfrom_to_world.m[3][2] = 0;
|
| - transfrom_to_world.m[3][3] = 1;
|
| -}
|
| +static constexpr float kScreenHeightMeters = 2.0f;
|
| +static constexpr float kScreenWidthMeters = 2.0f;
|
| +} // namespace
|
|
|
| -void ContentRect::Translate(float x, float y, float z) {
|
| - transfrom_to_world.m[0][3] += x;
|
| - transfrom_to_world.m[1][3] += y;
|
| - transfrom_to_world.m[2][3] += z;
|
| -}
|
| +namespace vr_shell {
|
|
|
| -VrShell::VrShell(JNIEnv* env, jobject obj) :
|
| - webvr_mode_(false) {
|
| +VrShell::VrShell(JNIEnv* env, jobject obj)
|
| + : desktop_screen_tilt_(kDesktopScreenTiltDefault),
|
| + desktop_height_(kDesktopHeightDefault),
|
| + desktop_position_(kDesktopPositionDefault) {
|
| j_vr_shell_.Reset(env, obj);
|
| + ui_rects_.emplace_back(new ContentRectangle());
|
| + desktop_plane_ = ui_rects_.back().get();
|
| + desktop_plane_->id = 0;
|
| + desktop_plane_->copy_rect = {0.0f, 0.0f, 1.0f, 1.0f};
|
| + // TODO(cjgrant): If we use the native path for content clicks, fix this.
|
| + desktop_plane_->window_rect = {0, 0, 0, 0};
|
| + desktop_plane_->translation = {0.0f, 0.0f, 0.0f};
|
| + desktop_plane_->x_anchoring = XNONE;
|
| + desktop_plane_->y_anchoring = YNONE;
|
| + desktop_plane_->anchor_z = false;
|
| + desktop_plane_->orientation_axis_angle = {{1.0f, 0.0f, 0.0f, 0.0f}};
|
| + desktop_plane_->rotation_axis_angle = {{0.0f, 0.0f, 0.0f, 0.0f}};
|
| }
|
|
|
| -void VrShell::Destroy(JNIEnv* env,
|
| - const base::android::JavaParamRef<jobject>& obj) {
|
| +void VrShell::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| delete this;
|
| + gl::init::ClearGLBindings();
|
| }
|
|
|
| bool RegisterVrShell(JNIEnv* env) {
|
| @@ -75,7 +66,7 @@ VrShell::~VrShell() {
|
| }
|
|
|
| void VrShell::GvrInit(JNIEnv* env,
|
| - const base::android::JavaParamRef<jobject>& obj,
|
| + const JavaParamRef<jobject>& obj,
|
| jlong native_gvr_api) {
|
| gvr_api_ =
|
| gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(native_gvr_api));
|
| @@ -84,17 +75,20 @@ void VrShell::GvrInit(JNIEnv* env,
|
| }
|
|
|
| void VrShell::InitializeGl(JNIEnv* env,
|
| - const base::android::JavaParamRef<jobject>& obj,
|
| + const JavaParamRef<jobject>& obj,
|
| jint texture_data_handle) {
|
| - gl::init::InitializeGLOneOff();
|
| + CHECK(gl::GetGLImplementation() != gl::kGLImplementationNone ||
|
| + gl::init::InitializeGLOneOff());
|
| +
|
| + content_texture_id_ = texture_data_handle;
|
| gvr_api_->InitializeGl();
|
| std::vector<gvr::BufferSpec> specs;
|
| specs.push_back(gvr_api_->CreateBufferSpec());
|
| render_size_ = specs[0].GetSize();
|
| swap_chain_.reset(new gvr::SwapChain(gvr_api_->CreateSwapchain(specs)));
|
| - content_rect_.reset(new ContentRect());
|
| - content_rect_->content_texture_handle =
|
| - reinterpret_cast<int>(texture_data_handle);
|
| +
|
| + desktop_plane_->content_texture_handle = content_texture_id_;
|
| +
|
| vr_shell_renderer_.reset(new VrShellRenderer());
|
| buffer_viewport_list_.reset(
|
| new gvr::BufferViewportList(gvr_api_->CreateEmptyBufferViewportList()));
|
| @@ -102,9 +96,34 @@ void VrShell::InitializeGl(JNIEnv* env,
|
| new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
|
| }
|
|
|
| -void VrShell::DrawFrame(JNIEnv* env,
|
| - const base::android::JavaParamRef<jobject>& obj) {
|
| +void ApplyNeckModel(gvr::Mat4f& mat_forward) {
|
| + // This assumes that the input matrix is a pure rotation matrix. The
|
| + // input object_from_reference matrix has the inverse rotation of
|
| + // the head rotation. Invert it (this is just a transpose).
|
| + gvr::Mat4f mat = MatrixTranspose(mat_forward);
|
| +
|
| + // Position of the point between the eyes, relative to the neck pivot:
|
| + const float kNeckHorizontalOffset = -0.080f; // meters in Z
|
| + const float kNeckVerticalOffset = 0.075f; // meters in Y
|
| +
|
| + std::array<float, 4> neckOffset = {
|
| + {0.0f, kNeckVerticalOffset, kNeckHorizontalOffset, 1.0f}};
|
| +
|
| + // Rotate eyes around neck pivot point.
|
| + auto offset = MatrixVectorMul(mat, neckOffset);
|
| +
|
| + // Measure new position relative to original center of head, because
|
| + // applying a neck model should not elevate the camera.
|
| + offset[1] -= kNeckVerticalOffset;
|
| +
|
| + // Right-multiply the inverse translation onto the
|
| + // object_from_reference_matrix.
|
| + TranslateMRight(mat_forward, mat_forward, -offset[0], -offset[1], -offset[2]);
|
| +}
|
| +
|
| +void VrShell::DrawFrame(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| buffer_viewport_list_->SetToRecommendedBufferViewports();
|
| +
|
| gvr::Frame frame = swap_chain_->AcquireFrame();
|
| gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
|
| target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
|
| @@ -124,12 +143,30 @@ void VrShell::DrawFrame(JNIEnv* env,
|
| }
|
|
|
| void VrShell::DrawVrShell() {
|
| - // Content area positioning.
|
| - content_rect_->SetIdentity();
|
| - content_rect_->Translate(kContentRectPositionDefault.x,
|
| - kContentRectPositionDefault.y,
|
| - kContentRectPositionDefault.z);
|
| + float screen_width = kScreenWidthMeters * desktop_height_;
|
| + float screen_height = kScreenHeightMeters * desktop_height_;
|
| +
|
| + float screen_tilt = desktop_screen_tilt_ * M_PI / 180.0f;
|
| +
|
| + gvr::Vec3f headPos = getTranslation(head_pose_);
|
| + if (headPos.x == 0.0f && headPos.y == 0.0f && headPos.z == 0.0f) {
|
| + // This appears to be a 3DOF pose without a neck model. Add one.
|
| + // The head pose has redundant data. Assume we're only using the
|
| + // object_from_reference_matrix, we're not updating position_external.
|
| + // TODO: Not sure what object_from_reference_matrix is. The new api removed
|
| + // it. For now, removing it seems working fine.
|
| + ApplyNeckModel(head_pose_);
|
| + }
|
| +
|
| + desktop_plane_->size = {screen_width, screen_height, 1.0f};
|
| + desktop_plane_->translation.x = desktop_position_.x;
|
| + desktop_plane_->translation.y = desktop_position_.y;
|
| + desktop_plane_->translation.z = desktop_position_.z;
|
|
|
| + // Update position of all UI elements (including desktop)
|
| + UpdateTransforms(screen_width, screen_height, screen_tilt);
|
| +
|
| + // Everything should be positioned now, ready for drawing.
|
| gvr::Mat4f left_eye_view_matrix =
|
| MatrixMul(gvr_api_->GetEyeFromHeadMatrix(GVR_LEFT_EYE), head_pose_);
|
| gvr::Mat4f right_eye_view_matrix =
|
| @@ -144,10 +181,6 @@ void VrShell::DrawVrShell() {
|
|
|
| glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
|
|
| - // Enable transparency.
|
| - glEnable(GL_BLEND);
|
| - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
| -
|
| buffer_viewport_list_->GetBufferViewport(GVR_LEFT_EYE,
|
| buffer_viewport_.get());
|
| DrawEye(left_eye_view_matrix, *buffer_viewport_);
|
| @@ -173,16 +206,18 @@ void VrShell::DrawEye(const gvr::Mat4f& view_matrix,
|
| PerspectiveMatrixFromView(params.GetSourceFov(), kZNear, kZFar);
|
|
|
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
| - DrawContentRect();
|
| + DrawUI();
|
| }
|
|
|
| -void VrShell::DrawContentRect() {
|
| - gvr::Mat4f content_rect_combined_matrix =
|
| - MatrixMul(view_matrix_, content_rect_->transfrom_to_world);
|
| - content_rect_combined_matrix =
|
| - MatrixMul(projection_matrix_, content_rect_combined_matrix);
|
| - vr_shell_renderer_->GetTexturedQuadRenderer()->Draw(
|
| - content_rect_->content_texture_handle, content_rect_combined_matrix);
|
| +void VrShell::DrawUI() {
|
| + for (std::size_t i = 0; i < ui_rects_.size(); ++i) {
|
| + gvr::Mat4f combined_matrix =
|
| + MatrixMul(view_matrix_, ui_rects_[i].get()->transform.to_world);
|
| + combined_matrix = MatrixMul(projection_matrix_, combined_matrix);
|
| + vr_shell_renderer_->GetTexturedQuadRenderer()->Draw(
|
| + ui_rects_[i].get()->content_texture_handle, combined_matrix,
|
| + ui_rects_[i].get()->copy_rect);
|
| + }
|
| }
|
|
|
| void VrShell::DrawWebVr() {
|
| @@ -198,20 +233,19 @@ void VrShell::DrawWebVr() {
|
|
|
| glViewport(0, 0, render_size_.width, render_size_.height);
|
| vr_shell_renderer_->GetWebVrRenderer()->Draw(
|
| - reinterpret_cast<int>(content_rect_->content_texture_handle));
|
| + reinterpret_cast<int>(desktop_plane_->content_texture_handle));
|
| }
|
|
|
| -void VrShell::OnPause(JNIEnv* env,
|
| - const base::android::JavaParamRef<jobject>& obj) {
|
| +void VrShell::OnPause(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| if (gvr_api_ == nullptr)
|
| return;
|
| gvr_api_->PauseTracking();
|
| }
|
|
|
| -void VrShell::OnResume(JNIEnv* env,
|
| - const base::android::JavaParamRef<jobject>& obj) {
|
| +void VrShell::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| if (gvr_api_ == nullptr)
|
| return;
|
| +
|
| gvr_api_->RefreshViewerProfile();
|
| gvr_api_->ResumeTracking();
|
| }
|
| @@ -237,13 +271,58 @@ gvr::GvrApi* VrShell::gvr_api() {
|
| return gvr_api_.get();
|
| }
|
|
|
| +void VrShell::UpdateTransforms(float screen_width_meters,
|
| + float screen_height_meters,
|
| + float screen_tilt) {
|
| + for (std::unique_ptr<ContentRectangle>& rect : ui_rects_) {
|
| + rect->transform.MakeIdentity();
|
| + rect->transform.Scale(rect->size.x, rect->size.y, rect->size.z);
|
| + float x_anchor_translate;
|
| + switch (rect->x_anchoring) {
|
| + case XLEFT:
|
| + x_anchor_translate = desktop_position_.x - screen_width_meters * 0.5;
|
| + break;
|
| + case XRIGHT:
|
| + x_anchor_translate = desktop_position_.x + screen_width_meters * 0.5;
|
| + break;
|
| + case XCENTER:
|
| + x_anchor_translate = desktop_position_.x;
|
| + break;
|
| + case XNONE:
|
| + x_anchor_translate = 0;
|
| + break;
|
| + }
|
| + float y_anchor_translate;
|
| + switch (rect->y_anchoring) {
|
| + case YTOP:
|
| + y_anchor_translate = desktop_position_.y + screen_height_meters * 0.5;
|
| + break;
|
| + case YBOTTOM:
|
| + y_anchor_translate = desktop_position_.y - screen_height_meters * 0.5;
|
| + break;
|
| + case YCENTER:
|
| + y_anchor_translate = desktop_position_.y;
|
| + break;
|
| + case YNONE:
|
| + y_anchor_translate = 0;
|
| + break;
|
| + }
|
| + float z_anchor_translate = rect->anchor_z ? desktop_position_.z : 0;
|
| + rect->transform.Translate(x_anchor_translate + rect->translation.x,
|
| + y_anchor_translate + rect->translation.y,
|
| + z_anchor_translate + rect->translation.z);
|
| + // TODO(cjgrant): Establish which exact rotations we'll provide.
|
| + // Adjust for screen tilt.
|
| + rect->transform.Rotate(1.0f, 0.0f, 0.0f, screen_tilt);
|
| + }
|
| +}
|
| +
|
| // ----------------------------------------------------------------------------
|
| // Native JNI methods
|
| // ----------------------------------------------------------------------------
|
|
|
| -jlong Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
|
| - VrShell* vrShell = new VrShell(env, obj);
|
| - return reinterpret_cast<intptr_t>(vrShell);
|
| +jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| + return reinterpret_cast<intptr_t>(new VrShell(env, obj));
|
| }
|
|
|
| } // namespace vr_shell
|
|
|