Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

SpriteShapeController.cs 36KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. using System.Collections.Generic;
  2. using Unity.Jobs;
  3. using Unity.Collections;
  4. using Unity.Mathematics;
  5. using Unity.Collections.LowLevel.Unsafe;
  6. using Unity.Profiling;
  7. #if UNITY_EDITOR
  8. using UnityEditor.U2D;
  9. #endif
  10. namespace UnityEngine.U2D
  11. {
  12. /// <summary>
  13. /// SpriteShapeController component contains Spline and SpriteShape Profile information that is used when generating SpriteShape geometry.
  14. /// </summary>
  15. [ExecuteInEditMode]
  16. [RequireComponent(typeof(SpriteShapeRenderer))]
  17. [DisallowMultipleComponent]
  18. [HelpURLAttribute("https://docs.unity3d.com/Packages/com.unity.2d.spriteshape@latest/index.html?subfolder=/manual/SSController.html")]
  19. public class SpriteShapeController : MonoBehaviour
  20. {
  21. // Internal Dataset.
  22. const float s_DistanceTolerance = 0.001f;
  23. // Cached Objects.
  24. SpriteShape m_ActiveSpriteShape;
  25. EdgeCollider2D m_EdgeCollider2D;
  26. PolygonCollider2D m_PolygonCollider2D;
  27. SpriteShapeRenderer m_SpriteShapeRenderer;
  28. SpriteShapeGeometryCache m_SpriteShapeGeometryCache;
  29. Sprite[] m_SpriteArray = new Sprite[0];
  30. Sprite[] m_EdgeSpriteArray = new Sprite[0];
  31. Sprite[] m_CornerSpriteArray = new Sprite[0];
  32. AngleRangeInfo[] m_AngleRangeInfoArray = new AngleRangeInfo[0];
  33. // Required for Generation.
  34. NativeArray<float2> m_ColliderData;
  35. NativeArray<Vector4> m_TangentData;
  36. NativeArray<SpriteShapeGeneratorStats> m_Statistics;
  37. // Renderer Stuff.
  38. bool m_DynamicOcclusionLocal;
  39. bool m_DynamicOcclusionOverriden;
  40. // Added so that shadows will work correctly when no collider is assigned
  41. bool m_ForceColliderShapeUpdate = false;
  42. // Hash Check.
  43. int m_ActiveSplineHash = 0;
  44. int m_ActiveSpriteShapeHash = 0;
  45. int m_MaxArrayCount = 0;
  46. JobHandle m_JobHandle;
  47. SpriteShapeParameters m_ActiveShapeParameters;
  48. // Serialized Data.
  49. [SerializeField]
  50. Spline m_Spline = new Spline();
  51. [SerializeField]
  52. SpriteShape m_SpriteShape;
  53. [SerializeField]
  54. float m_FillPixelPerUnit = 100.0f;
  55. [SerializeField]
  56. float m_StretchTiling = 1.0f;
  57. [SerializeField]
  58. int m_SplineDetail;
  59. [SerializeField]
  60. bool m_AdaptiveUV;
  61. [SerializeField]
  62. bool m_StretchUV;
  63. [SerializeField]
  64. bool m_WorldSpaceUV;
  65. [SerializeField]
  66. float m_CornerAngleThreshold = 30.0f;
  67. [SerializeField]
  68. int m_ColliderDetail;
  69. [SerializeField, Range(-0.5f, 0.5f)]
  70. float m_ColliderOffset;
  71. [SerializeField]
  72. bool m_UpdateCollider = true;
  73. [SerializeField]
  74. bool m_EnableTangents = false;
  75. [SerializeField]
  76. [HideInInspector]
  77. bool m_GeometryCached = false;
  78. [SerializeField]
  79. bool m_UTess2D = true;
  80. [SerializeField]
  81. SpriteShapeGeometryCreator m_Creator;
  82. [SerializeField]
  83. List<SpriteShapeGeometryModifier> m_Modifiers = new List<SpriteShapeGeometryModifier>();
  84. [SerializeField]
  85. List<Vector2> m_ColliderSegment = new List<Vector2>();
  86. internal static readonly ProfilerMarker generateGeometry = new ProfilerMarker("SpriteShape.GenerateGeometry");
  87. internal static readonly ProfilerMarker generateCollider = new ProfilerMarker("SpriteShape.GenerateCollider");
  88. #region GetSet
  89. internal int maxArrayCount
  90. {
  91. get { return m_MaxArrayCount; }
  92. set { m_MaxArrayCount = value; }
  93. }
  94. internal bool geometryCached
  95. {
  96. get { return m_GeometryCached; }
  97. set { m_GeometryCached = value; }
  98. }
  99. internal int splineHashCode
  100. {
  101. get { return m_ActiveSplineHash; }
  102. }
  103. internal Sprite[] spriteArray
  104. {
  105. get { return m_SpriteArray; }
  106. }
  107. internal SpriteShapeParameters spriteShapeParameters
  108. {
  109. get { return m_ActiveShapeParameters; }
  110. }
  111. internal SpriteShapeGeometryCache spriteShapeGeometryCache
  112. {
  113. get
  114. {
  115. if (!m_SpriteShapeGeometryCache)
  116. {
  117. bool b = TryGetComponent(typeof(SpriteShapeGeometryCache), out Component comp);
  118. m_SpriteShapeGeometryCache = b ? (comp as SpriteShapeGeometryCache) : null;
  119. }
  120. return m_SpriteShapeGeometryCache;
  121. }
  122. }
  123. internal Sprite[] cornerSpriteArray
  124. {
  125. get { return m_CornerSpriteArray; }
  126. }
  127. internal Sprite[] edgeSpriteArray
  128. {
  129. get { return m_EdgeSpriteArray; }
  130. }
  131. /// <summary> Angle Ranges </summary>
  132. public AngleRangeInfo[] angleRangeInfoArray
  133. {
  134. get { return m_AngleRangeInfoArray; }
  135. }
  136. /// <summary>Get/Set SpriteShape Geometry Creator. </summary>
  137. public SpriteShapeGeometryCreator spriteShapeCreator
  138. {
  139. get
  140. {
  141. if (m_Creator == null)
  142. m_Creator = SpriteShapeDefaultCreator.defaultInstance;
  143. return m_Creator;
  144. }
  145. set
  146. {
  147. if (value != null)
  148. m_Creator = value;
  149. }
  150. }
  151. /// <summary>Get a list of Modifiers. </summary>
  152. public List<SpriteShapeGeometryModifier> modifiers
  153. {
  154. get { return m_Modifiers; }
  155. }
  156. /// <summary>Hash code for SpriteShape used to check for changes. </summary>
  157. public int spriteShapeHashCode
  158. {
  159. get { return m_ActiveSpriteShapeHash; }
  160. }
  161. /// <summary>Defines whether UV for fill geometry uses local or global space. </summary>
  162. public bool worldSpaceUVs
  163. {
  164. get { return m_WorldSpaceUV; }
  165. set { m_WorldSpaceUV = value; }
  166. }
  167. /// <summary>Defines pixel per unit for fill geometry UV generation. </summary>
  168. public float fillPixelsPerUnit
  169. {
  170. get { return m_FillPixelPerUnit; }
  171. set { m_FillPixelPerUnit = value; }
  172. }
  173. /// <summary>Enable tangent channel when generating SpriteShape geometry (used in Shaders) </summary>
  174. public bool enableTangents
  175. {
  176. get { return m_EnableTangents; }
  177. set { m_EnableTangents = value; }
  178. }
  179. /// <summary>Stretch tiling for inner fill geometry UV generation. </summary>
  180. public float stretchTiling
  181. {
  182. get { return m_StretchTiling; }
  183. set { m_StretchTiling = value; }
  184. }
  185. /// <summary>Level of detail for generated geometry. </summary>
  186. public int splineDetail
  187. {
  188. get { return m_SplineDetail; }
  189. set { m_SplineDetail = Mathf.Max(0, value); }
  190. }
  191. /// <summary>Level of detail for geometry generated for colliders. </summary>
  192. public int colliderDetail
  193. {
  194. get { return m_ColliderDetail; }
  195. set { m_ColliderDetail = Mathf.Max(0, value); }
  196. }
  197. /// <summary>Offset for colliders. </summary>
  198. public float colliderOffset
  199. {
  200. get { return m_ColliderOffset; }
  201. set { m_ColliderOffset = value; }
  202. }
  203. /// <summary>Angle threshold within which corners are enabled. </summary>
  204. public float cornerAngleThreshold
  205. {
  206. get { return m_CornerAngleThreshold; }
  207. set { m_CornerAngleThreshold = value; }
  208. }
  209. /// <summary>Auto update colliders on any change to SpriteShape geometry. </summary>
  210. public bool autoUpdateCollider
  211. {
  212. get { return m_UpdateCollider; }
  213. set { m_UpdateCollider = value; }
  214. }
  215. /// <summary>Optimize generated collider geometry. </summary>
  216. public bool optimizeCollider
  217. {
  218. get { return true; }
  219. }
  220. /// <summary>Optimize generated SpriteShape geometry. </summary>
  221. public bool optimizeGeometry
  222. {
  223. get { return true; }
  224. }
  225. /// <summary>Does this SpriteShapeController object has colliders ?</summary>
  226. public bool hasCollider
  227. {
  228. get { return (edgeCollider != null) || (polygonCollider != null); }
  229. }
  230. /// <summary>Spline object that has data to create the Bezier curve of this SpriteShape Controller. </summary>
  231. public Spline spline
  232. {
  233. get { return m_Spline; }
  234. }
  235. /// <summary>SpriteShape Profile asset that contains information on how to generate/render SpriteShapes. </summary>
  236. public SpriteShape spriteShape
  237. {
  238. get { return m_SpriteShape; }
  239. set { m_SpriteShape = value; }
  240. }
  241. /// <summary>EdgeCollider2D component attached to this Object.</summary>
  242. public EdgeCollider2D edgeCollider
  243. {
  244. get
  245. {
  246. if (!m_EdgeCollider2D)
  247. {
  248. bool b = TryGetComponent(typeof(EdgeCollider2D), out Component comp);
  249. m_EdgeCollider2D = b ? (comp as EdgeCollider2D) : null;
  250. }
  251. return m_EdgeCollider2D;
  252. }
  253. }
  254. /// <summary>PolygonCollider2D component attached to this Object.</summary>
  255. public PolygonCollider2D polygonCollider
  256. {
  257. get
  258. {
  259. if (!m_PolygonCollider2D)
  260. {
  261. bool b = TryGetComponent(typeof(PolygonCollider2D), out Component comp);
  262. m_PolygonCollider2D = b ? (comp as PolygonCollider2D) : null;
  263. }
  264. return m_PolygonCollider2D;
  265. }
  266. }
  267. /// <summary>SpriteShapeRenderer component of this Object. </summary>
  268. public SpriteShapeRenderer spriteShapeRenderer
  269. {
  270. get
  271. {
  272. if (!m_SpriteShapeRenderer)
  273. m_SpriteShapeRenderer = GetComponent<SpriteShapeRenderer>();
  274. return m_SpriteShapeRenderer;
  275. }
  276. }
  277. internal bool forceColliderShapeUpdate
  278. {
  279. get { return m_ForceColliderShapeUpdate; }
  280. }
  281. internal NativeArray<SpriteShapeGeneratorStats> stats
  282. {
  283. get
  284. {
  285. if (!m_Statistics.IsCreated)
  286. m_Statistics = new NativeArray<SpriteShapeGeneratorStats>(1, Allocator.Persistent);
  287. return m_Statistics;
  288. }
  289. }
  290. #endregion
  291. #region EventHandles.
  292. void DisposeInternal()
  293. {
  294. m_JobHandle.Complete();
  295. if (m_ColliderData.IsCreated)
  296. m_ColliderData.Dispose();
  297. if (m_TangentData.IsCreated)
  298. m_TangentData.Dispose();
  299. if (m_Statistics.IsCreated)
  300. m_Statistics.Dispose();
  301. }
  302. void OnApplicationQuit()
  303. {
  304. DisposeInternal();
  305. }
  306. void OnEnable()
  307. {
  308. m_DynamicOcclusionOverriden = true;
  309. m_DynamicOcclusionLocal = spriteShapeRenderer.allowOcclusionWhenDynamic;
  310. spriteShapeRenderer.allowOcclusionWhenDynamic = false;
  311. InitBounds();
  312. UpdateSpriteData();
  313. }
  314. void OnDisable()
  315. {
  316. UpdateGeometryCache();
  317. DisposeInternal();
  318. }
  319. void OnDestroy()
  320. {
  321. }
  322. void Reset()
  323. {
  324. m_SplineDetail = (int)QualityDetail.High;
  325. m_AdaptiveUV = true;
  326. m_StretchUV = false;
  327. m_FillPixelPerUnit = 100f;
  328. m_ColliderDetail = (int)QualityDetail.High;
  329. m_StretchTiling = 1.0f;
  330. m_WorldSpaceUV = false;
  331. m_CornerAngleThreshold = 30.0f;
  332. m_ColliderOffset = 0;
  333. m_UpdateCollider = true;
  334. m_EnableTangents = false;
  335. spline.Clear();
  336. spline.InsertPointAt(0, Vector2.left + Vector2.down);
  337. spline.InsertPointAt(1, Vector2.left + Vector2.up);
  338. spline.InsertPointAt(2, Vector2.right + Vector2.up);
  339. spline.InsertPointAt(3, Vector2.right + Vector2.down);
  340. }
  341. static void SmartDestroy(UnityEngine.Object o)
  342. {
  343. if (o == null)
  344. return;
  345. #if UNITY_EDITOR
  346. if (!Application.isPlaying)
  347. DestroyImmediate(o);
  348. else
  349. #endif
  350. Destroy(o);
  351. }
  352. #endregion
  353. #region HashAndDataCheck
  354. internal Bounds InitBounds()
  355. {
  356. var pointCount = spline.GetPointCount();
  357. if (pointCount > 1)
  358. {
  359. Bounds bounds = new Bounds(spline.GetPosition(0), Vector3.zero);
  360. for (int i = 1; i < pointCount; ++i)
  361. bounds.Encapsulate(spline.GetPosition(i));
  362. bounds.Encapsulate(spriteShapeRenderer.localBounds);
  363. spriteShapeRenderer.SetLocalAABB(bounds);
  364. return bounds;
  365. }
  366. return new Bounds();
  367. }
  368. /// <summary>
  369. /// Refresh SpriteShape Hash so its force generated again on the next frame if its visible.
  370. /// </summary>
  371. public void RefreshSpriteShape()
  372. {
  373. m_ActiveSplineHash = 0;
  374. }
  375. // Ensure Neighbor points are not too close to each other.
  376. bool ValidateSpline()
  377. {
  378. int pointCount = spline.GetPointCount();
  379. if (pointCount < 2)
  380. return false;
  381. for (int i = 0; i < pointCount - 1; ++i)
  382. {
  383. var vec = spline.GetPosition(i) - spline.GetPosition(i + 1);
  384. if (vec.sqrMagnitude < s_DistanceTolerance)
  385. {
  386. Debug.LogWarningFormat(gameObject, "[SpriteShape] Control points {0} & {1} are too close. SpriteShape will not be generated for < {2} >.", i, i + 1, gameObject.name);
  387. return false;
  388. }
  389. }
  390. return true;
  391. }
  392. // Ensure SpriteShape is valid if not
  393. bool ValidateSpriteShapeTexture()
  394. {
  395. bool valid = false;
  396. // Check if SpriteShape Profile is valid.
  397. if (spriteShape != null)
  398. {
  399. // Open ended and no valid Sprites set. Check if it has a valid fill texture.
  400. if (!spline.isOpenEnded)
  401. {
  402. valid = (spriteShape.fillTexture != null);
  403. }
  404. }
  405. else
  406. {
  407. // Warn that no SpriteShape is set.
  408. Debug.LogWarningFormat(gameObject, "[SpriteShape] A valid SpriteShape profile has not been set for gameObject < {0} >.", gameObject.name);
  409. #if UNITY_EDITOR
  410. // We allow null SpriteShape for rapid prototyping in Editor.
  411. valid = true;
  412. #endif
  413. }
  414. return valid;
  415. }
  416. internal bool ValidateUTess2D()
  417. {
  418. bool uTess2D = m_UTess2D;
  419. // Check for all properties that can create overlaps/intersections.
  420. if (m_UTess2D && null != spriteShape)
  421. {
  422. uTess2D = (spriteShape.fillOffset == 0);
  423. }
  424. return uTess2D;
  425. }
  426. bool HasSpriteShapeChanged()
  427. {
  428. bool changed = (m_ActiveSpriteShape != spriteShape);
  429. if (changed)
  430. m_ActiveSpriteShape = spriteShape;
  431. return changed;
  432. }
  433. bool HasSpriteShapeDataChanged()
  434. {
  435. bool updateSprites = HasSpriteShapeChanged();
  436. if (spriteShape)
  437. {
  438. var hashCode = SpriteShape.GetSpriteShapeHashCode(spriteShape);
  439. if (spriteShapeHashCode != hashCode)
  440. {
  441. m_ActiveSpriteShapeHash = hashCode;
  442. updateSprites = true;
  443. }
  444. }
  445. return updateSprites;
  446. }
  447. int GetCustomScriptHashCode()
  448. {
  449. int hashCode = 0;
  450. unchecked
  451. {
  452. hashCode = (int)2166136261 ^ spriteShapeCreator.GetVersion();
  453. foreach (var mod in m_Modifiers)
  454. if (null != mod)
  455. hashCode = hashCode * 16777619 ^ mod.GetVersion();
  456. }
  457. return hashCode;
  458. }
  459. bool HasSplineDataChanged()
  460. {
  461. unchecked
  462. {
  463. // Spline.
  464. int hashCode = (int)2166136261 ^ spline.GetHashCode();
  465. // Local Stuff.
  466. hashCode = hashCode * 16777619 ^ (m_UTess2D ? 1 : 0);
  467. hashCode = hashCode * 16777619 ^ (m_WorldSpaceUV ? 1 : 0);
  468. hashCode = hashCode * 16777619 ^ (m_EnableTangents ? 1 : 0);
  469. hashCode = hashCode * 16777619 ^ (m_GeometryCached ? 1 : 0);
  470. hashCode = hashCode * 16777619 ^ (m_StretchTiling.GetHashCode());
  471. hashCode = hashCode * 16777619 ^ (m_ColliderOffset.GetHashCode());
  472. hashCode = hashCode * 16777619 ^ (m_ColliderDetail.GetHashCode());
  473. hashCode = hashCode * 16777619 ^ (GetCustomScriptHashCode());
  474. hashCode = hashCode * 16777619 ^ (edgeCollider == null ? 0 : 1);
  475. hashCode = hashCode * 16777619 ^ (polygonCollider == null ? 0 : 1);
  476. if (splineHashCode != hashCode)
  477. {
  478. m_ActiveSplineHash = hashCode;
  479. return true;
  480. }
  481. }
  482. return false;
  483. }
  484. void LateUpdate()
  485. {
  486. BakeCollider();
  487. }
  488. void OnWillRenderObject()
  489. {
  490. BakeMesh();
  491. }
  492. /// <summary>
  493. /// Generate geometry on a Job.
  494. /// </summary>
  495. /// <returns>JobHandle for the SpriteShape geometry generation job.</returns>
  496. public JobHandle BakeMesh()
  497. {
  498. JobHandle jobHandle = default;
  499. #if !UNITY_EDITOR
  500. if (spriteShapeGeometryCache)
  501. {
  502. // If SpriteShapeGeometry has already been uploaded, don't bother checking further.
  503. if (0 != m_ActiveSplineHash && 0 != spriteShapeGeometryCache.maxArrayCount)
  504. return jobHandle;
  505. }
  506. #endif
  507. bool valid = ValidateSpline();
  508. if (valid)
  509. {
  510. bool splineChanged = HasSplineDataChanged();
  511. bool spriteShapeChanged = HasSpriteShapeDataChanged();
  512. bool spriteShapeParametersChanged = UpdateSpriteShapeParameters();
  513. if (splineChanged || spriteShapeChanged || spriteShapeParametersChanged)
  514. {
  515. if (spriteShapeChanged)
  516. {
  517. UpdateSpriteData();
  518. }
  519. jobHandle = ScheduleBake();
  520. #if UNITY_EDITOR
  521. UpdateGeometryCache();
  522. #endif
  523. }
  524. }
  525. return jobHandle;
  526. }
  527. #endregion
  528. #region UpdateData
  529. /// <summary>
  530. /// Update Cache.
  531. /// </summary>
  532. internal void UpdateGeometryCache()
  533. {
  534. if (spriteShapeGeometryCache && geometryCached)
  535. {
  536. m_JobHandle.Complete();
  537. spriteShapeGeometryCache.UpdateGeometryCache();
  538. }
  539. }
  540. /// <summary>
  541. /// Force update SpriteShape parameters.
  542. /// </summary>
  543. /// <returns>Returns true if there are changes</returns>
  544. public bool UpdateSpriteShapeParameters()
  545. {
  546. bool carpet = !spline.isOpenEnded;
  547. bool smartSprite = true;
  548. bool adaptiveUV = m_AdaptiveUV;
  549. bool stretchUV = m_StretchUV;
  550. bool spriteBorders = false;
  551. uint fillScale = 0;
  552. uint splineDetail = (uint)m_SplineDetail;
  553. float borderPivot = 0f;
  554. float angleThreshold = (m_CornerAngleThreshold >= 0 && m_CornerAngleThreshold < 90) ? m_CornerAngleThreshold : 89.9999f;
  555. Texture2D fillTexture = null;
  556. Matrix4x4 transformMatrix = Matrix4x4.identity;
  557. if (spriteShape)
  558. {
  559. if (worldSpaceUVs)
  560. transformMatrix = transform.localToWorldMatrix;
  561. fillTexture = spriteShape.fillTexture;
  562. fillScale = stretchUV ? (uint)stretchTiling : (uint)fillPixelsPerUnit;
  563. borderPivot = spriteShape.fillOffset;
  564. spriteBorders = spriteShape.useSpriteBorders;
  565. // If Corners are enabled, set smart-sprite to false.
  566. if (spriteShape.cornerSprites.Count > 0)
  567. smartSprite = false;
  568. }
  569. else
  570. {
  571. #if UNITY_EDITOR
  572. fillTexture = UnityEditor.EditorGUIUtility.whiteTexture;
  573. fillScale = 100;
  574. #endif
  575. }
  576. bool changed = m_ActiveShapeParameters.adaptiveUV != adaptiveUV ||
  577. m_ActiveShapeParameters.angleThreshold != angleThreshold ||
  578. m_ActiveShapeParameters.borderPivot != borderPivot ||
  579. m_ActiveShapeParameters.carpet != carpet ||
  580. m_ActiveShapeParameters.fillScale != fillScale ||
  581. m_ActiveShapeParameters.fillTexture != fillTexture ||
  582. m_ActiveShapeParameters.smartSprite != smartSprite ||
  583. m_ActiveShapeParameters.splineDetail != splineDetail ||
  584. m_ActiveShapeParameters.spriteBorders != spriteBorders ||
  585. m_ActiveShapeParameters.transform != transformMatrix ||
  586. m_ActiveShapeParameters.stretchUV != stretchUV;
  587. m_ActiveShapeParameters.adaptiveUV = adaptiveUV;
  588. m_ActiveShapeParameters.stretchUV = stretchUV;
  589. m_ActiveShapeParameters.angleThreshold = angleThreshold;
  590. m_ActiveShapeParameters.borderPivot = borderPivot;
  591. m_ActiveShapeParameters.carpet = carpet;
  592. m_ActiveShapeParameters.fillScale = fillScale;
  593. m_ActiveShapeParameters.fillTexture = fillTexture;
  594. m_ActiveShapeParameters.smartSprite = smartSprite;
  595. m_ActiveShapeParameters.splineDetail = splineDetail;
  596. m_ActiveShapeParameters.spriteBorders = spriteBorders;
  597. m_ActiveShapeParameters.transform = transformMatrix;
  598. return changed;
  599. }
  600. void UpdateSpriteData()
  601. {
  602. if (spriteShape)
  603. {
  604. List<Sprite> edgeSpriteList = new List<Sprite>();
  605. List<Sprite> cornerSpriteList = new List<Sprite>();
  606. List<AngleRangeInfo> angleRangeInfoList = new List<AngleRangeInfo>();
  607. List<AngleRange> sortedAngleRanges = new List<AngleRange>(spriteShape.angleRanges);
  608. sortedAngleRanges.Sort((a, b) => a.order.CompareTo(b.order));
  609. for (int i = 0; i < sortedAngleRanges.Count; i++)
  610. {
  611. bool validSpritesFound = false;
  612. AngleRange angleRange = sortedAngleRanges[i];
  613. foreach (Sprite edgeSprite in angleRange.sprites)
  614. {
  615. if (edgeSprite != null)
  616. {
  617. validSpritesFound = true;
  618. break;
  619. }
  620. }
  621. if (validSpritesFound)
  622. {
  623. AngleRangeInfo angleRangeInfo = new AngleRangeInfo();
  624. angleRangeInfo.start = angleRange.start;
  625. angleRangeInfo.end = angleRange.end;
  626. angleRangeInfo.order = (uint)angleRange.order;
  627. List<int> spriteIndices = new List<int>();
  628. foreach (Sprite edgeSprite in angleRange.sprites)
  629. {
  630. edgeSpriteList.Add(edgeSprite);
  631. spriteIndices.Add(edgeSpriteList.Count - 1);
  632. }
  633. angleRangeInfo.sprites = spriteIndices.ToArray();
  634. angleRangeInfoList.Add(angleRangeInfo);
  635. }
  636. }
  637. bool validCornerSpritesFound = false;
  638. foreach (CornerSprite cornerSprite in spriteShape.cornerSprites)
  639. {
  640. if (cornerSprite.sprites[0] != null)
  641. {
  642. validCornerSpritesFound = true;
  643. break;
  644. }
  645. }
  646. if (validCornerSpritesFound)
  647. {
  648. for (int i = 0; i < spriteShape.cornerSprites.Count; i++)
  649. {
  650. CornerSprite cornerSprite = spriteShape.cornerSprites[i];
  651. cornerSpriteList.Add(cornerSprite.sprites[0]);
  652. }
  653. }
  654. m_EdgeSpriteArray = edgeSpriteList.ToArray();
  655. m_CornerSpriteArray = cornerSpriteList.ToArray();
  656. m_AngleRangeInfoArray = angleRangeInfoList.ToArray();
  657. List<Sprite> spriteList = new List<Sprite>();
  658. spriteList.AddRange(m_EdgeSpriteArray);
  659. spriteList.AddRange(m_CornerSpriteArray);
  660. m_SpriteArray = spriteList.ToArray();
  661. }
  662. else
  663. {
  664. m_SpriteArray = new Sprite[0];
  665. m_EdgeSpriteArray = new Sprite[0];
  666. m_CornerSpriteArray = new Sprite[0];
  667. m_AngleRangeInfoArray = new AngleRangeInfo[0];
  668. }
  669. }
  670. internal NativeArray<ShapeControlPoint> GetShapeControlPoints()
  671. {
  672. int pointCount = spline.GetPointCount();
  673. NativeArray<ShapeControlPoint> shapePoints = new NativeArray<ShapeControlPoint>(pointCount, Allocator.Temp);
  674. for (int i = 0; i < pointCount; ++i)
  675. {
  676. ShapeControlPoint shapeControlPoint;
  677. shapeControlPoint.position = spline.GetPosition(i);
  678. shapeControlPoint.leftTangent = spline.GetLeftTangent(i);
  679. shapeControlPoint.rightTangent = spline.GetRightTangent(i);
  680. shapeControlPoint.mode = (int)spline.GetTangentMode(i);
  681. shapePoints[i] = shapeControlPoint;
  682. }
  683. return shapePoints;
  684. }
  685. internal NativeArray<SplinePointMetaData> GetSplinePointMetaData()
  686. {
  687. int pointCount = spline.GetPointCount();
  688. NativeArray<SplinePointMetaData> shapeMetaData = new NativeArray<SplinePointMetaData>(pointCount, Allocator.Temp);
  689. for (int i = 0; i < pointCount; ++i)
  690. {
  691. SplinePointMetaData metaData;
  692. metaData.height = m_Spline.GetHeight(i);
  693. metaData.spriteIndex = (uint)m_Spline.GetSpriteIndex(i);
  694. metaData.cornerMode = (int)m_Spline.GetCornerMode(i);
  695. shapeMetaData[i] = metaData;
  696. }
  697. return shapeMetaData;
  698. }
  699. internal int CalculateMaxArrayCount(NativeArray<ShapeControlPoint> shapePoints)
  700. {
  701. int maxVertexCount = 1024 * 64;
  702. bool hasSprites = false;
  703. float smallestWidth = 99999.0f;
  704. if (null != spriteArray)
  705. {
  706. foreach (var sprite in m_SpriteArray)
  707. {
  708. if (sprite != null)
  709. {
  710. hasSprites = true;
  711. float pixelWidth = BezierUtility.GetSpritePixelWidth(sprite);
  712. smallestWidth = (smallestWidth > pixelWidth) ? pixelWidth : smallestWidth;
  713. }
  714. }
  715. }
  716. // Approximate vertex Array Count. Include Corners and Wide Sprites into account.
  717. float smallestSegment = smallestWidth;
  718. float shapeLength = BezierUtility.BezierLength(shapePoints, splineDetail, ref smallestSegment) * 4.0f;
  719. int adjustShape = shapePoints.Length * 4 * splineDetail;
  720. int adjustWidth = hasSprites ? ((int)(shapeLength / smallestSegment) * splineDetail) + adjustShape : 0;
  721. adjustShape = optimizeGeometry ? (adjustShape) : (adjustShape * 2);
  722. adjustShape = ValidateSpriteShapeTexture() ? adjustShape : 0;
  723. maxArrayCount = adjustShape + adjustWidth;
  724. maxArrayCount = math.min(maxArrayCount, maxVertexCount);
  725. return maxArrayCount;
  726. }
  727. #endregion
  728. #region ScheduleAndGenerate
  729. unsafe JobHandle ScheduleBake()
  730. {
  731. JobHandle jobHandle = default;
  732. bool staticUpload = Application.isPlaying;
  733. #if !UNITY_EDITOR
  734. staticUpload = true;
  735. #endif
  736. if (staticUpload && geometryCached)
  737. {
  738. if (spriteShapeGeometryCache)
  739. if (spriteShapeGeometryCache.maxArrayCount != 0)
  740. return spriteShapeGeometryCache.Upload(spriteShapeRenderer, this);
  741. }
  742. maxArrayCount = spriteShapeCreator.GetVertexArrayCount(this);
  743. if (maxArrayCount > 0 && enabled)
  744. {
  745. // Complate previos
  746. m_JobHandle.Complete();
  747. // Collider Data
  748. if (m_ColliderData.IsCreated)
  749. m_ColliderData.Dispose();
  750. m_ColliderData = new NativeArray<float2>(maxArrayCount, Allocator.Persistent);
  751. // Tangent Data
  752. if (!m_TangentData.IsCreated)
  753. m_TangentData = new NativeArray<Vector4>(1, Allocator.Persistent);
  754. NativeArray<ushort> indexArray;
  755. NativeSlice<Vector3> posArray;
  756. NativeSlice<Vector2> uv0Array;
  757. NativeArray<SpriteShapeSegment> geomArray = spriteShapeRenderer.GetSegments(spline.GetPointCount() * 8);
  758. NativeSlice<Vector4> tanArray = new NativeSlice<Vector4>(m_TangentData);
  759. if (m_EnableTangents)
  760. {
  761. spriteShapeRenderer.GetChannels(maxArrayCount, out indexArray, out posArray, out uv0Array, out tanArray);
  762. }
  763. else
  764. {
  765. spriteShapeRenderer.GetChannels(maxArrayCount, out indexArray, out posArray, out uv0Array);
  766. }
  767. m_JobHandle = jobHandle = spriteShapeCreator.MakeCreatorJob(this, indexArray, posArray, uv0Array, tanArray, geomArray, m_ColliderData);
  768. foreach (var geomMod in m_Modifiers)
  769. if (null != geomMod)
  770. m_JobHandle = geomMod.MakeModifierJob(m_JobHandle, this, indexArray, posArray, uv0Array, tanArray, geomArray, m_ColliderData);
  771. // Prepare Renderer.
  772. spriteShapeRenderer.Prepare(m_JobHandle, m_ActiveShapeParameters, m_SpriteArray);
  773. jobHandle = m_JobHandle;
  774. #if UNITY_EDITOR
  775. if (spriteShapeGeometryCache && geometryCached)
  776. spriteShapeGeometryCache.SetGeometryCache(maxArrayCount, posArray, uv0Array, tanArray, indexArray, geomArray);
  777. #endif
  778. JobHandle.ScheduleBatchedJobs();
  779. }
  780. if (m_DynamicOcclusionOverriden)
  781. {
  782. spriteShapeRenderer.allowOcclusionWhenDynamic = m_DynamicOcclusionLocal;
  783. m_DynamicOcclusionOverriden = false;
  784. }
  785. return jobHandle;
  786. }
  787. /// <summary>
  788. /// Update Collider of this Object.
  789. /// </summary>
  790. public void BakeCollider()
  791. {
  792. // Previously this must be explicitly called if using BakeMesh.
  793. // But now we do it internally. BakeCollider_CanBeCalledMultipleTimesWithoutJobComplete
  794. m_JobHandle.Complete();
  795. if (m_ColliderData.IsCreated)
  796. {
  797. if ((autoUpdateCollider && hasCollider) || forceColliderShapeUpdate)
  798. {
  799. int maxCount = short.MaxValue - 1;
  800. float2 last = (float2)0;
  801. m_ColliderSegment.Clear();
  802. for (int i = 0; i < maxCount; ++i)
  803. {
  804. float2 now = m_ColliderData[i];
  805. if (!math.any(last) && !math.any(now))
  806. {
  807. if ((i+1) < maxCount)
  808. {
  809. float2 next = m_ColliderData[i+1];
  810. if (!math.any(next) && !math.any(next))
  811. break;
  812. }
  813. else
  814. break;
  815. }
  816. m_ColliderSegment.Add(new Vector2(now.x, now.y));
  817. }
  818. if (autoUpdateCollider)
  819. {
  820. if (edgeCollider != null)
  821. edgeCollider.points = m_ColliderSegment.ToArray();
  822. if (polygonCollider != null)
  823. polygonCollider.points = m_ColliderSegment.ToArray();
  824. }
  825. #if UNITY_EDITOR
  826. UnityEditor.SceneView.RepaintAll();
  827. #endif
  828. }
  829. // Dispose Collider as its no longer needed.
  830. m_ColliderData.Dispose();
  831. // Print Once.
  832. if (m_Statistics.IsCreated)
  833. {
  834. var stats = m_Statistics[0];
  835. switch (stats.status)
  836. {
  837. case SpriteShapeGeneratorResult.ErrorNativeDataOverflow:
  838. Debug.LogWarningFormat(gameObject, "NativeArray access not within range. Please submit a bug report.");
  839. break;
  840. case SpriteShapeGeneratorResult.ErrorSpritesTightPacked:
  841. Debug.LogWarningFormat(gameObject, "Sprites used in SpriteShape profile must use FullRect.");
  842. break;
  843. case SpriteShapeGeneratorResult.ErrorSpritesWrongBorder:
  844. Debug.LogWarningFormat(gameObject, "Sprites used in SpriteShape profile have invalid borders. Please check SpriteShape profile.");
  845. break;
  846. case SpriteShapeGeneratorResult.ErrorVertexLimitReached:
  847. Debug.LogWarningFormat(gameObject, "Mesh data has reached Limits. Please try dividing shape into smaller blocks.");
  848. break;
  849. case SpriteShapeGeneratorResult.ErrorDefaultQuadCreated:
  850. Debug.LogWarningFormat(gameObject, "Fill tessellation (C# Job) encountered errors. Please disable it to use default tessellation for fill geometry.");
  851. break;
  852. }
  853. }
  854. }
  855. }
  856. internal void BakeMeshForced()
  857. {
  858. if (spriteShapeRenderer != null)
  859. {
  860. var hasSplineChanged = HasSplineDataChanged();
  861. if (hasSplineChanged)
  862. {
  863. BakeMesh();
  864. Rendering.CommandBuffer rc = new Rendering.CommandBuffer();
  865. rc.GetTemporaryRT(0, 256, 256, 0);
  866. rc.SetRenderTarget(0);
  867. rc.DrawRenderer(spriteShapeRenderer, spriteShapeRenderer.sharedMaterial);
  868. rc.ReleaseTemporaryRT(0);
  869. Graphics.ExecuteCommandBuffer(rc);
  870. }
  871. }
  872. }
  873. #endregion
  874. internal void ForceColliderShapeUpdate(bool forceUpdate)
  875. {
  876. m_ForceColliderShapeUpdate = forceUpdate;
  877. }
  878. internal NativeArray<float2> GetColliderShapeData()
  879. {
  880. if (m_ColliderData.IsCreated)
  881. {
  882. JobHandle handle = BakeMesh();
  883. handle.Complete();
  884. BakeCollider();
  885. }
  886. NativeArray<float2> retNativeArray = new NativeArray<float2>(m_ColliderSegment.Count, Allocator.Temp);
  887. for (int i = 0; i < retNativeArray.Length; i++)
  888. retNativeArray[i] = m_ColliderSegment[i];
  889. return retNativeArray;
  890. }
  891. }
  892. }