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