842 lines
32 KiB
C#
842 lines
32 KiB
C#
#pragma warning disable 0168 // variable declared but not used.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.Scripting;
|
|
using UnityEngine.U2D.Common;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Profiling;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Scripting.APIUpdating;
|
|
|
|
namespace UnityEngine.U2D.Animation
|
|
{
|
|
/// <summary>
|
|
/// Represents vertex position.
|
|
/// </summary>
|
|
internal struct PositionVertex
|
|
{
|
|
/// <summary>
|
|
/// Vertex position.
|
|
/// </summary>
|
|
public Vector3 position;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents vertex position and tangent.
|
|
/// </summary>
|
|
internal struct PositionTangentVertex
|
|
{
|
|
/// <summary>
|
|
/// Vertex position.
|
|
/// </summary>
|
|
public Vector3 position;
|
|
|
|
/// <summary>
|
|
/// Vertex tangent.
|
|
/// </summary>
|
|
public Vector4 tangent;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deforms the Sprite that is currently assigned to the SpriteRenderer in the same GameObject.
|
|
/// </summary>
|
|
[Preserve]
|
|
[ExecuteInEditMode]
|
|
[DefaultExecutionOrder(-1)]
|
|
[DisallowMultipleComponent]
|
|
[RequireComponent(typeof(SpriteRenderer))]
|
|
[AddComponentMenu("2D Animation/Sprite Skin")]
|
|
[IconAttribute(IconUtility.IconPath + "Animation.SpriteSkin.png")]
|
|
[MovedFrom("UnityEngine.U2D.Experimental.Animation")]
|
|
[HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.animation@9.0/manual/SpriteSkin.html")]
|
|
public sealed class SpriteSkin : MonoBehaviour, IPreviewable, ISerializationCallbackReceiver
|
|
{
|
|
static class Profiling
|
|
{
|
|
public static readonly ProfilerMarker cacheCurrentSprite = new ProfilerMarker("SpriteSkin.CacheCurrentSprite");
|
|
public static readonly ProfilerMarker cacheHierarchy = new ProfilerMarker("SpriteSkin.CacheHierarchy");
|
|
public static readonly ProfilerMarker getSpriteBonesTransformFromGuid = new ProfilerMarker("SpriteSkin.GetSpriteBoneTransformsFromGuid");
|
|
public static readonly ProfilerMarker getSpriteBonesTransformFromPath = new ProfilerMarker("SpriteSkin.GetSpriteBoneTransformsFromPath");
|
|
}
|
|
|
|
struct TransformData
|
|
{
|
|
public string fullName;
|
|
public Transform transform;
|
|
}
|
|
|
|
[SerializeField]
|
|
Transform m_RootBone;
|
|
[SerializeField]
|
|
Transform[] m_BoneTransforms = new Transform[0];
|
|
[SerializeField]
|
|
Bounds m_Bounds;
|
|
[SerializeField]
|
|
bool m_AlwaysUpdate = true;
|
|
[SerializeField]
|
|
bool m_AutoRebind = false;
|
|
|
|
// The deformed m_SpriteVertices stores all 'HOT' channels only in single-stream and essentially depends on Sprite Asset data.
|
|
// The order of storage if present is POSITION, NORMALS, TANGENTS.
|
|
NativeByteArray m_DeformedVertices;
|
|
int m_CurrentDeformVerticesLength = 0;
|
|
SpriteRenderer m_SpriteRenderer;
|
|
int m_CurrentDeformSprite = 0;
|
|
bool m_ForceSkinning;
|
|
bool m_IsValid = false;
|
|
int m_TransformsHash = 0;
|
|
|
|
int m_TransformId;
|
|
NativeArray<int> m_BoneTransformId;
|
|
int m_RootBoneTransformId;
|
|
NativeCustomSlice<Vector2> m_SpriteUVs;
|
|
NativeCustomSlice<Vector3> m_SpriteVertices;
|
|
NativeCustomSlice<Vector4> m_SpriteTangents;
|
|
NativeCustomSlice<BoneWeight> m_SpriteBoneWeights;
|
|
NativeCustomSlice<Matrix4x4> m_SpriteBindPoses;
|
|
NativeCustomSlice<int> m_BoneTransformIdNativeSlice;
|
|
bool m_SpriteHasTangents;
|
|
int m_SpriteVertexStreamSize;
|
|
int m_SpriteVertexCount;
|
|
int m_SpriteTangentVertexOffset;
|
|
int m_DataIndex = -1;
|
|
bool m_BoneCacheUpdateToDate = false;
|
|
|
|
Dictionary<int, List<TransformData>> m_HierarchyCache = new Dictionary<int, List<TransformData>>();
|
|
|
|
internal Sprite sprite => m_SpriteRenderer != null ? m_SpriteRenderer.sprite : null;
|
|
internal SpriteRenderer spriteRenderer => m_SpriteRenderer;
|
|
internal NativeCustomSlice<BoneWeight> spriteBoneWeights => m_SpriteBoneWeights;
|
|
|
|
/// <summary>
|
|
/// Gets the index of the SpriteSkin in the SpriteSkinComposite.
|
|
/// </summary>
|
|
internal int dataIndex => m_DataIndex;
|
|
|
|
/// <summary>
|
|
/// Get and set the Auto Rebind property.
|
|
/// When enabled, Sprite Skin attempts to automatically locate the Transform that is needed for the current Sprite assigned to the Sprite Renderer.
|
|
/// </summary>
|
|
public bool autoRebind
|
|
{
|
|
get => m_AutoRebind;
|
|
set
|
|
{
|
|
m_AutoRebind = value;
|
|
if (isActiveAndEnabled)
|
|
{
|
|
CacheHierarchy();
|
|
CacheCurrentSprite(m_AutoRebind);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the Transform Components that is used for deformation.
|
|
/// Do not modify elements of the returned array.
|
|
/// </summary>
|
|
/// <returns>An array of Transform Components.</returns>
|
|
public Transform[] boneTransforms
|
|
{
|
|
get => m_BoneTransforms;
|
|
internal set
|
|
{
|
|
m_BoneTransforms = value;
|
|
CacheValidFlag();
|
|
if(isActiveAndEnabled)
|
|
OnBoneTransformChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the Transform Component that represents the root bone for deformation.
|
|
/// </summary>
|
|
/// <returns>A Transform Component.</returns>
|
|
public Transform rootBone
|
|
{
|
|
get => m_RootBone;
|
|
internal set
|
|
{
|
|
m_RootBone = value;
|
|
CacheValidFlag();
|
|
if (isActiveAndEnabled)
|
|
{
|
|
CacheHierarchy();
|
|
OnRootBoneTransformChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal Bounds bounds
|
|
{
|
|
get => m_Bounds;
|
|
set => m_Bounds = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the SpriteSkin executes even if the associated
|
|
/// SpriteRenderer has been culled from view.
|
|
/// </summary>
|
|
public bool alwaysUpdate
|
|
{
|
|
get => m_AlwaysUpdate;
|
|
set => m_AlwaysUpdate = value;
|
|
}
|
|
|
|
internal bool isValid => this.Validate() == SpriteSkinValidationResult.Ready;
|
|
|
|
#if UNITY_EDITOR
|
|
internal static Events.UnityEvent onDrawGizmos = new Events.UnityEvent();
|
|
void OnDrawGizmos()
|
|
{ onDrawGizmos.Invoke(); }
|
|
|
|
internal bool ignoreNextSpriteChange
|
|
{
|
|
get;
|
|
set;
|
|
} = true;
|
|
#endif
|
|
|
|
int GetSpriteInstanceID()
|
|
{
|
|
return sprite != null ? sprite.GetInstanceID() : 0;
|
|
}
|
|
|
|
internal void Awake()
|
|
{
|
|
m_SpriteRenderer = GetComponent<SpriteRenderer>();
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
Awake();
|
|
m_TransformsHash = 0;
|
|
CacheCurrentSprite(false);
|
|
|
|
if (m_HierarchyCache.Count == 0)
|
|
CacheHierarchy();
|
|
|
|
OnEnableBatch();
|
|
}
|
|
|
|
void OnEnableBatch()
|
|
{
|
|
m_TransformId = gameObject.transform.GetInstanceID();
|
|
UpdateSpriteDeform();
|
|
|
|
CacheBoneTransformIds(true);
|
|
SpriteSkinComposite.instance.AddSpriteSkin(this);
|
|
}
|
|
|
|
void OnResetBatch()
|
|
{
|
|
CacheBoneTransformIds(true);
|
|
SpriteSkinComposite.instance.CopyToSpriteSkinData(this);
|
|
}
|
|
|
|
void OnDisableBatch()
|
|
{
|
|
RemoveTransformFromSpriteSkinComposite();
|
|
SpriteSkinComposite.instance.RemoveSpriteSkin(this);
|
|
}
|
|
|
|
void OnBoneTransformChanged()
|
|
{
|
|
CacheBoneTransformIds(true);
|
|
}
|
|
|
|
void OnRootBoneTransformChanged()
|
|
{
|
|
CacheBoneTransformIds(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called before object is serialized.
|
|
/// </summary>
|
|
public void OnBeforeSerialize()
|
|
{
|
|
OnBeforeSerializeBatch();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called after object is deserialized.
|
|
/// </summary>
|
|
public void OnAfterDeserialize()
|
|
{
|
|
OnAfterSerializeBatch();
|
|
}
|
|
|
|
void OnBeforeSerializeBatch() {}
|
|
|
|
void OnAfterSerializeBatch()
|
|
{
|
|
#if UNITY_EDITOR
|
|
m_BoneCacheUpdateToDate = false;
|
|
#endif
|
|
}
|
|
|
|
internal void OnEditorEnable()
|
|
{
|
|
Awake();
|
|
}
|
|
|
|
void CacheValidFlag()
|
|
{
|
|
m_IsValid = isValid;
|
|
if(!m_IsValid)
|
|
DeactivateSkinning();
|
|
}
|
|
|
|
internal bool BatchValidate()
|
|
{
|
|
CacheBoneTransformIds();
|
|
CacheCurrentSprite(m_AutoRebind);
|
|
var hasSprite = m_CurrentDeformSprite != 0;
|
|
return (m_IsValid && hasSprite && spriteRenderer.enabled && (alwaysUpdate || spriteRenderer.isVisible));
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
Awake();
|
|
if (isActiveAndEnabled)
|
|
{
|
|
CacheValidFlag();
|
|
OnResetBatch();
|
|
}
|
|
}
|
|
|
|
void CacheBoneTransformIds(bool forceUpdate = false)
|
|
{
|
|
if (!m_BoneCacheUpdateToDate || forceUpdate)
|
|
{
|
|
SpriteSkinComposite.instance.RemoveTransformById(m_RootBoneTransformId);
|
|
if (rootBone != null)
|
|
{
|
|
m_RootBoneTransformId = rootBone.GetInstanceID();
|
|
if (enabled)
|
|
SpriteSkinComposite.instance.AddSpriteSkinRootBoneTransform(this);
|
|
}
|
|
else
|
|
m_RootBoneTransformId = 0;
|
|
|
|
if (boneTransforms != null)
|
|
{
|
|
var boneCount = 0;
|
|
for (var i = 0; i < boneTransforms.Length; ++i)
|
|
{
|
|
if (boneTransforms[i] != null)
|
|
++boneCount;
|
|
}
|
|
|
|
if (m_BoneTransformId.IsCreated)
|
|
{
|
|
for (var i = 0; i < m_BoneTransformId.Length; ++i)
|
|
SpriteSkinComposite.instance.RemoveTransformById(m_BoneTransformId[i]);
|
|
NativeArrayHelpers.ResizeIfNeeded(ref m_BoneTransformId, boneCount);
|
|
}
|
|
else
|
|
{
|
|
m_BoneTransformId = new NativeArray<int>(boneCount, Allocator.Persistent);
|
|
}
|
|
|
|
m_BoneTransformIdNativeSlice = new NativeCustomSlice<int>(m_BoneTransformId);
|
|
for (int i = 0, j = 0; i < boneTransforms.Length; ++i)
|
|
{
|
|
if (boneTransforms[i] != null)
|
|
{
|
|
m_BoneTransformId[j] = boneTransforms[i].GetInstanceID();
|
|
++j;
|
|
}
|
|
}
|
|
if (enabled)
|
|
{
|
|
SpriteSkinComposite.instance.AddSpriteSkinBoneTransform(this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_BoneTransformId.IsCreated)
|
|
NativeArrayHelpers.ResizeIfNeeded(ref m_BoneTransformId, 0);
|
|
else
|
|
m_BoneTransformId = new NativeArray<int>(0, Allocator.Persistent);
|
|
}
|
|
CacheValidFlag();
|
|
m_BoneCacheUpdateToDate = true;
|
|
SpriteSkinComposite.instance.CopyToSpriteSkinData(this);
|
|
}
|
|
}
|
|
|
|
void RemoveTransformFromSpriteSkinComposite()
|
|
{
|
|
if (m_BoneTransformId.IsCreated)
|
|
{
|
|
for (var i = 0; i < m_BoneTransformId.Length; ++i)
|
|
SpriteSkinComposite.instance.RemoveTransformById(m_BoneTransformId[i]);
|
|
m_BoneTransformId.Dispose();
|
|
}
|
|
SpriteSkinComposite.instance.RemoveTransformById(m_RootBoneTransformId);
|
|
m_RootBoneTransformId = -1;
|
|
m_BoneCacheUpdateToDate = false;
|
|
}
|
|
|
|
internal NativeByteArray GetDeformedVertices(int spriteVertexCount)
|
|
{
|
|
if (sprite != null)
|
|
{
|
|
if (m_CurrentDeformVerticesLength != spriteVertexCount)
|
|
{
|
|
m_TransformsHash = 0;
|
|
m_CurrentDeformVerticesLength = spriteVertexCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_CurrentDeformVerticesLength = 0;
|
|
}
|
|
|
|
m_DeformedVertices = BufferManager.instance.GetBuffer(GetInstanceID(), m_CurrentDeformVerticesLength);
|
|
return m_DeformedVertices;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether this SpriteSkin has currently deformed vertices.
|
|
/// </summary>
|
|
/// <returns>Returns true if this SpriteSkin has currently deformed vertices. Returns false otherwise.</returns>
|
|
public bool HasCurrentDeformedVertices()
|
|
{
|
|
if (!m_IsValid)
|
|
return false;
|
|
|
|
return m_DataIndex >= 0 && SpriteSkinComposite.instance.HasDeformableBufferForSprite(m_DataIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a byte array to the currently deformed vertices for this SpriteSkin.
|
|
/// </summary>
|
|
/// <returns>Returns a reference to the currently deformed vertices. This is valid only for this calling frame.</returns>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// Thrown when there are no currently deformed vertices.
|
|
/// HasCurrentDeformedVertices can be used to verify if there are any deformed vertices available.
|
|
/// </exception>
|
|
internal NativeArray<byte> GetCurrentDeformedVertices()
|
|
{
|
|
if (!m_IsValid)
|
|
throw new InvalidOperationException("The SpriteSkin deformation is not valid.");
|
|
|
|
if (m_DataIndex < 0)
|
|
{
|
|
throw new InvalidOperationException("There are no currently deformed vertices.");
|
|
}
|
|
return SpriteSkinComposite.instance.GetDeformableBufferForSprite(m_DataIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an array of currently deformed position vertices for this SpriteSkin.
|
|
/// </summary>
|
|
/// <returns>Returns a reference to the currently deformed vertices. This is valid only for this calling frame.</returns>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// Thrown when there are no currently deformed vertices or if the deformed vertices does not contain only
|
|
/// position data. HasCurrentDeformedVertices can be used to verify if there are any deformed vertices available.
|
|
/// </exception>
|
|
internal NativeSlice<PositionVertex> GetCurrentDeformedVertexPositions()
|
|
{
|
|
if (!m_IsValid)
|
|
throw new InvalidOperationException("The SpriteSkin deformation is not valid.");
|
|
|
|
if (sprite.HasVertexAttribute(VertexAttribute.Tangent))
|
|
throw new InvalidOperationException("This SpriteSkin has deformed tangents");
|
|
if (!sprite.HasVertexAttribute(VertexAttribute.Position))
|
|
throw new InvalidOperationException("This SpriteSkin does not have deformed positions.");
|
|
|
|
var deformedBuffer = GetCurrentDeformedVertices();
|
|
return deformedBuffer.Slice().SliceConvert<PositionVertex>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an array of currently deformed position and tangent vertices for this SpriteSkin.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Returns a reference to the currently deformed position and tangent vertices. This is valid only for this calling frame.
|
|
/// </returns>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// Thrown when there are no currently deformed vertices or if the deformed vertices does not contain only
|
|
/// position and tangent data. HasCurrentDeformedVertices can be used to verify if there are any deformed vertices available.
|
|
/// </exception>
|
|
internal NativeSlice<PositionTangentVertex> GetCurrentDeformedVertexPositionsAndTangents()
|
|
{
|
|
if (!m_IsValid)
|
|
throw new InvalidOperationException("The SpriteSkin deformation is not valid.");
|
|
|
|
if (!sprite.HasVertexAttribute(VertexAttribute.Tangent))
|
|
throw new InvalidOperationException("This SpriteSkin does not have deformed tangents");
|
|
if (!sprite.HasVertexAttribute(VertexAttribute.Position))
|
|
throw new InvalidOperationException("This SpriteSkin does not have deformed positions.");
|
|
|
|
var deformedBuffer = GetCurrentDeformedVertices();
|
|
return deformedBuffer.Slice().SliceConvert<PositionTangentVertex>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an enumerable to iterate through all deformed vertex positions of this SpriteSkin.
|
|
/// </summary>
|
|
/// <returns>Returns an IEnumerable to deformed vertex positions.</returns>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// Thrown when there is no vertex positions or deformed vertices.
|
|
/// HasCurrentDeformedVertices can be used to verify if there are any deformed vertices available.
|
|
/// </exception>
|
|
public IEnumerable<Vector3> GetDeformedVertexPositionData()
|
|
{
|
|
if (!m_IsValid)
|
|
throw new InvalidOperationException("The SpriteSkin deformation is not valid.");
|
|
|
|
var hasPosition = sprite.HasVertexAttribute(VertexAttribute.Position);
|
|
if (!hasPosition)
|
|
throw new InvalidOperationException("Sprite does not have vertex position data.");
|
|
|
|
var rawBuffer = GetCurrentDeformedVertices();
|
|
var rawSlice = rawBuffer.Slice(sprite.GetVertexStreamOffset(VertexAttribute.Position));
|
|
return new NativeCustomSliceEnumerator<Vector3>(rawSlice, sprite.GetVertexCount(), sprite.GetVertexStreamSize());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an enumerable to iterate through all deformed vertex tangents of this SpriteSkin.
|
|
/// </summary>
|
|
/// <returns>Returns an IEnumerable to deformed vertex tangents.</returns>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// Thrown when there is no vertex tangents or deformed vertices.
|
|
/// HasCurrentDeformedVertices can be used to verify if there are any deformed vertices available.
|
|
/// </exception>
|
|
public IEnumerable<Vector4> GetDeformedVertexTangentData()
|
|
{
|
|
if (!m_IsValid)
|
|
throw new InvalidOperationException("The SpriteSkin deformation is not valid.");
|
|
|
|
var hasTangent = sprite.HasVertexAttribute(VertexAttribute.Tangent);
|
|
if (!hasTangent)
|
|
throw new InvalidOperationException("Sprite does not have vertex tangent data.");
|
|
|
|
var rawBuffer = GetCurrentDeformedVertices();
|
|
var rawSlice = rawBuffer.Slice(sprite.GetVertexStreamOffset(VertexAttribute.Tangent));
|
|
return new NativeCustomSliceEnumerator<Vector4>(rawSlice, sprite.GetVertexCount(), sprite.GetVertexStreamSize());
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
DeactivateSkinning();
|
|
BufferManager.instance.ReturnBuffer(GetInstanceID());
|
|
OnDisableBatch();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used by the animation clip preview window.
|
|
/// Recommended to not use outside of this purpose.
|
|
/// </summary>
|
|
public void OnPreviewUpdate()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if(IsInGUIUpdateLoop())
|
|
Deform();
|
|
#endif
|
|
}
|
|
|
|
static bool IsInGUIUpdateLoop() => Event.current != null;
|
|
|
|
void Deform()
|
|
{
|
|
CacheCurrentSprite(m_AutoRebind);
|
|
if (isValid && isActiveAndEnabled && (this.alwaysUpdate || this.spriteRenderer.isVisible))
|
|
{
|
|
var transformHash = SpriteSkinUtility.CalculateTransformHash(this);
|
|
var spriteVertexCount = sprite.GetVertexStreamSize() * sprite.GetVertexCount();
|
|
if (spriteVertexCount > 0 && m_TransformsHash != transformHash)
|
|
{
|
|
var inputVertices = GetDeformedVertices(spriteVertexCount);
|
|
SpriteSkinUtility.Deform(sprite, gameObject.transform.worldToLocalMatrix, boneTransforms, inputVertices.array);
|
|
SpriteSkinUtility.UpdateBounds(this, inputVertices.array);
|
|
InternalEngineBridge.SetDeformableBuffer(spriteRenderer, inputVertices.array);
|
|
m_TransformsHash = transformHash;
|
|
m_CurrentDeformSprite = GetSpriteInstanceID();
|
|
}
|
|
}
|
|
else if(!InternalEngineBridge.IsUsingDeformableBuffer(spriteRenderer, IntPtr.Zero))
|
|
{
|
|
DeactivateSkinning();
|
|
}
|
|
}
|
|
|
|
void CacheCurrentSprite(bool rebind)
|
|
{
|
|
if (m_CurrentDeformSprite != GetSpriteInstanceID())
|
|
{
|
|
using (Profiling.cacheCurrentSprite.Auto())
|
|
{
|
|
DeactivateSkinning();
|
|
m_CurrentDeformSprite = GetSpriteInstanceID();
|
|
if (rebind && m_CurrentDeformSprite > 0 && rootBone != null)
|
|
{
|
|
if (!GetSpriteBonesTransforms(this, out var transforms))
|
|
Debug.LogWarning($"Rebind failed for {name}. Could not find all bones required by the Sprite: {sprite.name}.");
|
|
boneTransforms = transforms;
|
|
}
|
|
UpdateSpriteDeform();
|
|
CacheValidFlag();
|
|
m_TransformsHash = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateSpriteDeform()
|
|
{
|
|
if (sprite == null)
|
|
{
|
|
m_SpriteUVs = NativeCustomSlice<Vector2>.Default();
|
|
m_SpriteVertices = NativeCustomSlice<Vector3>.Default();
|
|
m_SpriteTangents = NativeCustomSlice<Vector4>.Default();
|
|
m_SpriteBoneWeights = NativeCustomSlice<BoneWeight>.Default();
|
|
m_SpriteBindPoses = NativeCustomSlice<Matrix4x4>.Default();
|
|
m_SpriteHasTangents = false;
|
|
m_SpriteVertexStreamSize = 0;
|
|
m_SpriteVertexCount = 0;
|
|
m_SpriteTangentVertexOffset = 0;
|
|
}
|
|
else
|
|
{
|
|
m_SpriteUVs = new NativeCustomSlice<Vector2>(sprite.GetVertexAttribute<Vector2>(VertexAttribute.TexCoord0));
|
|
m_SpriteVertices = new NativeCustomSlice<Vector3>(sprite.GetVertexAttribute<Vector3>(VertexAttribute.Position));
|
|
m_SpriteTangents = new NativeCustomSlice<Vector4>(sprite.GetVertexAttribute<Vector4>(VertexAttribute.Tangent));
|
|
m_SpriteBoneWeights = new NativeCustomSlice<BoneWeight>(sprite.GetVertexAttribute<BoneWeight>(VertexAttribute.BlendWeight));
|
|
m_SpriteBindPoses = new NativeCustomSlice<Matrix4x4>(sprite.GetBindPoses());
|
|
m_SpriteHasTangents = sprite.HasVertexAttribute(VertexAttribute.Tangent);
|
|
m_SpriteVertexStreamSize = sprite.GetVertexStreamSize();
|
|
m_SpriteVertexCount = sprite.GetVertexCount();
|
|
m_SpriteTangentVertexOffset = sprite.GetVertexStreamOffset(VertexAttribute.Tangent);
|
|
}
|
|
SpriteSkinComposite.instance.CopyToSpriteSkinData(this);
|
|
}
|
|
|
|
internal void CopyToSpriteSkinData(ref SpriteSkinData data, int spriteSkinIndex)
|
|
{
|
|
CacheBoneTransformIds();
|
|
CacheCurrentSprite(m_AutoRebind);
|
|
|
|
data.vertices = m_SpriteVertices;
|
|
data.boneWeights = m_SpriteBoneWeights;
|
|
data.bindPoses = m_SpriteBindPoses;
|
|
data.tangents = m_SpriteTangents;
|
|
data.hasTangents = m_SpriteHasTangents;
|
|
data.spriteVertexStreamSize = m_SpriteVertexStreamSize;
|
|
data.spriteVertexCount = m_SpriteVertexCount;
|
|
data.tangentVertexOffset = m_SpriteTangentVertexOffset;
|
|
data.transformId = m_TransformId;
|
|
data.boneTransformId = m_BoneTransformIdNativeSlice;
|
|
m_DataIndex = spriteSkinIndex;
|
|
}
|
|
|
|
internal bool NeedUpdateCompositeCache()
|
|
{
|
|
unsafe
|
|
{
|
|
var iptr = new IntPtr(sprite.GetVertexAttribute<Vector2>(VertexAttribute.TexCoord0).GetUnsafeReadOnlyPtr());
|
|
var rs = m_SpriteUVs.data != iptr;
|
|
if (rs)
|
|
{
|
|
UpdateSpriteDeform();
|
|
}
|
|
return rs;
|
|
}
|
|
}
|
|
|
|
void CacheHierarchy()
|
|
{
|
|
using (Profiling.cacheHierarchy.Auto())
|
|
{
|
|
m_HierarchyCache.Clear();
|
|
if (rootBone == null || !m_AutoRebind)
|
|
return;
|
|
|
|
var boneCount = CountChildren(rootBone);
|
|
m_HierarchyCache.EnsureCapacity(boneCount + 1);
|
|
CacheChildren(rootBone, m_HierarchyCache);
|
|
|
|
foreach (var entry in m_HierarchyCache)
|
|
{
|
|
if (entry.Value.Count == 1)
|
|
continue;
|
|
var count = entry.Value.Count;
|
|
for (var i = 0; i < count; ++i)
|
|
{
|
|
var transformEntry = entry.Value[i];
|
|
transformEntry.fullName = GenerateTransformPath(rootBone, transformEntry.transform);
|
|
entry.Value[i] = transformEntry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CacheChildren(Transform current, Dictionary<int, List<TransformData>> cache)
|
|
{
|
|
var nameHash = current.name.GetHashCode();
|
|
var entry = new TransformData()
|
|
{
|
|
fullName = String.Empty,
|
|
transform = current
|
|
};
|
|
if (cache.ContainsKey(nameHash))
|
|
cache[nameHash].Add(entry);
|
|
else
|
|
cache.Add(nameHash, new List<TransformData>(1) { entry });
|
|
|
|
for (var i = 0; i < current.childCount; ++i)
|
|
CacheChildren(current.GetChild(i), cache);
|
|
}
|
|
|
|
static string GenerateTransformPath(Transform rootBone, Transform child)
|
|
{
|
|
var path = child.name;
|
|
if (child == rootBone)
|
|
return path;
|
|
var parent = child.parent;
|
|
do
|
|
{
|
|
path = parent.name + "/" + path;
|
|
parent = parent.parent;
|
|
}
|
|
while (parent != rootBone && parent != null);
|
|
return path;
|
|
}
|
|
|
|
internal static bool GetSpriteBonesTransforms(SpriteSkin spriteSkin, out Transform[] outTransform)
|
|
{
|
|
var rootBone = spriteSkin.rootBone;
|
|
var spriteBones = spriteSkin.sprite.GetBones();
|
|
|
|
if(rootBone == null)
|
|
throw new ArgumentException("rootBone parameter cannot be null");
|
|
if(spriteBones == null)
|
|
throw new ArgumentException("spriteBones parameter cannot be null");
|
|
|
|
outTransform = new Transform[spriteBones.Length];
|
|
|
|
var boneObjects = rootBone.GetComponentsInChildren<Bone>();
|
|
if (boneObjects != null && boneObjects.Length >= spriteBones.Length)
|
|
{
|
|
using (Profiling.getSpriteBonesTransformFromGuid.Auto())
|
|
{
|
|
var i = 0;
|
|
for (; i < spriteBones.Length; ++i)
|
|
{
|
|
var boneHash = spriteBones[i].guid;
|
|
var boneTransform = Array.Find(boneObjects, x => (x.guid == boneHash));
|
|
if (boneTransform == null)
|
|
break;
|
|
|
|
outTransform[i] = boneTransform.transform;
|
|
}
|
|
if (i >= spriteBones.Length)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
var hierarchyCache = spriteSkin.m_HierarchyCache;
|
|
if (hierarchyCache.Count == 0)
|
|
spriteSkin.CacheHierarchy();
|
|
|
|
// If unable to successfully map via guid, fall back to path
|
|
return GetSpriteBonesTransformFromPath(spriteBones, hierarchyCache, outTransform);
|
|
}
|
|
|
|
static bool GetSpriteBonesTransformFromPath(SpriteBone[] spriteBones, Dictionary<int, List<TransformData>> hierarchyCache, Transform[] outNewBoneTransform)
|
|
{
|
|
using (Profiling.getSpriteBonesTransformFromPath.Auto())
|
|
{
|
|
string[] bonePath = null;
|
|
var foundBones = true;
|
|
for (var i = 0; i < spriteBones.Length; ++i)
|
|
{
|
|
var nameHash = spriteBones[i].name.GetHashCode();
|
|
if (!hierarchyCache.TryGetValue(nameHash, out var children))
|
|
{
|
|
outNewBoneTransform[i] = null;
|
|
foundBones = false;
|
|
continue;
|
|
}
|
|
|
|
if (children.Count == 1)
|
|
outNewBoneTransform[i] = children[0].transform;
|
|
else
|
|
{
|
|
if (bonePath == null)
|
|
bonePath = new string[spriteBones.Length];
|
|
if (bonePath[i] == null)
|
|
CalculateBoneTransformsPath(i, spriteBones, bonePath);
|
|
|
|
var m = 0;
|
|
for (; m < children.Count; ++m)
|
|
{
|
|
if (children[m].fullName.Contains(bonePath[i]))
|
|
{
|
|
outNewBoneTransform[i] = children[m].transform;
|
|
break;
|
|
}
|
|
}
|
|
if (m >= children.Count)
|
|
{
|
|
outNewBoneTransform[i] = null;
|
|
foundBones = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return foundBones;
|
|
}
|
|
}
|
|
|
|
static void CalculateBoneTransformsPath(int index, SpriteBone[] spriteBones, string[] paths)
|
|
{
|
|
var spriteBone = spriteBones[index];
|
|
var parentId = spriteBone.parentId;
|
|
var bonePath = spriteBone.name;
|
|
if (parentId != -1)
|
|
{
|
|
if (paths[parentId] == null)
|
|
CalculateBoneTransformsPath(spriteBone.parentId, spriteBones, paths);
|
|
paths[index] = $"{paths[parentId]}/{bonePath}";
|
|
}
|
|
else
|
|
paths[index] = bonePath;
|
|
}
|
|
|
|
internal void DeactivateSkinning()
|
|
{
|
|
if (m_SpriteRenderer != null)
|
|
{
|
|
var currentSprite = m_SpriteRenderer.sprite;
|
|
if (currentSprite != null)
|
|
InternalEngineBridge.SetLocalAABB(m_SpriteRenderer, currentSprite.bounds);
|
|
|
|
m_SpriteRenderer.DeactivateDeformableBuffer();
|
|
}
|
|
|
|
m_TransformsHash = 0;
|
|
}
|
|
|
|
internal void ResetSprite()
|
|
{
|
|
m_CurrentDeformSprite = 0;
|
|
CacheValidFlag();
|
|
}
|
|
|
|
static int CountChildren(Transform transform)
|
|
{
|
|
var childCount = transform.childCount;
|
|
var count = childCount;
|
|
for (var i = 0; i < childCount; ++i)
|
|
count += CountChildren(transform.GetChild(i));
|
|
|
|
return count;
|
|
}
|
|
}
|
|
}
|