OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/android/vr_shell/vr_controller.h" |
| 6 |
| 7 #include <android/log.h> |
| 8 |
| 9 #include <cmath> |
| 10 |
| 11 #include "base/logging.h" |
| 12 #include "third_party/WebKit/public/web/WebInputEvent.h" |
| 13 |
| 14 using blink::WebInputEvent; |
| 15 |
| 16 namespace vr_shell { |
| 17 |
| 18 namespace { |
| 19 |
| 20 constexpr float kDisplacementScaleFactor = 800.0f; |
| 21 |
| 22 // A slop represents a small rectangular region around the first touch point of |
| 23 // a gesture. |
| 24 // If the user does not move outside of the slop, no gesture is detected. |
| 25 // Gestures start to be detected when the user moves outside of the slop. |
| 26 // Vertical distance from the border to the center of slop. |
| 27 constexpr float kSlopVertical = 0.165f; |
| 28 |
| 29 // Horizontal distance from the border to the center of slop. |
| 30 constexpr float kSlopHorizontal = 0.125f; |
| 31 |
| 32 // Minimum distance needed in at least one direction to call two vectors |
| 33 // not equal. |
| 34 constexpr float kDelta = 1.0e-7f; |
| 35 |
| 36 // Minimum angle needed to start zoom gesture. |
| 37 constexpr float kMinZoomAngle = 0.25f; |
| 38 |
| 39 class Vector { |
| 40 public: |
| 41 static inline void ClampTouchpadPosition(gvr::Vec2f* position) { |
| 42 position->x = std::min(std::max(0.0f, position->x), 1.0f); |
| 43 position->y = std::min(std::max(0.0f, position->y), 1.0f); |
| 44 } |
| 45 |
| 46 static inline void VectSetZero(gvr::Vec2f* v) { |
| 47 v->x = 0; |
| 48 v->y = 0; |
| 49 } |
| 50 |
| 51 static inline gvr::Vec2f VectSubtract(gvr::Vec2f v1, gvr::Vec2f v2) { |
| 52 gvr::Vec2f result; |
| 53 result.x = v1.x - v2.x; |
| 54 result.y = v1.y - v2.y; |
| 55 return result; |
| 56 } |
| 57 |
| 58 static inline bool VectEqual(const gvr::Vec2f v1, const gvr::Vec2f v2) { |
| 59 return (std::abs(v1.x - v2.x) < kDelta) && (std::abs(v1.y - v2.y) < kDelta); |
| 60 } |
| 61 }; // Vector |
| 62 |
| 63 } // namespace |
| 64 |
| 65 VrController::VrController(gvr_context* vr_context) { |
| 66 Initialize(vr_context); |
| 67 Reset(); |
| 68 } |
| 69 |
| 70 VrController::~VrController() {} |
| 71 |
| 72 void VrController::OnResume() { |
| 73 if (controller_api_) |
| 74 controller_api_->Resume(); |
| 75 } |
| 76 |
| 77 void VrController::OnPause() { |
| 78 if (controller_api_) |
| 79 controller_api_->Pause(); |
| 80 } |
| 81 |
| 82 bool VrController::IsTouching() { |
| 83 return controller_state_.IsTouching(); |
| 84 } |
| 85 |
| 86 float VrController::TouchPosX() { |
| 87 return controller_state_.GetTouchPos().x; |
| 88 } |
| 89 |
| 90 float VrController::TouchPosY() { |
| 91 return controller_state_.GetTouchPos().y; |
| 92 } |
| 93 |
| 94 const gvr::Quatf VrController::Orientation() { |
| 95 return controller_state_.GetOrientation(); |
| 96 } |
| 97 |
| 98 bool VrController::IsTouchDown() { |
| 99 return controller_state_.GetTouchDown(); |
| 100 } |
| 101 |
| 102 bool VrController::IsTouchUp() { |
| 103 return controller_state_.GetTouchUp(); |
| 104 } |
| 105 |
| 106 bool VrController::IsButtonDown(const int32_t button) { |
| 107 return controller_state_.GetButtonDown(button); |
| 108 } |
| 109 |
| 110 bool VrController::IsButtonUp(const int32_t button) { |
| 111 return controller_state_.GetButtonUp(button); |
| 112 } |
| 113 |
| 114 bool VrController::IsConnected() { |
| 115 return controller_state_.GetConnectionState() == gvr::kControllerConnected; |
| 116 } |
| 117 |
| 118 void VrController::UpdateState() { |
| 119 const int32_t old_status = controller_state_.GetApiStatus(); |
| 120 const int32_t old_connection_state = controller_state_.GetConnectionState(); |
| 121 // Read current controller state. |
| 122 controller_state_.Update(*controller_api_); |
| 123 // Print new API status and connection state, if they changed. |
| 124 if (controller_state_.GetApiStatus() != old_status || |
| 125 controller_state_.GetConnectionState() != old_connection_state) { |
| 126 VLOG(1) << "Controller Connection status: " |
| 127 << gvr_controller_connection_state_to_string( |
| 128 controller_state_.GetConnectionState()); |
| 129 } |
| 130 } |
| 131 |
| 132 void VrController::Update(bool touch_up, |
| 133 bool touch_down, |
| 134 bool is_touching, |
| 135 const gvr::Vec2f position, |
| 136 int64_t timestamp) { |
| 137 CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly."; |
| 138 touch_info_->touch_up = touch_up; |
| 139 touch_info_->touch_down = touch_down; |
| 140 touch_info_->is_touching = is_touching; |
| 141 touch_info_->touch_point.position = position; |
| 142 Vector::ClampTouchpadPosition(&touch_info_->touch_point.position); |
| 143 touch_info_->touch_point.timestamp = timestamp; |
| 144 |
| 145 UpdateGestureFromTouchInfo(); |
| 146 } |
| 147 |
| 148 void VrController::Initialize(gvr_context* gvr_context) { |
| 149 CHECK(gvr_context != nullptr) << "invalid gvr_context"; |
| 150 controller_api_.reset(new gvr::ControllerApi); |
| 151 int32_t options = gvr::ControllerApi::DefaultOptions(); |
| 152 |
| 153 // Enable non-default options, if you need them: |
| 154 // options |= GVR_CONTROLLER_ENABLE_GYRO; |
| 155 CHECK(controller_api_->Init(options, gvr_context)); |
| 156 controller_api_->Resume(); |
| 157 } |
| 158 |
| 159 VrGesture VrController::DetectGesture() { |
| 160 if (controller_state_.GetConnectionState() == gvr::kControllerConnected) { |
| 161 gvr::Vec2f position; |
| 162 position.x = TouchPosX(); |
| 163 position.y = TouchPosY(); |
| 164 Update(IsTouchUp(), IsTouchDown(), IsTouching(), position, |
| 165 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos); |
| 166 if (GetGestureListSize() > 0 && |
| 167 GetGesturePtr(0)->type == kGestureTypeScroll) { |
| 168 switch (GetGesturePtr(0)->details.scroll.state) { |
| 169 case WebInputEvent::GestureScrollBegin: |
| 170 return VrGesture( |
| 171 kGestureScroll, |
| 172 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 173 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 174 GetGesturePtr(0)->displacement.x * kDisplacementScaleFactor, |
| 175 GetGesturePtr(0)->displacement.y * kDisplacementScaleFactor, |
| 176 WebInputEvent::GestureScrollBegin, Orientation()); |
| 177 case WebInputEvent::GestureScrollUpdate: |
| 178 return VrGesture( |
| 179 kGestureScroll, |
| 180 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 181 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 182 GetGesturePtr(0)->displacement.x * kDisplacementScaleFactor, |
| 183 GetGesturePtr(0)->displacement.y * kDisplacementScaleFactor, |
| 184 WebInputEvent::GestureScrollUpdate, Orientation()); |
| 185 case WebInputEvent::GestureScrollEnd: |
| 186 return VrGesture( |
| 187 kGestureScroll, |
| 188 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 189 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 190 GetGesturePtr(0)->displacement.x * kDisplacementScaleFactor, |
| 191 GetGesturePtr(0)->displacement.y * kDisplacementScaleFactor, |
| 192 WebInputEvent::GestureScrollEnd, Orientation()); |
| 193 } |
| 194 } |
| 195 |
| 196 if (IsButtonDown(gvr::kControllerButtonClick)) { |
| 197 return VrGesture( |
| 198 kGestureButtonsChange, |
| 199 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 200 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, 1, 0, |
| 201 Orientation()); |
| 202 } |
| 203 float dqx = 0.0f; |
| 204 |
| 205 dqx = last_qx_ - Orientation().qx; |
| 206 |
| 207 // Don't accept rapid moves |
| 208 if (dqx < -1.0f || dqx > 1.0f) |
| 209 dqx = 0.0f; |
| 210 last_qx_ = Orientation().qx; |
| 211 |
| 212 if (IsButtonDown(gvr::kControllerButtonApp)) |
| 213 zoom_in_progress_ = true; |
| 214 if (IsButtonUp(gvr::kControllerButtonApp)) { |
| 215 zoom_in_progress_ = false; |
| 216 if (pinch_started_) { |
| 217 pinch_started_ = false; |
| 218 } |
| 219 } |
| 220 if (zoom_in_progress_) { |
| 221 if (dqx != 0.0f) { |
| 222 // dz == 1 means no zoom. dz < 1 means zoom-out and dz > 1 means |
| 223 // zoom-in. |
| 224 // dqx * 2 + 1 results to dz \in [0,2] |
| 225 return VrGesture( |
| 226 kGestureZoom, |
| 227 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 228 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 229 dqx * 2 + 1, Orientation()); |
| 230 } |
| 231 if (Orientation().qz < kMinZoomAngle && |
| 232 Orientation().qz > -1 * kMinZoomAngle && pinch_started_) { |
| 233 pinch_started_ = false; |
| 234 } |
| 235 } |
| 236 return VrGesture(kGestureAngularMove, |
| 237 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 238 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos, |
| 239 Orientation()); |
| 240 } |
| 241 return VrGesture(); |
| 242 } |
| 243 |
| 244 void VrController::UpdateGestureFromTouchInfo() { |
| 245 // Clear the gesture list. |
| 246 gesture_list_.clear(); |
| 247 |
| 248 switch (state_) { |
| 249 // User has not put finger on touch pad. |
| 250 case WAITING: |
| 251 HandleWaitingState(); |
| 252 break; |
| 253 // User has not started a gesture (by moving out of slop). |
| 254 case TOUCHING: |
| 255 HandleDetectingState(); |
| 256 break; |
| 257 // User is scrolling on touchpad |
| 258 case SCROLLING: |
| 259 HandleScrollingState(); |
| 260 break; |
| 261 default: |
| 262 LOG(ERROR) << "Wrong gesture detector state: " << state_; |
| 263 break; |
| 264 } |
| 265 } |
| 266 |
| 267 const VrGesture* VrController::GetGesturePtr(const size_t index) { |
| 268 CHECK(index < gesture_list_.size()) << "The gesture index exceeds the" |
| 269 "size of gesture list."; |
| 270 return const_cast<VrGesture*>(&gesture_list_[index]); |
| 271 } |
| 272 |
| 273 void VrController::Update(const gvr_controller_state* controller_state) { |
| 274 // Update touch information. |
| 275 CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly."; |
| 276 touch_info_->touch_up = gvr_controller_state_get_touch_up(controller_state); |
| 277 touch_info_->touch_down = |
| 278 gvr_controller_state_get_touch_down(controller_state); |
| 279 touch_info_->is_touching = gvr_controller_state_is_touching(controller_state); |
| 280 touch_info_->touch_point.position.x = |
| 281 gvr_controller_state_get_touch_pos(controller_state).x; |
| 282 touch_info_->touch_point.position.y = |
| 283 gvr_controller_state_get_touch_pos(controller_state).y; |
| 284 Vector::ClampTouchpadPosition(&(touch_info_->touch_point.position)); |
| 285 touch_info_->touch_point.timestamp = |
| 286 gvr_controller_state_get_last_touch_timestamp(controller_state); |
| 287 |
| 288 UpdateGestureFromTouchInfo(); |
| 289 } |
| 290 |
| 291 void VrController::HandleWaitingState() { |
| 292 // User puts finger on touch pad (or when the touch down for current gesture |
| 293 // is missed, initiate gesture from current touch point). |
| 294 if (touch_info_->touch_down || touch_info_->is_touching) { |
| 295 // update initial touchpoint |
| 296 *init_touch_point_ = touch_info_->touch_point; |
| 297 // update current touchpoint |
| 298 *cur_touch_point_ = touch_info_->touch_point; |
| 299 state_ = TOUCHING; |
| 300 } |
| 301 } |
| 302 |
| 303 void VrController::HandleDetectingState() { |
| 304 // User lifts up finger from touch pad. |
| 305 if (touch_info_->touch_up || !(touch_info_->is_touching)) { |
| 306 Reset(); |
| 307 return; |
| 308 } |
| 309 |
| 310 // Touch position is changed and the touch point moves outside of slop. |
| 311 if (UpdateCurrentTouchpoint() && touch_info_->is_touching && |
| 312 !InSlop(touch_info_->touch_point.position)) { |
| 313 state_ = SCROLLING; |
| 314 VrGesture gesture; |
| 315 gesture.type = kGestureTypeScroll; |
| 316 gesture.details.scroll.state = WebInputEvent::GestureScrollBegin; |
| 317 UpdateGesture(&gesture); |
| 318 gesture_list_.push_back(gesture); |
| 319 } |
| 320 } |
| 321 |
| 322 void VrController::HandleScrollingState() { |
| 323 // Update current touch point. |
| 324 bool touch_position_changed = UpdateCurrentTouchpoint(); |
| 325 if (touch_info_->touch_up || !(touch_info_->is_touching)) { // gesture ends |
| 326 VrGesture scroll_end; |
| 327 scroll_end.type = kGestureTypeScroll; |
| 328 scroll_end.details.scroll.state = WebInputEvent::GestureScrollEnd; |
| 329 UpdateGesture(&scroll_end); |
| 330 gesture_list_.push_back(scroll_end); |
| 331 |
| 332 Reset(); |
| 333 } else if (touch_position_changed) { // User continues scrolling and there is |
| 334 // a change in touch position. |
| 335 VrGesture scroll_update; |
| 336 scroll_update.type = kGestureTypeScroll; |
| 337 scroll_update.details.scroll.state = WebInputEvent::GestureScrollUpdate; |
| 338 UpdateGesture(&scroll_update); |
| 339 gesture_list_.push_back(scroll_update); |
| 340 } |
| 341 } |
| 342 |
| 343 bool VrController::InSlop(const gvr::Vec2f touch_position) { |
| 344 return (std::abs(touch_position.x - init_touch_point_->position.x) < |
| 345 kSlopHorizontal) && |
| 346 (std::abs(touch_position.y - init_touch_point_->position.y) < |
| 347 kSlopVertical); |
| 348 } |
| 349 |
| 350 void VrController::Reset() { |
| 351 // Reset state. |
| 352 state_ = WAITING; |
| 353 |
| 354 // Reset the pointers. |
| 355 prev_touch_point_.reset(new TouchPoint); |
| 356 cur_touch_point_.reset(new TouchPoint); |
| 357 init_touch_point_.reset(new TouchPoint); |
| 358 touch_info_.reset(new TouchInfo); |
| 359 Vector::VectSetZero(&overall_velocity_); |
| 360 } |
| 361 |
| 362 void VrController::UpdateGesture(VrGesture* gesture) { |
| 363 if (!gesture) |
| 364 LOG(ERROR) << "The gesture pointer is not initiated properly."; |
| 365 gesture->velocity = overall_velocity_; |
| 366 gesture->displacement = Vector::VectSubtract(cur_touch_point_->position, |
| 367 prev_touch_point_->position); |
| 368 } |
| 369 |
| 370 bool VrController::UpdateCurrentTouchpoint() { |
| 371 if (touch_info_->is_touching || touch_info_->touch_up) { |
| 372 // Update the touch point when the touch position has changed. |
| 373 if (!Vector::VectEqual(cur_touch_point_->position, |
| 374 touch_info_->touch_point.position)) { |
| 375 prev_touch_point_.swap(cur_touch_point_); |
| 376 *cur_touch_point_ = touch_info_->touch_point; |
| 377 |
| 378 return true; |
| 379 } |
| 380 } |
| 381 return false; |
| 382 } |
| 383 |
| 384 } // namespace vr_shell |
OLD | NEW |