/* * 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; /// /// OVR Component to drive blend shapes on a SkinnedMeshRenderer based on Face Tracking provided by . /// /// /// 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 to blend shapes is accomplished by overriding . /// Needs to be linked to an component to fetch tracking data from. /// [RequireComponent(typeof(SkinnedMeshRenderer))] [HelpURL("https://developer.oculus.com/reference/unity/latest/class_o_v_r_face")] public class OVRFace : MonoBehaviour { /// /// Start this instance. /// Will validate that all properties are set correctly /// public OVRFaceExpressions FaceExpressions { get => _faceExpressions; set => _faceExpressions = value; } public float BlendShapeStrengthMultiplier { get => _blendShapeStrengthMultiplier; set => _blendShapeStrengthMultiplier = value; } internal SkinnedMeshRenderer RetrieveSkinnedMeshRenderer() { return GetComponent(); } [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(); protected virtual void Start() { Assert.IsNotNull(_faceExpressions, "OVRFace requires OVRFaceExpressions to function."); _skinnedMeshRenderer = GetComponent(); 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)); } } } } /// /// Fetches the for a given blend shape index on the shared mesh of the SkinnedMeshRenderer on the same component /// /// /// Override this function to provide the mapping between blend shapes and face expressions /// /// The index of the blend shape, will be in-between 0 and the number of blend shapes on the shared mesh. /// Returns the to drive the bland shape identified by . internal protected virtual OVRFaceExpressions.FaceExpression GetFaceExpression(int blendShapeIndex) => OVRFaceExpressions.FaceExpression.Invalid; /// /// Calculates the value for the specific target blend shape of the shared mesh SkinnedMeshRenderer /// /// Index of the blend shape of the shared mesh SkinnedMeshRenderer /// Calculated value /// true if value was calculated, false if no value available for that blend shape 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; } }