Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/android/vr_shell/vr_controller.h" | 5 #include "chrome/browser/android/vr_shell/vr_controller.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/time/time.h" | 10 #include "base/time/time.h" |
| 11 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/ gvr.h" | 11 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/ gvr.h" |
| 12 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/ gvr_controller.h" | 12 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/ gvr_controller.h" |
| 13 | 13 |
| 14 namespace vr_shell { | 14 namespace vr_shell { |
| 15 | 15 |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 constexpr float kDisplacementScaleFactor = 300.0f; | 18 constexpr float kDisplacementScaleFactor = 300.0f; |
| 19 | 19 |
| 20 // A slop represents a small rectangular region around the first touch point of | 20 // A slop represents a small rectangular region around the first touch point of |
| 21 // a gesture. | 21 // a gesture. |
| 22 // If the user does not move outside of the slop, no gesture is detected. | 22 // If the user does not move outside of the slop, no gesture is detected. |
| 23 // Gestures start to be detected when the user moves outside of the slop. | 23 // Gestures start to be detected when the user moves outside of the slop. |
| 24 // Vertical distance from the border to the center of slop. | 24 // Vertical distance from the border to the center of slop. |
| 25 constexpr float kSlopVertical = 0.165f; | 25 constexpr float kSlopVertical = 0.165f; |
| 26 | 26 |
| 27 // Horizontal distance from the border to the center of slop. | 27 // Horizontal distance from the border to the center of slop. |
| 28 constexpr float kSlopHorizontal = 0.125f; | 28 constexpr float kSlopHorizontal = 0.15f; |
| 29 | 29 |
| 30 // Minimum distance needed in at least one direction to call two vectors | 30 // Minimum distance needed in at least one direction to call two vectors |
| 31 // not equal. Also, minimum time distance needed to call two timestamps | 31 // not equal. Also, minimum time distance needed to call two timestamps |
| 32 // not equal. | 32 // not equal. |
| 33 constexpr float kDelta = 1.0e-7f; | 33 constexpr float kDelta = 1.0e-7f; |
| 34 | 34 |
| 35 constexpr float kCutoffHz = 10.0f; | 35 constexpr float kCutoffHz = 10.0f; |
| 36 constexpr float kRC = static_cast<float>(1.0 / (2.0 * M_PI * kCutoffHz)); | 36 constexpr float kRC = static_cast<float>(1.0 / (2.0 * M_PI * kCutoffHz)); |
| 37 constexpr float kNanoSecondsPerSecond = 1.0e9f; | 37 constexpr float kNanoSecondsPerSecond = 1.0e9f; |
| 38 | 38 |
| 39 constexpr int kMaxNumOfExtrapolations = 2; | |
| 40 | |
| 39 class Vector { | 41 class Vector { |
| 40 public: | 42 public: |
| 41 static inline void ClampTouchpadPosition(gvr::Vec2f* position) { | 43 static inline void ClampTouchpadPosition(gvr::Vec2f* position) { |
| 42 position->x = std::min(std::max(0.0f, position->x), 1.0f); | 44 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); | 45 position->y = std::min(std::max(0.0f, position->y), 1.0f); |
| 44 } | 46 } |
| 45 | 47 |
| 46 static inline void SetZero(gvr::Vec2f* v) { | 48 static inline void SetZero(gvr::Vec2f* v) { |
| 47 v->x = 0; | 49 v->x = 0; |
| 48 v->y = 0; | 50 v->y = 0; |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 139 if (controller_state_->GetApiStatus() != old_status || | 141 if (controller_state_->GetApiStatus() != old_status || |
| 140 controller_state_->GetConnectionState() != old_connection_state) { | 142 controller_state_->GetConnectionState() != old_connection_state) { |
| 141 VLOG(1) << "Controller Connection status: " | 143 VLOG(1) << "Controller Connection status: " |
| 142 << gvr_controller_connection_state_to_string( | 144 << gvr_controller_connection_state_to_string( |
| 143 controller_state_->GetConnectionState()); | 145 controller_state_->GetConnectionState()); |
| 144 } | 146 } |
| 145 } | 147 } |
| 146 | 148 |
| 147 void VrController::UpdateTouchInfo() { | 149 void VrController::UpdateTouchInfo() { |
| 148 CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly."; | 150 CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly."; |
| 149 gvr::Vec2f position; | 151 touch_point_->position.x = TouchPosX(); |
| 150 position.x = TouchPosX(); | 152 touch_point_->position.y = TouchPosY(); |
| 151 position.y = TouchPosY(); | |
| 152 touch_info_->touch_up = TouchUpHappened(); | 153 touch_info_->touch_up = TouchUpHappened(); |
| 153 touch_info_->touch_down = TouchDownHappened(); | 154 touch_info_->touch_down = TouchDownHappened(); |
| 154 touch_info_->is_touching = IsTouching(); | 155 touch_info_->is_touching = IsTouching(); |
| 155 touch_info_->touch_point.position = position; | 156 touch_info_->touch_point.position = touch_point_->position; |
| 156 Vector::ClampTouchpadPosition(&touch_info_->touch_point.position); | 157 Vector::ClampTouchpadPosition(&touch_info_->touch_point.position); |
| 157 touch_info_->touch_point.timestamp = | 158 touch_info_->touch_point.timestamp = |
| 158 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos; | 159 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos; |
| 159 if (controller_state_->GetLastTouchTimestamp() == last_touch_timestamp_) { | 160 touch_point_->timestamp = touch_info_->touch_point.timestamp; |
| 161 | |
| 162 if (IsTouching() && state_ == SCROLLING && | |
| 163 (controller_state_->GetLastTouchTimestamp() == last_touch_timestamp_ || | |
| 164 (touch_point_->position.x == cur_touch_point_->position.x && | |
| 165 touch_point_->position.y == cur_touch_point_->position.y && | |
|
bshe
2017/01/09 19:38:54
Vector::Equal(touch_point_->position, cur_touch_po
asimjour1
2017/01/11 21:59:45
Used Vector::Equal
touch_point_ is the point that
| |
| 166 extrapolated_touch_ < kMaxNumOfExtrapolations))) { | |
| 167 extrapolated_touch_++; | |
| 160 // Fill the touch_info | 168 // Fill the touch_info |
| 161 float duration = | 169 float duration = |
| 162 (gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos - | 170 (gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos - |
| 163 last_timestamp_nanos_) / | 171 last_timestamp_nanos_) / |
| 164 kNanoSecondsPerSecond; | 172 kNanoSecondsPerSecond; |
| 165 | 173 |
| 166 position.x += overall_velocity_.x * duration; | 174 touch_info_->touch_point.position.x = |
| 167 position.y += overall_velocity_.y * duration; | 175 touch_point_->position.x + overall_velocity_.x * duration; |
| 168 touch_info_->touch_point.position.x = position.x; | 176 touch_info_->touch_point.position.y = |
| 169 touch_info_->touch_point.position.y = position.y; | 177 touch_point_->position.y + overall_velocity_.y * duration; |
| 178 } else { | |
| 179 if (extrapolated_touch_ == kMaxNumOfExtrapolations) { | |
| 180 Vector::SetZero(&overall_velocity_); | |
| 181 } | |
| 182 extrapolated_touch_ = 0; | |
| 170 } | 183 } |
| 171 last_touch_timestamp_ = controller_state_->GetLastTouchTimestamp(); | 184 last_touch_timestamp_ = controller_state_->GetLastTouchTimestamp(); |
| 172 last_timestamp_nanos_ = | 185 last_timestamp_nanos_ = |
| 173 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos; | 186 gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos; |
| 187 touch_position_changed_ = UpdateCurrentTouchpoint(); | |
| 174 } | 188 } |
| 175 | 189 |
| 176 void VrController::Initialize(gvr_context* gvr_context) { | 190 void VrController::Initialize(gvr_context* gvr_context) { |
| 177 CHECK(gvr_context != nullptr) << "invalid gvr_context"; | 191 CHECK(gvr_context != nullptr) << "invalid gvr_context"; |
| 178 controller_api_.reset(new gvr::ControllerApi); | 192 controller_api_.reset(new gvr::ControllerApi); |
| 179 controller_state_.reset(new gvr::ControllerState); | 193 controller_state_.reset(new gvr::ControllerState); |
| 180 int32_t options = gvr::ControllerApi::DefaultOptions(); | 194 int32_t options = gvr::ControllerApi::DefaultOptions(); |
| 181 | 195 |
| 182 // Enable non-default options, if you need them: | 196 // Enable non-default options, if you need them: |
| 183 // options |= GVR_CONTROLLER_ENABLE_GYRO; | 197 // options |= GVR_CONTROLLER_ENABLE_GYRO; |
| 184 CHECK(controller_api_->Init(options, gvr_context)); | 198 CHECK(controller_api_->Init(options, gvr_context)); |
| 185 controller_api_->Resume(); | 199 controller_api_->Resume(); |
| 186 } | 200 } |
| 187 | 201 |
| 188 std::vector<std::unique_ptr<WebGestureEvent>> VrController::DetectGestures() { | 202 std::vector<std::unique_ptr<WebGestureEvent>> VrController::DetectGestures() { |
| 189 std::vector<std::unique_ptr<WebGestureEvent>> gesture_list; | 203 std::vector<std::unique_ptr<WebGestureEvent>> gesture_list; |
| 190 std::unique_ptr<WebGestureEvent> gesture(new WebGestureEvent()); | 204 std::unique_ptr<WebGestureEvent> gesture(new WebGestureEvent()); |
| 205 touch_point_.reset(new TouchPoint); | |
| 191 | 206 |
| 192 if (controller_state_->GetConnectionState() != gvr::kControllerConnected) { | 207 if (controller_state_->GetConnectionState() != gvr::kControllerConnected) { |
| 193 gesture_list.push_back(std::move(gesture)); | 208 gesture_list.push_back(std::move(gesture)); |
| 194 return gesture_list; | 209 return gesture_list; |
| 195 } | 210 } |
| 196 UpdateTouchInfo(); | 211 UpdateTouchInfo(); |
| 197 UpdateGestureFromTouchInfo(gesture.get()); | 212 UpdateGestureFromTouchInfo(gesture.get()); |
| 198 | 213 |
| 199 if (gesture->type == WebInputEvent::Undefined && | 214 if (gesture->type == WebInputEvent::Undefined && |
| 200 ButtonUpHappened(gvr::kControllerButtonClick)) { | 215 ButtonUpHappened(gvr::kControllerButtonClick)) { |
| 201 gesture->type = WebInputEvent::GestureTapDown; | 216 gesture->type = WebInputEvent::GestureTapDown; |
| 202 gesture->x = 0; | 217 gesture->x = 0; |
| 203 gesture->y = 0; | 218 gesture->y = 0; |
| 204 } | 219 } |
| 205 gesture->sourceDevice = blink::WebGestureDeviceTouchpad; | 220 gesture->sourceDevice = blink::WebGestureDeviceTouchpad; |
| 206 gesture_list.push_back(std::move(gesture)); | 221 gesture_list.push_back(std::move(gesture)); |
| 207 | 222 |
| 208 if (gesture_list.back()->type == WebInputEvent::GestureScrollEnd) { | 223 if (gesture_list.back()->type == WebInputEvent::GestureScrollEnd) { |
| 209 if (!ButtonDownHappened(gvr::kControllerButtonClick)) { | 224 if (!ButtonDownHappened(gvr::kControllerButtonClick) && |
| 225 (last_velocity_.x != 0.0 || last_velocity_.y != 0.0)) { | |
| 210 std::unique_ptr<WebGestureEvent> fling(new WebGestureEvent()); | 226 std::unique_ptr<WebGestureEvent> fling(new WebGestureEvent()); |
| 211 fling->timeStampSeconds = gesture_list.back()->timeStampSeconds; | 227 fling->timeStampSeconds = gesture_list.back()->timeStampSeconds; |
| 212 fling->sourceDevice = blink::WebGestureDeviceTouchpad; | 228 fling->sourceDevice = blink::WebGestureDeviceTouchpad; |
| 213 fling->type = WebInputEvent::GestureFlingStart; | 229 fling->type = WebInputEvent::GestureFlingStart; |
| 214 if (IsHorizontalGesture()) { | 230 if (IsHorizontalGesture()) { |
| 215 fling->data.flingStart.velocityX = | 231 fling->data.flingStart.velocityX = |
| 216 overall_velocity_.x * kDisplacementScaleFactor; | 232 last_velocity_.x * kDisplacementScaleFactor; |
| 217 } else { | 233 } else { |
| 218 fling->data.flingStart.velocityY = | 234 fling->data.flingStart.velocityY = |
| 219 overall_velocity_.y * kDisplacementScaleFactor; | 235 last_velocity_.y * kDisplacementScaleFactor; |
| 220 } | 236 } |
| 221 gesture_list.push_back(std::move(fling)); | 237 gesture_list.push_back(std::move(fling)); |
| 222 } | 238 } |
| 223 Reset(); | 239 Reset(); |
| 224 } | 240 } |
| 225 | 241 |
| 226 return gesture_list; | 242 return gesture_list; |
| 227 } | 243 } |
| 228 | 244 |
| 229 void VrController::UpdateGestureFromTouchInfo(WebGestureEvent* gesture) { | 245 void VrController::UpdateGestureFromTouchInfo(WebGestureEvent* gesture) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 265 | 281 |
| 266 void VrController::HandleDetectingState(WebGestureEvent* gesture) { | 282 void VrController::HandleDetectingState(WebGestureEvent* gesture) { |
| 267 // User lifts up finger from touch pad. | 283 // User lifts up finger from touch pad. |
| 268 if (touch_info_->touch_up || !(touch_info_->is_touching)) { | 284 if (touch_info_->touch_up || !(touch_info_->is_touching)) { |
| 269 Reset(); | 285 Reset(); |
| 270 return; | 286 return; |
| 271 } | 287 } |
| 272 | 288 |
| 273 // Touch position is changed, the touch point moves outside of slop, | 289 // Touch position is changed, the touch point moves outside of slop, |
| 274 // and the Controller's button is not down. | 290 // and the Controller's button is not down. |
| 275 if (UpdateCurrentTouchpoint() && touch_info_->is_touching && | 291 if (touch_position_changed_ && touch_info_->is_touching && |
| 276 !InSlop(touch_info_->touch_point.position) && | 292 !InSlop(touch_info_->touch_point.position) && |
| 277 !ButtonDownHappened(gvr::kControllerButtonClick)) { | 293 !ButtonDownHappened(gvr::kControllerButtonClick)) { |
| 278 state_ = SCROLLING; | 294 state_ = SCROLLING; |
| 279 gesture->type = WebInputEvent::GestureScrollBegin; | 295 gesture->type = WebInputEvent::GestureScrollBegin; |
| 280 UpdateGesture(gesture); | 296 UpdateGesture(gesture); |
| 281 gesture->data.scrollBegin.deltaXHint = | 297 gesture->data.scrollBegin.deltaXHint = |
| 282 displacement_.x * kDisplacementScaleFactor; | 298 displacement_.x * kDisplacementScaleFactor; |
| 283 gesture->data.scrollBegin.deltaYHint = | 299 gesture->data.scrollBegin.deltaYHint = |
| 284 displacement_.y * kDisplacementScaleFactor; | 300 displacement_.y * kDisplacementScaleFactor; |
| 285 } | 301 } |
| 286 } | 302 } |
| 287 | 303 |
| 288 void VrController::HandleScrollingState(WebGestureEvent* gesture) { | 304 void VrController::HandleScrollingState(WebGestureEvent* gesture) { |
| 289 // Update current touch point. | 305 // Update current touch point. |
|
bshe
2017/01/09 19:38:54
nit: remove the above comment
asimjour1
2017/01/11 21:59:45
Done.
| |
| 290 bool touch_position_changed = UpdateCurrentTouchpoint(); | |
| 291 if (touch_info_->touch_up || !(touch_info_->is_touching) || | 306 if (touch_info_->touch_up || !(touch_info_->is_touching) || |
| 292 ButtonDownHappened(gvr::kControllerButtonClick)) { | 307 ButtonDownHappened(gvr::kControllerButtonClick)) { |
| 293 // Gesture ends. | 308 // Gesture ends. |
| 294 gesture->type = WebInputEvent::GestureScrollEnd; | 309 gesture->type = WebInputEvent::GestureScrollEnd; |
| 295 UpdateGesture(gesture); | 310 UpdateGesture(gesture); |
| 296 } else if (touch_position_changed) { | 311 } else if (touch_position_changed_) { |
| 297 // User continues scrolling and there is a change in touch position. | 312 // User continues scrolling and there is a change in touch position. |
| 298 gesture->type = WebInputEvent::GestureScrollUpdate; | 313 gesture->type = WebInputEvent::GestureScrollUpdate; |
| 299 UpdateGesture(gesture); | 314 UpdateGesture(gesture); |
| 300 if (IsHorizontalGesture()) { | 315 if (IsHorizontalGesture()) { |
| 301 gesture->data.scrollUpdate.deltaX = | 316 gesture->data.scrollUpdate.deltaX = |
| 302 displacement_.x * kDisplacementScaleFactor; | 317 displacement_.x * kDisplacementScaleFactor; |
| 303 } else { | 318 } else { |
| 304 gesture->data.scrollUpdate.deltaY = | 319 gesture->data.scrollUpdate.deltaY = |
| 305 displacement_.y * kDisplacementScaleFactor; | 320 displacement_.y * kDisplacementScaleFactor; |
| 306 } | 321 } |
| 322 last_velocity_ = overall_velocity_; | |
| 307 } | 323 } |
| 308 } | 324 } |
| 309 | 325 |
| 310 bool VrController::IsHorizontalGesture() { | 326 bool VrController::IsHorizontalGesture() { |
| 311 return std::abs(overall_velocity_.x) > std::abs(overall_velocity_.y); | 327 return std::abs(last_velocity_.x) > std::abs(last_velocity_.y); |
| 312 } | 328 } |
| 313 | 329 |
| 314 bool VrController::InSlop(const gvr::Vec2f touch_position) { | 330 bool VrController::InSlop(const gvr::Vec2f touch_position) { |
| 315 return (std::abs(touch_position.x - init_touch_point_->position.x) < | 331 return (std::abs(touch_position.x - init_touch_point_->position.x) < |
| 316 kSlopHorizontal) && | 332 kSlopHorizontal) && |
| 317 (std::abs(touch_position.y - init_touch_point_->position.y) < | 333 (std::abs(touch_position.y - init_touch_point_->position.y) < |
| 318 kSlopVertical); | 334 kSlopVertical); |
| 319 } | 335 } |
| 320 | 336 |
| 321 void VrController::Reset() { | 337 void VrController::Reset() { |
| 322 // Reset state. | 338 // Reset state. |
| 323 state_ = WAITING; | 339 state_ = WAITING; |
| 324 | 340 |
| 325 // Reset the pointers. | 341 // Reset the pointers. |
| 326 prev_touch_point_.reset(new TouchPoint); | 342 prev_touch_point_.reset(new TouchPoint); |
| 327 cur_touch_point_.reset(new TouchPoint); | 343 cur_touch_point_.reset(new TouchPoint); |
| 328 init_touch_point_.reset(new TouchPoint); | 344 init_touch_point_.reset(new TouchPoint); |
| 329 touch_info_.reset(new TouchInfo); | 345 touch_info_.reset(new TouchInfo); |
| 330 Vector::SetZero(&overall_velocity_); | 346 Vector::SetZero(&overall_velocity_); |
| 347 Vector::SetZero(&last_velocity_); | |
| 331 } | 348 } |
| 332 | 349 |
| 333 void VrController::UpdateGesture(WebGestureEvent* gesture) { | 350 void VrController::UpdateGesture(WebGestureEvent* gesture) { |
| 334 if (!gesture) | 351 if (!gesture) |
| 335 LOG(ERROR) << "The gesture pointer is not initiated properly."; | 352 LOG(ERROR) << "The gesture pointer is not initiated properly."; |
| 336 displacement_ = | 353 displacement_ = Vector::Subtract(touch_info_->touch_point.position, |
| 337 Vector::Subtract(cur_touch_point_->position, prev_touch_point_->position); | 354 prev_touch_point_->position); |
|
bshe
2017/01/09 19:38:54
IIUC, should prev_touch_point save previous extrop
asimjour1
2017/01/11 21:59:45
It shouldn't be the previous extrapolated point. i
| |
| 338 } | 355 } |
| 339 | 356 |
| 340 bool VrController::UpdateCurrentTouchpoint() { | 357 bool VrController::UpdateCurrentTouchpoint() { |
| 341 if (touch_info_->is_touching || touch_info_->touch_up) { | 358 if (touch_info_->is_touching || touch_info_->touch_up) { |
| 342 // Update the touch point when the touch position has changed. | 359 // Update the touch point when the touch position has changed. |
| 343 if (!Vector::Equal(cur_touch_point_->position, | 360 if (!Vector::Equal(cur_touch_point_->position, |
| 344 touch_info_->touch_point.position)) { | 361 touch_info_->touch_point.position)) { |
| 345 prev_touch_point_.swap(cur_touch_point_); | 362 prev_touch_point_.swap(cur_touch_point_); |
| 346 *cur_touch_point_ = touch_info_->touch_point; | 363 cur_touch_point_ = std::move(touch_point_); |
| 347 UpdateOverallVelocity(); | 364 UpdateOverallVelocity(); |
| 348 return true; | 365 return true; |
| 349 } | 366 } |
| 350 } | 367 } |
| 351 return false; | 368 return false; |
| 352 } | 369 } |
| 353 | 370 |
| 354 void VrController::UpdateOverallVelocity() { | 371 void VrController::UpdateOverallVelocity() { |
| 355 float duration = | 372 float duration = |
| 356 (cur_touch_point_->timestamp - prev_touch_point_->timestamp) / | 373 (touch_info_->touch_point.timestamp - prev_touch_point_->timestamp) / |
| 357 kNanoSecondsPerSecond; | 374 kNanoSecondsPerSecond; |
| 358 | 375 |
| 359 // If the timestamp does not change, do not update velocity. | 376 // If the timestamp does not change, do not update velocity. |
| 360 if (duration < kDelta) | 377 if (duration < kDelta) |
| 361 return; | 378 return; |
| 362 | 379 |
| 363 gvr::Vec2f displacement = | 380 gvr::Vec2f displacement = Vector::Subtract(touch_info_->touch_point.position, |
| 364 Vector::Subtract(cur_touch_point_->position, prev_touch_point_->position); | 381 prev_touch_point_->position); |
| 365 | 382 |
| 366 gvr::Vec2f velocity = Vector::ScalarMult(displacement, 1 / duration); | 383 gvr::Vec2f velocity = Vector::ScalarMult(displacement, 1 / duration); |
| 367 | 384 |
| 368 float weight = duration / (kRC + duration); | 385 float weight = duration / (kRC + duration); |
| 369 | 386 |
| 370 overall_velocity_ = | 387 overall_velocity_ = |
| 371 Vector::Add(Vector::ScalarMult(overall_velocity_, 1 - weight), | 388 Vector::Add(Vector::ScalarMult(overall_velocity_, 1 - weight), |
| 372 Vector::ScalarMult(velocity, weight)); | 389 Vector::ScalarMult(velocity, weight)); |
| 373 } | 390 } |
| 374 | 391 |
| 375 } // namespace vr_shell | 392 } // namespace vr_shell |
| OLD | NEW |