CubeLutAssetImporter.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using System.Collections.Generic;
  2. using System.Globalization;
  3. using System.IO;
  4. using System.Text;
  5. using UnityEngine;
  6. namespace UnityEditor.Rendering.PostProcessing
  7. {
  8. sealed class CubeLutAssetImporter : AssetPostprocessor
  9. {
  10. static List<string> s_Excluded = new List<string>()
  11. {
  12. "Linear to sRGB r1",
  13. "Linear to Unity Log r1",
  14. "sRGB to Linear r1",
  15. "sRGB to Unity Log r1",
  16. "Unity Log to Linear r1",
  17. "Unity Log to sRGB r1"
  18. };
  19. static void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] moved, string[] movedFrom)
  20. {
  21. foreach (string path in imported)
  22. {
  23. string ext = Path.GetExtension(path);
  24. string filename = Path.GetFileNameWithoutExtension(path);
  25. if (string.IsNullOrEmpty(ext) || s_Excluded.Contains(filename))
  26. continue;
  27. ext = ext.ToLowerInvariant();
  28. if (ext.Equals(".cube"))
  29. ImportCubeLut(path);
  30. }
  31. }
  32. // Basic CUBE lut parser
  33. // Specs: http://wwwimages.adobe.com/content/dam/Adobe/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf
  34. static void ImportCubeLut(string path)
  35. {
  36. // Remove the 'Assets' part of the path & build absolute path
  37. string fullpath = path.Substring(7);
  38. fullpath = Path.Combine(Application.dataPath, fullpath);
  39. // Read the lut data
  40. string[] lines = File.ReadAllLines(fullpath);
  41. // Start parsing
  42. int i = 0;
  43. int size = -1;
  44. int sizeCube = -1;
  45. var table = new List<Color>();
  46. var domainMin = Color.black;
  47. var domainMax = Color.white;
  48. while (true)
  49. {
  50. if (i >= lines.Length)
  51. {
  52. if (table.Count != sizeCube)
  53. Debug.LogError("Premature end of file");
  54. break;
  55. }
  56. string line = FilterLine(lines[i]);
  57. if (string.IsNullOrEmpty(line))
  58. goto next;
  59. // Header data
  60. if (line.StartsWith("TITLE"))
  61. goto next; // Skip the title tag, we don't need it
  62. if (line.StartsWith("LUT_3D_SIZE"))
  63. {
  64. string sizeStr = line.Substring(11).TrimStart();
  65. if (!int.TryParse(sizeStr, out size))
  66. {
  67. Debug.LogError("Invalid data on line " + i);
  68. break;
  69. }
  70. if (size < 2 || size > 256)
  71. {
  72. Debug.LogError("LUT size out of range");
  73. break;
  74. }
  75. sizeCube = size * size * size;
  76. goto next;
  77. }
  78. if (line.StartsWith("DOMAIN_MIN"))
  79. {
  80. if (!ParseDomain(i, line, ref domainMin)) break;
  81. goto next;
  82. }
  83. if (line.StartsWith("DOMAIN_MAX"))
  84. {
  85. if (!ParseDomain(i, line, ref domainMax)) break;
  86. goto next;
  87. }
  88. // Table
  89. string[] row = line.Split();
  90. if (row.Length != 3)
  91. {
  92. Debug.LogError("Invalid data on line " + i);
  93. break;
  94. }
  95. var color = Color.black;
  96. for (int j = 0; j < 3; j++)
  97. {
  98. float d;
  99. if (!float.TryParse(row[j], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out d))
  100. {
  101. Debug.LogError("Invalid data on line " + i);
  102. break;
  103. }
  104. color[j] = d;
  105. }
  106. table.Add(color);
  107. next:
  108. i++;
  109. }
  110. if (sizeCube != table.Count)
  111. {
  112. Debug.LogError("Wrong table size - Expected " + sizeCube + " elements, got " + table.Count);
  113. return;
  114. }
  115. // Check if the Texture3D already exists, update it in this case (better workflow for
  116. // the user)
  117. string assetPath = Path.ChangeExtension(path, ".asset");
  118. var tex = AssetDatabase.LoadAssetAtPath<Texture3D>(assetPath);
  119. if (tex != null)
  120. {
  121. tex.SetPixels(table.ToArray(), 0);
  122. tex.Apply();
  123. }
  124. else
  125. {
  126. // Generate a new Texture3D
  127. tex = new Texture3D(size, size, size, TextureFormat.RGBAHalf, false)
  128. {
  129. anisoLevel = 0,
  130. filterMode = FilterMode.Bilinear,
  131. wrapMode = TextureWrapMode.Clamp,
  132. };
  133. tex.SetPixels(table.ToArray(), 0);
  134. tex.Apply();
  135. // Save to disk
  136. AssetDatabase.CreateAsset(tex, assetPath);
  137. }
  138. AssetDatabase.SaveAssets();
  139. AssetDatabase.Refresh();
  140. }
  141. static string FilterLine(string line)
  142. {
  143. var filtered = new StringBuilder();
  144. line = line.TrimStart().TrimEnd();
  145. int len = line.Length;
  146. int i = 0;
  147. while (i < len)
  148. {
  149. char c = line[i];
  150. if (c == '#') // Filters comment out
  151. break;
  152. filtered.Append(c);
  153. i++;
  154. }
  155. return filtered.ToString();
  156. }
  157. static bool ParseDomain(int i, string line, ref Color domain)
  158. {
  159. string[] domainStrs = line.Substring(10).TrimStart().Split();
  160. if (domainStrs.Length != 3)
  161. {
  162. Debug.LogError("Invalid data on line " + i);
  163. return false;
  164. }
  165. for (int j = 0; j < 3; j++)
  166. {
  167. float d;
  168. if (!float.TryParse(domainStrs[j], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out d))
  169. {
  170. Debug.LogError("Invalid data on line " + i);
  171. return false;
  172. }
  173. domain[j] = d;
  174. }
  175. return true;
  176. }
  177. }
  178. }