PO/Library/PackageCache/com.unity.2d.animation@5.0.7/Editor/SkinningModule/SkinningCache/SpriteCacheExtensions.cs

326 lines
10 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityEditor.U2D.Animation
{
internal static class SpriteCacheExtensions
{
public static MeshCache GetMesh(this SpriteCache sprite)
{
if (sprite != null)
return sprite.skinningCache.GetMesh(sprite);
return null;
}
public static MeshPreviewCache GetMeshPreview(this SpriteCache sprite)
{
if (sprite != null)
return sprite.skinningCache.GetMeshPreview(sprite);
return null;
}
public static SkeletonCache GetSkeleton(this SpriteCache sprite)
{
if (sprite != null)
return sprite.skinningCache.GetSkeleton(sprite);
return null;
}
public static CharacterPartCache GetCharacterPart(this SpriteCache sprite)
{
if (sprite != null)
return sprite.skinningCache.GetCharacterPart(sprite);
return null;
}
public static bool IsVisible(this SpriteCache sprite)
{
var isVisible = true;
var characterPart = sprite.GetCharacterPart();
if (sprite.skinningCache.mode == SkinningMode.Character && characterPart != null)
isVisible = characterPart.isVisible;
return isVisible;
}
public static Matrix4x4 GetLocalToWorldMatrixFromMode(this SpriteCache sprite)
{
var skinningCache = sprite.skinningCache;
if (skinningCache.mode == SkinningMode.SpriteSheet)
return sprite.localToWorldMatrix;
var characterPart = sprite.GetCharacterPart();
Debug.Assert(characterPart != null);
return characterPart.localToWorldMatrix;
}
public static BoneCache[] GetBonesFromMode(this SpriteCache sprite)
{
var skinningCache = sprite.skinningCache;
if (skinningCache.mode == SkinningMode.SpriteSheet)
return sprite.GetSkeleton().bones;
var characterPart = sprite.GetCharacterPart();
Debug.Assert(characterPart != null);
return characterPart.bones;
}
public static void UpdateMesh(this SpriteCache sprite, BoneCache[] bones)
{
var mesh = sprite.GetMesh();
var previewMesh = sprite.GetMeshPreview();
Debug.Assert(mesh != null);
Debug.Assert(previewMesh != null);
mesh.bones = bones;
previewMesh.SetWeightsDirty();
}
public static void SmoothFill(this SpriteCache sprite)
{
var mesh = sprite.GetMesh();
if (mesh == null)
return;
var controller = new SpriteMeshDataController();
controller.spriteMeshData = mesh;
controller.SmoothFill();
}
public static void RestoreBindPose(this SpriteCache sprite)
{
var skinningCache = sprite.skinningCache;
var skeleton = sprite.GetSkeleton();
Debug.Assert(skeleton != null);
skeleton.RestoreDefaultPose();
skinningCache.events.skeletonPreviewPoseChanged.Invoke(skeleton);
}
public static bool AssociateAllBones(this SpriteCache sprite)
{
var skinningCache = sprite.skinningCache;
if (skinningCache.mode == SkinningMode.SpriteSheet)
return false;
var character = skinningCache.character;
Debug.Assert(character != null);
Debug.Assert(character.skeleton != null);
var characterPart = sprite.GetCharacterPart();
Debug.Assert(characterPart != null);
var bones = character.skeleton.bones.Where(x => x.isVisible).ToArray();
characterPart.bones = bones;
characterPart.sprite.UpdateMesh(bones);
return true;
}
public static bool AssociatePossibleBones(this SpriteCache sprite)
{
var skinningCache = sprite.skinningCache;
if (skinningCache.mode == SkinningMode.SpriteSheet)
return false;
var character = skinningCache.character;
Debug.Assert(character != null);
Debug.Assert(character.skeleton != null);
var characterPart = sprite.GetCharacterPart();
Debug.Assert(characterPart != null);
var bones = character.skeleton.bones.Where(x => x.isVisible).ToArray();
var possibleBones = new List<BoneCache>();
// check if any of the bones overlapped
BoneCache shortestBoneDistance = null;
var minDistances = float.MaxValue;
var characterSpriteRect = new Rect(characterPart.position.x , characterPart.position.y, characterPart.sprite.textureRect.width, characterPart.sprite.textureRect.height);
foreach (var bone in bones)
{
var startPoint = bone.position;
var endPoint = bone.endPosition;
if (IntersectsSegment(characterSpriteRect, startPoint, endPoint))
possibleBones.Add(bone);
if (possibleBones.Count == 0)
{
// compare bone start end with rect's 4 line
// compare rect point with bone line
var points = new Vector2[] { startPoint, endPoint };
var rectLinePoints = new []
{
new Vector2Int(0, 1),
new Vector2Int(0, 2),
new Vector2Int(1, 3),
new Vector2Int(2, 3),
};
var rectPoints = new []
{
new Vector2(characterSpriteRect.xMin, characterSpriteRect.yMin),
new Vector2(characterSpriteRect.xMin, characterSpriteRect.yMax),
new Vector2(characterSpriteRect.xMax, characterSpriteRect.yMin),
new Vector2(characterSpriteRect.xMax, characterSpriteRect.yMax)
};
foreach (var point in points)
{
foreach (var rectLine in rectLinePoints)
{
var distance = PointToLineSegmentDistance(point, rectPoints[rectLine.x], rectPoints[rectLine.y]);
if (distance < minDistances)
{
minDistances = distance;
shortestBoneDistance = bone;
}
}
}
foreach (var rectPoint in rectPoints)
{
var distance = PointToLineSegmentDistance(rectPoint, startPoint, endPoint);
if (distance < minDistances)
{
minDistances = distance;
shortestBoneDistance = bone;
}
}
}
}
// if none overlapped, we use the bone that is closest to us
if (possibleBones.Count == 0 && shortestBoneDistance != null)
{
possibleBones.Add(shortestBoneDistance);
}
characterPart.bones = possibleBones.ToArray();
characterPart.sprite.UpdateMesh(possibleBones.ToArray());
return true;
}
static float PointToLineSegmentDistance(Vector2 p, Vector2 a, Vector2 b)
{
Vector2 n = b - a;
Vector2 pa = a - p;
float c = Vector2.Dot(n, pa);
// Closest point is a
if (c > 0.0f)
return Vector2.Dot(pa, pa);
Vector2 bp = p - b;
// Closest point is b
if (Vector2.Dot(n, bp) > 0.0f)
return Vector2.Dot(bp, bp);
// Closest point is between a and b
Vector2 e = pa - n * (c / Vector2.Dot(n, n));
return Vector2.Dot( e, e );
}
static bool IntersectsSegment(Rect rect, Vector2 p1, Vector2 p2)
{
float minX = Mathf.Min(p1.x, p2.x);
float maxX = Mathf.Max(p1.x, p2.x);
if (maxX > rect.xMax)
{
maxX = rect.xMax;
}
if (minX < rect.xMin)
{
minX = rect.xMin;
}
if (minX > maxX)
{
return false;
}
float minY = Mathf.Min(p1.y, p2.y);
float maxY = Mathf.Max(p1.y, p2.y);
float dx = p2.x - p1.x;
if (Mathf.Abs(dx) > float.Epsilon)
{
float a = (p2.y - p1.y) / dx;
float b = p1.y - a * p1.x;
minY = a * minX + b;
maxY = a * maxX + b;
}
if (minY > maxY)
{
float tmp = maxY;
maxY = minY;
minY = tmp;
}
if (maxY > rect.yMax)
{
maxY = rect.yMax;
}
if (minY < rect.yMin)
{
minY = rect.yMin;
}
if (minY > maxY)
{
return false;
}
return true;
}
public static void DeassociateUnusedBones(this SpriteCache sprite)
{
var skinningCache = sprite.skinningCache;
Debug.Assert(skinningCache.mode == SkinningMode.Character);
var characterPart = sprite.GetCharacterPart();
Debug.Assert(characterPart != null);
characterPart.DeassociateUnusedBones();
}
public static void DeassociateAllBones(this SpriteCache sprite)
{
var skinningCache = sprite.skinningCache;
if (skinningCache.mode == SkinningMode.SpriteSheet)
return;
var part = sprite.GetCharacterPart();
if (part.bones.Length == 0)
return;
Debug.Assert(part.sprite != null);
part.bones = new BoneCache[0];
part.sprite.UpdateMesh(part.bones);
skinningCache.events.characterPartChanged.Invoke(part);
}
}
}