Sin descripción
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.

SpriteShapeController.cs 39KB

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