////////////////////////////////////////////////////// // MicroSplat // Copyright (c) Jason Booth ////////////////////////////////////////////////////// using UnityEngine; using System.Collections; using UnityEditor; using UnityEditor.Callbacks; using System.Collections.Generic; namespace JBooth.MicroSplat { #if __MICROSPLAT__ [InitializeOnLoad] public class MicroSplatDecalModule : FeatureDescriptor { [MenuItem ("Window/MicroSplat/Create Decal")] static void AddEmitter () { GameObject go = new GameObject ("Decal"); go.transform.localScale = new Vector3 (10, 10, 10); go.AddComponent (); } const string sDefine = "__MICROSPLAT_DECAL__"; static MicroSplatDecalModule() { MicroSplatDefines.InitDefine(sDefine); } [PostProcessSceneAttribute (0)] public static void OnPostprocessScene() { MicroSplatDefines.InitDefine(sDefine); } public override string ModuleName() { return "Decal"; } public override string GetHelpPath () { return "https://docs.google.com/document/d/1I7kDLojwVQbnTkmtSMfVrFW9yaVQLLQi0ibqFLY960A/edit?usp=sharing"; } public enum DefineFeature { _DECAL, _DECAL_MAX0, _DECAL_MAX4, _DECAL_MAX8, _DECAL_MAX16, _DECAL_MAX32, _DECAL_MAX64, _DECAL_MAX128, _DECAL_MAX256, _DECAL_STATICMAX0, _DECAL_STATICMAX64, _DECAL_STATICMAX128, _DECAL_STATICMAX256, _DECAL_STATICMAX512, _DECAL_STATICMAX1024, _DECAL_STATICMAX2048, _DECAL_NOTEXTURES, _DECAL_EMISMETAL, _DECAL_SPLAT, _DECAL_TESS, _DECAL_TINT, kNumFeatures, } public enum MaxDecals { None, k4, k8, k16, k32, k64, } public enum MaxStaticDecals { None, k64, k128, k256, k512, k1024, k2048, } public bool decals = false; public MaxDecals maxDecals = MaxDecals.k16; public MaxStaticDecals maxStaticDecals = MaxStaticDecals.k512; public bool effectTextures = true; public bool effectSplats = false; public bool tint = false; public bool emisMetal = false; #if __MICROSPLAT_TESSELLATION__ public bool effectTess = false; #endif static TextAsset properties; static TextAsset funcs; static TextAsset cbuffer; GUIContent CDecals = new GUIContent ("Decals", "Enable decal system"); GUIContent CEffectTextures = new GUIContent ("Effect Diffuse/Normal", "Should decals modify the albedo and normal data"); GUIContent CEffectEmisMetal = new GUIContent ("Support Emissive/Metallic map", "When enabled, you can use an emissive/metallic array with decals"); GUIContent CEffectSplats = new GUIContent ("Effect Splat Maps", "Should decals modify the splat map or FX (wetness, puddles, streams, lava) data"); #if __MICROSPLAT_TESSELLATION__ GUIContent CAffectTess = new GUIContent ("Effect Displacement", "Should decals modify the displacement when tessellation is enabled"); #endif GUIContent CMaxDecals = new GUIContent ("Max Dynamic", "Maximum number of dynamic decals allowed on a single object or terrain. Dynamic decals have a runtime GPU cost based on how many decals are on an object"); GUIContent CMaxStaticDecals = new GUIContent ("Max Static", "Maximum number of static decals allowed on a single terrain. Static Decals have very small runtime cost, but are CPU intensive when moved or adjusted at runtime"); GUIContent CAlbedo = new GUIContent ("Albedo Array", "Albedo/alpha for decals"); GUIContent CEmisMetal = new GUIContent ("Emissive/Metal Array", "Emissive/Metallic array for decals"); GUIContent CNormalSAO = new GUIContent ("Normal SAO Array", "NSAO Array for decals"); GUIContent CSplat = new GUIContent ("Splat Map Array", "Array for splat maps, 3 texture weights (RGB) and alpha blend (A)"); GUIContent CPerDecalTint = new GUIContent ("Decal Tint", "Allows you to tint each decal"); static Dictionary sFeatureNames = new Dictionary(); public static string GetFeatureName(DefineFeature feature) { string ret; if (sFeatureNames.TryGetValue(feature, out ret)) { return ret; } string fn = System.Enum.GetName(typeof(DefineFeature), feature); sFeatureNames[feature] = fn; return fn; } public static bool HasFeature(string[] keywords, DefineFeature feature) { string f = GetFeatureName(feature); for (int i = 0; i < keywords.Length; ++i) { if (keywords[i] == f) return true; } return false; } public override string GetVersion() { return "3.9"; } public override void DrawFeatureGUI(MicroSplatKeywords keywords) { decals = EditorGUILayout.Toggle (CDecals, decals); if (decals) { EditorGUI.indentLevel++; using (new GUILayout.VerticalScope (GUI.skin.box)) { maxDecals = (MaxDecals)EditorGUILayout.EnumPopup (CMaxDecals, maxDecals); maxStaticDecals = (MaxStaticDecals)EditorGUILayout.EnumPopup (CMaxStaticDecals, maxStaticDecals); } effectTextures = EditorGUILayout.Toggle (CEffectTextures, effectTextures); emisMetal = EditorGUILayout.Toggle (CEffectEmisMetal, emisMetal); effectSplats = EditorGUILayout.Toggle (CEffectSplats, effectSplats); #if __MICROSPLAT_TESSELLATION__ effectTess = EditorGUILayout.Toggle (CAffectTess, effectTess); #endif tint = EditorGUILayout.Toggle (CPerDecalTint, tint); EditorGUI.indentLevel--; } } public override void DrawShaderGUI(MicroSplatShaderGUI shaderGUI, MicroSplatKeywords keywords, Material mat, MaterialEditor materialEditor, MaterialProperty[] props) { if (decals) { if (MicroSplatUtilities.DrawRollup("Decal") && mat.HasProperty("_DecalAlbedo")) { if (effectTextures) { var albedoMap = shaderGUI.FindProp ("_DecalAlbedo", props); materialEditor.TexturePropertySingleLine (CAlbedo, albedoMap); var normalMap = shaderGUI.FindProp ("_DecalNormalSAO", props); materialEditor.TexturePropertySingleLine (CNormalSAO, normalMap); } if (emisMetal && mat.HasProperty("_DecalEmisMetal")) { var emis = shaderGUI.FindProp ("_DecalEmisMetal", props); materialEditor.TexturePropertySingleLine (CEmisMetal, emis); } if (effectSplats && mat.HasProperty("_DecalSplats")) { var splatsMap = shaderGUI.FindProp ("_DecalSplats", props); materialEditor.TexturePropertySingleLine (CSplat, splatsMap); } } } } public override string[] Pack() { List features = new List(); if (decals) { features.Add(GetFeatureName(DefineFeature._DECAL)); if (maxDecals == MaxDecals.None) { features.Add (GetFeatureName (DefineFeature._DECAL_MAX0)); } else if (maxDecals == MaxDecals.k4) { features.Add (GetFeatureName (DefineFeature._DECAL_MAX4)); } else if (maxDecals == MaxDecals.k8) { features.Add (GetFeatureName (DefineFeature._DECAL_MAX8)); } else if (maxDecals == MaxDecals.k16) { features.Add (GetFeatureName (DefineFeature._DECAL_MAX16)); } else if (maxDecals == MaxDecals.k32) { features.Add (GetFeatureName (DefineFeature._DECAL_MAX32)); } else if (maxDecals == MaxDecals.k64) { features.Add (GetFeatureName (DefineFeature._DECAL_MAX64)); } if (maxStaticDecals == MaxStaticDecals.None) { features.Add (GetFeatureName (DefineFeature._DECAL_STATICMAX0)); } if (maxStaticDecals == MaxStaticDecals.k64) { features.Add (GetFeatureName (DefineFeature._DECAL_STATICMAX64)); } else if (maxStaticDecals == MaxStaticDecals.k128) { features.Add (GetFeatureName (DefineFeature._DECAL_STATICMAX128)); } else if (maxStaticDecals == MaxStaticDecals.k256) { features.Add (GetFeatureName (DefineFeature._DECAL_STATICMAX256)); } else if (maxStaticDecals == MaxStaticDecals.k512) { features.Add (GetFeatureName (DefineFeature._DECAL_STATICMAX512)); } else if (maxStaticDecals == MaxStaticDecals.k1024) { features.Add (GetFeatureName (DefineFeature._DECAL_STATICMAX1024)); } else if (maxStaticDecals == MaxStaticDecals.k2048) { features.Add (GetFeatureName (DefineFeature._DECAL_STATICMAX2048)); } if (!effectTextures) { features.Add (GetFeatureName (DefineFeature._DECAL_NOTEXTURES)); } if (effectSplats) { features.Add (GetFeatureName (DefineFeature._DECAL_SPLAT)); } #if __MICROSPLAT_TESSELLATION__ if (effectTess) { features.Add (GetFeatureName (DefineFeature._DECAL_TESS)); } #endif if (tint) { features.Add (GetFeatureName (DefineFeature._DECAL_TINT)); } if (emisMetal) { features.Add (GetFeatureName (DefineFeature._DECAL_EMISMETAL)); } } return features.ToArray(); } public override void Unpack(string[] keywords) { decals = HasFeature (keywords, DefineFeature._DECAL); maxDecals = MaxDecals.k4; if (HasFeature(keywords, DefineFeature._DECAL_MAX0)) { maxDecals = MaxDecals.None; } else if (HasFeature (keywords, DefineFeature._DECAL_MAX8)) { maxDecals = MaxDecals.k8; } else if (HasFeature(keywords, DefineFeature._DECAL_MAX16)) { maxDecals = MaxDecals.k16; } else if (HasFeature (keywords, DefineFeature._DECAL_MAX32)) { maxDecals = MaxDecals.k32; } else if (HasFeature (keywords, DefineFeature._DECAL_MAX64)) { maxDecals = MaxDecals.k64; } maxStaticDecals = MaxStaticDecals.k512; if (HasFeature(keywords, DefineFeature._DECAL_STATICMAX0)) { maxStaticDecals = MaxStaticDecals.None; } else if (HasFeature (keywords, DefineFeature._DECAL_STATICMAX64)) { maxStaticDecals = MaxStaticDecals.k64; } else if (HasFeature (keywords, DefineFeature._DECAL_STATICMAX128)) { maxStaticDecals = MaxStaticDecals.k128; } else if (HasFeature (keywords, DefineFeature._DECAL_STATICMAX256)) { maxStaticDecals = MaxStaticDecals.k256; } else if (HasFeature (keywords, DefineFeature._DECAL_STATICMAX512)) { maxStaticDecals = MaxStaticDecals.k512; } else if (HasFeature (keywords, DefineFeature._DECAL_STATICMAX1024)) { maxStaticDecals = MaxStaticDecals.k1024; } else if (HasFeature (keywords, DefineFeature._DECAL_STATICMAX2048)) { maxStaticDecals = MaxStaticDecals.k2048; } effectTextures = !HasFeature (keywords, DefineFeature._DECAL_NOTEXTURES); emisMetal = HasFeature (keywords, DefineFeature._DECAL_EMISMETAL); effectSplats = HasFeature (keywords, DefineFeature._DECAL_SPLAT); tint = HasFeature (keywords, DefineFeature._DECAL_TINT); #if __MICROSPLAT_TESSELLATION__ effectTess = HasFeature (keywords, DefineFeature._DECAL_TESS); #endif } public override void InitCompiler(string[] paths) { for (int i = 0; i < paths.Length; ++i) { string p = paths[i]; if (p.EndsWith("microsplat_properties_decal.txt")) { properties = AssetDatabase.LoadAssetAtPath(p); } if (p.EndsWith("microsplat_func_decal.txt")) { funcs = AssetDatabase.LoadAssetAtPath(p); } if (p.EndsWith ("microsplat_cbuffer_decal.txt")) { cbuffer = AssetDatabase.LoadAssetAtPath (p); } } } public override void WriteProperties(string[] features, System.Text.StringBuilder sb) { if (decals) { sb.Append(properties.text); if (emisMetal) { sb.AppendLine ("_DecalEmisMetal(\"Decal Emissive Metal\", 2DArray) = \"black\" {}"); } } } public override void WriteFunctions(string [] features, System.Text.StringBuilder sb) { if (decals) { sb.Append(funcs.text); } } public override void WritePerMaterialCBuffer (string[] features, System.Text.StringBuilder sb) { if (decals) { sb.Append(cbuffer.text); } } public override void ComputeSampleCounts(string[] features, ref int arraySampleCount, ref int textureSampleCount, ref int maxSamples, ref int tessellationSamples, ref int depTexReadLevel) { if (decals) { // this is rather arbitrary, could do some approximation based on static vs. dynamic counts, but wouldn't be any more or less // correct than this.. arraySampleCount += 8; } #if __MICROSPLAT_TESSELLATION__ if (effectTess) { tessellationSamples += 8; } #endif } } #endif }