Canvas3DImpl.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. public class Canvas3DImpl : Singleton<Canvas3DImpl>
  5. {
  6. private Dictionary<int, Sprite3D> m_childSprites = new Dictionary<int, Sprite3D>();
  7. private Dictionary<int, TextMesh> m_childText = new Dictionary<int, TextMesh>();
  8. private int m_depth = 0;
  9. private bool m_needRefreshLayout = false;
  10. private Dictionary<string, AutoAtlasInfo> m_atlas = new Dictionary<string, AutoAtlasInfo>();
  11. private bool m_needRebuildAtlas = false;
  12. private class AutoAtlasInfo
  13. {
  14. private static int[] textureSize =
  15. {
  16. 128,
  17. 256,
  18. 512,
  19. 1024
  20. };
  21. private int counter = 1;
  22. private Dictionary<int, AtlasInfo.UVDetail> textures = new Dictionary<int, AtlasInfo.UVDetail>(); //Texture的GUID和uv的映射关系
  23. public bool needRebuildAtlas = false;
  24. private bool needCompress = false;
  25. private int padding = 0;
  26. private HashSet<Sprite3D> sprites = new HashSet<Sprite3D>();
  27. public Texture2D atlas = null;
  28. public Texture2D altasAlpha = null; //做纹理压缩用的
  29. private Material mat = null;
  30. private Dictionary<int, Texture2D> waitForCombineTextures = new Dictionary<int, Texture2D>();
  31. public void Register(Sprite3D sprite)
  32. {
  33. if (null == sprite || null == sprite.texture)
  34. {
  35. return;
  36. }
  37. int id = sprite.texture.GetNativeTextureID();
  38. sprite.m_textureGUID = id;
  39. AtlasInfo.UVDetail uv = null;
  40. padding = Mathf.Max(padding, sprite.padding);
  41. needCompress = needCompress | sprite.compress;
  42. if (textures.TryGetValue(id, out uv))
  43. {
  44. sprites.Add(sprite);
  45. if (null != mat)
  46. {
  47. sprite.SetMaterial(mat);
  48. }
  49. sprite.SetAutoAtlas(atlas, uv);
  50. return;
  51. }
  52. else
  53. {
  54. uv = new AtlasInfo.UVDetail();
  55. uv.width = 0;
  56. uv.height = 0;
  57. uv.width = sprite.texture.width;
  58. uv.height = sprite.texture.height;
  59. uv.rotate = false;
  60. textures.Add(id, uv);
  61. waitForCombineTextures.Add(id, sprite.texture);
  62. }
  63. needRebuildAtlas = true;
  64. sprites.Add(sprite);
  65. }
  66. public void Unregister(Sprite3D sprite)
  67. {
  68. sprites.Remove(sprite);
  69. if (sprites.Count == 0)
  70. {
  71. textures.Clear();
  72. if (mat != null)
  73. {
  74. Object.Destroy(mat);
  75. }
  76. mat = null;
  77. if (atlas != null)
  78. {
  79. Object.Destroy(atlas);
  80. }
  81. atlas = null;
  82. }
  83. }
  84. public void Rebuild()
  85. {
  86. needRebuildAtlas = false;
  87. bool succ = false;
  88. for (int i = 0; i < textureSize.Length; ++i)
  89. {
  90. if (atlas != null && textureSize[i] < atlas.width)
  91. {
  92. continue;
  93. }
  94. succ = Pack(textureSize[i]);
  95. if (succ)
  96. {
  97. break;
  98. }
  99. }
  100. if (!succ)
  101. {
  102. var it = sprites.GetEnumerator();
  103. it.MoveNext();
  104. Debug.LogError("Dynamic Combine Atlas Failed, maybe too many pictures of atlas tag:\"" + it.Current.autoAtlasTag + "\"");
  105. }
  106. }
  107. private bool Pack(int size)
  108. {
  109. int columnPosition = 0;
  110. int x = 0;
  111. int y = 0;
  112. int spacing = padding;
  113. Vector2 atlasSize = Vector2.zero;
  114. Dictionary<int, AtlasInfo.UVDetail> tmp = new Dictionary<int, AtlasInfo.UVDetail>();
  115. var iter = textures.GetEnumerator();
  116. while (iter.MoveNext())
  117. {
  118. int width = iter.Current.Value.width;
  119. int height = iter.Current.Value.height;
  120. AtlasInfo.UVDetail uv = new AtlasInfo.UVDetail();
  121. uv.rotate = false;
  122. tmp.Add(iter.Current.Key, uv);
  123. if (y + height + spacing <= size && x + width + spacing <= size)
  124. {
  125. uv.x = x;
  126. uv.y = y;
  127. uv.width = width;
  128. uv.height = height;
  129. y += height + spacing;
  130. if (columnPosition < x + width + spacing)
  131. columnPosition = x + width + spacing;
  132. }
  133. else if (columnPosition + width <= size && height <= size)
  134. {
  135. x = columnPosition;
  136. uv.x = x;
  137. uv.y = 0;
  138. uv.width = width;
  139. uv.height = height;
  140. y = height + spacing;
  141. columnPosition = x + width + spacing;
  142. }
  143. else
  144. {
  145. return false;
  146. }
  147. }
  148. TextureFormat format = TextureFormat.ARGB32;
  149. if (needCompress)
  150. {
  151. #if UNITY_ANDROID && !UNITY_EDITOR
  152. format = TextureFormat.ARGB32; //format = TextureFormat.ETC_RGB4;
  153. #elif UNITY_IOS && !UNITY_EDITOR
  154. format = TextureFormat.ARGB32; //format = TextureFormat.PVRTC_RGBA4;
  155. #else
  156. format = TextureFormat.ARGB32;
  157. #endif
  158. }
  159. Texture2D newTex = new Texture2D(size, size, format, false);
  160. Color[] clearColor = new Color[newTex.width * newTex.height]; //默认全是0
  161. newTex.SetPixels(clearColor);
  162. newTex.name = "Auto_UI3D_Atlas_" + size + "_" + counter + "_format" + format.ToString();
  163. counter++;
  164. iter = textures.GetEnumerator();
  165. while (iter.MoveNext())
  166. {
  167. Texture2D tex = null;
  168. if (!waitForCombineTextures.TryGetValue(iter.Current.Key, out tex))
  169. {
  170. tex = this.atlas;
  171. }
  172. var uv = iter.Current.Value;
  173. var uvNew = tmp[iter.Current.Key];
  174. Color[] orig = tex.GetPixels(uv.x, uv.y, uv.width, uv.height);
  175. newTex.SetPixels(uvNew.x, uvNew.y, uv.width, uv.height, orig);
  176. newTex.Apply(false, false);
  177. uvNew.uvTL = new Vector2((float)uvNew.x / newTex.width, (float)(uvNew.y + uvNew.height) / newTex.height);
  178. uvNew.uvTR = new Vector2((float)(uvNew.x + uvNew.width) / newTex.width, (float)(uvNew.y + uvNew.height) / newTex.height);
  179. uvNew.uvBL = new Vector2((float)uvNew.x / newTex.width, (float)(uvNew.y) / newTex.height);
  180. uvNew.uvBR = new Vector2((float)(uvNew.x + uvNew.width) / newTex.width, (float)(uvNew.y) / newTex.height);
  181. }
  182. textures = tmp;
  183. Object.Destroy(atlas);
  184. atlas = newTex;
  185. //Shader shader = CResourceManager.GetInstance().GetResource("Shaders/UI/UI3D.shader", typeof(Shader), enResourceType.BattleScene, true, true).m_content as Shader;
  186. //todo bhy
  187. Shader shader = Shader.Find("UI/UI3D");
  188. mat = new Material(shader);
  189. mat.SetTexture("_MainTex", atlas);
  190. var iterSprites = sprites.GetEnumerator();
  191. while (iterSprites.MoveNext())
  192. {
  193. iterSprites.Current.SetMaterial(mat);
  194. iterSprites.Current.SetAutoAtlas(atlas, textures[iterSprites.Current.m_textureGUID]);
  195. }
  196. var iterWaitForRelease = waitForCombineTextures.GetEnumerator();
  197. waitForCombineTextures.Clear();
  198. //todo bhy
  199. //CResourceManager.GetInstance().UnloadUnusedAssets();
  200. return true;
  201. }
  202. }
  203. public void Reset()
  204. {
  205. m_childSprites.Clear();
  206. m_childText.Clear();
  207. #if UNITY_EDITOR
  208. var iter = m_atlas.GetEnumerator();
  209. while (iter.MoveNext())
  210. {
  211. if (iter.Current.Value.atlas != null)
  212. {
  213. Debug.LogError("还有3D UI自动生成的altas没有清理干净!");
  214. }
  215. }
  216. #endif
  217. m_atlas.Clear();
  218. //todo bhy
  219. //CResourceManager.GetInstance().UnloadUnusedAssets();
  220. }
  221. public void Update(Transform root)
  222. {
  223. if (m_needRefreshLayout)
  224. {
  225. _DoRefreshLayout(root);
  226. m_needRefreshLayout = false;
  227. }
  228. if (m_needRebuildAtlas)
  229. {
  230. _DoRebuildAtlas();
  231. m_needRebuildAtlas = true;
  232. }
  233. }
  234. public void registerAutoAtlas(Sprite3D sprite)
  235. {
  236. if (sprite.texture == null)
  237. {
  238. return;
  239. }
  240. #if UNITY_EDITOR
  241. //非运行时也不做任何合并工作
  242. if (string.IsNullOrEmpty(sprite.autoAtlasTag) || !UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  243. #else
  244. if (string.IsNullOrEmpty(sprite.autoAtlasTag))
  245. #endif
  246. {
  247. //不打图集的直接就生成uv了
  248. AtlasInfo.UVDetail uv = new AtlasInfo.UVDetail();
  249. uv.uvBL = new Vector2(0, 0);
  250. uv.uvTL = new Vector2(0, 1);
  251. uv.uvBR = new Vector2(1, 0);
  252. uv.uvTR = new Vector2(1, 1);
  253. uv.rotate = false;
  254. uv.x = 0;
  255. uv.y = 0;
  256. uv.width = sprite.texture.width;
  257. uv.height = sprite.texture.height;
  258. sprite.SetUV(uv);
  259. return;
  260. }
  261. AutoAtlasInfo info = null;
  262. if (!m_atlas.TryGetValue(sprite.autoAtlasTag, out info))
  263. {
  264. info = new AutoAtlasInfo();
  265. m_atlas.Add(sprite.autoAtlasTag, info);
  266. }
  267. info.Register(sprite);
  268. m_needRebuildAtlas = true;
  269. }
  270. public void registerSprite3D(Sprite3D sprite)
  271. {
  272. if (!m_childSprites.ContainsKey(sprite.transform.GetInstanceID()))
  273. {
  274. m_childSprites.Add(sprite.transform.GetInstanceID(), sprite);
  275. }
  276. }
  277. public void unregisterSprite3d(Sprite3D sprite)
  278. {
  279. m_childSprites.Remove(sprite.transform.GetInstanceID());
  280. }
  281. public void unregisterAutoAtlas(Sprite3D sprite)
  282. {
  283. if (sprite.texture == null || string.IsNullOrEmpty(sprite.autoAtlasTag))
  284. {
  285. return;
  286. }
  287. AutoAtlasInfo info = null;
  288. if (!m_atlas.TryGetValue(sprite.autoAtlasTag, out info))
  289. {
  290. return;
  291. }
  292. info.Unregister(sprite);
  293. }
  294. private void _DoRebuildAtlas()
  295. {
  296. var iter = m_atlas.GetEnumerator();
  297. while (iter.MoveNext())
  298. {
  299. AutoAtlasInfo info = iter.Current.Value;
  300. if (!info.needRebuildAtlas)
  301. {
  302. continue;
  303. }
  304. info.Rebuild();
  305. }
  306. }
  307. private void RefreshHierachy(Transform root)
  308. {
  309. if (root == null) return;
  310. if (!root.gameObject.activeSelf)
  311. {
  312. return;
  313. }
  314. for (int i = root.childCount - 1; i >= 0; --i)
  315. {
  316. RefreshHierachy(root.GetChild(i));
  317. }
  318. Sprite3D sprite = null;
  319. if (m_childSprites.TryGetValue(root.GetInstanceID(), out sprite))
  320. {
  321. if (null != sprite)
  322. {
  323. ++m_depth;
  324. #if UNITY_EDITOR
  325. if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  326. {
  327. sprite.PrepareMesh();
  328. registerAutoAtlas(sprite);
  329. sprite.GenerateMesh();
  330. sprite.RefreshAtlasMaterial();
  331. sprite.RefreshAutoAtlasMaterial();
  332. }
  333. #endif
  334. sprite.depth = m_depth;
  335. }
  336. }
  337. else
  338. {
  339. #if UNITY_EDITOR
  340. if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  341. {
  342. //Debug.LogError("GameObject \"" + root.name + "\" with Sprite3D is not registered in Canvas3D!");
  343. }
  344. #endif
  345. sprite = root.GetComponent<Sprite3D>();
  346. m_childSprites.Add(root.GetInstanceID(), sprite);
  347. if (null != sprite)
  348. {
  349. ++m_depth;
  350. #if UNITY_EDITOR
  351. if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  352. {
  353. #if UNITY_EDITOR
  354. if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  355. {
  356. sprite.PrepareMesh();
  357. registerAutoAtlas(sprite);
  358. sprite.GenerateMesh();
  359. sprite.RefreshAtlasMaterial();
  360. sprite.RefreshAutoAtlasMaterial();
  361. }
  362. #endif
  363. }
  364. #endif
  365. sprite.depth = m_depth;
  366. }
  367. }
  368. TextMesh text = null;
  369. //针对文本的排序
  370. if (sprite == null)
  371. {
  372. if (m_childText.TryGetValue(root.GetInstanceID(), out text))
  373. {
  374. if (null != text)
  375. {
  376. ++m_depth;
  377. text.offsetZ = (float)m_depth / 10;
  378. }
  379. }
  380. else
  381. {
  382. text = root.GetComponent<TextMesh>();
  383. m_childText.Add(root.GetInstanceID(), text);
  384. if (null != text)
  385. {
  386. ++m_depth;
  387. text.offsetZ = (float)m_depth / 10;
  388. }
  389. }
  390. }
  391. }
  392. public void RebuildAtlasImmediately()
  393. {
  394. _DoRebuildAtlas();
  395. }
  396. public void RefreshLayout(Transform root = null)
  397. {
  398. m_needRefreshLayout = true;
  399. #if UNITY_EDITOR
  400. if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  401. {
  402. _DoRefreshLayout(root);
  403. }
  404. #endif
  405. }
  406. private void _DoRefreshLayout(Transform root)
  407. {
  408. m_depth = 0;
  409. RefreshHierachy(root);
  410. }
  411. }