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