GraphicData.cs 23 KB

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