add package files
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using System;
|
||||
namespace VIVE.FacialTracking
|
||||
{
|
||||
public class FacialManager
|
||||
{
|
||||
private class FacialData
|
||||
{
|
||||
public bool isStarted = false;
|
||||
public XrFacialTrackingTypeHTC trackingType;
|
||||
public bool isActive;
|
||||
public ulong trackerHandle;
|
||||
public int updatedFrame = -1;
|
||||
public Dictionary<XrEyeShapeHTC, float> eyeWeightings = new Dictionary<XrEyeShapeHTC, float>();
|
||||
public Dictionary<XrLipShapeHTC, float> LipWeightings = new Dictionary<XrLipShapeHTC, float>();
|
||||
public float[] blendshapes;
|
||||
public bool isCreated { get { return trackerHandle != default; } }
|
||||
public FacialData(XrFacialTrackingTypeHTC type)
|
||||
{
|
||||
trackingType = type;
|
||||
for (int i = 0; i < (int)XrEyeShapeHTC.XR_EYE_EXPRESSION_MAX_ENUM_HTC; ++i) eyeWeightings.Add((XrEyeShapeHTC)i, 0.0f);
|
||||
for (int i = 0; i < (int)XrLipShapeHTC.XR_LIP_SHAPE_MAX_ENUM_HTC; ++i) LipWeightings.Add((XrLipShapeHTC)i, 0.0f);
|
||||
int shapeSize = type == XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC
|
||||
? (int)XrEyeShapeHTC.XR_EYE_EXPRESSION_MAX_ENUM_HTC : (int)XrLipShapeHTC.XR_LIP_SHAPE_MAX_ENUM_HTC;
|
||||
blendshapes = new float[shapeSize];
|
||||
}
|
||||
public void ClearData()
|
||||
{
|
||||
Array.Clear(blendshapes, 0, blendshapes.Length);
|
||||
eyeWeightings.Clear();
|
||||
LipWeightings.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private FacialData eyeFacialData = new FacialData(XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC);
|
||||
private FacialData lipFacialData = new FacialData(XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_LIP_DEFAULT_HTC);
|
||||
private VIVE_FacialTracking_OpenXR_API feature;
|
||||
private static bool isInitialized;
|
||||
private static bool isSystemSupportEye;
|
||||
private static bool isSystemSupportLip;
|
||||
|
||||
private FacialData getFacialData(XrFacialTrackingTypeHTC type)
|
||||
{
|
||||
if (type == XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC)
|
||||
return eyeFacialData;
|
||||
else
|
||||
return lipFacialData;
|
||||
}
|
||||
|
||||
private void OnFeatureSessionCreate(ulong xrSession)
|
||||
{
|
||||
TryCreateFacialTracker(eyeFacialData);
|
||||
TryCreateFacialTracker(lipFacialData);
|
||||
}
|
||||
private void OnFeatureSessionDestroy(ulong xrSession)
|
||||
{
|
||||
TryDestroyFacialTracker(eyeFacialData);
|
||||
TryDestroyFacialTracker(lipFacialData);
|
||||
}
|
||||
|
||||
private void OnFeatureSystemChange(ulong systemId)
|
||||
{
|
||||
UpdateSystemSupported();
|
||||
}
|
||||
|
||||
private void UpdateSystemSupported()
|
||||
{
|
||||
if (feature == null || !feature.IsEnabledAndInitialized)
|
||||
{
|
||||
isSystemSupportEye = false;
|
||||
isSystemSupportLip = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
feature.SystemSupportsFacialTracking(out var result,out isSystemSupportEye,out isSystemSupportLip);
|
||||
if (result != XrResult.XR_SUCCESS)
|
||||
{
|
||||
Debug.LogWarning("Fail SystemSupportsHandTracking: " + result);
|
||||
}
|
||||
if (!isSystemSupportEye)
|
||||
{
|
||||
UnityEngine.Debug.Log("Initial eyetracking failed , the device may not support EyeExpression");
|
||||
}
|
||||
if (!isSystemSupportLip)
|
||||
{
|
||||
UnityEngine.Debug.Log("Initial liptracking failed , the device may not support LipExpression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool Initialize()
|
||||
{
|
||||
if(feature == null)
|
||||
feature = OpenXRSettings.Instance.GetFeature<VIVE_FacialTracking_OpenXR_API>();
|
||||
if (!isInitialized)
|
||||
{
|
||||
if (feature != null)
|
||||
{
|
||||
feature.onSessionCreate += OnFeatureSessionCreate;
|
||||
feature.onSessionDestroy += OnFeatureSessionDestroy;
|
||||
feature.onSystemChange += OnFeatureSystemChange;
|
||||
}
|
||||
if (feature != null && feature.IsEnabledAndInitialized)
|
||||
{
|
||||
UpdateSystemSupported();
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isInitialized;
|
||||
}
|
||||
private bool TryCreateFacialTracker(FacialData facialData)
|
||||
{
|
||||
if (!facialData.isStarted) return false;
|
||||
if (!Initialize()) { return false; }
|
||||
if (facialData.trackingType == XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC && !isSystemSupportEye) { return false; }
|
||||
if (facialData.trackingType == XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_LIP_DEFAULT_HTC && !isSystemSupportLip) { return false; }
|
||||
if (!feature.IsSessionCreated) { return false; }
|
||||
if (!facialData.isCreated)
|
||||
{
|
||||
if (!feature.TryCreateFacialTracker(facialData.trackingType, out facialData.trackerHandle, out var result))
|
||||
{
|
||||
facialData.trackerHandle = default;
|
||||
Debug.LogWarning("Fail CreateFacialTracker: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
return facialData.isCreated;
|
||||
}
|
||||
|
||||
private void TryDestroyFacialTracker(FacialData facialData)
|
||||
{
|
||||
if (!facialData.isCreated) { return; }
|
||||
if (!Initialize()) { return; }
|
||||
|
||||
if(!feature.TryDestroFacialTracker(facialData.trackerHandle, out var res))
|
||||
{
|
||||
Debug.LogWarning("Fail DestroyFacialTracker: " + res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Success DestroyFacialTracker: " + res);
|
||||
}
|
||||
facialData.trackerHandle = default;
|
||||
facialData.ClearData();
|
||||
}
|
||||
private void TryGetFacialData(FacialData facialData)
|
||||
{
|
||||
if(facialData.isCreated)
|
||||
{
|
||||
if(facialData.updatedFrame != Time.frameCount)
|
||||
{
|
||||
int maxExpressionCount = facialData.trackingType == XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC
|
||||
? (int)XrEyeShapeHTC.XR_EYE_EXPRESSION_MAX_ENUM_HTC : (int)XrLipShapeHTC.XR_LIP_SHAPE_MAX_ENUM_HTC;
|
||||
facialData.updatedFrame = Time.frameCount;
|
||||
if (!feature.TryGetFacialData(
|
||||
facialData.trackerHandle,
|
||||
out facialData.isActive,
|
||||
maxExpressionCount,
|
||||
out var result,
|
||||
facialData.blendshapes))
|
||||
{
|
||||
facialData.isActive = false;
|
||||
Debug.LogWarning("Fail TryGetFacialData: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public void StartFramework(XrFacialTrackingTypeHTC type)
|
||||
{
|
||||
var facialdata = getFacialData(type);
|
||||
facialdata.isStarted = true;
|
||||
TryCreateFacialTracker(facialdata);
|
||||
}
|
||||
public void StopFramework(XrFacialTrackingTypeHTC type)
|
||||
{
|
||||
var facialdata = getFacialData(type);
|
||||
facialdata.isStarted = false;
|
||||
TryDestroyFacialTracker(facialdata);
|
||||
}
|
||||
public bool GetWeightings<T>(out Dictionary<T, float> shapes)
|
||||
{
|
||||
if (typeof(T) == typeof(XrEyeShapeHTC))
|
||||
{
|
||||
TryGetFacialData(eyeFacialData);
|
||||
for (int i = 0; i < (int)XrEyeShapeHTC.XR_EYE_EXPRESSION_MAX_ENUM_HTC; ++i)
|
||||
{
|
||||
eyeFacialData.eyeWeightings[(XrEyeShapeHTC)i] = eyeFacialData.blendshapes[i];
|
||||
}
|
||||
shapes = (Dictionary<T, float>)(System.Object)(eyeFacialData.eyeWeightings);
|
||||
return true;
|
||||
}
|
||||
else if (typeof(T) == typeof(XrLipShapeHTC))
|
||||
{
|
||||
TryGetFacialData(lipFacialData);
|
||||
for (int i = 0; i < (int)XrLipShapeHTC.XR_LIP_SHAPE_MAX_ENUM_HTC; ++i)
|
||||
{
|
||||
lipFacialData.LipWeightings[(XrLipShapeHTC)i] = lipFacialData.blendshapes[i];
|
||||
}
|
||||
shapes = (Dictionary<T, float>)(System.Object)(lipFacialData.LipWeightings);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
shapes = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9da6a2c254e9354891b5ab88cd630f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEditor;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
namespace VIVE
|
||||
{
|
||||
namespace FacialTracking
|
||||
{
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "Facial Tracking",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone },
|
||||
Company = "HTC",
|
||||
Desc = "Facial Tracking OpenXR Feature",
|
||||
DocumentationLink = "https://developer.vive.com/resources/openxr/openxr-pcvr/tutorials/unity/integrate-facial-tracking-your-avatar/",
|
||||
OpenxrExtensionStrings = "XR_HTC_facial_tracking",
|
||||
Version = "0.0.1",
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
public class VIVE_FacialTracking_OpenXR_API : OpenXRFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "com.htc.openxr.facialtracking.feature";
|
||||
private ulong m_XrInstance;
|
||||
private ulong m_XrSession;
|
||||
private ulong m_systemid;
|
||||
[Obsolete] XrSystemProperties systemProperties;
|
||||
[Obsolete] public ulong m_expressionHandle;
|
||||
[Obsolete] public ulong m_expressionHandle_Lip;
|
||||
|
||||
public bool IsInitialized { get { return m_xrGetSystemProperties != null; } }
|
||||
public bool IsEnabledAndInitialized { get { return enabled && IsInitialized; } }
|
||||
public bool IsSessionCreated { get { return XrSession != default; } }
|
||||
public ulong XrInstance { get { return m_XrInstance; } }
|
||||
public ulong XrSession { get { return m_XrSession; } }
|
||||
public ulong SystemId { get { return m_systemid; } }
|
||||
|
||||
public event Action<ulong> onSessionCreate;
|
||||
public event Action<ulong> onSessionDestroy;
|
||||
public event Action<ulong> onSystemChange;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
UnityEngine.Debug.Log($"OnInstanceCreate({xrInstance:X})");
|
||||
m_XrInstance = xrInstance;
|
||||
|
||||
return GetXrFunctionDelegates(xrInstance);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||
{
|
||||
UnityEngine.Debug.Log($"OnInstanceDestroy({xrInstance:X})");
|
||||
m_XrInstance = default;
|
||||
}
|
||||
protected override void OnSessionCreate(ulong xrSession)
|
||||
{
|
||||
UnityEngine.Debug.Log($"OnSessionCreate({xrSession:X})");
|
||||
m_XrSession = xrSession;
|
||||
|
||||
try { onSessionCreate?.Invoke(xrSession); }
|
||||
catch (Exception e) { UnityEngine.Debug.LogError(e); }
|
||||
}
|
||||
protected override void OnSystemChange(ulong xrSystem)
|
||||
{
|
||||
UnityEngine.Debug.Log($"OnSystemChange({xrSystem:X})");
|
||||
m_systemid = xrSystem;
|
||||
|
||||
try { onSystemChange?.Invoke(xrSystem); }
|
||||
catch (Exception e) { UnityEngine.Debug.LogError(e); }
|
||||
}
|
||||
|
||||
protected override void OnSessionDestroy(ulong xrSession)
|
||||
{
|
||||
UnityEngine.Debug.Log($"OnSessionDestroy({xrSession:X})");
|
||||
m_XrSession = default;
|
||||
try { onSessionDestroy?.Invoke(xrSession); }
|
||||
catch (Exception e) { UnityEngine.Debug.LogError(e); }
|
||||
}
|
||||
|
||||
private bool GetXrFunctionDelegates(ulong xrInstance)
|
||||
{
|
||||
if (xrGetInstanceProcAddr == null || xrGetInstanceProcAddr == IntPtr.Zero)
|
||||
UnityEngine.Debug.LogError("xrGetInstanceProcAddr is null");
|
||||
// Get delegate of xrGetInstanceProcAddr.
|
||||
var xrGetProc = Marshal.GetDelegateForFunctionPointer<xrGetInstanceProcDelegate>(xrGetInstanceProcAddr);
|
||||
// Get delegate of other OpenXR functions using xrGetInstanceProcAddr.
|
||||
if (!MarshelFunc(xrInstance, xrGetProc, "xrGetSystemProperties", ref m_xrGetSystemProperties)) { return false; }
|
||||
if (!MarshelFunc(xrInstance, xrGetProc, "xrCreateFacialTrackerHTC", ref m_xrCreateFacialTrackerHTC)) { return false; }
|
||||
if (!MarshelFunc(xrInstance, xrGetProc, "xrDestroyFacialTrackerHTC", ref m_xrDestroyFacialTrackerHTC)) { return false; }
|
||||
if (!MarshelFunc(xrInstance, xrGetProc, "xrGetFacialExpressionsHTC", ref m_xrGetFacialExpressionsHTC)) { return false; }
|
||||
return true;
|
||||
}
|
||||
private static bool MarshelFunc<T>(ulong instance, xrGetInstanceProcDelegate instanceProc, string funcName, ref T func, bool verbose = true)
|
||||
where T : Delegate
|
||||
{
|
||||
if (instanceProc(instance, funcName, out var fp) != 0)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
UnityEngine.Debug.LogError("Fail getting function " + funcName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
func = Marshal.GetDelegateForFunctionPointer<T>(fp);
|
||||
return true;
|
||||
}
|
||||
delegate int XrGetInstanceProcAddrDelegate(ulong instance, string name, out IntPtr function);
|
||||
XrGetInstanceProcAddrDelegate m_XrGetInstanceProcAddr;
|
||||
|
||||
delegate int xrGetSystemPropertiesDelegate(ulong instance, ulong systemId, ref XrSystemProperties properties);
|
||||
xrGetSystemPropertiesDelegate m_xrGetSystemProperties;
|
||||
public int xrGetSystemProperties(ref XrSystemProperties properties) =>
|
||||
m_xrGetSystemProperties(m_XrInstance, m_systemid, ref properties);
|
||||
public int xrGetSystemProperties(ulong instance, ulong systemId, ref XrSystemProperties properties) =>
|
||||
m_xrGetSystemProperties(instance, systemId, ref properties);
|
||||
|
||||
delegate int xrCreateFacialTrackerHTCDelegate(ulong session, XrFacialTrackerCreateInfoHTC createInfo, out ulong expression);
|
||||
xrCreateFacialTrackerHTCDelegate m_xrCreateFacialTrackerHTC;
|
||||
public int xrCreateFacialTrackerHTC(XrFacialTrackerCreateInfoHTC createInfo ,out ulong handle) =>
|
||||
m_xrCreateFacialTrackerHTC(m_XrSession, createInfo, out handle);
|
||||
public int xrCreateFacialTrackerHTC(ulong session, XrFacialTrackerCreateInfoHTC createInfo, out ulong handle) =>
|
||||
m_xrCreateFacialTrackerHTC(session, createInfo, out handle);
|
||||
|
||||
delegate int xrDestroyFacialTrackerHTCDelegate(ulong facialTracker);
|
||||
xrDestroyFacialTrackerHTCDelegate m_xrDestroyFacialTrackerHTC;
|
||||
public int xrDestroyFacialTrackerHTC(ulong handle) =>
|
||||
m_xrDestroyFacialTrackerHTC(handle);
|
||||
|
||||
delegate int xrGetFacialExpressionsHTCDelegate(ulong facialTracker, ref XrFacialExpressionsHTC eyeExpressionData);
|
||||
xrGetFacialExpressionsHTCDelegate m_xrGetFacialExpressionsHTC;
|
||||
public int xrGetFacialExpressionsHTC(ulong handle,ref XrFacialExpressionsHTC eyeExpressionData) =>
|
||||
m_xrGetFacialExpressionsHTC(handle, ref eyeExpressionData);
|
||||
|
||||
public unsafe void SystemSupportsFacialTracking(out XrResult result,out bool isSupportEye,out bool isSupportLip)
|
||||
{
|
||||
XrSystemFacialTrackingPropertiesHTC expressionProperties = new XrSystemFacialTrackingPropertiesHTC
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_SYSTEM_FACIAL_TRACKING_PROPERTIES_HTC,
|
||||
next = IntPtr.Zero,
|
||||
};
|
||||
|
||||
var systemProp = new XrSystemProperties()
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_SYSTEM_PROPERTIES,
|
||||
next = (IntPtr)(&expressionProperties),
|
||||
};
|
||||
result = (XrResult)xrGetSystemProperties(XrInstance, SystemId ,ref systemProp);
|
||||
isSupportEye = expressionProperties.supportEyeFacialTracking != 0;
|
||||
isSupportLip = expressionProperties.supportLipFacialTracking != 0;
|
||||
}
|
||||
|
||||
public bool TryCreateFacialTracker(XrFacialTrackingTypeHTC type,out ulong handle, out XrResult result)
|
||||
{
|
||||
XrFacialTrackerCreateInfoHTC createInfo = new XrFacialTrackerCreateInfoHTC(
|
||||
XrStructureType.XR_TYPE_FACIAL_TRACKER_CREATE_INFO_HTC,
|
||||
IntPtr.Zero,
|
||||
type);
|
||||
result = (XrResult)xrCreateFacialTrackerHTC(XrSession,createInfo, out handle);
|
||||
return result == XrResult.XR_SUCCESS;
|
||||
}
|
||||
public bool TryDestroFacialTracker(ulong handle, out XrResult result)
|
||||
{
|
||||
result= (XrResult)xrDestroyFacialTrackerHTC(handle);
|
||||
return result == XrResult.XR_SUCCESS;
|
||||
}
|
||||
public bool TryGetFacialData(ulong handle,out bool isActive,int maxExpressioncount,out XrResult result,float[] blendshapes)
|
||||
{
|
||||
XrFacialExpressionsHTC facialExpression = new XrFacialExpressionsHTC()
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_FACIAL_EXPRESSIONS_HTC,
|
||||
expressionCount = maxExpressioncount,
|
||||
sampleTime = 10L, //An arbitrary number greater than 0
|
||||
blendShapeWeightings = Marshal.AllocCoTaskMem(sizeof(float) * maxExpressioncount),
|
||||
};
|
||||
result = (XrResult)xrGetFacialExpressionsHTC(handle, ref facialExpression);
|
||||
|
||||
Marshal.Copy(facialExpression.blendShapeWeightings, blendshapes, 0, maxExpressioncount);
|
||||
isActive = facialExpression.isActive != 0u;
|
||||
|
||||
return result == XrResult.XR_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be5cb237345f49840bbc00c128fde3d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user