Files
2025-06-04 09:09:39 +08:00

439 lines
14 KiB
C#

//////////////////////////////////////////////////////
// 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<MicroSplatDecal> ();
}
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<DefineFeature, string> sFeatureNames = new Dictionary<DefineFeature, string>();
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<string> features = new List<string>();
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<TextAsset>(p);
}
if (p.EndsWith("microsplat_func_decal.txt"))
{
funcs = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith ("microsplat_cbuffer_decal.txt"))
{
cbuffer = AssetDatabase.LoadAssetAtPath<TextAsset> (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
}