139 lines
5.7 KiB
C#
139 lines
5.7 KiB
C#
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the Oculus SDK License Agreement (the "License");
|
|
* you may not use the Oculus SDK except in compliance with the License,
|
|
* which is provided at the time of installation or download, or which
|
|
* otherwise accompanies this software in either electronic or hard copy form.
|
|
*
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://developer.oculus.com/licenses/oculussdk/
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.Assertions;
|
|
|
|
/// <summary>
|
|
/// OVR Component to drive blend shapes on a <c>SkinnedMeshRenderer</c> based on Face Tracking provided by <see cref="OVRFaceExpressions"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Intended to be used as a base type that is inherited from, in order to provide mapping logic from blend shape indices.
|
|
/// The mapping of <see cref="OVRFaceExpressions.FaceExpression"/> to blend shapes is accomplished by overriding <see cref="OVRFace.GetFaceExpression(int)"/>.
|
|
/// Needs to be linked to an <see cref="OVRFaceExpressions"/> component to fetch tracking data from.
|
|
/// </remarks>
|
|
[RequireComponent(typeof(SkinnedMeshRenderer))]
|
|
[HelpURL("https://developer.oculus.com/reference/unity/latest/class_o_v_r_face")]
|
|
public class OVRFace : MonoBehaviour
|
|
{
|
|
/// <summary>
|
|
/// Start this instance.
|
|
/// Will validate that all properties are set correctly
|
|
/// </summary>
|
|
public OVRFaceExpressions FaceExpressions
|
|
{
|
|
get => _faceExpressions;
|
|
set => _faceExpressions = value;
|
|
}
|
|
|
|
public float BlendShapeStrengthMultiplier
|
|
{
|
|
get => _blendShapeStrengthMultiplier;
|
|
set => _blendShapeStrengthMultiplier = value;
|
|
}
|
|
|
|
internal SkinnedMeshRenderer RetrieveSkinnedMeshRenderer()
|
|
{
|
|
return GetComponent<SkinnedMeshRenderer>();
|
|
}
|
|
|
|
[SerializeField]
|
|
[Tooltip("The OVRFaceExpressions Component to fetch the Face Tracking weights from that are to be applied")]
|
|
protected internal OVRFaceExpressions _faceExpressions;
|
|
|
|
[SerializeField]
|
|
[Tooltip("A multiplier to the weights read from the OVRFaceExpressions to exaggerate facial expressions")]
|
|
protected internal float _blendShapeStrengthMultiplier = 100.0f;
|
|
|
|
private SkinnedMeshRenderer _skinnedMeshRenderer;
|
|
protected SkinnedMeshRenderer SkinnedMesh => _skinnedMeshRenderer;
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
if (_faceExpressions == null)
|
|
{
|
|
_faceExpressions = SearchFaceExpressions();
|
|
Debug.Log($"Found OVRFaceExpression reference in {_faceExpressions.name} due to unassigned field.");
|
|
}
|
|
}
|
|
|
|
internal OVRFaceExpressions SearchFaceExpressions() => gameObject.GetComponentInParent<OVRFaceExpressions>();
|
|
|
|
protected virtual void Start()
|
|
{
|
|
Assert.IsNotNull(_faceExpressions, "OVRFace requires OVRFaceExpressions to function.");
|
|
|
|
_skinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
|
|
Assert.IsNotNull(_skinnedMeshRenderer);
|
|
Assert.IsNotNull(_skinnedMeshRenderer.sharedMesh);
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
if (!_faceExpressions.FaceTrackingEnabled || !_faceExpressions.enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_faceExpressions.ValidExpressions)
|
|
{
|
|
int numBlendshapes = _skinnedMeshRenderer.sharedMesh.blendShapeCount;
|
|
|
|
for (int blendShapeIndex = 0; blendShapeIndex < numBlendshapes; ++blendShapeIndex)
|
|
{
|
|
if (GetWeightValue(blendShapeIndex, out var currentWeight))
|
|
{
|
|
_skinnedMeshRenderer.SetBlendShapeWeight(blendShapeIndex, Mathf.Clamp(currentWeight, 0f, 100f));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches the <see cref="OVRFaceExpressions.FaceExpression"/> for a given blend shape index on the shared mesh of the <c>SkinnedMeshRenderer</c> on the same component
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Override this function to provide the mapping between blend shapes and face expressions
|
|
/// </remarks>
|
|
/// <param name="blendShapeIndex">The index of the blend shape, will be in-between 0 and the number of blend shapes on the shared mesh.</param>
|
|
/// <returns>Returns the <see cref="OVRFaceExpressions.FaceExpression"/> to drive the bland shape identified by <paramref name="blendShapeIndex"/>.</returns>
|
|
internal protected virtual OVRFaceExpressions.FaceExpression GetFaceExpression(int blendShapeIndex) =>
|
|
OVRFaceExpressions.FaceExpression.Invalid;
|
|
|
|
/// <summary>
|
|
/// Calculates the value for the specific target blend shape of the shared mesh <c>SkinnedMeshRenderer</c>
|
|
/// </summary>
|
|
/// <param name="blendShapeIndex">Index of the blend shape of the shared mesh <c>SkinnedMeshRenderer</c></param>
|
|
/// <param name="weightValue">Calculated value</param>
|
|
/// <returns>true if value was calculated, false if no value available for that blend shape</returns>
|
|
internal protected virtual bool GetWeightValue(int blendShapeIndex, out float weightValue)
|
|
{
|
|
OVRFaceExpressions.FaceExpression blendShapeToFaceExpression = GetFaceExpression(blendShapeIndex);
|
|
if (blendShapeToFaceExpression >= OVRFaceExpressions.FaceExpression.Max || blendShapeToFaceExpression < 0)
|
|
{
|
|
weightValue = 0;
|
|
return false;
|
|
}
|
|
|
|
weightValue = _faceExpressions[blendShapeToFaceExpression] * _blendShapeStrengthMultiplier;
|
|
return true;
|
|
}
|
|
}
|