VR4RoboticArm2/VR4RoboticArm/Library/PackageCache/com.meta.xr.sdk.movement/Runtime/Native/Scripts/Retargeting/CharacterRetargeterConfig.cs
IonutMocanu 48cccc22ad Main2
2025-09-08 11:13:29 +03:00

312 lines
10 KiB
C#

// Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
using System;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Jobs;
using static Meta.XR.Movement.MSDKUtility;
namespace Meta.XR.Movement.Retargeting
{
/// <summary>
/// The joint index that is represented as a name by the character retargeter config.
/// </summary>
[Serializable]
public struct TargetJointIndex : IComparable
{
/// <summary>
/// The index of the target joint.
/// </summary>
[SerializeField]
public int Index;
/// <summary>
/// Create a target joint index.
/// </summary>
/// <param name="index">The index value</param>
public TargetJointIndex(int index)
{
Index = index;
}
/// <summary>
/// Compares this TargetJointIndex to another object for ordering.
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>A value indicating the relative order of the objects being compared.</returns>
/// <exception cref="ArgumentException">Thrown when the object is not a TargetJointIndex.</exception>
public int CompareTo(object obj)
{
if (obj is TargetJointIndex other)
{
return Index.CompareTo(other.Index);
}
throw new ArgumentException("Object is not a JointIndex", nameof(obj));
}
/// <summary>
/// Determines whether this instance is equal to another TargetJointIndex.
/// </summary>
/// <param name="other">The TargetJointIndex to compare with this instance.</param>
/// <returns>True if the specified TargetJointIndex has the same Index as this instance; otherwise, false.</returns>
public bool Equals(TargetJointIndex other)
{
return Index == other.Index;
}
/// <summary>
/// Implicitly converts a TargetJointIndex to an integer.
/// </summary>
/// <param name="targetJointIndex">The TargetJointIndex to convert.</param>
/// <returns>The integer value of the TargetJointIndex's Index.</returns>
public static implicit operator int(TargetJointIndex targetJointIndex)
{
return targetJointIndex.Index;
}
/// <summary>
/// Implicitly converts an integer to a TargetJointIndex.
/// </summary>
/// <param name="index">The integer value to convert.</param>
/// <returns>A new TargetJointIndex with the specified index.</returns>
public static implicit operator TargetJointIndex(int index)
{
return new TargetJointIndex(index);
}
}
/// <summary>
/// Indicates what kind of world space to use.
/// </summary>
public enum JointType
{
NoWorldSpace = 0,
WorldSpaceRootOnly = 1,
WorldSpaceAllJoints = 2
}
/// <summary>
/// Contain configuration information for retargeting poses.
/// </summary>
public class CharacterRetargeterConfig : MonoBehaviour
{
/// <summary>
/// Store information about a pair of joint transforms.
/// </summary>
[Serializable]
public struct JointPair
{
/// <summary>
/// The joint transform.
/// </summary>
public Transform Joint;
/// <summary>
/// The parent joint transform.
/// </summary>
public Transform ParentJoint;
}
/// <summary>
/// Represents shape pose data.
/// </summary>
[Serializable]
public struct ShapePoseData
{
/// <summary>
/// The skinned mesh renderer.
/// </summary>
public SkinnedMeshRenderer SkinnedMesh;
/// <summary>
/// The shape name.
/// </summary>
public string ShapeName;
/// <summary>
/// The shape index.
/// </summary>
public int ShapeIndex;
/// <summary>
/// Initializes the <see cref="ShapePoseData"/> struct.
/// </summary>
/// <param name="skinnedMesh">The skinned mesh renderer.</param>
/// <param name="shapeName">The shape name.</param>
/// <param name="shapeIndex">The shape index.</param>
public ShapePoseData(SkinnedMeshRenderer skinnedMesh, string shapeName, int shapeIndex)
{
SkinnedMesh = skinnedMesh;
ShapeName = shapeName;
ShapeIndex = shapeIndex;
}
}
/// <summary>
/// Gets the configuration text.
/// </summary>
public string Config => _config != null ? _config.text : string.Empty;
/// <summary>
/// Getter and setter for the configuration asset.
/// </summary>
public TextAsset ConfigAsset
{
get => _config;
set => _config = value;
}
/// <summary>
/// Gets the number of joints in the configuration.
/// </summary>
public int NumberOfJoints => _jointPairs?.Length ?? 0;
/// <summary>
/// The joint pairs for the character retargeter corresponding to this object.
/// </summary>
public JointPair[] JointPairs => _jointPairs;
/// <summary>
/// Gets the number of shapes in the configuration.
/// </summary>
public int NumberOfShapes => _shapePoseData.Length;
/// <summary>
/// Joints used for retargeted character.
/// </summary>
public TransformAccessArray Joints => _joints;
/// <summary>
/// The configuration text asset containing retargeting data.
/// </summary>
[SerializeField]
protected TextAsset _config;
/// <summary>
/// The joint pair data from the configuration, mapping joints to their parent joints.
/// </summary>
[SerializeField]
protected JointPair[] _jointPairs;
/// <summary>
/// The shape pose data from the configuration, used for facial expressions and blendshapes.
/// </summary>
[SerializeField]
protected ShapePoseData[] _shapePoseData;
protected TransformAccessArray _joints;
/// <summary>
/// Initializes the TransformAccessArray with joint transforms when the component starts.
/// </summary>
public virtual void Start()
{
// Fill out joints and native information.
var joints = new Transform[_jointPairs.Length];
for (var i = 0; i < _jointPairs.Length; i++)
{
joints[i] = _jointPairs[i].Joint;
}
_joints = new TransformAccessArray(joints);
}
/// <summary>
/// Gets the current body pose.
/// </summary>
/// <param name="jointType">The joint type.</param>
/// <returns></returns>
public NativeArray<NativeTransform> GetCurrentBodyPose(JointType jointType)
{
var bodyPose = new NativeArray<NativeTransform>(_jointPairs.Length, Allocator.TempJob);
var job = new SkeletonJobs.GetPoseJob
{
BodyPose = bodyPose,
JobJointType = jointType
};
job.Schedule(_joints).Complete();
return bodyPose;
}
/// <summary>
/// Gets the current face pose.
/// </summary>
/// <returns>The current face pose.</returns>
public NativeArray<float> GetCurrentFacePose()
{
// create flattened array to be used for data serialization.
int numShapesTotal = NumberOfShapes;
var faceShapePoses = new NativeArray<float>(numShapesTotal,
Allocator.Temp,
NativeArrayOptions.UninitializedMemory);
int shapePoseIndex = 0;
foreach (var shape in _shapePoseData)
{
Assert.IsNotNull(shape.SkinnedMesh, "Skinned mesh null.");
faceShapePoses[shapePoseIndex++] = shape.SkinnedMesh.GetBlendShapeWeight(shape.ShapeIndex);
}
return faceShapePoses;
}
/// <summary>
/// Applies the specified body pose to the character.
/// </summary>
/// <param name="bodyPose">The body pose to apply.</param>
/// <param name="jointType">The joint type.</param>
public void ApplyBodyPose(NativeArray<NativeTransform> bodyPose, JointType jointType)
{
for (var i = 0; i < _jointPairs.Length; i++)
{
var joint = _jointPairs[i].Joint;
var pose = bodyPose[i];
var useWorldSpace = UseWorldSpace(i, jointType);
if (useWorldSpace)
{
joint.SetPositionAndRotation(pose.Position, pose.Orientation);
}
else
{
joint.SetLocalPositionAndRotation(pose.Position, pose.Orientation);
}
}
}
/// <summary>
/// Applies the given face pose to the character.
/// </summary>
/// <param name="facePose">The face pose to apply.</param>
public void ApplyFacePose(NativeArray<float> facePose)
{
var shapePoseIndex = 0;
foreach (var shape in _shapePoseData)
{
shape.SkinnedMesh.SetBlendShapeWeight(
shape.ShapeIndex, facePose[shapePoseIndex++]);
}
}
private bool UseWorldSpace(int jointIndex, JointType jointType)
{
bool isRoot = jointIndex == 0;
bool useWorldSpace = false;
if (jointType == JointType.WorldSpaceRootOnly)
{
useWorldSpace = isRoot;
}
else if (jointType == JointType.WorldSpaceAllJoints)
{
useWorldSpace = true;
}
return useWorldSpace;
}
}
}