Brak opisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SpritePostProcess.cs 16KB

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