1 // Copyright 2017 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 "device/vr/oculus/oculus_gamepad_helper.h"
6 
7 #include <algorithm>
8 #include <memory>
9 
10 #include "base/logging.h"
11 #include "device/gamepad/public/cpp/gamepads.h"
12 #include "device/vr/util/xr_standard_gamepad_builder.h"
13 #include "device/vr/vr_device.h"
14 #include "third_party/libovr/src/Include/OVR_CAPI.h"
15 #include "ui/gfx/transform.h"
16 #include "ui/gfx/transform_util.h"
17 
18 namespace device {
19 
20 namespace {
21 
ApplyTriggerDeadzone(float value)22 float ApplyTriggerDeadzone(float value) {
23   // Trigger value should be between 0 and 1.  We apply a deadzone for small
24   // values so a loose controller still reports a value of 0 when not in use.
25   float kTriggerDeadzone = 0.01f;
26 
27   return (value < kTriggerDeadzone) ? 0 : value;
28 }
29 
OculusToMojomHand(ovrHandType hand)30 device::mojom::XRHandedness OculusToMojomHand(ovrHandType hand) {
31   switch (hand) {
32     case ovrHand_Left:
33       return device::mojom::XRHandedness::LEFT;
34     case ovrHand_Right:
35       return device::mojom::XRHandedness::RIGHT;
36     default:
37       return device::mojom::XRHandedness::NONE;
38   }
39 }
40 
41 class OculusGamepadBuilder : public XRStandardGamepadBuilder {
42  public:
OculusGamepadBuilder(ovrInputState state,ovrHandType hand)43   OculusGamepadBuilder(ovrInputState state, ovrHandType hand)
44       : XRStandardGamepadBuilder(OculusToMojomHand(hand)),
45         state_(state),
46         ovr_hand_(hand) {
47     switch (ovr_hand_) {
48       case ovrHand_Left:
49         SetPrimaryButton(GetTouchTriggerButton(ovrTouch_LIndexTrigger,
50                                                state_.IndexTrigger[ovr_hand_]));
51         SetSecondaryButton(GetTriggerButton(state_.HandTrigger[ovr_hand_]));
52         SetThumbstickData(GetThumbstickData(ovrButton_LThumb));
53         AddOptionalButtonData(GetStandardButton(ovrButton_X));
54         AddOptionalButtonData(GetStandardButton(ovrButton_Y));
55         AddOptionalButtonData(GetTouchButton(ovrTouch_LThumbRest));
56         break;
57       case ovrHand_Right:
58         SetPrimaryButton(GetTouchTriggerButton(ovrTouch_RIndexTrigger,
59                                                state_.IndexTrigger[ovr_hand_]));
60         SetSecondaryButton(GetTriggerButton(state_.HandTrigger[ovr_hand_]));
61         SetThumbstickData(GetThumbstickData(ovrButton_RThumb));
62         AddOptionalButtonData(GetStandardButton(ovrButton_A));
63         AddOptionalButtonData(GetStandardButton(ovrButton_B));
64         AddOptionalButtonData(GetTouchButton(ovrTouch_RThumbRest));
65         break;
66       default:
67         DLOG(WARNING) << "Unsupported hand configuration.";
68     }
69   }
70 
71   ~OculusGamepadBuilder() override = default;
72 
73  private:
GetStandardButton(ovrButton id)74   GamepadButton GetStandardButton(ovrButton id) {
75     bool pressed = (state_.Buttons & id) != 0;
76     bool touched = (state_.Touches & id) != 0;
77     double value = pressed ? 1.0 : 0.0;
78     return GamepadButton(pressed, touched, value);
79   }
80 
GetTouchButton(ovrTouch id)81   GamepadButton GetTouchButton(ovrTouch id) {
82     bool touched = (state_.Touches & id) != 0;
83     return GamepadButton(false, touched, 0.0f);
84   }
85 
GetTriggerButton(float value)86   GamepadButton GetTriggerButton(float value) {
87     value = ApplyTriggerDeadzone(value);
88     bool pressed = value != 0;
89     bool touched = pressed;
90     return GamepadButton(pressed, touched, value);
91   }
92 
GetTouchTriggerButton(ovrTouch id,float value)93   GamepadButton GetTouchTriggerButton(ovrTouch id, float value) {
94     value = ApplyTriggerDeadzone(value);
95     bool pressed = value != 0;
96     bool touched = (state_.Touches & id) != 0;
97     return GamepadButton(pressed, touched, value);
98   }
99 
GetThumbstickData(ovrButton id)100   GamepadBuilder::ButtonData GetThumbstickData(ovrButton id) {
101     GamepadButton button = GetStandardButton(id);
102     GamepadBuilder::ButtonData data;
103     data.touched = button.touched;
104     data.pressed = button.pressed;
105     data.value = button.value;
106 
107     // Invert the y axis because -1 is up in the Gamepad API but down in Oculus.
108     data.type = GamepadBuilder::ButtonData::Type::kThumbstick;
109     data.x_axis = state_.Thumbstick[ovr_hand_].x;
110     data.y_axis = -state_.Thumbstick[ovr_hand_].y;
111 
112     return data;
113   }
114 
115  private:
116   ovrInputState state_;
117   ovrHandType ovr_hand_;
118 
119   DISALLOW_COPY_AND_ASSIGN(OculusGamepadBuilder);
120 };
121 
122 }  // namespace
123 
124 // Order of buttons 1-4 is dictated by the xr-standard Gamepad mapping.
125 // Buttons 5-7 are in order of decreasing importance.
126 // 1) index trigger (primary trigger/button)
127 // 2) hand trigger (secondary trigger/button)
128 // 3) EMPTY (no touchpad press)
129 // 4) thumbstick press
130 // 5) A or X
131 // 6) B or Y
132 // 7) thumbrest touch sensor
133 //
134 // Order of axes 1-4 is dictated by the xr-standard Gamepad mapping.
135 // 1) EMPTY (no touchpad)
136 // 2) EMPTY (no touchpad)
137 // 3) thumbstick X
138 // 4) thumbstick Y
CreateGamepad(ovrSession session,ovrHandType hand)139 base::Optional<Gamepad> OculusGamepadHelper::CreateGamepad(ovrSession session,
140                                                            ovrHandType hand) {
141   ovrInputState input_touch;
142   bool have_touch = OVR_SUCCESS(
143       ovr_GetInputState(session, ovrControllerType_Touch, &input_touch));
144   if (!have_touch) {
145     return base::nullopt;
146   }
147 
148   OculusGamepadBuilder touch(input_touch, hand);
149   return touch.GetGamepad();
150 }
151 
152 }  // namespace device
153