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