672 lines
28 KiB
C#
672 lines
28 KiB
C#
using System;
|
|
using SevenZip;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
|
|
namespace AssetStudio
|
|
{
|
|
public sealed class CustomCurveResolver
|
|
{
|
|
public CustomCurveResolver(AnimationClip clip)
|
|
{
|
|
if (clip == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(clip));
|
|
}
|
|
m_clip = clip;
|
|
}
|
|
|
|
public string ToAttributeName(BindingCustomType type, uint attribute, string path)
|
|
{
|
|
switch (type)
|
|
{
|
|
case BindingCustomType.BlendShape:
|
|
{
|
|
const string Prefix = "blendShape.";
|
|
if (AnimationClipConverter.UnknownPathRegex.IsMatch(path))
|
|
{
|
|
return Prefix + attribute;
|
|
}
|
|
|
|
foreach (GameObject root in Roots)
|
|
{
|
|
var rootTransform = root.m_Transform;
|
|
var child = rootTransform.FindChild(path);
|
|
if (child == null)
|
|
{
|
|
continue;
|
|
}
|
|
SkinnedMeshRenderer skin = null;
|
|
if (child.m_GameObject.TryGet(out var go))
|
|
{
|
|
skin = go.m_SkinnedMeshRenderer;
|
|
}
|
|
if (skin == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (!skin.m_Mesh.TryGet(out var mesh))
|
|
{
|
|
continue;
|
|
}
|
|
string shapeName = mesh.FindBlendShapeNameByCRC(attribute);
|
|
if (shapeName == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return Prefix + shapeName;
|
|
}
|
|
return Prefix + attribute;
|
|
}
|
|
|
|
case BindingCustomType.Renderer:
|
|
return "m_Materials." + CommonString.StringBuffer[0x31] + "." + CommonString.StringBuffer[0x6A] + $"[{attribute}]";
|
|
|
|
case BindingCustomType.RendererMaterial:
|
|
{
|
|
const string Prefix = "material.";
|
|
if (AnimationClipConverter.UnknownPathRegex.IsMatch(path))
|
|
{
|
|
return Prefix + attribute;
|
|
}
|
|
|
|
foreach (GameObject root in Roots)
|
|
{
|
|
Transform rootTransform = root.m_Transform;
|
|
Transform child = rootTransform.FindChild(path);
|
|
if (child == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
uint crc28 = attribute & 0xFFFFFFF;
|
|
Renderer renderer = null;
|
|
if (child.m_GameObject.TryGet(out var go))
|
|
{
|
|
renderer = (Renderer)go.m_SkinnedMeshRenderer ?? go.m_MeshRenderer;
|
|
}
|
|
if (renderer == null)
|
|
{
|
|
continue;
|
|
}
|
|
string property = renderer.FindMaterialPropertyNameByCRC28(crc28);
|
|
if (property == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ((attribute & 0x80000000) != 0)
|
|
{
|
|
return Prefix + property;
|
|
}
|
|
char subProperty;
|
|
uint subPropIndex = attribute >> 28 & 3;
|
|
bool isRgba = (attribute & 0x40000000) != 0;
|
|
switch (subPropIndex)
|
|
{
|
|
case 0:
|
|
subProperty = isRgba ? 'r' : 'x';
|
|
break;
|
|
case 1:
|
|
subProperty = isRgba ? 'g' : 'y';
|
|
break;
|
|
case 2:
|
|
subProperty = isRgba ? 'b' : 'z';
|
|
break;
|
|
|
|
default:
|
|
subProperty = isRgba ? 'a' : 'w';
|
|
break;
|
|
}
|
|
return Prefix + property + "." + subProperty;
|
|
}
|
|
return Prefix + attribute;
|
|
}
|
|
|
|
case BindingCustomType.SpriteRenderer:
|
|
{
|
|
if (attribute == 0)
|
|
{
|
|
return "m_Sprite";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
case BindingCustomType.MonoBehaviour:
|
|
{
|
|
if (attribute == CRC.CalculateDigestAscii("m_Enabled"))
|
|
{
|
|
return "m_Enabled";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
case BindingCustomType.Light:
|
|
{
|
|
string ColorR = "m_Color.r";
|
|
if (attribute == CRC.CalculateDigestAscii(ColorR))
|
|
{
|
|
return ColorR;
|
|
}
|
|
string ColorG = "m_Color.g";
|
|
if (attribute == CRC.CalculateDigestAscii(ColorG))
|
|
{
|
|
return ColorG;
|
|
}
|
|
string ColorB = "m_Color.b";
|
|
if (attribute == CRC.CalculateDigestAscii(ColorB))
|
|
{
|
|
return ColorB;
|
|
}
|
|
string ColorA = "m_Color.a";
|
|
if (attribute == CRC.CalculateDigestAscii(ColorA))
|
|
{
|
|
return ColorA;
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_CookieSize"))
|
|
{
|
|
return "m_CookieSize";
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_DrawHalo"))
|
|
{
|
|
return "m_DrawHalo";
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_Intensity"))
|
|
{
|
|
return "m_Intensity";
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_Range"))
|
|
{
|
|
return "m_Range";
|
|
}
|
|
const string ShadowsStrength = "m_Shadows.m_Strength";
|
|
if (attribute == CRC.CalculateDigestAscii(ShadowsStrength))
|
|
{
|
|
return ShadowsStrength;
|
|
}
|
|
const string ShadowsBias = "m_Shadows.m_Bias";
|
|
if (attribute == CRC.CalculateDigestAscii(ShadowsBias))
|
|
{
|
|
return ShadowsBias;
|
|
}
|
|
const string ShadowsNormalBias = "m_Shadows.m_NormalBias";
|
|
if (attribute == CRC.CalculateDigestAscii(ShadowsNormalBias))
|
|
{
|
|
return ShadowsNormalBias;
|
|
}
|
|
const string ShadowsNearPlane = "m_Shadows.m_NearPlane";
|
|
if (attribute == CRC.CalculateDigestAscii(ShadowsNearPlane))
|
|
{
|
|
return ShadowsNearPlane;
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_SpotAngle"))
|
|
{
|
|
return "m_SpotAngle";
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_ColorTemperature"))
|
|
{
|
|
return "m_ColorTemperature";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
case BindingCustomType.RendererShadows:
|
|
{
|
|
if (attribute == CRC.CalculateDigestAscii("m_ReceiveShadows"))
|
|
{
|
|
return "m_ReceiveShadows";
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_SortingOrder"))
|
|
{
|
|
return "m_SortingOrder";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.ParticleSystem:
|
|
return "ParticleSystem_" + attribute;
|
|
/*{
|
|
// TODO: ordinal propertyName
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {_this}");*/
|
|
|
|
case BindingCustomType.RectTransform:
|
|
{
|
|
string LocalPositionZ = "m_LocalPosition.z";
|
|
if (attribute == CRC.CalculateDigestAscii(LocalPositionZ))
|
|
{
|
|
return LocalPositionZ;
|
|
}
|
|
string AnchoredPositionX = "m_AnchoredPosition.x";
|
|
if (attribute == CRC.CalculateDigestAscii(AnchoredPositionX))
|
|
{
|
|
return AnchoredPositionX;
|
|
}
|
|
string AnchoredPositionY = "m_AnchoredPosition.y";
|
|
if (attribute == CRC.CalculateDigestAscii(AnchoredPositionY))
|
|
{
|
|
return AnchoredPositionY;
|
|
}
|
|
string AnchorMinX = "m_AnchorMin.x";
|
|
if (attribute == CRC.CalculateDigestAscii(AnchorMinX))
|
|
{
|
|
return AnchorMinX;
|
|
}
|
|
string AnchorMinY = "m_AnchorMin.y";
|
|
if (attribute == CRC.CalculateDigestAscii(AnchorMinY))
|
|
{
|
|
return AnchorMinY;
|
|
}
|
|
string AnchorMaxX = "m_AnchorMax.x";
|
|
if (attribute == CRC.CalculateDigestAscii(AnchorMaxX))
|
|
{
|
|
return AnchorMaxX;
|
|
}
|
|
string AnchorMaxY = "m_AnchorMax.y";
|
|
if (attribute == CRC.CalculateDigestAscii(AnchorMaxY))
|
|
{
|
|
return AnchorMaxY;
|
|
}
|
|
string SizeDeltaX = "m_SizeDelta.x";
|
|
if (attribute == CRC.CalculateDigestAscii(SizeDeltaX))
|
|
{
|
|
return SizeDeltaX;
|
|
}
|
|
string SizeDeltaY = "m_SizeDelta.y";
|
|
if (attribute == CRC.CalculateDigestAscii(SizeDeltaY))
|
|
{
|
|
return SizeDeltaY;
|
|
}
|
|
string PivotX = "m_Pivot.x";
|
|
if (attribute == CRC.CalculateDigestAscii(PivotX))
|
|
{
|
|
return PivotX;
|
|
}
|
|
string PivotY = "m_Pivot.y";
|
|
if (attribute == CRC.CalculateDigestAscii(PivotY))
|
|
{
|
|
return PivotY;
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.LineRenderer:
|
|
{
|
|
const string ParametersWidthMultiplier = "m_Parameters" + "." + "widthMultiplier";
|
|
if (attribute == CRC.CalculateDigestAscii(ParametersWidthMultiplier))
|
|
{
|
|
return ParametersWidthMultiplier;
|
|
}
|
|
}
|
|
// TODO: old versions animate all properties as custom curves
|
|
return "LineRenderer_" + attribute;
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.TrailRenderer:
|
|
{
|
|
const string ParametersWidthMultiplier = "m_Parameters" + "." + "widthMultiplier";
|
|
if (attribute == CRC.CalculateDigestAscii(ParametersWidthMultiplier))
|
|
{
|
|
return ParametersWidthMultiplier;
|
|
}
|
|
}
|
|
// TODO: old versions animate all properties as custom curves
|
|
return "TrailRenderer_" + attribute;
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.PositionConstraint:
|
|
{
|
|
uint property = attribute & 0xF;
|
|
switch (property)
|
|
{
|
|
case 0:
|
|
return "m_RestTranslation.x";
|
|
case 1:
|
|
return "m_RestTranslation.y";
|
|
case 2:
|
|
return "m_RestTranslation.z";
|
|
case 3:
|
|
return "m_Weight";
|
|
case 4:
|
|
return "m_TranslationOffset.x";
|
|
case 5:
|
|
return "m_TranslationOffset.y";
|
|
case 6:
|
|
return "m_TranslationOffset.z";
|
|
case 7:
|
|
return "m_AffectTranslationX";
|
|
case 8:
|
|
return "m_AffectTranslationY";
|
|
case 9:
|
|
return "m_AffectTranslationZ";
|
|
case 10:
|
|
return "m_Active";
|
|
case 11:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
|
|
case 12:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].weight";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.RotationConstraint:
|
|
{
|
|
uint property = attribute & 0xF;
|
|
switch (property)
|
|
{
|
|
case 0:
|
|
return "m_RestRotation.x";
|
|
case 1:
|
|
return "m_RestRotation.y";
|
|
case 2:
|
|
return "m_RestRotation.z";
|
|
case 3:
|
|
return "m_Weight";
|
|
case 4:
|
|
return "m_RotationOffset.x";
|
|
case 5:
|
|
return "m_RotationOffset.y";
|
|
case 6:
|
|
return "m_RotationOffset.z";
|
|
case 7:
|
|
return "m_AffectRotationX";
|
|
case 8:
|
|
return "m_AffectRotationY";
|
|
case 9:
|
|
return "m_AffectRotationZ";
|
|
case 10:
|
|
return "m_Active";
|
|
case 11:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
|
|
case 12:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].weight";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.ScaleConstraint:
|
|
{
|
|
uint property = attribute & 0xF;
|
|
switch (property)
|
|
{
|
|
case 0:
|
|
return "m_ScaleAtRest.x";
|
|
case 1:
|
|
return "m_ScaleAtRest.y";
|
|
case 2:
|
|
return "m_ScaleAtRest.z";
|
|
case 3:
|
|
return "m_Weight";
|
|
case 4:
|
|
return "m_ScalingOffset.x";
|
|
case 5:
|
|
return "m_ScalingOffset.y";
|
|
case 6:
|
|
return "m_ScalingOffset.z";
|
|
case 7:
|
|
return "m_AffectScalingX";
|
|
case 8:
|
|
return "m_AffectScalingY";
|
|
case 9:
|
|
return "m_AffectScalingZ";
|
|
case 10:
|
|
return "m_Active";
|
|
case 11:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
|
|
case 12:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].weight";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.AimConstraint:
|
|
{
|
|
uint property = attribute & 0xF;
|
|
switch (property)
|
|
{
|
|
case 0:
|
|
return "m_Weight";
|
|
case 1:
|
|
return "m_AffectRotationX";
|
|
case 2:
|
|
return "m_AffectRotationY";
|
|
case 3:
|
|
return "m_AffectRotationZ";
|
|
case 4:
|
|
return "m_Active";
|
|
case 5:
|
|
return "m_WorldUpObject";
|
|
case 6:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
|
|
case 7:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].weight";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.ParentConstraint:
|
|
{
|
|
uint property = attribute & 0xF;
|
|
switch (property)
|
|
{
|
|
case 0:
|
|
return "m_Weight";
|
|
case 1:
|
|
return "m_AffectTranslationX";
|
|
case 2:
|
|
return "m_AffectTranslationY";
|
|
case 3:
|
|
return "m_AffectTranslationZ";
|
|
case 4:
|
|
return "m_AffectRotationX";
|
|
case 5:
|
|
return "m_AffectRotationY";
|
|
case 6:
|
|
return "m_AffectRotationZ";
|
|
case 7:
|
|
return "m_Active";
|
|
case 8:
|
|
return $"m_TranslationOffsets.Array.data[{attribute >> 8}].x";
|
|
case 9:
|
|
return $"m_TranslationOffsets.Array.data[{attribute >> 8}].y";
|
|
case 10:
|
|
return $"m_TranslationOffsets.Array.data[{attribute >> 8}].z";
|
|
case 11:
|
|
return $"m_RotationOffsets.Array.data[{attribute >> 8}].x";
|
|
case 12:
|
|
return $"m_RotationOffsets.Array.data[{attribute >> 8}].y";
|
|
case 13:
|
|
return $"m_RotationOffsets.Array.data[{attribute >> 8}].z";
|
|
case 14:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
|
|
case 15:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].weight";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
#warning TODO:
|
|
case BindingCustomType.LookAtConstraint:
|
|
{
|
|
uint property = attribute & 0xF;
|
|
switch (property)
|
|
{
|
|
case 0:
|
|
return "m_Weight";
|
|
case 1:
|
|
return "m_Active";
|
|
case 2:
|
|
return "m_WorldUpObject";
|
|
case 3:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
|
|
case 4:
|
|
return $"m_Sources.Array.data[{attribute >> 8}].weight";
|
|
case 5:
|
|
return "m_Roll";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
case BindingCustomType.Camera:
|
|
{
|
|
if (attribute == CRC.CalculateDigestAscii("field of view"))
|
|
{
|
|
return "field of view";
|
|
}
|
|
if (attribute == CRC.CalculateDigestAscii("m_FocalLength"))
|
|
{
|
|
return "m_FocalLength";
|
|
}
|
|
}
|
|
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
|
|
|
|
default:
|
|
throw new ArgumentException(type.ToString());
|
|
}
|
|
}
|
|
|
|
private GameObject[] Roots
|
|
{
|
|
get
|
|
{
|
|
if (!m_rootInited)
|
|
{
|
|
m_roots = m_clip.FindRoots().ToArray();
|
|
m_rootInited = true;
|
|
}
|
|
return m_roots;
|
|
}
|
|
}
|
|
|
|
private readonly AnimationClip m_clip = null;
|
|
|
|
private GameObject[] m_roots = null;
|
|
private bool m_rootInited = false;
|
|
}
|
|
|
|
public static class CustomCurveResolverExtensions
|
|
{
|
|
public static Transform FindChild(this Transform transform, string path)
|
|
{
|
|
if (path.Length == 0)
|
|
{
|
|
return transform;
|
|
}
|
|
return transform.FindChild(path, 0);
|
|
}
|
|
|
|
private static Transform FindChild(this Transform transform, string path, int startIndex)
|
|
{
|
|
int separatorIndex = path.IndexOf('/', startIndex);
|
|
string childName = separatorIndex == -1 ?
|
|
path.Substring(startIndex, path.Length - startIndex) :
|
|
path.Substring(startIndex, separatorIndex - startIndex);
|
|
|
|
foreach (PPtr<Transform> childPtr in transform.m_Children)
|
|
{
|
|
if (childPtr.TryGet(out var child))
|
|
{
|
|
if (child.m_GameObject.TryGet(out var childGO) && childGO.m_Name == childName)
|
|
{
|
|
return separatorIndex == -1 ? child : child.FindChild(path, separatorIndex + 1);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static string FindBlendShapeNameByCRC(this Mesh mesh, uint crc)
|
|
{
|
|
if (mesh.version[0] > 4 || (mesh.version[0] == 4 && mesh.version[1] >= 3))
|
|
{
|
|
return mesh.m_Shapes.FindShapeNameByCRC(crc);
|
|
}
|
|
else
|
|
{
|
|
foreach (var blendShape in mesh.m_Shapes.shapes)
|
|
{
|
|
if (CRC.VerifyDigestUTF8(blendShape.name, crc))
|
|
{
|
|
return blendShape.name;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static string FindShapeNameByCRC(this BlendShapeData blendShapeData, uint crc)
|
|
{
|
|
foreach (var blendChannel in blendShapeData.channels)
|
|
{
|
|
if (blendChannel.nameHash == crc)
|
|
{
|
|
return blendChannel.name;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static string FindMaterialPropertyNameByCRC28(this Renderer renderer, uint crc)
|
|
{
|
|
foreach (PPtr<Material> materialPtr in renderer.m_Materials)
|
|
{
|
|
if (!materialPtr.TryGet(out var material))
|
|
{
|
|
continue;
|
|
}
|
|
string property = material.FindPropertyNameByCRC28(crc);
|
|
if (property == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return property;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static string FindPropertyNameByCRC28(this Material material, uint crc)
|
|
{
|
|
foreach (var property in material.m_SavedProperties.m_TexEnvs.Keys)
|
|
{
|
|
string hdrName = $"{property}_HDR";
|
|
if (CRC.Verify28DigestUTF8(hdrName, crc))
|
|
{
|
|
return hdrName;
|
|
}
|
|
string stName = $"{property}_ST";
|
|
if (CRC.Verify28DigestUTF8(stName, crc))
|
|
{
|
|
return stName;
|
|
}
|
|
string texelName = $"{property}_TexelSize";
|
|
if (CRC.Verify28DigestUTF8(texelName, crc))
|
|
{
|
|
return texelName;
|
|
}
|
|
}
|
|
foreach (var property in material.m_SavedProperties.m_Floats.Keys)
|
|
{
|
|
if (CRC.Verify28DigestUTF8(property, crc))
|
|
{
|
|
return property;
|
|
}
|
|
}
|
|
foreach (var property in material.m_SavedProperties.m_Colors.Keys)
|
|
{
|
|
if (CRC.Verify28DigestUTF8(property, crc))
|
|
{
|
|
return property;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
} |