blob: 26d435a338206a3caa646f5e9505310f04889f50 [file] [log] [blame]
/*
* Copyright (C) 2020 Igalia S.L. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WebFakeXRInputController.h"
#if ENABLE(WEBXR)
#include "WebFakeXRDevice.h"
#include "XRHandJoint.h"
namespace WebCore {
using InputSource = PlatformXR::Device::FrameData::InputSource;
using InputSourceButton = PlatformXR::Device::FrameData::InputSourceButton;
using InputSourcePose = PlatformXR::Device::FrameData::InputSourcePose;
using ButtonType = FakeXRButtonStateInit::Type;
#if ENABLE(WEBXR_HANDS)
using HandJointsVector = PlatformXR::Device::FrameData::HandJointsVector;
using InputSourceHandJoint = PlatformXR::Device::FrameData::InputSourceHandJoint;
#endif
// https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping
constexpr std::array<ButtonType, 5> XR_STANDARD_BUTTONS = { ButtonType::Grip, ButtonType::Touchpad, ButtonType::Thumbstick, ButtonType::OptionalButton, ButtonType::OptionalThumbstick };
Ref<WebFakeXRInputController> WebFakeXRInputController::create(PlatformXR::InputSourceHandle handle, const FakeXRInputSourceInit& init)
{
return adoptRef(*new WebFakeXRInputController(handle, init));
}
WebFakeXRInputController::WebFakeXRInputController(PlatformXR::InputSourceHandle handle, const FakeXRInputSourceInit& init)
: m_handle(handle)
, m_handeness(init.handedness)
, m_targetRayMode(init.targetRayMode)
, m_profiles(init.profiles)
, m_primarySelected(init.selectionStarted)
, m_simulateSelect(init.selectionClicked)
{
setPointerOrigin(init.pointerOrigin, false);
setGripOrigin(init.gripOrigin, false);
setSupportedButtons(init.supportedButtons);
#if ENABLE(WEBXR_HANDS)
updateHandJoints(init.handJoints);
#endif
}
void WebFakeXRInputController::setGripOrigin(FakeXRRigidTransformInit gripOrigin, bool emulatedPosition)
{
auto transform = WebFakeXRDevice::parseRigidTransform(gripOrigin);
if (transform.hasException())
return;
m_gripOrigin = InputSourcePose { transform.releaseReturnValue(), emulatedPosition };
}
void WebFakeXRInputController::setPointerOrigin(FakeXRRigidTransformInit pointerOrigin, bool emulatedPosition)
{
auto transform = WebFakeXRDevice::parseRigidTransform(pointerOrigin);
if (transform.hasException())
return;
m_pointerOrigin = { transform.releaseReturnValue(), emulatedPosition };
}
void WebFakeXRInputController::disconnect()
{
m_connected = false;
}
void WebFakeXRInputController::reconnect()
{
m_connected = true;
}
void WebFakeXRInputController::setSupportedButtons(const Vector<FakeXRButtonStateInit>& buttons)
{
m_buttons.clear();
for (auto& button : buttons)
m_buttons.add(button.buttonType, button);
}
void WebFakeXRInputController::updateButtonState(const FakeXRButtonStateInit& init)
{
auto it = m_buttons.find(init.buttonType);
if (it != m_buttons.end())
it->value = init;
}
InputSource WebFakeXRInputController::getFrameData()
{
InputSource state;
state.handle = m_handle;
state.handeness = m_handeness;
state.targetRayMode = m_targetRayMode;
state.profiles = m_profiles;
state.pointerOrigin = m_pointerOrigin;
state.gripOrigin = m_gripOrigin;
#if ENABLE(WEBXR_HANDS)
state.handJoints = m_handJoints;
#endif
if (m_simulateSelect)
m_primarySelected = true;
// https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping
// Mimic xr-standard gamepad layout
// Primary trigger is required and must be at index 0
state.buttons.append({
.touched = m_primarySelected,
.pressed = m_primarySelected,
.pressedValue = m_primarySelected ? 1.0f : 0.0f
});
// Next buttons in xr-standard order
for (auto buttonType : XR_STANDARD_BUTTONS) {
auto data = getButtonOrPlaceholder(buttonType);
if (data.button)
state.buttons.append(*data.button);
if (data.axes)
state.axes.appendVector(*data.axes);
}
if (m_simulateSelect) {
m_primarySelected = false;
m_simulateSelect = false;
}
return state;
}
WebFakeXRInputController::ButtonOrPlaceholder WebFakeXRInputController::getButtonOrPlaceholder(FakeXRButtonStateInit::Type buttonType) const
{
ButtonOrPlaceholder result;
auto it = m_buttons.find(buttonType);
if (it != m_buttons.end()) {
result.button = InputSourceButton {
.touched = it->value.touched,
.pressed = it->value.pressed,
.pressedValue = it->value.pressedValue
};
if (buttonType == ButtonType::Touchpad || buttonType == ButtonType::Thumbstick)
result.axes = Vector<float> { it->value.xValue, it->value.yValue };
} else {
// Add a placeholder if needed
// Devices that lack one of the optional inputs listed in the tables above MUST preserve their place in the
// buttons or axes array, reporting a placeholder button or placeholder axis, respectively.
if (buttonType != ButtonType::OptionalButton && buttonType != ButtonType::OptionalThumbstick) {
auto priority = std::find(XR_STANDARD_BUTTONS.begin(), XR_STANDARD_BUTTONS.end(), buttonType);
ASSERT(priority != XR_STANDARD_BUTTONS.end());
for (auto it = priority + 1; it != XR_STANDARD_BUTTONS.end(); ++it) {
if (m_buttons.contains(*it)) {
result.button = InputSourceButton();
break;
}
}
}
if (buttonType == ButtonType::Touchpad && m_buttons.contains(ButtonType::Thumbstick))
result.axes = Vector<float> { 0.0, 0.0 };
}
return result;
}
#if ENABLE(WEBXR_HANDS)
void WebFakeXRInputController::updateHandJoints(const Vector<FakeXRJointStateInit>& handJoints)
{
if (handJoints.isEmpty() || handJoints.size() != static_cast<size_t>(XRHandJoint::Count)) {
m_handJoints = std::nullopt;
return;
}
HandJointsVector updatedJoints;
for (auto handJoint : handJoints) {
auto transform = WebFakeXRDevice::parseRigidTransform(handJoint.pose);
if (transform.hasException()) {
updatedJoints.append(std::nullopt);
continue;
}
updatedJoints.append(InputSourceHandJoint { InputSourcePose { transform.releaseReturnValue(), false }, handJoint.radius });
}
m_handJoints = WTFMove(updatedJoints);
}
#endif // ENABLE(WEBXR_HANDS)
} // namespace WebCore
#endif // ENABLE(WEBXR)