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_shell_gl.h" | 5 #include "chrome/browser/android/vr_shell/vr_shell_gl.h" |
6 | 6 |
7 #include <chrono> | 7 #include <chrono> |
8 #include <limits> | 8 #include <limits> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
(...skipping 30 matching lines...) Expand all Loading... |
41 static constexpr int64_t kPredictionTimeWithoutVsyncNanos = 50000000; | 41 static constexpr int64_t kPredictionTimeWithoutVsyncNanos = 50000000; |
42 | 42 |
43 static constexpr float kZNear = 0.1f; | 43 static constexpr float kZNear = 0.1f; |
44 static constexpr float kZFar = 1000.0f; | 44 static constexpr float kZFar = 1000.0f; |
45 | 45 |
46 static constexpr float kReticleWidth = 0.025f; | 46 static constexpr float kReticleWidth = 0.025f; |
47 static constexpr float kReticleHeight = 0.025f; | 47 static constexpr float kReticleHeight = 0.025f; |
48 | 48 |
49 static constexpr float kLaserWidth = 0.01f; | 49 static constexpr float kLaserWidth = 0.01f; |
50 | 50 |
51 // Angle (radians) the beam down from the controller axis, for wrist comfort. | |
52 static constexpr float kErgoAngleOffset = 0.26f; | |
53 | |
54 static constexpr gvr::Vec3f kOrigin = {0.0f, 0.0f, 0.0f}; | 51 static constexpr gvr::Vec3f kOrigin = {0.0f, 0.0f, 0.0f}; |
55 | 52 |
56 // In lieu of an elbow model, we assume a position for the user's hand. | |
57 // TODO(mthiesse): Handedness options. | |
58 static constexpr gvr::Vec3f kHandPosition = {0.2f, -0.5f, -0.2f}; | |
59 | |
60 // Fraction of the distance to the object the cursor is drawn at to avoid | 53 // Fraction of the distance to the object the cursor is drawn at to avoid |
61 // rounding errors drawing the cursor behind the object. | 54 // rounding errors drawing the cursor behind the object. |
62 static constexpr float kReticleOffset = 0.99f; | 55 static constexpr float kReticleOffset = 0.99f; |
63 | 56 |
64 // GVR buffer indices for use with viewport->SetSourceBufferIndex | 57 // GVR buffer indices for use with viewport->SetSourceBufferIndex |
65 // or frame.BindBuffer. We use one for world content (with reprojection) | 58 // or frame.BindBuffer. We use one for world content (with reprojection) |
66 // including main VrShell and WebVR content plus world-space UI. | 59 // including main VrShell and WebVR content plus world-space UI. |
67 // The headlocked buffer is for UI that should not use reprojection. | 60 // The headlocked buffer is for UI that should not use reprojection. |
68 static constexpr int kFramePrimaryBuffer = 0; | 61 static constexpr int kFramePrimaryBuffer = 0; |
69 static constexpr int kFrameHeadlockedBuffer = 1; | 62 static constexpr int kFrameHeadlockedBuffer = 1; |
(...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
462 webvr_right_viewport_.reset( | 455 webvr_right_viewport_.reset( |
463 new gvr::BufferViewport(gvr_api_->CreateBufferViewport())); | 456 new gvr::BufferViewport(gvr_api_->CreateBufferViewport())); |
464 buffer_viewport_list_->GetBufferViewport(GVR_RIGHT_EYE, | 457 buffer_viewport_list_->GetBufferViewport(GVR_RIGHT_EYE, |
465 webvr_right_viewport_.get()); | 458 webvr_right_viewport_.get()); |
466 webvr_right_viewport_->SetSourceBufferIndex(kFramePrimaryBuffer); | 459 webvr_right_viewport_->SetSourceBufferIndex(kFramePrimaryBuffer); |
467 | 460 |
468 main_thread_task_runner_->PostTask( | 461 main_thread_task_runner_->PostTask( |
469 FROM_HERE, base::Bind(&VrShell::GvrDelegateReady, weak_vr_shell_)); | 462 FROM_HERE, base::Bind(&VrShell::GvrDelegateReady, weak_vr_shell_)); |
470 } | 463 } |
471 | 464 |
472 void VrShellGl::UpdateController() { | 465 void VrShellGl::UpdateController(const gvr::Vec3f& head_direction) { |
473 controller_->UpdateState(); | 466 controller_->UpdateState(head_direction); |
| 467 pointer_start_ = controller_->GetPointerStart(); |
474 | 468 |
475 device::GvrGamepadData pad = controller_->GetGamepadData(); | 469 device::GvrGamepadData pad = controller_->GetGamepadData(); |
476 main_thread_task_runner_->PostTask( | 470 main_thread_task_runner_->PostTask( |
477 FROM_HERE, base::Bind(&VrShell::UpdateGamepadData, weak_vr_shell_, pad)); | 471 FROM_HERE, base::Bind(&VrShell::UpdateGamepadData, weak_vr_shell_, pad)); |
478 } | 472 } |
479 | 473 |
480 void VrShellGl::HandleControllerInput(const gvr::Vec3f& forward_vector) { | 474 void VrShellGl::HandleControllerInput(const gvr::Vec3f& head_direction) { |
481 if (ShouldDrawWebVr()) { | 475 if (ShouldDrawWebVr()) { |
482 // Process screen touch events for Cardboard button compatibility. | 476 // Process screen touch events for Cardboard button compatibility. |
483 // Also send tap events for controller "touchpad click" events. | 477 // Also send tap events for controller "touchpad click" events. |
484 if (touch_pending_ || | 478 if (touch_pending_ || |
485 controller_->ButtonUpHappened( | 479 controller_->ButtonUpHappened( |
486 gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK)) { | 480 gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK)) { |
487 touch_pending_ = false; | 481 touch_pending_ = false; |
488 std::unique_ptr<WebGestureEvent> gesture(new WebGestureEvent( | 482 std::unique_ptr<WebGestureEvent> gesture(new WebGestureEvent( |
489 WebInputEvent::GestureTapDown, WebInputEvent::NoModifiers, | 483 WebInputEvent::GestureTapDown, WebInputEvent::NoModifiers, |
490 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF())); | 484 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF())); |
491 gesture->sourceDevice = blink::WebGestureDeviceTouchpad; | 485 gesture->sourceDevice = blink::WebGestureDeviceTouchpad; |
492 gesture->x = 0; | 486 gesture->x = 0; |
493 gesture->y = 0; | 487 gesture->y = 0; |
494 SendGesture(InputTarget::CONTENT, std::move(gesture)); | 488 SendGesture(InputTarget::CONTENT, std::move(gesture)); |
495 DVLOG(1) << __FUNCTION__ << ": sent CLICK gesture"; | 489 DVLOG(1) << __FUNCTION__ << ": sent CLICK gesture"; |
496 } | 490 } |
497 } | 491 } |
498 | 492 |
499 gvr::Vec3f ergo_neutral_pose; | 493 gvr::Vec3f ergo_neutral_pose; |
500 if (!controller_->IsConnected()) { | 494 if (!controller_->IsConnected()) { |
501 // No controller detected, set up a gaze cursor that tracks the | 495 // No controller detected, set up a gaze cursor that tracks the |
502 // forward direction. | 496 // forward direction. |
503 ergo_neutral_pose = {0.0f, 0.0f, -1.0f}; | 497 ergo_neutral_pose = {0.0f, 0.0f, -1.0f}; |
504 controller_quat_ = GetRotationFromZAxis(forward_vector); | 498 controller_quat_ = GetRotationFromZAxis(head_direction); |
505 } else { | 499 } else { |
506 ergo_neutral_pose = {0.0f, -sin(kErgoAngleOffset), -cos(kErgoAngleOffset)}; | 500 ergo_neutral_pose = {0.0f, -sin(kErgoAngleOffset), -cos(kErgoAngleOffset)}; |
507 controller_quat_ = controller_->Orientation(); | 501 controller_quat_ = controller_->Orientation(); |
508 } | 502 } |
509 | 503 |
510 gvr::Mat4f mat = QuatToMatrix(controller_quat_); | 504 gvr::Mat4f mat = QuatToMatrix(controller_quat_); |
511 gvr::Vec3f controller_direction = MatrixVectorMul(mat, ergo_neutral_pose); | 505 gvr::Vec3f controller_direction = MatrixVectorMul(mat, ergo_neutral_pose); |
512 | 506 |
513 HandleControllerAppButtonActivity(controller_direction); | 507 HandleControllerAppButtonActivity(controller_direction); |
514 | 508 |
515 if (ShouldDrawWebVr()) { | 509 if (ShouldDrawWebVr()) { |
516 return; | 510 return; |
517 } | 511 } |
518 | 512 |
519 // If we place the reticle based on elements intersecting the controller beam, | 513 // If we place the reticle based on elements intersecting the controller beam, |
520 // we can end up with the reticle hiding behind elements, or jumping laterally | 514 // we can end up with the reticle hiding behind elements, or jumping laterally |
521 // in the field of view. This is physically correct, but hard to use. For | 515 // in the field of view. This is physically correct, but hard to use. For |
522 // usability, do the following instead: | 516 // usability, do the following instead: |
523 // | 517 // |
524 // - Project the controller laser onto a distance-limiting sphere. | 518 // - Project the controller laser onto a distance-limiting sphere. |
525 // - Create a vector between the eyes and the outer surface point. | 519 // - Create a vector between the eyes and the outer surface point. |
526 // - If any UI elements intersect this vector, and is within the bounding | 520 // - If any UI elements intersect this vector, and is within the bounding |
527 // sphere, choose the closest to the eyes, and place the reticle at the | 521 // sphere, choose the closest to the eyes, and place the reticle at the |
528 // intersection point. | 522 // intersection point. |
529 | 523 |
530 // Compute the distance from the eyes to the distance limiting sphere. Note | 524 // Compute the distance from the eyes to the distance limiting sphere. Note |
531 // that the sphere is centered at the controller, rather than the eye, for | 525 // that the sphere is centered at the controller, rather than the eye, for |
532 // simplicity. | 526 // simplicity. |
533 float distance = scene_->GetBackgroundDistance(); | 527 float distance = scene_->GetBackgroundDistance(); |
534 target_point_ = GetRayPoint(kHandPosition, controller_direction, distance); | 528 target_point_ = GetRayPoint(pointer_start_, controller_direction, distance); |
535 gvr::Vec3f eye_to_target = target_point_; | 529 gvr::Vec3f eye_to_target = target_point_; |
536 NormalizeVector(eye_to_target); | 530 NormalizeVector(eye_to_target); |
537 | 531 |
538 // Determine which UI element (if any) intersects the line between the eyes | 532 // Determine which UI element (if any) intersects the line between the eyes |
539 // and the controller target position. | 533 // and the controller target position. |
540 float closest_element_distance = VectorLength(target_point_); | 534 float closest_element_distance = VectorLength(target_point_); |
541 target_element_ = nullptr; | 535 target_element_ = nullptr; |
542 float target_x; | 536 float target_x; |
543 float target_y; | 537 float target_y; |
544 | 538 |
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
812 head_pose = gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time); | 806 head_pose = gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time); |
813 } | 807 } |
814 | 808 |
815 gvr::Vec3f position = GetTranslation(head_pose); | 809 gvr::Vec3f position = GetTranslation(head_pose); |
816 if (position.x == 0.0f && position.y == 0.0f && position.z == 0.0f) { | 810 if (position.x == 0.0f && position.y == 0.0f && position.z == 0.0f) { |
817 // This appears to be a 3DOF pose without a neck model. Add one. | 811 // This appears to be a 3DOF pose without a neck model. Add one. |
818 // The head pose has redundant data. Assume we're only using the | 812 // The head pose has redundant data. Assume we're only using the |
819 // object_from_reference_matrix, we're not updating position_external. | 813 // object_from_reference_matrix, we're not updating position_external. |
820 // TODO: Not sure what object_from_reference_matrix is. The new api removed | 814 // TODO: Not sure what object_from_reference_matrix is. The new api removed |
821 // it. For now, removing it seems working fine. | 815 // it. For now, removing it seems working fine. |
822 gvr_api_->ApplyNeckModel(head_pose, 1.0f); | 816 head_pose = gvr_api_->ApplyNeckModel(head_pose, 1.0f); |
823 } | 817 } |
824 | 818 |
825 // Update the render position of all UI elements (including desktop). | 819 // Update the render position of all UI elements (including desktop). |
826 scene_->UpdateTransforms(TimeInMicroseconds()); | 820 scene_->UpdateTransforms(TimeInMicroseconds()); |
827 | 821 |
828 { | 822 { |
829 // TODO(crbug.com/704690): Acquire controller state in a way that's timely | 823 // TODO(crbug.com/704690): Acquire controller state in a way that's timely |
830 // for both the gamepad API and UI input handling. | 824 // for both the gamepad API and UI input handling. |
831 TRACE_EVENT0("gpu", "VrShellGl::UpdateController"); | 825 TRACE_EVENT0("gpu", "VrShellGl::UpdateController"); |
832 UpdateController(); | 826 auto head_direction = GetForwardVector(head_pose); |
833 HandleControllerInput(GetForwardVector(head_pose)); | 827 UpdateController(head_direction); |
| 828 HandleControllerInput(head_direction); |
834 } | 829 } |
835 | 830 |
836 DrawWorldElements(head_pose); | 831 DrawWorldElements(head_pose); |
837 | 832 |
838 frame.Unbind(); | 833 frame.Unbind(); |
839 | 834 |
840 // Draw head-locked elements to a separate, non-reprojected buffer. | 835 // Draw head-locked elements to a separate, non-reprojected buffer. |
841 if (scene_->HasVisibleHeadLockedElements()) { | 836 if (scene_->HasVisibleHeadLockedElements()) { |
842 frame.BindBuffer(kFrameHeadlockedBuffer); | 837 frame.BindBuffer(kFrameHeadlockedBuffer); |
843 DrawHeadLockedElements(); | 838 DrawHeadLockedElements(); |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
927 pixel_rect.right - pixel_rect.left, | 922 pixel_rect.right - pixel_rect.left, |
928 pixel_rect.top - pixel_rect.bottom); | 923 pixel_rect.top - pixel_rect.bottom); |
929 | 924 |
930 const gvr::Mat4f render_matrix = | 925 const gvr::Mat4f render_matrix = |
931 MatrixMul(PerspectiveMatrixFromView(buffer_viewport_->GetSourceFov(), | 926 MatrixMul(PerspectiveMatrixFromView(buffer_viewport_->GetSourceFov(), |
932 kZNear, kZFar), | 927 kZNear, kZFar), |
933 eye_view_matrix); | 928 eye_view_matrix); |
934 | 929 |
935 DrawElements(render_matrix, elementsInDrawOrder); | 930 DrawElements(render_matrix, elementsInDrawOrder); |
936 if (draw_cursor) { | 931 if (draw_cursor) { |
| 932 DrawController(render_matrix); |
937 DrawCursor(render_matrix); | 933 DrawCursor(render_matrix); |
938 DrawController(render_matrix); | |
939 } | 934 } |
940 } | 935 } |
941 } | 936 } |
942 | 937 |
943 void VrShellGl::DrawElements( | 938 void VrShellGl::DrawElements( |
944 const gvr::Mat4f& view_proj_matrix, | 939 const gvr::Mat4f& view_proj_matrix, |
945 const std::vector<const ContentRectangle*>& elements) { | 940 const std::vector<const ContentRectangle*>& elements) { |
946 for (const auto* rect : elements) { | 941 for (const auto* rect : elements) { |
947 gvr::Mat4f transform = MatrixMul(view_proj_matrix, rect->TransformMatrix()); | 942 gvr::Mat4f transform = MatrixMul(view_proj_matrix, rect->TransformMatrix()); |
948 | 943 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1050 TranslateM(mat, mat, target_point_.x * kReticleOffset, | 1045 TranslateM(mat, mat, target_point_.x * kReticleOffset, |
1051 target_point_.y * kReticleOffset, | 1046 target_point_.y * kReticleOffset, |
1052 target_point_.z * kReticleOffset); | 1047 target_point_.z * kReticleOffset); |
1053 | 1048 |
1054 gvr::Mat4f transform = MatrixMul(render_matrix, mat); | 1049 gvr::Mat4f transform = MatrixMul(render_matrix, mat); |
1055 vr_shell_renderer_->GetReticleRenderer()->Draw(transform); | 1050 vr_shell_renderer_->GetReticleRenderer()->Draw(transform); |
1056 | 1051 |
1057 // Draw the laser. | 1052 // Draw the laser. |
1058 | 1053 |
1059 // Find the length of the beam (from hand to target). | 1054 // Find the length of the beam (from hand to target). |
1060 const float laser_length = Distance(kHandPosition, target_point_); | 1055 const float laser_length = Distance(pointer_start_, target_point_); |
1061 | 1056 |
1062 // Build a beam, originating from the origin. | 1057 // Build a beam, originating from the origin. |
1063 SetIdentityM(mat); | 1058 SetIdentityM(mat); |
1064 | 1059 |
1065 // Move the beam half its height so that its end sits on the origin. | 1060 // Move the beam half its height so that its end sits on the origin. |
1066 TranslateM(mat, mat, 0.0f, 0.5f, 0.0f); | 1061 TranslateM(mat, mat, 0.0f, 0.5f, 0.0f); |
1067 ScaleM(mat, mat, kLaserWidth, laser_length, 1); | 1062 ScaleM(mat, mat, kLaserWidth, laser_length, 1); |
1068 | 1063 |
1069 // Tip back 90 degrees to flat, pointing at the scene. | 1064 // Tip back 90 degrees to flat, pointing at the scene. |
1070 const gvr::Quatf q = QuatFromAxisAngle({1.0f, 0.0f, 0.0f}, -M_PI / 2); | 1065 const gvr::Quatf q = QuatFromAxisAngle({1.0f, 0.0f, 0.0f}, -M_PI / 2); |
1071 mat = MatrixMul(QuatToMatrix(q), mat); | 1066 mat = MatrixMul(QuatToMatrix(q), mat); |
1072 | 1067 |
1073 const gvr::Vec3f beam_direction = {target_point_.x - kHandPosition.x, | 1068 const gvr::Vec3f beam_direction = {target_point_.x - pointer_start_.x, |
1074 target_point_.y - kHandPosition.y, | 1069 target_point_.y - pointer_start_.y, |
1075 target_point_.z - kHandPosition.z}; | 1070 target_point_.z - pointer_start_.z}; |
1076 const gvr::Mat4f beam_direction_mat = | 1071 const gvr::Mat4f beam_direction_mat = |
1077 QuatToMatrix(GetRotationFromZAxis(beam_direction)); | 1072 QuatToMatrix(GetRotationFromZAxis(beam_direction)); |
1078 | 1073 |
| 1074 float opacity = controller_->GetOpacity(); |
1079 // Render multiple faces to make the laser appear cylindrical. | 1075 // Render multiple faces to make the laser appear cylindrical. |
1080 const int faces = 4; | 1076 const int faces = 4; |
1081 for (int i = 0; i < faces; i++) { | 1077 for (int i = 0; i < faces; i++) { |
1082 // Rotate around Z. | 1078 // Rotate around Z. |
1083 const float angle = M_PI * 2 * i / faces; | 1079 const float angle = M_PI * 2 * i / faces; |
1084 const gvr::Quatf rot = QuatFromAxisAngle({0.0f, 0.0f, 1.0f}, angle); | 1080 const gvr::Quatf rot = QuatFromAxisAngle({0.0f, 0.0f, 1.0f}, angle); |
1085 gvr::Mat4f face_transform = MatrixMul(QuatToMatrix(rot), mat); | 1081 gvr::Mat4f face_transform = MatrixMul(QuatToMatrix(rot), mat); |
1086 | 1082 |
1087 // Orient according to target direction. | 1083 // Orient according to target direction. |
1088 face_transform = MatrixMul(beam_direction_mat, face_transform); | 1084 face_transform = MatrixMul(beam_direction_mat, face_transform); |
1089 | 1085 |
1090 // Move the beam origin to the hand. | 1086 // Move the beam origin to the hand. |
1091 TranslateM(face_transform, face_transform, kHandPosition.x, kHandPosition.y, | 1087 TranslateM(face_transform, face_transform, pointer_start_.x, |
1092 kHandPosition.z); | 1088 pointer_start_.y, pointer_start_.z); |
1093 | 1089 |
1094 transform = MatrixMul(render_matrix, face_transform); | 1090 transform = MatrixMul(render_matrix, face_transform); |
1095 vr_shell_renderer_->GetLaserRenderer()->Draw(transform); | 1091 vr_shell_renderer_->GetLaserRenderer()->Draw(opacity, transform); |
1096 } | 1092 } |
1097 } | 1093 } |
1098 | 1094 |
1099 void VrShellGl::DrawController(const gvr::Mat4f& view_proj_matrix) { | 1095 void VrShellGl::DrawController(const gvr::Mat4f& view_proj_matrix) { |
1100 if (!vr_shell_renderer_->GetControllerRenderer()->IsSetUp()) | 1096 if (!vr_shell_renderer_->GetControllerRenderer()->IsSetUp()) |
1101 return; | 1097 return; |
| 1098 auto state = controller_->GetModelState(); |
| 1099 auto opacity = controller_->GetOpacity(); |
1102 auto transform = MatrixMul(view_proj_matrix, controller_->GetTransform()); | 1100 auto transform = MatrixMul(view_proj_matrix, controller_->GetTransform()); |
1103 auto state = controller_->GetModelState(); | 1101 vr_shell_renderer_->GetControllerRenderer()->Draw(state, opacity, transform); |
1104 vr_shell_renderer_->GetControllerRenderer()->Draw(state, transform); | |
1105 } | 1102 } |
1106 | 1103 |
1107 bool VrShellGl::ShouldDrawWebVr() { | 1104 bool VrShellGl::ShouldDrawWebVr() { |
1108 return web_vr_mode_ && scene_->GetWebVrRenderingEnabled(); | 1105 return web_vr_mode_ && scene_->GetWebVrRenderingEnabled(); |
1109 } | 1106 } |
1110 | 1107 |
1111 void VrShellGl::DrawWebVr() { | 1108 void VrShellGl::DrawWebVr() { |
1112 TRACE_EVENT0("gpu", "VrShellGl::DrawWebVr"); | 1109 TRACE_EVENT0("gpu", "VrShellGl::DrawWebVr"); |
1113 // Don't need face culling, depth testing, blending, etc. Turn it all off. | 1110 // Don't need face culling, depth testing, blending, etc. Turn it all off. |
1114 glDisable(GL_CULL_FACE); | 1111 glDisable(GL_CULL_FACE); |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1304 // appropriate recommended render resolution as the default size during | 1301 // appropriate recommended render resolution as the default size during |
1305 // InitializeGl. Revisit if the initialization order changes. | 1302 // InitializeGl. Revisit if the initialization order changes. |
1306 device::mojom::VRDisplayInfoPtr info = VrShell::CreateVRDisplayInfo( | 1303 device::mojom::VRDisplayInfoPtr info = VrShell::CreateVRDisplayInfo( |
1307 gvr_api_.get(), webvr_surface_size_, device_id); | 1304 gvr_api_.get(), webvr_surface_size_, device_id); |
1308 main_thread_task_runner_->PostTask( | 1305 main_thread_task_runner_->PostTask( |
1309 FROM_HERE, | 1306 FROM_HERE, |
1310 base::Bind(&RunVRDisplayInfoCallback, callback, base::Passed(&info))); | 1307 base::Bind(&RunVRDisplayInfoCallback, callback, base::Passed(&info))); |
1311 } | 1308 } |
1312 | 1309 |
1313 } // namespace vr_shell | 1310 } // namespace vr_shell |
OLD | NEW |