clean project
This commit is contained in:
86
Assets/Oculus/VR/Scripts/OVRTrackedKeyboard/OVRKeyboard.cs
Normal file
86
Assets/Oculus/VR/Scripts/OVRTrackedKeyboard/OVRKeyboard.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public static class OVRKeyboard
|
||||
{
|
||||
public struct TrackedKeyboardState
|
||||
{
|
||||
public bool isPositionValid;
|
||||
public bool isPositionTracked;
|
||||
public bool isOrientationValid;
|
||||
public bool isOrientationTracked;
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
public double timeInSeconds;
|
||||
}
|
||||
|
||||
public struct TrackedKeyboardInfo
|
||||
{
|
||||
public string Name;
|
||||
public UInt64 Identifier;
|
||||
public Vector3 Dimensions;
|
||||
public OVRPlugin.TrackedKeyboardFlags KeyboardFlags;
|
||||
public OVRPlugin.TrackedKeyboardPresentationStyles SupportedPresentationStyles;
|
||||
}
|
||||
|
||||
public static TrackedKeyboardState GetKeyboardState()
|
||||
{
|
||||
TrackedKeyboardState keyboardState;
|
||||
|
||||
OVRPlugin.KeyboardState keyboardStatePlugin;
|
||||
OVRPlugin.GetKeyboardState(OVRPlugin.Step.Render, out keyboardStatePlugin);
|
||||
keyboardState.timeInSeconds = keyboardStatePlugin.PoseState.Time;
|
||||
|
||||
OVRPose nodePose = keyboardStatePlugin.PoseState.Pose.ToOVRPose();
|
||||
keyboardState.position = nodePose.position;
|
||||
keyboardState.rotation = nodePose.orientation;
|
||||
|
||||
keyboardState.isPositionValid = (keyboardStatePlugin.PositionValid == OVRPlugin.Bool.True);
|
||||
keyboardState.isPositionTracked = (keyboardStatePlugin.PositionTracked == OVRPlugin.Bool.True);
|
||||
keyboardState.isOrientationValid = (keyboardStatePlugin.OrientationValid == OVRPlugin.Bool.True);
|
||||
keyboardState.isOrientationTracked = (keyboardStatePlugin.OrientationTracked == OVRPlugin.Bool.True);
|
||||
|
||||
return keyboardState;
|
||||
}
|
||||
|
||||
// Query for information about the system keyboards.
|
||||
public static bool GetSystemKeyboardInfo(OVRPlugin.TrackedKeyboardQueryFlags keyboardQueryFlags, out TrackedKeyboardInfo keyboardInfo)
|
||||
{
|
||||
keyboardInfo = default(TrackedKeyboardInfo);
|
||||
|
||||
OVRPlugin.KeyboardDescription keyboardDescription;
|
||||
if(OVRPlugin.GetSystemKeyboardDescription(keyboardQueryFlags, out keyboardDescription))
|
||||
{
|
||||
keyboardInfo.Name = Encoding.UTF8.GetString(keyboardDescription.Name).TrimEnd('\0');
|
||||
keyboardInfo.Identifier = keyboardDescription.TrackedKeyboardId;
|
||||
keyboardInfo.Dimensions = new Vector3(keyboardDescription.Dimensions.x, keyboardDescription.Dimensions.y, keyboardDescription.Dimensions.z);
|
||||
keyboardInfo.KeyboardFlags = keyboardDescription.KeyboardFlags;
|
||||
keyboardInfo.SupportedPresentationStyles = keyboardDescription.SupportedPresentationStyles;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool StopKeyboardTracking(TrackedKeyboardInfo keyboardInfo)
|
||||
{
|
||||
return OVRPlugin.StopKeyboardTracking();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9cee92a63d840b43b5ba9d872653170
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,902 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
public class OVRTrackedKeyboard : MonoBehaviour
|
||||
{
|
||||
private static readonly float underlayScaleMultX_ = 1.475f;
|
||||
private static readonly float underlayScaleConstY_ = 0.001f;
|
||||
private static readonly float underlayScaleMultZ_ = 2.138f;
|
||||
private static readonly Vector3 underlayOffset_ = new Vector3 { x = 0.0f, y = 0.0f, z = -0.028f };
|
||||
private static readonly float boundingBoxAboveKeyboardY_ = 0.08f;
|
||||
|
||||
/// <summary>
|
||||
/// Used by TrackingState property to give the current state of keyboard tracking.
|
||||
/// </summary>
|
||||
public enum TrackedKeyboardState
|
||||
{
|
||||
/// <summary>
|
||||
/// The OVRTrackedKeyboard component has not yet been initialized.
|
||||
/// </summary>
|
||||
Uninitialized,
|
||||
/// <summary>
|
||||
/// Component is initialized but user has not selected a keyboard
|
||||
/// to track in the system settings.
|
||||
/// </summary>
|
||||
NoTrackableKeyboard,
|
||||
/// <summary>
|
||||
/// Keyboard tracking has been stopped or has not yet started for current keyboard.
|
||||
/// </summary>
|
||||
Offline,
|
||||
/// <summary>
|
||||
/// Keyboard tracking has been started but no tracking data is yet available.
|
||||
/// This can occur if the keyboard is not visible to the cameras.
|
||||
/// </summary>
|
||||
StartedNotTracked,
|
||||
/// <summary>
|
||||
/// Keyboard tracking has been started but no tracking data has been available for a while.
|
||||
/// This can occur if the keyboard is no longer visible to the cameras.
|
||||
/// </summary>
|
||||
Stale,
|
||||
/// <summary>
|
||||
/// Keyboard is currently being tracked and recent tracking data is available.
|
||||
/// </summary>
|
||||
Valid,
|
||||
/// <summary>
|
||||
/// An error occurred while initializing keyboard tracking.
|
||||
/// </summary>
|
||||
Error,
|
||||
/// <summary>
|
||||
/// Was unable to retrieve system keyboard info. Can occur if required
|
||||
/// keyboard extension is not properly enabled in the application manifest.
|
||||
/// </summary>
|
||||
ErrorExtensionFailed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines which visualization is used for the tracked keyboard.
|
||||
/// </summary>
|
||||
public enum KeyboardPresentation
|
||||
{
|
||||
/// <summary>
|
||||
/// The keyboard is rendered as an opaque model in VR and if the user's hands are
|
||||
/// placed over it, they are rendered using passthrough.
|
||||
/// </summary>
|
||||
PreferOpaque,
|
||||
/// <summary>
|
||||
/// The keyboard and hands are rendered using a rectangular passthrough window
|
||||
/// around the keyboard, and only the key labels are rendered in VR on top of the keyboard.
|
||||
/// </summary>
|
||||
PreferKeyLabels,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines amount that keyboard is tilted from its ordinary horizontal position. For internal use.
|
||||
/// </summary>
|
||||
public float CurrentKeyboardAngleFromUp { get; private set; } = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// Current state of keyboard tracking.
|
||||
/// </summary>
|
||||
public TrackedKeyboardState TrackingState { get; private set; } = TrackedKeyboardState.Uninitialized;
|
||||
/// <summary>
|
||||
/// Provides information about the keyboard currently being tracked by this
|
||||
/// OVRTrackedKeyboard component.
|
||||
/// </summary>
|
||||
public OVRKeyboard.TrackedKeyboardInfo ActiveKeyboardInfo { get; private set; }
|
||||
/// <summary>
|
||||
/// Provides information about the keyboard currently selected for tracking in
|
||||
/// the system settings. May not yet be tracked by this OVRTrackedKeyboard component.
|
||||
/// </summary>
|
||||
public OVRKeyboard.TrackedKeyboardInfo SystemKeyboardInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines which visualization will be used to present the tracked keyboard
|
||||
/// to the user.
|
||||
/// </summary>
|
||||
public KeyboardPresentation Presentation
|
||||
{
|
||||
get
|
||||
{
|
||||
return presentation;
|
||||
}
|
||||
set
|
||||
{
|
||||
presentation = value;
|
||||
UpdatePresentation(GetKeyboardVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not the OVRTrackedKeyboard component will attempt to search
|
||||
/// for and track a keyboard. If true, the component will continually search
|
||||
/// for a tracked keyboard. If one is detected it will be shown. If false,
|
||||
/// no keyboard is shown and the prefab is inactive. The keyboard can still
|
||||
/// be used to enter text into input fields even though it cannot be seen in VR.
|
||||
/// </summary>
|
||||
public bool TrackingEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return trackingEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
trackingEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not the keyboard must be connected via Bluetooth in
|
||||
/// order to be tracked. If set to true, the keyboard must be connected to the
|
||||
/// headset via Bluetooth in order to be tracked. The keyboard will stop being
|
||||
/// tracked if it is powered off or disconnected from the headset. If set to false,
|
||||
/// the keyboard will be tracked as long as it is visible to the headset's cameras.
|
||||
/// </summary>
|
||||
public bool ConnectionRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
return connectionRequired;
|
||||
}
|
||||
set
|
||||
{
|
||||
connectionRequired = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether to search for local keyboards attached to the headset
|
||||
/// or for remote keyboards not attached to the headset.
|
||||
/// </summary>
|
||||
public OVRPlugin.TrackedKeyboardQueryFlags KeyboardQueryFlags
|
||||
{
|
||||
get
|
||||
{
|
||||
return keyboardQueryFlags;
|
||||
}
|
||||
set
|
||||
{
|
||||
keyboardQueryFlags = value;
|
||||
}
|
||||
}
|
||||
|
||||
#region User settings
|
||||
// These properties can be modified by the user of the prefab
|
||||
[Header("Settings")]
|
||||
[SerializeField]
|
||||
[Tooltip("If true, will continually try to track and show keyboard. If false, no keyboard will be shown.")]
|
||||
private bool trackingEnabled = true;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("If true, system keyboard must be paired and connected to track.")]
|
||||
private bool connectionRequired = true;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Which type of keyboard you wish to use.")]
|
||||
private OVRPlugin.TrackedKeyboardQueryFlags keyboardQueryFlags = OVRPlugin.TrackedKeyboardQueryFlags.Local;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Opaque will render a solid model of the keyboard with passthrough hands. " +
|
||||
"Key Labels will render the entire keyboard in passthrough other than the key labels. " +
|
||||
"These are both suggestions and may not always be available.")]
|
||||
private KeyboardPresentation presentation = KeyboardPresentation.PreferOpaque;
|
||||
|
||||
/// <summary>
|
||||
/// How large of a passthrough area to show surrounding the keyboard when using Key Label presentation.
|
||||
/// </summary>
|
||||
[Tooltip("How large of a passthrough area to show surrounding the keyboard when using Key Label presentation")]
|
||||
public float PassthroughBorderMultiplier = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// The shader used for rendering the keyboard model in opaque mode.
|
||||
/// </summary>
|
||||
[Tooltip("The shader used for rendering the keyboard model")]
|
||||
public Shader keyboardModelShader;
|
||||
#endregion
|
||||
|
||||
private OVRPlugin.TrackedKeyboardPresentationStyles currentKeyboardPresentationStyles = 0;
|
||||
private OVROverlay projectedPassthroughOpaque_;
|
||||
private MeshRenderer[] activeKeyboardRenderers_;
|
||||
private GameObject activeKeyboardMesh_;
|
||||
private MeshRenderer activeKeyboardMeshRenderer_;
|
||||
private GameObject passthroughQuad_;
|
||||
private Shader opaqueShader_;
|
||||
|
||||
// These properties generally don't need to be modified by the user of the prefab
|
||||
|
||||
/// <summary>
|
||||
/// Internal only. The shader used to render the keyboard in key label mode.
|
||||
/// </summary>
|
||||
[Header("Internal")]
|
||||
public Shader KeyLabelModeShader;
|
||||
/// <summary>
|
||||
/// Internal only. The shader used to render the passthrough rectangle in opaque mode.
|
||||
/// </summary>
|
||||
public Shader PassthroughShader;
|
||||
|
||||
#region MR Service Setup
|
||||
[SerializeField] private Transform projectedPassthroughRoot;
|
||||
[SerializeField] private MeshFilter projectedPassthroughMesh;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Internal only. The passthrough layer used to render the passthrough rectangle in key label mode.
|
||||
/// </summary>
|
||||
public OVRPassthroughLayer ProjectedPassthroughKeyLabel;
|
||||
/// <summary>
|
||||
/// Internal only. The passthrough layer used to render the passthrough rectangle in opaque mode.
|
||||
/// </summary>
|
||||
public OVROverlay PassthroughOverlay
|
||||
{
|
||||
get { return projectedPassthroughOpaque_; }
|
||||
private set {}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event that is dispatched when the component starts or stops actively tracking the keyboard.
|
||||
/// </summary>
|
||||
public Action<TrackedKeyboardSetActiveEvent> TrackedKeyboardActiveChanged = delegate { };
|
||||
/// <summary>
|
||||
/// Event that is dispatched when the state of keyboard tracking changes (e.g. tracking
|
||||
/// becomes stale or valid as keyboard passes in/out of camera view).
|
||||
/// </summary>
|
||||
public Action<TrackedKeyboardVisibilityChangedEvent> TrackedKeyboardVisibilityChanged = delegate { };
|
||||
|
||||
/// <summary>
|
||||
/// Transform that determines current position and rotation of the keyboard.
|
||||
/// </summary>
|
||||
public Transform ActiveKeyboardTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Internal only. Determines whether the hands are currently positioned over the keyboard.
|
||||
/// In opaque presentation mode, passthrough hands are only shown when this is true.
|
||||
/// </summary>
|
||||
[HideInInspector]
|
||||
public bool HandsOverKeyboard = false;
|
||||
|
||||
private OVRCameraRig cameraRig_;
|
||||
|
||||
private Coroutine updateKeyboardRoutine_;
|
||||
|
||||
private BoxCollider keyboardBoundingBox_;
|
||||
|
||||
private float staleTimeoutCounter_ = 0f;
|
||||
private const float STALE_TIMEOUT = 10f;
|
||||
private float reacquisitionTimer_ = 0f;
|
||||
private float sendFilteredPoseEventTimer_ = 0f;
|
||||
private int skippedPoseCount_ = 0;
|
||||
private const float FILTERED_POSE_TIMEOUT = 15f;
|
||||
|
||||
// Exponentially-weighted average filter (EWA), smooths out changes in keyboard tracking over time
|
||||
private Vector3? EWAPosition = null;
|
||||
private Quaternion? EWARotation = null;
|
||||
private float HAND_HEIGHT_TUNING = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether rolling average filter and keyboard angle filters are applied.
|
||||
/// If true, keyboard will be shown in latest tracked position at all times.
|
||||
/// </summary>
|
||||
[HideInInspector]
|
||||
public bool UseHeuristicRollback = false;
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
cameraRig_ = FindObjectOfType<OVRCameraRig>();
|
||||
|
||||
SystemKeyboardInfo = new OVRKeyboard.TrackedKeyboardInfo
|
||||
{
|
||||
Name = "None",
|
||||
Dimensions = new Vector3(0f, 0f, 0f),
|
||||
Identifier = uint.MaxValue
|
||||
};
|
||||
|
||||
yield return InitializeHandPresenceData();
|
||||
|
||||
yield return UpdateTrackingStateCoroutine();
|
||||
}
|
||||
|
||||
private IEnumerator InitializeHandPresenceData()
|
||||
{
|
||||
GameObject ovrCameraRig = GameObject.Find("OVRCameraRig");
|
||||
if (ovrCameraRig == null)
|
||||
{
|
||||
Debug.LogError("Scene does not contain an OVRCameraRig");
|
||||
yield break;
|
||||
}
|
||||
|
||||
projectedPassthroughOpaque_ = ovrCameraRig.AddComponent<OVROverlay>();
|
||||
|
||||
projectedPassthroughOpaque_.currentOverlayShape = OVROverlay.OverlayShape.KeyboardHandsPassthrough;
|
||||
|
||||
projectedPassthroughOpaque_.hidden = true;
|
||||
projectedPassthroughOpaque_.gameObject.SetActive(true);
|
||||
|
||||
ProjectedPassthroughKeyLabel.hidden = true;
|
||||
ProjectedPassthroughKeyLabel.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
void RegisterPassthroughMeshToSDK()
|
||||
{
|
||||
if (ProjectedPassthroughKeyLabel.IsSurfaceGeometry(projectedPassthroughMesh.gameObject))
|
||||
{
|
||||
ProjectedPassthroughKeyLabel.RemoveSurfaceGeometry(projectedPassthroughMesh.gameObject);
|
||||
}
|
||||
|
||||
ProjectedPassthroughKeyLabel.AddSurfaceGeometry(projectedPassthroughMesh.gameObject, true);
|
||||
}
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance from the given point to the keyboard
|
||||
/// </summary>
|
||||
/// <param name="point">A 3D vector coordinate to use as the reference point</param>
|
||||
/// <returns>A floating point value that is the distance to intersect within the keyboard bounds</returns>
|
||||
public float GetDistanceToKeyboard(Vector3 point)
|
||||
{
|
||||
if (keyboardBoundingBox_ == null)
|
||||
{
|
||||
return Mathf.Infinity;
|
||||
}
|
||||
if (keyboardBoundingBox_.bounds.Contains(point))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
var closestPointToKb = keyboardBoundingBox_.ClosestPointOnBounds(point);
|
||||
var pointToKeyboard = closestPointToKb - point;
|
||||
RaycastHit hitInfo;
|
||||
bool didHit = keyboardBoundingBox_.Raycast(
|
||||
new Ray(point, pointToKeyboard),
|
||||
out hitInfo,
|
||||
Mathf.Infinity);
|
||||
return didHit ? hitInfo.distance : Mathf.Infinity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes an Android broadcast to launch a keyboard selection dialog for local keyboard type.
|
||||
/// </summary>
|
||||
public void LaunchLocalKeyboardSelectionDialog()
|
||||
{
|
||||
LaunchOverlayIntent("systemux://dialog/set-local-physical-tracked-keyboard");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes an Android broadcast to launch a keyboard selection dialog for remote keyboard type.
|
||||
/// </summary>
|
||||
public void LaunchRemoteKeyboardSelectionDialog()
|
||||
{
|
||||
LaunchOverlayIntent("systemux://dialog/set-remote-physical-tracked-keyboard");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Helpers
|
||||
private bool KeyboardTrackerIsRunning()
|
||||
{
|
||||
return (TrackingState != TrackedKeyboardState.NoTrackableKeyboard
|
||||
&& TrackingState != TrackedKeyboardState.Offline);
|
||||
}
|
||||
|
||||
private IEnumerator UpdateTrackingStateCoroutine()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
// On Link this is called before initialization.
|
||||
//We don't want this on our normal flow because it breaks our tests.
|
||||
#if !UNITY_ANDROID && !UNITY_EDITOR
|
||||
if(OVRPlugin.initialized) {
|
||||
#endif
|
||||
OVRKeyboard.TrackedKeyboardInfo keyboardInfo;
|
||||
if (OVRKeyboard.GetSystemKeyboardInfo(KeyboardQueryFlags, out keyboardInfo))
|
||||
{
|
||||
bool systemKeyboardSwitched = false;
|
||||
if (SystemKeyboardInfo.Identifier != keyboardInfo.Identifier || SystemKeyboardInfo.KeyboardFlags != keyboardInfo.KeyboardFlags)
|
||||
{
|
||||
Debug.Log(String.Format("New System keyboard info: [{0}] {1} (Flags {2}) ({3} {4})",
|
||||
keyboardInfo.Identifier, keyboardInfo.Name,
|
||||
keyboardInfo.KeyboardFlags,
|
||||
(keyboardInfo.SupportedPresentationStyles & OVRPlugin.TrackedKeyboardPresentationStyles.Opaque) != 0 ? "Supports Opaque" : "",
|
||||
(keyboardInfo.SupportedPresentationStyles & OVRPlugin.TrackedKeyboardPresentationStyles.KeyLabel) != 0 ? "Supports Key Label" : ""));
|
||||
if (TrackingState == TrackedKeyboardState.NoTrackableKeyboard){
|
||||
SetKeyboardState(TrackedKeyboardState.Offline);
|
||||
}
|
||||
SystemKeyboardInfo = keyboardInfo;
|
||||
systemKeyboardSwitched = true;
|
||||
}
|
||||
|
||||
bool keyboardExists = (keyboardInfo.KeyboardFlags & OVRPlugin.TrackedKeyboardFlags.Exists) != 0;
|
||||
if (keyboardExists && trackingEnabled)
|
||||
{
|
||||
bool localKeyboard = (keyboardInfo.KeyboardFlags & OVRPlugin.TrackedKeyboardFlags.Local) != 0;
|
||||
bool remoteKeyboard = (keyboardInfo.KeyboardFlags & OVRPlugin.TrackedKeyboardFlags.Remote) != 0;
|
||||
bool connectedKeyboard = (keyboardInfo.KeyboardFlags & OVRPlugin.TrackedKeyboardFlags.Connected) != 0;
|
||||
bool shouldBeRunning = remoteKeyboard || (localKeyboard && (!connectionRequired || connectedKeyboard));
|
||||
|
||||
if(KeyboardTrackerIsRunning() && (systemKeyboardSwitched || !shouldBeRunning))
|
||||
{
|
||||
StopKeyboardTrackingInternal();
|
||||
}
|
||||
|
||||
if(!KeyboardTrackerIsRunning() && shouldBeRunning)
|
||||
{
|
||||
yield return StartKeyboardTrackingCoroutine();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (KeyboardTrackerIsRunning()){
|
||||
StopKeyboardTrackingInternal();
|
||||
}
|
||||
|
||||
if (!keyboardExists)
|
||||
{
|
||||
SetKeyboardState(TrackedKeyboardState.NoTrackableKeyboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (KeyboardTrackerIsRunning()){
|
||||
StopKeyboardTrackingInternal();
|
||||
}
|
||||
SetKeyboardState(TrackedKeyboardState.ErrorExtensionFailed);
|
||||
}
|
||||
SystemKeyboardInfo = keyboardInfo;
|
||||
#if !UNITY_ANDROID && !UNITY_EDITOR
|
||||
}
|
||||
#endif
|
||||
yield return new WaitForSeconds(.1f);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator StartKeyboardTrackingCoroutine()
|
||||
{
|
||||
if (KeyboardTrackerIsRunning())
|
||||
{
|
||||
Debug.Log("StartKeyboardTracking(): Keyboard already being tracked");
|
||||
yield break;
|
||||
}
|
||||
|
||||
Assert.IsTrue(
|
||||
!KeyboardTrackerIsRunning()
|
||||
&& activeKeyboardMesh_ == null
|
||||
&& activeKeyboardRenderers_ == null
|
||||
&& updateKeyboardRoutine_ == null,
|
||||
$"State: {TrackingState}, Mesh: {activeKeyboardMesh_}, Coroutine: {updateKeyboardRoutine_}");
|
||||
|
||||
InitializeKeyboardInfo();
|
||||
RegisterPassthroughMeshToSDK();
|
||||
|
||||
Debug.Log("Calling StartKeyboardTracking with id " + SystemKeyboardInfo.Identifier);
|
||||
|
||||
if (!OVRPlugin.StartKeyboardTracking(SystemKeyboardInfo.Identifier))
|
||||
{
|
||||
Debug.LogWarning("OVRKeyboard.StartKeyboardTracking Failed");
|
||||
yield break;
|
||||
}
|
||||
|
||||
projectedPassthroughRoot.localScale = new Vector3 { x = SystemKeyboardInfo.Dimensions.x * underlayScaleMultX_, y = underlayScaleConstY_, z = SystemKeyboardInfo.Dimensions.z * underlayScaleMultZ_ };
|
||||
|
||||
currentKeyboardPresentationStyles = SystemKeyboardInfo.SupportedPresentationStyles;
|
||||
ActiveKeyboardInfo = SystemKeyboardInfo;
|
||||
LoadKeyboardMesh();
|
||||
updateKeyboardRoutine_ = StartCoroutine(UpdateKeyboardPose());
|
||||
EWAPosition = null;
|
||||
EWARotation = null;
|
||||
|
||||
TrackedKeyboardActiveChanged?.Invoke(new TrackedKeyboardSetActiveEvent(isEnabled: true));
|
||||
SetKeyboardState(TrackedKeyboardState.StartedNotTracked);
|
||||
}
|
||||
|
||||
private void StopKeyboardTrackingInternal()
|
||||
{
|
||||
if (!KeyboardTrackerIsRunning() || updateKeyboardRoutine_ == null)
|
||||
{
|
||||
SetKeyboardState(TrackedKeyboardState.Offline);
|
||||
return;
|
||||
}
|
||||
|
||||
projectedPassthroughOpaque_.hidden = true;
|
||||
ProjectedPassthroughKeyLabel.hidden = true;
|
||||
|
||||
TrackedKeyboardActiveChanged?.Invoke(new TrackedKeyboardSetActiveEvent(isEnabled: false));
|
||||
|
||||
Debug.Log($"StopKeyboardTracking {ActiveKeyboardInfo.Name}");
|
||||
|
||||
StopCoroutine(updateKeyboardRoutine_);
|
||||
updateKeyboardRoutine_ = null;
|
||||
|
||||
OVRKeyboard.StopKeyboardTracking(ActiveKeyboardInfo);
|
||||
InitializeKeyboardInfo();
|
||||
|
||||
if (activeKeyboardMesh_ != null)
|
||||
{
|
||||
Destroy(activeKeyboardMesh_.gameObject);
|
||||
activeKeyboardMesh_ = null;
|
||||
activeKeyboardRenderers_ = null;
|
||||
keyboardBoundingBox_ = null;
|
||||
}
|
||||
|
||||
SetKeyboardState(TrackedKeyboardState.Offline);
|
||||
}
|
||||
|
||||
private IEnumerator UpdateKeyboardPose()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
transform.position = cameraRig_.trackingSpace.transform.position;
|
||||
transform.rotation = cameraRig_.trackingSpace.transform.rotation;
|
||||
|
||||
var poseState = OVRKeyboard.GetKeyboardState();
|
||||
TrackedKeyboardState keyboardState = TrackedKeyboardState.StartedNotTracked;
|
||||
|
||||
if (poseState.isPositionValid)
|
||||
{
|
||||
if (poseState.isPositionTracked && activeKeyboardMesh_ != null)
|
||||
{
|
||||
float keyboardAngleFilter = UseHeuristicRollback ? 360f : 20f;
|
||||
float ewaAlpha = UseHeuristicRollback ? 0f : 0.65f;
|
||||
|
||||
var worldRotation = transform.rotation * poseState.rotation;
|
||||
var upRotated = worldRotation * Vector3.up;
|
||||
CurrentKeyboardAngleFromUp = Vector3.Angle(upRotated, Vector3.up);
|
||||
|
||||
if (CurrentKeyboardAngleFromUp < keyboardAngleFilter)
|
||||
{
|
||||
if (!EWAPosition.HasValue)
|
||||
{
|
||||
EWAPosition = poseState.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
EWAPosition = ewaAlpha * EWAPosition + (1f - ewaAlpha) * poseState.position;
|
||||
}
|
||||
|
||||
if (!EWARotation.HasValue)
|
||||
{
|
||||
EWARotation = poseState.rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
EWARotation = Quaternion.Slerp(EWARotation.Value, poseState.rotation, 1f - ewaAlpha);
|
||||
}
|
||||
|
||||
ActiveKeyboardTransform.localPosition = EWAPosition.Value;
|
||||
ActiveKeyboardTransform.localRotation = EWARotation.Value;
|
||||
|
||||
projectedPassthroughRoot.localPosition = EWAPosition.Value + underlayOffset_ + new Vector3(0f, HAND_HEIGHT_TUNING, 0f);
|
||||
projectedPassthroughRoot.localRotation = EWARotation.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
skippedPoseCount_++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
keyboardState = poseState.isPositionTracked
|
||||
? TrackedKeyboardState.Valid
|
||||
: TrackedKeyboardState.Stale;
|
||||
}
|
||||
|
||||
SetKeyboardState(keyboardState);
|
||||
UpdateSkippedPoseTimer();
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSkippedPoseTimer()
|
||||
{
|
||||
sendFilteredPoseEventTimer_ += Time.deltaTime;
|
||||
if (sendFilteredPoseEventTimer_ > FILTERED_POSE_TIMEOUT
|
||||
&& skippedPoseCount_ > 0)
|
||||
{
|
||||
// dispatcher_.Dispatch(new TrackedKeyboardSkippedPoseEvent(skippedPoseCount_));
|
||||
skippedPoseCount_ = 0;
|
||||
sendFilteredPoseEventTimer_ = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadKeyboardMesh()
|
||||
{
|
||||
Debug.Log("LoadKeyboardMesh");
|
||||
activeKeyboardMesh_ = LoadRuntimeKeyboardMesh();
|
||||
if(activeKeyboardMesh_ == null) {
|
||||
Debug.LogError("Failed to load keyboard mesh.");
|
||||
SetKeyboardState(TrackedKeyboardState.Error);
|
||||
}
|
||||
keyboardBoundingBox_ = activeKeyboardMesh_.AddComponent<BoxCollider>();
|
||||
|
||||
keyboardBoundingBox_.center =
|
||||
new Vector3(0.0f, ActiveKeyboardInfo.Dimensions.y / 2.0f, 0.0f);
|
||||
keyboardBoundingBox_.size =
|
||||
new Vector3(ActiveKeyboardInfo.Dimensions.x,
|
||||
ActiveKeyboardInfo.Dimensions.y + boundingBoxAboveKeyboardY_,
|
||||
ActiveKeyboardInfo.Dimensions.z);
|
||||
|
||||
activeKeyboardMeshRenderer_ = activeKeyboardMesh_.GetComponentInChildren<MeshRenderer>();
|
||||
opaqueShader_ = activeKeyboardMeshRenderer_.material.shader;
|
||||
activeKeyboardMeshRenderer_.material.shader = KeyLabelModeShader;
|
||||
|
||||
passthroughQuad_ = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||
passthroughQuad_.transform.localPosition = new Vector3(0.0f, -0.01f, 0.0f);
|
||||
passthroughQuad_.transform.parent = activeKeyboardMesh_.transform;
|
||||
passthroughQuad_.transform.localRotation = Quaternion.Euler(90.0f, 0.0f, 0.0f);
|
||||
float borderSize = ActiveKeyboardInfo.Dimensions.x * PassthroughBorderMultiplier;
|
||||
passthroughQuad_.transform.localScale = new Vector3(
|
||||
ActiveKeyboardInfo.Dimensions.x + borderSize,
|
||||
ActiveKeyboardInfo.Dimensions.z + borderSize,
|
||||
ActiveKeyboardInfo.Dimensions.y);
|
||||
|
||||
MeshRenderer meshRenderer = passthroughQuad_.GetComponent<MeshRenderer>();
|
||||
meshRenderer.material.shader = PassthroughShader;
|
||||
|
||||
GameObject parent = new GameObject();
|
||||
activeKeyboardMesh_.transform.parent = parent.transform;
|
||||
activeKeyboardMesh_ = parent;
|
||||
|
||||
activeKeyboardRenderers_ = activeKeyboardMesh_.GetComponentsInChildren<MeshRenderer>();
|
||||
activeKeyboardMesh_.transform.SetParent(ActiveKeyboardTransform, worldPositionStays: false);
|
||||
|
||||
UpdateKeyboardVisibility();
|
||||
}
|
||||
|
||||
void UpdatePresentation(bool isVisible)
|
||||
{
|
||||
KeyboardPresentation presentationToUse = Presentation;
|
||||
if(currentKeyboardPresentationStyles != 0) {
|
||||
if (Presentation == KeyboardPresentation.PreferOpaque && (currentKeyboardPresentationStyles & OVRPlugin.TrackedKeyboardPresentationStyles.Opaque) == 0) {
|
||||
if((currentKeyboardPresentationStyles & OVRPlugin.TrackedKeyboardPresentationStyles.KeyLabel) != 0) {
|
||||
presentationToUse = KeyboardPresentation.PreferKeyLabels;
|
||||
}
|
||||
}
|
||||
else if (Presentation == KeyboardPresentation.PreferKeyLabels && (currentKeyboardPresentationStyles & OVRPlugin.TrackedKeyboardPresentationStyles.KeyLabel) == 0) {
|
||||
if((currentKeyboardPresentationStyles & OVRPlugin.TrackedKeyboardPresentationStyles.Opaque) != 0) {
|
||||
presentationToUse = KeyboardPresentation.PreferOpaque;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isVisible) {
|
||||
projectedPassthroughOpaque_.hidden = true;
|
||||
ProjectedPassthroughKeyLabel.hidden = true;
|
||||
} else if (presentationToUse == KeyboardPresentation.PreferOpaque) {
|
||||
activeKeyboardMeshRenderer_.material.shader = opaqueShader_;
|
||||
passthroughQuad_.SetActive(false);
|
||||
projectedPassthroughOpaque_.hidden = !GetKeyboardVisibility() || !HandsOverKeyboard;
|
||||
ProjectedPassthroughKeyLabel.hidden = true;
|
||||
} else {
|
||||
activeKeyboardMeshRenderer_.material.shader = KeyLabelModeShader;
|
||||
passthroughQuad_.SetActive(true);
|
||||
projectedPassthroughOpaque_.hidden = true;
|
||||
ProjectedPassthroughKeyLabel.hidden = false; // Always shown
|
||||
}
|
||||
}
|
||||
|
||||
private GameObject LoadRuntimeKeyboardMesh()
|
||||
{
|
||||
Debug.Log("LoadRuntimekeyboardMesh");
|
||||
string[] modelPaths = OVRPlugin.GetRenderModelPaths();
|
||||
if (modelPaths != null)
|
||||
{
|
||||
for (int i = 0; i < modelPaths.Length; i++)
|
||||
{
|
||||
if (modelPaths[i].Equals("/model_fb/keyboard/local"))
|
||||
{
|
||||
OVRPlugin.RenderModelProperties modelProps = new OVRPlugin.RenderModelProperties();
|
||||
if (OVRPlugin.GetRenderModelProperties(modelPaths[i], ref modelProps))
|
||||
{
|
||||
if (modelProps.ModelKey != OVRPlugin.RENDER_MODEL_NULL_KEY)
|
||||
{
|
||||
byte[] data = OVRPlugin.LoadRenderModel(modelProps.ModelKey);
|
||||
if (data != null)
|
||||
{
|
||||
OVRGLTFLoader gltfLoader = new OVRGLTFLoader(data);
|
||||
gltfLoader.SetModelShader(keyboardModelShader);
|
||||
OVRGLTFScene scene = gltfLoader.LoadGLB(false);
|
||||
return scene.root;
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug.LogError("Failed to load model. Ensure that the correct keyboard is connected.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug.LogError("Failed to find keyboard model.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal only. Updates rendering of keyboard based on its current visibility.
|
||||
/// </summary>
|
||||
public void UpdateKeyboardVisibility()
|
||||
{
|
||||
var isVisible = GetKeyboardVisibility();
|
||||
UpdatePresentation(isVisible);
|
||||
|
||||
if (activeKeyboardRenderers_ == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderer in activeKeyboardRenderers_)
|
||||
{
|
||||
renderer.enabled = isVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetKeyboardState(TrackedKeyboardState state)
|
||||
{
|
||||
var oldState = TrackingState;
|
||||
TrackingState = state;
|
||||
|
||||
bool timedOut = false;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TrackedKeyboardState.Stale:
|
||||
if (!HandsOverKeyboard)
|
||||
{
|
||||
staleTimeoutCounter_ += Time.deltaTime;
|
||||
timedOut = staleTimeoutCounter_ - STALE_TIMEOUT > 0f;
|
||||
|
||||
if (timedOut) {
|
||||
reacquisitionTimer_ += Time.deltaTime;
|
||||
EWAPosition = null;
|
||||
EWARotation = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reacquisitionTimer_ = 0f;
|
||||
staleTimeoutCounter_ = 0f;
|
||||
}
|
||||
break;
|
||||
case TrackedKeyboardState.Valid:
|
||||
staleTimeoutCounter_ = 0f;
|
||||
|
||||
if (oldState == TrackedKeyboardState.Stale
|
||||
&& reacquisitionTimer_ > 0f)
|
||||
{
|
||||
// dispatcher_.Dispatch(new TrackedKeyboardReacquiredEvent(reacquisitionTimer_));
|
||||
}
|
||||
break;
|
||||
case TrackedKeyboardState.StartedNotTracked:
|
||||
case TrackedKeyboardState.NoTrackableKeyboard:
|
||||
case TrackedKeyboardState.Offline:
|
||||
reacquisitionTimer_ = 0f;
|
||||
staleTimeoutCounter_ = 0f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldState != state || timedOut)
|
||||
{
|
||||
DispatchVisibilityEvent(timedOut);
|
||||
}
|
||||
|
||||
UpdateKeyboardVisibility();
|
||||
}
|
||||
|
||||
private bool GetKeyboardVisibility()
|
||||
{
|
||||
switch (TrackingState)
|
||||
{
|
||||
case TrackedKeyboardState.Stale:
|
||||
if (!HandsOverKeyboard)
|
||||
{
|
||||
return !(staleTimeoutCounter_ - STALE_TIMEOUT > 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
case TrackedKeyboardState.Valid:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InitializeKeyboardInfo()
|
||||
{
|
||||
ActiveKeyboardInfo = new OVRKeyboard.TrackedKeyboardInfo
|
||||
{
|
||||
Name = "None",
|
||||
Dimensions = new Vector3(0f, 0f, 0f),
|
||||
Identifier = uint.MaxValue
|
||||
};
|
||||
}
|
||||
|
||||
private void LaunchOverlayIntent(String dataUri)
|
||||
{
|
||||
AndroidJavaObject activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
||||
AndroidJavaObject currentActivity = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
|
||||
var intent = new AndroidJavaObject("android.content.Intent");
|
||||
|
||||
intent.Call<AndroidJavaObject>("setPackage", "com.oculus.vrshell");
|
||||
intent.Call<AndroidJavaObject>("setAction", "com.oculus.vrshell.intent.action.LAUNCH");
|
||||
intent.Call<AndroidJavaObject>("putExtra", "intent_data", dataUri);
|
||||
|
||||
// Broadcast instead of starting activity, so that it goes to overlay
|
||||
currentActivity.Call("sendBroadcast", intent);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Stops keyboard tracking and cleans up associated resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (KeyboardTrackerIsRunning())
|
||||
{
|
||||
StopKeyboardTrackingInternal();
|
||||
}
|
||||
|
||||
if (ProjectedPassthroughKeyLabel.IsSurfaceGeometry(projectedPassthroughMesh.gameObject))
|
||||
{
|
||||
ProjectedPassthroughKeyLabel.RemoveSurfaceGeometry(projectedPassthroughMesh.gameObject);
|
||||
}
|
||||
|
||||
if (activeKeyboardMesh_ != null)
|
||||
{
|
||||
Destroy(activeKeyboardMesh_.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatchVisibilityEvent(bool timeOut)
|
||||
{
|
||||
TrackedKeyboardVisibilityChanged?.Invoke(
|
||||
new TrackedKeyboardVisibilityChangedEvent(ActiveKeyboardInfo.Name, TrackingState, timeOut));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event sent when tracked keyboard changes visibility (passes in or out of camera view).
|
||||
/// </summary>
|
||||
public struct TrackedKeyboardVisibilityChangedEvent
|
||||
{
|
||||
public readonly string ActiveKeyboardName;
|
||||
public readonly TrackedKeyboardState State;
|
||||
public readonly bool TrackingTimeout;
|
||||
|
||||
public TrackedKeyboardVisibilityChangedEvent(string keyboardModel, TrackedKeyboardState state, bool timeout)
|
||||
{
|
||||
ActiveKeyboardName = keyboardModel;
|
||||
State = state;
|
||||
TrackingTimeout = timeout;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event sent when tracked keyboard starts or stops actively tracking.
|
||||
/// </summary>
|
||||
public struct TrackedKeyboardSetActiveEvent
|
||||
{
|
||||
public readonly bool IsEnabled;
|
||||
|
||||
public TrackedKeyboardSetActiveEvent(bool isEnabled)
|
||||
{
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cde6014a6f09af34c934c03defe61f3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,502 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
public class OVRTrackedKeyboardHands : MonoBehaviour
|
||||
{
|
||||
public GameObject LeftHandPresence;
|
||||
public GameObject RightHandPresence;
|
||||
private bool handPresenceInitialized_ = false;
|
||||
|
||||
private Transform leftHandRoot_;
|
||||
private Transform rightHandRoot_;
|
||||
|
||||
public OVRTrackedKeyboard KeyboardTracker;
|
||||
|
||||
private OVRCameraRig cameraRig_;
|
||||
private OVRHand leftHand_;
|
||||
private OVRSkeleton leftHandSkeleton_;
|
||||
private OVRSkeletonRenderer leftHandSkeletonRenderer_;
|
||||
private GameObject leftHandSkeletonRendererGO_;
|
||||
private SkinnedMeshRenderer leftHandSkinnedMeshRenderer_;
|
||||
private OVRMeshRenderer leftHandMeshRenderer_;
|
||||
private OVRHand rightHand_;
|
||||
private OVRSkeleton rightHandSkeleton_;
|
||||
private OVRSkeletonRenderer rightHandSkeletonRenderer_;
|
||||
private GameObject rightHandSkeletonRendererGO_;
|
||||
private OVRMeshRenderer rightHandMeshRenderer_;
|
||||
private SkinnedMeshRenderer rightHandSkinnedMeshRenderer_;
|
||||
|
||||
public bool RightHandOverKeyboard { get; private set; } = false;
|
||||
public bool LeftHandOverKeyboard { get; private set; } = false;
|
||||
|
||||
private static readonly float handInnerAlphaThreshold_ = 0.08f;
|
||||
private static readonly float handOuterAlphaThreshold_ = 0.20f;
|
||||
private static readonly float maximumPassthroughHandsDistance_ = 0.18f;
|
||||
private static readonly float minimumModelHandsDistance_ = 0.11f;
|
||||
|
||||
private TrackedKeyboardHandsVisibilityChangedEvent? lastVisibilityEvent_ = null;
|
||||
|
||||
private struct HandBoneMapping
|
||||
{
|
||||
public Transform LeftHandTransform;
|
||||
public Transform LeftPresenceTransform;
|
||||
public Transform RightHandTransform;
|
||||
public Transform RightPresenceTransform;
|
||||
|
||||
public OVRSkeleton.BoneId BoneName;
|
||||
public string HandPresenceLeftBoneName;
|
||||
public string HandPresenceRightBoneName;
|
||||
};
|
||||
|
||||
private readonly HandBoneMapping[] boneMappings_ = new HandBoneMapping[]
|
||||
{
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_WristRoot,
|
||||
HandPresenceLeftBoneName = "b_l_wrist",
|
||||
HandPresenceRightBoneName = "b_r_wrist"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Thumb0,
|
||||
HandPresenceLeftBoneName = "b_l_thumb0",
|
||||
HandPresenceRightBoneName = "b_r_thumb0"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Thumb1,
|
||||
HandPresenceLeftBoneName = "b_l_thumb1",
|
||||
HandPresenceRightBoneName = "b_r_thumb1"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Thumb2,
|
||||
HandPresenceLeftBoneName = "b_l_thumb2",
|
||||
HandPresenceRightBoneName = "b_r_thumb2"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Thumb3,
|
||||
HandPresenceLeftBoneName = "b_l_thumb3",
|
||||
HandPresenceRightBoneName = "b_r_thumb3"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Index1,
|
||||
HandPresenceLeftBoneName = "b_l_index1",
|
||||
HandPresenceRightBoneName = "b_r_index1"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Index2,
|
||||
HandPresenceLeftBoneName = "b_l_index2",
|
||||
HandPresenceRightBoneName = "b_r_index2"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Index3,
|
||||
HandPresenceLeftBoneName = "b_l_index3",
|
||||
HandPresenceRightBoneName = "b_r_index3"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Middle1,
|
||||
HandPresenceLeftBoneName = "b_l_middle1",
|
||||
HandPresenceRightBoneName = "b_r_middle1"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Middle2,
|
||||
HandPresenceLeftBoneName = "b_l_middle2",
|
||||
HandPresenceRightBoneName = "b_r_middle2"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Middle3,
|
||||
HandPresenceLeftBoneName = "b_l_middle3",
|
||||
HandPresenceRightBoneName = "b_r_middle3"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Ring1,
|
||||
HandPresenceLeftBoneName = "b_l_ring1",
|
||||
HandPresenceRightBoneName = "b_r_ring1"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Ring2,
|
||||
HandPresenceLeftBoneName = "b_l_ring2",
|
||||
HandPresenceRightBoneName = "b_r_ring2"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Ring3,
|
||||
HandPresenceLeftBoneName = "b_l_ring3",
|
||||
HandPresenceRightBoneName = "b_r_ring3"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Pinky0,
|
||||
HandPresenceLeftBoneName = "b_l_pinky0",
|
||||
HandPresenceRightBoneName = "b_r_pinky0"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Pinky1,
|
||||
HandPresenceLeftBoneName = "b_l_pinky1",
|
||||
HandPresenceRightBoneName = "b_r_pinky1"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Pinky2,
|
||||
HandPresenceLeftBoneName = "b_l_pinky2",
|
||||
HandPresenceRightBoneName = "b_r_pinky2"
|
||||
},
|
||||
new HandBoneMapping
|
||||
{
|
||||
BoneName = OVRSkeleton.BoneId.Hand_Pinky3,
|
||||
HandPresenceLeftBoneName = "b_l_pinky3",
|
||||
HandPresenceRightBoneName = "b_r_pinky3"
|
||||
}
|
||||
};
|
||||
|
||||
public Material HandsMaterial;
|
||||
|
||||
#region MATERIAL PROPERTIES
|
||||
|
||||
private const float XSCALE = 0.73f;
|
||||
private const float YSCALE = 0.8f;
|
||||
private const float FORWARD_OFFSET = -0.02f;
|
||||
|
||||
private int keyboardPositionID_;
|
||||
private int keyboardRotationID_;
|
||||
private int keyboardScaleID_;
|
||||
|
||||
#endregion
|
||||
|
||||
private void Awake() {
|
||||
KeyboardTracker.TrackedKeyboardActiveChanged += TrackedKeyboardActiveUpdated;
|
||||
KeyboardTracker.TrackedKeyboardVisibilityChanged += TrackedKeyboardVisibilityChanged;
|
||||
|
||||
keyboardPositionID_ = Shader.PropertyToID("_KeyboardPosition");
|
||||
keyboardRotationID_ = Shader.PropertyToID("_KeyboardRotation");
|
||||
keyboardScaleID_ = Shader.PropertyToID("_KeyboardScale");
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
enabled = false;
|
||||
|
||||
cameraRig_ = FindObjectOfType<OVRCameraRig>();
|
||||
leftHand_ = cameraRig_.leftHandAnchor.GetComponentInChildren<OVRHand>();
|
||||
rightHand_ = cameraRig_.rightHandAnchor.GetComponentInChildren<OVRHand>();
|
||||
leftHandSkeleton_ = leftHand_.GetComponent<OVRSkeleton>();
|
||||
rightHandSkeleton_ = rightHand_.GetComponent<OVRSkeleton>();
|
||||
|
||||
leftHandMeshRenderer_ = leftHand_.GetComponent<OVRMeshRenderer>();
|
||||
rightHandMeshRenderer_ = rightHand_.GetComponent<OVRMeshRenderer>();
|
||||
|
||||
leftHandSkeletonRenderer_ = leftHand_.GetComponent<OVRSkeletonRenderer>();
|
||||
rightHandSkeletonRenderer_ = rightHand_.GetComponent<OVRSkeletonRenderer>();
|
||||
if (!leftHandSkeletonRenderer_.enabled)
|
||||
{
|
||||
// App is not using skeleton renderer
|
||||
leftHandSkeletonRenderer_ = null;
|
||||
rightHandSkeletonRenderer_ = null;
|
||||
}
|
||||
|
||||
leftHandSkinnedMeshRenderer_ = leftHand_.GetComponent<SkinnedMeshRenderer>();
|
||||
rightHandSkinnedMeshRenderer_ = rightHand_.GetComponent<SkinnedMeshRenderer>();
|
||||
|
||||
var leftHand = GameObject.Instantiate(LeftHandPresence);
|
||||
var rightHand = GameObject.Instantiate(RightHandPresence);
|
||||
leftHandRoot_ = leftHand.transform;
|
||||
rightHandRoot_ = rightHand.transform;
|
||||
|
||||
leftHand.SetActive(false);
|
||||
rightHand.SetActive(false);
|
||||
|
||||
#if !UNITY_EDITOR // GameObject trees for hands only available on-device
|
||||
RetargetHandTrackingToHandPresence();
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool AreControllersActive =>
|
||||
!(leftHand_.IsTracked || rightHand_.IsTracked);
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (AreControllersActive)
|
||||
{
|
||||
DisableHandObjects();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var boneEntry in boneMappings_)
|
||||
{
|
||||
boneEntry.LeftPresenceTransform.localRotation = boneEntry.LeftHandTransform.localRotation;
|
||||
|
||||
boneEntry.RightPresenceTransform.localRotation = boneEntry.RightHandTransform.localRotation;
|
||||
|
||||
if (boneEntry.BoneName == OVRSkeleton.BoneId.Hand_WristRoot)
|
||||
{
|
||||
boneEntry.LeftPresenceTransform.rotation = boneEntry.LeftHandTransform.rotation;
|
||||
|
||||
boneEntry.RightPresenceTransform.rotation = boneEntry.RightHandTransform.rotation;
|
||||
|
||||
var leftScale = leftHand_.HandScale;
|
||||
var rightScale = rightHand_.HandScale;
|
||||
|
||||
boneEntry.RightPresenceTransform.localScale = new Vector3(rightScale, rightScale, rightScale);
|
||||
boneEntry.LeftPresenceTransform.localScale = new Vector3(leftScale, leftScale, leftScale);
|
||||
}
|
||||
}
|
||||
rightHandRoot_.position = rightHand_.transform.position;
|
||||
rightHandRoot_.rotation = rightHand_.transform.rotation;
|
||||
|
||||
leftHandRoot_.position = leftHand_.transform.position;
|
||||
leftHandRoot_.rotation = leftHand_.transform.rotation;
|
||||
|
||||
var leftHandDistance = GetHandDistanceToKeyboard(leftHandSkeleton_);
|
||||
var rightHandDistance = GetHandDistanceToKeyboard(rightHandSkeleton_);
|
||||
|
||||
LeftHandOverKeyboard = ShouldEnablePassthrough(leftHandDistance);
|
||||
RightHandOverKeyboard = ShouldEnablePassthrough(rightHandDistance);
|
||||
|
||||
KeyboardTracker.HandsOverKeyboard = RightHandOverKeyboard || LeftHandOverKeyboard;
|
||||
|
||||
var enableLeftModel = ShouldEnableModel(leftHandDistance);
|
||||
var enableRightModel = ShouldEnableModel(rightHandDistance);
|
||||
SetHandModelsEnabled(enableLeftModel, enableRightModel);
|
||||
|
||||
if (KeyboardTracker.Presentation == OVRTrackedKeyboard.KeyboardPresentation.PreferOpaque)
|
||||
{
|
||||
// Used mixed reality service hands
|
||||
leftHandRoot_.gameObject.SetActive(false);
|
||||
rightHandRoot_.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
leftHandRoot_.gameObject.SetActive(LeftHandOverKeyboard);
|
||||
rightHandRoot_.gameObject.SetActive(RightHandOverKeyboard);
|
||||
}
|
||||
|
||||
var position = KeyboardTracker.ActiveKeyboardTransform?.position;
|
||||
var rotation = KeyboardTracker.ActiveKeyboardTransform?.rotation;
|
||||
var offset = KeyboardTracker.ActiveKeyboardTransform == null
|
||||
? Vector3.zero
|
||||
: KeyboardTracker.ActiveKeyboardTransform.forward * FORWARD_OFFSET;
|
||||
|
||||
HandsMaterial.SetVector(keyboardPositionID_, position.HasValue ? position.Value + offset : Vector3.zero);
|
||||
HandsMaterial.SetVector(keyboardRotationID_, rotation.HasValue ? rotation.Value.eulerAngles : Vector3.zero);
|
||||
HandsMaterial.SetVector(
|
||||
keyboardScaleID_,
|
||||
new Vector4(
|
||||
KeyboardTracker.ActiveKeyboardInfo.Dimensions.x * XSCALE,
|
||||
0.1f,
|
||||
KeyboardTracker.ActiveKeyboardInfo.Dimensions.z * YSCALE,
|
||||
1f
|
||||
)
|
||||
);
|
||||
|
||||
if (lastVisibilityEvent_ == null
|
||||
|| LeftHandOverKeyboard != lastVisibilityEvent_.Value.leftVisible
|
||||
|| RightHandOverKeyboard != lastVisibilityEvent_.Value.rightVisible)
|
||||
{
|
||||
lastVisibilityEvent_ = new TrackedKeyboardHandsVisibilityChangedEvent
|
||||
{
|
||||
leftVisible = LeftHandOverKeyboard,
|
||||
rightVisible = RightHandOverKeyboard
|
||||
};
|
||||
KeyboardTracker.UpdateKeyboardVisibility();
|
||||
}
|
||||
|
||||
if (LeftHandOverKeyboard || RightHandOverKeyboard)
|
||||
{
|
||||
var handsIntensity = new OVRPlugin.InsightPassthroughKeyboardHandsIntensity
|
||||
{
|
||||
LeftHandIntensity =
|
||||
ComputeOpacity(leftHandDistance, handInnerAlphaThreshold_, handOuterAlphaThreshold_),
|
||||
RightHandIntensity =
|
||||
ComputeOpacity(rightHandDistance, handInnerAlphaThreshold_, handOuterAlphaThreshold_)
|
||||
};
|
||||
OVRPlugin.SetInsightPassthroughKeyboardHandsIntensity(KeyboardTracker.PassthroughOverlay.layerId, handsIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldEnablePassthrough(float distance)
|
||||
{
|
||||
return distance <= maximumPassthroughHandsDistance_;
|
||||
}
|
||||
|
||||
private bool ShouldEnableModel(float distance)
|
||||
{
|
||||
return distance >= minimumModelHandsDistance_;
|
||||
}
|
||||
|
||||
private float GetHandDistanceToKeyboard(OVRSkeleton handSkeleton)
|
||||
{
|
||||
// TODO: Switch back to PointerPose once it's working in OpenXR
|
||||
var pinchPosition = handSkeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Index3].Transform.position;
|
||||
var handPosition = handSkeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Middle1].Transform.position;
|
||||
var pinkyPosition = handSkeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Pinky3].Transform.position;
|
||||
|
||||
return Mathf.Min(KeyboardTracker.GetDistanceToKeyboard(pinchPosition),
|
||||
KeyboardTracker.GetDistanceToKeyboard(handPosition),
|
||||
KeyboardTracker.GetDistanceToKeyboard(pinkyPosition));
|
||||
}
|
||||
|
||||
private float ComputeOpacity(float distance, float innerThreshold, float outerThreshold)
|
||||
{
|
||||
return Mathf.Clamp((outerThreshold - distance) / (outerThreshold - innerThreshold), 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private void SetHandModelsEnabled(bool enableLeftModel, bool enableRightModel)
|
||||
{
|
||||
leftHandMeshRenderer_.enabled = enableLeftModel;
|
||||
rightHandMeshRenderer_.enabled = enableRightModel;
|
||||
|
||||
leftHandSkinnedMeshRenderer_.enabled = enableLeftModel;
|
||||
rightHandSkinnedMeshRenderer_.enabled = enableRightModel;
|
||||
|
||||
if (leftHandSkeletonRenderer_ != null)
|
||||
{
|
||||
if (leftHandSkeletonRendererGO_ == null)
|
||||
{
|
||||
leftHandSkeletonRendererGO_ = leftHandSkeletonRenderer_.gameObject.transform.Find("SkeletonRenderer")?.gameObject;
|
||||
rightHandSkeletonRendererGO_ = rightHandSkeletonRenderer_.gameObject.transform.Find("SkeletonRenderer")?.gameObject;
|
||||
}
|
||||
|
||||
if (leftHandSkeletonRendererGO_ != null)
|
||||
{
|
||||
leftHandSkeletonRendererGO_.SetActive(enableLeftModel);
|
||||
}
|
||||
|
||||
if (rightHandSkeletonRendererGO_ != null)
|
||||
{
|
||||
rightHandSkeletonRendererGO_.SetActive(enableRightModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RetargetHandTrackingToHandPresence()
|
||||
{
|
||||
Assert.IsTrue(LeftHandPresence != null && RightHandPresence != null);
|
||||
|
||||
for (int index = 0; index < boneMappings_.Length; index++)
|
||||
{
|
||||
var entry = boneMappings_[index];
|
||||
|
||||
var ovrBoneStringLeft = OVRSkeleton.BoneLabelFromBoneId(OVRSkeleton.SkeletonType.HandLeft, entry.BoneName);
|
||||
var ovrBoneStringRight = OVRSkeleton.BoneLabelFromBoneId(OVRSkeleton.SkeletonType.HandRight, entry.BoneName);
|
||||
|
||||
boneMappings_[index].LeftHandTransform =
|
||||
leftHand_.transform.FindChildRecursive(ovrBoneStringLeft);
|
||||
boneMappings_[index].LeftPresenceTransform = leftHandRoot_.FindChildRecursive(entry.HandPresenceLeftBoneName);
|
||||
|
||||
boneMappings_[index].RightHandTransform =
|
||||
rightHand_.transform.FindChildRecursive(ovrBoneStringRight);
|
||||
boneMappings_[index].RightPresenceTransform = rightHandRoot_.FindChildRecursive(entry.HandPresenceRightBoneName);
|
||||
|
||||
Assert.IsTrue(
|
||||
boneMappings_[index].LeftPresenceTransform != null
|
||||
&& boneMappings_[index].RightPresenceTransform != null
|
||||
&& boneMappings_[index].RightHandTransform != null
|
||||
&& boneMappings_[index].LeftHandTransform != null,
|
||||
string.Format(
|
||||
"[tracked_keyboard] - entry.lp {0} && entry.rp {1} && entry.rt {2} && entry.lt {3}, {4}, {5}",
|
||||
boneMappings_[index].LeftPresenceTransform,
|
||||
boneMappings_[index].RightPresenceTransform,
|
||||
boneMappings_[index].RightHandTransform,
|
||||
boneMappings_[index].LeftHandTransform,
|
||||
ovrBoneStringRight,
|
||||
ovrBoneStringLeft
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
handPresenceInitialized_ = true;
|
||||
}
|
||||
|
||||
private void StopHandPresence()
|
||||
{
|
||||
enabled = false;
|
||||
// Re-enable hand models if they are disabled, let OVRHand handle controller/hands switching
|
||||
SetHandModelsEnabled(true, true);
|
||||
DisableHandObjects();
|
||||
}
|
||||
|
||||
private void DisableHandObjects()
|
||||
{
|
||||
KeyboardTracker.HandsOverKeyboard = false;
|
||||
RightHandOverKeyboard = false;
|
||||
LeftHandOverKeyboard = false;
|
||||
|
||||
if (leftHandRoot_ != null)
|
||||
{
|
||||
leftHandRoot_.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (rightHandRoot_ != null)
|
||||
{
|
||||
rightHandRoot_.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void TrackedKeyboardActiveUpdated(OVRTrackedKeyboard.TrackedKeyboardSetActiveEvent e)
|
||||
{
|
||||
if (!e.IsEnabled)
|
||||
{
|
||||
StopHandPresence();
|
||||
}
|
||||
}
|
||||
|
||||
public void TrackedKeyboardVisibilityChanged(OVRTrackedKeyboard.TrackedKeyboardVisibilityChangedEvent e)
|
||||
{
|
||||
switch (e.State)
|
||||
{
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Offline:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.NoTrackableKeyboard:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.StartedNotTracked:
|
||||
StopHandPresence();
|
||||
break;
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Valid:
|
||||
enabled = handPresenceInitialized_;
|
||||
break;
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Stale:
|
||||
if (e.TrackingTimeout)
|
||||
{
|
||||
StopHandPresence();
|
||||
}
|
||||
break;
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Uninitialized:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Error:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.ErrorExtensionFailed:
|
||||
StopHandPresence();
|
||||
Debug.LogWarning("Invalid state passed into TrackedKeyboardVisibilityChanged " + e.State.ToString());
|
||||
break;
|
||||
default:
|
||||
throw new System.Exception(
|
||||
$"[tracked_keyboard] - unhandled state: TrackedKeyboardVisibilityChanged {e.State}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public struct TrackedKeyboardHandsVisibilityChangedEvent
|
||||
{
|
||||
public bool leftVisible;
|
||||
public bool rightVisible;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f856490bcef82364d9770bece066fcf5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
/************************************************************************************
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
|
||||
https://developer.oculus.com/licenses/oculussdk/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Utilities 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 UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class OVRTrackedKeyboardSampleControls : MonoBehaviour
|
||||
{
|
||||
public OVRTrackedKeyboard trackedKeyboard;
|
||||
public InputField StartingFocusField;
|
||||
public Text NameValue;
|
||||
public Text ConnectedValue;
|
||||
public Text StateValue;
|
||||
public Color GoodStateColor = new Color(0.25f, 1, 0.25f, 1);
|
||||
public Color BadStateColor = new Color(1, 0.25f, 0.25f, 1);
|
||||
public Toggle TrackingToggle;
|
||||
public Toggle ConnectionToggle;
|
||||
|
||||
void Start()
|
||||
{
|
||||
StartingFocusField.Select();
|
||||
StartingFocusField.ActivateInputField();
|
||||
if(TrackingToggle.isOn != trackedKeyboard.TrackingEnabled){
|
||||
TrackingToggle.isOn = trackedKeyboard.TrackingEnabled;
|
||||
}
|
||||
if (ConnectionToggle.isOn != trackedKeyboard.ConnectionRequired)
|
||||
{
|
||||
ConnectionToggle.isOn = trackedKeyboard.ConnectionRequired;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
NameValue.text = trackedKeyboard.SystemKeyboardInfo.Name;
|
||||
ConnectedValue.text = ((bool)((trackedKeyboard.SystemKeyboardInfo.KeyboardFlags & OVRPlugin.TrackedKeyboardFlags.Connected) > 0)).ToString();
|
||||
StateValue.text = trackedKeyboard.TrackingState.ToString();
|
||||
switch (trackedKeyboard.TrackingState)
|
||||
{
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Uninitialized:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Error:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.ErrorExtensionFailed:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.StartedNotTracked:
|
||||
case OVRTrackedKeyboard.TrackedKeyboardState.Stale:
|
||||
StateValue.color = BadStateColor;
|
||||
break;
|
||||
default:
|
||||
StateValue.color = GoodStateColor;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void SetPresentationOpaque()
|
||||
{
|
||||
trackedKeyboard.Presentation = OVRTrackedKeyboard.KeyboardPresentation.PreferOpaque;
|
||||
}
|
||||
|
||||
public void SetPresentationKeyLabels()
|
||||
{
|
||||
trackedKeyboard.Presentation = OVRTrackedKeyboard.KeyboardPresentation.PreferKeyLabels;
|
||||
}
|
||||
|
||||
public void LaunchKeyboardSelection()
|
||||
{
|
||||
trackedKeyboard.LaunchLocalKeyboardSelectionDialog();
|
||||
}
|
||||
|
||||
public void SetTrackingEnabled(bool value)
|
||||
{
|
||||
trackedKeyboard.TrackingEnabled = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58529f2accd8ef7449ea22070cdfd2ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user