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) { | |
tdresser
2016/09/23 20:36:44
We can't modify Vec2f? Where is it defined?
asimjour
2016/09/30 13:37:43
We can't. It's in gvr library (third_party/gvr-and
| |
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 |