Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(750)

Unified Diff: chrome/browser/android/vr_shell/vr_controller.cc

Issue 2350253004: Controller support for VrShell (Closed)
Patch Set: Controller support for VrShell Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/android/vr_shell/vr_controller.cc
diff --git a/chrome/browser/android/vr_shell/vr_controller.cc b/chrome/browser/android/vr_shell/vr_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ed69f4f4ad9a20894816e1fd547a10a5ad31b917
--- /dev/null
+++ b/chrome/browser/android/vr_shell/vr_controller.cc
@@ -0,0 +1,380 @@
+// 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 "chrome/browser/android/vr_shell/vr_controller.h"
+
+#include <android/log.h>
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+using blink::WebInputEvent;
+
+namespace vr_shell {
+
+namespace {
+constexpr float kDisplacementScaleFactor = 800.0f;
+
+// A slop represents a small rectangular region around the first touch point of
+// a gesture.
+// If the user does not move outside of the slop, no gesture is detected.
+// Gestures start to be detected when the user moves outside of the slop.
+// Vertical distance from the border to the center of slop.
+constexpr float kSlopVertical = 0.165f;
+
+// Horizontal distance from the border to the center of slop.
+constexpr float kSlopHorizontal = 0.125f;
+
+// Minimum distance needed in at least one direction to call two vectors
+// not equal.
+constexpr float kDelta = 1.0e-7f;
+
+// Minimum angle needed to start zoom gesture.
+constexpr float kMinZoomAngle = 0.25f;
+
+inline void ClampTouchpadPosition(gvr::Vec2f* position) {
+ position->x = std::min(std::max(0.0f, position->x), 1.0f);
+ position->y = std::min(std::max(0.0f, position->y), 1.0f);
+}
+
+inline void VectSetZero(gvr::Vec2f* v) {
+ v->x = 0;
+ v->y = 0;
+}
+
+inline gvr::Vec2f VectSubtract(gvr::Vec2f v1, gvr::Vec2f v2) {
+ gvr::Vec2f result;
+ result.x = v1.x - v2.x;
+ result.y = v1.y - v2.y;
+ return result;
+}
+
+inline bool VectEqual(const gvr::Vec2f v1, const gvr::Vec2f v2) {
+ return (std::abs(v1.x - v2.x) < kDelta) && (std::abs(v1.y - v2.y) < kDelta);
+}
tdresser 2016/09/23 17:45:17 Can these be methods on the vector class?
asimjour 2016/09/23 19:56:31 Done.
+
+} // namespace
+
+VrController::VrController(gvr_context* vr_context) {
+ Initialize(vr_context);
+ Reset();
+}
+
+VrController::~VrController() {}
+
+void VrController::OnResume() {
+ if (controller_api_)
+ controller_api_->Resume();
+}
+
+void VrController::OnPause() {
+ if (controller_api_)
+ controller_api_->Pause();
+}
+
+bool VrController::IsTouching() {
+ return controller_state_.IsTouching();
+}
+
+float VrController::TouchPosX() {
+ return controller_state_.GetTouchPos().x;
+}
+
+float VrController::TouchPosY() {
+ return controller_state_.GetTouchPos().y;
+}
+
+const gvr::Quatf VrController::Orientation() {
+ return controller_state_.GetOrientation();
+}
+
+bool VrController::IsTouchDown() {
+ return controller_state_.GetTouchDown();
+}
+
+bool VrController::IsTouchUp() {
+ return controller_state_.GetTouchUp();
+}
+
+bool VrController::IsButtonDown(const int32_t button) {
+ return controller_state_.GetButtonDown(button);
+}
+
+bool VrController::IsButtonUp(const int32_t button) {
+ return controller_state_.GetButtonUp(button);
+}
+
+bool VrController::IsConnected() {
+ return controller_state_.GetConnectionState() == gvr::kControllerConnected;
+}
+
+void VrController::UpdateState() {
+ const int32_t old_status = controller_state_.GetApiStatus();
+ const int32_t old_connection_state = controller_state_.GetConnectionState();
+ // Read current controller state.
+ controller_state_.Update(*controller_api_);
+ // Print new API status and connection state, if they changed.
+ if (controller_state_.GetApiStatus() != old_status ||
+ controller_state_.GetConnectionState() != old_connection_state) {
+ VLOG(1) << "Controller Connection status: "
+ << gvr_controller_connection_state_to_string(
+ controller_state_.GetConnectionState());
+ }
+}
+
+void VrController::Update(bool touch_up,
+ bool touch_down,
+ bool is_touching,
+ const gvr::Vec2f position,
+ int64_t timestamp) {
+ CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly.";
+ touch_info_->touch_up = touch_up;
+ touch_info_->touch_down = touch_down;
+ touch_info_->is_touching = is_touching;
+ touch_info_->touch_point.position = position;
+ ClampTouchpadPosition(&touch_info_->touch_point.position);
+ touch_info_->touch_point.timestamp = timestamp;
+
+ UpdateGestureFromTouchInfo();
+}
+
+void VrController::Initialize(gvr_context* gvr_context) {
+ CHECK(gvr_context != nullptr) << "invalid gvr_context";
+ controller_api_.reset(new gvr::ControllerApi);
+ int32_t options = gvr::ControllerApi::DefaultOptions();
+
+ // Enable non-default options, if you need them:
+ // options |= GVR_CONTROLLER_ENABLE_GYRO;
+ CHECK(controller_api_->Init(options, gvr_context));
+ controller_api_->Resume();
+}
+
+VrGesture VrController::DetectGesture() {
+ if (controller_state_.GetConnectionState() == gvr::kControllerConnected) {
+ gvr::Vec2f position;
+ position.x = TouchPosX();
+ position.y = TouchPosY();
+ Update(IsTouchUp(), IsTouchDown(), IsTouching(), position,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos);
+ if (GetGestureListSize() > 0 &&
+ GetGesturePtr(0)->type == kGestureTypeScroll) {
+ switch (GetGesturePtr(0)->details.scroll.state) {
+ case WebInputEvent::GestureScrollBegin:
+ return VrGesture(
+ kGestureScroll,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ GetGesturePtr(0)->displacement.x * kDisplacementScaleFactor,
+ GetGesturePtr(0)->displacement.y * kDisplacementScaleFactor,
+ WebInputEvent::GestureScrollBegin, Orientation());
+ case WebInputEvent::GestureScrollUpdate:
+ return VrGesture(
+ kGestureScroll,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ GetGesturePtr(0)->displacement.x * kDisplacementScaleFactor,
+ GetGesturePtr(0)->displacement.y * kDisplacementScaleFactor,
+ WebInputEvent::GestureScrollUpdate, Orientation());
+ case WebInputEvent::GestureScrollEnd:
+ return VrGesture(
+ kGestureScroll,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ GetGesturePtr(0)->displacement.x * kDisplacementScaleFactor,
+ GetGesturePtr(0)->displacement.y * kDisplacementScaleFactor,
+ WebInputEvent::GestureScrollEnd, Orientation());
+ }
+ }
+
+ if (IsButtonDown(gvr::kControllerButtonClick)) {
+ return VrGesture(
+ kGestureButtonsChange,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, 1, 0,
+ Orientation());
+ }
+ float dqx = 0.0f;
+
+ dqx = last_qx_ - Orientation().qx;
+
+ // don't accept rapid moves
+ if (dqx < -1.0f || dqx > 1.0f)
+ dqx = 0.0f;
+ last_qx_ = Orientation().qx;
+
+ if (IsButtonDown(gvr::kControllerButtonApp))
+ zoom_in_progress_ = true;
+ if (IsButtonUp(gvr::kControllerButtonApp)) {
+ zoom_in_progress_ = false;
+ if (pinch_started_) {
+ pinch_started_ = false;
+ }
+ }
+ if (zoom_in_progress_) {
+ if (dqx != 0.0f) {
+ // dz == 1 means no zoom. dz < 1 means zoom-out and dz > 1 means
+ // zoom-in.
+ // dqx * 2 + 1 results to dz \in [0,2]
+ return VrGesture(
+ kGestureZoom,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ dqx * 2 + 1, Orientation());
+ }
+ if (Orientation().qz < kMinZoomAngle &&
+ Orientation().qz > -1 * kMinZoomAngle && pinch_started_) {
+ pinch_started_ = false;
+ }
+ }
+ return VrGesture(kGestureAngularMove,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos,
+ Orientation());
+ }
+ return VrGesture();
+}
+
+void VrController::UpdateGestureFromTouchInfo() {
+ // Clear the gesture list.
+ gesture_list_.clear();
+
+ switch (state_) {
+ // user has not put finger on touch pad.
+ case WAITING:
+ HandleWaitingState();
+ break;
+ // user has not started a gesture (by moving out of slop).
+ case TOUCHING:
+ HandleDetectingState();
+ break;
+ // user is scrolling on touchpad
+ case SCROLLING:
+ HandleScrollingState();
+ break;
+ default:
+ LOG(ERROR) << "Wrong gesture detector state: " << state_;
+ break;
+ }
+}
+
+const VrGesture* VrController::GetGesturePtr(const size_t index) {
+ CHECK(index < gesture_list_.size()) << "The gesture index exceeds the"
+ "size of gesture list.";
+ return const_cast<VrGesture*>(&gesture_list_[index]);
+}
+
+void VrController::Update(const gvr_controller_state* controller_state) {
+ // Update touch information.
+ CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly.";
+ touch_info_->touch_up = gvr_controller_state_get_touch_up(controller_state);
+ touch_info_->touch_down =
+ gvr_controller_state_get_touch_down(controller_state);
+ touch_info_->is_touching = gvr_controller_state_is_touching(controller_state);
+ touch_info_->touch_point.position.x =
+ gvr_controller_state_get_touch_pos(controller_state).x;
+ touch_info_->touch_point.position.y =
+ gvr_controller_state_get_touch_pos(controller_state).y;
+ ClampTouchpadPosition(&(touch_info_->touch_point.position));
+ touch_info_->touch_point.timestamp =
+ gvr_controller_state_get_last_touch_timestamp(controller_state);
+
+ UpdateGestureFromTouchInfo();
+}
+
+void VrController::HandleWaitingState() {
+ // User puts finger on touch pad (or when the touch down for current gesture
+ // is missed, initiate gesture from current touch point).
+ if (touch_info_->touch_down || touch_info_->is_touching) {
+ // update initial touchpoint
+ *init_touch_point_ = touch_info_->touch_point;
+ // update current touchpoint
+ *cur_touch_point_ = touch_info_->touch_point;
+ state_ = TOUCHING;
+ }
+}
+
+void VrController::HandleDetectingState() {
+ // User lifts up finger from touch pad.
+ if (touch_info_->touch_up || !(touch_info_->is_touching)) {
+ Reset();
+ return;
+ }
+
+ // Touch position is changed and the touch point moves outside of slop.
+ if (UpdateCurrentTouchpoint() && touch_info_->is_touching &&
+ !InSlop(touch_info_->touch_point.position)) {
+ state_ = SCROLLING;
+ VrGesture gesture;
+ gesture.type = kGestureTypeScroll;
+ gesture.details.scroll.state = WebInputEvent::GestureScrollBegin;
+ UpdateGesture(&gesture);
+ gesture_list_.push_back(gesture);
+ }
+}
+
+void VrController::HandleScrollingState() {
+ // Update current touch point.
+ bool touch_position_changed = UpdateCurrentTouchpoint();
+ if (touch_info_->touch_up || !(touch_info_->is_touching)) { // gesture ends
+ VrGesture scroll_end;
+ scroll_end.type = kGestureTypeScroll;
+ scroll_end.details.scroll.state = WebInputEvent::GestureScrollEnd;
+ UpdateGesture(&scroll_end);
+ gesture_list_.push_back(scroll_end);
+
+ Reset();
+ } else if (touch_position_changed) { // User continues scrolling and there is
+ // a change in touch position.
+ VrGesture scroll_update;
+ scroll_update.type = kGestureTypeScroll;
+ scroll_update.details.scroll.state = WebInputEvent::GestureScrollUpdate;
+ UpdateGesture(&scroll_update);
+ gesture_list_.push_back(scroll_update);
+ }
+}
+
+bool VrController::InSlop(const gvr::Vec2f touch_position) {
+ return (std::abs(touch_position.x - init_touch_point_->position.x) <
+ kSlopHorizontal) &&
+ (std::abs(touch_position.y - init_touch_point_->position.y) <
+ kSlopVertical);
+}
+
+void VrController::Reset() {
+ // Reset state.
+ state_ = WAITING;
+
+ // Reset the pointers.
+ prev_touch_point_.reset(new TouchPoint);
+ cur_touch_point_.reset(new TouchPoint);
+ init_touch_point_.reset(new TouchPoint);
+ touch_info_.reset(new TouchInfo);
+ VectSetZero(&overall_velocity_);
+}
+
+void VrController::UpdateGesture(VrGesture* gesture) {
+ if (!gesture)
+ LOG(ERROR) << "The gesture pointer is not initiated properly.";
+ gesture->velocity = overall_velocity_;
+ gesture->displacement =
+ VectSubtract(cur_touch_point_->position, prev_touch_point_->position);
+}
+
+bool VrController::UpdateCurrentTouchpoint() {
+ if (touch_info_->is_touching || touch_info_->touch_up) {
+ // Update the touch point when the touch position has changed.
+ if (!VectEqual(cur_touch_point_->position,
+ touch_info_->touch_point.position)) {
+ prev_touch_point_.swap(cur_touch_point_);
+ *cur_touch_point_ = touch_info_->touch_point;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace vr_shell

Powered by Google App Engine
This is Rietveld 408576698