A game about forced loneliness, made by TACStudios
at master 325 lines 10 kB view raw
1using System.Collections.Generic; 2using System.Linq; 3using UnityEngine; 4 5namespace UnityEditor.U2D.Animation 6{ 7 internal static class SpriteCacheExtensions 8 { 9 public static MeshCache GetMesh(this SpriteCache sprite) 10 { 11 if (sprite != null) 12 return sprite.skinningCache.GetMesh(sprite); 13 return null; 14 } 15 16 public static MeshPreviewCache GetMeshPreview(this SpriteCache sprite) 17 { 18 if (sprite != null) 19 return sprite.skinningCache.GetMeshPreview(sprite); 20 return null; 21 } 22 23 public static SkeletonCache GetSkeleton(this SpriteCache sprite) 24 { 25 if (sprite != null) 26 return sprite.skinningCache.GetSkeleton(sprite); 27 return null; 28 } 29 30 public static CharacterPartCache GetCharacterPart(this SpriteCache sprite) 31 { 32 if (sprite != null) 33 return sprite.skinningCache.GetCharacterPart(sprite); 34 return null; 35 } 36 37 public static bool IsVisible(this SpriteCache sprite) 38 { 39 var isVisible = true; 40 var characterPart = sprite.GetCharacterPart(); 41 42 if (sprite.skinningCache.mode == SkinningMode.Character && characterPart != null) 43 isVisible = characterPart.isVisible; 44 45 return isVisible; 46 } 47 48 public static Matrix4x4 GetLocalToWorldMatrixFromMode(this SpriteCache sprite) 49 { 50 var skinningCache = sprite.skinningCache; 51 52 if (skinningCache.mode == SkinningMode.SpriteSheet) 53 return sprite.localToWorldMatrix; 54 55 var characterPart = sprite.GetCharacterPart(); 56 57 Debug.Assert(characterPart != null); 58 59 return characterPart.localToWorldMatrix; 60 } 61 62 public static BoneCache[] GetBonesFromMode(this SpriteCache sprite) 63 { 64 var skinningCache = sprite.skinningCache; 65 66 if (skinningCache.mode == SkinningMode.SpriteSheet) 67 return sprite.GetSkeleton().bones; 68 69 var characterPart = sprite.GetCharacterPart(); 70 Debug.Assert(characterPart != null); 71 return characterPart.bones; 72 } 73 74 public static void UpdateMesh(this SpriteCache sprite, BoneCache[] bones) 75 { 76 var mesh = sprite.GetMesh(); 77 var previewMesh = sprite.GetMeshPreview(); 78 79 Debug.Assert(mesh != null); 80 Debug.Assert(previewMesh != null); 81 82 mesh.bones = bones; 83 84 previewMesh.SetWeightsDirty(); 85 } 86 87 public static void SmoothFill(this SpriteCache sprite) 88 { 89 var mesh = sprite.GetMesh(); 90 91 if (mesh == null) 92 return; 93 94 var controller = new SpriteMeshDataController(); 95 controller.spriteMeshData = mesh; 96 controller.SmoothFill(); 97 } 98 99 public static void RestoreBindPose(this SpriteCache sprite) 100 { 101 var skinningCache = sprite.skinningCache; 102 var skeleton = sprite.GetSkeleton(); 103 Debug.Assert(skeleton != null); 104 skeleton.RestoreDefaultPose(); 105 skinningCache.events.skeletonPreviewPoseChanged.Invoke(skeleton); 106 } 107 108 public static bool AssociateAllBones(this SpriteCache sprite) 109 { 110 var skinningCache = sprite.skinningCache; 111 112 if (skinningCache.mode == SkinningMode.SpriteSheet) 113 return false; 114 115 var character = skinningCache.character; 116 Debug.Assert(character != null); 117 Debug.Assert(character.skeleton != null); 118 119 var characterPart = sprite.GetCharacterPart(); 120 121 Debug.Assert(characterPart != null); 122 123 var bones = character.skeleton.bones.Where(x => x.isVisible).ToArray(); 124 characterPart.bones = bones; 125 126 characterPart.sprite.UpdateMesh(bones); 127 128 return true; 129 } 130 131 public static bool AssociatePossibleBones(this SpriteCache sprite) 132 { 133 var skinningCache = sprite.skinningCache; 134 135 if (skinningCache.mode == SkinningMode.SpriteSheet) 136 return false; 137 138 var character = skinningCache.character; 139 Debug.Assert(character != null); 140 Debug.Assert(character.skeleton != null); 141 142 var characterPart = sprite.GetCharacterPart(); 143 144 Debug.Assert(characterPart != null); 145 146 var bones = character.skeleton.bones.Where(x => x.isVisible).ToArray(); 147 var possibleBones = new List<BoneCache>(); 148 // check if any of the bones overlapped 149 BoneCache shortestBoneDistance = null; 150 var minDistances = float.MaxValue; 151 var characterSpriteRect = new Rect(characterPart.position.x , characterPart.position.y, characterPart.sprite.textureRect.width, characterPart.sprite.textureRect.height); 152 foreach (var bone in bones) 153 { 154 var startPoint = bone.position; 155 var endPoint = bone.endPosition; 156 if (IntersectsSegment(characterSpriteRect, startPoint, endPoint)) 157 possibleBones.Add(bone); 158 if (possibleBones.Count == 0) 159 { 160 // compare bone start end with rect's 4 line 161 // compare rect point with bone line 162 var points = new Vector2[] { startPoint, endPoint }; 163 var rectLinePoints = new [] 164 { 165 new Vector2Int(0, 1), 166 new Vector2Int(0, 2), 167 new Vector2Int(1, 3), 168 new Vector2Int(2, 3), 169 }; 170 var rectPoints = new [] 171 { 172 new Vector2(characterSpriteRect.xMin, characterSpriteRect.yMin), 173 new Vector2(characterSpriteRect.xMin, characterSpriteRect.yMax), 174 new Vector2(characterSpriteRect.xMax, characterSpriteRect.yMin), 175 new Vector2(characterSpriteRect.xMax, characterSpriteRect.yMax) 176 }; 177 foreach (var point in points) 178 { 179 foreach (var rectLine in rectLinePoints) 180 { 181 var distance = PointToLineSegmentDistance(point, rectPoints[rectLine.x], rectPoints[rectLine.y]); 182 if (distance < minDistances) 183 { 184 minDistances = distance; 185 shortestBoneDistance = bone; 186 } 187 } 188 } 189 190 foreach (var rectPoint in rectPoints) 191 { 192 var distance = PointToLineSegmentDistance(rectPoint, startPoint, endPoint); 193 if (distance < minDistances) 194 { 195 minDistances = distance; 196 shortestBoneDistance = bone; 197 } 198 } 199 } 200 } 201 // if none overlapped, we use the bone that is closest to us 202 if (possibleBones.Count == 0 && shortestBoneDistance != null) 203 { 204 possibleBones.Add(shortestBoneDistance); 205 } 206 characterPart.bones = possibleBones.ToArray(); 207 208 characterPart.sprite.UpdateMesh(possibleBones.ToArray()); 209 210 return true; 211 } 212 213 static float PointToLineSegmentDistance(Vector2 p, Vector2 a, Vector2 b) 214 { 215 Vector2 n = b - a; 216 Vector2 pa = a - p; 217 218 float c = Vector2.Dot(n, pa); 219 220 // Closest point is a 221 if (c > 0.0f) 222 return Vector2.Dot(pa, pa); 223 224 Vector2 bp = p - b; 225 226 // Closest point is b 227 if (Vector2.Dot(n, bp) > 0.0f) 228 return Vector2.Dot(bp, bp); 229 230 // Closest point is between a and b 231 Vector2 e = pa - n * (c / Vector2.Dot(n, n)); 232 return Vector2.Dot( e, e ); 233 } 234 235 static bool IntersectsSegment(Rect rect, Vector2 p1, Vector2 p2) 236 { 237 float minX = Mathf.Min(p1.x, p2.x); 238 float maxX = Mathf.Max(p1.x, p2.x); 239 240 if (maxX > rect.xMax) 241 { 242 maxX = rect.xMax; 243 } 244 245 if (minX < rect.xMin) 246 { 247 minX = rect.xMin; 248 } 249 250 if (minX > maxX) 251 { 252 return false; 253 } 254 255 float minY = Mathf.Min(p1.y, p2.y); 256 float maxY = Mathf.Max(p1.y, p2.y); 257 258 float dx = p2.x - p1.x; 259 260 if (Mathf.Abs(dx) > float.Epsilon) 261 { 262 float a = (p2.y - p1.y) / dx; 263 float b = p1.y - a * p1.x; 264 minY = a * minX + b; 265 maxY = a * maxX + b; 266 } 267 268 if (minY > maxY) 269 { 270 float tmp = maxY; 271 maxY = minY; 272 minY = tmp; 273 } 274 275 if (maxY > rect.yMax) 276 { 277 maxY = rect.yMax; 278 } 279 280 if (minY < rect.yMin) 281 { 282 minY = rect.yMin; 283 } 284 285 if (minY > maxY) 286 { 287 return false; 288 } 289 290 return true; 291 } 292 293 public static void DeassociateUnusedBones(this SpriteCache sprite) 294 { 295 var skinningCache = sprite.skinningCache; 296 297 Debug.Assert(skinningCache.mode == SkinningMode.Character); 298 299 var characterPart = sprite.GetCharacterPart(); 300 301 Debug.Assert(characterPart != null); 302 303 characterPart.DissociateUnusedBones(); 304 } 305 306 public static void DeassociateAllBones(this SpriteCache sprite) 307 { 308 var skinningCache = sprite.skinningCache; 309 310 if (skinningCache.mode == SkinningMode.SpriteSheet) 311 return; 312 313 var part = sprite.GetCharacterPart(); 314 if (part.bones.Length == 0) 315 return; 316 317 Debug.Assert(part.sprite != null); 318 319 part.bones = new BoneCache[0]; 320 part.sprite.UpdateMesh(part.bones); 321 322 skinningCache.events.characterPartChanged.Invoke(part); 323 } 324 } 325}