/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
using Live2D.Cubism.Editor.Deleters;
using Live2D.Cubism.Editor.Importers;
using Live2D.Cubism.Rendering;
using Live2D.Cubism.Rendering.Masking;
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
namespace Live2D.Cubism.Editor
{
///
/// Hooks into Unity's asset pipeline allowing custom processing of assets.
///
public class CubismAssetProcessor : AssetPostprocessor
{
#region Unity Event Handling
///
/// Called by Unity. Makes sure code is allowed.
///
// ReSharper disable once InconsistentNaming
public static void OnGeneratedCSProjectFiles()
{
AllowUnsafeCode();
}
///
/// Called by Unity on asset import. Handles importing of Cubism related assets.
///
/// Paths of imported assets.
/// Paths of removed assets.
/// Paths of moved assets
/// Paths of moved assets before moving
private static void OnPostprocessAllAssets(
string[] importedAssetPaths,
string[] deletedAssetPaths,
string[] movedAssetPaths,
string[] movedFromAssetPaths)
{
// Make sure builtin resources are available.
GenerateBuiltinResources();
var assetList = CubismCreatedAssetList.GetInstance();
// Handle any imported Cubism assets.
foreach (var assetPath in importedAssetPaths)
{
var importer = CubismImporter.GetImporterAtPath(assetPath);
if (importer == null)
{
continue;
}
try
{
importer.Import();
}
catch(Exception e)
{
Debug.LogError("CubismAssetProcessor : Following error occurred while importing " + assetPath);
Debug.LogError(e);
}
}
assetList.OnPostImport();
// Handle any deleted Cubism assets.
foreach (var assetPath in deletedAssetPaths)
{
var deleter = CubismDeleter.GetDeleterAsPath(assetPath);
if (deleter == null)
{
continue;
}
deleter.Delete();
}
}
#endregion
#region C# Project Patching
///
/// Makes sure code is allowed in the runtime project.
///
private static void AllowUnsafeCode()
{
foreach (var csproj in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.csproj"))
{
// Skip Editor assembly.
if (csproj.EndsWith(".Editor.csproj"))
{
continue;
}
var document = XDocument.Load(csproj);
var project = document.Root;
// Allow unsafe code.
for (var propertyGroup = project.FirstNode as XElement; propertyGroup != null; propertyGroup = propertyGroup.NextNode as XElement)
{
// Skip non-relevant groups.
if (!propertyGroup.ToString().Contains("PropertyGroup") || !propertyGroup.ToString().Contains("$(Configuration)|$(Platform)"))
{
continue;
}
// Add unsafe-block element if necessary.
if (!propertyGroup.ToString().Contains("AllowUnsafeBlocks"))
{
var nameSpace = propertyGroup.GetDefaultNamespace();
propertyGroup.Add(new XElement(nameSpace + "AllowUnsafeBlocks", "true"));
}
// Make sure unsafe-block element is always set to true.
for (var allowUnsafeBlocks = propertyGroup.FirstNode as XElement; allowUnsafeBlocks != null; allowUnsafeBlocks = allowUnsafeBlocks.NextNode as XElement)
{
if (!allowUnsafeBlocks.ToString().Contains("AllowUnsafeBlocks"))
{
continue;
}
allowUnsafeBlocks.SetValue("true");
}
}
// Store changes.
document.Save(csproj);
}
}
#endregion
#region Resources Generation
///
/// Sets Cubism-style normal blending for a material.
///
/// Material to set up.
private static void EnableNormalBlending(Material material)
{
material.SetInt("_SrcColor", (int)BlendMode.One);
material.SetInt("_DstColor", (int)BlendMode.OneMinusSrcAlpha);
material.SetInt("_SrcAlpha", (int)BlendMode.One);
material.SetInt("_DstAlpha", (int)BlendMode.OneMinusSrcAlpha);
}
///
/// Sets Cubism-style additive blending for a material.
///
/// Material to set up.
private static void EnableAdditiveBlending(Material material)
{
material.SetInt("_SrcColor", (int)BlendMode.One);
material.SetInt("_DstColor", (int)BlendMode.One);
material.SetInt("_SrcAlpha", (int)BlendMode.Zero);
material.SetInt("_DstAlpha", (int)BlendMode.One);
}
///
/// Sets Cubism-style multiplicative blending for a material.
///
/// Material to set up.
private static void EnableMultiplicativeBlending(Material material)
{
material.SetInt("_SrcColor", (int)BlendMode.DstColor);
material.SetInt("_DstColor", (int)BlendMode.OneMinusSrcAlpha);
material.SetInt("_SrcAlpha", (int)BlendMode.Zero);
material.SetInt("_DstAlpha", (int)BlendMode.One);
}
///
/// Sets Cubism-style culling for a mask material.
///
/// Material to set up.
private static void EnableCulling(Material material)
{
material.SetInt("_Cull", (int)CullMode.Front);
}
///
/// Enables Cubism-style masking for a material.
///
/// Material to set up.
private static void EnableMasking(Material material)
{
// Set toggle.
material.SetInt("cubism_MaskOn", 1);
// Enable keyword.
var shaderKeywords = material.shaderKeywords.ToList();
shaderKeywords.Clear();
if (!shaderKeywords.Contains("CUBISM_MASK_ON"))
{
shaderKeywords.Add("CUBISM_MASK_ON");
}
material.shaderKeywords = shaderKeywords.ToArray();
}
///
/// Enables Cubism-style inverted mask for a material.
///
/// Material to set up.
private static void EnableInvertedMask(Material material)
{
// Set toggle.
material.SetInt("cubism_MaskOn", 1);
material.SetInt("cubism_InvertOn", 1);
// Enable keyword.
var shaderKeywords = material.shaderKeywords.ToList();
shaderKeywords.Clear();
if (!shaderKeywords.Contains("CUBISM_INVERT_ON"))
{
shaderKeywords.Add("CUBISM_INVERT_ON");
}
material.shaderKeywords = shaderKeywords.ToArray();
}
///
/// Generates the builtin resources as necessary.
///
private static void GenerateBuiltinResources()
{
var resourcesRoot = AssetDatabase
.GetAssetPath(CubismBuiltinShaders.Unlit)
.Replace("/Shaders/Unlit.shader", "");
// Create materials.
if (CubismBuiltinMaterials.Mask == null)
{
var materialsRoot = resourcesRoot + "/Materials";
// Make sure materials folder exists.
if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), materialsRoot)))
{
Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(), materialsRoot));
}
// Create mask material.
var material = new Material (CubismBuiltinShaders.Mask)
{
name = "Mask"
};
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
// Create mask material.
material = new Material (CubismBuiltinShaders.Mask)
{
name = "MaskCulling"
};
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
// Create non-masked materials.
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "Unlit"
};
EnableNormalBlending(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitAdditive"
};
EnableAdditiveBlending(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitMultiply"
};
EnableMultiplicativeBlending(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
// Create masked materials.
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitMasked"
};
EnableNormalBlending(material);
EnableMasking(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitAdditiveMasked"
};
EnableAdditiveBlending(material);
EnableMasking(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitMultiplyMasked"
};
EnableMultiplicativeBlending(material);
EnableMasking(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
// Create inverted mask materials.
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitMaskedInverted"
};
EnableNormalBlending(material);
EnableInvertedMask(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitAdditiveMaskedInverted"
};
EnableAdditiveBlending(material);
EnableInvertedMask(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material (CubismBuiltinShaders.Unlit)
{
name = "UnlitMultiplyMaskedInverted"
};
EnableMultiplicativeBlending(material);
EnableInvertedMask(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
// Create non-masked materials.
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitCulling"
};
EnableNormalBlending(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitAdditiveCulling"
};
EnableAdditiveBlending(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitMultiplyCulling"
};
EnableMultiplicativeBlending(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
// Create masked materials.
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitMaskedCulling"
};
EnableNormalBlending(material);
EnableMasking(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitAdditiveMaskedCulling"
};
EnableAdditiveBlending(material);
EnableMasking(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitMultiplyMaskedCulling"
};
EnableMultiplicativeBlending(material);
EnableMasking(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
// Create inverted mask materials.
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitMaskedInvertedCulling"
};
EnableNormalBlending(material);
EnableInvertedMask(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitAdditiveMaskedInvertedCulling"
};
EnableAdditiveBlending(material);
EnableInvertedMask(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
material = new Material(CubismBuiltinShaders.Unlit)
{
name = "UnlitMultiplyMaskedInvertedCulling"
};
EnableMultiplicativeBlending(material);
EnableInvertedMask(material);
EnableCulling(material);
AssetDatabase.CreateAsset(material, string.Format("{0}/{1}.mat", materialsRoot, material.name));
EditorUtility.SetDirty(CubismBuiltinShaders.Unlit);
AssetDatabase.SaveAssets();
}
// Create global mask texture.
if (CubismMaskTexture.GlobalMaskTexture == null)
{
var globalMaskTexture = ScriptableObject.CreateInstance();
globalMaskTexture.name = "GlobalMaskTexture";
AssetDatabase.CreateAsset(globalMaskTexture, string.Format("{0}/{1}.asset", resourcesRoot, globalMaskTexture.name));
}
}
#endregion
}
}