Нема описа
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.

SpriteSkinUtility.cs 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. using System;
  2. using Unity.Burst;
  3. using Unity.Collections;
  4. using Unity.Mathematics;
  5. using Unity.Collections.LowLevel.Unsafe;
  6. using UnityEngine.U2D.Common;
  7. namespace UnityEngine.U2D.Animation
  8. {
  9. internal enum SpriteSkinValidationResult
  10. {
  11. SpriteNotFound,
  12. SpriteHasNoSkinningInformation,
  13. SpriteHasNoWeights,
  14. RootTransformNotFound,
  15. InvalidTransformArray,
  16. InvalidTransformArrayLength,
  17. TransformArrayContainsNull,
  18. InvalidBoneWeights,
  19. Ready
  20. }
  21. internal class NativeByteArray
  22. {
  23. public int Length => array.Length;
  24. public bool IsCreated => array.IsCreated;
  25. public byte this[int index] => array[index];
  26. public NativeArray<byte> array
  27. { get; }
  28. public NativeByteArray(NativeArray<byte> array)
  29. {
  30. this.array = array;
  31. }
  32. public void Dispose() => array.Dispose();
  33. }
  34. internal static class SpriteSkinUtility
  35. {
  36. internal static SpriteSkinValidationResult Validate(this SpriteSkin spriteSkin)
  37. {
  38. if (spriteSkin.spriteRenderer.sprite == null)
  39. return SpriteSkinValidationResult.SpriteNotFound;
  40. var bindPoses = spriteSkin.spriteRenderer.sprite.GetBindPoses();
  41. var bindPoseCount = bindPoses.Length;
  42. if (bindPoseCount == 0)
  43. return SpriteSkinValidationResult.SpriteHasNoSkinningInformation;
  44. if (spriteSkin.rootBone == null)
  45. return SpriteSkinValidationResult.RootTransformNotFound;
  46. if (spriteSkin.boneTransforms == null)
  47. return SpriteSkinValidationResult.InvalidTransformArray;
  48. if (bindPoseCount != spriteSkin.boneTransforms.Length)
  49. return SpriteSkinValidationResult.InvalidTransformArrayLength;
  50. foreach (var boneTransform in spriteSkin.boneTransforms)
  51. {
  52. if (boneTransform == null)
  53. return SpriteSkinValidationResult.TransformArrayContainsNull;
  54. }
  55. var boneWeights = spriteSkin.spriteBoneWeights;
  56. if (!BurstedSpriteSkinUtilities.ValidateBoneWeights(in boneWeights, bindPoseCount))
  57. return SpriteSkinValidationResult.InvalidBoneWeights;
  58. return SpriteSkinValidationResult.Ready;
  59. }
  60. internal static void CreateBoneHierarchy(this SpriteSkin spriteSkin)
  61. {
  62. if (spriteSkin.spriteRenderer.sprite == null)
  63. throw new InvalidOperationException("SpriteRenderer has no Sprite set");
  64. var spriteBones = spriteSkin.spriteRenderer.sprite.GetBones();
  65. var transforms = new Transform[spriteBones.Length];
  66. Transform root = null;
  67. for (var i = 0; i < spriteBones.Length; ++i)
  68. {
  69. CreateGameObject(i, spriteBones, transforms, spriteSkin.transform);
  70. if (spriteBones[i].parentId < 0 && root == null)
  71. root = transforms[i];
  72. }
  73. spriteSkin.rootBone = root;
  74. spriteSkin.boneTransforms = transforms;
  75. }
  76. internal static int GetVertexStreamSize(this Sprite sprite)
  77. {
  78. var vertexStreamSize = 12;
  79. if (sprite.HasVertexAttribute(Rendering.VertexAttribute.Normal))
  80. vertexStreamSize = vertexStreamSize + 12;
  81. if (sprite.HasVertexAttribute(Rendering.VertexAttribute.Tangent))
  82. vertexStreamSize = vertexStreamSize + 16;
  83. return vertexStreamSize;
  84. }
  85. internal static int GetVertexStreamOffset(this Sprite sprite, Rendering.VertexAttribute channel )
  86. {
  87. var hasPosition = sprite.HasVertexAttribute(Rendering.VertexAttribute.Position);
  88. var hasNormals = sprite.HasVertexAttribute(Rendering.VertexAttribute.Normal);
  89. var hasTangents = sprite.HasVertexAttribute(Rendering.VertexAttribute.Tangent);
  90. switch(channel)
  91. {
  92. case Rendering.VertexAttribute.Position:
  93. return hasPosition ? 0 : -1;
  94. case Rendering.VertexAttribute.Normal:
  95. return hasNormals ? 12 : -1;
  96. case Rendering.VertexAttribute.Tangent:
  97. return hasTangents ? (hasNormals ? 24 : 12) : -1;
  98. }
  99. return -1;
  100. }
  101. static void CreateGameObject(int index, SpriteBone[] spriteBones, Transform[] transforms, Transform root)
  102. {
  103. if (transforms[index] == null)
  104. {
  105. var spriteBone = spriteBones[index];
  106. if (spriteBone.parentId >= 0)
  107. CreateGameObject(spriteBone.parentId, spriteBones, transforms, root);
  108. var go = new GameObject(spriteBone.name);
  109. var transform = go.transform;
  110. if (spriteBone.parentId >= 0)
  111. transform.SetParent(transforms[spriteBone.parentId]);
  112. else
  113. transform.SetParent(root);
  114. transform.localPosition = spriteBone.position;
  115. transform.localRotation = spriteBone.rotation;
  116. transform.localScale = Vector3.one;
  117. transforms[index] = transform;
  118. }
  119. }
  120. internal static void ResetBindPose(this SpriteSkin spriteSkin)
  121. {
  122. if (!spriteSkin.isValid)
  123. throw new InvalidOperationException("SpriteSkin is not valid");
  124. var spriteBones = spriteSkin.spriteRenderer.sprite.GetBones();
  125. var boneTransforms = spriteSkin.boneTransforms;
  126. for (int i = 0; i < boneTransforms.Length; ++i)
  127. {
  128. var boneTransform = boneTransforms[i];
  129. var spriteBone = spriteBones[i];
  130. if (spriteBone.parentId != -1)
  131. {
  132. boneTransform.localPosition = spriteBone.position;
  133. boneTransform.localRotation = spriteBone.rotation;
  134. boneTransform.localScale = Vector3.one;
  135. }
  136. }
  137. }
  138. static int GetHash(Matrix4x4 matrix)
  139. {
  140. unsafe
  141. {
  142. var b = (uint*)&matrix;
  143. {
  144. var c = (char*)b;
  145. return (int)math.hash(c, 16 * sizeof(float));
  146. }
  147. }
  148. }
  149. internal static int CalculateTransformHash(this SpriteSkin spriteSkin)
  150. {
  151. var bits = 0;
  152. var boneTransformHash = GetHash(spriteSkin.transform.localToWorldMatrix) >> bits;
  153. bits++;
  154. foreach (var transform in spriteSkin.boneTransforms)
  155. {
  156. boneTransformHash ^= GetHash(transform.localToWorldMatrix) >> bits;
  157. bits = (bits + 1) % 8;
  158. }
  159. return boneTransformHash;
  160. }
  161. internal unsafe static void Deform(Sprite sprite, Matrix4x4 rootInv, NativeSlice<Vector3> vertices, NativeSlice<Vector4> tangents, NativeSlice<BoneWeight> boneWeights, NativeArray<Matrix4x4> boneTransforms, NativeSlice<Matrix4x4> bindPoses, NativeArray<byte> deformableVertices)
  162. {
  163. var verticesFloat3 = vertices.SliceWithStride<float3>();
  164. var tangentsFloat4 = tangents.SliceWithStride<float4>();
  165. var bindPosesFloat4x4 = bindPoses.SliceWithStride<float4x4>();
  166. var spriteVertexCount = sprite.GetVertexCount();
  167. var spriteVertexStreamSize = sprite.GetVertexStreamSize();
  168. var boneTransformsFloat4x4 = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<float4x4>(boneTransforms.GetUnsafePtr(), boneTransforms.Length, Allocator.None);
  169. byte* deformedPosOffset = (byte*)NativeArrayUnsafeUtility.GetUnsafePtr(deformableVertices);
  170. NativeSlice<float3> deformableVerticesFloat3 = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice<float3>(deformedPosOffset, spriteVertexStreamSize, spriteVertexCount);
  171. NativeSlice<float4> deformableTangentsFloat4 = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice<float4>(deformedPosOffset, spriteVertexStreamSize, 1); // Just Dummy.
  172. if (sprite.HasVertexAttribute(Rendering.VertexAttribute.Tangent))
  173. {
  174. byte* deformedTanOffset = deformedPosOffset + sprite.GetVertexStreamOffset(Rendering.VertexAttribute.Tangent);
  175. deformableTangentsFloat4 = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice<float4>(deformedTanOffset, spriteVertexStreamSize, spriteVertexCount);
  176. }
  177. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  178. var handle1 = CreateSafetyChecks<float4x4>(ref boneTransformsFloat4x4);
  179. var handle2 = CreateSafetyChecks<float3>(ref deformableVerticesFloat3);
  180. var handle3 = CreateSafetyChecks<float4>(ref deformableTangentsFloat4);
  181. #endif
  182. if (sprite.HasVertexAttribute(Rendering.VertexAttribute.Tangent))
  183. Deform(rootInv, verticesFloat3, tangentsFloat4, boneWeights, boneTransformsFloat4x4, bindPosesFloat4x4, deformableVerticesFloat3, deformableTangentsFloat4);
  184. else
  185. Deform(rootInv, verticesFloat3, boneWeights, boneTransformsFloat4x4, bindPosesFloat4x4, deformableVerticesFloat3);
  186. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  187. DisposeSafetyChecks(handle1);
  188. DisposeSafetyChecks(handle2);
  189. DisposeSafetyChecks(handle3);
  190. #endif
  191. }
  192. internal static void Deform(float4x4 rootInv, NativeSlice<float3> vertices, NativeSlice<BoneWeight> boneWeights, NativeArray<float4x4> boneTransforms, NativeSlice<float4x4> bindPoses, NativeSlice<float3> deformed)
  193. {
  194. if (boneTransforms.Length == 0)
  195. return;
  196. for (var i = 0; i < boneTransforms.Length; i++)
  197. {
  198. var bindPoseMat = bindPoses[i];
  199. var boneTransformMat = boneTransforms[i];
  200. boneTransforms[i] = math.mul(rootInv, math.mul(boneTransformMat, bindPoseMat));
  201. }
  202. for (var i = 0; i < vertices.Length; i++)
  203. {
  204. var bone0 = boneWeights[i].boneIndex0;
  205. var bone1 = boneWeights[i].boneIndex1;
  206. var bone2 = boneWeights[i].boneIndex2;
  207. var bone3 = boneWeights[i].boneIndex3;
  208. var vertex = vertices[i];
  209. deformed[i] =
  210. math.transform(boneTransforms[bone0], vertex) * boneWeights[i].weight0 +
  211. math.transform(boneTransforms[bone1], vertex) * boneWeights[i].weight1 +
  212. math.transform(boneTransforms[bone2], vertex) * boneWeights[i].weight2 +
  213. math.transform(boneTransforms[bone3], vertex) * boneWeights[i].weight3;
  214. }
  215. }
  216. internal static void Deform(float4x4 rootInv, NativeSlice<float3> vertices, NativeSlice<float4> tangents, NativeSlice<BoneWeight> boneWeights, NativeArray<float4x4> boneTransforms, NativeSlice<float4x4> bindPoses, NativeSlice<float3> deformed, NativeSlice<float4> deformedTangents)
  217. {
  218. if(boneTransforms.Length == 0)
  219. return;
  220. for (var i = 0; i < boneTransforms.Length; i++)
  221. {
  222. var bindPoseMat = bindPoses[i];
  223. var boneTransformMat = boneTransforms[i];
  224. boneTransforms[i] = math.mul(rootInv, math.mul(boneTransformMat, bindPoseMat));
  225. }
  226. for (var i = 0; i < vertices.Length; i++)
  227. {
  228. var bone0 = boneWeights[i].boneIndex0;
  229. var bone1 = boneWeights[i].boneIndex1;
  230. var bone2 = boneWeights[i].boneIndex2;
  231. var bone3 = boneWeights[i].boneIndex3;
  232. var vertex = vertices[i];
  233. deformed[i] =
  234. math.transform(boneTransforms[bone0], vertex) * boneWeights[i].weight0 +
  235. math.transform(boneTransforms[bone1], vertex) * boneWeights[i].weight1 +
  236. math.transform(boneTransforms[bone2], vertex) * boneWeights[i].weight2 +
  237. math.transform(boneTransforms[bone3], vertex) * boneWeights[i].weight3;
  238. var tangent = new float4(tangents[i].xyz, 0.0f);
  239. tangent =
  240. math.mul(boneTransforms[bone0], tangent) * boneWeights[i].weight0 +
  241. math.mul(boneTransforms[bone1], tangent) * boneWeights[i].weight1 +
  242. math.mul(boneTransforms[bone2], tangent) * boneWeights[i].weight2 +
  243. math.mul(boneTransforms[bone3], tangent) * boneWeights[i].weight3;
  244. deformedTangents[i] = new float4(math.normalize(tangent.xyz), tangents[i].w);
  245. }
  246. }
  247. internal static void Deform(Sprite sprite, Matrix4x4 invRoot, Transform[] boneTransformsArray, NativeArray<byte> deformVertexData)
  248. {
  249. Debug.Assert(sprite != null);
  250. Debug.Assert(sprite.GetVertexCount() == (deformVertexData.Length / sprite.GetVertexStreamSize()));
  251. var vertices = sprite.GetVertexAttribute<Vector3>(UnityEngine.Rendering.VertexAttribute.Position);
  252. var tangents = sprite.GetVertexAttribute<Vector4>(UnityEngine.Rendering.VertexAttribute.Tangent);
  253. var boneWeights = sprite.GetVertexAttribute<BoneWeight>(UnityEngine.Rendering.VertexAttribute.BlendWeight);
  254. var bindPoses = sprite.GetBindPoses();
  255. Debug.Assert(bindPoses.Length == boneTransformsArray.Length);
  256. Debug.Assert(boneWeights.Length == sprite.GetVertexCount());
  257. var boneTransforms = new NativeArray<Matrix4x4>(boneTransformsArray.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
  258. for (var i = 0; i < boneTransformsArray.Length; ++i)
  259. boneTransforms[i] = boneTransformsArray[i].localToWorldMatrix;
  260. Deform(sprite, invRoot, vertices, tangents, boneWeights, boneTransforms, bindPoses, deformVertexData);
  261. boneTransforms.Dispose();
  262. }
  263. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  264. static AtomicSafetyHandle CreateSafetyChecks<T>(ref NativeArray<T> array) where T : struct
  265. {
  266. var handle = AtomicSafetyHandle.Create();
  267. AtomicSafetyHandle.SetAllowSecondaryVersionWriting(handle, true);
  268. AtomicSafetyHandle.UseSecondaryVersion(ref handle);
  269. NativeArrayUnsafeUtility.SetAtomicSafetyHandle<T>(ref array, handle);
  270. return handle;
  271. }
  272. static AtomicSafetyHandle CreateSafetyChecks<T>(ref NativeSlice<T> array) where T : struct
  273. {
  274. var handle = AtomicSafetyHandle.Create();
  275. AtomicSafetyHandle.SetAllowSecondaryVersionWriting(handle, true);
  276. AtomicSafetyHandle.UseSecondaryVersion(ref handle);
  277. NativeSliceUnsafeUtility.SetAtomicSafetyHandle<T>(ref array, handle);
  278. return handle;
  279. }
  280. static void DisposeSafetyChecks(AtomicSafetyHandle handle)
  281. {
  282. AtomicSafetyHandle.Release(handle);
  283. }
  284. #endif
  285. internal static void Bake(this SpriteSkin spriteSkin, NativeArray<byte> deformVertexData)
  286. {
  287. if (!spriteSkin.isValid)
  288. throw new Exception("Bake error: invalid SpriteSkin");
  289. var sprite = spriteSkin.spriteRenderer.sprite;
  290. var boneTransformsArray = spriteSkin.boneTransforms;
  291. Deform(sprite, Matrix4x4.identity, boneTransformsArray, deformVertexData);
  292. }
  293. internal static unsafe void CalculateBounds(this SpriteSkin spriteSkin)
  294. {
  295. Debug.Assert(spriteSkin.isValid);
  296. var sprite = spriteSkin.sprite;
  297. var deformVertexData = new NativeArray<byte>(sprite.GetVertexStreamSize() * sprite.GetVertexCount(), Allocator.Temp, NativeArrayOptions.UninitializedMemory);
  298. var dataPtr = deformVertexData.GetUnsafePtr();
  299. var deformedPosSlice = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice<Vector3>(dataPtr, sprite.GetVertexStreamSize(), sprite.GetVertexCount());
  300. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  301. NativeSliceUnsafeUtility.SetAtomicSafetyHandle(ref deformedPosSlice, NativeArrayUnsafeUtility.GetAtomicSafetyHandle(deformVertexData));
  302. #endif
  303. spriteSkin.Bake(deformVertexData);
  304. UpdateBounds(spriteSkin, deformVertexData);
  305. deformVertexData.Dispose();
  306. }
  307. internal static Bounds CalculateSpriteSkinBounds(NativeSlice<float3> deformablePositions)
  308. {
  309. var min = deformablePositions[0];
  310. var max = deformablePositions[0];
  311. for (int j = 1; j < deformablePositions.Length; ++j)
  312. {
  313. min = math.min(min, deformablePositions[j]);
  314. max = math.max(max, deformablePositions[j]);
  315. }
  316. var ext = (max - min) * 0.5F;
  317. var ctr = min + ext;
  318. var bounds = new Bounds();
  319. bounds.center = ctr;
  320. bounds.extents = ext;
  321. return bounds;
  322. }
  323. internal static unsafe void UpdateBounds(this SpriteSkin spriteSkin, NativeArray<byte> deformedVertices)
  324. {
  325. var deformedPosOffset = (byte*)NativeArrayUnsafeUtility.GetUnsafePtr(deformedVertices);
  326. var spriteVertexCount = spriteSkin.sprite.GetVertexCount();
  327. var spriteVertexStreamSize = spriteSkin.sprite.GetVertexStreamSize();
  328. var deformedPositions = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice<float3>(deformedPosOffset, spriteVertexStreamSize, spriteVertexCount);
  329. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  330. var handle = CreateSafetyChecks<float3>(ref deformedPositions);
  331. #endif
  332. spriteSkin.bounds = CalculateSpriteSkinBounds(deformedPositions);
  333. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  334. DisposeSafetyChecks(handle);
  335. #endif
  336. InternalEngineBridge.SetLocalAABB(spriteSkin.spriteRenderer, spriteSkin.bounds);
  337. }
  338. }
  339. [BurstCompile]
  340. internal static class BurstedSpriteSkinUtilities
  341. {
  342. [BurstCompile]
  343. internal static bool ValidateBoneWeights(in NativeCustomSlice<BoneWeight> boneWeights, int bindPoseCount)
  344. {
  345. var boneWeightCount = boneWeights.Length;
  346. for (var i = 0; i < boneWeightCount; ++i)
  347. {
  348. var boneWeight = boneWeights[i];
  349. var idx0 = boneWeight.boneIndex0;
  350. var idx1 = boneWeight.boneIndex1;
  351. var idx2 = boneWeight.boneIndex2;
  352. var idx3 = boneWeight.boneIndex3;
  353. if ((idx0 < 0 || idx0 >= bindPoseCount) ||
  354. (idx1 < 0 || idx1 >= bindPoseCount) ||
  355. (idx2 < 0 || idx2 >= bindPoseCount) ||
  356. (idx3 < 0 || idx3 >= bindPoseCount))
  357. return false;
  358. }
  359. return true;
  360. }
  361. }
  362. }