Chromium Code Reviews| 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 cbc58091fe6421241007f0f10fd073949453836f..61fef6a8944c251ee8a5767006da88e35c586f6b 100644 |
| --- a/chrome/browser/android/vr_shell/vr_shell.cc |
| +++ b/chrome/browser/android/vr_shell/vr_shell.cc |
| @@ -4,65 +4,40 @@ |
| #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 { |
| - |
| -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}; |
| - |
| -} // namespace |
| - |
| -ContentRect::ContentRect() { |
| - SetIdentity(); |
| -} |
| +using base::android::JavaParamRef; |
| -ContentRect::~ContentRect() {} |
| - |
| -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; |
| -} |
| +namespace vr_shell { |
| -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; |
| -} |
| +constexpr gvr::Vec3f VrShell::kDesktopPositionDefault; |
| VrShell::VrShell(JNIEnv* env, jobject obj) { |
| j_vr_shell_.Reset(env, obj); |
| + ui_rects_.emplace_back(new ContentRectangle()); |
| + desktop_plane_ = ui_rects_.back().get(); |
| + desktop_plane_->id = 0; |
| + desktop_plane_->copyRect = {0.0f, 0.0f, 1.0f, 1.0f}; |
| + // TODO(cjgrant): If we use the native path for content clicks, fix this. |
| + desktop_plane_->windowRect = {0, 0, 0, 0}; |
| + desktop_plane_->translation = {0.0f, 0.0f, 0.0f}; |
| + desktop_plane_->xAnchoring = XNONE; |
| + desktop_plane_->yAnchoring = YNONE; |
| + desktop_plane_->anchorZ = false; |
| + desktop_plane_->orientationAxisAngle = {{1.0f, 0.0f, 0.0f, 0.0f}}; |
| + desktop_plane_->rotationAxisAngle = {{0.0f, 0.0f, 0.0f, 0.0f}}; |
| } |
| void VrShell::Destroy(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| delete this; |
| + gl::init::ClearGLBindings(); |
| } |
| bool RegisterVrShell(JNIEnv* env) { |
| @@ -72,24 +47,27 @@ bool RegisterVrShell(JNIEnv* env) { |
| 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)); |
| } |
| void VrShell::InitializeGl(JNIEnv* env, |
| - const base::android::JavaParamRef<jobject>& obj, |
| - jint texture_data_handle) { |
| - gl::init::InitializeGLOneOff(); |
| + const JavaParamRef<jobject>& obj, |
| + jint textureDataHandle) { |
| + CHECK(gl::GetGLImplementation() != gl::kGLImplementationNone || |
| + gl::init::InitializeGLOneOff()); |
| + |
| + content_texture_id_ = textureDataHandle; |
| 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_->contentTextureHandle = content_texture_id_; |
| + |
| vr_shell_renderer_.reset(new VrShellRenderer()); |
| buffer_viewport_list_.reset( |
| new gvr::BufferViewportList(gvr_api_->CreateEmptyBufferViewportList())); |
| @@ -97,20 +75,67 @@ void VrShell::InitializeGl(JNIEnv* env, |
| new gvr::BufferViewport(gvr_api_->CreateBufferViewport())); |
| } |
| +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 base::android::JavaParamRef<jobject>& obj) { |
| + const JavaParamRef<jobject>& obj) { |
| + // Java passes in a screen height of 1.0 with a width based on the configured |
| + // aspect ratio. The overall size is handled here, scale to match the user's |
| + // preferred overall screen height while maintaining the aspect ratio. |
|
amp
2016/09/01 20:50:41
Could be just me, but I don't understand this comm
mthiesse
2016/09/02 01:05:33
More stale comments, sorry.
|
| + float screen_width = kScreenWidthMeters * desktop_height_; |
| + float screen_height = kScreenHeightMeters * desktop_height_; |
| + |
| + float screenTilt = desktop_screen_tilt_ * M_PI / 180.0f; |
| + |
| buffer_viewport_list_->SetToRecommendedBufferViewports(); |
| + |
| gvr::Frame frame = swap_chain_->AcquireFrame(); |
| gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow(); |
| target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos; |
| head_pose_ = gvr_api_->GetHeadPoseInStartSpace(target_time); |
| - // Content area positioning. |
| - content_rect_->SetIdentity(); |
| - content_rect_->Translate(kContentRectPositionDefault.x, |
| - kContentRectPositionDefault.y, |
| - kContentRectPositionDefault.z); |
| - |
| + 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_width, screenTilt); |
|
amp
2016/09/01 20:50:40
Are the first two params both supposed to be width
mthiesse
2016/09/02 01:05:33
Good catch, that was a typo.
|
| + |
| + // 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 = |
| @@ -128,10 +153,6 @@ void VrShell::DrawFrame(JNIEnv* env, |
| 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_); |
| @@ -160,40 +181,89 @@ 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 combinedMatrix = |
| + MatrixMul(view_matrix_, ui_rects_[i].get()->mTransform.mToWorld); |
| + combinedMatrix = MatrixMul(projection_matrix_, combinedMatrix); |
| + vr_shell_renderer_->GetTexturedQuadRenderer()->Draw( |
| + ui_rects_[i].get()->contentTextureHandle, combinedMatrix, |
| + ui_rects_[i].get()->copyRect); |
| + } |
| } |
| -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(); |
| + // Refreshing the viewer profile accesses disk, so we do it off main thread |
| + // to avoid strict mode errors. |
| + std::thread t([this] { gvr_api_->RefreshViewerProfile(); }); |
| + t.join(); |
| gvr_api_->ResumeTracking(); |
| } |
| +void VrShell::UpdateTransforms(float screenWidthMeters, |
| + float screenHeightMeters, |
| + float screenTilt) { |
| + // TODO(mthiesse): Thread safety. This is not thread safe. |
| + for (std::unique_ptr<ContentRectangle>& rect : ui_rects_) { |
| + rect->mTransform.SetIdentity(); |
| + rect->mTransform.Scale(rect->size.x, rect->size.y, rect->size.z); |
| + float xAnchorTranslate; |
| + switch (rect->xAnchoring) { |
| + case XLEFT: |
| + xAnchorTranslate = desktop_position_.x - screenWidthMeters * 0.5; |
| + break; |
| + case XRIGHT: |
| + xAnchorTranslate = desktop_position_.x + screenWidthMeters * 0.5; |
| + break; |
| + case XCENTER: |
| + xAnchorTranslate = desktop_position_.x; |
| + break; |
| + case XNONE: |
| + xAnchorTranslate = 0; |
| + break; |
| + } |
| + float yAnchorTranslate; |
| + switch (rect->yAnchoring) { |
| + case YTOP: |
| + yAnchorTranslate = desktop_position_.y + screenHeightMeters * 0.5; |
| + break; |
| + case YBOTTOM: |
| + yAnchorTranslate = desktop_position_.y - screenHeightMeters * 0.5; |
| + break; |
| + case YCENTER: |
| + yAnchorTranslate = desktop_position_.y; |
| + break; |
| + case YNONE: |
| + yAnchorTranslate = 0; |
| + break; |
| + } |
| + float zAnchorTranslate = rect->anchorZ ? desktop_position_.z : 0; |
| + rect->mTransform.Translate(xAnchorTranslate + rect->translation.x, |
| + yAnchorTranslate + rect->translation.y, |
| + zAnchorTranslate + rect->translation.z); |
| + // TODO(cjgrant): Establish which exact rotations we'll provide. |
| + // Adjust for screen tilt. |
| + rect->mTransform.Rotate(1.0f, 0.0f, 0.0f, screenTilt); |
| + } |
| +} |
| + |
| // ---------------------------------------------------------------------------- |
| // 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 |