/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using Meta.XR.Util; using UnityEngine; namespace Oculus.Interaction.Input { interface IUsage { void Apply(ControllerDataAsset controllerDataAsset, OVRInput.Controller controllerMask); } class UsageTouchMapping : IUsage { public ControllerButtonUsage Usage { get; } public OVRInput.Touch Touch { get; } public UsageTouchMapping(ControllerButtonUsage usage, OVRInput.Touch touch) { Usage = usage; Touch = touch; } public void Apply(ControllerDataAsset controllerDataAsset, OVRInput.Controller controllerMask) { bool value = OVRInput.Get(Touch, controllerMask); controllerDataAsset.Input.SetButton(Usage, value); } } class UsageButtonMapping : IUsage { public ControllerButtonUsage Usage { get; } public OVRInput.Button Button { get; } public UsageButtonMapping(ControllerButtonUsage usage, OVRInput.Button button) { Usage = usage; Button = button; } public void Apply(ControllerDataAsset controllerDataAsset, OVRInput.Controller controllerMask) { bool value = OVRInput.Get(Button, controllerMask); controllerDataAsset.Input.SetButton(Usage, value); } } class UsageAxis1DMapping : IUsage { public ControllerAxis1DUsage Usage { get; } public OVRInput.Axis1D Axis1D { get; } public UsageAxis1DMapping(ControllerAxis1DUsage usage, OVRInput.Axis1D axis1D) { Usage = usage; Axis1D = axis1D; } public void Apply(ControllerDataAsset controllerDataAsset, OVRInput.Controller controllerMask) { float value = OVRInput.Get(Axis1D, controllerMask); controllerDataAsset.Input.SetAxis1D(Usage, value); } } class UsageAxis2DMapping : IUsage { public ControllerAxis2DUsage Usage { get; } public OVRInput.Axis2D Axis2D { get; } public UsageAxis2DMapping(ControllerAxis2DUsage usage, OVRInput.Axis2D axis2D) { Usage = usage; Axis2D = axis2D; } public void Apply(ControllerDataAsset controllerDataAsset, OVRInput.Controller controllerMask) { Vector2 value = OVRInput.Get(Axis2D, controllerMask); controllerDataAsset.Input.SetAxis2D(Usage, value); } } /// /// Returns the Pointer Pose for the active controller model /// as found in the official prefabs. /// This point is usually located at the front tip of the controller. /// struct OVRPointerPoseSelector { private static readonly Pose[] QUEST1_POINTERS = new Pose[2] { new Pose(new Vector3(-0.00779999979f,-0.00410000002f,0.0375000015f), Quaternion.Euler(359.209534f, 6.45196056f, 6.95544577f)), new Pose(new Vector3(0.00779999979f,-0.00410000002f,0.0375000015f), Quaternion.Euler(359.209534f, 353.548035f, 353.044556f)) }; private static readonly Pose[] QUEST2_POINTERS = new Pose[2] { new Pose(new Vector3(0.00899999961f, -0.00321028521f, 0.030869998f), Quaternion.Euler(359.209534f, 6.45196056f, 6.95544577f)), new Pose(new Vector3(-0.00899999961f, -0.00321028521f, 0.030869998f), Quaternion.Euler(359.209534f, 353.548035f, 353.044556f)) }; public Pose LocalPointerPose { get; private set; } public OVRPointerPoseSelector(Handedness handedness) { OVRPlugin.SystemHeadset headset = OVRPlugin.GetSystemHeadsetType(); switch (headset) { case OVRPlugin.SystemHeadset.Oculus_Quest_2: case OVRPlugin.SystemHeadset.Oculus_Link_Quest_2: LocalPointerPose = QUEST2_POINTERS[(int)handedness]; break; default: LocalPointerPose = QUEST1_POINTERS[(int)handedness]; break; } } } [Feature(Feature.Interaction)] public class FromOVRControllerDataSource : DataSource { [Header("OVR Data Source")] [SerializeField, Interface(typeof(IOVRCameraRigRef))] private UnityEngine.Object _cameraRigRef; public IOVRCameraRigRef CameraRigRef { get; private set; } [SerializeField] private bool _processLateUpdates = false; [Header("Shared Configuration")] [SerializeField] private Handedness _handedness; [SerializeField, Interface(typeof(ITrackingToWorldTransformer))] private UnityEngine.Object _trackingToWorldTransformer; private ITrackingToWorldTransformer TrackingToWorldTransformer; public bool ProcessLateUpdates { get { return _processLateUpdates; } set { _processLateUpdates = value; } } private readonly ControllerDataAsset _controllerDataAsset = new ControllerDataAsset(); private OVRInput.Controller _ovrController; private ControllerDataSourceConfig _config; private OVRPointerPoseSelector _pointerPoseSelector; #region OVR Controller Mappings // Mappings from Unity XR CommonUsage to Oculus Button/Touch. private static readonly IUsage[] ControllerUsageMappings = { new UsageButtonMapping(ControllerButtonUsage.PrimaryButton, OVRInput.Button.One), new UsageTouchMapping(ControllerButtonUsage.PrimaryTouch, OVRInput.Touch.One), new UsageButtonMapping(ControllerButtonUsage.SecondaryButton, OVRInput.Button.Two), new UsageTouchMapping(ControllerButtonUsage.SecondaryTouch, OVRInput.Touch.Two), new UsageButtonMapping(ControllerButtonUsage.GripButton, OVRInput.Button.PrimaryHandTrigger), new UsageButtonMapping(ControllerButtonUsage.TriggerButton, OVRInput.Button.PrimaryIndexTrigger), new UsageButtonMapping(ControllerButtonUsage.MenuButton, OVRInput.Button.Start), new UsageButtonMapping(ControllerButtonUsage.Primary2DAxisClick, OVRInput.Button.PrimaryThumbstick), new UsageTouchMapping(ControllerButtonUsage.Primary2DAxisTouch, OVRInput.Touch.PrimaryThumbstick), new UsageTouchMapping(ControllerButtonUsage.Thumbrest, OVRInput.Touch.PrimaryThumbRest), new UsageAxis1DMapping(ControllerAxis1DUsage.Trigger, OVRInput.Axis1D.PrimaryIndexTrigger), new UsageAxis1DMapping(ControllerAxis1DUsage.Grip, OVRInput.Axis1D.PrimaryHandTrigger), new UsageAxis2DMapping(ControllerAxis2DUsage.Primary2DAxis, OVRInput.Axis2D.PrimaryThumbstick), }; #endregion protected void Awake() { TrackingToWorldTransformer = _trackingToWorldTransformer as ITrackingToWorldTransformer; CameraRigRef = _cameraRigRef as IOVRCameraRigRef; UpdateConfig(); } protected override void Start() { this.BeginStart(ref _started, () => base.Start()); this.AssertField(CameraRigRef, nameof(CameraRigRef)); this.AssertField(TrackingToWorldTransformer, nameof(TrackingToWorldTransformer)); if (_handedness == Handedness.Left) { _ovrController = OVRInput.Controller.LTouch; } else { _ovrController = OVRInput.Controller.RTouch; } _pointerPoseSelector = new OVRPointerPoseSelector(_handedness); UpdateConfig(); this.EndStart(ref _started); } protected override void OnEnable() { base.OnEnable(); if (_started) { CameraRigRef.WhenInputDataDirtied += HandleInputDataDirtied; } } protected override void OnDisable() { if (_started) { CameraRigRef.WhenInputDataDirtied -= HandleInputDataDirtied; } base.OnDisable(); MarkInputDataRequiresUpdate(); } private void HandleInputDataDirtied(bool isLateUpdate) { if (isLateUpdate && !_processLateUpdates) { return; } MarkInputDataRequiresUpdate(); } private ControllerDataSourceConfig Config { get { if (_config != null) { return _config; } _config = new ControllerDataSourceConfig() { Handedness = _handedness }; return _config; } } private void UpdateConfig() { Config.Handedness = _handedness; Config.TrackingToWorldTransformer = TrackingToWorldTransformer; } protected override void UpdateData() { _controllerDataAsset.Config = Config; _controllerDataAsset.IsDataValid = true; _controllerDataAsset.IsConnected = (OVRInput.GetConnectedControllers() & _ovrController) > 0; if (!_controllerDataAsset.IsConnected) { // revert state fields to their defaults _controllerDataAsset.IsTracked = default; _controllerDataAsset.Input = default; _controllerDataAsset.RootPoseOrigin = default; return; } _controllerDataAsset.IsTracked = true; OVRInput.Handedness dominantHand = OVRInput.GetDominantHand(); _controllerDataAsset.IsDominantHand = (dominantHand == OVRInput.Handedness.LeftHanded && _handedness == Handedness.Left) || (dominantHand == OVRInput.Handedness.RightHanded && _handedness == Handedness.Right); // Update button usages _controllerDataAsset.Input.Clear(); OVRInput.Controller controllerMask = _ovrController; foreach (IUsage mapping in ControllerUsageMappings) { mapping.Apply(_controllerDataAsset, controllerMask); } // Update poses // Root pose, in tracking space. _controllerDataAsset.RootPose = new Pose( OVRInput.GetLocalControllerPosition(_ovrController), OVRInput.GetLocalControllerRotation(_ovrController)); _controllerDataAsset.RootPoseOrigin = PoseOrigin.RawTrackedPose; // Convert controller pointer pose from local to tracking space. Matrix4x4 controllerModelToTracking = Matrix4x4.TRS( _controllerDataAsset.RootPose.position, _controllerDataAsset.RootPose.rotation, Vector3.one); _controllerDataAsset.PointerPose = new Pose(controllerModelToTracking.MultiplyPoint3x4(_pointerPoseSelector.LocalPointerPose.position), _controllerDataAsset.RootPose.rotation * _pointerPoseSelector.LocalPointerPose.rotation); _controllerDataAsset.PointerPoseOrigin = PoseOrigin.RawTrackedPose; } protected override ControllerDataAsset DataAsset => _controllerDataAsset; #region Inject public void InjectAllFromOVRControllerDataSource(UpdateModeFlags updateMode, IDataSource updateAfter, Handedness handedness, ITrackingToWorldTransformer trackingToWorldTransformer) { base.InjectAllDataSource(updateMode, updateAfter); InjectHandedness(handedness); InjectTrackingToWorldTransformer(trackingToWorldTransformer); } public void InjectHandedness(Handedness handedness) { _handedness = handedness; } public void InjectTrackingToWorldTransformer(ITrackingToWorldTransformer trackingToWorldTransformer) { _trackingToWorldTransformer = trackingToWorldTransformer as UnityEngine.Object; TrackingToWorldTransformer = trackingToWorldTransformer; } #endregion } }