CubismAssetProcessor.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /**
  2. * Copyright(c) Live2D Inc. All rights reserved.
  3. *
  4. * Use of this source code is governed by the Live2D Open Software license
  5. * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
  6. */
  7. using Live2D.Cubism.Editor.Deleters;
  8. using Live2D.Cubism.Editor.Importers;
  9. using Live2D.Cubism.Rendering;
  10. using Live2D.Cubism.Rendering.Masking;
  11. using System;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Xml.Linq;
  15. using UnityEditor;
  16. using UnityEngine;
  17. using UnityEngine.Rendering;
  18. namespace Live2D.Cubism.Editor
  19. {
  20. /// <summary>
  21. /// Hooks into Unity's asset pipeline allowing custom processing of assets.
  22. /// </summary>
  23. public class CubismAssetProcessor : AssetPostprocessor
  24. {
  25. #region Unity Event Handling
  26. #if !UNITY_2017_3_OR_NEWER
  27. /// <summary>
  28. /// Called by Unity. Makes sure <see langword="unsafe"/> code is allowed.
  29. /// </summary>
  30. // ReSharper disable once InconsistentNaming
  31. public static void OnGeneratedCSProjectFiles()
  32. {
  33. AllowUnsafeCode();
  34. }
  35. #endif
  36. /// <summary>
  37. /// Called by Unity on asset import. Handles importing of Cubism related assets.
  38. /// </summary>
  39. /// <param name="importedAssetPaths">Paths of imported assets.</param>
  40. /// <param name="deletedAssetPaths">Paths of removed assets.</param>
  41. /// <param name="movedAssetPaths">Paths of moved assets</param>
  42. /// <param name="movedFromAssetPaths">Paths of moved assets before moving</param>
  43. private static void OnPostprocessAllAssets(
  44. string[] importedAssetPaths,
  45. string[] deletedAssetPaths,
  46. string[] movedAssetPaths,
  47. string[] movedFromAssetPaths)
  48. {
  49. // Make sure builtin resources are available.
  50. GenerateBuiltinResources();
  51. var assetList = CubismCreatedAssetList.GetInstance();
  52. // Handle any imported Cubism assets.
  53. foreach (var assetPath in importedAssetPaths)
  54. {
  55. var importer = CubismImporter.GetImporterAtPath(assetPath);
  56. if (importer == null)
  57. {
  58. continue;
  59. }
  60. try
  61. {
  62. importer.Import();
  63. }
  64. catch(Exception e)
  65. {
  66. Debug.LogError("CubismAssetProcessor : Following error occurred while importing " + assetPath);
  67. Debug.LogError(e);
  68. }
  69. }
  70. assetList.OnPostImport();
  71. // Handle any deleted Cubism assets.
  72. foreach (var assetPath in deletedAssetPaths)
  73. {
  74. var deleter = CubismDeleter.GetDeleterAsPath(assetPath);
  75. if (deleter == null)
  76. {
  77. continue;
  78. }
  79. deleter.Delete();
  80. }
  81. }
  82. #endregion
  83. #region C# Project Patching
  84. /// <summary>
  85. /// Makes sure <see langword="unsafe"/> code is allowed in the runtime project.
  86. /// </summary>
  87. private static void AllowUnsafeCode()
  88. {
  89. foreach (var csproj in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.csproj"))
  90. {
  91. // Skip Editor assembly.
  92. if (csproj.EndsWith(".Editor.csproj"))
  93. {
  94. continue;
  95. }
  96. var document = XDocument.Load(csproj);
  97. var project = document.Root;
  98. // Allow unsafe code.
  99. for (var propertyGroup = project.FirstNode as XElement; propertyGroup != null; propertyGroup = propertyGroup.NextNode as XElement)
  100. {
  101. // Skip non-relevant groups.
  102. if (!propertyGroup.ToString().Contains("PropertyGroup") || !propertyGroup.ToString().Contains("$(Configuration)|$(Platform)"))
  103. {
  104. continue;
  105. }
  106. // Add unsafe-block element if necessary.
  107. if (!propertyGroup.ToString().Contains("AllowUnsafeBlocks"))
  108. {
  109. var nameSpace = propertyGroup.GetDefaultNamespace();
  110. propertyGroup.Add(new XElement(nameSpace + "AllowUnsafeBlocks", "true"));
  111. }
  112. // Make sure unsafe-block element is always set to true.
  113. for (var allowUnsafeBlocks = propertyGroup.FirstNode as XElement; allowUnsafeBlocks != null; allowUnsafeBlocks = allowUnsafeBlocks.NextNode as XElement)
  114. {
  115. if (!allowUnsafeBlocks.ToString().Contains("AllowUnsafeBlocks"))
  116. {
  117. continue;
  118. }
  119. allowUnsafeBlocks.SetValue("true");
  120. }
  121. }
  122. // Store changes.
  123. document.Save(csproj);
  124. }
  125. }
  126. #endregion
  127. #region Resources Generation
  128. /// <summary>
  129. /// Sets Cubism-style normal blending for a material.
  130. /// </summary>
  131. /// <param name="material">Material to set up.</param>
  132. private static void EnableNormalBlending(Material material)
  133. {
  134. material.SetInt("_SrcColor", (int)BlendMode.One);
  135. material.SetInt("_DstColor", (int)BlendMode.OneMinusSrcAlpha);
  136. material.SetInt("_SrcAlpha", (int)BlendMode.One);
  137. material.SetInt("_DstAlpha", (int)BlendMode.OneMinusSrcAlpha);
  138. }
  139. /// <summary>
  140. /// Sets Cubism-style additive blending for a material.
  141. /// </summary>
  142. /// <param name="material">Material to set up.</param>
  143. private static void EnableAdditiveBlending(Material material)
  144. {
  145. material.SetInt("_SrcColor", (int)BlendMode.One);
  146. material.SetInt("_DstColor", (int)BlendMode.One);
  147. material.SetInt("_SrcAlpha", (int)BlendMode.Zero);
  148. material.SetInt("_DstAlpha", (int)BlendMode.One);
  149. }
  150. /// <summary>
  151. /// Sets Cubism-style multiplicative blending for a material.
  152. /// </summary>
  153. /// <param name="material">Material to set up.</param>
  154. private static void EnableMultiplicativeBlending(Material material)
  155. {
  156. material.SetInt("_SrcColor", (int)BlendMode.DstColor);
  157. material.SetInt("_DstColor", (int)BlendMode.OneMinusSrcAlpha);
  158. material.SetInt("_SrcAlpha", (int)BlendMode.Zero);
  159. material.SetInt("_DstAlpha", (int)BlendMode.One);
  160. }
  161. /// <summary>
  162. /// Sets Cubism-style culling for a mask material.
  163. /// </summary>
  164. /// <param name="material">Material to set up.</param>
  165. private static void EnableCulling(Material material)
  166. {
  167. material.SetInt("_Cull", (int)CullMode.Front);
  168. }
  169. /// <summary>
  170. /// Enables Cubism-style masking for a material.
  171. /// </summary>
  172. /// <param name="material">Material to set up.</param>
  173. private static void EnableMasking(Material material)
  174. {
  175. // Set toggle.
  176. material.SetInt("cubism_MaskOn", 1);
  177. // Enable keyword.
  178. var shaderKeywords = material.shaderKeywords.ToList();
  179. shaderKeywords.Clear();
  180. if (!shaderKeywords.Contains("CUBISM_MASK_ON"))
  181. {
  182. shaderKeywords.Add("CUBISM_MASK_ON");
  183. }
  184. material.shaderKeywords = shaderKeywords.ToArray();
  185. }
  186. /// <summary>
  187. /// Enables Cubism-style inverted mask for a material.
  188. /// </summary>
  189. /// <param name="material">Material to set up.</param>
  190. private static void EnableInvertedMask(Material material)
  191. {
  192. // Set toggle.
  193. material.SetInt("cubism_MaskOn", 1);
  194. material.SetInt("cubism_InvertOn", 1);
  195. // Enable keyword.
  196. var shaderKeywords = material.shaderKeywords.ToList();
  197. shaderKeywords.Clear();
  198. if (!shaderKeywords.Contains("CUBISM_INVERT_ON"))
  199. {
  200. shaderKeywords.Add("CUBISM_INVERT_ON");
  201. }
  202. material.shaderKeywords = shaderKeywords.ToArray();
  203. }
  204. /// <summary>
  205. /// Generates the builtin resources as necessary.
  206. /// </summary>
  207. private static void GenerateBuiltinResources()
  208. {
  209. var resourcesRoot = AssetDatabase
  210. .GetAssetPath(CubismBuiltinShaders.Unlit)
  211. .Replace("/Shaders/Unlit.shader", "");
  212. // Create materials.
  213. if (CubismBuiltinMaterials.Mask == null)
  214. {
  215. var materialsRoot = resourcesRoot + "/Materials";
  216. // Make sure materials folder exists.
  217. if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), materialsRoot)))
  218. {
  219. Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(), materialsRoot));
  220. }
  221. // Create mask material.
  222. var material = new Material (CubismBuiltinShaders.Mask)
  223. {
  224. name = "Mask"
  225. };
  226. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  227. // Create mask material.
  228. material = new Material (CubismBuiltinShaders.Mask)
  229. {
  230. name = "MaskCulling"
  231. };
  232. EnableCulling(material);
  233. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  234. // Create non-masked materials.
  235. material = new Material (CubismBuiltinShaders.Unlit)
  236. {
  237. name = "Unlit"
  238. };
  239. EnableNormalBlending(material);
  240. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  241. material = new Material (CubismBuiltinShaders.Unlit)
  242. {
  243. name = "UnlitAdditive"
  244. };
  245. EnableAdditiveBlending(material);
  246. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  247. material = new Material (CubismBuiltinShaders.Unlit)
  248. {
  249. name = "UnlitMultiply"
  250. };
  251. EnableMultiplicativeBlending(material);
  252. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  253. // Create masked materials.
  254. material = new Material (CubismBuiltinShaders.Unlit)
  255. {
  256. name = "UnlitMasked"
  257. };
  258. EnableNormalBlending(material);
  259. EnableMasking(material);
  260. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  261. material = new Material (CubismBuiltinShaders.Unlit)
  262. {
  263. name = "UnlitAdditiveMasked"
  264. };
  265. EnableAdditiveBlending(material);
  266. EnableMasking(material);
  267. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  268. material = new Material (CubismBuiltinShaders.Unlit)
  269. {
  270. name = "UnlitMultiplyMasked"
  271. };
  272. EnableMultiplicativeBlending(material);
  273. EnableMasking(material);
  274. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  275. // Create inverted mask materials.
  276. material = new Material (CubismBuiltinShaders.Unlit)
  277. {
  278. name = "UnlitMaskedInverted"
  279. };
  280. EnableNormalBlending(material);
  281. EnableInvertedMask(material);
  282. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  283. material = new Material (CubismBuiltinShaders.Unlit)
  284. {
  285. name = "UnlitAdditiveMaskedInverted"
  286. };
  287. EnableAdditiveBlending(material);
  288. EnableInvertedMask(material);
  289. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  290. material = new Material (CubismBuiltinShaders.Unlit)
  291. {
  292. name = "UnlitMultiplyMaskedInverted"
  293. };
  294. EnableMultiplicativeBlending(material);
  295. EnableInvertedMask(material);
  296. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  297. // Create non-masked materials.
  298. material = new Material(CubismBuiltinShaders.Unlit)
  299. {
  300. name = "UnlitCulling"
  301. };
  302. EnableNormalBlending(material);
  303. EnableCulling(material);
  304. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  305. material = new Material(CubismBuiltinShaders.Unlit)
  306. {
  307. name = "UnlitAdditiveCulling"
  308. };
  309. EnableAdditiveBlending(material);
  310. EnableCulling(material);
  311. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  312. material = new Material(CubismBuiltinShaders.Unlit)
  313. {
  314. name = "UnlitMultiplyCulling"
  315. };
  316. EnableMultiplicativeBlending(material);
  317. EnableCulling(material);
  318. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  319. // Create masked materials.
  320. material = new Material(CubismBuiltinShaders.Unlit)
  321. {
  322. name = "UnlitMaskedCulling"
  323. };
  324. EnableNormalBlending(material);
  325. EnableMasking(material);
  326. EnableCulling(material);
  327. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  328. material = new Material(CubismBuiltinShaders.Unlit)
  329. {
  330. name = "UnlitAdditiveMaskedCulling"
  331. };
  332. EnableAdditiveBlending(material);
  333. EnableMasking(material);
  334. EnableCulling(material);
  335. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  336. material = new Material(CubismBuiltinShaders.Unlit)
  337. {
  338. name = "UnlitMultiplyMaskedCulling"
  339. };
  340. EnableMultiplicativeBlending(material);
  341. EnableMasking(material);
  342. EnableCulling(material);
  343. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  344. // Create inverted mask materials.
  345. material = new Material(CubismBuiltinShaders.Unlit)
  346. {
  347. name = "UnlitMaskedInvertedCulling"
  348. };
  349. EnableNormalBlending(material);
  350. EnableInvertedMask(material);
  351. EnableCulling(material);
  352. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  353. material = new Material(CubismBuiltinShaders.Unlit)
  354. {
  355. name = "UnlitAdditiveMaskedInvertedCulling"
  356. };
  357. EnableAdditiveBlending(material);
  358. EnableInvertedMask(material);
  359. EnableCulling(material);
  360. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  361. material = new Material(CubismBuiltinShaders.Unlit)
  362. {
  363. name = "UnlitMultiplyMaskedInvertedCulling"
  364. };
  365. EnableMultiplicativeBlending(material);
  366. EnableInvertedMask(material);
  367. EnableCulling(material);
  368. AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
  369. EditorUtility.SetDirty(CubismBuiltinShaders.Unlit);
  370. AssetDatabase.SaveAssets();
  371. }
  372. // Create global mask texture.
  373. if (CubismMaskTexture.GlobalMaskTexture == null)
  374. {
  375. var globalMaskTexture = ScriptableObject.CreateInstance<CubismMaskTexture>();
  376. globalMaskTexture.name = "GlobalMaskTexture";
  377. AssetDatabase.CreateAsset(globalMaskTexture, string.Format("{0}/{1}.asset", resourcesRoot, globalMaskTexture.name));
  378. }
  379. }
  380. #endregion
  381. }
  382. }