Initialer Upload neues Unity-Projekt
This commit is contained in:
407
Assets/Oculus/VR/Scripts/OVRUnityHumanoidSkeletonRetargeter.cs
Normal file
407
Assets/Oculus/VR/Scripts/OVRUnityHumanoidSkeletonRetargeter.cs
Normal file
@ -0,0 +1,407 @@
|
||||
/*
|
||||
* 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.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
public partial class OVRUnityHumanoidSkeletonRetargeter : OVRSkeleton
|
||||
{
|
||||
private OVRSkeletonMetadata _sourceSkeletonData;
|
||||
protected OVRSkeletonMetadata SourceSkeletonData => _sourceSkeletonData;
|
||||
|
||||
private OVRSkeletonMetadata _sourceSkeletonTPoseData;
|
||||
protected OVRSkeletonMetadata SourceSkeletonTPoseData => _sourceSkeletonTPoseData;
|
||||
|
||||
private OVRSkeletonMetadata _targetSkeletonData;
|
||||
protected OVRSkeletonMetadata TargetSkeletonData => _targetSkeletonData;
|
||||
|
||||
private Animator _animatorTargetSkeleton;
|
||||
protected Animator AnimatorTargetSkeleton => _animatorTargetSkeleton;
|
||||
|
||||
private Dictionary<BoneId, HumanBodyBones> _customBoneIdToHumanBodyBone =
|
||||
new Dictionary<BoneId, HumanBodyBones>();
|
||||
|
||||
protected Dictionary<BoneId, HumanBodyBones> CustomBoneIdToHumanBodyBone
|
||||
{
|
||||
get => _customBoneIdToHumanBodyBone;
|
||||
}
|
||||
|
||||
private readonly Dictionary<HumanBodyBones, Quaternion> _targetTPoseRotations =
|
||||
new Dictionary<HumanBodyBones, Quaternion>();
|
||||
|
||||
protected Dictionary<HumanBodyBones, Quaternion> TargetTPoseRotations
|
||||
{
|
||||
get => _targetTPoseRotations;
|
||||
}
|
||||
|
||||
private int _lastSkelChangeCount = -1;
|
||||
|
||||
[Serializable]
|
||||
public class JointAdjustment
|
||||
{
|
||||
/// <summary>
|
||||
/// Joint to adjust.
|
||||
/// </summary>
|
||||
public HumanBodyBones Joint;
|
||||
|
||||
/// <summary>
|
||||
/// Rotation to apply to the joint, post-retargeting.
|
||||
/// </summary>
|
||||
public Quaternion RotationChange = Quaternion.identity;
|
||||
|
||||
/// <summary>
|
||||
/// Allows disable rotational transform on joint.
|
||||
/// </summary>
|
||||
public bool DisableRotationTransform = false;
|
||||
|
||||
/// <summary>
|
||||
/// Allows disable position transform on joint.
|
||||
/// </summary>
|
||||
public bool DisablePositionTransform = false;
|
||||
|
||||
/// <summary>
|
||||
/// Allows mapping this human body bone to OVRSkeleton bone different from the
|
||||
/// standard. An ignore value indicates to not override; remove means to exclude
|
||||
/// from retargeting. Cannot be changed at runtime.
|
||||
/// </summary>
|
||||
public OVRHumanBodyBonesMappings.BodyTrackingBoneId BoneIdOverrideValue =
|
||||
OVRHumanBodyBonesMappings.BodyTrackingBoneId.NoOverride;
|
||||
}
|
||||
|
||||
public OVRUnityHumanoidSkeletonRetargeter()
|
||||
{
|
||||
_skeletonType = SkeletonType.Body;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
protected JointAdjustment[] _adjustments =
|
||||
{
|
||||
new JointAdjustment
|
||||
{
|
||||
Joint = HumanBodyBones.Hips,
|
||||
RotationChange = Quaternion.Euler(60.0f, 0.0f, 0.0f)
|
||||
}
|
||||
};
|
||||
|
||||
protected JointAdjustment[] Adjustments
|
||||
{
|
||||
get => _adjustments;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
protected OVRHumanBodyBonesMappings.BodySection[] _bodySectionsToAlign =
|
||||
{
|
||||
OVRHumanBodyBonesMappings.BodySection.LeftArm, OVRHumanBodyBonesMappings.BodySection.RightArm,
|
||||
OVRHumanBodyBonesMappings.BodySection.LeftHand, OVRHumanBodyBonesMappings.BodySection.RightHand,
|
||||
OVRHumanBodyBonesMappings.BodySection.Hips, OVRHumanBodyBonesMappings.BodySection.Back,
|
||||
OVRHumanBodyBonesMappings.BodySection.Neck, OVRHumanBodyBonesMappings.BodySection.Head
|
||||
};
|
||||
|
||||
protected OVRHumanBodyBonesMappings.BodySection[] BodySectionsToAlign
|
||||
{
|
||||
get => _bodySectionsToAlign;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
protected OVRHumanBodyBonesMappings.BodySection[] _bodySectionToPosition =
|
||||
{
|
||||
OVRHumanBodyBonesMappings.BodySection.LeftArm, OVRHumanBodyBonesMappings.BodySection.RightArm,
|
||||
OVRHumanBodyBonesMappings.BodySection.LeftHand, OVRHumanBodyBonesMappings.BodySection.RightHand,
|
||||
OVRHumanBodyBonesMappings.BodySection.Hips, OVRHumanBodyBonesMappings.BodySection.Neck,
|
||||
OVRHumanBodyBonesMappings.BodySection.Head
|
||||
};
|
||||
|
||||
protected OVRHumanBodyBonesMappings.BodySection[] BodySectionToPosition
|
||||
{
|
||||
get => _bodySectionToPosition;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
Assert.IsTrue(OVRSkeleton.IsBodySkeleton(_skeletonType));
|
||||
|
||||
ValidateGameObjectForUnityHumanoidRetargeting(gameObject);
|
||||
_animatorTargetSkeleton = gameObject.GetComponent<Animator>();
|
||||
|
||||
CreateCustomBoneIdToHumanBodyBoneMapping();
|
||||
StoreTTargetPoseRotations();
|
||||
|
||||
_targetSkeletonData = new OVRSkeletonMetadata(_animatorTargetSkeleton);
|
||||
_targetSkeletonData.BuildCoordinateAxesForAllBones();
|
||||
}
|
||||
|
||||
internal static void ValidateGameObjectForUnityHumanoidRetargeting(GameObject go)
|
||||
{
|
||||
if (go.GetComponent<Animator>() == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Retargeting to Unity Humanoid requires an {nameof(Animator)} component with a humanoid avatar on T-Pose");
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreTTargetPoseRotations()
|
||||
{
|
||||
for (var i = HumanBodyBones.Hips; i < HumanBodyBones.LastBone; i++)
|
||||
{
|
||||
var boneTransform = _animatorTargetSkeleton.GetBoneTransform(i);
|
||||
_targetTPoseRotations[i] = boneTransform ? boneTransform.rotation : Quaternion.identity;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCustomBoneIdToHumanBodyBoneMapping()
|
||||
{
|
||||
CopyBoneIdToHumanBodyBoneMapping();
|
||||
AdjustCustomBoneIdToHumanBodyBoneMapping();
|
||||
}
|
||||
|
||||
private void CopyBoneIdToHumanBodyBoneMapping()
|
||||
{
|
||||
_customBoneIdToHumanBodyBone.Clear();
|
||||
foreach (var keyValuePair in OVRHumanBodyBonesMappings.BoneIdToHumanBodyBone)
|
||||
{
|
||||
_customBoneIdToHumanBodyBone.Add(keyValuePair.Key, keyValuePair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void AdjustCustomBoneIdToHumanBodyBoneMapping()
|
||||
{
|
||||
// if there is a mapping override that the user provided,
|
||||
// enforce it.
|
||||
foreach (var adjustment in _adjustments)
|
||||
{
|
||||
if (adjustment.BoneIdOverrideValue == OVRHumanBodyBonesMappings.BodyTrackingBoneId.NoOverride)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (adjustment.BoneIdOverrideValue == OVRHumanBodyBonesMappings.BodyTrackingBoneId.Remove)
|
||||
{
|
||||
RemoveMappingCorrespondingToHumanBodyBone(adjustment.Joint);
|
||||
}
|
||||
else
|
||||
{
|
||||
_customBoneIdToHumanBodyBone[(BoneId)adjustment.BoneIdOverrideValue]
|
||||
= adjustment.Joint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveMappingCorrespondingToHumanBodyBone(HumanBodyBones boneId)
|
||||
{
|
||||
foreach (var key in _customBoneIdToHumanBodyBone.Keys)
|
||||
{
|
||||
if (_customBoneIdToHumanBodyBone[key] == boneId)
|
||||
{
|
||||
_customBoneIdToHumanBodyBone.Remove(key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
UpdateSkeleton();
|
||||
|
||||
RecomputeSkeletalOffsetsIfNecessary();
|
||||
|
||||
AlignTargetWithSource();
|
||||
}
|
||||
|
||||
protected void RecomputeSkeletalOffsetsIfNecessary()
|
||||
{
|
||||
if (_lastSkelChangeCount != SkeletonChangedCount)
|
||||
{
|
||||
ComputeOffsetsUsingSkeletonComponent();
|
||||
}
|
||||
}
|
||||
|
||||
private void ComputeOffsetsUsingSkeletonComponent()
|
||||
{
|
||||
if (!IsInitialized ||
|
||||
BindPoses == null || BindPoses.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sourceSkeletonData == null)
|
||||
{
|
||||
_sourceSkeletonData = new OVRSkeletonMetadata(this, false, _customBoneIdToHumanBodyBone
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sourceSkeletonData.BuildBoneDataSkeleton(this, false, _customBoneIdToHumanBodyBone);
|
||||
}
|
||||
|
||||
_sourceSkeletonData.BuildCoordinateAxesForAllBones();
|
||||
|
||||
if (_sourceSkeletonTPoseData == null)
|
||||
{
|
||||
_sourceSkeletonTPoseData = new OVRSkeletonMetadata(this, true, _customBoneIdToHumanBodyBone
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sourceSkeletonTPoseData.BuildBoneDataSkeleton(this, true, _customBoneIdToHumanBodyBone);
|
||||
}
|
||||
|
||||
_sourceSkeletonTPoseData.BuildCoordinateAxesForAllBones();
|
||||
|
||||
for (var i = 0; i < BindPoses.Count; i++)
|
||||
{
|
||||
if (!_customBoneIdToHumanBodyBone.TryGetValue(BindPoses[i].Id, out var humanBodyBone))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_targetSkeletonData.BodyToBoneData.TryGetValue(humanBodyBone, out var targetData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var bodySection = OVRHumanBodyBonesMappings.BoneToBodySection[humanBodyBone];
|
||||
|
||||
if (!IsBodySectionInArray(bodySection,
|
||||
_bodySectionsToAlign
|
||||
))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_sourceSkeletonTPoseData.BodyToBoneData.TryGetValue(humanBodyBone, out var sourceTPoseData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_sourceSkeletonData.BodyToBoneData.TryGetValue(humanBodyBone, out var sourcePoseData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// if encountered degenerate source bones, skip
|
||||
if (sourceTPoseData.DegenerateJoint || sourcePoseData.DegenerateJoint)
|
||||
{
|
||||
targetData.CorrectionQuaternion = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
var forwardSource = sourceTPoseData.JointPairOrientation * Vector3.forward;
|
||||
var forwardTarget = targetData.JointPairOrientation * Vector3.forward;
|
||||
var targetToSrc = Quaternion.FromToRotation(forwardTarget, forwardSource);
|
||||
|
||||
var sourceRotationValueInv = Quaternion.Inverse(BindPoses[i].Transform.rotation);
|
||||
|
||||
targetData.CorrectionQuaternion =
|
||||
sourceRotationValueInv * targetToSrc * _targetTPoseRotations[humanBodyBone];
|
||||
}
|
||||
|
||||
_lastSkelChangeCount = SkeletonChangedCount;
|
||||
}
|
||||
|
||||
protected static bool IsBodySectionInArray(
|
||||
OVRHumanBodyBonesMappings.BodySection bodySectionToCheck,
|
||||
OVRHumanBodyBonesMappings.BodySection[] sectionArrayToCheck)
|
||||
{
|
||||
foreach (var bodySection in sectionArrayToCheck)
|
||||
{
|
||||
if (bodySection == bodySectionToCheck)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AlignTargetWithSource()
|
||||
{
|
||||
if (!IsInitialized || Bones == null || Bones.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < Bones.Count; i++)
|
||||
{
|
||||
if (!_customBoneIdToHumanBodyBone.TryGetValue(Bones[i].Id, out var humanBodyBone))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_targetSkeletonData.BodyToBoneData.TryGetValue(humanBodyBone, out var targetData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if we cannot map the joint at all.
|
||||
if (!targetData.CorrectionQuaternion.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetJoint = targetData.OriginalJoint;
|
||||
var correctionQuaternion = targetData.CorrectionQuaternion.Value;
|
||||
var adjustment = FindAdjustment(humanBodyBone);
|
||||
|
||||
var bodySectionOfJoint = OVRHumanBodyBonesMappings.BoneToBodySection[humanBodyBone];
|
||||
var shouldUpdatePosition = IsBodySectionInArray(
|
||||
bodySectionOfJoint,
|
||||
_bodySectionToPosition
|
||||
);
|
||||
|
||||
if (adjustment == null)
|
||||
{
|
||||
targetJoint.rotation = Bones[i].Transform.rotation * correctionQuaternion;
|
||||
if (shouldUpdatePosition)
|
||||
{
|
||||
targetJoint.position = Bones[i].Transform.position;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!adjustment.DisableRotationTransform)
|
||||
{
|
||||
targetJoint.rotation = Bones[i].Transform.rotation * correctionQuaternion;
|
||||
}
|
||||
|
||||
targetJoint.rotation *= adjustment.RotationChange;
|
||||
if (!adjustment.DisablePositionTransform && shouldUpdatePosition)
|
||||
{
|
||||
targetJoint.position = Bones[i].Transform.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected JointAdjustment FindAdjustment(HumanBodyBones boneId)
|
||||
{
|
||||
foreach (var adjustment in _adjustments)
|
||||
{
|
||||
if (adjustment.Joint == boneId)
|
||||
{
|
||||
return adjustment;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user