A game about forced loneliness, made by TACStudios
at master 378 lines 17 kB view raw
1using System; 2using System.Collections.Generic; 3using UnityEngine; 4using Unity.Collections; 5using System.Linq; 6using UnityEditor.U2D.Sprites; 7using UnityEngine.U2D.Animation; 8using UnityEngine.Rendering; 9using UnityEngine.U2D; 10 11namespace UnityEditor.U2D.Animation 12{ 13 internal class SpritePostProcess : AssetPostprocessor 14 { 15 void OnPreprocessAsset() 16 { 17 var dataProvider = GetSpriteEditorDataProvider(assetPath); 18 if (dataProvider != null) 19 InjectMainSkeletonBones(dataProvider); 20 } 21 22 void OnPostprocessSprites(Texture2D texture, Sprite[] sprites) 23 { 24 var ai = GetSpriteEditorDataProvider(assetPath); 25 if (ai != null) 26 { 27 // Injecting these bones a second time, because the Sprite Rect positions 28 // might have updated between OnPreprocessAsset and OnPostprocessSprites. 29 InjectMainSkeletonBones(ai); 30 31 var definitionScale = CalculateDefinitionScale(texture, ai.GetDataProvider<ITextureDataProvider>()); 32 ai.InitSpriteEditorDataProvider(); 33 PostProcessBoneData(ai, definitionScale, sprites); 34 PostProcessSpriteMeshData(ai, definitionScale, sprites, assetImporter); 35 BoneGizmo.instance.ClearSpriteBoneCache(); 36 } 37 38 // Get all SpriteSkin in scene and inform them to refresh their cache 39 RefreshSpriteSkinCache(); 40 } 41 42 static void InjectMainSkeletonBones(ISpriteEditorDataProvider dataProvider) 43 { 44 var characterDataProvider = dataProvider.GetDataProvider<ICharacterDataProvider>(); 45 var mainSkeletonBonesDataProvider = dataProvider.GetDataProvider<IMainSkeletonDataProvider>(); 46 if (characterDataProvider == null || mainSkeletonBonesDataProvider == null) 47 return; 48 49 var skinningCache = Cache.Create<SkinningCache>(); 50 skinningCache.Create(dataProvider, new SkinningCachePersistentStateTemp()); 51 52 var skeletonBones = mainSkeletonBonesDataProvider.GetMainSkeletonData().bones ?? new SpriteBone[0]; 53 RemapCharacterPartsToNewBones(skinningCache, skeletonBones); 54 55 SkinningModule.ApplyChanges(skinningCache, dataProvider); 56 } 57 58 static void RemapCharacterPartsToNewBones(SkinningCache skinningCache, SpriteBone[] newBones) 59 { 60 var skeleton = skinningCache.character.skeleton; 61 var previousStateBones = skeleton.bones; 62 var skeletonBones = skinningCache.CreateBoneCacheFromSpriteBones(newBones, 1.0f); 63 skeleton.SetBones(skeletonBones); 64 65 for (var i = 0; i < skinningCache.character.parts.Length; i++) 66 { 67 var characterPart = skinningCache.character.parts[i]; 68 var useGuids = !skeletonBones.All(newBone => previousStateBones.All(oldBone => newBone.guid != oldBone.guid)); 69 characterPart.bones = useGuids ? characterPart.bones.Select(partBone => Array.Find(skeletonBones, skeletonBone => partBone.guid == skeletonBone.guid)).ToArray() : characterPart.bones.Select(partBone => skeletonBones.ElementAtOrDefault(Array.FindIndex(previousStateBones, oldBone => partBone.guid == oldBone.guid))).ToArray(); 70 71 var mesh = skinningCache.GetMesh(characterPart.sprite); 72 if (mesh != null) 73 mesh.SetCompatibleBoneSet(characterPart.bones); 74 75 skinningCache.character.parts[i] = characterPart; 76 } 77 } 78 79 static void RefreshSpriteSkinCache() 80 { 81 var spriteSkins = GameObject.FindObjectsByType<SpriteSkin>(FindObjectsSortMode.None); 82 foreach (var ss in spriteSkins) 83 { 84 ss.ResetSprite(); 85 } 86 } 87 88 static void CalculateLocaltoWorldMatrix(int i, SpriteRect spriteRect, float definitionScale, float pixelsPerUnit, List<UnityEngine.U2D.SpriteBone> spriteBone, ref UnityEngine.U2D.SpriteBone?[] outpriteBone, ref NativeArray<Matrix4x4> bindPose) 89 { 90 if (outpriteBone[i] != null) 91 return; 92 UnityEngine.U2D.SpriteBone sp = spriteBone[i]; 93 var isRoot = sp.parentId == -1; 94 var position = isRoot ? (spriteBone[i].position - Vector3.Scale(spriteRect.rect.size, spriteRect.pivot)) : spriteBone[i].position; 95 position.z = 0f; 96 sp.position = position * definitionScale / pixelsPerUnit; 97 sp.length = spriteBone[i].length * definitionScale / pixelsPerUnit; 98 outpriteBone[i] = sp; 99 100 // Calculate bind poses 101 var worldPosition = Vector3.zero; 102 var worldRotation = Quaternion.identity; 103 104 if (sp.parentId == -1) 105 { 106 worldPosition = sp.position; 107 worldRotation = sp.rotation; 108 } 109 else 110 { 111 if (outpriteBone[sp.parentId] == null) 112 { 113 CalculateLocaltoWorldMatrix(sp.parentId, spriteRect, definitionScale, pixelsPerUnit, spriteBone, ref outpriteBone, ref bindPose); 114 } 115 116 var parentBindPose = bindPose[sp.parentId]; 117 var invParentBindPose = Matrix4x4.Inverse(parentBindPose); 118 119 worldPosition = invParentBindPose.MultiplyPoint(sp.position); 120 worldRotation = sp.rotation * invParentBindPose.rotation; 121 } 122 123 // Practically Matrix4x4.SetTRInverse 124 var rot = Quaternion.Inverse(worldRotation); 125 Matrix4x4 mat = Matrix4x4.identity; 126 mat = Matrix4x4.Rotate(rot); 127 mat = mat * Matrix4x4.Translate(-worldPosition); 128 129 130 bindPose[i] = mat; 131 } 132 133 static bool PostProcessBoneData(ISpriteEditorDataProvider spriteDataProvider, float definitionScale, Sprite[] sprites) 134 { 135 var boneDataProvider = spriteDataProvider.GetDataProvider<ISpriteBoneDataProvider>(); 136 var textureDataProvider = spriteDataProvider.GetDataProvider<ITextureDataProvider>(); 137 138 if (sprites == null || sprites.Length == 0 || boneDataProvider == null || textureDataProvider == null) 139 return false; 140 141 var dataChanged = false; 142 var spriteRects = spriteDataProvider.GetSpriteRects(); 143 foreach (var sprite in sprites) 144 { 145 var guid = sprite.GetSpriteID(); 146 { 147 var spriteBone = boneDataProvider.GetBones(guid); 148 if (spriteBone == null) 149 continue; 150 151 var spriteBoneCount = spriteBone.Count; 152 if (spriteBoneCount == 0) 153 continue; 154 155 var spriteRect = spriteRects.First(s => { return s.spriteID == guid; }); 156 157 var bindPose = new NativeArray<Matrix4x4>(spriteBoneCount, Allocator.Temp); 158 var outputSpriteBones = new UnityEngine.U2D.SpriteBone? [spriteBoneCount]; 159 for (int i = 0; i < spriteBoneCount; ++i) 160 { 161 CalculateLocaltoWorldMatrix(i, spriteRect, definitionScale, sprite.pixelsPerUnit, spriteBone, ref outputSpriteBones, ref bindPose); 162 } 163 164 sprite.SetBindPoses(bindPose); 165 sprite.SetBones(outputSpriteBones.Select(x => x.Value).ToArray()); 166 bindPose.Dispose(); 167 168 dataChanged = true; 169 } 170 } 171 172 return dataChanged; 173 } 174 175 static bool PostProcessSpriteMeshData(ISpriteEditorDataProvider spriteDataProvider, float definitionScale, Sprite[] sprites, AssetImporter assetImporter) 176 { 177 var spriteMeshDataProvider = spriteDataProvider.GetDataProvider<ISpriteMeshDataProvider>(); 178 var boneDataProvider = spriteDataProvider.GetDataProvider<ISpriteBoneDataProvider>(); 179 var textureDataProvider = spriteDataProvider.GetDataProvider<ITextureDataProvider>(); 180 var outlineDataProvider = spriteDataProvider.GetDataProvider<ISpriteOutlineDataProvider>(); 181 if (sprites == null || sprites.Length == 0 || spriteMeshDataProvider == null || textureDataProvider == null) 182 return false; 183 184 var dataChanged = false; 185 var spriteRects = spriteDataProvider.GetSpriteRects(); 186 var showMeshOverwriteWarning = SkinningModuleSettings.showSpriteMeshOverwriteWarning; 187 foreach (var sprite in sprites) 188 { 189 var guid = sprite.GetSpriteID(); 190 var vertices = spriteMeshDataProvider.GetVertices(guid); 191 int[] indices = null; 192 if (vertices.Length > 2) 193 indices = spriteMeshDataProvider.GetIndices(guid); 194 195 var spriteBone = boneDataProvider.GetBones(guid); 196 var hasBones = spriteBone is { Count: > 0 }; 197 198 if (indices != null && indices.Length > 2 && vertices.Length > 2) 199 { 200 var spriteRect = spriteRects.First(s => { return s.spriteID == guid; }); 201 var hasInvalidWeights = false; 202 203 var vertexArray = new NativeArray<Vector3>(vertices.Length, Allocator.Temp); 204 var boneWeightArray = new NativeArray<BoneWeight>(vertices.Length, Allocator.Temp); 205 206 for (int i = 0; i < vertices.Length; ++i) 207 { 208 var boneWeight = vertices[i].boneWeight; 209 210 vertexArray[i] = (Vector3)(vertices[i].position - Vector2.Scale(spriteRect.rect.size, spriteRect.pivot)) * definitionScale / sprite.pixelsPerUnit; 211 boneWeightArray[i] = boneWeight; 212 213 if (hasBones && !hasInvalidWeights) 214 { 215 var sum = boneWeight.weight0 + boneWeight.weight1 + boneWeight.weight2 + boneWeight.weight3; 216 hasInvalidWeights = sum < 0.999f; 217 } 218 } 219 220 var indicesArray = new NativeArray<ushort>(indices.Length, Allocator.Temp); 221 for (int i = 0; i < indices.Length; ++i) 222 indicesArray[i] = (ushort)indices[i]; 223 224 if (showMeshOverwriteWarning && outlineDataProvider?.GetOutlines(guid)?.Count > 0) 225 Debug.LogWarning(string.Format(TextContent.spriteMeshOverwriteWarning, sprite.name), assetImporter); 226 sprite.SetVertexCount(vertices.Length); 227 sprite.SetVertexAttribute<Vector3>(VertexAttribute.Position, vertexArray); 228 sprite.SetIndices(indicesArray); 229 if (hasBones) 230 sprite.SetVertexAttribute<BoneWeight>(VertexAttribute.BlendWeight, boneWeightArray); 231 vertexArray.Dispose(); 232 boneWeightArray.Dispose(); 233 indicesArray.Dispose(); 234 235 // Deformed Sprites require proper Tangent Channels if Lit. Enable Tangent channels. 236 if (hasBones) 237 { 238 var tangentArray = new NativeArray<Vector4>(vertices.Length, Allocator.Temp); 239 for (int i = 0; i < vertices.Length; ++i) 240 tangentArray[i] = new Vector4(1.0f, 0.0f, 0, -1.0f); 241 sprite.SetVertexAttribute<Vector4>(VertexAttribute.Tangent, tangentArray); 242 tangentArray.Dispose(); 243 } 244 245 dataChanged = true; 246 247 if (hasBones && hasInvalidWeights) 248 Debug.LogWarning(string.Format(TextContent.boneWeightsNotSumZeroWarning, spriteRect.name), assetImporter); 249 } 250 else 251 { 252 if (hasBones) 253 { 254 var boneWeightArray = new NativeArray<BoneWeight>(sprite.GetVertexCount(), Allocator.Temp); 255 var defaultBoneWeight = new BoneWeight() { weight0 = 1f }; 256 257 for (var i = 0; i < boneWeightArray.Length; ++i) 258 boneWeightArray[i] = defaultBoneWeight; 259 260 sprite.SetVertexAttribute<BoneWeight>(VertexAttribute.BlendWeight, boneWeightArray); 261 } 262 } 263 } 264 265 return dataChanged; 266 } 267 268 static float CalculateDefinitionScale(Texture2D texture, ITextureDataProvider dataProvider) 269 { 270 float definitionScale = 1; 271 if (texture != null && dataProvider != null) 272 { 273 int actualWidth = 0, actualHeight = 0; 274 dataProvider.GetTextureActualWidthAndHeight(out actualWidth, out actualHeight); 275 float definitionScaleW = texture.width / (float)actualWidth; 276 float definitionScaleH = texture.height / (float)actualHeight; 277 definitionScale = Mathf.Min(definitionScaleW, definitionScaleH); 278 } 279 280 return definitionScale; 281 } 282 283 static ISpriteEditorDataProvider GetSpriteEditorDataProvider(string assetPath) 284 { 285 var dataProviderFactories = new SpriteDataProviderFactories(); 286 dataProviderFactories.Init(); 287 return dataProviderFactories.GetSpriteEditorDataProviderFromObject(AssetImporter.GetAtPath(assetPath)); 288 } 289 290 internal class SkinningCachePersistentStateTemp : ISkinningCachePersistentState 291 { 292 private string _lastSpriteId; 293 private Tools _lastUsedTool; 294 private List<int> _lastBoneSelectionIds = null; 295 private Texture2D _lastTexture = null; 296 private SerializableDictionary<int, BonePose> _lastPreviewPose = null; 297 private SerializableDictionary<int, bool> _lastBoneVisibility = null; 298 private SerializableDictionary<int, bool> _lastBoneExpansion = null; 299 private SerializableDictionary<string, bool> _lastSpriteVisibility = null; 300 private SerializableDictionary<int, bool> _lastGroupVisibility = null; 301 private SkinningMode _lastMode; 302 private bool _lastVisibilityToolActive; 303 private int _lastVisibilityToolIndex; 304 private IndexedSelection _lastVertexSelection = null; 305 private float _lastBrushSize; 306 private float _lastBrushHardness; 307 private float _lastBrushStep; 308 309 string ISkinningCachePersistentState.lastSpriteId 310 { 311 get => _lastSpriteId; 312 set => _lastSpriteId = value; 313 } 314 315 Tools ISkinningCachePersistentState.lastUsedTool 316 { 317 get => _lastUsedTool; 318 set => _lastUsedTool = value; 319 } 320 321 List<int> ISkinningCachePersistentState.lastBoneSelectionIds => _lastBoneSelectionIds; 322 323 Texture2D ISkinningCachePersistentState.lastTexture 324 { 325 get => _lastTexture; 326 set => _lastTexture = value; 327 } 328 329 SerializableDictionary<int, BonePose> ISkinningCachePersistentState.lastPreviewPose => _lastPreviewPose; 330 331 SerializableDictionary<int, bool> ISkinningCachePersistentState.lastBoneVisibility => _lastBoneVisibility; 332 333 SerializableDictionary<int, bool> ISkinningCachePersistentState.lastBoneExpansion => _lastBoneExpansion; 334 335 SerializableDictionary<string, bool> ISkinningCachePersistentState.lastSpriteVisibility => _lastSpriteVisibility; 336 337 SerializableDictionary<int, bool> ISkinningCachePersistentState.lastGroupVisibility => _lastGroupVisibility; 338 339 SkinningMode ISkinningCachePersistentState.lastMode 340 { 341 get => _lastMode; 342 set => _lastMode = value; 343 } 344 345 bool ISkinningCachePersistentState.lastVisibilityToolActive 346 { 347 get => _lastVisibilityToolActive; 348 set => _lastVisibilityToolActive = value; 349 } 350 351 int ISkinningCachePersistentState.lastVisibilityToolIndex 352 { 353 get => _lastVisibilityToolIndex; 354 set => _lastVisibilityToolIndex = value; 355 } 356 357 IndexedSelection ISkinningCachePersistentState.lastVertexSelection => _lastVertexSelection; 358 359 float ISkinningCachePersistentState.lastBrushSize 360 { 361 get => _lastBrushSize; 362 set => _lastBrushSize = value; 363 } 364 365 float ISkinningCachePersistentState.lastBrushHardness 366 { 367 get => _lastBrushHardness; 368 set => _lastBrushHardness = value; 369 } 370 371 float ISkinningCachePersistentState.lastBrushStep 372 { 373 get => _lastBrushStep; 374 set => _lastBrushStep = value; 375 } 376 } 377 } 378}