// Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. using System.Linq; using Meta.XR.Movement.FaceTracking; using Meta.XR.Movement.FaceTracking.Samples; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; namespace Meta.XR.Movement { /// /// Add project setup validation tasks for MovementSDK Samples. /// [InitializeOnLoad] internal static class MovementSDKProjectSetupTasks { /// /// The HiddenMesh layer is required for RecalculateNormals to function correctly. /// private static readonly string _hiddenMeshLayerName = "HiddenMesh"; private const OVRProjectSetup.TaskGroup _group = OVRProjectSetup.TaskGroup.Features; static MovementSDKProjectSetupTasks() { // Skin weights settings. OVRProjectSetup.AddTask( level: OVRProjectSetup.TaskLevel.Recommended, group: _group, platform: BuildTargetGroup.Unknown, isDone: group => QualitySettings.skinWeights == SkinWeights.FourBones, message: "Four skin weights should be used to avoid skinning problems.", fix: group => { QualitySettings.skinWeights = SkinWeights.FourBones; }, fixMessage: "Set quality settings skin weights to four bones." ); // Body tracking settings. OVRProjectSetup.AddTask( level: OVRProjectSetup.TaskLevel.Recommended, group: _group, platform: BuildTargetGroup.Unknown, isDone: group => OVRRuntimeSettings.Instance.BodyTrackingFidelity == OVRPlugin.BodyTrackingFidelity2.High, message: "Body Tracking Fidelity should be set to High.", fix: group => { OVRRuntimeSettings.Instance.BodyTrackingFidelity = OVRPlugin.BodyTrackingFidelity2.High; OVRRuntimeSettings.CommitRuntimeSettings(OVRRuntimeSettings.Instance); }, fixMessage: "Set OVRRuntimeSettings.BodyTrackingFidelity = High" ); OVRProjectSetup.AddTask( level: OVRProjectSetup.TaskLevel.Recommended, group: _group, platform: BuildTargetGroup.Unknown, isDone: group => OVRRuntimeSettings.GetRuntimeSettings().BodyTrackingJointSet == OVRPlugin.BodyJointSet.FullBody, message: "Body Tracking Joint Set should be set to Full Body.", fix: group => { OVRRuntimeSettings.Instance.BodyTrackingJointSet = OVRPlugin.BodyJointSet.FullBody; OVRRuntimeSettings.CommitRuntimeSettings(OVRRuntimeSettings.Instance); }, fixMessage: "Set OVRRuntimeSettings.BodyTrackingJointSet = FullBody" ); // Face tracking settings. OVRProjectSetup.AddTask( level: OVRProjectSetup.TaskLevel.Required, group: _group, platform: BuildTargetGroup.Unknown, isDone: group => FindComponentInScene() == null || OVRRuntimeSettings.GetRuntimeSettings().RequestsAudioFaceTracking, message: "The audio to expression feature should be enabled.", fix: group => { OVRRuntimeSettings.Instance.RequestsAudioFaceTracking = true; OVRRuntimeSettings.CommitRuntimeSettings(OVRRuntimeSettings.Instance); }, fixMessage: "Set OVRRuntimeSettings.RequestsAudioFaceTracking = true" ); #if META_XR_CORE_V78_MIN OVRProjectSetup.AddTask( level: OVRProjectSetup.TaskLevel.Required, group: _group, platform: BuildTargetGroup.Unknown, isDone: group => FindComponentInScene() == null || OVRRuntimeSettings.GetRuntimeSettings().EnableFaceTrackingVisemesOutput, message: "The visemes feature should be enabled.", fix: group => { OVRRuntimeSettings.Instance.RequestsAudioFaceTracking = true; OVRRuntimeSettings.Instance.EnableFaceTrackingVisemesOutput = true; OVRRuntimeSettings.CommitRuntimeSettings(OVRRuntimeSettings.Instance); }, fixMessage: "Set OVRRuntimeSettings.EnableFaceTrackingVisemesOutput = true" ); #endif // Test layers. OVRProjectSetup.AddTask( level: OVRProjectSetup.TaskLevel.Recommended, group: _group, platform: BuildTargetGroup.Unknown, isDone: group => FindComponentInScene() == null || LayerMask.NameToLayer(_hiddenMeshLayerName) != -1, message: $"The layer '{_hiddenMeshLayerName}' needs to exist for Recalculate Normals to function properly.", fix: group => { int unusedLayer = -1; for (int i = 0; i < 32; i++) { if (string.IsNullOrEmpty(LayerMask.LayerToName(i))) { unusedLayer = i; break; } } if (LayerMask.NameToLayer(_hiddenMeshLayerName) == -1) { if (unusedLayer == -1) { Debug.LogError("All Layers are Used!"); } else { SetLayerName(unusedLayer, "HiddenMesh"); } } }, fixMessage: $"Set an unused layer to '{_hiddenMeshLayerName}'." ); } private static void SetLayerName(int layer, string name) { SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAssetAtPath("ProjectSettings/TagManager.asset")); SerializedProperty it = tagManager.GetIterator(); while (it.NextVisible(true)) { if (it.name == "layers") { it.GetArrayElementAtIndex(layer).stringValue = name; break; } } tagManager.ApplyModifiedProperties(); } private static T FindComponentInScene() where T : Component { var scene = SceneManager.GetActiveScene(); var rootGameObjects = scene.GetRootGameObjects(); return rootGameObjects.FirstOrDefault(go => go.GetComponentInChildren())?.GetComponentInChildren(); } } }