GraphicData.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /**
  2. * 魔力宝贝图档解析脚本 - CGTool
  3. *
  4. * @Author HonorLee (dev@honorlee.me)
  5. * @Version 1.0 (2023-11-20)
  6. * @License GPL-3.0
  7. *
  8. * GraphicData.cs 图档解析类
  9. */
  10. using System;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. using System.IO;
  14. using System.Linq;
  15. using Unity.Burst;
  16. using Unity.Collections;
  17. using Unity.Jobs;
  18. using UnityEngine;
  19. namespace CrossgateToolkit
  20. {
  21. //图档数据详情
  22. public class GraphicDetail
  23. {
  24. //索引
  25. public uint Index;
  26. //编号
  27. public uint Serial;
  28. //图档宽度
  29. public uint Width;
  30. //图档高度
  31. public uint Height;
  32. //图档偏移X
  33. public int OffsetX;
  34. //图档偏移Y
  35. public int OffsetY;
  36. //Palet调色板Index
  37. public int Palet;
  38. //图档Sprite
  39. public Sprite Sprite;
  40. //图档主色调,用于小地图绘制
  41. public Color32 PrimaryColor;
  42. }
  43. // 图档数据
  44. public static class GraphicData
  45. {
  46. // // 图档缓存 Serial -> Palet -> GraphicDetail
  47. // public static Dictionary<uint,Dictionary<int,GraphicDetail>> _cache = new Dictionary<uint, Dictionary<int, GraphicDetail>>();
  48. //
  49. // // 图档索引缓存 Index -> Palet -> GraphicDetail
  50. // public static Dictionary<uint,Dictionary<int,GraphicDetail>> _indexCache = new Dictionary<uint, Dictionary<int, GraphicDetail>>();
  51. public static Dictionary<GraphicInfoData,Dictionary<int,GraphicDetail>> _cache = new Dictionary<GraphicInfoData, Dictionary<int, GraphicDetail>>();
  52. // 获取图档
  53. public static GraphicDetail GetGraphicDetail(GraphicInfoData graphicInfoData, int palet = 0)
  54. {
  55. GraphicDetail graphicDetail = null;
  56. if (_cache.ContainsKey(graphicInfoData))
  57. {
  58. if (_cache[graphicInfoData].ContainsKey(palet))
  59. {
  60. graphicDetail = _cache[graphicInfoData][palet];
  61. }
  62. else
  63. {
  64. graphicDetail = _loadGraphicDetail(graphicInfoData, palet);
  65. _cache[graphicInfoData].Add(palet, graphicDetail);
  66. }
  67. }
  68. else
  69. {
  70. graphicDetail = _loadGraphicDetail(graphicInfoData, palet);
  71. _cache.Add(graphicInfoData, new Dictionary<int, GraphicDetail>());
  72. _cache[graphicInfoData].Add(palet, graphicDetail);
  73. }
  74. return graphicDetail;
  75. }
  76. // 解析图档
  77. private static GraphicDetail _loadGraphicDetail(GraphicInfoData graphicInfoData,int palet = 0)
  78. {
  79. GraphicDetail graphicDetail = new GraphicDetail();
  80. //获取图像数据
  81. List<Color32> pixels = UnpackGraphic(graphicInfoData, palet);
  82. graphicDetail.PrimaryColor = pixels.Last();
  83. pixels.RemoveAt(pixels.Count - 1);
  84. //直接通过Texture2D做偏移,并转为Sprite的偏移量
  85. Vector2 offset = new Vector2(0f, 1f);
  86. offset.x += -(graphicInfoData.OffsetX * 1f) / graphicInfoData.Width;
  87. offset.y -= (-graphicInfoData.OffsetY * 1f) / graphicInfoData.Height;
  88. //创建Texture2D对象
  89. Texture2D texture2D;
  90. Sprite sprite;
  91. // RGBA4444 减少内存占用
  92. texture2D = new Texture2D((int) graphicInfoData.Width, (int) graphicInfoData.Height,
  93. TextureFormat.RGBA4444, false, false);
  94. // 固定点过滤
  95. texture2D.filterMode = FilterMode.Point;
  96. texture2D.SetPixels32(pixels.ToArray());
  97. // texture2D.LoadRawTextureData(rawTextureData);
  98. texture2D.Apply();
  99. sprite = Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), offset, 1,1,SpriteMeshType.FullRect);
  100. //写入数据
  101. graphicDetail.Index = graphicInfoData.Index;
  102. graphicDetail.Serial = graphicInfoData.Serial;
  103. graphicDetail.Width = graphicInfoData.Width;
  104. graphicDetail.Height = graphicInfoData.Height;
  105. graphicDetail.OffsetX = graphicInfoData.OffsetX;
  106. graphicDetail.OffsetY = graphicInfoData.OffsetY;
  107. graphicDetail.Palet = palet;
  108. graphicDetail.Sprite = sprite;
  109. return graphicDetail;
  110. }
  111. #region 地图合批
  112. // 合批数据
  113. private class BatchData
  114. {
  115. public int BatchOffsetX;
  116. public int BatchOffsetY;
  117. public GraphicDetail GraphicDetail;
  118. }
  119. // 图档合批
  120. private class TextureData
  121. {
  122. public int MaxHeight;
  123. public int MaxWidth;
  124. public List<BatchData> BatchDatas = new List<BatchData>();
  125. public List<GraphicInfoData> GraphicInfoDatas = new List<GraphicInfoData>();
  126. }
  127. //预备地图缓存
  128. [Obsolete("该方法已废弃,请使用BakeGraphics方法")]
  129. public static Dictionary<uint, GraphicDetail> BakeAsGround(List<GraphicInfoData> groundInfos,int palet=0)
  130. {
  131. Dictionary<uint, GraphicDetail> graphicDataDic = new Dictionary<uint, GraphicDetail>();
  132. // _mapSpriteMap[MapID].Add(PaletIndex, graphicDataDic);
  133. List<BatchData> batchDatas = new List<BatchData>();
  134. Texture2D texture2D = null;
  135. for (var i = 0; i < groundInfos.Count; i++)
  136. {
  137. //每1344个图像合并一次,即不超过2048*2048尺寸
  138. if (i % 1344 == 0)
  139. {
  140. //合并
  141. if (i != 0) Combine(texture2D, batchDatas);
  142. //清空
  143. batchDatas.Clear();
  144. int height = 2048;
  145. if (i + 1344 > groundInfos.Count-1)
  146. {
  147. height = Mathf.CeilToInt((groundInfos.Count - i) / 32f) * 48;
  148. }
  149. texture2D = new Texture2D(2048, height, TextureFormat.RGBA4444, false, true);
  150. texture2D.filterMode = FilterMode.Point;
  151. //默认填充全透明
  152. Color32[] colors = Enumerable.Repeat(new Color32(0, 0, 0, 0), 2048 * height).ToArray();
  153. texture2D.SetPixels32(colors);
  154. }
  155. GraphicInfoData graphicInfoData = groundInfos[i];
  156. GraphicDetail graphicData = GetGraphicDetail(graphicInfoData, palet);
  157. int x = i % 32 * 64;
  158. int y = i / 32 * 48;
  159. if(graphicData!=null && graphicData.Sprite!=null)
  160. {
  161. Color32[] pixels = graphicData.Sprite.texture.GetPixels32();
  162. texture2D.SetPixels32(x, y, (int) graphicInfoData.Width, (int) graphicInfoData.Height,
  163. pixels.ToArray());
  164. }
  165. BatchData batchData = new BatchData();
  166. batchData.BatchOffsetX = x;
  167. batchData.BatchOffsetY = y;
  168. batchData.GraphicDetail = graphicData;
  169. batchDatas.Add(batchData);
  170. }
  171. //最后一次合并
  172. if (batchDatas.Count > 0)
  173. {
  174. Combine(texture2D, batchDatas);
  175. batchDatas.Clear();
  176. }
  177. void Combine(Texture2D texture2D,List<BatchData> batchDatas)
  178. {
  179. texture2D.Apply();
  180. for (var i = 0; i < batchDatas.Count; i++)
  181. {
  182. GraphicDetail graphicDataPiece = batchDatas[i].GraphicDetail;
  183. //直接通过Texture2D做偏移,并转为Sprite的偏移量
  184. Vector2 offset = new Vector2(0f, 1f);
  185. offset.x += -(graphicDataPiece.OffsetX * 1f) / graphicDataPiece.Width;
  186. offset.y -= (-graphicDataPiece.OffsetY * 1f) / graphicDataPiece.Height;
  187. int X = i % 32 * 64;
  188. int Y = i / 32 * 48;
  189. Sprite sprite = Sprite.Create(texture2D,
  190. new Rect(X, Y, (int)graphicDataPiece.Width, (int)graphicDataPiece.Height), offset, 1, 1,
  191. SpriteMeshType.FullRect);
  192. GraphicDetail graphicData = new GraphicDetail()
  193. {
  194. Index = graphicDataPiece.Index,
  195. Serial = graphicDataPiece.Serial,
  196. Width = graphicDataPiece.Width,
  197. Height = graphicDataPiece.Height,
  198. OffsetX = graphicDataPiece.OffsetX,
  199. OffsetY = graphicDataPiece.OffsetY,
  200. Palet = graphicDataPiece.Palet,
  201. Sprite = sprite,
  202. PrimaryColor = graphicDataPiece.PrimaryColor
  203. };
  204. graphicDataDic.Add(graphicData.Serial, graphicData);
  205. }
  206. }
  207. return graphicDataDic;
  208. }
  209. /// <summary>
  210. /// 合批图档
  211. /// 通过指定图档序列,对图档进行合批处理,并返回合批后的图档数据
  212. /// </summary>
  213. /// <param name="graphicInfoDatas">图档索引数据序列</param>
  214. /// <param name="palet">调色板序号</param>
  215. /// <param name="maxTextureSize">单个Texture最大尺寸,地面数据建议2048,物件数据建议4096</param>
  216. /// <param name="padding">图档间隔,可以有效避免图档渲染时出现多余的黑边或像素黏连</param>
  217. /// <returns>合批后的图档数据,Key(unit)为图档数据编号,Value为图档数据</returns>
  218. public static Dictionary<uint, GraphicDetail> BakeGraphics(List<GraphicInfoData> graphicInfoDatas,int palet = 0, int maxTextureSize = 2048,int padding = 0)
  219. {
  220. // 单个Texture最大尺寸
  221. int maxWidth = maxTextureSize;
  222. int maxHeight = maxTextureSize;
  223. List<TextureData> textureDatas = new List<TextureData>();
  224. Dictionary<uint, GraphicDetail> graphicDataDic = new Dictionary<uint, GraphicDetail>();
  225. // 根据objectInfos的内,GraphicInfoData的Width,Height进行排序,优先排序Width,使图档从小到大排列
  226. graphicInfoDatas = graphicInfoDatas.OrderBy(obj => obj.Width).ThenBy(obj => obj.Height).ToList();
  227. int offsetX = 0; // X轴偏移量
  228. int offsetY = 0; // Y轴偏移量
  229. int maxRowHeight = 0; // 当前行最大高度
  230. TextureData textureData = new TextureData();
  231. for (var i = 0; i < graphicInfoDatas.Count; i++)
  232. {
  233. GraphicInfoData graphicInfoData = graphicInfoDatas[i];
  234. // 如果宽度超过4096,则换行
  235. if((graphicInfoData.Width + offsetX) > maxWidth)
  236. {
  237. offsetX = 0;
  238. offsetY = offsetY + maxRowHeight + padding;
  239. maxRowHeight = 0;
  240. }
  241. // 如果高度超过2048,则生成新的Texture2D
  242. if ((graphicInfoData.Height + offsetY) > maxHeight)
  243. {
  244. offsetX = 0;
  245. offsetY = 0;
  246. maxRowHeight = 0;
  247. textureDatas.Add(textureData);
  248. textureData = new TextureData();
  249. }
  250. BatchData batchData = new BatchData();
  251. batchData.BatchOffsetX = offsetX;
  252. batchData.BatchOffsetY = offsetY;
  253. batchData.GraphicDetail = GetGraphicDetail(graphicInfoData, palet);
  254. // graphicDatas.Add(graphicData);
  255. textureData.BatchDatas.Add(batchData);
  256. textureData.GraphicInfoDatas.Add(graphicInfoData);
  257. maxRowHeight = Mathf.Max(maxRowHeight, (int) graphicInfoData.Height);
  258. textureData.MaxHeight = Mathf.Max(textureData.MaxHeight, offsetY + maxRowHeight);
  259. textureData.MaxWidth = Mathf.Max(textureData.MaxWidth, offsetX + (int) graphicInfoData.Width);
  260. offsetX += (int) graphicInfoData.Width + padding;
  261. }
  262. //最后一次合并
  263. if (textureData.BatchDatas.Count > 0) textureDatas.Add(textureData);
  264. //合并Texture2D
  265. for (var i = 0; i < textureDatas.Count; i++)
  266. {
  267. TextureData textureDataPiece = textureDatas[i];
  268. // Debug.Log($"合并第{i}个Texture2D,最大高度:{textureDataPiece.MaxHeight},图像数量:{textureDataPiece.GraphicDatas.Count}");
  269. Color32[] colors = Enumerable.Repeat(new Color32(0,0,0,0), textureDataPiece.MaxWidth * textureDataPiece.MaxHeight).ToArray();
  270. Texture2D texture2DPiece = new Texture2D(textureDataPiece.MaxWidth, textureDataPiece.MaxHeight, TextureFormat.RGBA4444, false, false);
  271. texture2DPiece.filterMode = FilterMode.Point;
  272. texture2DPiece.SetPixels32(colors);
  273. for (var n = 0; n < textureDataPiece.BatchDatas.Count; n++)
  274. {
  275. BatchData batchData = textureDataPiece.BatchDatas[n];
  276. GraphicInfoData graphicInfoData = textureDataPiece.GraphicInfoDatas[n];
  277. if (batchData.GraphicDetail!=null)
  278. {
  279. Color32[] pixels = batchData.GraphicDetail.Sprite.texture.GetPixels32();
  280. texture2DPiece.SetPixels32(batchData.BatchOffsetX, batchData.BatchOffsetY, (int) graphicInfoData.Width, (int) graphicInfoData.Height,
  281. pixels.ToArray());
  282. }
  283. }
  284. texture2DPiece.Apply();
  285. Combine(texture2DPiece, textureDataPiece.BatchDatas);
  286. }
  287. void Combine(Texture2D texture2D,List<BatchData> batchDatas)
  288. {
  289. for (var i = 0; i < batchDatas.Count; i++)
  290. {
  291. BatchData batchData = batchDatas[i];
  292. //直接通过Texture2D做偏移,并转为Sprite的偏移量
  293. Vector2 offset = new Vector2(0f, 1f);
  294. offset.x += -(batchData.GraphicDetail.OffsetX * 1f) / batchData.GraphicDetail.Width;
  295. offset.y -= (-batchData.GraphicDetail.OffsetY * 1f) / batchData.GraphicDetail.Height;
  296. Sprite sprite = Sprite.Create(texture2D, new Rect(batchData.BatchOffsetX, batchData.BatchOffsetY, (int)batchData.GraphicDetail.Width, (int)batchData.GraphicDetail.Height),offset, 1, 1, SpriteMeshType.FullRect);
  297. GraphicDetail graphicDetail = new GraphicDetail()
  298. {
  299. Index = batchData.GraphicDetail.Index,
  300. Serial = batchData.GraphicDetail.Serial,
  301. Width = batchData.GraphicDetail.Width,
  302. Height = batchData.GraphicDetail.Height,
  303. OffsetX = batchData.GraphicDetail.OffsetX,
  304. OffsetY = batchData.GraphicDetail.OffsetY,
  305. Palet = batchData.GraphicDetail.Palet,
  306. Sprite = sprite,
  307. PrimaryColor = batchData.GraphicDetail.PrimaryColor
  308. };
  309. // graphicDataPiece.Sprite = sprite;
  310. graphicDataDic.Add(graphicDetail.Serial, graphicDetail);
  311. }
  312. }
  313. return graphicDataDic;
  314. }
  315. #endregion
  316. //解压图像数据
  317. private static List<Color32> UnpackGraphic(GraphicInfoData graphicInfoData,int PaletIndex=0){
  318. List<Color32> pixels = new List<Color32>();
  319. //获取调色板
  320. List<Color32> palet = Palet.GetPalet(PaletIndex);
  321. //调整流指针
  322. BinaryReader fileReader = graphicInfoData.GraphicReader;
  323. fileReader.BaseStream.Position = graphicInfoData.Addr;
  324. //读入目标字节集
  325. byte[] Content = fileReader.ReadBytes((int) graphicInfoData.Length);
  326. //读取缓存字节集
  327. BinaryReader contentReader = new BinaryReader(new MemoryStream(Content));
  328. //16字节头信息
  329. byte[] HEAD = contentReader.ReadBytes(2);
  330. int Version = contentReader.ReadByte();
  331. int Unknow = contentReader.ReadByte();
  332. uint Width = contentReader.ReadUInt32();
  333. uint Height = contentReader.ReadUInt32();
  334. uint Length = contentReader.ReadUInt32();
  335. //数据长度
  336. uint contentLen = Length - 16;
  337. int pixelLen = (int) (graphicInfoData.Width * graphicInfoData.Height);
  338. int[] paletIndex;
  339. if (graphicInfoData.UnpackedPaletIndex == null)
  340. {
  341. //解压数据
  342. byte[] contentBytes = contentReader.ReadBytes((int) contentLen);
  343. NativeArray<byte> bytes = new NativeArray<byte>((int) contentBytes.Length, Allocator.TempJob);
  344. bytes.CopyFrom(contentBytes);
  345. // Debug.Log(contentBytes.Length + " " + bytes.Length);
  346. NativeArray<int> colorIndexs =
  347. new NativeArray<int>(pixelLen, Allocator.TempJob);
  348. DecompressJob decompressJob = new DecompressJob()
  349. {
  350. bytes = bytes,
  351. compressd = Version != 0,
  352. colorIndexs = colorIndexs
  353. };
  354. // decompressJob.Execute();
  355. decompressJob.Schedule().Complete();
  356. bytes.Dispose();
  357. paletIndex = colorIndexs.ToArray();
  358. graphicInfoData.UnpackedPaletIndex = paletIndex;
  359. colorIndexs.Dispose();
  360. }
  361. else
  362. {
  363. paletIndex = graphicInfoData.UnpackedPaletIndex;
  364. }
  365. //释放连接
  366. contentReader.Dispose();
  367. contentReader.Close();
  368. //主色调色值
  369. int r = 0;
  370. int g = 0;
  371. int b = 0;
  372. foreach (int index in paletIndex)
  373. {
  374. // Debug.Log(index);
  375. Color32 color32 = palet[index];
  376. pixels.Add(color32);
  377. r += color32.r;
  378. g += color32.g;
  379. b += color32.b;
  380. }
  381. //主色调计算及提亮
  382. r = r / pixels.Count * 3;
  383. g = g / pixels.Count * 3;
  384. b = b / pixels.Count * 3;
  385. if (r > 255) r = 255;
  386. if (g > 255) g = 255;
  387. if (b > 255) b = 255;
  388. int len = (int) (graphicInfoData.Width * graphicInfoData.Height);
  389. if (pixels.Count != len)
  390. {
  391. if (pixels.Count > len)
  392. {
  393. pixels = pixels.GetRange(0, len);
  394. }
  395. else
  396. {
  397. Color32[] temc = new Color32[len - pixels.Count];
  398. ArrayList.Repeat(Color.clear, len - pixels.Count).CopyTo(temc);
  399. pixels.AddRange(temc);
  400. }
  401. }
  402. //主色调加入最后
  403. pixels.Add(new Color32((byte) r, (byte) g, (byte) b, 255));
  404. return pixels;
  405. }
  406. }
  407. //解压缩交给IJob处理
  408. [BurstCompile]
  409. public struct DecompressJob : IJob
  410. {
  411. [ReadOnly]
  412. public NativeArray<byte> bytes;
  413. public bool compressd;
  414. public NativeArray<int> colorIndexs;
  415. private int _maxIndex;
  416. private int _index;
  417. private int _colorIndex;
  418. private int NextByte()
  419. {
  420. _index++;
  421. if (_index > _maxIndex) return -1;
  422. return bytes[_index];
  423. }
  424. private void AddColorIndex(int index)
  425. {
  426. colorIndexs[_colorIndex] = index;
  427. _colorIndex++;
  428. }
  429. [BurstCompile]
  430. public void Execute()
  431. {
  432. _maxIndex = bytes.Length - 1;
  433. _index = -1;
  434. _colorIndex = 0;
  435. if (!compressd)
  436. {
  437. while (_index<=_maxIndex)
  438. {
  439. int index = NextByte();
  440. if(index==-1) break;
  441. AddColorIndex(index);
  442. }
  443. }
  444. else
  445. //压缩型数据解压
  446. {
  447. // int count = 0;
  448. while (_index<=_maxIndex)
  449. {
  450. // count++;
  451. int head = NextByte();
  452. if(head==-1) break;
  453. int repeat = 0;
  454. if (head < 0x10)
  455. {
  456. repeat = head;
  457. for (var i = 0; i < repeat; i++)
  458. {
  459. AddColorIndex(NextByte());
  460. }
  461. }
  462. else if (head < 0x20)
  463. {
  464. repeat = head % 0x10 * 0x100 + NextByte();
  465. for (var i = 0; i < repeat; i++)
  466. {
  467. AddColorIndex(NextByte());
  468. }
  469. }
  470. else if (head < 0x80)
  471. {
  472. repeat = head % 0x20 * 0x10000 + NextByte() * 0x100 + NextByte();
  473. for (var i = 0; i < repeat; i++)
  474. {
  475. AddColorIndex(NextByte());
  476. }
  477. }
  478. else if (head < 0x90)
  479. {
  480. repeat = head % 0x80;
  481. int index = NextByte();
  482. for (var i = 0; i < repeat; i++)
  483. {
  484. AddColorIndex(index);
  485. }
  486. }
  487. else if (head < 0xa0)
  488. {
  489. int index = NextByte();
  490. repeat = head % 0x90 * 0x100 + NextByte();
  491. for (var i = 0; i < repeat; i++)
  492. {
  493. AddColorIndex(index);
  494. }
  495. }
  496. else if (head < 0xc0)
  497. {
  498. int index = NextByte();
  499. repeat = head % 0xa0 * 0x10000 + NextByte() * 0x100 + NextByte();
  500. for (var i = 0; i < repeat; i++)
  501. {
  502. AddColorIndex(index);
  503. }
  504. }
  505. else if (head < 0xd0)
  506. {
  507. repeat = head % 0xc0;
  508. for (var i = 0; i < repeat; i++)
  509. {
  510. AddColorIndex(256);
  511. }
  512. }
  513. else if (head < 0xe0)
  514. {
  515. repeat = head % 0xd0 * 0x100 + NextByte();
  516. for (var i = 0; i < repeat; i++)
  517. {
  518. AddColorIndex(256);
  519. }
  520. }
  521. else
  522. {
  523. repeat = head % 0xe0 * 0x10000 + NextByte() * 0x100 + NextByte();
  524. for (var i = 0; i < repeat; i++)
  525. {
  526. AddColorIndex(256);
  527. }
  528. }
  529. }
  530. }
  531. }
  532. }
  533. }