Files
2025-07-04 20:33:06 +03:00

286 lines
8.9 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 System;
using System.ComponentModel;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
[CustomEditor(typeof(OVRCustomSkeleton))]
public class OVRCustomSkeletonEditor : OVRSkeletonEditor
{
public override void OnInspectorGUI()
{
var skeleton = (OVRCustomSkeleton)target;
base.OnInspectorGUI();
DrawBonesMapping(skeleton);
}
private static void DrawBonesMapping(OVRCustomSkeleton skeleton)
{
if (GUILayout.Button($"Auto Map Bones"))
{
skeleton.AutoMapBones(skeleton.retargetingType);
EditorUtility.SetDirty(skeleton);
EditorSceneManager.MarkSceneDirty(skeleton.gameObject.scene);
}
EditorGUILayout.LabelField("Bones", EditorStyles.boldLabel);
var start = skeleton.GetCurrentStartBoneId();
var end = skeleton.GetCurrentEndBoneId();
if (skeleton.IsValidBone(start) && skeleton.IsValidBone(end))
{
for (var i = (int)start; i < (int)end; ++i)
{
var boneName = OVRSkeleton.BoneLabelFromBoneId(skeleton.GetSkeletonType(), (OVRSkeleton.BoneId)i);
EditorGUI.BeginChangeCheck();
var val =
EditorGUILayout.ObjectField(boneName, skeleton.CustomBones[i], typeof(Transform), true);
if (EditorGUI.EndChangeCheck())
{
skeleton.CustomBones[i] = (Transform)val;
EditorUtility.SetDirty(skeleton);
}
}
}
}
}
/// <summary>
/// Extensions class for the editor methods of <see cref="OVRCustomSkeleton"/>.
/// </summary>
public static class OVRCustomSkeletonEditorExtensions
{
/// <summary>
/// This method tries to retarget the skeleton structure present in the current <see cref="GameObject"/> to the one supported by the body tracking system.
/// </summary>
/// <param name="customSkeleton" cref="OVRCustomSkeleton">The custom skeleton to run this method on</param>
/// <param name="type" cref="OVRCustomSkeleton.RetargetingType">The skeleton structure to auto map from</param>
public static void AutoMapBones(this OVRCustomSkeleton customSkeleton, OVRCustomSkeleton.RetargetingType type)
{
try
{
switch (type)
{
case OVRCustomSkeleton.RetargetingType.OculusSkeleton:
customSkeleton.AutoMapBonesFromOculusSkeleton();
break;
default:
throw new InvalidEnumArgumentException($"Invalid {nameof(OVRCustomSkeleton.RetargetingType)}");
}
}
catch (Exception e)
{
EditorUtility.DisplayDialog($"Auto Map Bones Error", e.Message, "Ok");
}
}
public static void TryAutoMapBonesByName(this OVRCustomSkeleton customSkeleton)
{
customSkeleton.AutoMapBonesFromOculusSkeleton();
}
internal static void AutoMapBonesFromOculusSkeleton(this OVRCustomSkeleton customSkeleton)
{
var start = customSkeleton.GetCurrentStartBoneId();
var end = customSkeleton.GetCurrentEndBoneId();
var skeletonType = customSkeleton.GetSkeletonType();
if (customSkeleton.IsValidBone(start) && customSkeleton.IsValidBone(end))
{
for (var bi = (int)start; bi < (int)end; ++bi)
{
string fbxBoneName = FbxBoneNameFromBoneId(skeletonType, (OVRSkeleton.BoneId)bi);
Transform t = customSkeleton.transform.FindChildRecursive(fbxBoneName);
if (t == null && skeletonType == OVRSkeleton.SkeletonType.Body)
{
var legacyBoneName = fbxBoneName
.Replace("Little", "Pinky")
.Replace("Metacarpal", "Meta");
t = customSkeleton.transform.FindChildRecursive(legacyBoneName);
}
if (t != null)
{
customSkeleton.CustomBones[bi] = t;
}
}
}
}
internal static bool ClearBonesMapping(this OVRCustomSkeleton skeleton)
{
var start = skeleton.GetCurrentStartBoneId();
var end = skeleton.GetCurrentEndBoneId();
var cleared = false;
if (!skeleton.IsValidBone(start) || !skeleton.IsValidBone(end))
{
return false;
}
for (var i = (int)start; i < (int)end; ++i)
{
if (skeleton.CustomBones[i] != null)
{
skeleton.CustomBones[i] = null;
cleared = true;
}
}
return cleared;
}
private static string FbxBoneNameFromBoneId(OVRSkeleton.SkeletonType skeletonType, OVRSkeleton.BoneId bi)
{
if (skeletonType == OVRSkeleton.SkeletonType.Body)
{
return FBXBodyBoneNames[(int)bi];
}
else
{
if (bi >= OVRSkeleton.BoneId.Hand_ThumbTip && bi <= OVRSkeleton.BoneId.Hand_PinkyTip)
{
return FBXHandSidePrefix[(int)skeletonType] +
FBXHandFingerNames[(int)bi - (int)OVRSkeleton.BoneId.Hand_ThumbTip] +
"_finger_tip_marker";
}
else
{
return FBXHandBonePrefix + FBXHandSidePrefix[(int)skeletonType] + FBXHandBoneNames[(int)bi];
}
}
}
private static readonly string[] FBXBodyBoneNames =
{
"Root",
"Hips",
"SpineLower",
"SpineMiddle",
"SpineUpper",
"Chest",
"Neck",
"Head",
"LeftShoulder",
"LeftScapula",
"LeftArmUpper",
"LeftArmLower",
"LeftHandWristTwist",
"RightShoulder",
"RightScapula",
"RightArmUpper",
"RightArmLower",
"RightHandWristTwist",
"LeftHandPalm",
"LeftHandWrist",
"LeftHandThumbMetacarpal",
"LeftHandThumbProximal",
"LeftHandThumbDistal",
"LeftHandThumbTip",
"LeftHandIndexMetacarpal",
"LeftHandIndexProximal",
"LeftHandIndexIntermediate",
"LeftHandIndexDistal",
"LeftHandIndexTip",
"LeftHandMiddleMetacarpal",
"LeftHandMiddleProximal",
"LeftHandMiddleIntermediate",
"LeftHandMiddleDistal",
"LeftHandMiddleTip",
"LeftHandRingMetacarpal",
"LeftHandRingProximal",
"LeftHandRingIntermediate",
"LeftHandRingDistal",
"LeftHandRingTip",
"LeftHandLittleMetacarpal",
"LeftHandLittleProximal",
"LeftHandLittleIntermediate",
"LeftHandLittleDistal",
"LeftHandLittleTip",
"RightHandPalm",
"RightHandWrist",
"RightHandThumbMetacarpal",
"RightHandThumbProximal",
"RightHandThumbDistal",
"RightHandThumbTip",
"RightHandIndexMetacarpal",
"RightHandIndexProximal",
"RightHandIndexIntermediate",
"RightHandIndexDistal",
"RightHandIndexTip",
"RightHandMiddleMetacarpal",
"RightHandMiddleProximal",
"RightHandMiddleIntermediate",
"RightHandMiddleDistal",
"RightHandMiddleTip",
"RightHandRingMetacarpal",
"RightHandRingProximal",
"RightHandRingIntermediate",
"RightHandRingDistal",
"RightHandRingTip",
"RightHandLittleMetacarpal",
"RightHandLittleProximal",
"RightHandLittleIntermediate",
"RightHandLittleDistal",
"RightHandLittleTip"
};
private static readonly string[] FBXHandSidePrefix = { "l_", "r_" };
private const string FBXHandBonePrefix = "b_";
private static readonly string[] FBXHandBoneNames =
{
"wrist",
"forearm_stub",
"thumb0",
"thumb1",
"thumb2",
"thumb3",
"index1",
"index2",
"index3",
"middle1",
"middle2",
"middle3",
"ring1",
"ring2",
"ring3",
"pinky0",
"pinky1",
"pinky2",
"pinky3"
};
private static readonly string[] FBXHandFingerNames =
{
"thumb",
"index",
"middle",
"ring",
"pinky"
};
}