CubismAssetProcessor.cs 17 KB

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