blob: aec6d3c16fe2866dd666dd129d8b7d1734f77d4e [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/oculus/oculus_gamepad_data_fetcher.h"
#include <memory>
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "device/gamepad/public/cpp/gamepads.h"
#include "third_party/libovr/src/Include/OVR_CAPI.h"
#include "ui/gfx/transform.h"
#include "ui/gfx/transform_util.h"
namespace device {
namespace {
float ApplyTriggerDeadzone(float value) {
// Trigger value should be between 0 and 1. We apply a deadzone for small
// values so a loose controller still reports a value of 0 when not in use.
float kTriggerDeadzone = 0.01f;
return (value < kTriggerDeadzone) ? 0 : value;
}
void SetGamepadButton(Gamepad* pad,
const ovrInputState& input_state,
ovrButton button_id) {
bool pressed = (input_state.Buttons & button_id) != 0;
bool touched = (input_state.Touches & button_id) != 0;
pad->buttons[pad->buttons_length].pressed = pressed;
pad->buttons[pad->buttons_length].touched = touched;
pad->buttons[pad->buttons_length].value = pressed ? 1.0f : 0.0f;
pad->buttons_length++;
}
void SetGamepadTouchTrigger(Gamepad* pad,
const ovrInputState& input_state,
ovrTouch touch_id,
float value) {
bool touched = (input_state.Touches & touch_id) != 0;
value = ApplyTriggerDeadzone(value);
pad->buttons[pad->buttons_length].pressed = value != 0;
pad->buttons[pad->buttons_length].touched = touched;
pad->buttons[pad->buttons_length].value = value;
pad->buttons_length++;
}
void SetGamepadTrigger(Gamepad* pad, float value) {
value = ApplyTriggerDeadzone(value);
pad->buttons[pad->buttons_length].pressed = value != 0;
pad->buttons[pad->buttons_length].touched = value != 0;
pad->buttons[pad->buttons_length].value = value;
pad->buttons_length++;
}
void SetGamepadTouch(Gamepad* pad,
const ovrInputState& input_state,
ovrTouch touch_id) {
bool touched = (input_state.Touches & touch_id) != 0;
pad->buttons[pad->buttons_length].pressed = false;
pad->buttons[pad->buttons_length].touched = touched;
pad->buttons[pad->buttons_length].value = 0.0f;
pad->buttons_length++;
}
void SetTouchData(PadState* state,
const ovrPoseStatef& pose,
const ovrInputState& input_state,
ovrHandType hand,
unsigned int display_id) {
if (!state)
return;
Gamepad& pad = state->data;
if (!state->is_initialized) {
state->is_initialized = true;
pad.connected = true;
pad.is_xr = true;
pad.pose.not_null = true;
pad.pose.has_orientation = true;
pad.pose.has_position = true;
pad.display_id = display_id;
switch (hand) {
case ovrHand_Left:
swprintf(pad.id, Gamepad::kIdLengthCap, L"Oculus Touch (Left)");
pad.hand = GamepadHand::kLeft;
break;
case ovrHand_Right:
swprintf(pad.id, Gamepad::kIdLengthCap, L"Oculus Touch (Right)");
pad.hand = GamepadHand::kRight;
break;
default:
NOTREACHED();
return;
}
}
pad.timestamp = OculusGamepadDataFetcher::CurrentTimeInMicroseconds();
pad.axes_length = 0;
pad.buttons_length = 0;
pad.axes[pad.axes_length++] = input_state.Thumbstick[hand].x;
pad.axes[pad.axes_length++] = -input_state.Thumbstick[hand].y;
// This gamepad layout is the defacto standard, but can be adjusted for WebXR.
switch (hand) {
case ovrHand_Left:
SetGamepadButton(&pad, input_state, ovrButton_LThumb);
break;
case ovrHand_Right:
SetGamepadButton(&pad, input_state, ovrButton_RThumb);
break;
default:
NOTREACHED();
break;
}
SetGamepadTouchTrigger(
&pad, input_state,
hand == ovrHand_Left ? ovrTouch_LIndexTrigger : ovrTouch_RIndexTrigger,
input_state.IndexTrigger[hand]);
SetGamepadTrigger(&pad, input_state.HandTrigger[hand]);
switch (hand) {
case ovrHand_Left:
SetGamepadButton(&pad, input_state, ovrButton_X);
SetGamepadButton(&pad, input_state, ovrButton_Y);
SetGamepadTouch(&pad, input_state, ovrTouch_LThumbRest);
break;
case ovrHand_Right:
SetGamepadButton(&pad, input_state, ovrButton_A);
SetGamepadButton(&pad, input_state, ovrButton_B);
SetGamepadTouch(&pad, input_state, ovrTouch_RThumbRest);
break;
default:
NOTREACHED();
break;
}
pad.pose.orientation.not_null = true;
pad.pose.orientation.x = pose.ThePose.Orientation.x;
pad.pose.orientation.y = pose.ThePose.Orientation.y;
pad.pose.orientation.z = pose.ThePose.Orientation.z;
pad.pose.orientation.w = pose.ThePose.Orientation.w;
pad.pose.position.not_null = true;
pad.pose.position.x = pose.ThePose.Position.x;
pad.pose.position.y = pose.ThePose.Position.y;
pad.pose.position.z = pose.ThePose.Position.z;
pad.pose.angular_velocity.not_null = true;
pad.pose.angular_velocity.x = pose.AngularVelocity.x;
pad.pose.angular_velocity.y = pose.AngularVelocity.y;
pad.pose.angular_velocity.z = pose.AngularVelocity.z;
pad.pose.linear_velocity.not_null = true;
pad.pose.linear_velocity.x = pose.LinearVelocity.x;
pad.pose.linear_velocity.y = pose.LinearVelocity.y;
pad.pose.linear_velocity.z = pose.LinearVelocity.z;
pad.pose.angular_acceleration.not_null = true;
pad.pose.angular_acceleration.x = pose.AngularAcceleration.x;
pad.pose.angular_acceleration.y = pose.AngularAcceleration.y;
pad.pose.angular_acceleration.z = pose.AngularAcceleration.z;
pad.pose.linear_acceleration.not_null = true;
pad.pose.linear_acceleration.x = pose.LinearAcceleration.x;
pad.pose.linear_acceleration.y = pose.LinearAcceleration.y;
pad.pose.linear_acceleration.z = pose.LinearAcceleration.z;
}
} // namespace
OculusGamepadDataFetcher::Factory::Factory(unsigned int display_id,
OculusGamepadDataProvider* provider)
: display_id_(display_id), provider_(provider) {
DVLOG(1) << __FUNCTION__ << "=" << this;
}
OculusGamepadDataFetcher::Factory::~Factory() {
DVLOG(1) << __FUNCTION__ << "=" << this;
}
std::unique_ptr<GamepadDataFetcher>
OculusGamepadDataFetcher::Factory::CreateDataFetcher() {
return std::make_unique<OculusGamepadDataFetcher>(display_id_, provider_);
}
GamepadSource OculusGamepadDataFetcher::Factory::source() {
return GAMEPAD_SOURCE_OCULUS;
}
OculusGamepadDataFetcher::OculusGamepadDataFetcher(
unsigned int display_id,
OculusGamepadDataProvider* provider)
: display_id_(display_id), weak_ptr_factory_(this) {
DVLOG(1) << __FUNCTION__ << "=" << this;
// Register for updates.
provider->RegisterDataFetcher(this);
}
OculusGamepadDataFetcher::~OculusGamepadDataFetcher() {
DVLOG(1) << __FUNCTION__ << "=" << this;
}
GamepadSource OculusGamepadDataFetcher::source() {
return GAMEPAD_SOURCE_OCULUS;
}
void OculusGamepadDataFetcher::OnAddedToProvider() {}
void OculusGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
base::AutoLock lock(lock_);
if (data_.have_input_touch) {
SetTouchData(GetPadState(ovrControllerType_LTouch),
data_.tracking.HandPoses[ovrHand_Left], data_.input_touch,
ovrHand_Left, display_id_);
SetTouchData(GetPadState(ovrControllerType_RTouch),
data_.tracking.HandPoses[ovrHand_Right], data_.input_touch,
ovrHand_Right, display_id_);
}
if (data_.have_input_remote) {
PadState* state = GetPadState(ovrControllerType_Remote);
if (state) {
Gamepad& pad = state->data;
if (!state->is_initialized) {
state->is_initialized = true;
swprintf(pad.id, Gamepad::kIdLengthCap, L"Oculus Remote");
pad.connected = true;
pad.is_xr = true;
pad.display_id = display_id_;
}
pad.timestamp = CurrentTimeInMicroseconds();
pad.axes_length = 0;
pad.buttons_length = 0;
SetGamepadButton(&pad, data_.input_remote, ovrButton_Enter);
SetGamepadButton(&pad, data_.input_remote, ovrButton_Back);
SetGamepadButton(&pad, data_.input_remote, ovrButton_Up);
SetGamepadButton(&pad, data_.input_remote, ovrButton_Down);
SetGamepadButton(&pad, data_.input_remote, ovrButton_Left);
SetGamepadButton(&pad, data_.input_remote, ovrButton_Right);
}
}
}
void OculusGamepadDataFetcher::UpdateGamepadData(OculusInputData data) {
base::AutoLock lock(lock_);
data_ = data;
}
void OculusGamepadDataFetcher::PauseHint(bool paused) {}
} // namespace device