Magically import images and more into Gamecraft as blocks
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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

485 lines
17KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using RobocraftX.Common;
  7. using Newtonsoft.Json;
  8. using Unity.Mathematics;
  9. using UnityEngine;
  10. using GamecraftModdingAPI.Blocks;
  11. using GamecraftModdingAPI.Utility;
  12. using GamecraftModdingAPI;
  13. using Pixi.Common;
  14. namespace Pixi.Robots
  15. {
  16. public static class CubeUtility
  17. {
  18. private static Dictionary<uint, string> map = null;
  19. private static Dictionary<uint, BlockJsonInfo[]> blueprintMap = null;
  20. public static RobotStruct? ParseRobotInfo(string robotInfo)
  21. {
  22. try
  23. {
  24. return JsonConvert.DeserializeObject<RobotStruct>(robotInfo);
  25. }
  26. catch (Exception e)
  27. {
  28. Logging.MetaLog(e);
  29. return null;
  30. }
  31. }
  32. public static CubeInfo[] ParseCubes(RobotStruct robot)
  33. {
  34. return ParseCubes(robot.cubeData, robot.colourData);
  35. }
  36. public static CubeInfo[] ParseCubes(string cubeData, string colourData)
  37. {
  38. BinaryBufferReader cubes = new BinaryBufferReader(Convert.FromBase64String(cubeData), 0);
  39. BinaryBufferReader colours = new BinaryBufferReader(Convert.FromBase64String(colourData), 0);
  40. uint cubeCount = cubes.ReadUint();
  41. uint colourCount = colours.ReadUint();
  42. if (cubeCount != colourCount)
  43. {
  44. Logging.MetaLog("Something is fucking broken");
  45. return null;
  46. }
  47. Logging.MetaLog($"Detected {cubeCount} cubes");
  48. CubeInfo[] result = new CubeInfo[cubeCount];
  49. for (int cube = 0; cube < cubeCount; cube++)
  50. {
  51. result[cube] = TranslateSpacialEnumerations(
  52. cubes.ReadUint(),
  53. cubes.ReadByte(),
  54. cubes.ReadByte(),
  55. cubes.ReadByte(),
  56. cubes.ReadByte(),
  57. colours.ReadByte(),
  58. colours.ReadByte(),
  59. colours.ReadByte(),
  60. colours.ReadByte()
  61. );
  62. }
  63. return result;
  64. }
  65. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  66. public static CubeInfo TranslateSpacialEnumerations(uint cubeId, byte x, byte y, byte z, byte rotation, byte colour, byte colour_x, byte colour_y, byte colour_z)
  67. {
  68. if (x != colour_x || z != colour_z || y != colour_y) return default;
  69. CubeInfo result = new CubeInfo { visible = true, cubeId = cubeId };
  70. TranslateBlockColour(colour, ref result);
  71. TranslateBlockPosition(x, y, z, ref result);
  72. TranslateBlockRotation(rotation, ref result);
  73. TranslateBlockId(cubeId, ref result);
  74. #if DEBUG
  75. Logging.MetaLog($"Cube {cubeId} ({x}, {y}, {z}) rot:{rotation} decoded as {result.block} {result.position} rot: {result.rotation} color: {result.color} {result.darkness}");
  76. #endif
  77. return result;
  78. }
  79. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  80. private static void TranslateBlockRotation(byte rotation, ref CubeInfo result)
  81. {
  82. // face refers to the face of the block connected to the bottom of the current one
  83. // nvm, they're all incorrect
  84. switch (rotation)
  85. {
  86. case 0:
  87. result.rotation = new float3(0, 0, 0); // top face, forwards
  88. break;
  89. case 1:
  90. result.rotation = new float3(0, 0, 90); // left face, forwards
  91. break;
  92. case 2:
  93. result.rotation = new float3(0, 0, 180); // bottom face, forwards
  94. break;
  95. case 3:
  96. result.rotation = new float3(0, 0, -90); // front face, down
  97. break;
  98. case 4:
  99. result.rotation = new float3(0, 90, 0); // top face, right
  100. break;
  101. case 5:
  102. result.rotation = new float3(0, 90, 90); // front face, right
  103. break;
  104. case 6:
  105. result.rotation = new float3(-90, -90, 0); // right face, backwards
  106. break;
  107. case 7:
  108. result.rotation = new float3(0, 90, -90); // back face, right
  109. break;
  110. case 8:
  111. result.rotation = new float3(0, -90, 90); // back face, left
  112. break;
  113. case 9:
  114. result.rotation = new float3(0, -90, -90); // front face, left
  115. break;
  116. case 10:
  117. result.rotation = new float3(90, -90, 0); // left face, down
  118. break;
  119. case 11:
  120. result.rotation = new float3(90, 90, 0); // right face, forwards
  121. break;
  122. case 12:
  123. result.rotation = new float3(-90, 90, 0); // left face, up
  124. break;
  125. case 13:
  126. result.rotation = new float3(0, 90, 180); // bottom face, right
  127. break;
  128. case 14:
  129. result.rotation = new float3(0, 180, 0); // top face, backwards
  130. break;
  131. case 15:
  132. result.rotation = new float3(0, 180, 90); // right face, up
  133. break;
  134. case 16:
  135. result.rotation = new float3(0, 180, 180); // bottom face, backwards
  136. break;
  137. case 17:
  138. result.rotation = new float3(0, 180, -90); // left face, backwards
  139. break;
  140. case 18:
  141. result.rotation = new float3(0, -90, 0); // top face, left
  142. break;
  143. case 19:
  144. result.rotation = new float3(0, -90, 180); // bottom face, left
  145. break;
  146. case 20:
  147. result.rotation = new float3(90, 0, 0); // front face, down
  148. break;
  149. case 21:
  150. result.rotation = new float3(90, 180, 0); // back face, down
  151. break;
  152. case 22:
  153. result.rotation = new float3(-90, 0, 0); // back face, up
  154. break;
  155. case 23:
  156. result.rotation = new float3(-90, 180, 0); // front face, up
  157. break;
  158. default:
  159. #if DEBUG
  160. Logging.MetaLog($"Unknown rotation {rotation.ToString("X2")}");
  161. #endif
  162. result.rotation = float3.zero;
  163. break;
  164. }
  165. // my brain hurts after figuring out all of those rotations
  166. // I wouldn't recommend trying to redo this
  167. }
  168. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  169. private static void TranslateBlockPosition(byte x, byte y, byte z, ref CubeInfo result)
  170. {
  171. // for some reason, z is forwards in garage bays
  172. result.position = new float3(x, y, z);
  173. }
  174. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  175. private static void TranslateBlockColour(byte colour, ref CubeInfo result)
  176. {
  177. // I hope these colours are accurate, I just guessed
  178. // TODO colour accuracy (lol that won't ever happen)
  179. switch (colour)
  180. {
  181. case 0:
  182. result.color = BlockColors.White;
  183. result.darkness = 0;
  184. break;
  185. case 1:
  186. result.color = BlockColors.White;
  187. result.darkness = 5;
  188. break;
  189. case 2:
  190. result.color = BlockColors.Orange;
  191. result.darkness = 0;
  192. break;
  193. case 3:
  194. result.color = BlockColors.Blue;
  195. result.darkness = 2;
  196. break;
  197. case 4:
  198. result.color = BlockColors.White;
  199. result.darkness = 8;
  200. break;
  201. case 5:
  202. result.color = BlockColors.Red;
  203. result.darkness = 0;
  204. break;
  205. case 6:
  206. result.color = BlockColors.Yellow;
  207. result.darkness = 0;
  208. break;
  209. case 7:
  210. result.color = BlockColors.Green;
  211. result.darkness = 0;
  212. break;
  213. case 8:
  214. result.color = BlockColors.Purple;
  215. result.darkness = 0;
  216. break;
  217. case 9:
  218. result.color = BlockColors.Blue;
  219. result.darkness = 7;
  220. break;
  221. case 10:
  222. result.color = BlockColors.Purple;
  223. result.darkness = 5;
  224. break;
  225. case 11:
  226. result.color = BlockColors.Orange;
  227. result.darkness = 7;
  228. break;
  229. case 12:
  230. result.color = BlockColors.Green;
  231. result.darkness = 3;
  232. break;
  233. case 13:
  234. result.color = BlockColors.Green;
  235. result.darkness = 2;
  236. break;
  237. case 14:
  238. result.color = BlockColors.Pink;
  239. result.darkness = 3;
  240. break;
  241. case 15:
  242. result.color = BlockColors.Pink;
  243. result.darkness = 2;
  244. break;
  245. case 16:
  246. result.color = BlockColors.Red;
  247. result.darkness = 2;
  248. break;
  249. case 17:
  250. result.color = BlockColors.Orange;
  251. result.darkness = 8;
  252. break;
  253. case 18:
  254. result.color = BlockColors.Red;
  255. result.darkness = 7;
  256. break;
  257. case 19:
  258. result.color = BlockColors.Pink;
  259. result.darkness = 0;
  260. break;
  261. case 20:
  262. result.color = BlockColors.Yellow;
  263. result.darkness = 2;
  264. break;
  265. case 21:
  266. result.color = BlockColors.Green;
  267. result.darkness = 7;
  268. break;
  269. case 22:
  270. result.color = BlockColors.Green;
  271. result.darkness = 8;
  272. break;
  273. case 23:
  274. result.color = BlockColors.Blue;
  275. result.darkness = 8;
  276. break;
  277. case 24:
  278. result.color = BlockColors.Aqua;
  279. result.darkness = 7;
  280. break;
  281. case 25:
  282. result.color = BlockColors.Blue;
  283. result.darkness = 6;
  284. break;
  285. case 26:
  286. result.color = BlockColors.Aqua;
  287. result.darkness = 5;
  288. break;
  289. case 27:
  290. result.color = BlockColors.Blue;
  291. result.darkness = 4;
  292. break;
  293. case 28:
  294. result.color = BlockColors.Aqua;
  295. result.darkness = 3;
  296. break;
  297. case 29:
  298. result.color = BlockColors.Blue;
  299. result.darkness = 5;
  300. break;
  301. case 30:
  302. result.color = BlockColors.Purple;
  303. result.darkness = 3;
  304. break;
  305. case 31:
  306. result.color = BlockColors.Purple;
  307. result.darkness = 1;
  308. break;
  309. default:
  310. result.color = BlockColors.Aqua;
  311. result.darkness = 0;
  312. break;
  313. }
  314. }
  315. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  316. private static void TranslateBlockId(uint cubeId, ref CubeInfo result)
  317. {
  318. if (map == null)
  319. {
  320. StreamReader cubemap = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Pixi.cubes-id.json"));
  321. map = JsonConvert.DeserializeObject<Dictionary<uint, string>>(cubemap.ReadToEnd());
  322. }
  323. if (!map.ContainsKey(cubeId))
  324. {
  325. result.block = BlockIDs.TextBlock;
  326. result.name = "Unknown cube #" + cubeId.ToString();
  327. //result.rotation = float3.zero;
  328. #if DEBUG
  329. Logging.MetaLog($"Unknown cubeId {cubeId}");
  330. #endif
  331. }
  332. string cubeName = map[cubeId];
  333. if (cubeName.Contains("cube"))
  334. {
  335. result.block = BlockIDs.AluminiumCube;
  336. result.rotation = float3.zero;
  337. }
  338. else if (cubeName.Contains("prism") || cubeName.Contains("edge"))
  339. {
  340. if (cubeName.Contains("round"))
  341. {
  342. if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
  343. {
  344. result.block = BlockIDs.GlassRoundedSlope;
  345. } else
  346. result.block = BlockIDs.AluminiumRoundedSlope;
  347. }
  348. else
  349. {
  350. if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
  351. {
  352. result.block = BlockIDs.GlassSlope;
  353. } else
  354. result.block = BlockIDs.AluminiumSlope;
  355. }
  356. }
  357. else if (cubeName.Contains("inner"))
  358. {
  359. if (cubeName.Contains("round"))
  360. {
  361. if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
  362. {
  363. result.block = BlockIDs.GlassRoundedSlicedCube;
  364. } else
  365. result.block = BlockIDs.AluminiumRoundedSlicedCube;
  366. }
  367. else
  368. {
  369. if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
  370. {
  371. result.block = BlockIDs.GlassSlicedCube;
  372. } else
  373. result.block = BlockIDs.AluminiumSlicedCube;
  374. }
  375. }
  376. else if (cubeName.Contains("tetra") || cubeName.Contains("corner"))
  377. {
  378. if (cubeName.Contains("round"))
  379. {
  380. if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
  381. {
  382. result.block = BlockIDs.GlassRoundedCorner;
  383. } else
  384. result.block = BlockIDs.AluminiumRoundedCorner;
  385. }
  386. else
  387. {
  388. if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
  389. {
  390. result.block = BlockIDs.GlassCorner;
  391. } else
  392. result.block = BlockIDs.AluminiumCorner;
  393. }
  394. }
  395. else if (cubeName.Contains("pyramid"))
  396. {
  397. result.block = BlockIDs.AluminiumPyramidSegment;
  398. }
  399. else if (cubeName.Contains("cone"))
  400. {
  401. result.block = BlockIDs.AluminiumConeSegment;
  402. }
  403. else
  404. {
  405. result.block = BlockIDs.TextBlock;
  406. result.name = cubeName;
  407. }
  408. }
  409. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  410. public static Block[] BuildBlueprintOrTextBlock(CubeInfo cube, float3 actualPosition, int scale = 3)
  411. {
  412. // actualPosition is the middle of the cube
  413. if (blueprintMap == null) LoadBlueprintMap();
  414. if (!blueprintMap.ContainsKey(cube.cubeId) || scale != 3)
  415. {
  416. #if DEBUG
  417. Logging.LogWarning($"Missing blueprint for {cube.name} (id:{cube.cubeId}), substituting {cube.block}");
  418. #endif
  419. return new Block[] { Block.PlaceNew(cube.block, actualPosition, cube.rotation, cube.color, cube.darkness, scale: cube.scale) };
  420. }
  421. #if DEBUG
  422. Logging.MetaLog($"Found blueprint for {cube.name} (id:{cube.cubeId})");
  423. #endif
  424. Quaternion cubeQuaternion = Quaternion.Euler(cube.rotation);
  425. BlockJsonInfo[] blueprint = blueprintMap[cube.cubeId];
  426. float3 correctionVec = new float3((float)(0), (float)(RobotCommands.blockSize), (float)(0));
  427. Block[] placedBlocks = new Block[blueprint.Length];
  428. for (int i = 0; i < blueprint.Length; i++)
  429. {
  430. BlockColor blueprintBlockColor = ColorSpaceUtility.QuantizeToBlockColor(blueprint[i].color);
  431. BlockColors blockColor = blueprintBlockColor.Color == BlockColors.White && blueprintBlockColor.Darkness == 0 ? cube.color : blueprintBlockColor.Color;
  432. byte blockDarkness = blueprintBlockColor.Color == BlockColors.White && blueprintBlockColor.Darkness == 0 ? cube.darkness : blueprintBlockColor.Darkness;
  433. float3 bluePos = new float3(blueprint[i].position[0], blueprint[i].position[1], blueprint[i].position[2]);
  434. float3 blueScale = new float3(blueprint[i].scale[0], blueprint[i].scale[1], blueprint[i].scale[2]);
  435. float3 blueRot = new float3(blueprint[i].rotation[0], blueprint[i].rotation[1], blueprint[i].rotation[2]);
  436. float3 physicalLocation = (float3)(cubeQuaternion * bluePos) + actualPosition;// + (blueprintSizeRotated / 2);
  437. //physicalLocation.x += blueprintSize.x / 2;
  438. physicalLocation -= (float3)(cubeQuaternion * correctionVec);
  439. //physicalLocation.y -= (float)(RobotCommands.blockSize * scale / 2);
  440. //float3 physicalScale = (float3)(cubeQuaternion * blueScale); // this actually over-rotates when combined with rotation
  441. float3 physicalScale = blueScale;
  442. float3 physicalRotation = (cubeQuaternion * Quaternion.Euler(blueRot)).eulerAngles;
  443. #if DEBUG
  444. Logging.MetaLog($"Placing blueprint block at {physicalLocation} rot{physicalRotation} scale{physicalScale}");
  445. Logging.MetaLog($"Location math check original:{bluePos} rotated: {(float3)(cubeQuaternion * bluePos)} actualPos: {actualPosition} result: {physicalLocation}");
  446. Logging.MetaLog($"Scale math check original:{blueScale} rotation: {(float3)cubeQuaternion.eulerAngles} result: {physicalScale}");
  447. Logging.MetaLog($"Rotation math check original:{blueRot} rotated: {(cubeQuaternion * Quaternion.Euler(blueRot))} result: {physicalRotation}");
  448. #endif
  449. placedBlocks[i] = Block.PlaceNew(VoxelObjectNotationUtility.NameToEnum(blueprint[i].name),
  450. physicalLocation,
  451. physicalRotation,
  452. blockColor,
  453. blockDarkness,
  454. scale: physicalScale);
  455. }
  456. #if DEBUG
  457. Logging.MetaLog($"Placed {placedBlocks.Length} blocks for blueprint {cube.name} (id:{cube.cubeId})");
  458. #endif
  459. return placedBlocks;
  460. }
  461. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  462. private static void LoadBlueprintMap()
  463. {
  464. StreamReader bluemap = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Pixi.blueprints.json"));
  465. blueprintMap = JsonConvert.DeserializeObject<Dictionary<uint, BlockJsonInfo[]>>(bluemap.ReadToEnd());
  466. }
  467. }
  468. }