Heroes_of_Hiis/Assets/Oculus/VR/Scripts/OVRTrackedKeyboard/OVRTrackedKeyboard.cs

903 lines
28 KiB
C#
Raw Normal View History

2022-03-07 15:52:41 +00:00
/************************************************************************************
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;
}
}
}