RoboticArms/Library/PackageCache/com.unity.animation.rigging@68167b505d2b/Runtime/AnimationRig/RigUtils.cs
2025-11-17 15:16:36 +07:00

335 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace UnityEngine.Animations.Rigging
{
static class RigUtils
{
internal static readonly Dictionary<Type, PropertyDescriptor> s_SupportedPropertyTypeToDescriptor = new Dictionary<Type, PropertyDescriptor>
{
{ typeof(float) , new PropertyDescriptor{ size = 1, type = PropertyType.Float } },
{ typeof(int) , new PropertyDescriptor{ size = 1, type = PropertyType.Int } },
{ typeof(bool) , new PropertyDescriptor{ size = 1, type = PropertyType.Bool } },
{ typeof(Vector2) , new PropertyDescriptor{ size = 2, type = PropertyType.Float } },
{ typeof(Vector3) , new PropertyDescriptor{ size = 3, type = PropertyType.Float } },
{ typeof(Vector4) , new PropertyDescriptor{ size = 4, type = PropertyType.Float } },
{ typeof(Quaternion) , new PropertyDescriptor{ size = 4, type = PropertyType.Float } },
{ typeof(Vector3Int) , new PropertyDescriptor{ size = 3, type = PropertyType.Int } },
{ typeof(Vector3Bool), new PropertyDescriptor{ size = 3, type = PropertyType.Bool } }
};
public static IRigConstraint[] GetConstraints(Rig rig)
{
IRigConstraint[] constraints = rig.GetComponentsInChildren<IRigConstraint>();
if (constraints.Length == 0)
return null;
List<IRigConstraint> tmp = new List<IRigConstraint>(constraints.Length);
foreach (var constraint in constraints)
{
if (constraint.IsValid())
tmp.Add(constraint);
}
return tmp.Count == 0 ? null : tmp.ToArray();
}
private static Transform[] GetSyncableRigTransforms(Animator animator)
{
RigTransform[] rigTransforms = animator.GetComponentsInChildren<RigTransform>();
if (rigTransforms.Length == 0)
return null;
Transform[] transforms = new Transform[rigTransforms.Length];
for (int i = 0; i < transforms.Length; ++i)
transforms[i] = rigTransforms[i].transform;
return transforms;
}
private static bool ExtractTransformType(
Animator animator,
FieldInfo field,
object data,
List<Transform> syncableTransforms
)
{
bool handled = true;
Type fieldType = field.FieldType;
if (fieldType == typeof(Transform))
{
var value = (Transform)field.GetValue(data);
if (value != null && value.IsChildOf(animator.avatarRoot))
syncableTransforms.Add(value);
}
else if (fieldType == typeof(Transform[]) || fieldType == typeof(List<Transform>))
{
var list = (IEnumerable<Transform>)field.GetValue(data);
foreach (var element in list)
if (element != null && element.IsChildOf(animator.avatarRoot))
syncableTransforms.Add(element);
}
else
handled = false;
return handled;
}
private static bool ExtractPropertyType(
FieldInfo field,
object data,
List<Property> syncableProperties,
string namePrefix = ""
)
{
if (!s_SupportedPropertyTypeToDescriptor.TryGetValue(field.FieldType, out PropertyDescriptor descriptor))
return false;
syncableProperties.Add(
new Property { name = ConstraintsUtils.ConstructConstraintDataPropertyName(namePrefix + field.Name), descriptor = descriptor }
);
return true;
}
private static bool ExtractWeightedTransforms(
Animator animator,
FieldInfo field,
object data,
List<Transform> syncableTransforms,
List<Property> syncableProperties)
{
bool handled = true;
Type fieldType = field.FieldType;
if (fieldType == typeof(WeightedTransform))
{
var value = ((WeightedTransform)field.GetValue(data)).transform;
if (value != null && value.IsChildOf(animator.avatarRoot))
syncableTransforms.Add(value);
syncableProperties.Add(
new Property { name = ConstraintsUtils.ConstructConstraintDataPropertyName(field.Name + ".weight"), descriptor = s_SupportedPropertyTypeToDescriptor[typeof(float)] }
);
}
else if (fieldType == typeof(WeightedTransformArray))
{
var list = (IEnumerable<WeightedTransform>)field.GetValue(data);
int index = 0;
foreach (var element in list)
{
if (element.transform != null && element.transform.IsChildOf(animator.avatarRoot))
syncableTransforms.Add(element.transform);
syncableProperties.Add(
new Property { name = ConstraintsUtils.ConstructConstraintDataPropertyName(field.Name + ".m_Item" + index + ".weight"), descriptor = s_SupportedPropertyTypeToDescriptor[typeof(float)] }
);
++index;
}
}
else
handled = false;
return handled;
}
private static bool ExtractNestedPropertyType(
Animator animator,
FieldInfo field,
object data,
List<Transform> syncableTransforms,
List<Property> syncableProperties,
string namePrefix = "")
{
Type fieldType = field.FieldType;
var fieldData = field.GetValue(data);
var fieldName = namePrefix + field.Name + ".";
// Only structs
if (!fieldType.IsValueType || fieldType.IsPrimitive)
return false;
var fields = fieldType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
).Where(info => info.GetCustomAttribute<SyncSceneToStreamAttribute>() != null);
foreach (var childField in fields)
{
if (ExtractTransformType(animator, childField, fieldData, syncableTransforms))
continue;
if (ExtractPropertyType(childField, fieldData, syncableProperties, fieldName))
continue;
if (ExtractNestedPropertyType(animator, childField, fieldData, syncableTransforms, syncableProperties, fieldName))
continue;
throw new NotSupportedException("Field type [" + field.FieldType + "] is not a supported syncable property type.");
}
return true;
}
private static void ExtractAllSyncableData(Animator animator, IList<IRigLayer> layers, out List<Transform> syncableTransforms, out List<SyncableProperties> syncableProperties)
{
syncableTransforms = new List<Transform>();
syncableProperties = new List<SyncableProperties>(layers.Count);
Dictionary<Type, FieldInfo[]> typeToSyncableFields = new Dictionary<Type, FieldInfo[]>();
foreach (var layer in layers)
{
if (!layer.IsValid())
continue;
var constraints = layer.constraints;
List<ConstraintProperties> allConstraintProperties = new List<ConstraintProperties>(constraints.Length);
foreach (var constraint in constraints)
{
var data = constraint.data;
var dataType = constraint.data.GetType();
if (!typeToSyncableFields.TryGetValue(dataType, out FieldInfo[] syncableFields))
{
FieldInfo[] allFields = dataType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
);
List<FieldInfo> filteredFields = new List<FieldInfo>(allFields.Length);
foreach (var field in allFields)
if (field.GetCustomAttribute<SyncSceneToStreamAttribute>() != null)
filteredFields.Add(field);
syncableFields = filteredFields.ToArray();
typeToSyncableFields[dataType] = syncableFields;
}
List<Property> properties = new List<Property>(syncableFields.Length);
foreach (var field in syncableFields)
{
if (ExtractWeightedTransforms(animator, field, data, syncableTransforms, properties))
continue;
if (ExtractTransformType(animator, field, data, syncableTransforms))
continue;
if (ExtractPropertyType(field, data, properties))
continue;
if (ExtractNestedPropertyType(animator, field, data, syncableTransforms, properties))
continue;
throw new NotSupportedException("Field type [" + field.FieldType + "] is not a supported syncable property type.");
}
allConstraintProperties.Add(
new ConstraintProperties {
component = constraint.component,
properties = properties.ToArray()
}
);
}
syncableProperties.Add(
new SyncableProperties {
rig = new RigProperties { component = layer.rig as Component },
constraints = allConstraintProperties.ToArray()
}
);
}
var extraTransforms = GetSyncableRigTransforms(animator);
if (extraTransforms != null)
syncableTransforms.AddRange(extraTransforms);
}
public static IAnimationJob[] CreateAnimationJobs(Animator animator, IRigConstraint[] constraints)
{
if (constraints == null || constraints.Length == 0)
return null;
IAnimationJob[] jobs = new IAnimationJob[constraints.Length];
for (int i = 0; i < constraints.Length; ++i)
jobs[i] = constraints[i].CreateJob(animator);
return jobs;
}
public static void DestroyAnimationJobs(IRigConstraint[] constraints, IAnimationJob[] jobs)
{
if (jobs == null || jobs.Length != constraints.Length)
return;
for (int i = 0; i < constraints.Length; ++i)
constraints[i].DestroyJob(jobs[i]);
}
private struct RigSyncSceneToStreamData : IAnimationJobData, IRigSyncSceneToStreamData
{
public RigSyncSceneToStreamData(Transform[] transforms, SyncableProperties[] properties, int rigCount)
{
if (transforms != null && transforms.Length > 0)
{
var unique = UniqueTransformIndices(transforms);
if (unique.Length != transforms.Length)
{
syncableTransforms = new Transform[unique.Length];
for (int i = 0; i < unique.Length; ++i)
syncableTransforms[i] = transforms[unique[i]];
}
else
syncableTransforms = transforms;
}
else
syncableTransforms = null;
syncableProperties = properties;
rigStates = rigCount > 0 ? new bool[rigCount] : null;
m_IsValid = !(((syncableTransforms == null || syncableTransforms.Length == 0) &&
(syncableProperties == null || syncableProperties.Length == 0) &&
rigStates == null));
}
static int[] UniqueTransformIndices(Transform[] transforms)
{
if (transforms == null || transforms.Length == 0)
return null;
HashSet<int> instanceIDs = new HashSet<int>();
List<int> unique = new List<int>(transforms.Length);
for (int i = 0; i < transforms.Length; ++i)
if (instanceIDs.Add(transforms[i].GetInstanceID()))
unique.Add(i);
return unique.ToArray();
}
public Transform[] syncableTransforms { get; private set; }
public SyncableProperties[] syncableProperties { get; private set; }
public bool[] rigStates { get; set; }
private readonly bool m_IsValid;
bool IAnimationJobData.IsValid() => m_IsValid;
void IAnimationJobData.SetDefaultValues()
{
syncableTransforms = null;
syncableProperties = null;
rigStates = null;
}
}
internal static IAnimationJobData CreateSyncSceneToStreamData(Animator animator, IList<IRigLayer> layers)
{
ExtractAllSyncableData(animator, layers, out List<Transform> syncableTransforms, out List<SyncableProperties> syncableProperties);
return new RigSyncSceneToStreamData(syncableTransforms.ToArray(), syncableProperties.ToArray(), layers.Count);
}
public static IAnimationJobBinder syncSceneToStreamBinder { get; } = new RigSyncSceneToStreamJobBinder<RigSyncSceneToStreamData>();
}
}