This commit is contained in:
CortexCore
2024-05-17 16:24:41 +08:00
parent 81913ff82f
commit e2650195a5
186 changed files with 72475 additions and 1 deletions

View File

@@ -0,0 +1,586 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace JBooth.MicroSplat
{
public abstract class FeatureDescriptor
{
/// <summary>
/// All versions must match for module to be active
/// </summary>
/// <returns>The version.</returns>
public abstract string GetVersion();
public virtual bool HideModule()
{
return false;
}
// used when you have compiler ordering issues
public virtual int CompileSortOrder()
{
return 0;
}
public virtual int DisplaySortOrder()
{
return 0;
}
public virtual string GetHelpPath() { return null; }
public abstract string ModuleName();
/// <summary>
/// Called after the shader is generated, for search/replace operations
/// </summary>
/// <param name="sb"></param>
/// <param name="features"></param>
/// <param name="name"></param>
/// <param name="baseName"></param>
/// <param name="auxShader"></param>
public virtual void OnPostGeneration(ref StringBuilder sb, string[] features, string name, string baseName = null, MicroSplatShaderGUI.MicroSplatCompiler.AuxShader auxShader = null) { }
/// <summary>
/// If you wish to generate an addtional shader with special functionality, return one of these
/// </summary>
/// <returns></returns>
public virtual MicroSplatShaderGUI.MicroSplatCompiler.AuxShader GetAuxShader () { return null; }
/// <summary>
/// if you return above, this will be called when your additional shader is about to be compiled, allowing you to modify the keywords list for you extra shader
/// </summary>
/// <param name="keywords"></param>
public virtual void ModifyKeywordsForAuxShader(List<string> keywords) { }
/// <summary>
/// Requireses the shader model46.
/// </summary>
/// <returns><c>true</c>, if shader model46 was requiresed, <c>false</c> otherwise.</returns>
public virtual bool RequiresShaderModel46() { return false; }
/// <summary>
/// DrawGUI for shader compiler feature options
/// </summary>
/// <param name="mat">Mat.</param>
public abstract void DrawFeatureGUI(MicroSplatKeywords keywords);
/// <summary>
/// Draw the editor for the shaders options
/// </summary>
/// <param name="shaderGUI">Shader GU.</param>
/// <param name="mat">Mat.</param>
/// <param name="materialEditor">Material editor.</param>
/// <param name="props">Properties.</param>
public abstract void DrawShaderGUI(MicroSplatShaderGUI shaderGUI, MicroSplatKeywords keywords, Material mat, MaterialEditor materialEditor, MaterialProperty[] props);
/// <summary>
/// Got per texture properties? Draw the GUI for them here..
/// </summary>
/// <param name="index">Index.</param>
/// <param name="shaderGUI">Shader GU.</param>
/// <param name="mat">Mat.</param>
/// <param name="materialEditor">Material editor.</param>
/// <param name="props">Properties.</param>
public virtual void DrawPerTextureGUI(int index, MicroSplatKeywords keywords, Material mat, MicroSplatPropData propData)
{
}
/// <summary>
/// Unpack your keywords from the material
/// </summary>
/// <param name="keywords">Keywords.</param>
public abstract void Unpack(string[] keywords);
/// <summary>
/// pack keywords to a string[]
/// </summary>
public abstract string[] Pack();
/// <summary>
/// Init yourself
/// </summary>
/// <param name="paths">Paths.</param>
public abstract void InitCompiler(string[] paths);
/// <summary>
/// write property definitions to the shader
/// </summary>
/// <param name="features">Features.</param>
/// <param name="sb">Sb.</param>
public abstract void WriteProperties(string[] features, StringBuilder sb);
/// <summary>
/// HDRP/LWRP benifit from declaring variables in CBuffers for instancing
/// </summary>
/// <param name="features">Features.</param>
/// <param name="sb">Sb.</param>
public virtual void WritePerMaterialCBuffer(string[] features, StringBuilder sb) { }
/// <summary>
/// Write any functions which might be used by other modules, basically, a prepass so that something can rely on
/// code included by another module
/// </summary>
/// <param name="sb">Sb.</param>
public virtual void WriteSharedFunctions(string[] features, StringBuilder sb) { }
/// <summary>
/// Some things, like tessellation in LWRP, require being able to call the vertex function
/// </summary>
/// <param name="sb">Sb.</param>
public virtual void WriteAfterVetrexFunctions(StringBuilder sb) { }
/// <summary>
/// Write the core functions you use to the shader
/// </summary>
/// <param name="sb">Sb.</param>
public abstract void WriteFunctions(string[] features, StringBuilder sb);
/// <summary>
/// Compute rough cost parameters for your section of the shader
/// </summary>
/// <param name="features">List of material features.<param>
/// <param name="arraySampleCount">Array sample count.</param>
/// <param name="textureSampleCount">Texture sample count.</param>
/// <param name="maxSamples">Max samples.</param>
/// <param name="tessellationSamples">Tessellation samples.</param>
/// <param name="depTexReadLevel">Dep tex read level.</param>
public abstract void ComputeSampleCounts(string[] features, ref int arraySampleCount, ref int textureSampleCount, ref int maxSamples,
ref int tessellationSamples, ref int depTexReadLevel);
public void Pack(MicroSplatKeywords keywords)
{
var pck = Pack();
for (int i = 0; i < pck.Length; ++i)
{
keywords.EnableKeyword(pck[i]);
}
}
public enum Channel
{
R = 0,
G,
B,
A
}
static bool drawPertexToggle = true;
static protected int noPerTexToggleWidth = 20;
static bool PerTexToggle(MicroSplatKeywords keywords, string keyword)
{
if (drawPertexToggle)
{
bool enabled = keywords.IsKeywordEnabled(keyword);
bool newEnabled = EditorGUILayout.Toggle(enabled, GUILayout.Width(20));
if (enabled != newEnabled)
{
if (newEnabled)
keywords.EnableKeyword(keyword);
else
keywords.DisableKeyword(keyword);
}
return newEnabled;
}
else
{
EditorGUILayout.LabelField("", GUILayout.Width(noPerTexToggleWidth));
drawPertexToggle = true;
return keywords.IsKeywordEnabled(keyword);
}
}
static protected void InitPropData(int pixel, MicroSplatPropData propData, Color defaultValues)
{
if (propData == null)
{
return;
}
// we reserve the last row of potential values as an initialization bit.
if (propData.GetValue(pixel, 15) == new Color(0,0,0,0))
{
for (int i = 0; i < propData.maxTextures; ++i)
{
propData.SetValue(i, pixel, defaultValues);
}
propData.SetValue(pixel, 15, Color.white);
}
}
static protected bool DrawPerTexFloatSlider(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData, Channel channel,
GUIContent label, float min = 0, float max = 0, bool showHeader = true)
{
EditorGUILayout.BeginHorizontal();
bool enabled = keywords.IsKeywordEnabled (keyword);
if (showHeader)
{
enabled = PerTexToggle (keywords, keyword);
GUI.enabled = enabled;
}
else
{
EditorGUILayout.LabelField ("", GUILayout.Width (20));
GUI.enabled = enabled;
}
Color c = propData.GetValue(curIdx, pixel);
float v = c[(int)channel];
float nv = v;
if (min != max)
{
nv = EditorGUILayout.Slider(label, v, min, max);
}
else
{
nv = EditorGUILayout.FloatField(label, v);
}
if (nv != v)
{
c[(int)channel] = nv;
propData.SetValue(curIdx, pixel, c);
}
if (GUILayout.Button("All", GUILayout.Width(40)))
{
for (int i = 0; i < propData.maxTextures; ++i)
{
propData.SetValue(i, pixel, (int)channel, nv);
}
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
return enabled;
}
public enum V2Cannel
{
RG = 0,
BA
}
static protected bool DrawPerTexVector2(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData, V2Cannel channel,
GUIContent label)
{
EditorGUILayout.BeginHorizontal();
bool enabled = PerTexToggle(keywords, keyword);
GUI.enabled = enabled;
Color c = propData.GetValue(curIdx, pixel);
Vector2 v2 = new Vector2(c.r, c.g);
if (channel == V2Cannel.BA)
{
v2.x = c.b;
v2.y = c.a;
}
Vector2 nv = v2;
nv = EditorGUILayout.Vector2Field(label, v2);
if (nv != v2)
{
if (channel == V2Cannel.RG)
{
c.r = nv.x;
c.g = nv.y;
}
else
{
c.b = nv.x;
c.a = nv.y;
}
propData.SetValue(curIdx, pixel, c);
}
if (GUILayout.Button("All", GUILayout.Width(40)))
{
for (int i = 0; i < propData.maxTextures; ++i)
{
// don't erase other pixels..
var fv = propData.GetValue(i, pixel);
if (channel == V2Cannel.RG)
{
c.r = nv.x;
c.g = nv.y;
}
else
{
c.b = nv.x;
c.a = nv.y;
}
propData.SetValue(i, pixel, fv);
}
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
return enabled;
}
static protected bool DrawPerTexVector3 (int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData,
GUIContent label)
{
EditorGUILayout.BeginHorizontal ();
bool enabled = PerTexToggle (keywords, keyword);
GUI.enabled = enabled;
Color c = propData.GetValue (curIdx, pixel);
Vector3 v = new Vector3 (c.r, c.g, c.b);
Vector3 nv = EditorGUILayout.Vector2Field (label, v);
if (nv != v)
{
c.r = nv.x;
c.g = nv.y;
c.b = nv.z;
propData.SetValue (curIdx, pixel, c);
}
if (GUILayout.Button ("All", GUILayout.Width (40)))
{
for (int i = 0; i < propData.maxTextures; ++i) {
// don't erase other pixels..
var fv = propData.GetValue (i, pixel);
c.r = nv.x;
c.g = nv.y;
c.b = nv.z;
propData.SetValue (i, pixel, fv);
}
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal ();
return enabled;
}
static protected bool DrawPerTexVector2Vector2(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData,
GUIContent label, GUIContent label2)
{
EditorGUILayout.BeginHorizontal();
bool enabled = PerTexToggle(keywords, keyword);
GUI.enabled = enabled;
Color c = propData.GetValue(curIdx, pixel);
Vector2 v1 = new Vector2(c.r, c.g);
Vector2 v2 = new Vector2(c.b, c.a);
Vector2 nv1 = v1;
Vector2 nv2 = v2;
EditorGUILayout.BeginVertical();
nv1 = EditorGUILayout.Vector2Field(label, v1);
nv2 = EditorGUILayout.Vector2Field(label2, v2);
EditorGUILayout.EndVertical();
if (nv1 != v1 || nv2 != v2)
{
c.r = nv1.x;
c.g = nv1.y;
c.b = nv2.x;
c.a = nv2.y;
propData.SetValue(curIdx, pixel, c);
}
if (GUILayout.Button("All", GUILayout.Width(40)))
{
c.r = nv1.x;
c.g = nv1.y;
c.b = nv2.x;
c.a = nv2.y;
for (int i = 0; i < propData.maxTextures; ++i)
{
propData.SetValue(i, pixel, c);
}
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
return enabled;
}
protected bool DrawPerTexColor(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData,
GUIContent label, bool hasAlpha)
{
EditorGUILayout.BeginHorizontal();
bool enabled = PerTexToggle(keywords, keyword);
GUI.enabled = enabled;
Color c = propData.GetValue(curIdx, pixel);
Color nv = EditorGUILayout.ColorField(label, c);
if (nv != c)
{
if (!hasAlpha)
{
nv.a = c.a;
}
propData.SetValue(curIdx, pixel, nv);
}
if (GUILayout.Button("All", GUILayout.Width(40)))
{
for (int i = 0; i < propData.maxTextures; ++i)
{
if (!hasAlpha)
{
nv.a = propData.GetValue(i, pixel).a;
}
propData.SetValue(i, pixel, nv);
}
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
return enabled;
}
static protected bool DrawPerTexPopUp(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData, Channel channel,
GUIContent label, GUIContent[] options)
{
EditorGUILayout.BeginHorizontal();
bool enabled = PerTexToggle(keywords, keyword);
GUI.enabled = enabled;
Color c = propData.GetValue(curIdx, pixel);
float v = c[(int)channel];
EditorGUI.BeginChangeCheck ();
int selected = EditorGUILayout.Popup(label, (int)v, options);
if (EditorGUI.EndChangeCheck())
{
c [(int)channel] = selected;
propData.SetValue (curIdx, pixel, c);
}
if (GUILayout.Button("All", GUILayout.Width(40)))
{
for (int i = 0; i < propData.maxTextures; ++i)
{
Color nv = propData.GetValue(i, pixel);
nv[(int)channel] = selected;
propData.SetValue(i, pixel, nv);
}
}
GUI.enabled = true;
drawPertexToggle = true;
EditorGUILayout.EndHorizontal();
return enabled;
}
static protected void DrawPerTexPopUpNoToggle (int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData, Channel channel,
GUIContent label, GUIContent [] options)
{
drawPertexToggle = false;
DrawPerTexPopUp (curIdx, pixel, keyword, keywords, propData, channel, label, options);
}
static protected void DrawPerTexVector2NoToggle(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData, V2Cannel channel,
GUIContent label)
{
drawPertexToggle = false;
DrawPerTexVector2(curIdx, pixel, keyword, keywords, propData, channel, label);
}
static protected void DrawPerTexVector2Vector2NoToggle(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData,
GUIContent label, GUIContent label2)
{
drawPertexToggle = false;
DrawPerTexVector2Vector2(curIdx, pixel, keyword, keywords, propData, label, label2);
}
static protected void DrawPerTexFloatSliderNoToggle(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData, Channel channel,
GUIContent label, float min = 0, float max = 0)
{
drawPertexToggle = false;
DrawPerTexFloatSlider(curIdx, pixel, keyword, keywords, propData, channel, label, min, max);
}
static protected void DrawPerTexColorNoToggle(int curIdx, int pixel, MicroSplatPropData propData, GUIContent label)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("", GUILayout.Width(20));
Color c = propData.GetValue(curIdx, pixel);
Color nv = EditorGUILayout.ColorField(label, c);
if (nv != c)
{
propData.SetValue(curIdx, pixel, nv);
}
if (GUILayout.Button("All", GUILayout.Width(40)))
{
for (int i = 0; i < propData.maxTextures; ++i)
{
propData.SetValue(i, pixel, nv);
}
}
EditorGUILayout.EndHorizontal();
drawPertexToggle = true;
}
static protected void DrawPerTexPopUpNoToggle(int curIdx, int pixel, string keyword, MicroSplatKeywords keywords, MicroSplatPropData propData, Channel channel,
GUIContent label, GUIContent[] options, float[] values)
{
drawPertexToggle = false;
DrawPerTexPopUp(curIdx, pixel, keyword, keywords, propData, channel, label, options);
}
GUIStyle globalButtonPressedStyle = null;
static GUIContent globalButton = new GUIContent("G", "Make property driven by a global variable. Used to integrate with external weathering systems");
protected bool DrawGlobalToggle(string keyword, MicroSplatKeywords keywords)
{
bool b = keywords.IsKeywordEnabled(keyword);
if (globalButtonPressedStyle == null)
{
globalButtonPressedStyle = new GUIStyle(GUI.skin.label);
globalButtonPressedStyle.normal.background = new Texture2D(1, 1);
globalButtonPressedStyle.normal.background.SetPixel(0, 0, Color.yellow);
globalButtonPressedStyle.normal.background.Apply();
globalButtonPressedStyle.normal.textColor = Color.black;
}
bool pressed = (GUILayout.Button(globalButton, b ? globalButtonPressedStyle : GUI.skin.label, GUILayout.Width(14)));
if (keywords.IsKeywordEnabled ("_ISOBJECTSHADER"))
return b;
if (pressed)
{
if (b)
{
keywords.DisableKeyword(keyword);
}
else
{
keywords.EnableKeyword(keyword);
}
b = !b;
EditorUtility.SetDirty(keywords);
}
return b;
}
}
}

View File

@@ -0,0 +1,23 @@
Shader "%SHADERNAME%"
{
Properties
{
%PROPERTIES%
}
SubShader
{
%TAGS%
%PASSFORWARD%
%PASSFORWARDADD%
%PASSGBUFFER%
%PASSSHADOW%
%PASSMETA%
UsePass "Hidden/Nature/Terrain/Utilities/PICKING"
UsePass "Hidden/Nature/Terrain/Utilities/SELECTION"
}
%DEPENDENCY%
%FALLBACK%
%CUSTOMEDITOR%
}

View File

@@ -0,0 +1,515 @@
// If your looking in here and thinking WTF, yeah, I know. These are taken from the SRPs, to allow us to use the same
// texturing library they use. However, since they are not included in the standard pipeline by default, there is no
// way to include them in and they have to be inlined, since someone could copy this shader onto another machine without
// MicroSplat installed. Unfortunate, but I'd rather do this and have a nice library for texture sampling instead
// of the patchy one Unity provides being inlined/emulated in HDRP/URP. Strangely, PSSL and XBoxOne libraries are not
// included in the standard SRP code, but they are in tons of Unity own projects on the web, so I grabbed them from there.
#if defined(SHADER_API_GAMECORE)
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURECUBE(textureName) TextureCube textureName
#define TEXTURECUBE_ARRAY(textureName) TextureCubeArray textureName
#define TEXTURE3D(textureName) Texture3D textureName
#define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName)
#define TEXTURECUBE_FLOAT(textureName) TEXTURECUBE(textureName)
#define TEXTURECUBE_ARRAY_FLOAT(textureName) TEXTURECUBE_ARRAY(textureName)
#define TEXTURE3D_FLOAT(textureName) TEXTURE3D(textureName)
#define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName)
#define TEXTURECUBE_HALF(textureName) TEXTURECUBE(textureName)
#define TEXTURECUBE_ARRAY_HALF(textureName) TEXTURECUBE_ARRAY(textureName)
#define TEXTURE3D_HALF(textureName) TEXTURE3D(textureName)
#define TEXTURE2D_SHADOW(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_SHADOW(textureName) TEXTURE2D_ARRAY(textureName)
#define TEXTURECUBE_SHADOW(textureName) TEXTURECUBE(textureName)
#define TEXTURECUBE_ARRAY_SHADOW(textureName) TEXTURECUBE_ARRAY(textureName)
#define RW_TEXTURE2D(type, textureName) RWTexture2D<type> textureName
#define RW_TEXTURE2D_ARRAY(type, textureName) RWTexture2DArray<type> textureName
#define RW_TEXTURE3D(type, textureName) RWTexture3D<type> textureName
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define ASSIGN_SAMPLER(samplerName, samplerValue) samplerName = samplerValue
#define TEXTURE2D_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER(samplerName)
#define TEXTURE2D_ARRAY_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER(samplerName)
#define TEXTURECUBE_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER(samplerName)
#define TEXTURECUBE_ARRAY_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER(samplerName)
#define TEXTURE3D_PARAM(textureName, samplerName) TEXTURE3D(textureName), SAMPLER(samplerName)
#define TEXTURE2D_SHADOW_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER_CMP(samplerName)
#define TEXTURE2D_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER_CMP(samplerName)
#define TEXTURECUBE_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER_CMP(samplerName)
#define TEXTURECUBE_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER_CMP(samplerName)
#define TEXTURE2D_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURE2D_ARRAY_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURECUBE_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURECUBE_ARRAY_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURE3D_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURE2D_SHADOW_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURE2D_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURECUBE_SHADOW_ARGS(textureName, samplerName) textureName, samplerName
#define TEXTURECUBE_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName
#define PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy)
#define PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#define PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3)
#define PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod)
#define PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) textureName.SampleBias(samplerName, coord3, bias)
#define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Sample(samplerName, float4(coord3, index))
#define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) textureName.SampleLevel(samplerName, float4(coord3, index), lod)
#define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) textureName.SampleBias(samplerName, float4(coord3, index), bias)
#define PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3)
#define PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod)
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index)
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy)
#define SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3)
#define SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod)
#define SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias)
#define SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index)
#define SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod)
#define SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias)
#define SAMPLE_TEXTURE3D(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3)
#define SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod)
#define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z)
#define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z)
#define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord4).xyz, (coord4).w)
#define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w)
#define SAMPLE_DEPTH_TEXTURE(textureName, samplerName, coord2) SAMPLE_TEXTURE2D(textureName, samplerName, coord2).r
#define SAMPLE_DEPTH_TEXTURE_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod).r
#define LOAD_TEXTURE2D(textureName, unCoord2) textureName.Load(int3(unCoord2, 0))
#define LOAD_TEXTURE2D_LOD(textureName, unCoord2, lod) textureName.Load(int3(unCoord2, lod))
#define LOAD_TEXTURE2D_MSAA(textureName, unCoord2, sampleIndex) textureName.Load(unCoord2, sampleIndex)
#define LOAD_TEXTURE2D_ARRAY(textureName, unCoord2, index) textureName.Load(int4(unCoord2, index, 0))
#define LOAD_TEXTURE2D_ARRAY_MSAA(textureName, unCoord2, index, sampleIndex) textureName.Load(int3(unCoord2, index), sampleIndex)
#define LOAD_TEXTURE2D_ARRAY_LOD(textureName, unCoord2, index, lod) textureName.Load(int4(unCoord2, index, lod))
#define LOAD_TEXTURE3D(textureName, unCoord3) textureName.Load(int4(unCoord3, 0))
#define LOAD_TEXTURE3D_LOD(textureName, unCoord3, lod) textureName.Load(int4(unCoord3, lod))
#define PLATFORM_SUPPORT_GATHER
#define GATHER_TEXTURE2D(textureName, samplerName, coord2) textureName.Gather(samplerName, coord2)
#define GATHER_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Gather(samplerName, float3(coord2, index))
#define GATHER_TEXTURECUBE(textureName, samplerName, coord3) textureName.Gather(samplerName, coord3)
#define GATHER_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Gather(samplerName, float4(coord3, index))
#define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2)
#define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2)
#define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2)
#define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2)
#elif defined(SHADER_API_XBOXONE)
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName)
#define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName)
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_PSSL)
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.GetLOD(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName)
#define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName)
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_D3D11)
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName)
#define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName)
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_METAL)
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName
#define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define TEXTURE2D_HALF(textureName) Texture2D_half textureName
#define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_VULKAN)
// This file assume SHADER_API_VULKAN is defined
// TODO: This is a straight copy from D3D11.hlsl. Go through all this stuff and adjust where needed.
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName
#define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define TEXTURE2D_HALF(textureName) Texture2D_half textureName
#define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_SWITCH)
// This file assume SHADER_API_SWITCH is defined
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName
#define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define TEXTURE2D_HALF(textureName) Texture2D_half textureName
#define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_GLCORE)
// OpenGL 4.1 SM 5.0 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
#if (SHADER_TARGET >= 46)
#define OPENGL4_1_SM5 1
#else
#define OPENGL4_1_SM5 0
#endif
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName)
#define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName)
#define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName)
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_GLES3)
// GLES 3.1 + AEP shader feature https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
#if (SHADER_TARGET >= 40)
#define GLES3_1_AEP 1
#else
#define GLES3_1_AEP 0
#endif
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2)
// Texture abstraction
#define TEXTURE2D(textureName) Texture2D textureName
#define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName
#define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName
#define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define TEXTURE2D_HALF(textureName) Texture2D_half textureName
#define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias)
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index))
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy)
#elif defined(SHADER_API_GLES)
#define uint int
#define rcp(x) 1.0 / (x)
#define ddx_fine ddx
#define ddy_fine ddy
#define asfloat
#define asuint(x) asint(x)
#define f32tof16
#define f16tof32
#define ERROR_ON_UNSUPPORTED_FUNCTION(funcName) #error #funcName is not supported on GLES 2.0
// Initialize arbitrary structure with zero values.
// Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0
#define ZERO_INITIALIZE(type, name) name = (type)0;
#define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } }
// Texture util abstraction
#define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) #error calculate Level of Detail not supported in GLES2
// Texture abstraction
#define TEXTURE2D(textureName) sampler2D textureName
#define TEXTURE2D_ARRAY(textureName) samplerCUBE textureName // No support to texture2DArray
#define TEXTURECUBE(textureName) samplerCUBE textureName
#define TEXTURECUBE_ARRAY(textureName) samplerCUBE textureName // No supoport to textureCubeArray and can't emulate with texture2DArray
#define TEXTURE3D(textureName) sampler3D textureName
#define TEXTURE2D_FLOAT(textureName) sampler2D_float textureName
#define TEXTURECUBE_FLOAT(textureName) samplerCUBE_float textureName
#define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURECUBE_FLOAT(textureName) // No support to texture2DArray
#define TEXTURE2D_HALF(textureName) sampler2D_half textureName
#define TEXTURE2D_ARRAY_HALF(textureName) TEXTURECUBE_HALF(textureName) // No support to texture2DArray
#define SAMPLER(samplerName)
#define SAMPLER_CMP(samplerName)
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) tex2D(textureName, coord2)
#if (SHADER_TARGET >= 30)
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) tex2Dlod(textureName, float4(coord2, 0, lod))
#else
// No lod support. Very poor approximation with bias.
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, lod)
#endif
#define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) tex2Dbias(textureName, float4(coord2, 0, bias))
#define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) SAMPLE_TEXTURE2D(textureName, samplerName, coord2)
#define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY)
#define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_LOD)
#define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_BIAS)
#define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_GRAD)
#else
#error unsupported shader api
#endif
// default flow control attributes
#ifndef UNITY_BRANCH
# define UNITY_BRANCH
#endif
#ifndef UNITY_FLATTEN
# define UNITY_FLATTEN
#endif
#ifndef UNITY_UNROLL
# define UNITY_UNROLL
#endif
#ifndef UNITY_UNROLLX
# define UNITY_UNROLLX(_x)
#endif
#ifndef UNITY_LOOP
# define UNITY_LOOP
#endif

View File

@@ -0,0 +1,333 @@

Pass
{
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" }
%FORWARDBASEBLEND%
CGPROGRAM
// compile directives
%PRAGMAS%
#pragma target %SHADERTARGET%
#pragma multi_compile_instancing
#pragma multi_compile_local __ _ALPHATEST_ON
#pragma multi_compile_fog
#pragma multi_compile_fwdbase
#include "HLSLSupport.cginc"
#define _PASSFORWARD 1
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
// -------- variant for: <when no other keywords are defined>
%DEFINES%
#include "UnityCG.cginc"
#if _NOMINDIELETRIC
// for Standard
#ifdef unity_ColorSpaceDielectricSpec
#undef unity_ColorSpaceDielectricSpec
#endif
#define unity_ColorSpaceDielectricSpec half4(0,0,0,1)
#endif
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"
#if _MICROTERRAIN && !_TERRAINBLENDABLESHADER
#define UNITY_ASSUME_UNIFORM_SCALING
#define UNITY_DONT_INSTANCE_OBJECT_MATRICES
#define UNITY_INSTANCED_LOD_FADE
#else
#define UNITY_INSTANCED_LOD_FADE
#define UNITY_INSTANCED_SH
#define UNITY_INSTANCED_LIGHTMAPSTS
#endif
// data across stages, stripped like the above.
struct VertexToPixel
{
UNITY_POSITION(pos);
float3 worldPos : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float4 worldTangent : TEXCOORD2;
%UV0% float4 texcoord0 : TEXCCOORD3;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% float4 texcoord1 : TEXCCOORD4;
%UV2% float4 texcoord2 : TEXCCOORD5;
#endif
%UV3% float4 texcoord3 : TEXCCOORD6;
%SCREENPOS% float4 screenPos : TEXCOORD7;
%VERTEXCOLOR% float4 vertexColor : COLOR;
float4 lmap : TEXCOORD8;
#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD9; // SH
#endif
#ifdef LIGHTMAP_ON
UNITY_LIGHTING_COORDS(10,11)
UNITY_FOG_COORDS(12)
#else
UNITY_FOG_COORDS(10)
UNITY_SHADOW_COORDS(11)
#endif
%EXTRAV2F0% float4 extraV2F0 : TEXCOORD13;
%EXTRAV2F1% float4 extraV2F1 : TEXCOORD14;
%EXTRAV2F2% float4 extraV2F2 : TEXCOORD15;
%EXTRAV2F3% float4 extraV2F3 : TEXCOORD16;
%EXTRAV2F4% float4 extraV2F4 : TEXCOORD17;
%EXTRAV2F5% float4 extraV2F5 : TEXCOORD18;
%EXTRAV2F6% float4 extraV2F6 : TEXCOORD19;
%EXTRAV2F7% float4 extraV2F7 : TEXCOORD20;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
%TEMPLATE_SHARED%
%CBUFFER%
%CODE%
%SHADERDESC%
// vertex shader
VertexToPixel Vert (VertexData v)
{
UNITY_SETUP_INSTANCE_ID(v);
VertexToPixel o;
UNITY_INITIALIZE_OUTPUT(VertexToPixel,o);
UNITY_TRANSFER_INSTANCE_ID(v,o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
#if !_TESSELLATION_ON
ChainModifyVertex(v, o);
#endif
o.pos = UnityObjectToClipPos(v.vertex);
%UV0% o.texcoord0 = v.texcoord0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% o.texcoord1 = v.texcoord1;
%UV2% o.texcoord2 = v.texcoord2;
#endif
%UV3% o.texcoord3 = v.texcoord3;
%VERTEXCOLOR% o.vertexColor = v.vertexColor;
%SCREENPOS% o.screenPos = ComputeScreenPos(o.pos);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
o.worldTangent.w = tangentSign;
#endif
// MS Only
ApplyTerrainTangent(o);
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
float2 uv1 = v.texcoord1.xy;
float2 uv2 = v.texcoord2.xy;
#else
float2 uv1 = v.texcoord0.xy;
float2 uv2 = uv1;
#endif
#ifdef DYNAMICLIGHTMAP_ON
o.lmap.zw = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#endif
#ifdef LIGHTMAP_ON
o.lmap.xy = uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
#endif
// SH/ambient and vertex lights
#ifndef LIGHTMAP_ON
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
o.sh = 0;
// Approximated illumination from non-important point lights
#ifdef VERTEXLIGHT_ON
o.sh += Shade4PointLights (
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, o.worldPos, o.worldNormal);
#endif
o.sh = ShadeSHPerVertex (o.worldNormal, o.sh);
#endif
#endif // !LIGHTMAP_ON
UNITY_TRANSFER_LIGHTING(o, uv1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader
#ifdef FOG_COMBINED_WITH_TSPACE
UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,o.pos); // pass fog coordinates to pixel shader
#elif defined (FOG_COMBINED_WITH_WORLD_POS)
UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,o.pos); // pass fog coordinates to pixel shader
#else
UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
#endif
return o;
}
%TESSELLATION%
// fragment shader
fixed4 Frag (VertexToPixel IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
// prepare and unpack data
#ifdef FOG_COMBINED_WITH_TSPACE
UNITY_EXTRACT_FOG_FROM_TSPACE(IN);
#elif defined (FOG_COMBINED_WITH_WORLD_POS)
UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);
#else
UNITY_EXTRACT_FOG(IN);
#endif
ShaderData d = CreateShaderData(IN);
Surface l = (Surface)0;
l.Albedo = half3(0.5, 0.5, 0.5);
l.Normal = float3(0,0,1);
l.Occlusion = 1;
l.Alpha = 1;
ChainSurfaceFunction(l, d);
#ifndef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(d.worldSpacePosition));
#else
fixed3 lightDir = _WorldSpaceLightPos0.xyz;
#endif
// compute lighting & shadowing factor
UNITY_LIGHT_ATTENUATION(atten, IN, d.worldSpacePosition)
#if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandardSpecular o = (SurfaceOutputStandardSpecular)0;
#else
SurfaceOutputStandardSpecular o;
#endif
o.Specular = l.Specular;
#elif _BDRFLAMBERT || _BDRF3
#ifdef UNITY_COMPILER_HLSL
SurfaceOutput o = (SurfaceOutput)0;
#else
SurfaceOutput o;
#endif
#else
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
#else
SurfaceOutputStandard o;
#endif
o.Metallic = l.Metallic;
#endif
o.Albedo = l.Albedo;
o.Emission = l.Emission;
o.Alpha = l.Alpha;
o.Normal = normalize(TangentToWorldSpace(d, l.Normal));
#if _BDRFLAMBERT || _BDRF3
o.Specular = l.Specular;
o.Gloss = l.Smoothness;
#elif _SPECULARFROMMETALLIC
o.Occlusion = l.Occlusion;
o.Smoothness = l.Smoothness;
o.Albedo = MicroSplatDiffuseAndSpecularFromMetallic(l.Albedo, l.Metallic, o.Specular, o.Smoothness);
o.Smoothness = 1-o.Smoothness;
#elif _USESPECULARWORKFLOW
o.Occlusion = l.Occlusion;
o.Smoothness = l.Smoothness;
o.Specular = l.Specular;
#else
o.Smoothness = l.Smoothness;
o.Metallic = l.Metallic;
o.Occlusion = l.Occlusion;
#endif
#if !_UNLIT
fixed4 c = 0;
// Setup lighting environment
UnityGI gi;
UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = _LightColor0.rgb;
gi.light.dir = lightDir;
// Call GI (lightmaps/SH/reflections) lighting function
UnityGIInput giInput;
UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
giInput.light = gi.light;
giInput.worldPos = d.worldSpacePosition;
giInput.worldViewDir = d.worldSpaceViewDir;
giInput.atten = atten;
#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
giInput.lightmapUV = IN.lmap;
#else
giInput.lightmapUV = 0;
#endif
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.probeHDR[0] = unity_SpecCube0_HDR;
giInput.probeHDR[1] = unity_SpecCube1_HDR;
#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
#endif
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
giInput.boxMax[0] = unity_SpecCube0_BoxMax;
giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
giInput.boxMax[1] = unity_SpecCube1_BoxMax;
giInput.boxMin[1] = unity_SpecCube1_BoxMin;
giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
#endif
#if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC
LightingStandardSpecular_GI(o, giInput, gi);
c += LightingStandardSpecular (o, d.worldSpaceViewDir, gi);
#elif _BDRFLAMBERT
LightingLambert_GI(o, giInput, gi);
c += LightingLambert (o, gi);
#elif _BDRF3
LightingBlinnPhong_GI(o, giInput, gi);
c += LightingBlinnPhong (o, d.worldSpaceViewDir, gi);
#else
LightingStandard_GI(o, giInput, gi);
c += LightingStandard (o, d.worldSpaceViewDir, gi);
#endif
c.rgb += o.Emission;
UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog
#else
fixed4 c = fixed4(o.Albedo.rgb, o.Alpha);
UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog
#endif
#if !_ALPHABLEND_ON
UNITY_OPAQUE_ALPHA(c.a);
#endif
ChainFinalColorForward(l, d, c);
return c;
}
ENDCG
}

View File

@@ -0,0 +1,266 @@

// ---- forward rendering additive lights pass:
Pass
{
Name "FORWARD"
Tags { "LightMode" = "ForwardAdd" }
ZWrite Off Blend One One
%FORWARDADDBLEND%
CGPROGRAM
%PRAGMAS%
// compile directives
#pragma target %SHADERTARGET%
#pragma multi_compile_instancing
#pragma multi_compile_fog
#pragma multi_compile_local __ _ALPHATEST_ON
#pragma multi_compile_fwdadd_fullshadows
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
#define _PASSFORWARD 1
%DEFINES%
#include "UnityCG.cginc"
#if _NOMINDIELETRIC
// for Standard
#ifdef unity_ColorSpaceDielectricSpec
#undef unity_ColorSpaceDielectricSpec
#endif
#define unity_ColorSpaceDielectricSpec half4(0,0,0,1)
#endif
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"
#if _MICROTERRAIN && !_TERRAINBLENDABLESHADER
#define UNITY_ASSUME_UNIFORM_SCALING
#define UNITY_DONT_INSTANCE_OBJECT_MATRICES
#define UNITY_INSTANCED_LOD_FADE
#else
#define UNITY_INSTANCED_LOD_FADE
#define UNITY_INSTANCED_SH
#define UNITY_INSTANCED_LIGHTMAPSTS
#endif
// data across stages, stripped like the above.
struct VertexToPixel
{
UNITY_POSITION(pos); // must be named pos because Unity does stupid macro stuff
float3 worldPos : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float4 worldTangent : TEXCOORD2;
%UV0% float4 texcoord0 : TEXCCOORD3;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% float4 texcoord1 : TEXCCOORD4;
%UV2% float4 texcoord2 : TEXCCOORD5;
#endif
%UV3% float4 texcoord3 : TEXCCOORD6;
%SCREENPOS% float4 screenPos : TEXCOORD7;
%VERTEXCOLOR% float4 vertexColor : COLOR;
UNITY_LIGHTING_COORDS(8,9)
UNITY_FOG_COORDS(10)
%EXTRAV2F0% float4 extraV2F0 : TEXCOORD11;
%EXTRAV2F1% float4 extraV2F1 : TEXCOORD12;
%EXTRAV2F2% float4 extraV2F2 : TEXCOORD13;
%EXTRAV2F3% float4 extraV2F3 : TEXCOORD14;
%EXTRAV2F4% float4 extraV2F4 : TEXCOORD15;
%EXTRAV2F5% float4 extraV2F5 : TEXCOORD16;
%EXTRAV2F6% float4 extraV2F6 : TEXCOORD17;
%EXTRAV2F7% float4 extraV2F7 : TEXCOORD18;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
%TEMPLATE_SHARED%
%CBUFFER%
%CODE%
%SHADERDESC%
// vertex shader
VertexToPixel Vert (VertexData v)
{
UNITY_SETUP_INSTANCE_ID(v);
VertexToPixel o;
UNITY_INITIALIZE_OUTPUT(VertexToPixel,o);
UNITY_TRANSFER_INSTANCE_ID(v,o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
#if !_TESSELLATION_ON
ChainModifyVertex(v, o);
#endif
o.pos = UnityObjectToClipPos(v.vertex);
%UV0% o.texcoord0 = v.texcoord0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% o.texcoord1 = v.texcoord1;
%UV2% o.texcoord2 = v.texcoord2;
#endif
%UV3% o.texcoord3 = v.texcoord3;
%VERTEXCOLOR% o.vertexColor = v.vertexColor;
%SCREENPOS% o.screenPos = ComputeScreenPos(o.pos);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
o.worldTangent.w = tangentSign;
#endif
// MS Only
ApplyTerrainTangent(o);
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
float2 uv1 = v.texcoord1.xy;
float2 uv2 = v.texcoord2.xy;
#else
float2 uv1 = v.texcoord0.xy;
float2 uv2 = uv1;
#endif
UNITY_TRANSFER_LIGHTING(o,uv1); // pass shadow and, possibly, light cookie coordinates to pixel shader
UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
return o;
}
%TESSELLATION%
// fragment shader
fixed4 Frag (VertexToPixel IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
// prepare and unpack data
#ifdef FOG_COMBINED_WITH_TSPACE
UNITY_EXTRACT_FOG_FROM_TSPACE(IN);
#elif defined (FOG_COMBINED_WITH_WORLD_POS)
UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);
#else
UNITY_EXTRACT_FOG(IN);
#endif
ShaderData d = CreateShaderData(IN);
Surface l = (Surface)0;
l.Albedo = half3(0.5, 0.5, 0.5);
l.Normal = float3(0,0,1);
l.Occlusion = 1;
l.Alpha = 1;
ChainSurfaceFunction(l, d);
#ifndef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(d.worldSpacePosition));
#else
fixed3 lightDir = _WorldSpaceLightPos0.xyz;
#endif
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(d.worldSpacePosition));
#if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandardSpecular o = (SurfaceOutputStandardSpecular)0;
#else
SurfaceOutputStandardSpecular o;
#endif
o.Specular = l.Specular;
#elif _BDRFLAMBERT || _BDRF3
#ifdef UNITY_COMPILER_HLSL
SurfaceOutput o = (SurfaceOutput)0;
#else
SurfaceOutput o;
#endif
#else
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
#else
SurfaceOutputStandard o;
#endif
o.Metallic = l.Metallic;
#endif
o.Albedo = l.Albedo;
o.Emission = l.Emission;
o.Alpha = l.Alpha;
o.Normal = normalize(TangentToWorldSpace(d, l.Normal));
#if _BDRFLAMBERT || _BDRF3
o.Specular = l.Specular;
o.Gloss = l.Smoothness;
#elif _SPECULARFROMMETALLIC
o.Occlusion = l.Occlusion;
o.Smoothness = l.Smoothness;
o.Albedo = MicroSplatDiffuseAndSpecularFromMetallic(l.Albedo, l.Metallic, o.Specular, o.Smoothness);
o.Smoothness = 1-o.Smoothness;
#elif _USESPECULARWORKFLOW
o.Occlusion = l.Occlusion;
o.Smoothness = l.Smoothness;
o.Specular = l.Specular;
#else
o.Smoothness = l.Smoothness;
o.Metallic = l.Metallic;
o.Occlusion = l.Occlusion;
#endif
UNITY_LIGHT_ATTENUATION(atten, IN, d.worldSpacePosition)
half4 c = 0;
// Setup lighting environment
UnityGI gi;
UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = _LightColor0.rgb;
gi.light.dir = lightDir;
gi.light.color *= atten;
#if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC
c += LightingStandardSpecular (o, worldViewDir, gi);
#elif _BDRFLAMBERT
c += LightingLambert(o, gi);
c.a = 0;
#elif _BDRF3
c += LightingBlinnPhong (o, worldViewDir, gi);
#else
c += LightingStandard (o, worldViewDir, gi);
#endif
ChainFinalColorForward(l, d, c);
UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog
#if !_ALPHABLEND_ON
UNITY_OPAQUE_ALPHA(c.a);
#endif
return c;
}
ENDCG
}

View File

@@ -0,0 +1,352 @@

// ---- deferred shading pass:
Pass
{
Name "DEFERRED"
Tags { "LightMode" = "Deferred" }
CGPROGRAM
%PRAGMAS%
// compile directives
#pragma target %SHADERTARGET%
#pragma multi_compile_instancing
#pragma exclude_renderers nomrt
#pragma multi_compile_local __ _ALPHATEST_ON
#pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2
#pragma multi_compile_prepassfinal
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
#include "UnityCG.cginc"
#define _PASSGBUFFER 1
%DEFINES%
#if _NOMINDIELETRIC
// for Standard
#ifdef unity_ColorSpaceDielectricSpec
#undef unity_ColorSpaceDielectricSpec
#endif
#define unity_ColorSpaceDielectricSpec half4(0,0,0,1)
#endif
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#if _MICROTERRAIN && !_TERRAINBLENDABLESHADER
#define UNITY_ASSUME_UNIFORM_SCALING
#define UNITY_DONT_INSTANCE_OBJECT_MATRICES
#define UNITY_INSTANCED_LOD_FADE
#else
#define UNITY_INSTANCED_LOD_FADE
#define UNITY_INSTANCED_SH
#define UNITY_INSTANCED_LIGHTMAPSTS
#endif
// data across stages, stripped like the above.
struct VertexToPixel
{
UNITY_POSITION(pos); // must be named pos because Unity does stupid macro stuff
float3 worldPos : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float4 worldTangent : TEXCOORD2;
%UV0% float4 texcoord0 : TEXCCOORD3;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% float4 texcoord1 : TEXCCOORD4;
%UV2% float4 texcoord2 : TEXCCOORD5;
#endif
%UV3% float4 texcoord3 : TEXCCOORD6;
%SCREENPOS% float4 screenPos : TEXCOORD7;
%VERTEXCOLOR% float4 vertexColor : COLOR;
#ifndef DIRLIGHTMAP_OFF
float3 viewDir : TEXCOORD8;
#endif
float4 lmap : TEXCOORD9;
#ifndef LIGHTMAP_ON
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
half3 sh : TEXCOORD10; // SH
#endif
#else
#ifdef DIRLIGHTMAP_OFF
float4 lmapFadePos : TEXCOORD11;
#endif
#endif
%EXTRAV2F0% float4 extraV2F0 : TEXCOORD12;
%EXTRAV2F1% float4 extraV2F1 : TEXCOORD13;
%EXTRAV2F2% float4 extraV2F2 : TEXCOORD14;
%EXTRAV2F3% float4 extraV2F3 : TEXCOORD15;
%EXTRAV2F4% float4 extraV2F4 : TEXCOORD16;
%EXTRAV2F5% float4 extraV2F5 : TEXCOORD17;
%EXTRAV2F6% float4 extraV2F6 : TEXCOORD18;
%EXTRAV2F7% float4 extraV2F7 : TEXCOORD19;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
%TEMPLATE_SHARED%
%CBUFFER%
%CODE%
%SHADERDESC%
// vertex shader
VertexToPixel Vert (VertexData v)
{
UNITY_SETUP_INSTANCE_ID(v);
VertexToPixel o;
UNITY_INITIALIZE_OUTPUT(VertexToPixel,o);
UNITY_TRANSFER_INSTANCE_ID(v,o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
#if !_TESSELLATION_ON
ChainModifyVertex(v, o);
#endif
o.pos = UnityObjectToClipPos(v.vertex);
%UV0% o.texcoord0 = v.texcoord0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% o.texcoord1 = v.texcoord1;
%UV2% o.texcoord2 = v.texcoord2;
#endif
%UV3% o.texcoord3 = v.texcoord3;
%VERTEXCOLOR% o.vertexColor = v.vertexColor;
%SCREENPOS% o.screenPos = ComputeScreenPos(o.pos);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
float3 worldBinormal = cross(o.worldNormal, o.worldTangent.xyz) * tangentSign;
o.worldTangent.w = tangentSign;
#else
// MS Only
ApplyTerrainTangent(o);
float3 worldBinormal = cross(o.worldNormal, o.worldTangent.xyz) * unity_WorldTransformParams.w;
#endif
float3 viewDirForLight = UnityWorldSpaceViewDir(o.worldPos);
#ifndef DIRLIGHTMAP_OFF
o.viewDir.x = dot(viewDirForLight, o.worldTangent.xyz);
o.viewDir.y = dot(viewDirForLight, worldBinormal);
o.viewDir.z = dot(viewDirForLight, o.worldNormal);
#endif
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
float2 uv1 = v.texcoord1.xy;
float2 uv2 = v.texcoord2.xy;
#else
float2 uv1 = v.texcoord0.xy;
float2 uv2 = uv1;
#endif
#ifdef DYNAMICLIGHTMAP_ON
o.lmap.zw = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#else
o.lmap.zw = 0;
#endif
#ifdef LIGHTMAP_ON
o.lmap.xy = uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
#ifdef DIRLIGHTMAP_OFF
o.lmapFadePos.xyz = (mul(unity_ObjectToWorld, v.vertex).xyz - unity_ShadowFadeCenterAndType.xyz) * unity_ShadowFadeCenterAndType.w;
o.lmapFadePos.w = (-UnityObjectToViewPos(v.vertex).z) * (1.0 - unity_ShadowFadeCenterAndType.w);
#endif
#else
o.lmap.xy = 0;
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
o.sh = 0;
o.sh = ShadeSHPerVertex (o.worldNormal, o.sh);
#endif
#endif
return o;
}
%TESSELLATION%
#ifdef LIGHTMAP_ON
float4 unity_LightmapFade;
#endif
fixed4 unity_Ambient;
// fragment shader
void Frag (VertexToPixel IN,
out half4 outGBuffer0 : SV_Target0,
out half4 outGBuffer1 : SV_Target1,
out half4 outGBuffer2 : SV_Target2,
out half4 outEmission : SV_Target3
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
, out half4 outShadowMask : SV_Target4
#endif
)
{
UNITY_SETUP_INSTANCE_ID(IN);
// prepare and unpack data
#ifdef FOG_COMBINED_WITH_TSPACE
UNITY_EXTRACT_FOG_FROM_TSPACE(IN);
#elif defined (FOG_COMBINED_WITH_WORLD_POS)
UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);
#else
UNITY_EXTRACT_FOG(IN);
#endif
ShaderData d = CreateShaderData(IN);
Surface l = (Surface)0;
l.Albedo = half3(0.5, 0.5, 0.5);
l.Normal = float3(0,0,1);
l.Occlusion = 1;
l.Alpha = 1;
ChainSurfaceFunction(l, d);
#ifndef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(d.worldSpacePosition));
#else
fixed3 lightDir = _WorldSpaceLightPos0.xyz;
#endif
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(d.worldSpacePosition));
#if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandardSpecular o = (SurfaceOutputStandardSpecular)0;
#else
SurfaceOutputStandardSpecular o;
#endif
o.Specular = l.Specular;
#elif _BDRFLAMBERT || _BDRF3
#ifdef UNITY_COMPILER_HLSL
SurfaceOutput o = (SurfaceOutput)0;
#else
SurfaceOutput o;
#endif
#else
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
#else
SurfaceOutputStandard o;
#endif
o.Metallic = l.Metallic;
#endif
o.Albedo = l.Albedo;
o.Emission = l.Emission;
o.Alpha = l.Alpha;
o.Normal = normalize(TangentToWorldSpace(d, l.Normal));
#if _BDRFLAMBERT || _BDRF3
o.Specular = l.Occlusion;
o.Gloss = l.Smoothness;
#elif _SPECULARFROMMETALLIC
o.Occlusion = l.Occlusion;
o.Smoothness = l.Smoothness;
o.Albedo = MicroSplatDiffuseAndSpecularFromMetallic(l.Albedo, l.Metallic, o.Specular, o.Smoothness);
o.Smoothness = 1-o.Smoothness;
#elif _USESPECULARWORKFLOW
o.Occlusion = l.Occlusion;
o.Smoothness = l.Smoothness;
o.Specular = l.Specular;
#else
o.Smoothness = l.Smoothness;
o.Metallic = l.Metallic;
o.Occlusion = l.Occlusion;
#endif
half atten = 1;
// Setup lighting environment
UnityGI gi;
UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = 0;
gi.light.dir = half3(0,1,0);
// Call GI (lightmaps/SH/reflections) lighting function
UnityGIInput giInput;
UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
giInput.light = gi.light;
giInput.worldPos = d.worldSpacePosition;
giInput.worldViewDir = worldViewDir;
giInput.atten = atten;
#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
giInput.lightmapUV = IN.lmap;
#else
giInput.lightmapUV = 0.0;
#endif
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.probeHDR[0] = unity_SpecCube0_HDR;
giInput.probeHDR[1] = unity_SpecCube1_HDR;
#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
#endif
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
giInput.boxMax[0] = unity_SpecCube0_BoxMax;
giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
giInput.boxMax[1] = unity_SpecCube1_BoxMax;
giInput.boxMin[1] = unity_SpecCube1_BoxMin;
giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
#endif
#if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC
LightingStandardSpecular_GI(o, giInput, gi);
// call lighting function to output g-buffer
outEmission = LightingStandardSpecular_Deferred (o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2);
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
outShadowMask = UnityGetRawBakedOcclusions (IN.lmap.xy, d.worldSpacePosition);
#endif
#ifndef UNITY_HDR_ON
outEmission.rgb = exp2(-outEmission.rgb);
#endif
#else
LightingStandard_GI(o, giInput, gi);
// call lighting function to output g-buffer
outEmission = LightingStandard_Deferred (o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2);
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
outShadowMask = UnityGetRawBakedOcclusions (IN.lmap.xy, d.worldSpacePosition);
#endif
#ifndef UNITY_HDR_ON
outEmission.rgb = exp2(-outEmission.rgb);
#endif
#endif
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
ChainFinalGBufferStandard(l, d, outGBuffer0, outGBuffer1, outGBuffer2, outEmission, outShadowMask);
#else
half4 outShadowMask = 0;
ChainFinalGBufferStandard(l, d, outGBuffer0, outGBuffer1, outGBuffer2, outEmission, outShadowMask);
#endif
}
ENDCG
}

View File

@@ -0,0 +1,197 @@

// ---- meta information extraction pass:
Pass
{
Name "Meta"
Tags { "LightMode" = "Meta" }
Cull Off
CGPROGRAM
%PRAGMAS%
// compile directives
#pragma target %SHADERTARGET%
#pragma multi_compile_instancing
#pragma multi_compile_local __ _ALPHATEST_ON
#pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2
#pragma shader_feature EDITOR_VISUALIZATION
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
#include "UnityCG.cginc"
#define _PASSMETA 1
%DEFINES%
#if _NOMINDIELETRIC
// for Standard
#ifdef unity_ColorSpaceDielectricSpec
#undef unity_ColorSpaceDielectricSpec
#endif
#define unity_ColorSpaceDielectricSpec half4(0,0,0,1)
#endif
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "UnityMetaPass.cginc"
#if _MICROTERRAIN && !_TERRAINBLENDABLESHADER
#define UNITY_ASSUME_UNIFORM_SCALING
#define UNITY_DONT_INSTANCE_OBJECT_MATRICES
#define UNITY_INSTANCED_LOD_FADE
#else
#define UNITY_INSTANCED_LOD_FADE
#define UNITY_INSTANCED_SH
#define UNITY_INSTANCED_LIGHTMAPSTS
#endif
// data across stages, stripped like the above.
struct VertexToPixel
{
UNITY_POSITION(pos);
float3 worldPos : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float4 worldTangent : TEXCOORD2;
%UV0% float4 texcoord0 : TEXCCOORD3;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% float4 texcoord1 : TEXCCOORD4;
%UV2% float4 texcoord2 : TEXCCOORD5;
#endif
%UV3% float4 texcoord3 : TEXCCOORD6;
%SCREENPOS% float4 screenPos : TEXCOORD7;
%VERTEXCOLOR% float4 vertexColor : COLOR;
#ifdef EDITOR_VISUALIZATION
float2 vizUV : TEXCOORD8;
float4 lightCoord : TEXCOORD9;
#endif
%EXTRAV2F0% float4 extraV2F0 : TEXCOORD10;
%EXTRAV2F1% float4 extraV2F1 : TEXCOORD11;
%EXTRAV2F2% float4 extraV2F2 : TEXCOORD12;
%EXTRAV2F3% float4 extraV2F3 : TEXCOORD13;
%EXTRAV2F4% float4 extraV2F4 : TEXCOORD14;
%EXTRAV2F5% float4 extraV2F5 : TEXCOORD15;
%EXTRAV2F6% float4 extraV2F6 : TEXCOORD16;
%EXTRAV2F7% float4 extraV2F7 : TEXCOORD17;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
%TEMPLATE_SHARED%
%CBUFFER%
%CODE%
%SHADERDESC%
// vertex shader
VertexToPixel Vert (VertexData v)
{
UNITY_SETUP_INSTANCE_ID(v);
VertexToPixel o;
UNITY_INITIALIZE_OUTPUT(VertexToPixel,o);
UNITY_TRANSFER_INSTANCE_ID(v,o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
#if !_TESSELLATION_ON
ChainModifyVertex(v, o);
#endif
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
float2 uv1 = v.texcoord1.xy;
float2 uv2 = v.texcoord2.xy;
#else
float2 uv1 = v.texcoord0.xy;
float2 uv2 = uv1;
#endif
o.pos = UnityMetaVertexPosition(v.vertex, uv1, uv2, unity_LightmapST, unity_DynamicLightmapST);
#ifdef EDITOR_VISUALIZATION
o.vizUV = 0;
o.lightCoord = 0;
if (unity_VisualizationMode == EDITORVIZ_TEXTURE)
o.vizUV = UnityMetaVizUV(unity_EditorViz_UVIndex, v.texcoord0.xy, uv1, uv2, unity_EditorViz_Texture_ST);
else if (unity_VisualizationMode == EDITORVIZ_SHOWLIGHTMASK)
{
o.vizUV = uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
o.lightCoord = mul(unity_EditorViz_WorldToLight, mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1)));
}
#endif
%UV0% o.texcoord0 = v.texcoord0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% o.texcoord1 = v.texcoord1;
%UV2% o.texcoord2 = v.texcoord2;
#endif
%UV3% o.texcoord3 = v.texcoord3;
%VERTEXCOLOR% o.vertexColor = v.vertexColor;
%SCREENPOS% o.screenPos = ComputeScreenPos(o.pos);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
o.worldTangent.w = tangentSign;
#endif
// MS Only
ApplyTerrainTangent(o);
return o;
}
%TESSELLATION%
// fragment shader
fixed4 Frag (VertexToPixel IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
#ifdef FOG_COMBINED_WITH_TSPACE
UNITY_EXTRACT_FOG_FROM_TSPACE(IN);
#elif defined (FOG_COMBINED_WITH_WORLD_POS)
UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);
#else
UNITY_EXTRACT_FOG(IN);
#endif
ShaderData d = CreateShaderData(IN);
Surface l = (Surface)0;
l.Albedo = half3(0.5, 0.5, 0.5);
l.Normal = float3(0,0,1);
l.Occlusion = 1;
l.Alpha = 1;
ChainSurfaceFunction(l, d);
UnityMetaInput metaIN;
UNITY_INITIALIZE_OUTPUT(UnityMetaInput, metaIN);
metaIN.Albedo = l.Albedo;
metaIN.Emission = l.Emission;
#if _USESPECULAR
metaIN.SpecularColor = l.Specular;
#endif
#ifdef EDITOR_VISUALIZATION
metaIN.VizUV = IN.vizUV;
metaIN.LightCoord = IN.lightCoord;
#endif
return UnityMetaFragment(metaIN);
}
ENDCG
}

View File

@@ -0,0 +1,155 @@

Pass {
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
ZWrite On ZTest LEqual
CGPROGRAM
%PRAGMAS%
// compile directives
#pragma target %SHADERTARGET%
#pragma multi_compile_instancing
#pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2
#pragma multi_compile_shadowcaster
#pragma multi_compile_local __ _ALPHATEST_ON
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#define _PASSSHADOW 1
%DEFINES%
#if _MICROTERRAIN && !_TERRAINBLENDABLESHADER
#define UNITY_ASSUME_UNIFORM_SCALING
#define UNITY_DONT_INSTANCE_OBJECT_MATRICES
#define UNITY_INSTANCED_LOD_FADE
#else
#define UNITY_INSTANCED_LOD_FADE
#define UNITY_INSTANCED_SH
#define UNITY_INSTANCED_LIGHTMAPSTS
#endif
// data across stages, stripped like the above.
struct VertexToPixel
{
V2F_SHADOW_CASTER;
float3 worldPos : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float4 worldTangent : TEXCOORD2;
%UV0% float4 texcoord0 : TEXCCOORD3;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% float4 texcoord1 : TEXCCOORD4;
%UV2% float4 texcoord2 : TEXCCOORD5;
#endif
%UV3% float4 texcoord3 : TEXCCOORD6;
%SCREENPOS% float4 screenPos : TEXCOORD7;
%VERTEXCOLOR% float4 vertexColor : COLOR;
%EXTRAV2F0% float4 extraV2F0 : TEXCOORD8;
%EXTRAV2F1% float4 extraV2F1 : TEXCOORD9;
%EXTRAV2F2% float4 extraV2F2 : TEXCOORD10;
%EXTRAV2F3% float4 extraV2F3 : TEXCOORD11;
%EXTRAV2F4% float4 extraV2F4 : TEXCOORD12;
%EXTRAV2F5% float4 extraV2F5 : TEXCOORD13;
%EXTRAV2F6% float4 extraV2F6 : TEXCOORD14;
%EXTRAV2F7% float4 extraV2F7 : TEXCOORD15;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
%TEMPLATE_SHARED%
%CBUFFER%
%CODE%
%SHADERDESC%
// vertex shader
VertexToPixel Vert (VertexData v)
{
UNITY_SETUP_INSTANCE_ID(v);
VertexToPixel o;
UNITY_INITIALIZE_OUTPUT(VertexToPixel,o);
UNITY_TRANSFER_INSTANCE_ID(v,o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
#if !_TESSELLATION_ON
ChainModifyVertex(v, o);
#endif
%UV0% o.texcoord0 = v.texcoord0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% o.texcoord1 = v.texcoord1;
%UV2% o.texcoord2 = v.texcoord2;
#endif
%UV3% o.texcoord3 = v.texcoord3;
%VERTEXCOLOR% o.vertexColor = v.vertexColor;
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
o.worldTangent.w = tangentSign;
#endif
// MS Only
ApplyTerrainTangent(o);
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
%SCREENPOS% o.screenPos = ComputeScreenPos(o.pos);
return o;
}
%TESSELLATION%
// fragment shader
fixed4 Frag (VertexToPixel IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
// prepare and unpack data
#ifdef FOG_COMBINED_WITH_TSPACE
UNITY_EXTRACT_FOG_FROM_TSPACE(IN);
#elif defined (FOG_COMBINED_WITH_WORLD_POS)
UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);
#else
UNITY_EXTRACT_FOG(IN);
#endif
#ifndef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(IN.worldPos));
#else
fixed3 lightDir = _WorldSpaceLightPos0.xyz;
#endif
ShaderData d = CreateShaderData(IN);
Surface l = (Surface)0;
l.Albedo = half3(0.5, 0.5, 0.5);
l.Normal = float3(0,0,1);
l.Occlusion = 1;
l.Alpha = 1;
ChainSurfaceFunction(l, d);
SHADOW_CASTER_FRAGMENT(IN)
}
ENDCG
}

View File

@@ -0,0 +1,72 @@

#if _MESHSUBARRAY
half4 _MeshSubArrayIndexes;
#endif
float4 _Diffuse_TexelSize;
float4 _NormalSAO_TexelSize;
#if _HYBRIDHEIGHTBLEND
float _HybridHeightBlendDistance;
#endif
#if _PACKINGHQ
float4 _SmoothAO_TexelSize;
#endif
#ifdef _ALPHATEST_ON
float4 _TerrainHolesTexture_TexelSize;
#endif
#if _USESPECULARWORKFLOW
float4 _Specular_TexelSize;
#endif
#if _USEEMISSIVEMETAL
float4 _EmissiveMetal_TexelSize;
#endif
#if _USEEMISSIVEMETAL
half _EmissiveMult;
#endif
#if _AUTONORMAL
half _AutoNormalHeightScale;
#endif
float4 _UVScale; // scale and offset
half _Contrast;
#if _VSSHADOWMAP
float4 gVSSunDirection;
#endif
#if _FORCELOCALSPACE && _PLANETVECTORS
float4x4 _PQSToLocal;
#endif
#if _ORIGINSHIFT
float4x4 _GlobalOriginMTX;
#endif
float4 _Control0_TexelSize;
#if _CUSTOMSPLATTEXTURES
float4 _CustomControl0_TexelSize;
#endif
float4 _PerPixelNormal_TexelSize;
#if _CONTROLNOISEUV || _GLOBALNOISEUV
float2 _NoiseUVParams;
#endif
float4 _PerTexProps_TexelSize;
#if _SURFACENORMALS
float3 surfTangent;
float3 surfBitangent;
float3 surfNormal;
#endif

View File

@@ -0,0 +1,16 @@

// Splats
[NoScaleOffset]_Diffuse ("Diffuse Array", 2DArray) = "white" {}
[NoScaleOffset]_NormalSAO ("Normal Array", 2DArray) = "bump" {}
[NoScaleOffset]_PerTexProps("Per Texture Properties", 2D) = "black" {}
[HideInInspector] _TerrainHolesTexture("Holes Map (RGB)", 2D) = "white" {}
[HideInInspector] _PerPixelNormal("Per Pixel Normal", 2D) = "bump" {}
_Contrast("Blend Contrast", Range(0.01, 0.99)) = 0.4
_UVScale("UV Scales", Vector) = (45, 45, 0, 0)
// for Unity 2020.3 bug
_MainTex("Unity Bug", 2D) = "white" {}
_TerrainHeightmapTexture("", 2D) = "black" {}
_TerrainNormalmapTexture("", 2D) = "bump" {}

View File

@@ -0,0 +1,11 @@

#undef WorldNormalVector
#define WorldNormalVector(data, normal) mul(normal, data.TBN)

View File

@@ -0,0 +1,51 @@
// CHAINS
void ChainModifyVertex(inout VertexData v, inout VertexToPixel v2p)
{
ExtraV2F d = (ExtraV2F)0;
ModifyVertex(v, d);
%EXTRAV2F0% v2p.extraV2F0 = d.extraV2F0;
%EXTRAV2F1% v2p.extraV2F1 = d.extraV2F1;
%EXTRAV2F2% v2p.extraV2F2 = d.extraV2F2;
%EXTRAV2F3% v2p.extraV2F3 = d.extraV2F3;
%EXTRAV2F4% v2p.extraV2F4 = d.extraV2F4;
%EXTRAV2F5% v2p.extraV2F5 = d.extraV2F5;
%EXTRAV2F6% v2p.extraV2F6 = d.extraV2F6;
%EXTRAV2F7% v2p.extraV2F7 = d.extraV2F7;
}
void ChainModifyTessellatedVertex(inout VertexData v, inout VertexToPixel v2p)
{
ExtraV2F d = (ExtraV2F)0;
%EXTRAV2F0% d.extraV2F0 = v2p.extraV2F0;
%EXTRAV2F1% d.extraV2F1 = v2p.extraV2F1;
%EXTRAV2F2% d.extraV2F2 = v2p.extraV2F2;
%EXTRAV2F3% d.extraV2F3 = v2p.extraV2F3;
%EXTRAV2F4% d.extraV2F4 = v2p.extraV2F4;
%EXTRAV2F5% d.extraV2F5 = v2p.extraV2F5;
%EXTRAV2F6% d.extraV2F6 = v2p.extraV2F6;
%EXTRAV2F7% d.extraV2F7 = v2p.extraV2F7;
ModifyTessellatedVertex(v, d);
%EXTRAV2F0% v2p.extraV2F0 = d.extraV2F0;
%EXTRAV2F1% v2p.extraV2F1 = d.extraV2F1;
%EXTRAV2F2% v2p.extraV2F2 = d.extraV2F2;
%EXTRAV2F3% v2p.extraV2F3 = d.extraV2F3;
%EXTRAV2F4% v2p.extraV2F4 = d.extraV2F4;
%EXTRAV2F5% v2p.extraV2F5 = d.extraV2F5;
%EXTRAV2F6% v2p.extraV2F6 = d.extraV2F6;
%EXTRAV2F7% v2p.extraV2F7 = d.extraV2F7;
}
void ChainFinalColorForward(inout Surface l, inout ShaderData d, inout half4 color)
{
}
void ChainFinalGBufferStandard(inout Surface s, inout ShaderData d, inout half4 GBuffer0, inout half4 GBuffer1, inout half4 GBuffer2, inout half4 outEmission, inout half4 outShadowMask)
{
}

View File

@@ -0,0 +1,47 @@
// SHADERDESC
ShaderData CreateShaderData(VertexToPixel i)
{
ShaderData d = (ShaderData)0;
d.worldSpacePosition = i.worldPos;
d.worldSpaceNormal = i.worldNormal;
d.worldSpaceTangent = i.worldTangent.xyz;
float3 bitangent = cross(i.worldTangent.xyz, i.worldNormal) * i.worldTangent.w * -1;
d.TBNMatrix = float3x3(d.worldSpaceTangent, bitangent, d.worldSpaceNormal);
d.worldSpaceViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix);
%UV0% d.texcoord0 = i.texcoord0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
%UV1% d.texcoord1 = i.texcoord1;
%UV2% d.texcoord2 = i.texcoord2;
#endif
%UV3% d.texcoord3 = i.texcoord3;
%VERTEXCOLOR% d.vertexColor = i.vertexColor;
// these rarely get used, so we back transform them. Usually will be stripped.
#if _HDRP
%LOCALSPACEPOSITION% d.localSpacePosition = mul(unity_WorldToObject, float4(GetCameraRelativePositionWS(i.worldPos), 1));
#else
%LOCALSPACEPOSITION% d.localSpacePosition = mul(unity_WorldToObject, float4(i.worldPos, 1));
#endif
%LOCALSPACENORMAL% d.localSpaceNormal = normalize(mul((float3x3)unity_WorldToObject, i.worldNormal));
%LOCALSPACETANGENT% d.localSpaceTangent = normalize(mul((float3x3)unity_WorldToObject, i.worldTangent.xyz));
%SCREENPOS% d.screenPos = i.screenPos;
%SCREENPOS% d.screenUV = i.screenPos.xy / i.screenPos.w;
%EXTRAV2F0% d.extraV2F0 = i.extraV2F0;
%EXTRAV2F1% d.extraV2F1 = i.extraV2F1;
%EXTRAV2F2% d.extraV2F2 = i.extraV2F2;
%EXTRAV2F3% d.extraV2F3 = i.extraV2F3;
%EXTRAV2F4% d.extraV2F4 = i.extraV2F4;
%EXTRAV2F5% d.extraV2F5 = i.extraV2F5;
%EXTRAV2F6% d.extraV2F6 = i.extraV2F6;
%EXTRAV2F7% d.extraV2F7 = i.extraV2F7;
return d;
}

View File

@@ -0,0 +1,229 @@
// TEMPLATE_SHARED
// data describing the user output of a pixel
struct Surface
{
half3 Albedo;
half Height;
half3 Normal;
half Smoothness;
half3 Emission;
half Metallic;
half3 Specular;
half Occlusion;
half Alpha;
// HDRP Only
half SpecularOcclusion;
half SubsurfaceMask;
half Thickness;
half CoatMask;
half Anisotropy;
half IridescenceMask;
half IridescenceThickness;
};
// data the user might need, this will grow to be big. But easy to strip
struct ShaderData
{
float3 localSpacePosition;
float3 localSpaceNormal;
float3 localSpaceTangent;
float3 worldSpacePosition;
float3 worldSpaceNormal;
float3 worldSpaceTangent;
float3 worldSpaceViewDir;
float3 tangentSpaceViewDir;
float4 texcoord0;
float4 texcoord1;
float4 texcoord2;
float4 texcoord3;
float2 screenUV;
float4 screenPos;
float4 vertexColor;
float4 extraV2F0;
float4 extraV2F1;
float4 extraV2F2;
float4 extraV2F3;
float4 extraV2F4;
float4 extraV2F5;
float4 extraV2F6;
float4 extraV2F7;
float3x3 TBNMatrix;
};
struct VertexData
{
#if SHADER_TARGET > 30 && _PLANETCOMPUTE
// %VERTEXID% uint vertexID : SV_VertexID;
#endif
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord0 : TEXCOORD0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
float4 tangent : TANGENT;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
#endif
%UV3% float4 texcoord3 : TEXCOORD3;
%VERTEXCOLOR% float4 vertexColor : COLOR;
#if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR)))
float3 previousPositionOS : TEXCOORD4; // Contain previous transform position (in case of skinning for example)
#if defined (_ADD_PRECOMPUTED_VELOCITY)
float3 precomputedVelocity : TEXCOORD5; // Add Precomputed Velocity (Alembic computes velocities on runtime side).
#endif
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct TessVertex
{
float4 vertex : INTERNALTESSPOS;
float3 normal : NORMAL;
float4 texcoord0 : TEXCOORD0;
#if !_MICROTERRAIN || _TERRAINBLENDABLESHADER
float4 tangent : TANGENT;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
#endif
%UV3% float4 texcoord3 : TEXCOORD3;
%VERTEXCOLOR% float4 vertexColor : COLOR;
%EXTRAV2F0% float4 extraV2F0 : TEXCOORD4;
%EXTRAV2F1% float4 extraV2F1 : TEXCOORD5;
%EXTRAV2F2% float4 extraV2F2 : TEXCOORD6;
%EXTRAV2F3% float4 extraV2F3 : TEXCOORD7;
%EXTRAV2F4% float4 extraV2F4 : TEXCOORD8;
%EXTRAV2F5% float4 extraV2F5 : TEXCOORD9;
%EXTRAV2F6% float4 extraV2F6 : TEXCOORD10;
%EXTRAV2F7% float4 extraV2F7 : TEXCOORD11;
#if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR)))
float3 previousPositionOS : TEXCOORD12; // Contain previous transform position (in case of skinning for example)
#if defined (_ADD_PRECOMPUTED_VELOCITY)
float3 precomputedVelocity : TEXCOORD13;
#endif
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
struct ExtraV2F
{
float4 extraV2F0;
float4 extraV2F1;
float4 extraV2F2;
float4 extraV2F3;
float4 extraV2F4;
float4 extraV2F5;
float4 extraV2F6;
float4 extraV2F7;
};
float3 WorldToTangentSpace(ShaderData d, float3 normal)
{
return mul(d.TBNMatrix, normal);
}
float3 TangentToWorldSpace(ShaderData d, float3 normal)
{
return mul(normal, d.TBNMatrix);
}
// in this case, make standard more like SRPs, because we can't fix
// unity_WorldToObject in HDRP, since it already does macro-fu there
#if _STANDARD
float3 TransformWorldToObject(float3 p) { return mul(unity_WorldToObject, float4(p, 1)); };
float3 TransformObjectToWorld(float3 p) { return mul(unity_ObjectToWorld, float4(p, 1)); };
float4 TransformWorldToObject(float4 p) { return mul(unity_WorldToObject, p); };
float4 TransformObjectToWorld(float4 p) { return mul(unity_ObjectToWorld, p); };
float4x4 GetWorldToObjectMatrix() { return unity_WorldToObject; }
float4x4 GetObjectToWorldMatrix() { return unity_ObjectToWorld; }
#endif
float3 GetCameraWorldPosition()
{
#if _HDRP
return GetCameraRelativePositionWS(_WorldSpaceCameraPos);
#else
return _WorldSpaceCameraPos;
#endif
}
#if _HDRP
half3 UnpackNormalmapRGorAG(half4 packednormal)
{
// This do the trick
packednormal.x *= packednormal.w;
half3 normal;
normal.xy = packednormal.xy * 2 - 1;
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return normal;
}
half3 UnpackNormal(half4 packednormal)
{
#if defined(UNITY_NO_DXT5nm)
return packednormal.xyz * 2 - 1;
#else
return UnpackNormalmapRGorAG(packednormal);
#endif
}
#endif
#if _HDRP || _URP
half3 UnpackScaleNormal(half4 packednormal, half scale)
{
#ifndef UNITY_NO_DXT5nm
// Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1)
// Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5
packednormal.x *= packednormal.w;
#endif
half3 normal;
normal.xy = (packednormal.xy * 2 - 1) * scale;
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return normal;
}
#endif
void GetSun(out float3 lightDir, out float3 color)
{
lightDir = float3(0.5, 0.5, 0);
color = 1;
#if _HDRP
if (_DirectionalLightCount > 0)
{
DirectionalLightData light = _DirectionalLightDatas[0];
lightDir = -light.forward.xyz;
color = light.color;
}
#elif _STANDARD
lightDir = normalize(_WorldSpaceLightPos0.xyz);
color = _LightColor0.rgb;
#elif _URP
Light light = GetMainLight();
lightDir = light.direction;
color = light.color;
#endif
}

View File

@@ -0,0 +1,103 @@

void SurfaceFunction(inout Surface o, inout ShaderData d)
{
float3 worldNormalVertex = d.worldSpaceNormal;
#if _MICROVERSEPREVIEW
float2 sampleCoords = d.texcoord0.xy;
#if _TOONHARDEDGENORMAL
sampleCoords = ToonEdgeUV(d.texcoord0.xy);
#endif
float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1);
float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1)));
float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1;
worldNormalVertex = geomNormal;
d.worldSpaceNormal = geomNormal;
d.worldSpaceTangent = geomTangent;
d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal);
d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix);
#elif (defined(UNITY_INSTANCING_ENABLED) && _MICROTERRAIN && !_TERRAINBLENDABLESHADER)
float2 sampleCoords = (d.texcoord0.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy;
#if _TOONHARDEDGENORMAL
sampleCoords = ToonEdgeUV(d.texcoord0.xy);
#endif
float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1);
float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1)));
float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1;
worldNormalVertex = geomNormal;
d.worldSpaceNormal = geomNormal;
d.worldSpaceTangent = geomTangent;
d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal);
d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix);
#elif _PERPIXNORMAL && (_MICROTERRAIN || _MICROMESHTERRAIN) && !_TERRAINBLENDABLESHADER
float2 sampleCoords = (d.texcoord0.xy * _PerPixelNormal_TexelSize.zw + 0.5f) * _PerPixelNormal_TexelSize.xy;
#if _TOONHARDEDGENORMAL
sampleCoords = ToonEdgeUV(d.texcoord0.xy);
#endif
float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_PerPixelNormal, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1);
float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1)));
float3 geomBitangent = normalize(cross(geomTangent, geomNormal)) * -1;
#if _MICROMESHTERRAIN
geomBitangent *= -1;
#endif
worldNormalVertex = geomNormal;
d.worldSpaceNormal = geomNormal;
d.worldSpaceTangent = geomTangent;
d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal);
d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix);
#endif
#if _TOONPOLYEDGE
FlatShade(d);
#endif
Input i = DescToInput(d);
#if _SRPTERRAINBLEND
MicroSplatLayer l = BlendWithTerrain(d);
#if _DEBUG_WORLDNORMAL
ClearAllButAlbedo(l, normalize(TangentToWorldSpace(d, l.Normal)) * saturate(l.Albedo.z+1));
#endif
#else
MicroSplatLayer l = SurfImpl(i, worldNormalVertex);
#endif
DoDebugOutput(l);
o.Albedo = l.Albedo;
o.Normal = l.Normal;
o.Smoothness = l.Smoothness;
o.Occlusion = l.Occlusion;
o.Metallic = l.Metallic;
o.Emission = l.Emission;
#if _USESPECULARWORKFLOW
o.Specular = l.Specular;
#endif
o.Height = l.Height;
o.Alpha = l.Alpha;
}

View File

@@ -0,0 +1,41 @@

Input DescToInput(ShaderData IN)
{
Input s = (Input)0;
s.shaderData = IN;
s.TBN = IN.TBNMatrix;
s.worldNormal = IN.worldSpaceNormal;
s.worldPos = IN.worldSpacePosition;
s.viewDir = IN.tangentSpaceViewDir;
s.uv_Control0 = IN.texcoord0.xy;
s.worldUpVector = float3(0,1,0);
s.worldHeight = IN.worldSpacePosition.y;
#if _PLANETVECTORS
float3 rwp = mul(_PQSToLocal, float4(IN.worldSpacePosition, 1));
s.worldHeight = distance(rwp, float3(0,0,0));
s.worldUpVector = normalize(rwp);
#endif
#if _MICROMESH && _MESHUV2
s.uv2_Diffuse = IN.texcoord1.xy;
#endif
#if _MEGASPLAT
UnpackMegaSplat(s, IN);
#endif
#if _MICROVERTEXMESH || _MICRODIGGERMESH
UnpackVertexWorkflow(s, IN);
#endif
#if _PLANETVECTORS
DoPlanetDataInputCopy(s, IN);
#endif
return s;
}

View File

@@ -0,0 +1,114 @@

float4 ConstructTerrainTangent(float3 normal, float3 positiveZ)
{
// Consider a flat terrain. It should have tangent be (1, 0, 0) and bitangent be (0, 0, 1) as the UV of the terrain grid mesh is a scale of the world XZ position.
// In CreateTangentToWorld function (in SpaceTransform.hlsl), it is cross(normal, tangent) * sgn for the bitangent vector.
// It is not true in a left-handed coordinate system for the terrain bitangent, if we provide 1 as the tangent.w. It would produce (0, 0, -1) instead of (0, 0, 1).
// Also terrain's tangent calculation was wrong in a left handed system because cross((0,0,1), terrainNormalOS) points to the wrong direction as negative X.
// Therefore all the 4 xyzw components of the tangent needs to be flipped to correct the tangent frame.
// (See TerrainLitData.hlsl - GetSurfaceAndBuiltinData)
float3 tangent = normalize(cross(normal, positiveZ));
return float4(tangent, -1);
}
void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv)
{
#if _MICROTERRAIN && defined(UNITY_INSTANCING_ENABLED) && !_TERRAINBLENDABLESHADER
float2 patchVertex = vertex.xy;
float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData);
float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale
uv = sampleCoords * _TerrainHeightmapRecipSize.zw;
float2 sampleUV = (uv / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy;
float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleUV, 0));
vertex.xz = sampleCoords * _TerrainHeightmapScale.xz;
vertex.y = height * _TerrainHeightmapScale.y;
normal = float3(0, 1, 0);
#endif
}
void ApplyMeshModification(inout VertexData input)
{
#if _MICROTERRAIN && !_TERRAINBLENDABLESHADER
float2 uv = input.texcoord0.xy;
TerrainInstancing(input.vertex, input.normal, uv);
input.texcoord0.xy = uv;
#endif
#if _PERPIXNORMAL && !_TERRAINBLENDABLESHADER
input.normal = float3(0,1,0);
#endif
#if _MICROVERSEPREVIEW
float4 recipSize = _TerrainHeightmapTexture_TexelSize;
recipSize.zw = (1.0f / (_TerrainHeightmapTexture_TexelSize.zw-1));
float2 sampleCoords = (input.texcoord0.xy / recipSize.zw + 0.5f) * recipSize.xy;
float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleCoords, 0));
input.vertex.xyz += float3(0,1,0) * height * _TerrainHeight * 2;
#endif
}
// called by the template, so we can remove tangent from VertexData
void ApplyTerrainTangent(inout VertexToPixel input)
{
#if (_MICROTERRAIN || _PERPIXNORMAL) && !_TERRAINBLENDABLESHADER
input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1));
#endif
// digger meshes ain't got no tangent either..
#if _MICRODIGGERMESH && !_TERRAINBLENDABLESHADER
input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1));
#endif
}
void ModifyVertex(inout VertexData v, inout ExtraV2F d)
{
ApplyMeshModification(v);
#if _MICROVERTEXMESH || _MICRODIGGERMESH
EncodeVertexWorkflow(v, d);
#elif _MEGASPLAT
EncodeMegaSplatVertex(v, d);
#endif
}
void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d)
{
#if _MICROVERSEPREVIEW
v.vertex.y = OffsetVertex(v, d).y;
#elif _TESSDISTANCE || _TESSEDGE
v.vertex.xyz += OffsetVertex(v, d);
#endif
}
float3 GetTessFactors ()
{
#if _TESSEDGE
return float3(_TessData1.x, _TessData1.w, 0);
#endif
#if _TESSDISTANCE
return float3(_TessData2.x, _TessData2.y, _TessData1.x);
#endif
return 0;
}

View File

@@ -0,0 +1,38 @@
// This contains functions that the MicroSplat compiler explicitly strips so that we
// don't add more cruft to the v2f structure. I'm not crazy about the way this is done,
// but without BS having a proper code parser this works.. Note that in most cases,
// the module system naturally doesn't add code it's not using to the shader, so
// the only stuff that has to go here is stuff that happens in the vertex stage.
void UnpackVertexWorkflow(inout Input i, ShaderData d)
{
i.w0.xy = d.texcoord0.zw;
i.w0.zw = d.texcoord1.zw;
%MAX8% i.w1 = d.vertexColor;
%MAX12% i.w2 = d.extraV2F0;
%MAX16% i.w3 = d.extraV2F1;
%MAX20% i.w4 = d.extraV2F2;
%MAX24% i.w5 = d.extraV2F3;
%MAX28% i.w6 = d.extraV2F4;
%FX% i.fx = d.extraV2F5;
}
void EncodeVertexWorkflow(inout VertexData i, inout ExtraV2F d)
{
float4 data0 = DecodeToFloat4(i.vertexColor.r);
%MAX12% d.extraV2F0 = DecodeToFloat4(i.vertexColor.b);
%MAX16% d.extraV2F1 = DecodeToFloat4(i.vertexColor.a);
%MAX8% i.vertexColor = DecodeToFloat4(i.vertexColor.g);
%MAX20% d.extraV2F2 = DecodeToFloat4(i.texcoord1.z);
%MAX24% d.extraV2F3 = DecodeToFloat4(i.texcoord1.w);
%MAX28% d.extraV2F4 = DecodeToFloat4(i.texcoord2.z);
%FX% d.extraV2F5 = DecodeToFloat4(i.texcoord2.w);
i.texcoord0.zw = data0.xy;
i.texcoord1.zw = data0.zw;
}

View File

@@ -0,0 +1,72 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;
#else
using UnityEditor.Experimental.AssetImporters;
#endif
using System.IO;
// Allows you to read/write any texture format from unity as if it's a native format.
namespace JBooth.MicroSplat
{
[ScriptedImporter (1, "hdtexture")]
public class HDTextureImporter : ScriptedImporter
{
public static void Write (Texture2D r, string path, bool mips, bool linear)
{
using (var bw = new BinaryWriter (File.Open (path + ".hdtexture", FileMode.OpenOrCreate)))
{
bw.Write (0);
bw.Write ((int)r.format);
bw.Write (r.width);
bw.Write (r.height);
bw.Write (mips);
bw.Write (linear);
bw.Write ((int)r.wrapMode);
bw.Write ((int)r.filterMode);
var bytes = r.GetRawTextureData ();
bw.Write (bytes.Length);
bw.Write (r.GetRawTextureData ());
}
}
public override void OnImportAsset (AssetImportContext ctx)
{
using (var r = new BinaryReader (File.Open (ctx.assetPath, FileMode.Open)))
{
int version = r.ReadInt32 ();
if (version != 0)
{
Debug.LogError ("Version mismatch in hdtexture aseset");
return;
}
TextureFormat format = (TextureFormat)r.ReadInt32 ();
int width = r.ReadInt32 ();
int height = r.ReadInt32 ();
bool mips = r.ReadBoolean ();
FilterMode filterMode = (FilterMode)(r.ReadInt32 ());
TextureWrapMode wrapMode = (TextureWrapMode)(r.ReadInt32 ());
bool linear = r.ReadBoolean ();
int length = r.ReadInt32 ();
byte [] bytes = r.ReadBytes (length);
var tex = new Texture2D (width, height, format, mips, linear);
tex.wrapMode = wrapMode;
tex.filterMode = filterMode;
tex.LoadRawTextureData (bytes);
tex.Apply ();
ctx.AddObjectToAsset ("main obj", tex);
ctx.SetMainObject (tex);
}
}
}
}

View File

@@ -0,0 +1,26 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
namespace JBooth.MicroSplat
{
public interface IRenderLoopAdapter
{
string GetDisplayName();
string GetRenderLoopKeyword();
string GetShaderExtension();
void Init(string[] paths);
StringBuilder WriteShader(string[] features,
MicroSplatShaderGUI.MicroSplatCompiler compiler,
MicroSplatShaderGUI.MicroSplatCompiler.AuxShader auxShader,
string name,
string baseName);
string GetVersion();
}
}

View File

@@ -0,0 +1,54 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
Shader "Hidden/MicroSplat/ClearNonNormalData"
{
Properties {
_MainTex ("Base (RGB)", 2D) = "bump" {}
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float _Swizzle;
half BlendOverlay(half base, half blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); }
fixed4 frag(v2f_img i) : SV_Target
{
half4 data = tex2D(_MainTex, i.uv);
if (_Swizzle > 0.5)
{
data.ga = data.gr;
}
data.r = 0;
data.b = 1;
return data;
}
ENDCG
}
}
Fallback off
}

View File

@@ -0,0 +1,51 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
Shader "Hidden/MicroSplat/HeightFromNormal"
{
// generate a height map from a normalSAO map
Properties {
_MainTex ("Base (RGB)", 2D) = "bump" {}
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
half BlendOverlay(half base, half blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); }
fixed4 frag(v2f_img i) : SV_Target
{
half2 n0 = tex2Dbias(_MainTex, float4(i.uv, 0, 0)).ga;
half n = BlendOverlay(n0.x, n0.y);
return fixed4(n,n,n,n);
}
ENDCG
}
}
Fallback off
}

View File

@@ -0,0 +1,110 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
Shader "Hidden/MicroSplat/MergeInChannel"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_TargetTex("Target", 2D) = "white" {}
_TargetChannel("Target Channel", Int) = 0
_MergeChannel("Merge Channel", Int) = 0
_Invert("Invert", Int) = 0
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _TargetTex;
float4 _MainTex_TexelSize;
int _TargetChannel;
int _MergeChannel;
int _Invert;
float4 frag(v2f_img i) : SV_Target
{
float4 main = tex2D(_MainTex, i.uv);
float4 target = tex2D(_TargetTex, i.uv);
// why not array access? Throws compile error!
// target[_TargetChannel] = main[_MergeChannel];
if (_TargetChannel == 0)
{
if (_MergeChannel == 0)
target.r = main.r;
else if (_MergeChannel == 1)
target.r = main.g;
else if (_MergeChannel == 2)
target.r = main.b;
else
target.r = main.a;
}
else if (_TargetChannel == 1)
{
if (_MergeChannel == 0)
target.g = main.r;
else if (_MergeChannel == 1)
target.g = main.g;
else if (_MergeChannel == 2)
target.g = main.b;
else
target.g = main.a;
}
else if (_TargetChannel == 2)
{
if (_MergeChannel == 0)
target.b = main.r;
else if (_MergeChannel == 1)
target.b = main.g;
else if (_MergeChannel == 2)
target.b = main.b;
else
target.b = main.a;
}
else
{
if (_MergeChannel == 0)
target.a = main.r;
else if (_MergeChannel == 1)
target.a = main.g;
else if (_MergeChannel == 2)
target.a = main.b;
else
target.a = main.a;
}
if (_Invert > 0.5)
{
if (_TargetChannel == 0)
target.r = 1.0 - target.r;
else if (_TargetChannel == 1)
target.g = 1.0 - target.g;
else if (_TargetChannel == 1)
target.b = 1.0 - target.b;
else
target.a = 1.0 - target.a;
}
return target;
}
ENDCG
}
}
Fallback off
}

View File

@@ -0,0 +1,102 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
Shader "Hidden/MicroSplat/NormalSAOFromDiffuse"
{
// generate a full NormalSAO texture from just a diffuse image
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
float4 GenerateNormal(float2 uv, float uvOffset, float amplitude, float bias)
{
float pixX = _MainTex_TexelSize.x;
float pixY = _MainTex_TexelSize.y;
float3 gx = tex2Dbias(_MainTex, float4(uv + float2(pixX, 0), 0, bias)).rgb;
float3 gy = tex2Dbias(_MainTex, float4(uv + float2(0, pixY), 0, bias)).rgb;
float3 gxb = tex2Dbias(_MainTex, float4(uv + float2(-pixX, 0), 0, bias)).rgb;
float3 gyb = tex2Dbias(_MainTex, float4(uv + float2(0, -pixY), 0, bias)).rgb;
gx = saturate( Luminance(gx + uvOffset));
gy = saturate( Luminance(gy + uvOffset));
gxb = saturate( Luminance(gxb + uvOffset));
gyb = saturate( Luminance(gyb + uvOffset));
gx = (gx - gxb) * -1;
gy = (gy - gyb) * -1;
half4 ret = half4(0.5, 0.5, 0, 1);
float len = sqrt( gx * gx + gy * gy + 1 );
if(len > 0)
{
ret.r = 10*amplitude*gx/len * 0.5 + 0.5;
ret.g = 10*amplitude*gy/len * 0.5 + 0.5;
ret.b = 1.0 / len;
}
return ret;
}
fixed4 frag(v2f_img i) : SV_Target
{
float4 finalNorm = 0;
finalNorm += GenerateNormal(i.uv, 0.1, 0.8, 6) * 6;
finalNorm += GenerateNormal(i.uv, 0.2, 0.7, 5) * 5;
finalNorm += GenerateNormal(i.uv, 0.3, 0.6, 4) * 4;
finalNorm += GenerateNormal(i.uv, 0.4, 0.5, 3) * 3;
finalNorm += GenerateNormal(i.uv, 0.5, 0.4, 2) * 2;
finalNorm += GenerateNormal(i.uv, 0.6, 0.3, 1);
finalNorm += GenerateNormal(i.uv, 0.7, 0.2, 0) * 2;
finalNorm /= 23.0;
finalNorm.xy -= 0.5;
finalNorm.xy *= 12;
finalNorm.xy += 0.5;
// ao is just the normal maps length
finalNorm.w = sqrt(1 - saturate(dot(finalNorm.xy - 0.5, finalNorm.xy - 0.5)));
// smoothness, be conservative
//finalNorm.b = 0.1 * (finalNorm.b + ((finalNorm.x + finalNorm.y)/2));
// Nope, people can't seem to understand that it generates a smoothness
// value, yet can totally understand it generates a normal, so fuck it,
// no smoothness it is.
finalNorm.b = 0;
// swizzle to Smoothness/NormalY/AO/NormalX format, which looks better for most cases
finalNorm.rgba = finalNorm.bgar;
return finalNorm;
}
ENDCG
}
}
Fallback off
}

View File

@@ -0,0 +1,107 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
Shader "Hidden/MicroSplat/NormalSAOFromHeight"
{
// generate a full NormalSAO texture from just a diffuse image
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
int _Channel;
float4 _MainTex_TexelSize;
float4 GenerateNormal(float2 uv, float uvOffset, float amplitude, float bias)
{
float pixX = _MainTex_TexelSize.x;
float pixY = _MainTex_TexelSize.y;
float3 gx = tex2Dbias(_MainTex, float4(uv + float2(pixX, 0), 0, bias))[_Channel];
float3 gy = tex2Dbias(_MainTex, float4(uv + float2(0, pixY), 0, bias))[_Channel];
float3 gxb = tex2Dbias(_MainTex, float4(uv + float2(-pixX, 0), 0, bias))[_Channel];
float3 gyb = tex2Dbias(_MainTex, float4(uv + float2(0, -pixY), 0, bias))[_Channel];
gx = saturate( Luminance(gx + uvOffset));
gy = saturate( Luminance(gy + uvOffset));
gxb = saturate( Luminance(gxb + uvOffset));
gyb = saturate( Luminance(gyb + uvOffset));
gx = (gx - gxb) * -1;
gy = (gy - gyb) * -1;
half4 ret = half4(0.5, 0.5, 0, 1);
float len = sqrt( gx * gx + gy * gy + 1 );
if(len > 0)
{
ret.r = 10*amplitude*gx/len * 0.5 + 0.5;
ret.g = 10*amplitude*gy/len * 0.5 + 0.5;
ret.b = 1.0 / len;
}
return ret;
}
fixed4 frag(v2f_img i) : SV_Target
{
float4 finalNorm = 0;
finalNorm += GenerateNormal(i.uv, 0.1, 0.8, 6) * 6;
finalNorm += GenerateNormal(i.uv, 0.2, 0.7, 5) * 5;
finalNorm += GenerateNormal(i.uv, 0.3, 0.6, 4) * 4;
finalNorm += GenerateNormal(i.uv, 0.4, 0.5, 3) * 3;
finalNorm += GenerateNormal(i.uv, 0.5, 0.4, 2) * 2;
finalNorm += GenerateNormal(i.uv, 0.6, 0.3, 1);
finalNorm += GenerateNormal(i.uv, 0.7, 0.2, 0) * 2;
finalNorm /= 23.0;
finalNorm.xy -= 0.5;
finalNorm.xy *= 10;
finalNorm.xy += 0.5;
finalNorm.xy = saturate(finalNorm.xy);
// ao is just the normal maps length
finalNorm.w = sqrt(1 - saturate(dot(finalNorm.xy - 0.5, finalNorm.xy - 0.5)));
// smoothness, be conservative
//finalNorm.b = 0.1 * (finalNorm.b + ((finalNorm.x + finalNorm.y)/2));
// Nope, people can't seem to understand that it generates a smoothness
// value, yet can totally understand it generates a normal, so fuck it,
// no smoothness it is.
finalNorm.b = 0;
// swizzle to better format.
finalNorm.rgba = finalNorm.bgar;
return finalNorm;
}
ENDCG
}
}
Fallback off
}

View File

@@ -0,0 +1,60 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
Shader "Hidden/MicroSplat/NormalSAOFromNormal"
{
// generate a full NormalSAO texture from just a diffuse image
Properties {
_MainTex ("Base (RGB)", 2D) = "bump" {}
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
fixed4 frag(v2f_img i) : SV_Target
{
half4 norm = tex2D(_MainTex, i.uv);
half3 un = UnpackNormal(norm);
un.xy *= 0.5;
un.xy += 0.5;
half4 nsao = 0;
nsao.xy = un.xy;
// ao is just the normal maps length
nsao.w = un.b * un.b;
// smoothness, be conservative
//nsao.z = un.b * 0.07;
// Nope, people can't seem to understand that it generates a smoothness
// value, yet can totally understand it generates a normal, so fuck it,
// no smoothness it is.
nsao.z = 0;
return nsao.bgar;
}
ENDCG
}
}
Fallback off
}

View File

@@ -0,0 +1,35 @@
{
"name": "JBooth.MicroSplat.Core.Editor",
"references": [
"GUID:4bdfb2239705740718731d0b4d54061d"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"Substance.Game.dll"
],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.modules.ui",
"expression": "",
"define": "__MICROSPLAT__"
},
{
"name": "com.unity.render-pipelines.universal",
"expression": "",
"define": "USING_URP"
},
{
"name": "com.unity.render-pipelines.high-definition",
"expression": "",
"define": "USING_HDRP"
}
],
"noEngineReferences": false
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,459 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
// This class can be used as part of your build process to automatically compress splat maps
// and other data before a build. See the DrawGUI function for an example of how you can do this.
namespace JBooth.MicroSplat
{
public class MicroSplatCompressor
{
static GUIContent CCompSplatMaps = new GUIContent ("Compress Splat Maps", "This will compress terrain and mesh splat maps. Note that terrain editing will not work until splat maps are reverted from this mode");
static GUIContent CCompStreamMap = new GUIContent ("Compress Stream Map", "This will compress the map holding wetness, puddles, streams and lava painting");
static GUIContent CCompSnowMaps = new GUIContent ("Compress Snow Maps", "This will compress snow masking maps");
static GUIContent CCompBiomeMaps = new GUIContent ("Compress Biome Maps", "This will compress procedural biome masks");
static GUIContent CCompTintMaps = new GUIContent ("Compress Tint Maps", "This will compress any tint maps");
public static void DrawGUI (MicroSplatObject mso, Options o)
{
if (MicroSplatUtilities.DrawRollup ("Compressor", false))
{
EditorGUILayout.HelpBox ("You can use this section to automatically covert various maps to compressed formats before a build. Please note, that once compressed, external files will be used for things like the terrain splat maps, and changing the terrain through Unity's tools will have no effect", MessageType.Info);
EditorGUILayout.LabelField ("Compression Options");
o.splatMaps = EditorGUILayout.Toggle (CCompSplatMaps, o.splatMaps);
if (o.splatMaps)
{
EditorGUILayout.HelpBox ("This will remove the terrain painting from your terrain file to save memory! The Uncompress button will restore this from the output textures, but if you delete or unhook them your painting may be lost.", MessageType.Error);
}
o.streamMaps = EditorGUILayout.Toggle (CCompStreamMap, o.streamMaps);
o.snowMask = EditorGUILayout.Toggle (CCompSnowMaps, o.snowMask);
o.biomeMask = EditorGUILayout.Toggle (CCompBiomeMaps, o.biomeMask);
o.tintMap = EditorGUILayout.Toggle (CCompTintMaps, o.tintMap);
EditorGUILayout.BeginHorizontal ();
EditorGUILayout.Space ();
if (GUILayout.Button ("Compress"))
{
var gos = Selection.gameObjects;
foreach (var go in gos)
{
var m = go.GetComponent<MicroSplatTerrain>();
if (m != null)
{
MicroSplatCompressor comp = new MicroSplatCompressor();
comp.Compress(m, o);
}
}
}
if (GUILayout.Button ("Uncompress"))
{
var gos = Selection.gameObjects;
foreach (var go in gos)
{
var m = go.GetComponent<MicroSplatTerrain>();
if (m != null)
{
MicroSplatCompressor comp = new MicroSplatCompressor();
comp.Revert(m);
}
}
}
EditorGUILayout.Space ();
EditorGUILayout.EndHorizontal ();
EditorGUILayout.BeginHorizontal ();
EditorGUILayout.Space ();
if (GUILayout.Button ("Compress Scene"))
{
MicroSplatCompressor comp = new MicroSplatCompressor ();
MicroSplatObject [] objs = GameObject.FindObjectsOfType<MicroSplatObject> ();
foreach (var obj in objs)
{
comp.Compress (obj, o);
}
}
if (GUILayout.Button ("Uncompress Scene"))
{
MicroSplatCompressor comp = new MicroSplatCompressor ();
MicroSplatObject [] objs = GameObject.FindObjectsOfType<MicroSplatObject> ();
foreach (var obj in objs)
{
comp.Revert (obj);
}
}
EditorGUILayout.Space ();
EditorGUILayout.EndHorizontal ();
}
}
[System.Serializable]
public class Options
{
public bool splatMaps = false;
public bool streamMaps = true;
public bool snowMask = true;
public bool biomeMask = true;
public bool tintMap = true;
}
Texture2D CompressTexture(string path, bool sRGB)
{
AssetImporter ai = AssetImporter.GetAtPath (path);
if (ai == null)
return null;
TextureImporter ti = (TextureImporter)ai;
if (ti != null)
{
ti.compressionQuality = 100;
ti.wrapMode = TextureWrapMode.Clamp;
ti.textureCompression = TextureImporterCompression.Compressed;
ti.sRGBTexture = sRGB;
ti.isReadable = false;
TextureImporterPlatformSettings settings = new TextureImporterPlatformSettings();
settings.format = TextureImporterFormat.Automatic;
settings.compressionQuality = 100;
settings.textureCompression = TextureImporterCompression.Compressed;
ti.SetPlatformTextureSettings(settings);
ti.SaveAndReimport();
}
return AssetDatabase.LoadAssetAtPath<Texture2D> (path);
}
Texture2D UncompressTexture (string path)
{
AssetImporter ai = AssetImporter.GetAtPath (path);
if (ai == null)
return null;
TextureImporter ti = (TextureImporter)ai;
if (ti != null)
{
ti.compressionQuality = 100;
ti.wrapMode = TextureWrapMode.Clamp;
ti.textureCompression = TextureImporterCompression.Uncompressed;
ti.isReadable = true;
ti.sRGBTexture = false;
TextureImporterPlatformSettings settings = new TextureImporterPlatformSettings();
settings.format = TextureImporterFormat.RGBA32;
settings.textureCompression = TextureImporterCompression.Uncompressed;
ti.SetPlatformTextureSettings(settings);
ti.SaveAndReimport();
}
return AssetDatabase.LoadAssetAtPath<Texture2D> (path);
}
void CompressTerrainSplats (MicroSplatTerrain t)
{
int splatCount = t.terrain.terrainData.alphamapTextureCount;
// write out
for (int i = 0; i < splatCount; ++i)
{
var tex = t.terrain.terrainData.GetAlphamapTexture (i);
var path = MicroSplatUtilities.RelativePathFromAsset (t);
path += "/" + t.name + "_splat" + i + ".tga";
System.IO.File.WriteAllBytes (path, tex.EncodeToTGA ());
}
AssetDatabase.Refresh ();
// load and adjust importer
for (int i = 0; i < splatCount; ++i)
{
var path = MicroSplatUtilities.RelativePathFromAsset (t);
path += "/" + t.name + "_splat" + i + ".tga";
var tex = CompressTexture (path, false);
if (i == 0)
{
t.customControl0 = tex;
}
else if (i == 1)
{
t.customControl1 = tex;
}
else if (i == 2)
{
t.customControl2 = tex;
}
else if (i == 3)
{
t.customControl3 = tex;
}
else if (i == 4)
{
t.customControl4 = tex;
}
else if (i == 5)
{
t.customControl5 = tex;
}
else if (i == 6)
{
t.customControl6 = tex;
}
else if (i == 7)
{
t.customControl7 = tex;
}
}
EditorUtility.SetDirty (t);
MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords (t.templateMaterial);
if (!keywords.IsKeywordEnabled("_CUSTOMSPLATTEXTURES"))
{
keywords.EnableKeyword ("_CUSTOMSPLATTEXTURES");
MicroSplatShaderGUI.MicroSplatCompiler compiler = new MicroSplatShaderGUI.MicroSplatCompiler ();
compiler.Compile (t.templateMaterial);
MicroSplatTerrain.SyncAll ();
}
// destructive operation
t.terrain.terrainData.alphamapResolution = 16;
}
void ExtractSplats(ref float[,,] maps, Texture2D tex, int index, int layers)
{
if (tex == null)
return;
int size = tex.width;
if (tex != null)
{
for (int x = 0; x < size; ++x)
{
for (int y = 0; y < size; ++y)
{
Color c = tex.GetPixel (x, y);
if (index < layers)
{
maps [y, x, index + 0] = c.r;
}
if (index + 1 < layers)
{
maps [y, x, index + 1] = c.g;
}
if (index + 2 < layers)
{
maps [y, x, index + 2] = c.b;
}
if (index + 3 < layers)
{
maps [y, x, index + 3] = c.a;
}
}
}
}
}
void RevertTerrainSplats(MicroSplatTerrain t)
{
MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords (t.templateMaterial);
if (keywords.IsKeywordEnabled ("_CUSTOMSPLATTEXTURES"))
{
if (t.customControl0 == null)
{
Debug.LogError ("Could not revert terrain because textures are missing!");
return;
}
UncompressTexture (t.customControl0);
UncompressTexture (t.customControl1);
UncompressTexture (t.customControl2);
UncompressTexture (t.customControl3);
UncompressTexture (t.customControl4);
UncompressTexture (t.customControl5);
UncompressTexture (t.customControl6);
UncompressTexture (t.customControl7);
int size = t.customControl0.width;
int layers = t.terrain.terrainData.alphamapLayers;
t.terrain.terrainData.alphamapResolution = size;
var maps = t.terrain.terrainData.GetAlphamaps (0, 0, size, size);
ExtractSplats (ref maps, t.customControl0, 0, layers);
ExtractSplats (ref maps, t.customControl1, 4, layers);
ExtractSplats (ref maps, t.customControl2, 8, layers);
ExtractSplats (ref maps, t.customControl3, 12, layers);
ExtractSplats (ref maps, t.customControl4, 16, layers);
ExtractSplats (ref maps, t.customControl5, 20, layers);
ExtractSplats (ref maps, t.customControl6, 24, layers);
ExtractSplats (ref maps, t.customControl7, 28, layers);
t.terrain.terrainData.SetAlphamaps (0, 0, maps);
EditorUtility.SetDirty (t.terrain.terrainData);
keywords.DisableKeyword ("_CUSTOMSPLATTEXTURES");
MicroSplatShaderGUI.MicroSplatCompiler compiler = new MicroSplatShaderGUI.MicroSplatCompiler ();
compiler.Compile (t.templateMaterial);
t.customControl0 = null;
t.customControl1 = null;
t.customControl2 = null;
t.customControl3 = null;
t.customControl4 = null;
t.customControl5 = null;
t.customControl6 = null;
t.customControl7 = null;
EditorUtility.SetDirty (t);
MicroSplatTerrain.SyncAll ();
}
}
void CompressTexture (Texture2D tex, bool sRGB)
{
if (tex != null)
{
CompressTexture (AssetDatabase.GetAssetPath (tex), sRGB);
}
}
void UncompressTexture (Texture2D tex)
{
if (tex != null)
{
UncompressTexture (AssetDatabase.GetAssetPath (tex));
}
}
void CompressTexture(Material mat, string name, bool sRGB)
{
if (mat.HasProperty(name))
{
CompressTexture (mat.GetTexture (name) as Texture2D, sRGB);
}
}
void UncompressTexture (Material mat, string name)
{
if (mat.HasProperty (name))
{
UncompressTexture (mat.GetTexture (name) as Texture2D);
}
}
public void Compress (MicroSplatObject mso, Options opt)
{
MicroSplatTerrain t = mso as MicroSplatTerrain;
if (t != null)
{
if (t.templateMaterial == null)
{
Debug.LogError ("MicroSplatTerrain " + mso.gameObject.name + " does not have template material");
}
else
{
if (opt.splatMaps)
{
CompressTerrainSplats (t);
}
}
}
#if __MICROSPLAT_MESH__
MicroSplatMesh msm = mso as MicroSplatMesh;
if (msm != null)
{
foreach (var subMesh in msm.subMeshEntries)
{
foreach (var tex in subMesh.subMeshOverride.controlTextures)
{
CompressTexture (tex, false);
}
}
}
#endif
#if __MICROSPLAT_PROCTEX__
if (opt.biomeMask)
{
CompressTexture (mso.procBiomeMask, false);
CompressTexture (mso.procBiomeMask2, false);
CompressTexture (mso.templateMaterial, "_ProcTexBiomeMask", false);
CompressTexture (mso.templateMaterial, "_ProcTexBiomeMask2", false);
}
#endif
#if __MICROSPLAT_SNOW__
if (opt.snowMask)
{
CompressTexture (mso.snowMaskOverride, false);
CompressTexture (mso.templateMaterial, "_SnowMask", false);
}
#endif
#if __MICROSPLAT_STREAMS__
if (opt.streamMaps)
{
CompressTexture (mso.streamTexture, false);
CompressTexture (mso.templateMaterial, "_StreamControl", false);
}
#endif
#if __MICROSPLAT_GLOBALTEXTURE__
if (opt.tintMap)
{
CompressTexture (mso.streamTexture, true);
CompressTexture (mso.templateMaterial, "_GlobalTintTex", true);
}
#endif
}
public void Revert(MicroSplatObject mso)
{
MicroSplatTerrain t = mso as MicroSplatTerrain;
if (t != null)
{
if (t.templateMaterial == null)
{
Debug.LogError ("MicroSplatTerrain " + mso.gameObject.name + " does not have template material");
}
else
{
RevertTerrainSplats (t);
}
}
#if __MICROSPLAT_MESH__
MicroSplatMesh msm = mso as MicroSplatMesh;
if (msm != null)
{
foreach (var subMesh in msm.subMeshEntries)
{
foreach (var tex in subMesh.subMeshOverride.controlTextures)
{
UncompressTexture (tex);
}
}
}
#endif
#if __MICROSPLAT_PROCTEX__
UncompressTexture (mso.procBiomeMask);
UncompressTexture (mso.procBiomeMask2);
UncompressTexture (mso.templateMaterial, "_ProcTexBiomeMask");
UncompressTexture (mso.templateMaterial, "_ProcTexBiomeMask2");
#endif
#if __MICROSPLAT_SNOW__
UncompressTexture (mso.snowMaskOverride);
UncompressTexture (mso.templateMaterial, "_SnowMask");
#endif
#if __MICROSPLAT_STREAMS__
UncompressTexture (mso.streamTexture);
UncompressTexture (mso.templateMaterial, "_StreamControl");
#endif
#if __MICROSPLAT_GLOBALTEXTURE__
UncompressTexture (mso.streamTexture);
UncompressTexture (mso.templateMaterial, "_GlobalTintTex");
#endif
}
}
}

View File

@@ -0,0 +1,79 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using UnityEditor;
using UnityEditor.Callbacks;
namespace JBooth.MicroSplat
{
[InitializeOnLoad]
public class MicroSplatDefines
{
const string sMicroSplatDefine = "__MICROSPLAT__";
static MicroSplatDefines ()
{
InitDefine (sMicroSplatDefine);
}
public static bool HasDefine (string def)
{
var target = EditorUserBuildSettings.selectedBuildTargetGroup;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup (target);
return defines.Contains (def);
}
public static void InitDefine (string def)
{
var target = EditorUserBuildSettings.selectedBuildTargetGroup;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup (target);
if (!defines.Contains (def))
{
if (string.IsNullOrEmpty (defines))
{
PlayerSettings.SetScriptingDefineSymbolsForGroup (target, def);
}
else
{
if (!defines [defines.Length - 1].Equals (';'))
{
defines += ';';
}
defines += def;
PlayerSettings.SetScriptingDefineSymbolsForGroup (target, defines);
}
}
}
[PostProcessSceneAttribute (0)]
public static void OnPostprocessScene ()
{
InitDefine (sMicroSplatDefine);
}
public static string link_globalTexture = "https://assetstore.unity.com/packages/tools/terrain/microsplat-global-texturing-96482?aid=25047";
public static string link_snow = "https://assetstore.unity.com/packages/tools/terrain/microsplat-dynamic-snow-96486?aid=25047";
public static string link_tessellation = "https://assetstore.unity.com/packages/tools/terrain/microsplat-tessellation-and-parallax-96484?aid=25047";
public static string link_antitile = "https://assetstore.unity.com/packages/tools/terrain/microsplat-anti-tiling-module-96480?aid=25047";
public static string link_terrainblend = "https://assetstore.unity.com/packages/tools/terrain/microsplat-terrain-blending-97364?aid=25047";
public static string link_streams = "https://assetstore.unity.com/packages/tools/terrain/microsplat-puddles-streams-lava-wetness-97993?aid=25047";
public static string link_alphahole = "https://assetstore.unity.com/packages/tools/terrain/microsplat-terrain-holes-97495?aid=25047";
public static string link_triplanar = "https://assetstore.unity.com/packages/tools/terrain/microsplat-triplanar-uvs-96777?aid=25047";
public static string link_textureclusters = "https://assetstore.unity.com/packages/tools/terrain/microsplat-texture-clusters-104223?aid=25047";
public static string link_windglitter = "https://assetstore.unity.com/packages/tools/terrain/microsplat-wind-and-glitter-105627?aid=25047";
public static string link_proctex = "https://assetstore.unity.com/packages/tools/terrain/microsplat-runtime-procedural-texturing-143039?aid=25047";
public static string link_lowpoly = "https://assetstore.unity.com/packages/tools/terrain/microsplat-low-poly-look-146119?aid=1011l37NJ&aid=25047";
public static string link_meshterrain = "https://assetstore.unity.com/packages/tools/terrain/microsplat-mesh-terrains-157356?aid=1011l37NJ&aid=25047";
public static string link_meshworkflow = "https://assetstore.unity.com/packages/tools/painting/microsplat-mesh-workflow-beta-120008?aid=1011l37NJ&aid=25047";
public static string link_digger = "https://assetstore.unity.com/packages/tools/terrain/microsplat-digger-integration-162840?pubref=25047";
public static string link_trax = "https://assetstore.unity.com/packages/tools/terrain/microsplat-trax-166218?pubref=25047";
public static string link_polaris = "https://assetstore.unity.com/packages/tools/terrain/microsplat-polaris-integration-166851?pubref=25047";
public static string link_scatter = "https://assetstore.unity.com/packages/tools/terrain/microsplat-scatter-170299?pubref=25047";
public static string link_decal = "https://assetstore.unity.com/packages/tools/terrain/microsplat-decals-178765?aid=25047";
}
}

View File

@@ -0,0 +1,431 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using UnityEditor;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace JBooth.MicroSplat
{
public static class StringExtensions
{
public static string RemoveBetween(this string s, char begin, char end)
{
Regex regex = new Regex(string.Format("\\{0}.*?\\{1}", begin, end));
return regex.Replace(s, string.Empty);
}
public static string StripComments(this string str)
{
var blockComments = @"/\*(.*?)\*/";
var lineComments = @"//(.*?)\r?\n";
var strings = @"""((\\[^\n]|[^""\n])*)""";
var verbatimStrings = @"@(""[^""]*"")+";
string noComments = Regex.Replace(str, blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, me =>
{
if (me.Value.StartsWith("/*") || me.Value.StartsWith("//"))
return me.Value.StartsWith("//") ? System.Environment.NewLine : "";
return me.Value;
},
RegexOptions.Singleline);
return noComments;
}
public static string[] ToLines(this string str)
{
return str.Split("\n\r".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries);
}
}
public class Blocks
{
public string cbuffer;
public string defines;
public string code;
public string properties;
public string options;
}
[InitializeOnLoad]
public class MicroSplatGenerator
{
void WriteOptions(string[] features, StringBuilder sb, MicroSplatShaderGUI.MicroSplatCompiler compiler, MicroSplatShaderGUI.MicroSplatCompiler.AuxShader auxShader, string baseName)
{
if (features.Contains("_TESSEDGE"))
{
sb.AppendLine("Tessellation \"Edge\"");
}
else if (features.Contains("_TESSDISTANCE"))
{
sb.AppendLine("Tessellation \"Distance\"");
}
sb.Append(" Tags {\"RenderType\" = \"Opaque\" \"Queue\" = \"Geometry+100\" \"IgnoreProjector\" = \"False\" ");
if (features.Contains("_MICROTERRAIN") || features.Contains("_MEGASPLATTEXTURE"))
{
sb.Append(" \"TerrainCompatible\" = \"true\" ");
}
if (features.Contains("_MAX4TEXTURES"))
{
sb.Append("\"SplatCount\" = \"4\"");
}
else if (features.Contains("_MAX8TEXTURES"))
{
sb.Append("\"SplatCount\" = \"8\"");
}
else if (features.Contains("_MAX12TEXTURES"))
{
sb.Append("\"SplatCount\" = \"12\"");
}
else if (features.Contains("_MAX20TEXTURES"))
{
sb.Append("\"SplatCount\" = \"20\"");
}
else if (features.Contains("_MAX24TEXTURES"))
{
sb.Append("\"SplatCount\" = \"24\"");
}
else if (features.Contains("_MAX28TEXTURES"))
{
sb.Append("\"SplatCount\" = \"28\"");
}
else if (features.Contains("_MAX32TEXTURES"))
{
sb.Append("\"SplatCount\" = \"32\"");
}
else
{
sb.Append("\"SplatCount\" = \"16\"");
}
sb.AppendLine("}");
if (features.Contains("_MESHOVERLAYSPLATS"))
{
sb.AppendLine(" Alpha \"Blend\"");
}
if (auxShader != null && !string.IsNullOrEmpty(auxShader.customEditor))
{
sb.AppendLine(" CustomEditor \"" + auxShader.customEditor + "\"");
}
if (auxShader != null && !string.IsNullOrEmpty(auxShader.options))
{
sb.AppendLine(auxShader.options);
}
else if (auxShader != null)
{
sb.AppendLine(" CustomEditor \"MicroSplatShaderGUI\"");
}
else if (baseName == null)
{
sb.AppendLine(" CustomEditor \"MicroSplatShaderGUI\"");
}
else if (baseName != null)
{
if (features.Contains ("_MICROTERRAIN") && !features.Contains ("_TERRAINBLENDABLESHADER"))
{
sb.AppendLine(" Dependency {\"BaseMapShader\" = \"" + baseName + "\"}");
}
sb.AppendLine(" CustomEditor \"MicroSplatShaderGUI\"");
}
sb.AppendLine(" Fallback \"Nature/Terrain/Diffuse\"");
}
static TextAsset adapter;
static TextAsset sharedInc;
static TextAsset terrainBody;
static TextAsset terrainBlendBody;
static TextAsset terrainBlendCBuffer;
static TextAsset sharedHD;
static TextAsset vertex;
static TextAsset vertexData;
static TextAsset megasplatData;
static TextAsset mainFunc;
public void Init(string[] paths)
{
for (int i = 0; i < paths.Length; ++i)
{
string p = paths[i];
if (p.EndsWith("microsplat_template_adapter.txt"))
{
adapter = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrainblend_body.txt"))
{
terrainBlendBody = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrainblend_cbuffer.txt"))
{
terrainBlendCBuffer = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrain_body.txt"))
{
terrainBody = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_shared.txt"))
{
sharedInc = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrain_core_shared.txt"))
{
sharedHD = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrain_core_vertex.txt"))
{
vertex = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrain_core_vertexdata.txt"))
{
vertexData = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrain_core_megasplat.txt"))
{
megasplatData = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
if (p.EndsWith("microsplat_terrain_core_mainfunc.txt"))
{
mainFunc = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
}
}
string StripMegaSplat(string[] features)
{
if (features.Contains("_MEGASPLAT") && megasplatData != null)
{
var lines = megasplatData.text.ToLines();
var stripList = new List<string>();
stripList.Add("%SCATTER%");
stripList.Add("%SCATTER2%");
stripList.Add("%FX%");
stripList.Add("%FX2%");
if (features.Contains("_SCATTER"))
{
stripList.Remove("%SCATTER%");
}
if (features.Contains("_SCATTERSECONDLAYER"))
{
stripList.Remove("%SCATTER2%");
}
if (features.Contains("_WETNESS") || features.Contains("_PUDDLES") || features.Contains("_STREAMS") || features.Contains("_LAVA"))
{
stripList.Remove("%FX%");
}
if (features.Contains("_SNOW") && features.Contains("_SNOWMASK"))
{
stripList.Remove("%FX2%");
}
// strip any lines we need to remove
for (int i = 0; i < lines.Length; ++i)
{
string l = lines[i];
foreach (var s in stripList)
{
if (l.Contains(s))
{
lines[i] = "";
}
}
}
// strip lines remaining.
string result = string.Join("\n", lines);
result = result.Replace("%SCATTER%", "");
result = result.Replace("%SCATTER2%", "");
result = result.Replace("%FX%", "");
result = result.Replace("%FX2%", "");
return result;
}
return "";
}
string StripVertexWorkflow(string[] features)
{
if (vertexData != null &&
(features.Contains("_MICROVERTEXMESH") || features.Contains("_MICRODIGGERMESH"))
)
{
var lines = vertexData.text.ToLines();
var stripList = new List<string>();
stripList.Add("%MAX8%");
stripList.Add("%MAX12%");
stripList.Add("%MAX16%");
stripList.Add("%MAX20%");
stripList.Add("%MAX24%");
stripList.Add("%MAX28%");
stripList.Add("%FX%");
stripList.Add("%FX2%");
if (features.Contains("_MAX8TEXTURES"))
{
stripList.Remove("%MAX8%");
}
else if (features.Contains("_MAX12TEXTURES"))
{
stripList.Remove("%MAX8%");
stripList.Remove("%MAX12%");
}
else if (features.Contains("_MAX20TEXTURES"))
{
stripList.Remove("%MAX8%");
stripList.Remove("%MAX12%");
stripList.Remove("%MAX16%");
stripList.Remove("%MAX20%");
}
else if (features.Contains("_MAX24TEXTURES"))
{
stripList.Remove("%MAX8%");
stripList.Remove("%MAX12%");
stripList.Remove("%MAX16%");
stripList.Remove("%MAX20%");
stripList.Remove("%MAX24%");
}
else if (features.Contains("_MAX28TEXTURES"))
{
stripList.Remove("%MAX8%");
stripList.Remove("%MAX12%");
stripList.Remove("%MAX16%");
stripList.Remove("%MAX20%");
stripList.Remove("%MAX24%");
stripList.Remove("%MAX28%");
}
else if (features.Contains("_MAX32TEXTURES"))
{
stripList.Remove("%MAX8%");
stripList.Remove("%MAX12%");
stripList.Remove("%MAX16%");
stripList.Remove("%MAX20%");
stripList.Remove("%MAX24%");
stripList.Remove("%MAX28%");
}
else
{
stripList.Remove("%MAX8%");
stripList.Remove("%MAX12%");
stripList.Remove("%MAX16%");
}
if (features.Contains("_WETNESS") || features.Contains("_PUDDLES") || features.Contains("_STREAMS") || features.Contains("_LAVA"))
{
stripList.Remove("%FX%");
}
if (features.Contains("_SNOW") && features.Contains("_SNOWMASK"))
{
stripList.Remove("%FX2%");
}
// strip any lines we need to remove
for (int i = 0; i < lines.Length; ++i)
{
string l = lines[i];
foreach (var s in stripList)
{
if (l.Contains(s))
{
lines[i] = "";
}
}
}
// strip lines remaining.
string result = string.Join("\n", lines);
result = result.Replace("%MAX8%", "");
result = result.Replace("%MAX12%", "");
result = result.Replace("%MAX16%", "");
result = result.Replace("%MAX20%", "");
result = result.Replace("%MAX24%", "");
result = result.Replace("%MAX28%", "");
result = result.Replace("%FX%", "");
result = result.Replace("%FX2%", "");
return result;
}
else
{
return "";
}
}
public Blocks GetShaderBlocks(string[] features,
MicroSplatShaderGUI.MicroSplatCompiler compiler,
MicroSplatShaderGUI.MicroSplatCompiler.AuxShader auxShader)
{
StringBuilder defines = new StringBuilder();
compiler.WriteDefines(features, defines);
if (auxShader != null && auxShader.trigger == "_TERRAINBLENDING")
{
defines.AppendLine(" #define _SRPTERRAINBLEND 1");
}
if (features.Contains("_USESPECULARWORKFLOW"))
{
defines.AppendLine(" #define _SPECULAR_SETUP");
}
if (features.Contains("_MICROTERRAIN") && !features.Contains("_TERRAINBLENDABLESHADER")) // digger? mesh terrain?
{
defines.AppendLine("#pragma instancing_options assumeuniformscaling nomatrices nolightprobe nolightmap forwardadd");
}
StringBuilder cbuffer = new StringBuilder();
compiler.WritePerMaterialCBuffer(features, cbuffer);
if (auxShader != null && auxShader.trigger == "_TERRAINBLENDING")
{
cbuffer.AppendLine(terrainBlendCBuffer.text);
}
StringBuilder options = new StringBuilder();
WriteOptions(features, options, compiler, auxShader, null);
StringBuilder properties = new StringBuilder();
compiler.WriteProperties(features, properties, auxShader);
StringBuilder ext = new StringBuilder();
compiler.WriteExtensions(features, ext);
StringBuilder afterVertex = new StringBuilder();
foreach (var e in compiler.extensions)
{
e.WriteAfterVetrexFunctions(afterVertex);
}
StringBuilder code = new StringBuilder(100000);
code.AppendLine(adapter.text);
code.AppendLine(sharedInc.text);
code.AppendLine(StripVertexWorkflow(features));
code.AppendLine((StripMegaSplat(features)));
code.AppendLine(sharedHD.text);
code.AppendLine(ext.ToString());
code.AppendLine(terrainBody.text);
code.AppendLine(vertex.text);
if (auxShader != null && auxShader.trigger == "_TERRAINBLENDING")
{
code.AppendLine(terrainBlendBody.text);
}
code.AppendLine(mainFunc.text);
code.AppendLine(afterVertex.ToString());
Blocks b = new Blocks();
b.code = code.ToString();
b.cbuffer = cbuffer.ToString();
b.properties = properties.ToString();
b.defines = defines.ToString();
b.options = options.ToString();
return b;
}
}
}

View File

@@ -0,0 +1,584 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using JBooth.MicroSplat;
public partial class MicroSplatShaderGUI : ShaderGUI
{
public static readonly string MicroSplatVersion = "3.9";
MicroSplatCompiler compiler = new MicroSplatCompiler ();
public MaterialProperty FindProp (string name, MaterialProperty [] props)
{
return FindProperty (name, props);
}
GUIContent CShaderName = new GUIContent ("Name", "Menu path with name for the shader");
GUIContent CRenderLoop = new GUIContent ("Render Loop", "You can select which render loop the shader should be compiled for here");
public static bool needsCompile = false;
bool bulkEditMode = false;
int perTexIndex = 0;
System.Text.StringBuilder builder = new System.Text.StringBuilder (1024);
GUIContent [] renderLoopNames;
bool DrawRenderLoopGUI (MicroSplatKeywords keywords, Material targetMat)
{
// init render loop name list
if (renderLoopNames == null || renderLoopNames.Length != availableRenderLoops.Count)
{
var rln = new List<GUIContent> ();
for (int i = 0; i < availableRenderLoops.Count; ++i)
{
rln.Add (new GUIContent (availableRenderLoops [i].GetDisplayName ()));
}
renderLoopNames = rln.ToArray ();
}
int curRenderLoopIndex = 0;
for (int i = 0; i < keywords.keywords.Count; ++i)
{
string s = keywords.keywords [i];
for (int j = 0; j < availableRenderLoops.Count; ++j)
{
if (s == availableRenderLoops [j].GetRenderLoopKeyword ())
{
curRenderLoopIndex = j;
compiler.renderLoop = availableRenderLoops [j];
break;
}
}
}
int oldIdx = curRenderLoopIndex;
curRenderLoopIndex = EditorGUILayout.Popup (CRenderLoop, curRenderLoopIndex, renderLoopNames);
if (oldIdx != curRenderLoopIndex && curRenderLoopIndex >= 0 && curRenderLoopIndex < availableRenderLoops.Count)
{
if (compiler.renderLoop != null)
{
keywords.DisableKeyword (compiler.renderLoop.GetRenderLoopKeyword ());
}
compiler.renderLoop = availableRenderLoops [curRenderLoopIndex];
keywords.EnableKeyword (compiler.renderLoop.GetRenderLoopKeyword ());
return true;
}
if (targetMat != null && !targetMat.enableInstancing)
{
EditorUtility.SetDirty (targetMat);
targetMat.enableInstancing = true;
}
return false;
}
int cachedKeywordCount;
Material cachedMaterial;
void Undo_UndoRedoPerformed ()
{
if (cachedMaterial != null && cachedKeywordCount > 0)
{
var keywordSO = MicroSplatUtilities.FindOrCreateKeywords (cachedMaterial);
if (cachedKeywordCount != keywordSO.keywords.Count)
{
needsCompile = true;
}
}
}
string cachedTitle;
GUIStyle moduleLabelStyle;
Dictionary<string, bool> moduleFoldoutState = new Dictionary<string, bool> ();
public static Material targetMat { get; private set; }
static int tabIdx = 0;
static GUIContent[] tabs = new GUIContent[] { new GUIContent("Features"), new GUIContent("Settings"), new GUIContent("Per-Texture"), new GUIContent("Arrays") };
public override void OnGUI (MaterialEditor materialEditor, MaterialProperty[] props)
{
Undo.undoRedoPerformed -= Undo_UndoRedoPerformed;
Undo.undoRedoPerformed += Undo_UndoRedoPerformed;
targetMat = materialEditor.target as Material;
if (cachedTitle == null)
{
cachedTitle = "Shader Generator v:" + MicroSplatVersion;
}
if (moduleLabelStyle == null)
{
moduleLabelStyle = new GUIStyle (EditorStyles.foldout);
moduleLabelStyle.fontStyle = FontStyle.Bold;
}
if (GUI.enabled == false || string.IsNullOrEmpty(AssetDatabase.GetAssetPath(targetMat)))
{
EditorGUILayout.HelpBox("You must edit the template material, not the instance being used. You can find this in the MicroSplatData directory, or assigned on your MicroSplatTerrain component", MessageType.Info);
return;
}
EditorGUI.BeginChangeCheck(); // sync materials
var keywordSO = MicroSplatUtilities.FindOrCreateKeywords(targetMat);
cachedKeywordCount = keywordSO.keywords.Count; // for undo
cachedMaterial = targetMat;
compiler.Init();
// must unpack everything before the generator draws- otherwise we get IMGUI errors
for (int i = 0; i < compiler.extensions.Count; ++i)
{
var ext = compiler.extensions[i];
ext.Unpack(keywordSO.keywords.ToArray());
}
string shaderName = targetMat.shader.name;
DrawModules();
tabIdx = GUILayout.Toolbar(tabIdx, tabs);
EditorGUI.BeginChangeCheck(); // needs compile
var propTex = FindOrCreatePropTex(targetMat);
Undo.RecordObjects(new Object[3] { targetMat, keywordSO, propTex }, "MicroSplat Material Edit");
if (tabIdx == 0)
{
using (new GUILayout.VerticalScope(MicroSplatUtilities.boxStyle))
{
if (MicroSplatUtilities.DrawRollup(cachedTitle))
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (bulkEditMode)
{
if (GUILayout.Button("Exit Bulk Shader Feature Edit Mode", GUILayout.Width(230)))
{
bulkEditMode = false;
needsCompile = true;
}
}
else
{
if (GUILayout.Button("Enter Bulk Shader Feature Edit Mode", GUILayout.Width(230)))
{
bulkEditMode = true;
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
if (bulkEditMode)
{
EditorGUILayout.HelpBox("Shader is in bulk edit mode, allowing you to change many options without recompiling the shader. No material properties will be shown during bulk editing, and the shader will be recompiled and properties shown once you exit this mode", MessageType.Warning);
}
shaderName = EditorGUILayout.DelayedTextField(CShaderName, shaderName);
if (DrawRenderLoopGUI(keywordSO, targetMat))
{
needsCompile = true;
}
for (int i = 0; i < compiler.extensions.Count; ++i)
{
var e = compiler.extensions[i];
if (e.GetVersion() == MicroSplatVersion)
{
needsCompile = EditorGUI.EndChangeCheck() || needsCompile;
if (!moduleFoldoutState.ContainsKey(e.ModuleName()))
{
moduleFoldoutState[e.ModuleName()] = true;
}
if (!e.HideModule())
{
using (new GUILayout.VerticalScope(MicroSplatUtilities.boxStyle))
{
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
moduleFoldoutState[e.ModuleName()] = EditorGUILayout.Foldout(moduleFoldoutState[e.ModuleName()], e.ModuleName(), moduleLabelStyle);
if (e.GetHelpPath() != null)
{
if (GUILayout.Button("Documentation", GUILayout.Width(100), GUILayout.Height(18)))
{
string p = e.GetHelpPath();
string path = p.Replace(" ", "%20");
Application.OpenURL(path);
}
}
EditorGUILayout.EndHorizontal();
EditorGUI.BeginChangeCheck();
if (moduleFoldoutState[e.ModuleName()])
{
//EditorGUI.indentLevel++;
e.DrawFeatureGUI(keywordSO);
//EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
}
else
{
EditorGUI.BeginChangeCheck();
}
}
else
{
EditorGUILayout.HelpBox("Extension : " + e.ModuleName() + " is version " + e.GetVersion() + " and MicroSplat is version " + MicroSplatVersion + ", please update", MessageType.Error);
}
}
for (int i = 0; i < availableRenderLoops.Count; ++i)
{
var rl = availableRenderLoops[i];
if (rl.GetVersion() != MicroSplatVersion)
{
EditorGUILayout.HelpBox("Render Loop : " + rl.GetDisplayName() + " is version " + rl.GetVersion() + " and MicroSplat is version " + MicroSplatVersion + ", please update", MessageType.Error);
}
}
}
if (bulkEditMode)
{
if (!keywordSO.IsKeywordEnabled("_DISABLESPLATMAPS"))
{
Texture2DArray diff = targetMat.GetTexture("_Diffuse") as Texture2DArray;
if (diff != null && MicroSplatUtilities.DrawRollup("Per Texture Properties"))
{
perTexIndex = MicroSplatUtilities.DrawTextureSelector(perTexIndex, diff);
for (int i = 0; i < compiler.extensions.Count; ++i)
{
var ext = compiler.extensions[i];
if (ext.GetVersion() == MicroSplatVersion)
{
ext.DrawPerTextureGUI(perTexIndex, keywordSO, targetMat, propTex);
}
}
}
}
needsCompile = needsCompile || EditorGUI.EndChangeCheck();
if (needsCompile)
{
keywordSO.keywords.Clear();
for (int i = 0; i < compiler.extensions.Count; ++i)
{
compiler.extensions[i].Pack(keywordSO);
}
if (compiler.renderLoop != null)
{
keywordSO.EnableKeyword(compiler.renderLoop.GetRenderLoopKeyword());
}
needsCompile = false;
}
return; // Don't draw rest of GUI
}
}
needsCompile = needsCompile || EditorGUI.EndChangeCheck();
}
int featureCount = keywordSO.keywords.Count;
if (tabIdx == 1)
{
// note, ideally we wouldn't draw the GUI for the rest of stuff if we need to compile.
// But we can't really do that without causing IMGUI to split warnings about
// mismatched GUILayout blocks
if (!needsCompile)
{
for (int i = 0; i < compiler.extensions.Count; ++i)
{
var ext = compiler.extensions[i];
if (ext.GetVersion() == MicroSplatVersion)
{
ext.DrawShaderGUI(this, keywordSO, targetMat, materialEditor, props);
}
else
{
EditorGUILayout.HelpBox("Extension : " + ext.ModuleName() + " is version " + ext.GetVersion() + " and MicroSplat is version " + MicroSplatVersion + ", please update so that all modules are using the same version.", MessageType.Error);
}
}
int arraySampleCount = 0;
int textureSampleCount = 0;
int maxSamples = 0;
int tessSamples = 0;
int depTexReadLevel = 0;
builder.Length = 0;
for (int i = 0; i < compiler.extensions.Count; ++i)
{
var ext = compiler.extensions[i];
if (ext.GetVersion() == MicroSplatVersion)
{
ext.ComputeSampleCounts(keywordSO.keywords.ToArray(), ref arraySampleCount, ref textureSampleCount, ref maxSamples, ref tessSamples, ref depTexReadLevel);
}
}
if (MicroSplatUtilities.DrawRollup("Debug"))
{
string shaderModel = compiler.GetShaderModel(keywordSO.keywords.ToArray());
builder.Append("Shader Model : ");
builder.AppendLine(shaderModel);
if (maxSamples != arraySampleCount)
{
builder.Append("Texture Array Samples : ");
builder.AppendLine(arraySampleCount.ToString());
builder.Append("Regular Samples : ");
builder.AppendLine(textureSampleCount.ToString());
}
else
{
builder.Append("Texture Array Samples : ");
builder.AppendLine(arraySampleCount.ToString());
builder.Append("Regular Samples : ");
builder.AppendLine(textureSampleCount.ToString());
}
if (tessSamples > 0)
{
builder.Append("Tessellation Samples : ");
builder.AppendLine(tessSamples.ToString());
}
if (depTexReadLevel > 0)
{
builder.Append(depTexReadLevel.ToString());
builder.AppendLine(" areas with dependent texture reads");
}
EditorGUILayout.HelpBox(builder.ToString(), MessageType.Info);
}
}
if (!needsCompile)
{
if (featureCount != keywordSO.keywords.Count)
{
needsCompile = true;
}
}
}
if (tabIdx == 2 && !needsCompile)
{
if (!keywordSO.IsKeywordEnabled("_DISABLESPLATMAPS"))
{
Texture2DArray diff = targetMat.GetTexture("_Diffuse") as Texture2DArray;
if (diff != null && MicroSplatUtilities.DrawRollup("Per Texture Properties"))
{
perTexIndex = MicroSplatUtilities.DrawTextureSelector(perTexIndex, diff);
for (int i = 0; i < compiler.extensions.Count; ++i)
{
var ext = compiler.extensions[i];
if (ext.GetVersion() == MicroSplatVersion)
{
ext.DrawPerTextureGUI(perTexIndex, keywordSO, targetMat, propTex);
}
}
if (!needsCompile)
{
if (featureCount != keywordSO.keywords.Count)
{
needsCompile = true;
}
}
}
}
}
if (EditorGUI.EndChangeCheck () && !needsCompile)
{
MicroSplatObject.SyncAll ();
}
if (needsCompile)
{
needsCompile = false;
keywordSO.keywords.Clear ();
for (int i = 0; i < compiler.extensions.Count; ++i)
{
compiler.extensions [i].Pack (keywordSO);
}
if (compiler.renderLoop != null)
{
keywordSO.EnableKeyword (compiler.renderLoop.GetRenderLoopKeyword ());
}
// horrible workaround to GUI warning issues
compileMat = targetMat;
compileName = shaderName;
targetCompiler = compiler;
EditorApplication.delayCall += TriggerCompile;
}
if (tabIdx == 3)
{
if (!keywordSO.IsKeywordEnabled("_DISABLESPLATMAPS"))
{
Texture2DArray diff = targetMat.GetTexture("_Diffuse") as Texture2DArray;
var path = AssetDatabase.GetAssetPath(diff);
path = path.Replace("_diff_tarray.asset", ".asset");
TextureArrayConfig cfg = AssetDatabase.LoadAssetAtPath<TextureArrayConfig>(path);
if (cfg != null)
{
if (configUI == null)
{
configUI = new TextureArrayConfigUI();
}
SerializedObject so = new SerializedObject(cfg);
configUI.OnInspectorGUI(cfg, so);
}
}
}
}
static TextureArrayConfigUI configUI;
static Material compileMat;
static string compileName;
static MicroSplatCompiler targetCompiler;
protected void TriggerCompile()
{
targetCompiler.Compile(compileMat, compileName);
}
class Module
{
public Module(string url, string img)
{
assetStore = url;
texture = Resources.Load<Texture2D>(img);
}
public string assetStore;
public Texture2D texture;
}
void InitModules()
{
if (modules.Count == 0)
{
//
#if !__MICROSPLAT_GLOBALTEXTURE__
modules.Add(new Module(MicroSplatDefines.link_globalTexture, "microsplat_module_globaltexture"));
#endif
#if !__MICROSPLAT_SNOW__
modules.Add(new Module(MicroSplatDefines.link_snow, "microsplat_module_snow"));
#endif
#if !__MICROSPLAT_TESSELLATION__
modules.Add(new Module(MicroSplatDefines.link_tessellation, "microsplat_module_tessellation"));
#endif
#if !__MICROSPLAT_DETAILRESAMPLE__
modules.Add(new Module(MicroSplatDefines.link_antitile, "microsplat_module_detailresample"));
#endif
#if !__MICROSPLAT_TERRAINBLEND__
modules.Add(new Module(MicroSplatDefines.link_terrainblend, "microsplat_module_terrainblending"));
#endif
#if !__MICROSPLAT_STREAMS__
modules.Add(new Module(MicroSplatDefines.link_streams, "microsplat_module_streams"));
#endif
#if !__MICROSPLAT_ALPHAHOLE__
modules.Add(new Module(MicroSplatDefines.link_alphahole, "microsplat_module_alphahole"));
#endif
#if !__MICROSPLAT_TRIPLANAR__
modules.Add(new Module(MicroSplatDefines.link_triplanar, "microsplat_module_triplanaruvs"));
#endif
#if !__MICROSPLAT_TEXTURECLUSTERS__
modules.Add(new Module(MicroSplatDefines.link_textureclusters, "microsplat_module_textureclusters"));
#endif
#if !__MICROSPLAT_WINDGLITTER__
modules.Add(new Module(MicroSplatDefines.link_windglitter, "microsplat_module_windglitter"));
#endif
#if !__MICROSPLAT_PROCTEX__
modules.Add(new Module(MicroSplatDefines.link_proctex, "microsplat_module_proctexture"));
#endif
#if !__MICROSPLAT_LOWPOLY__
modules.Add(new Module(MicroSplatDefines.link_lowpoly, "microsplat_module_lowpoly"));
#endif
#if !__MICROSPLAT_MESHTERRAIN__
modules.Add(new Module(MicroSplatDefines.link_meshterrain, "microsplat_module_terrainmesh"));
#endif
#if !__MICROSPLAT_MESH__
modules.Add(new Module(MicroSplatDefines.link_meshworkflow, "microsplat_module_meshworkflow"));
#endif
#if !__MICROSPLAT_DIGGER__
modules.Add(new Module(MicroSplatDefines.link_digger, "microsplat_module_digger"));
#endif
#if !__MICROSPLAT_TRAX__
modules.Add(new Module(MicroSplatDefines.link_trax, "microsplat_module_trax"));
#endif
#if !__MICROSPLAT_POLARIS__
modules.Add(new Module(MicroSplatDefines.link_polaris, "microsplat_module_polaris"));
#endif
#if !__MICROSPLAT_SCATTER__
modules.Add (new Module (MicroSplatDefines.link_scatter, "microsplat_module_scatter"));
#endif
#if !__MICROSPLAT_DECAL__
modules.Add (new Module (MicroSplatDefines.link_decal, "microsplat_module_decals"));
#endif
int n = modules.Count;
if (n > 1)
{
System.Random rnd = new System.Random((int)(UnityEngine.Random.value * 1000));
while (n > 1)
{
n--;
int k = rnd.Next(n + 1);
var value = modules[k];
modules[k] = modules[n];
modules[n] = value;
}
}
}
}
List<Module> modules = new List<Module>();
void DrawModule(Module m)
{
if (GUILayout.Button(m.texture, GUI.skin.box, GUILayout.Width(92), GUILayout.Height(92)))
{
Application.OpenURL(m.assetStore);
}
}
Vector2 moduleScroll;
void DrawModules()
{
InitModules();
if (modules.Count == 0)
{
return;
}
EditorGUILayout.LabelField("Want more features? Add them here..");
moduleScroll = EditorGUILayout.BeginScrollView(moduleScroll, GUILayout.Height(100));
GUILayout.BeginHorizontal();
for (int i = 0; i < modules.Count; ++i)
{
DrawModule(modules[i]);
}
GUILayout.EndHorizontal();
EditorGUILayout.EndScrollView();
}
}

View File

@@ -0,0 +1,630 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Text;
using JBooth.MicroSplat;
using System.Collections.Generic;
using System.Linq;
public partial class MicroSplatShaderGUI : ShaderGUI
{
static List<IRenderLoopAdapter> availableRenderLoops = new List<IRenderLoopAdapter> ();
[MenuItem ("Window/MicroSplat/Utilities/Regenerate all Texture Arrays")]
static void RegenAllArrays ()
{
var configs = AssetDatabase.FindAssets ("t:TextureArrayConfig");
foreach (var c in configs)
{
var path = AssetDatabase.GUIDToAssetPath (c);
TextureArrayConfig cfg = AssetDatabase.LoadAssetAtPath<TextureArrayConfig> (path);
if (cfg != null)
{
Debug.Log ("Recompressing " + path);
TextureArrayConfigEditor.CompileConfig (cfg);
}
Resources.UnloadAsset (cfg);
cfg = null;
}
}
[MenuItem ("Window/MicroSplat/Utilities/Regenerate all Shaders")]
static void RegenAllShaders ()
{
var mats = AssetDatabase.FindAssets ("t:Material");
foreach (var m in mats)
{
Material mat = AssetDatabase.LoadAssetAtPath<Material> (AssetDatabase.GUIDToAssetPath (m));
if (mat != null && mat.shader != null)
{
if (MicroSplatUtilities.CanFindKeywords (mat))
{
Debug.Log ("Regenerating shader " + AssetDatabase.GetAssetPath (mat.shader));
MicroSplatShaderGUI.MicroSplatCompiler compiler = new MicroSplatShaderGUI.MicroSplatCompiler ();
compiler.Compile (mat);
}
}
}
}
[MenuItem ("Assets/Create/Shader/MicroSplat Shader")]
static void NewShader2 ()
{
NewShader ();
}
[MenuItem ("Assets/Create/MicroSplat/MicroSplat Shader")]
public static Shader NewShader ()
{
string path = "Assets";
foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
{
path = AssetDatabase.GetAssetPath (obj);
if (System.IO.File.Exists (path))
{
path = System.IO.Path.GetDirectoryName (path);
}
break;
}
path = path.Replace ("\\", "/");
path = AssetDatabase.GenerateUniqueAssetPath (path + "/MicroSplat.shader");
string name = path.Substring (path.LastIndexOf ("/"));
name = name.Substring (0, name.IndexOf ("."));
name = name.Replace("/", "");
string [] keywords = new string [0];
MicroSplatCompiler compiler = new MicroSplatCompiler ();
compiler.Init ();
string ret = compiler.Compile (keywords, name, null);
System.IO.File.WriteAllText (path, ret);
AssetDatabase.Refresh ();
return AssetDatabase.LoadAssetAtPath<Shader> (path);
}
public static string ReplaceLastOccurrence(string source, string find, string replace)
{
int place = source.LastIndexOf(find);
if (place == -1)
return source;
return source.Remove(place, find.Length).Insert(place, replace);
}
public static Material NewShaderAndMaterial (string path, string name, string[] keywords = null)
{
// if no branch sampling is specified, go straight to aggressive. Usually defaults are not done this way, but this seems
// to make the most sense..
if (!keywords.Contains("_BRANCHSAMPLES"))
{
System.Array.Resize (ref keywords, keywords.Length + 2);
keywords [keywords.Length - 2] = "_BRANCHSAMPLES";
keywords [keywords.Length - 1] = "_BRANCHSAMPLESARG";
}
string shaderPath = AssetDatabase.GenerateUniqueAssetPath (path + "/MicroSplat.shader");
string shaderBasePath = ReplaceLastOccurrence(shaderPath, ".shader", "_Base.shader");
string matPath = AssetDatabase.GenerateUniqueAssetPath (path + "/MicroSplat.mat");
shaderPath = shaderPath.Replace ("//", "/");
shaderBasePath = shaderBasePath.Replace ("//", "/");
matPath = matPath.Replace ("//", "/");
MicroSplatCompiler compiler = new MicroSplatCompiler ();
compiler.Init ();
if (keywords == null)
{
keywords = new string[0];
}
string baseName = "Hidden/MicroSplat/" + name + "_Base";
string baseShader = compiler.Compile (keywords, baseName);
string regularShader = compiler.Compile (keywords, name, baseName);
System.IO.File.WriteAllText (shaderPath, regularShader);
System.IO.File.WriteAllText (shaderBasePath, baseShader);
compiler.GenerateAuxShaders (name, shaderPath, new List<string>(keywords));
AssetDatabase.Refresh ();
Shader s = AssetDatabase.LoadAssetAtPath<Shader> (shaderPath);
if (s == null)
{
Debug.LogError ("Shader not found at path " + shaderPath);
}
Material m = new Material (s);
AssetDatabase.CreateAsset (m, matPath);
AssetDatabase.SaveAssets ();
AssetDatabase.Refresh ();
var kwds = MicroSplatUtilities.FindOrCreateKeywords (m);
kwds.keywords = new List<string> (keywords);
EditorUtility.SetDirty (kwds);
var propData = MicroSplatShaderGUI.FindOrCreatePropTex (m);
if (propData != null)
{
EditorUtility.SetDirty (propData);
}
AssetDatabase.SaveAssets ();
return AssetDatabase.LoadAssetAtPath<Material> (matPath);
}
public static Material NewShaderAndMaterial (Terrain t, string [] keywords = null)
{
string path = MicroSplatUtilities.RelativePathFromAsset (t.terrainData);
return NewShaderAndMaterial (path, t.name, keywords);
}
public class MicroSplatCompiler
{
public class AuxShader
{
public AuxShader (string trig, string ext) { trigger = trig; extension = ext; }
public string trigger;
public string extension;
public string customEditor;
public string options;
}
public void GenerateAuxShaders(string name, string path, List<string> keywords)
{
var exts = new List<FeatureDescriptor> (extensions); // prevent recursive access due to init
foreach (var e in exts)
{
var aux = e.GetAuxShader ();
if (aux != null)
{
if (keywords.Contains (aux.trigger))
{
var okeys = new List<string> (keywords);
// remove all other trigger keywords
foreach (var oe in exts)
{
if (oe != e)
{
var oaux = oe.GetAuxShader ();
if (oaux != null)
{
okeys.Remove (oaux.trigger);
}
}
}
string opath = path.Replace (".shader", aux.extension + ".shader");
if (keywords.Contains ("_MSRENDERLOOP_BETTERSHADERS"))
{
opath = opath.Replace (".shader", ".surfshader");
}
e.ModifyKeywordsForAuxShader (okeys);
var shader = this.Compile (okeys.ToArray (), name + aux.extension, null, aux);
MicroSplatUtilities.Checkout (opath);
System.IO.File.WriteAllText (opath, shader);
}
}
}
}
public List<FeatureDescriptor> extensions = new List<FeatureDescriptor> ();
public string GetShaderModel (string[] features)
{
string minModel = "3.5";
for (int i = 0; i < extensions.Count; ++i)
{
if (extensions [i].RequiresShaderModel46 ())
{
minModel = "4.6";
}
}
if (features.Contains ("_FORCEMODEL46"))
{
minModel = "4.6";
}
if (features.Contains ("_FORCEMODEL50"))
{
minModel = "5.0";
}
return minModel;
}
public void Init ()
{
if (extensions.Count == 0)
{
string[] paths = AssetDatabase.FindAssets("microsplat_ t:TextAsset");
for (int i = 0; i < paths.Length; ++i)
{
paths[i] = AssetDatabase.GUIDToAssetPath(paths[i]);
}
// init extensions
List<System.Type> extensionTypes = new List<System.Type>();
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
{
var asTypes = (from System.Type type in MicroSplatUtilities.GetLoadableTypes(a)
where type.IsSubclassOf(typeof(FeatureDescriptor))
select type).ToArray();
extensionTypes.AddRange(asTypes);
}
for (int i = 0; i < extensionTypes.Count; ++i)
{
var typ = extensionTypes[i];
FeatureDescriptor ext = System.Activator.CreateInstance(typ) as FeatureDescriptor;
ext.InitCompiler(paths);
extensions.Add(ext);
}
extensions.Sort(delegate (FeatureDescriptor p1, FeatureDescriptor p2)
{
if (p1.DisplaySortOrder() != 0 || p2.DisplaySortOrder() != 0)
{
return p1.DisplaySortOrder().CompareTo(p2.DisplaySortOrder());
}
return p1.GetType().Name.CompareTo(p2.GetType().Name);
});
}
if (availableRenderLoops == null || availableRenderLoops.Count == 0)
{
string[] paths = AssetDatabase.FindAssets("microsplat_ t:TextAsset");
for (int i = 0; i < paths.Length; ++i)
{
paths[i] = AssetDatabase.GUIDToAssetPath(paths[i]);
}
List<System.Type> adapterTypes = new List<System.Type>();
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
{
var adaptTypes = (from System.Type type in MicroSplatUtilities.GetLoadableTypes(a)
where (type.GetInterfaces().Contains(typeof(IRenderLoopAdapter)))
select type).ToArray();
adapterTypes.AddRange(adaptTypes);
}
availableRenderLoops.Clear ();
for (int i = 0; i < adapterTypes.Count; ++i)
{
var typ = adapterTypes [i];
IRenderLoopAdapter adapter = System.Activator.CreateInstance (typ) as IRenderLoopAdapter;
adapter.Init (paths);
availableRenderLoops.Add (adapter);
}
}
}
public static void AddPipelineKeywords(ref string[] keywords)
{
var pipeline = MicroSplatUtilities.DetectPipeline();
if (pipeline == MicroSplatUtilities.PipelineType.HDPipeline)
{
System.Array.Resize(ref keywords, keywords.Length + 4);
keywords[keywords.Length - 4] = "_MSRENDERLOOP_UNITYHD";
keywords[keywords.Length - 3] = "_MSRENDERLOOP_UNITYHDRP2020";
keywords[keywords.Length - 2] = "_MSRENDERLOOP_UNITYHDRP2021";
keywords[keywords.Length - 1] = "_MSRENDERLOOP_UNITYHDRP2022";
}
else if (pipeline == MicroSplatUtilities.PipelineType.UniversalPipeline)
{
System.Array.Resize(ref keywords, keywords.Length + 4);
keywords[keywords.Length - 4] = "_MSRENDERLOOP_UNITYLD";
keywords[keywords.Length - 3] = "_MSRENDERLOOP_UNITYURP2020";
keywords[keywords.Length - 2] = "_MSRENDERLOOP_UNITYURP2021";
keywords[keywords.Length - 1] = "_MSRENDERLOOP_UNITYURP2022";
}
}
public void WriteDefines (string[] features, StringBuilder sb)
{
sb.AppendLine ();
for (int i = 0; i < features.Length; ++i)
{
sb.AppendLine (" #define " + features [i] + " 1");
}
sb.AppendLine ();
}
public void WritePerMaterialCBuffer(string[] features, StringBuilder sb)
{
for (int i = 0; i < extensions.Count; ++i)
{
var ext = extensions [i];
if (ext.GetVersion () == MicroSplatVersion)
{
extensions [i].WritePerMaterialCBuffer (features, sb);
}
}
}
public void WriteExtensions (string[] features, StringBuilder sb)
{
// sort for compile order
extensions.Sort (delegate (FeatureDescriptor p1, FeatureDescriptor p2)
{
if (p1.CompileSortOrder () != p2.CompileSortOrder ())
return (p1.CompileSortOrder () < p2.CompileSortOrder ()) ? -1 : 1;
return p1.GetType ().Name.CompareTo (p2.GetType ().Name);
});
// shared functions first, so modules can use them in other modules
for (int i = 0; i < extensions.Count; ++i)
{
var ext = extensions [i];
if (ext.GetVersion () == MicroSplatVersion)
{
extensions [i].WriteSharedFunctions (features, sb);
}
}
// now actual function.
for (int i = 0; i < extensions.Count; ++i)
{
var ext = extensions [i];
if (ext.GetVersion () == MicroSplatVersion)
{
extensions [i].WriteFunctions (features, sb);
}
}
// sort by name, then display order..
extensions.Sort (delegate (FeatureDescriptor p1, FeatureDescriptor p2)
{
if (p1.DisplaySortOrder () != 0 || p2.DisplaySortOrder () != 0)
{
return p1.DisplaySortOrder ().CompareTo (p2.DisplaySortOrder ());
}
return p1.GetType ().Name.CompareTo (p2.GetType ().Name);
});
}
public void WriteProperties (string[] features, StringBuilder sb, AuxShader auxShader)
{
bool max4 = features.Contains ("_MAX4TEXTURES");
bool max8 = features.Contains ("_MAX8TEXTURES");
bool max12 = features.Contains ("_MAX12TEXTURES");
bool max20 = features.Contains ("_MAX20TEXTURES");
bool max24 = features.Contains ("_MAX24TEXTURES");
bool max28 = features.Contains ("_MAX28TEXTURES");
bool max32 = features.Contains ("_MAX32TEXTURES");
// always have this for UVs
sb.AppendLine (" [HideInInspector] _Control0 (\"Control0\", 2D) = \"red\" {}");
bool custom = features.Contains<string> ("_CUSTOMSPLATTEXTURES");
string controlName = "_Control";
if (custom)
{
controlName = "_CustomControl";
}
if (custom)
{
sb.AppendLine (" [HideInInspector] _CustomControl0 (\"Control0\", 2D) = \"red\" {}");
}
if (!features.Contains ("_MICROVERTEXMESH") && !features.Contains("_MEGASPLAT") && !features.Contains("_MEGASPLATTEXTURE"))
{
if (!max4)
{
sb.AppendLine (" [HideInInspector] " + controlName + "1 (\"Control1\", 2D) = \"black\" {}");
}
if (!max4 && !max8)
{
sb.AppendLine (" [HideInInspector] " + controlName + "2 (\"Control2\", 2D) = \"black\" {}");
}
if (!max4 && !max8 && !max12)
{
sb.AppendLine (" [HideInInspector] " + controlName + "3 (\"Control3\", 2D) = \"black\" {}");
}
if (max20 || max24 || max28 || max32)
{
sb.AppendLine (" [HideInInspector] " + controlName + "4 (\"Control4\", 2D) = \"black\" {}");
}
if (max24 || max28 || max32)
{
sb.AppendLine (" [HideInInspector] " + controlName + "5 (\"Control5\", 2D) = \"black\" {}");
}
if (max28 || max32)
{
sb.AppendLine (" [HideInInspector] " + controlName + "6 (\"Control6\", 2D) = \"black\" {}");
}
if (max32)
{
sb.AppendLine (" [HideInInspector] " + controlName + "7 (\"Control7\", 2D) = \"black\" {}");
}
}
for (int i = 0; i < extensions.Count; ++i)
{
var ext = extensions [i];
if (ext.GetVersion () == MicroSplatVersion)
{
ext.WriteProperties (features, sb);
}
sb.AppendLine ("");
}
}
public static bool HasDebugFeature (string[] features)
{
for (int i = 0; i < features.Length; ++i)
{
if (features [i].StartsWith ("_DEBUG_", System.StringComparison.CurrentCulture))
return true;
}
return false;
}
public IRenderLoopAdapter renderLoop = null;
void SetPreferedRenderLoopByName(string[] features, string keyword)
{
if (!features.Contains (keyword))
return;
for (int i = 0; i < availableRenderLoops.Count; ++i)
{
if (availableRenderLoops [i].GetRenderLoopKeyword() == keyword)
{
renderLoop = availableRenderLoops [i];
}
}
}
public string Compile (string[] features, string name, string baseName = null, AuxShader auxShader = null)
{
try
{
EditorUtility.DisplayProgressBar ("Compiling Shaders", "...", 0.5f);
Init ();
// get default render loop if it doesn't exist
if (renderLoop == null)
{
for (int i = 0; i < availableRenderLoops.Count; ++i)
{
if (availableRenderLoops [i].GetType () == typeof (SurfaceShaderRenderLoopAdapter))
{
renderLoop = availableRenderLoops [i];
}
}
}
AddPipelineKeywords(ref features);
// TODO: this would be better if we asked the render loop if it is in the feature list, but
// would require a change to interface, so wait until we have a version bump.
#if UNITY_2022_2_OR_NEWER
SetPreferedRenderLoopByName(features, "_MSRENDERLOOP_UNITYHDRP2022");
SetPreferedRenderLoopByName(features, "_MSRENDERLOOP_UNITYURP2022");
#elif UNITY_2021_2_OR_NEWER
SetPreferedRenderLoopByName(features, "_MSRENDERLOOP_UNITYHDRP2021");
SetPreferedRenderLoopByName(features, "_MSRENDERLOOP_UNITYURP2021");
#elif UNITY_2020_2_OR_NEWER
SetPreferedRenderLoopByName (features, "_MSRENDERLOOP_UNITYURP2020");
SetPreferedRenderLoopByName (features, "_MSRENDERLOOP_UNITYHDRP2020");
#else
SetPreferedRenderLoopByName (features, "_MSRENDERLOOP_UNITYLD");
SetPreferedRenderLoopByName (features, "_MSRENDERLOOP_UNITYHD");
#endif
for (int i = 0; i < extensions.Count; ++i)
{
var ext = extensions [i];
ext.Unpack (features);
}
StringBuilder sb = renderLoop.WriteShader(features, this, auxShader, name, baseName);
for (int i = 0; i < extensions.Count; ++i)
{
var ext = extensions [i];
ext.OnPostGeneration (ref sb, features, name, baseName, auxShader);
}
// in URP/HDRP light layers require this for terrain
if (features.Contains("_MICROTERRAIN"))
{
sb = sb.Replace("#pragma instancing_options renderinglayer", "#pragma instancing_options norenderinglayer assumeuniformscaling nomatrices nolightprobe nolightmap");
}
string output = sb.ToString ();
// fix newline mixing warnings..
output = System.Text.RegularExpressions.Regex.Replace (output, "\r\n?|\n", System.Environment.NewLine);
EditorUtility.ClearProgressBar ();
return output;
}
catch
{
EditorUtility.ClearProgressBar ();
return "";
}
}
public void Compile (Material m, string shaderName = null)
{
int hash = 0;
MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords (m);
for (int i = 0; i < keywords.keywords.Count; ++i)
{
hash += 31 + keywords.keywords [i].GetHashCode ();
}
var path = AssetDatabase.GetAssetPath (m.shader);
string nm = m.shader.name;
if (!string.IsNullOrEmpty (shaderName))
{
nm = shaderName;
}
string baseName = "Hidden/" + nm + "_Base" + hash.ToString ();
string terrainShader = Compile (keywords.keywords.ToArray (), nm, baseName);
if (renderLoop != null)
{
keywords.EnableKeyword (renderLoop.GetRenderLoopKeyword ());
}
GenerateAuxShaders (nm, path, keywords.keywords);
if (keywords.keywords.Contains ("_MSRENDERLOOP_BETTERSHADERS"))
{
path = path.Replace (".shader", ".surfshader");
}
else
{
path = path.Replace(".surfshader", ".shader");
}
MicroSplatUtilities.Checkout (path);
System.IO.File.WriteAllText (path, terrainShader);
if (keywords.IsKeywordEnabled ("_MICROTERRAIN"))
{
// generate fallback
string[] oldKeywords = new string[keywords.keywords.Count];
System.Array.Copy (keywords.keywords.ToArray (), oldKeywords, keywords.keywords.Count);
keywords.DisableKeyword ("_TESSDISTANCE");
keywords.DisableKeyword("_TESSEDGE");
keywords.DisableKeyword ("_POM");
keywords.DisableKeyword ("_PARALLAX");
keywords.DisableKeyword ("_DETAILNOISE");
keywords.EnableKeyword ("_MICROSPLATBASEMAP");
string fallback = Compile (keywords.keywords.ToArray (), baseName);
keywords.keywords = new List<string> (oldKeywords);
string fallbackPath = ReplaceLastOccurrence(path, ".shader", "_Base.shader");
fallbackPath = ReplaceLastOccurrence(fallbackPath, ".surfshader", "_Base.surfshader");
MicroSplatUtilities.Checkout (fallbackPath);
System.IO.File.WriteAllText (fallbackPath, fallback);
}
EditorUtility.SetDirty (m);
AssetDatabase.Refresh ();
#if __MICROSPLAT_MESH__
MicroSplatMesh.ClearMaterialCache ();
#endif
MicroSplatObject.SyncAll ();
}
}
}

View File

@@ -0,0 +1,46 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using JBooth.MicroSplat;
using System.Linq;
public partial class MicroSplatShaderGUI : ShaderGUI
{
// get, load, or create the property texture for this material..
public static MicroSplatPropData FindOrCreatePropTex(Material targetMat)
{
MicroSplatPropData propData = null;
// look for it next to the material?
var path = AssetDatabase.GetAssetPath(targetMat);
path = path.Replace("\\", "/");
if (!string.IsNullOrEmpty(path))
{
path = path.Substring(0, path.LastIndexOf("."));
path += "_propdata.asset";
// mesh terrains are in a sub directory when lod'd, so seak back and get the shared propData
if (path.Contains ("MeshTerrain/MicroSplatData/") && !System.IO.File.Exists(path))
{
path = path.Replace ("MeshTerrain/MicroSplatData/", "");
}
propData = AssetDatabase.LoadAssetAtPath<MicroSplatPropData>(path);
if (propData == null)
{
propData = MicroSplatPropData.CreateInstance<MicroSplatPropData>();
AssetDatabase.CreateAsset(propData, path);
AssetDatabase.SaveAssets();
}
}
targetMat.SetTexture ("_PerTexProps", propData.GetTexture ());
return propData;
}
}

View File

@@ -0,0 +1,290 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using JBooth.MicroSplat;
namespace JBooth.MicroSplat
{
[CustomEditor(typeof(MicroSplatTerrain))]
[CanEditMultipleObjects]
public partial class MicroSplatTerrainEditor : Editor
{
#if __MICROSPLAT__
#if __MICROSPLAT_GLOBALTEXTURE__
static GUIContent geoTexOverride = new GUIContent("Geo Texture Override", "If you want each terrain object to have it's own geo texture instead of the one defined in the material, add it here");
static GUIContent geoTintOverride = new GUIContent("Tint Texture Override", "If you want each terrain object to have it's own global tint instead of the one defined in the material, add it here");
static GUIContent geoNormalOverride = new GUIContent("Global Normal Override", "If you want each terrain object to have it's own global normal instead of the one defined in the material, add it here");
static GUIContent geoSAOMOverride = new GUIContent("Global SOAM Override", "If you want each terrain to have it's own Smoothness(R), AO(G) and Metallic (B) map instead of the one defined in the material, add it here");
static GUIContent geoEmisOverride = new GUIContent("Global Emissive Override", "If you want each terrain to have it's own Emissive map instead of the one defined in the material, set it here");
#endif
#if __MICROSPLAT_SCATTER__
static GUIContent scatterMapOverride = new GUIContent("Scatter Map Override", "Scatter Control Texture");
#endif
#if __MICROSPLAT_SNOW__
static GUIContent snowMaskOverride = new GUIContent("Snow Mask Override", "If you want each terrain to have it's own snow mask, assign it here");
#endif
static GUIContent perPixelNormal = new GUIContent("Per Pixel Normal", "Per Pixel normal map if using non-instanced terrain rendering");
#if __MICROSPLAT_ALPHAHOLE__
static GUIContent clipMapOverride = new GUIContent("Clip Map Override", "Provide a unique clip map for each terrain");
#endif
#if __MICROSPLAT_PROCTEX__
static GUIContent biomeOverride = new GUIContent("Biome Map Override", "Biome map for this terrain");
static GUIContent biomeOverride2 = new GUIContent("Biome Map2 Override", "Biome map for this terrain");
static GUIContent CCavityMap = new GUIContent("Cavity Map Override", "Cavity map for this terrain");
#endif
#if __MICROSPLAT_STREAMS__
static GUIContent streamOverride = new GUIContent("Stream Map Override", "Wetness, Puddles, Streams and Lava map for this terrain");
#endif
static GUIContent CTemplateMaterial = new GUIContent("Template Material", "Material to use for this terrain");
#if (VEGETATION_STUDIO || VEGETATION_STUDIO_PRO)
static GUIContent CVSGrassMap = new GUIContent("Grass Map", "Grass Map from Vegetation Studio");
static GUIContent CVSShadowMap = new GUIContent("Shadow Map", "Shadow map texture from Vegetation Studio");
#endif
static GUIContent CBaseMap = new GUIContent("Base Map Shader", "Asset Bundles/Addressables have a bug that they don't work with the base map shader, so we keep a hard reference to it so the bundle will load it and it will be found");
static GUIContent CBlendMat = new GUIContent("Blend Mat", "Blending material for terrain blending");
static GUIContent CCustomSplat0 = new GUIContent("Custom Splat 0", "Custom splat map for textures 0-3");
static GUIContent CCustomSplat1 = new GUIContent("Custom Splat 1", "Custom splat map for textures 4-7");
static GUIContent CCustomSplat2 = new GUIContent("Custom Splat 2", "Custom splat map for textures 8-11");
static GUIContent CCustomSplat3 = new GUIContent("Custom Splat 3", "Custom splat map for textures 12-15");
static GUIContent CCustomSplat4 = new GUIContent("Custom Splat 4", "Custom splat map for textures 16-19");
static GUIContent CCustomSplat5 = new GUIContent("Custom Splat 5", "Custom splat map for textures 20-23");
static GUIContent CCustomSplat6 = new GUIContent("Custom Splat 6", "Custom splat map for textures 24-27");
static GUIContent CCustomSplat7 = new GUIContent("Custom Splat 7", "Custom splat map for textures 28-31");
static GUIContent CPatchBoundsMultiplier = new GUIContent("Patch Bounds Multiplier", "Unity can clip vertices when tessellation is enabled. Unfortunately it doesn't expose a way to expand the bounds by the max displacement amount, only a mutliplier. So if this is happening, you can scale the bounds here. A value of 1.15 on each axis is usually enough. Higher values will cause more off-screen terrain to be drawn");
static GUIContent CMegaSplatMap = new GUIContent("Splat Map", "MegaSplat format control map");
SerializedProperty templateMaterial;
void OnEnable()
{
templateMaterial = serializedObject.FindProperty("templateMaterial");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
MicroSplatTerrain t = target as MicroSplatTerrain;
if (t == null)
{
EditorGUILayout.HelpBox("No Terrain Present, please put this component on a terrain", MessageType.Error);
return;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
if (GUILayout.Button("Documentation"))
{
Application.OpenURL("https://docs.google.com/document/d/1t_ZEKW8bHJqVWulH9Tcu-V5PIMr-sBOV_Ob7k2y1XY8/");
}
EditorGUILayout.Space();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(templateMaterial, CTemplateMaterial);
if (EditorGUI.EndChangeCheck())
{
MicroSplatTerrain.SyncAll();
}
EditorGUI.BeginChangeCheck();
serializedObject.ApplyModifiedProperties();
if (DoConvertGUI(t))
{
return;
}
if (t.templateMaterial == null)
{
return;
}
// People love to rename stuff, but there's no way to associate various objects
// with a material in Unity, and the material needs the keywords, propdata, etc obejcts,
// so this is looked up via name. People are also sloppy about files, and end up with
// 10 different microsplat setups in one directory. To help reduce support, we automatically
// create these files with the correct names when missing. We also see if they are assigned,
// but named wrong, meaning the material will just create it later. When this happens, we
// reassign it to one with the correct name.
if (t.propData == null)
{
t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
EditorUtility.SetDirty(t);
MicroSplatObject.SyncAll();
}
else if (!t.propData.name.StartsWith(t.templateMaterial.name + "_propdata"))
{
Debug.LogWarning("PropData object assigned to terrain was not the one associated with the material, reassigning or creating one with the correct name");
t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
EditorUtility.SetDirty(t);
}
if (t.keywordSO == null)
{
t.keywordSO = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);
EditorUtility.SetDirty(t);
}
else
{
if (!t.keywordSO.name.StartsWith(t.templateMaterial.name + "_keywords"))
{
Debug.LogWarning("Keyword file is not named correctly! Attempting to find or create a new one");
t.keywordSO = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);
EditorUtility.SetDirty(t);
}
}
EditorGUI.BeginChangeCheck();
#if __MICROSPLAT_PROCTEX__
if (t.keywordSO.IsKeywordEnabled("_PROCEDURALTEXTURE") || t.keywordSO.IsKeywordEnabled("_PCHEIGHTGRADIENT") || t.keywordSO.IsKeywordEnabled("_PCHEIGHTHSV"))
{
var old = t.procTexCfg;
t.procTexCfg = MicroSplatProceduralTexture.FindOrCreateProceduralConfig(t.templateMaterial);
if (old != t.procTexCfg)
{
EditorUtility.SetDirty(t);
MicroSplatObject.SyncAll();
}
}
#endif
#if __MICROSPLAT_TERRAINBLEND__ || __MICROSPLAT_STREAMS__
DoTerrainDescGUI();
#endif
DoPerPixelNormalGUI();
#if __MICROSPLAT_PROCTEX__
if (t.keywordSO.IsKeywordEnabled(MicroSplatProceduralTexture.GetFeatureName(MicroSplatProceduralTexture.DefineFeature._PCCAVITY))
|| t.keywordSO.IsKeywordEnabled(MicroSplatProceduralTexture.GetFeatureName(MicroSplatProceduralTexture.DefineFeature._PCFLOW)))
{
DoCavityMapGUI();
}
#endif
// could move this to some type of interfaced component created by the module if this becomes a thing,
// but I think this will be most of the cases?
MicroSplatUtilities.DrawTextureField(t, CCustomSplat0, ref t.customControl0, "_CUSTOMSPLATTEXTURES");
MicroSplatUtilities.DrawTextureField(t, CCustomSplat1, ref t.customControl1, "_CUSTOMSPLATTEXTURES");
MicroSplatUtilities.DrawTextureField(t, CCustomSplat2, ref t.customControl2, "_CUSTOMSPLATTEXTURES");
MicroSplatUtilities.DrawTextureField(t, CCustomSplat3, ref t.customControl3, "_CUSTOMSPLATTEXTURES");
MicroSplatUtilities.DrawTextureField(t, CCustomSplat4, ref t.customControl4, "_CUSTOMSPLATTEXTURES");
MicroSplatUtilities.DrawTextureField(t, CCustomSplat5, ref t.customControl5, "_CUSTOMSPLATTEXTURES");
MicroSplatUtilities.DrawTextureField(t, CCustomSplat6, ref t.customControl6, "_CUSTOMSPLATTEXTURES");
MicroSplatUtilities.DrawTextureField(t, CCustomSplat7, ref t.customControl7, "_CUSTOMSPLATTEXTURES");
#if __MICROSPLAT_MEGA__
MicroSplatUtilities.DrawTextureField(t, CMegaSplatMap, ref t.megaSplatMap, "_MEGASPLATTEXTURE");
#endif
// perpixel normal
MicroSplatUtilities.DrawTextureField(t, perPixelNormal, ref t.perPixelNormal, "_PERPIXELNORMAL");
// global texture overrides
#if __MICROSPLAT_GLOBALTEXTURE__
MicroSplatUtilities.DrawTextureField(t, geoTexOverride, ref t.geoTextureOverride, "_GEOMAP");
MicroSplatUtilities.DrawTextureField(t, geoTintOverride, ref t.tintMapOverride, "_GLOBALTINT");
MicroSplatUtilities.DrawTextureField(t, geoNormalOverride, ref t.globalNormalOverride, "_GLOBALNORMALS");
MicroSplatUtilities.DrawTextureField(t, geoSAOMOverride, ref t.globalSAOMOverride, "_GLOBALSMOOTHAOMETAL");
MicroSplatUtilities.DrawTextureField(t, geoEmisOverride, ref t.globalEmisOverride, "_GLOBALEMIS");
#endif
#if __MICROSPLAT_SCATTER__
MicroSplatUtilities.DrawTextureField(t, scatterMapOverride, ref t.scatterMapOverride, "_SCATTER");
#endif
#if __MICROSPLAT_SNOW__
MicroSplatUtilities.DrawTextureField(t, snowMaskOverride, ref t.snowMaskOverride, "_SNOWMASK");
#endif
#if __MICROSPLAT_ALPHAHOLE__
// alpha hole override
MicroSplatUtilities.DrawTextureField(t, clipMapOverride, ref t.clipMap, "_ALPHAHOLETEXTURE");
#endif
#if (VEGETATION_STUDIO || VEGETATION_STUDIO_PRO)
// vsstudio overrides
MicroSplatUtilities.DrawTextureField(t, CVSGrassMap, ref t.vsGrassMap, "_VSGRASSMAP");
MicroSplatUtilities.DrawTextureField(t, CVSShadowMap, ref t.vsShadowMap, "_VSSHADOWMAP");
#endif
#if __MICROSPLAT_PROCTEX__
MicroSplatUtilities.DrawTextureField(t, biomeOverride, ref t.procBiomeMask, "_PCBIOMEMASK", "_PCBIOMEMASK16", null, null, false);
MicroSplatUtilities.DrawTextureField(t, biomeOverride2, ref t.procBiomeMask2, "_PCBIOMEMASK2");
MicroSplatUtilities.DrawTextureField(t, CCavityMap, ref t.cavityMap, "_PROCEDURALTEXTURE");
#endif
#if __MICROSPLAT_STREAMS__
MicroSplatUtilities.DrawTextureField(t, streamOverride, ref t.streamTexture, "_WETNESS", "_PUDDLES", "_STREAMS", "_LAVA", false);
#endif
#if __MICROSPLAT_TESSELLATION__
t.patchBoundsMultiplier = EditorGUILayout.Vector3Field(CPatchBoundsMultiplier, t.patchBoundsMultiplier);
#endif
if (EditorGUI.EndChangeCheck())
{
MicroSplatTerrain.SyncAll();
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Sync"))
{
var mgr = target as MicroSplatTerrain;
mgr.Sync();
EditorUtility.SetDirty(mgr);
}
if (GUILayout.Button("Sync All"))
{
MicroSplatTerrain.SyncAll();
var mgr = target as MicroSplatTerrain;
EditorUtility.SetDirty(mgr);
}
EditorGUILayout.EndHorizontal();
BakingGUI(t);
WeightLimitingGUI(t);
ImportExportGUI();
if (MicroSplatUtilities.DrawRollup("Debug", false, true))
{
EditorGUI.indentLevel += 2;
EditorGUILayout.HelpBox("These should not need to be edited unless something funky has happened. They are automatically managed by MicroSplat.", MessageType.Info);
t.propData = EditorGUILayout.ObjectField("Per Texture Data", t.propData, typeof(MicroSplatPropData), false) as MicroSplatPropData;
#if __MICROSPLAT_PROCTEX__
t.procTexCfg = EditorGUILayout.ObjectField("Procedural Config", t.procTexCfg, typeof(MicroSplatProceduralTextureConfig), false) as MicroSplatProceduralTextureConfig;
#endif
t.keywordSO = EditorGUILayout.ObjectField("Keywords", t.keywordSO, typeof(MicroSplatKeywords), false) as MicroSplatKeywords;
t.blendMat = EditorGUILayout.ObjectField(CBlendMat, t.blendMat, typeof(Material), false) as Material;
t.baseMapShader = EditorGUILayout.ObjectField(CBaseMap, t.baseMapShader, typeof(Shader), false) as Shader;
EditorGUI.indentLevel -= 2;
}
if (EditorGUI.EndChangeCheck())
{
EditorUtility.SetDirty(t);
}
}
#endif
}
}

View File

@@ -0,0 +1,631 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using JBooth.MicroSplat;
namespace JBooth.MicroSplat
{
public partial class MicroSplatTerrainEditor : Editor
{
public enum BakingResolutions
{
k256 = 256,
k512 = 512,
k1024 = 1024,
k2048 = 2048,
k4096 = 4096,
k8192 = 8192
};
public enum BakingPasses
{
Albedo = 1,
Height = 2,
Normal = 4,
Metallic = 8,
Smoothness = 16,
AO = 32,
Emissive = 64,
FinalNormal = 128,
#if __MICROSPLAT_PROCTEX__
ProceduralSplatOutput0 = 256,
ProceduralSplatOutput1 = 512,
ProceduralSplatOutput2 = 1024,
ProceduralSplatOutput3 = 2048,
ProceduralSplatOutput4 = 4096,
ProceduralSplatOutput5 = 8192,
ProceduralSplatOutput6 = 16384,
ProceduralSplatOutput7 = 32768,
ProceduralSplatOutput0A = 65536,
ProceduralSplatOutput1A = 131072,
ProceduralSplatOutput2A = 262144,
ProceduralSplatOutput3A = 524288,
ProceduralSplatOutput4A = 1048576,
ProceduralSplatOutput5A = 2097152,
ProceduralSplatOutput6A = 4194304,
ProceduralSplatOutput7A = 8388608,
#endif
};
public BakingPasses passes = 0;
public BakingResolutions res = BakingResolutions.k1024;
public static MicroSplatCompressor.Options compressOptions = new MicroSplatCompressor.Options();
#if __MICROSPLAT_PROCTEX__
public bool bakeSplats = false;
#endif
void DestroyTex(Texture2D tex)
{
if (tex != Texture2D.blackTexture)
{
DestroyImmediate(tex);
}
}
void GenerateWorldData(Terrain t, out Texture2D worldNormal, out Texture2D worldPos, int splatRes)
{
// World/normals are used in texturing, so we have to make them match.
worldPos = new Texture2D(splatRes, splatRes, TextureFormat.RGBAFloat, true, true);
worldNormal = new Texture2D(splatRes, splatRes, TextureFormat.RGBAFloat, true, true);
t.transform.rotation = Quaternion.identity;
for (int x = 0; x < splatRes; ++x)
{
float u = (float)x / (float)splatRes;
for (int y = 0; y < splatRes; ++y)
{
float v = (float)y / (float)splatRes;
float h = t.terrainData.GetInterpolatedHeight(u, v);
Vector3 n = t.terrainData.GetInterpolatedNormal(u, v);
Vector3 wp = t.transform.localToWorldMatrix.MultiplyPoint(new Vector3(u * t.terrainData.size.x, h, v * t.terrainData.size.z));
worldPos.SetPixel(x, y, new Color(wp.x, wp.y, wp.z));
worldNormal.SetPixel(x, y, new Color(n.x, n.y, n.z));
}
}
worldPos.Apply();
worldNormal.Apply();
}
bool needsBake = false;
public void BakingGUI(MicroSplatTerrain t)
{
if (needsBake && Event.current.type == EventType.Repaint)
{
needsBake = false;
Bake(t);
}
#if __MICROSPLAT_PROCTEX__
if (bakeSplats && Event.current.type == EventType.Repaint)
{
bakeSplats = false;
int alphaLayerCount = t.terrain.terrainData.alphamapLayers;
int splatRes = t.terrain.terrainData.alphamapResolution;
int splatCount = t.terrain.terrainData.terrainLayers.Length;
float[,,] splats = new float[splatRes, splatRes, splatCount];
// World/normals are used in texturing, so we have to make them match.
Texture2D worldPos, worldNormal;
GenerateWorldData(t.terrain, out worldNormal, out worldPos, splatRes);
for (int i = 0; i < alphaLayerCount; i = i + 4)
{
Texture2D tex = Texture2D.blackTexture;
Texture2D alpha = Texture2D.blackTexture;
if (i == 0)
{
tex = Bake(t, BakingPasses.ProceduralSplatOutput0, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput0A, splatRes, worldPos, worldNormal);
}
if (i == 4)
{
DestroyTex(tex);
DestroyTex(alpha);
tex = Bake(t, BakingPasses.ProceduralSplatOutput1, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput1A, splatRes, worldPos, worldNormal);
}
else if (i == 8)
{
DestroyTex(tex);
DestroyTex(alpha);
tex = Bake(t, BakingPasses.ProceduralSplatOutput2, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput2A, splatRes, worldPos, worldNormal);
}
else if (i == 12)
{
DestroyTex(tex);
DestroyTex(alpha);
tex = Bake(t, BakingPasses.ProceduralSplatOutput3, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput3A, splatRes, worldPos, worldNormal);
}
else if (i == 16)
{
DestroyTex(tex);
DestroyTex(alpha);
tex = Bake(t, BakingPasses.ProceduralSplatOutput4, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput4A, splatRes, worldPos, worldNormal);
}
else if (i == 20)
{
DestroyTex(tex);
DestroyTex(alpha);
tex = Bake(t, BakingPasses.ProceduralSplatOutput5, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput5A, splatRes, worldPos, worldNormal);
}
else if (i == 24)
{
DestroyTex(tex);
DestroyTex(alpha);
tex = Bake(t, BakingPasses.ProceduralSplatOutput6, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput6A, splatRes, worldPos, worldNormal);
}
else if (i == 28)
{
DestroyTex(tex);
DestroyTex(alpha);
tex = Bake(t, BakingPasses.ProceduralSplatOutput7, splatRes, worldPos, worldNormal);
alpha = Bake(t, BakingPasses.ProceduralSplatOutput7A, splatRes, worldPos, worldNormal);
}
for (int x = 0; x < splatRes; ++x)
{
for (int y = 0; y < splatRes; ++y)
{
Color c = tex.GetPixel(x, y);
Color a = alpha.GetPixel(x, y);
if (i < splatCount)
{
splats[y, x, i] = c.r;
}
if (i + 1 < splatCount)
{
splats[y, x, i + 1] = c.g;
}
if (i + 2 < splatCount)
{
splats[y, x, i + 2] = c.b;
}
if (i + 3 < splatCount)
{
splats[y, x, i + 3] = a.g;
}
}
}
}
DestroyImmediate(worldPos);
DestroyImmediate(worldNormal);
t.terrain.terrainData.SetAlphamaps(0, 0, splats);
EditorUtility.SetDirty(t.terrain.terrainData);
}
#endif
if (MicroSplatUtilities.DrawRollup("Render Baking", false))
{
res = (BakingResolutions)EditorGUILayout.EnumPopup(new GUIContent("Resolution"), res);
#if UNITY_2017_3_OR_NEWER
passes = (BakingPasses)EditorGUILayout.EnumFlagsField(new GUIContent("Features"), passes);
#else
passes = (BakingPasses)EditorGUILayout.EnumMaskPopup (new GUIContent ("Features"), passes);
#endif
if (GUILayout.Button("Export Selected"))
{
needsBake = true;
}
}
#if __MICROSPLAT_PROCTEX__
if (t.templateMaterial != null && t.keywordSO != null && t.keywordSO.IsKeywordEnabled("_PROCEDURALTEXTURE"))
{
if (MicroSplatUtilities.DrawRollup("Procedural Baking", false))
{
EditorGUILayout.Space();
if (GUILayout.Button("Bake Procedural to Terrain"))
{
bakeSplats = true;
}
EditorGUILayout.Space();
}
}
#endif
MicroSplatCompressor.DrawGUI(t, compressOptions);
}
bool IsEnabled(BakingPasses p)
{
return ((int)passes & (int)p) == (int)p;
}
static MicroSplatBaseFeatures.DefineFeature FeatureFromOutput(MicroSplatBaseFeatures.DebugOutput p)
{
if (p == MicroSplatBaseFeatures.DebugOutput.Albedo)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_ALBEDO;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.AO)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_AO;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.Emission)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_EMISSION;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.Height)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_HEIGHT;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.Metallic)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_METAL;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.Normal)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_NORMAL;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.Smoothness)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SMOOTHNESS;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.FinalNormalTangent)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_FINALNORMALTANGENT;
}
#if __MICROSPLAT_PROCTEX__
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput0)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT0;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput1)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT1;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput2)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT2;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput3)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT3;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput4)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT4;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput5)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT5;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput6)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT6;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput7)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT7;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput0A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT0A;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput1A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT1A;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput2A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT2A;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput3A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT3A;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput4A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT4A;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput5A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT5A;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput6A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT6A;
}
else if (p == MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput7A)
{
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_SPLAT7A;
}
#endif
return MicroSplatBaseFeatures.DefineFeature._DEBUG_OUTPUT_ALBEDO;
}
static MicroSplatBaseFeatures.DebugOutput OutputFromPass(BakingPasses p)
{
if (p == BakingPasses.Albedo)
{
return MicroSplatBaseFeatures.DebugOutput.Albedo;
}
else if (p == BakingPasses.AO)
{
return MicroSplatBaseFeatures.DebugOutput.AO;
}
else if (p == BakingPasses.Emissive)
{
return MicroSplatBaseFeatures.DebugOutput.Emission;
}
else if (p == BakingPasses.Height)
{
return MicroSplatBaseFeatures.DebugOutput.Height;
}
else if (p == BakingPasses.Metallic)
{
return MicroSplatBaseFeatures.DebugOutput.Metallic;
}
else if (p == BakingPasses.Normal)
{
return MicroSplatBaseFeatures.DebugOutput.Normal;
}
else if (p == BakingPasses.Smoothness)
{
return MicroSplatBaseFeatures.DebugOutput.Smoothness;
}
else if (p == BakingPasses.FinalNormal)
{
return MicroSplatBaseFeatures.DebugOutput.FinalNormalTangent;
}
#if __MICROSPLAT_PROCTEX__
else if (p == BakingPasses.ProceduralSplatOutput0)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput0;
}
else if (p == BakingPasses.ProceduralSplatOutput1)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput1;
}
else if (p == BakingPasses.ProceduralSplatOutput2)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput2;
}
else if (p == BakingPasses.ProceduralSplatOutput3)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput3;
}
else if (p == BakingPasses.ProceduralSplatOutput4)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput4;
}
else if (p == BakingPasses.ProceduralSplatOutput5)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput5;
}
else if (p == BakingPasses.ProceduralSplatOutput6)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput6;
}
else if (p == BakingPasses.ProceduralSplatOutput7)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput7;
}
else if (p == BakingPasses.ProceduralSplatOutput0A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput0A;
}
else if (p == BakingPasses.ProceduralSplatOutput1A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput1A;
}
else if (p == BakingPasses.ProceduralSplatOutput2A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput2A;
}
else if (p == BakingPasses.ProceduralSplatOutput3A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput3A;
}
else if (p == BakingPasses.ProceduralSplatOutput4A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput4A;
}
else if (p == BakingPasses.ProceduralSplatOutput5A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput5A;
}
else if (p == BakingPasses.ProceduralSplatOutput6A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput6A;
}
else if (p == BakingPasses.ProceduralSplatOutput7A)
{
return MicroSplatBaseFeatures.DebugOutput.ProceduralSplatOutput7A;
}
#endif
return MicroSplatBaseFeatures.DebugOutput.Albedo;
}
static void RemoveKeyword(List<string> keywords, string keyword)
{
if (keywords.Contains(keyword))
{
keywords.Remove(keyword);
}
}
static Material SetupMaterial(MicroSplatKeywords kwds, Material mat, MicroSplatBaseFeatures.DebugOutput debugOutput, bool useDebugTopo)
{
MicroSplatShaderGUI.MicroSplatCompiler comp = new MicroSplatShaderGUI.MicroSplatCompiler();
List<string> keywords = new List<string>(kwds.keywords);
RemoveKeyword(keywords, "_SNOW");
RemoveKeyword(keywords, "_TESSDISTANCE");
RemoveKeyword(keywords, "_WINDPARTICULATE");
RemoveKeyword(keywords, "_SNOWPARTICULATE");
RemoveKeyword(keywords, "_GLITTER");
RemoveKeyword(keywords, "_SNOWGLITTER");
RemoveKeyword(keywords, "_SPECULARFROMMETALLIC");
RemoveKeyword(keywords, "_USESPECULARWORKFLOW");
RemoveKeyword(keywords, "_BDRFLAMBERT");
RemoveKeyword(keywords, "_BDRF1");
RemoveKeyword(keywords, "_BDRF2");
RemoveKeyword(keywords, "_BDRF3");
// we only bake down to unity terrain format
if (keywords.Contains("_MEGASPLATTEXTURE"))
{
RemoveKeyword(keywords, "_MEGASPLATTEXTURE");
if (!keywords.Contains("_MICROTERRAIN"))
{
keywords.Add("_MICROTERRAIN");
}
}
keywords.Add(FeatureFromOutput(debugOutput).ToString());
if (useDebugTopo)
{
keywords.Add("_DEBUG_USE_TOPOLOGY");
}
keywords.Add("_RENDERBAKE");
keywords.Add("_UNLIT");
string shader = comp.Compile(keywords.ToArray(), "RenderBake_" + debugOutput.ToString());
//System.IO.File.WriteAllText("Assets/renderbake.shader", shader);
Shader s = ShaderUtil.CreateShaderAsset(shader);
Material renderMat = new Material(mat);
renderMat.shader = s;
renderMat.CopyPropertiesFromMaterial(mat); // because the constructor doesn't do it right in URP
renderMat.enableInstancing = false; // for some reason instance drawing breaks in URP
return renderMat;
}
public static Texture2D Bake(MicroSplatTerrain mst, BakingPasses p, int resolution, Texture2D worldPos, Texture2D worldNormal)
{
Camera cam = new GameObject("cam").AddComponent<Camera>();
cam.orthographic = true;
cam.orthographicSize = 0.5f;
cam.transform.position = new Vector3(0.5f, 10000.5f, -1);
cam.nearClipPlane = 0.1f;
cam.farClipPlane = 2.0f;
cam.enabled = false;
cam.depthTextureMode = DepthTextureMode.None;
cam.clearFlags = CameraClearFlags.Color;
cam.backgroundColor = Color.grey;
var debugOutput = OutputFromPass(p);
var readWrite = (debugOutput == MicroSplatBaseFeatures.DebugOutput.Albedo || debugOutput == MicroSplatBaseFeatures.DebugOutput.Emission) ?
RenderTextureReadWrite.sRGB : RenderTextureReadWrite.Linear;
RenderTexture rt = RenderTexture.GetTemporary(resolution, resolution, 32, RenderTextureFormat.ARGB32, readWrite);
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Quad);
go.transform.position = new Vector3(0, 10000, 0);
cam.transform.position = new Vector3(0, 10000, -1);
Material renderMat = SetupMaterial(MicroSplatUtilities.FindOrCreateKeywords(mst.templateMaterial), mst.matInstance, debugOutput, worldPos != null);
renderMat.SetTexture("_DebugWorldPos", worldPos);
renderMat.SetTexture("_DebugWorldNormal", worldNormal);
go.GetComponent<MeshRenderer>().sharedMaterial = renderMat;
bool fog = RenderSettings.fog;
if (p == BakingPasses.Normal)
{
cam.backgroundColor = Color.blue;
}
else
{
cam.backgroundColor = Color.gray;
}
// this is a strange one, at 0,0,0 rotation the albedo won't render on Windows platforms, so parent the cam to the quad and rotate it a bit
cam.transform.SetParent(go.transform);
go.transform.rotation = Quaternion.Euler(0.01f, 0, 0);
var ambInt = RenderSettings.ambientIntensity;
var reflectInt = RenderSettings.reflectionIntensity;
RenderSettings.ambientIntensity = 0;
RenderSettings.reflectionIntensity = 0;
Unsupported.SetRenderSettingsUseFogNoDirty(false);
RenderTexture.active = rt;
cam.targetTexture = rt;
cam.Render();
Unsupported.SetRenderSettingsUseFogNoDirty(fog);
RenderSettings.ambientIntensity = ambInt;
RenderSettings.reflectionIntensity = reflectInt;
Texture2D tex = new Texture2D(resolution, resolution, TextureFormat.ARGB32, false);
tex.ReadPixels(new Rect(0, 0, resolution, resolution), 0, 0);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(rt);
tex.Apply();
MeshRenderer mr = go.GetComponent<MeshRenderer>();
if (mr != null)
{
if (mr.sharedMaterial != null)
{
if (mr.sharedMaterial.shader != null)
GameObject.DestroyImmediate(mr.sharedMaterial.shader);
GameObject.DestroyImmediate(mr.sharedMaterial);
}
}
GameObject.DestroyImmediate(go); // cam is child, so will be destroyed too
return tex;
}
void Bake(MicroSplatTerrain mst)
{
// for each pass
int pass = 1;
while (pass <= (int)(BakingPasses.Emissive))
{
BakingPasses p = (BakingPasses)pass;
pass *= 2;
if (!IsEnabled(p))
{
continue;
}
Texture2D worldPos, worldNormal;
GenerateWorldData(mst.terrain, out worldNormal, out worldPos, (int)res);
var debugOutput = OutputFromPass(p);
var tex = Bake(mst, p, (int)res, worldPos, worldNormal);
var bytes = tex.EncodeToPNG();
DestroyImmediate(worldPos, worldNormal);
string texPath = MicroSplatUtilities.RelativePathFromAsset(mst.terrain) + "/" + mst.terrain.name + "_" + debugOutput.ToString();
System.IO.File.WriteAllBytes(texPath + ".png", bytes);
}
AssetDatabase.Refresh();
}
}
}

View File

@@ -0,0 +1,251 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using JBooth.MicroSplat;
namespace JBooth.MicroSplat
{
public partial class MicroSplatTerrainEditor : Editor
{
public static TextureArrayConfig ConvertTerrains(Terrain[] terrains, TerrainLayer[] terrainLayers)
{
if (terrains.Length == 0)
return null;
string[] defaultKeywords = new string[] { "_MICROSPLAT", "_BRANCHSAMPLES", "_BRANCHSAMPLESAGR", "_USEGRADMIP", "_MICROTERRAIN" };
foreach (Terrain ter in terrains)
{
var mst = ter.GetComponent<MicroSplatTerrain>();
if (mst == null)
ter.gameObject.AddComponent<MicroSplatTerrain>();
}
var terrain = terrains[0];
MicroSplatTerrain t = terrain.GetComponent<MicroSplatTerrain>();
int texcount = terrain.terrainData.terrainLayers.Length;
List<string> keywords = new List<string>(defaultKeywords);
// set initial render pipleine
var pipeline = MicroSplatUtilities.DetectPipeline();
if (pipeline == MicroSplatUtilities.PipelineType.HDPipeline)
{
keywords.Add("_MSRENDERLOOP_UNITYHD");
keywords.Add("_MSRENDERLOOP_UNITYHDRP2020");
keywords.Add("_MSRENDERLOOP_UNITYHDRP2021");
keywords.Add("_MSRENDERLOOP_UNITYHDRP2022");
}
else if (pipeline == MicroSplatUtilities.PipelineType.UniversalPipeline)
{
keywords.Add("_MSRENDERLOOP_UNITYLD");
keywords.Add("_MSRENDERLOOP_UNITYURP2020");
keywords.Add("_MSRENDERLOOP_UNITYURP2021");
keywords.Add("_MSRENDERLOOP_UNITYURP2022");
}
// this just looks better, IMO..
keywords.Add("_HYBRIDHEIGHTBLEND");
// Because new users won't read the manual or read settings before they act, we don't clamp the texture count
// down for maximum performance. Way to many support requests complaining of black terrain after adding textures because
// they didn't realize they needed to up the max texture count. So now, 16 minimum. This is why we can't have nice things.
/*
if (texcount <= 4)
{
keywords.Add ("_MAX4TEXTURES");
}
else if (texcount <= 8)
{
keywords.Add ("_MAX8TEXTURES");
}
else if (texcount <= 12)
{
keywords.Add ("_MAX12TEXTURES");
}
*/
if (texcount > 16 && texcount <= 20)
{
keywords.Add("_MAX20TEXTURES");
}
else if (texcount > 16 && texcount <= 24)
{
keywords.Add("_MAX24TEXTURES");
}
else if (texcount > 16 && texcount <= 28)
{
keywords.Add("_MAX28TEXTURES");
}
else if (texcount > 16 && texcount > 28)
{
keywords.Add("_MAX32TEXTURES");
}
// setup this terrain
t.templateMaterial = MicroSplatShaderGUI.NewShaderAndMaterial(terrain, keywords.ToArray());
var config = TextureArrayConfigEditor.CreateConfig(terrain);
t.templateMaterial.SetTexture("_Diffuse", config.diffuseArray);
t.templateMaterial.SetTexture("_NormalSAO", config.normalSAOArray);
// mask map format, now common.
config.allTextureChannelHeight = TextureArrayConfig.AllTextureChannel.B;
config.allTextureChannelAO = TextureArrayConfig.AllTextureChannel.G;
config.allTextureChannelSmoothness = TextureArrayConfig.AllTextureChannel.A;
t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
EditorUtility.SetDirty(t);
if (terrainLayers != null)
{
if (terrainLayers.Length > 0)
{
Vector2 min = new Vector2(99999, 99999);
Vector2 max = Vector2.zero;
for (int x = 0; x < terrainLayers.Length; ++x)
{
var uv = terrainLayers[x].tileSize;
if (min.x > uv.x)
min.x = uv.x;
if (min.y > uv.y)
min.y = uv.y;
if (max.x < uv.x)
max.x = uv.x;
if (max.y < uv.y)
max.y = uv.y;
}
Vector2 average = Vector2.Lerp(min, max, 0.5f);
// use per texture UVs instead..
float diff = Vector2.Distance(min, max);
if (diff > 0.1)
{
keywords.Add("_PERTEXUVSCALEOFFSET");
// if the user has widely different UVs, use the LOD sampler. This is because the gradient mode blends between mip levels,
// which looks bad with hugely different UVs. I still don't understand why people do this kind of crap though, ideally
// your UVs should not differ per texture, and if so, not by much..
if (diff > 10)
{
Debug.LogWarning("Terrain has wildly varing UV scales, it's best to keep consistent texture resolution. ");
}
if (!keywords.Contains("_USEGRADMIP"))
{
keywords.Add("_USEGRADMIP");
}
Vector4 scaleOffset = new Vector4(1, 1, 0, 0);
t.templateMaterial.SetVector("_UVScale", scaleOffset);
var propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
for (int x = 0; x < terrain.terrainData.terrainLayers.Length; ++x)
{
var uvScale = terrain.terrainData.terrainLayers[x].tileSize;
var uvOffset = terrain.terrainData.terrainLayers[x].tileOffset;
uvScale = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
uvScale.x = Mathf.RoundToInt(uvScale.x);
uvScale.y = Mathf.RoundToInt(uvScale.y);
propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVScale, uvScale);
propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVOffset, Vector2.zero);
}
for (int x = terrain.terrainData.terrainLayers.Length; x < 32; ++x)
{
propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVScale, average);
propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVOffset, Vector2.zero);
}
// must init the data, or the editor will write over it.
propData.SetValue(0, 15, Color.white);
EditorUtility.SetDirty(propData);
t.templateMaterial.SetVector("_TriplanarUVScale",
new Vector4(
10.0f / t.terrain.terrainData.size.x,
10.0f / t.terrain.terrainData.size.x, 0, 0));
}
else
{
var uvScale = terrain.terrainData.terrainLayers[0].tileSize;
var uvOffset = terrain.terrainData.terrainLayers[0].tileOffset;
uvScale = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
t.templateMaterial.SetVector("_UVScale", scaleOffset);
t.templateMaterial.SetVector("_TriplanarUVScale",
new Vector4(
10.0f / t.terrain.terrainData.size.x,
10.0f / t.terrain.terrainData.size.x, 0, 0));
}
}
}
// now make sure others all have the same settings as well.
for (int i = 0; i < terrains.Length; ++i)
{
var nt = terrains[i];
var mgr = nt.GetComponent<MicroSplatTerrain>();
mgr.templateMaterial = t.templateMaterial;
mgr.keywordSO = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);
if (mgr.propData == null)
{
mgr.propData = MicroSplatShaderGUI.FindOrCreatePropTex(mgr.templateMaterial);
}
}
t.keywordSO.keywords.Clear();
t.keywordSO.keywords = new List<string>(keywords);
// force recompile, so that basemap shader name gets reset correctly..
MicroSplatShaderGUI.MicroSplatCompiler comp = new MicroSplatShaderGUI.MicroSplatCompiler();
comp.Compile(t.templateMaterial);
MicroSplatTerrain.SyncAll();
return config;
}
bool DoConvertGUI(MicroSplatTerrain t)
{
if (t.templateMaterial == null)
{
if (GUILayout.Button("Convert to MicroSplat"))
{
// get all terrains in selection, not just this one, and treat as one giant terrain
var objs = Selection.gameObjects;
List<Terrain> terrains = new List<Terrain>();
for (int i = 0; i < objs.Length; ++i)
{
Terrain ter = objs[i].GetComponent<Terrain>();
if (ter != null)
{
terrains.Add(ter);
}
Terrain[] trs = objs[i].GetComponentsInChildren<Terrain>();
for (int x = 0; x < trs.Length; ++x)
{
if (!terrains.Contains(trs[x]))
{
terrains.Add(trs[x]);
}
}
}
var config = ConvertTerrains(terrains.ToArray(), terrains[0].terrainData.terrainLayers);
if (config != null)
{
Selection.SetActiveObjectWithContext(config, config);
}
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,171 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using JBooth.MicroSplat;
namespace JBooth.MicroSplat
{
public partial class MicroSplatTerrainEditor : Editor
{
void ImportExportGUI()
{
if (MicroSplatUtilities.DrawRollup("Splat Import/Export", false))
{
EditorGUI.BeginChangeCheck();
serializedObject.Update();
SerializedProperty prop = serializedObject.FindProperty("importSplatMaps");
EditorGUILayout.PropertyField(prop, true);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
}
if (GUILayout.Button("Import"))
{
ImportSplatMaps();
}
if (GUILayout.Button("Export"))
{
ExportSplatMaps();
}
}
}
void ImportSplatMaps()
{
MicroSplatTerrain mst = target as MicroSplatTerrain;
var terrain = mst.terrain;
if (terrain == null)
{
Debug.LogError("terrain is null");
return;
}
var tdata = terrain.terrainData;
if (tdata == null)
{
Debug.LogError("terrain data is null");
return;
}
List<Texture2D> importSplatMaps = mst.importSplatMaps;
// sanatize data
for (int i = 0; i < importSplatMaps.Count; ++i)
{
if (importSplatMaps[i] == null)
{
importSplatMaps.RemoveAt(i);
i--;
}
}
int mapCount = importSplatMaps.Count;
if (mapCount > 8)
{
mapCount = 8;
}
if (mapCount == 0)
{
Debug.LogError("No maps to import");
return;
}
int w = tdata.alphamapWidth;
int h = tdata.alphamapHeight;
RenderTexture rt = new RenderTexture(w, h, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Texture2D buffer = new Texture2D(w, h, TextureFormat.ARGB32, false, true);
int layerCount = tdata.alphamapLayers;
float[,,] data = new float[w, h, layerCount];
for (int i = 0; i < mapCount; ++i)
{
try
{
EditorUtility.DisplayProgressBar("Importing Splat Maps", "Map : " + i, (float)i / mapCount);
// scale texture to whatever size our alpha maps are set to
Graphics.Blit(importSplatMaps[i], rt);
RenderTexture.active = rt;
buffer.ReadPixels(new Rect(0, 0, w, h), 0, 0);
buffer.Apply();
for (int x = 0; x < w; ++x)
{
for (int y = 0; y < h; ++y)
{
Color c = buffer.GetPixel(x, y);
if (i * 4 < layerCount)
data[y, w - x - 1, i * 4] = c.r;
if (i * 4 + 1 < layerCount)
data[y, w - x - 1, i * 4 + 1] = c.g;
if (i * 4 + 2 < layerCount)
data[y, w - x - 1, i * 4 + 2] = c.b;
if (i * 4 + 3 < layerCount)
data[y, w - x - 1, i * 4 + 3] = c.a;
}
}
}
catch
{
Debug.LogError("Error in importing terrain");
EditorUtility.ClearProgressBar();
RenderTexture.active = null;
DestroyImmediate(rt);
DestroyImmediate(buffer);
return;
}
finally
{
RenderTexture.active = null;
EditorUtility.ClearProgressBar();
}
}
DestroyImmediate(rt);
DestroyImmediate(buffer);
tdata.SetAlphamaps(0, 0, data);
}
void ExportSplatMaps()
{
var path = EditorUtility.SaveFolderPanel("Save textures to directory", "", "");
if (string.IsNullOrEmpty(path))
return;
path = path.Replace("\\", "/");
if (!path.EndsWith("/"))
path += "/";
MicroSplatTerrain mst = target as MicroSplatTerrain;
var terrain = mst.terrain;
if (terrain == null)
{
return;
}
var tdata = terrain.terrainData;
if (tdata == null)
{
return;
}
for (int i = 0; i < tdata.alphamapTextures.Length; ++i)
{
var bytes = tdata.alphamapTextures[i].EncodeToTGA();
System.IO.File.WriteAllBytes(path + "SplatControl" + i + ".tga", bytes);
}
AssetDatabase.Refresh();
}
}
}

View File

@@ -0,0 +1,274 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using JBooth.MicroSplat;
#if __MICROSPLAT__
namespace JBooth.MicroSplat
{
public partial class MicroSplatTerrainEditor : Editor
{
public static Texture2D GenerateTerrainNormalMap(MicroSplatTerrain bt)
{
Terrain t = bt.terrain;
int w = t.terrainData.heightmapResolution;
int h = t.terrainData.heightmapResolution;
Texture2D data = new Texture2D(w, h, TextureFormat.RGBA32, true, true);
for (int x = 0; x < w; ++x)
{
for (int y = 0; y < h; ++y)
{
Vector3 normal = t.terrainData.GetInterpolatedNormal((float)x / w, (float)y / h);
normal *= 0.5f;
normal.x += 0.5f; normal.y += 0.5f; normal.z += 0.5f;
data.SetPixel(x, y, new Color(normal.x, normal.y, normal.z));
}
}
data.Apply();
var path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData);
path += "/" + t.name + "_normal.tga";
var bytes = data.EncodeToTGA();
System.IO.File.WriteAllBytes(path, bytes);
GameObject.DestroyImmediate(data);
AssetDatabase.Refresh();
bt.perPixelNormal = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
var ai = AssetImporter.GetAtPath(path);
var ti = ai as TextureImporter;
var ps = ti.GetDefaultPlatformTextureSettings();
if (ti.isReadable == true ||
ti.wrapMode != TextureWrapMode.Clamp ||
ti.npotScale != TextureImporterNPOTScale.None ||
ps.overridden != true ||
ti.isReadable != false ||
ti.sRGBTexture != false ||
ps.textureCompression != TextureImporterCompression.Uncompressed ||
ti.textureType != TextureImporterType.Default)
{
ti.textureType = TextureImporterType.Default;
ti.mipmapEnabled = true;
ps.overridden = true;
ti.sRGBTexture = false;
ps.textureCompression = TextureImporterCompression.Uncompressed;
ti.SetPlatformTextureSettings(ps);
ti.wrapMode = TextureWrapMode.Clamp;
ti.isReadable = false;
ti.npotScale = TextureImporterNPOTScale.None;
ti.SaveAndReimport();
}
EditorUtility.SetDirty(bt);
EditorUtility.SetDirty(bt.terrain);
MicroSplatTerrain.SyncAll();
AssetDatabase.SaveAssets();
return AssetDatabase.LoadAssetAtPath<Texture2D>(ti.assetPath);
}
#if __MICROSPLAT_PROCTEX__
public static void ComputeCavityFlowMap(MicroSplatTerrain bt)
{
Terrain t = bt.terrain;
Texture2D data = new Texture2D(t.terrainData.heightmapResolution, t.terrainData.heightmapResolution, TextureFormat.RGBA32, true, true);
CurvatureMapGenerator.CreateMap(t.terrainData.GetHeights(0, 0, data.width, data.height), data);
var path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData);
path += "/" + t.name + "_cavity.png";
var bytes = data.EncodeToPNG();
System.IO.File.WriteAllBytes(path, bytes);
GameObject.DestroyImmediate(data);
AssetDatabase.Refresh();
bt.cavityMap = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
var ai = AssetImporter.GetAtPath(path);
var ti = ai as TextureImporter;
if (ti.sRGBTexture != false)
{
ti.sRGBTexture = false;
ti.SaveAndReimport();
}
EditorUtility.SetDirty(bt);
EditorUtility.SetDirty(t);
MicroSplatTerrain.SyncAll();
AssetDatabase.SaveAssets();
}
#endif
static GUIContent CPerPixelNormal = new GUIContent("Per Pixel Normal", "Per Pixel normal map");
public void DoTerrainDescGUI()
{
MicroSplatTerrain bt = target as MicroSplatTerrain;
Terrain t = bt.GetComponent<Terrain>();
if (t == null || t.terrainData == null)
{
return;
}
if (t.materialTemplate == null)
{
return;
}
if (bt.keywordSO == null)
{
return;
}
if (!bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING") && !bt.keywordSO.IsKeywordEnabled("_DYNAMICFLOWS"))
{
return;
}
EditorGUILayout.Space();
if (bt.blendMat == null && bt.templateMaterial != null && bt.keywordSO != null && bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING"))
{
var path = AssetDatabase.GetAssetPath(bt.templateMaterial);
path = path.Replace(".mat", "_TerrainObjectBlend.mat");
bt.blendMat = AssetDatabase.LoadAssetAtPath<Material>(path);
if (bt.blendMat == null)
{
string shaderPath = path.Replace(".mat", ".shader");
Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(shaderPath);
if (shader == null)
{
shaderPath = path.Replace(".shader", ".surfshader");
shader = AssetDatabase.LoadAssetAtPath<Shader>(shaderPath);
}
if (shader == null)
{
shaderPath = AssetDatabase.GetAssetPath(bt.templateMaterial.shader);
shaderPath = shaderPath.Replace(".shader", "_TerrainObjectBlend.shader");
shaderPath = shaderPath.Replace(".surfshader", "_TerrainObjectBlend.surfshader");
shader = AssetDatabase.LoadAssetAtPath<Shader>(shaderPath);
}
if (shader != null)
{
Material mat = new Material(shader);
AssetDatabase.CreateAsset(mat, path);
AssetDatabase.SaveAssets();
MicroSplatTerrain.SyncAll();
}
}
}
EditorUtility.SetDirty(bt);
EditorUtility.SetDirty(bt.terrain);
}
public void DoPerPixelNormalGUI()
{
MicroSplatTerrain bt = target as MicroSplatTerrain;
Terrain t = bt.GetComponent<Terrain>();
if (t == null || t.terrainData == null)
{
EditorGUILayout.HelpBox("No Terrain data found", MessageType.Error);
return;
}
if (t.materialTemplate == null)
{
return;
}
if (bt.keywordSO == null)
return;
if (bt.terrain.drawInstanced && bt.perPixelNormal != null && !bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING"))
{
EditorGUILayout.HelpBox("Per Pixel Normal is assigned, but shader is using Instance rendering, which automatically provides per-pixel normal. You may turn off per pixel normal if it's on and clear the normal data to save memory.", MessageType.Warning);
if (bt.perPixelNormal != null && GUILayout.Button("Clear"))
{
bt.perPixelNormal = null;
EditorUtility.SetDirty(bt);
EditorUtility.SetDirty(bt.terrain);
}
}
MicroSplatUtilities.DrawTextureField(bt, CPerPixelNormal, ref bt.perPixelNormal, "_PERPIXNORMAL", "_TERRAINBLENDING", null, null, false);
if (bt.perPixelNormal == null &&
(bt.keywordSO.IsKeywordEnabled("_PERPIXNORMAL") || bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING")))
{
EditorGUILayout.HelpBox("Terrain Normal Data is not present, please generate", MessageType.Warning);
}
if (bt.keywordSO.IsKeywordEnabled("_PERPIXNORMAL") || bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING"))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Normal Data");
if (GUILayout.Button(bt.perPixelNormal == null ? "Generate" : "Update"))
{
GenerateTerrainNormalMap(bt);
EditorUtility.SetDirty(bt);
EditorUtility.SetDirty(bt.terrain);
}
if (bt.perPixelNormal != null && GUILayout.Button("Clear"))
{
bt.perPixelNormal = null;
EditorUtility.SetDirty(bt);
EditorUtility.SetDirty(bt.terrain);
}
EditorGUILayout.EndHorizontal();
}
}
#if __MICROSPLAT_PROCTEX__
public void DoCavityMapGUI()
{
MicroSplatTerrain bt = target as MicroSplatTerrain;
Terrain t = bt.GetComponent<Terrain>();
if (t == null || t.terrainData == null)
{
EditorGUILayout.HelpBox("No Terrain data found", MessageType.Error);
return;
}
if (t.materialTemplate == null)
{
return;
}
if (bt.keywordSO == null)
return;
if (bt.cavityMap == null)
{
EditorGUILayout.HelpBox("Cavity Map Data is not present, please generate or provide", MessageType.Warning);
}
bt.cavityMap = (Texture2D)EditorGUILayout.ObjectField("Cavity Map", bt.cavityMap, typeof(Texture2D), false);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Cavity Data");
if (GUILayout.Button(bt.cavityMap == null ? "Generate" : "Update"))
{
ComputeCavityFlowMap(bt);
}
if (bt.cavityMap != null && GUILayout.Button("Clear"))
{
bt.cavityMap = null;
}
EditorGUILayout.EndHorizontal();
}
#endif
}
#endif
}

View File

@@ -0,0 +1,200 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using JBooth.MicroSplat;
using System.Linq;
#if __MICROSPLAT__
namespace JBooth.MicroSplat
{
public partial class MicroSplatTerrainEditor : Editor
{
struct WeightPair
{
public int index;
public float weight;
}
int weightLimit = 4;
bool multipassWeightLimit = false;
GUIContent CMultipassWeight = new GUIContent("Multipass", "First pass limits weights per control pixel to weight limit. Second pass tests neighbors for non-shared weights, and reduces weights further on that pixel if found");
void WeightLimitingGUI(MicroSplatTerrain t)
{
if (MicroSplatUtilities.DrawRollup("Weight Limiting", false))
{
weightLimit = EditorGUILayout.IntSlider("Weight Limit", weightLimit, 2, 4);
multipassWeightLimit = EditorGUILayout.Toggle(CMultipassWeight, multipassWeightLimit);
if (GUILayout.Button("Limit"))
{
WeightLimitTerrain(t, weightLimit, false);
if (multipassWeightLimit)
{
WeightLimitTerrain(t, weightLimit, true);
}
}
}
}
/// <summary>
/// Preprocess the terrain to clamp down on the number of splat maps which have weights on each control point. First pass
/// limits the number of weights to the specified amount per control point. Since each rendered pixel is a blend of 4 possible
/// control points, this still means a given pixel may need up to 4 weights even if the control point is clamped to 1 weight.
/// In the second pass, we check all of the neighoring pixels to see if they have different weights- if they do, we clamp
/// down to one less weight on this control point. The idea here is to create some extra headroom for the blend, but since
/// you can still need 4 blend weights in some cases, there is no perfect solution to this issue when running with less than
/// 4 blend weights. It does, however, greatly help when running under those constraints.
///
/// </summary>
/// <param name="bt">Bt.</param>
/// <param name="maxWeights">Max weights.</param>
/// <param name="secondPass">If set to <c>true</c> second pass.</param>
public static void WeightLimitTerrain(MicroSplatTerrain bt, int maxWeights, bool secondPass = false)
{
Terrain t = bt.GetComponent<Terrain>();
if (t == null)
return;
TerrainData td = t.terrainData;
if (td == null)
return;
int w = td.alphamapWidth;
int h = td.alphamapHeight;
int l = td.alphamapLayers;
Undo.RegisterCompleteObjectUndo(t, "Weight Limit Terrain");
var splats = td.GetAlphamaps(0, 0, w, h);
float[] data = new float[l];
List<WeightPair> sorted = new List<WeightPair>();
List<int> validIndexes = new List<int>();
for (int x = 0; x < w; ++x)
{
for (int y = 0; y < h; ++y)
{
// gather all weights
for (int i = 0; i < l; ++i)
{
data[i] = splats[x, y, i];
}
sorted.Clear();
for (int i = 0; i < l; ++i)
{
var wp = new WeightPair();
wp.index = i;
wp.weight = data[i];
sorted.Add(wp);
}
sorted.Sort((w0, w1) => w1.weight.CompareTo(w0.weight));
// remove lower weights
int allowedWeights = maxWeights;
while (sorted.Count > allowedWeights)
{
sorted.RemoveAt(sorted.Count - 1);
}
// generate valid index list
validIndexes.Clear();
for (int i = 0; i < sorted.Count; ++i)
{
if (sorted[i].weight > 0)
{
validIndexes.Add(sorted[i].index);
}
}
// figure out if our neighbors have weights which we don't have- if so, clamp down harder to make room for blending..
// if not, allow us to blend fully. We do this in a second pass so that small weights are reduced before we make
// this decision..
if (secondPass)
{
for (int xm = -1; xm < 2; ++xm)
{
for (int ym = -1; ym < 2; ++ym)
{
int nx = x + xm;
int ny = y + ym;
if (nx >= 0 && ny >= 0 && nx < w && ny < y)
{
for (int layer = 0; layer < l; ++layer)
{
float weight = splats[nx, ny, layer];
if (weight > 0 && !validIndexes.Contains(layer))
{
allowedWeights = maxWeights - 1;
}
}
}
}
}
while (sorted.Count > allowedWeights)
{
sorted.RemoveAt(sorted.Count - 1);
}
// generate valid index list
validIndexes.Clear();
for (int i = 0; i < sorted.Count; ++i)
{
if (sorted[i].weight > 0)
{
validIndexes.Add(sorted[i].index);
}
}
}
// clear non-valid indexes
for (int j = 0; j < l; ++j)
{
if (!validIndexes.Contains(j))
{
data[j] = 0;
}
}
// now normalize weights so that they total one on each pixel
float total = 0;
for (int j = 0; j < l; ++j)
{
total += data[j];
}
float scale = 1.0f / total;
for (int j = 0; j < l; ++j)
{
data[j] *= scale;
}
// now map back to splat data array
for (int i = 0; i < l; ++i)
{
splats[x, y, i] = data[i];
}
}
}
td.SetAlphamaps(0, 0, splats);
}
}
#endif
}

View File

@@ -0,0 +1,822 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using UnityEngine.Rendering;
using System.Linq;
namespace JBooth.MicroSplat
{
public static class MicroSplatUtilities
{
public enum PipelineType
{
Unsupported,
BuiltInPipeline,
UniversalPipeline,
HDPipeline
}
public static IEnumerable<System.Type> GetLoadableTypes(this System.Reflection.Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (System.Reflection.ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
/// <summary>
/// Returns the type of renderpipeline that is currently running
/// </summary>
/// <returns></returns>
public static PipelineType DetectPipeline ()
{
#if UNITY_2019_1_OR_NEWER
if (GraphicsSettings.renderPipelineAsset != null) {
// SRP
var srpType = GraphicsSettings.renderPipelineAsset.GetType().ToString();
if (srpType.Contains("HDRenderPipelineAsset")) {
return PipelineType.HDPipeline;
}
else if (srpType.Contains("UniversalRenderPipelineAsset") || srpType.Contains("LightweightRenderPipelineAsset")) {
return PipelineType.UniversalPipeline;
}
else return PipelineType.Unsupported;
}
#elif UNITY_2017_1_OR_NEWER
if (GraphicsSettings.renderPipelineAsset != null)
{
// SRP not supported before 2019
return PipelineType.Unsupported;
}
#endif
// no SRP
return PipelineType.BuiltInPipeline;
}
public static bool CanFindKeywords(Material mat)
{
if (mat == null)
return false;
var path = AssetDatabase.GetAssetPath (mat);
if (string.IsNullOrEmpty (path))
{
return false;
}
if (!path.EndsWith(".mat"))
{
return false;
}
if (!path.StartsWith("Assets"))
{
return false;
}
if (mat.shader != null)
{
if (!AssetDatabase.GetAssetPath(mat.shader).StartsWith("Assets"))
{
return false;
}
}
path = path.Replace ("\\", "/");
path = path.Replace (".mat", "_keywords.asset");
return System.IO.File.Exists (path);
}
public static MicroSplatKeywords FindOrCreateKeywords(Material mat)
{
if (mat == null)
return null;
var path = AssetDatabase.GetAssetPath(mat);
if (string.IsNullOrEmpty(path))
{
Debug.LogWarning("Material has no path");
// not sure what to do about this case. Mat should always have a path
path = "Assets/MicroSplatKeywords.mat";
}
path = path.Replace("\\", "/");
path = path.Replace(".mat", "_keywords.asset");
MicroSplatKeywords keywords = AssetDatabase.LoadAssetAtPath<MicroSplatKeywords>(path);
if (keywords == null)
{
keywords = ScriptableObject.CreateInstance<MicroSplatKeywords>();
keywords.keywords = new List<string>(mat.shaderKeywords);
AssetDatabase.CreateAsset(keywords, path);
UnityEditor.AssetDatabase.SaveAssets();
mat.shaderKeywords = null;
}
return keywords;
}
public static void Checkout(string path)
{
if (UnityEditor.VersionControl.Provider.enabled && UnityEditor.VersionControl.Provider.isActive)
{
var asset = UnityEditor.VersionControl.Provider.GetAssetByPath(path);
if (asset == null)
return;
UnityEditor.VersionControl.AssetList lst = new UnityEditor.VersionControl.AssetList();
lst.Add(asset);
if (UnityEditor.VersionControl.Provider.AddIsValid(lst))
{
UnityEditor.VersionControl.Provider.Add(lst, false);
}
if (UnityEditor.VersionControl.Provider.hasCheckoutSupport)
{
UnityEditor.VersionControl.Provider.Checkout(asset, UnityEditor.VersionControl.CheckoutMode.Both);
}
}
if (System.IO.File.Exists(path))
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(path);
fileInfo.IsReadOnly = false;
}
}
public static string MakeRelativePath(string path)
{
path = path.Replace(Application.dataPath, "Assets/");
path = path.Replace("\\", "/");
path = path.Replace("//", "/");
return path;
}
public static string MakeAbsolutePath(string path)
{
if (!path.StartsWith(Application.dataPath))
{
if (path.StartsWith("Assets"))
{
path = path.Substring(6);
path = Application.dataPath + path;
}
}
return path;
}
static Dictionary<string, Texture2D> autoTextures = null;
public static void EnforceDefaultTexture(MaterialProperty texProp, string autoTextureName)
{
if (texProp.textureValue == null)
{
Texture2D def = MicroSplatUtilities.GetAutoTexture(autoTextureName);
if (def != null)
{
texProp.textureValue = def;
}
}
}
public static Texture2D GetAutoTexture(string name)
{
if (autoTextures == null)
{
autoTextures = new Dictionary<string, Texture2D>();
var guids = AssetDatabase.FindAssets("microsplat_def_ t:texture2D");
for (int i = 0; i < guids.Length; ++i)
{
Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath(guids[i]));
autoTextures.Add(tex.name, tex);
}
}
Texture2D ret;
if (autoTextures.TryGetValue(name, out ret))
{
return ret;
}
return null;
}
public static void DrawTextureField(MicroSplatObject t, GUIContent content, ref Texture2D tex,
string keyword, string keyword2 = null, string keyword3 = null, string keyword4 = null, bool allKeywordsRequired = true)
{
if (t.keywordSO == null)
return;
if (allKeywordsRequired)
{
if (keyword != null && !t.keywordSO.IsKeywordEnabled (keyword))
{
return;
}
if (keyword2 != null && !t.keywordSO.IsKeywordEnabled (keyword2))
{
return;
}
if (keyword3 != null && !t.keywordSO.IsKeywordEnabled (keyword3))
{
return;
}
if (keyword4 != null && !t.keywordSO.IsKeywordEnabled (keyword4))
{
return;
}
}
else
{
bool pass = false;
if (keyword != null && t.keywordSO.IsKeywordEnabled (keyword))
{
pass = true;
}
if (keyword2 != null && t.keywordSO.IsKeywordEnabled (keyword2))
{
pass = true;
}
if (keyword3 != null && t.keywordSO.IsKeywordEnabled (keyword3))
{
pass = true;
}
if (keyword4 != null && t.keywordSO.IsKeywordEnabled (keyword4))
{
pass = true;
}
if (!pass)
{
return;
}
}
EditorGUI.BeginChangeCheck();
Rect r = EditorGUILayout.GetControlRect (GUILayout.Height (18));
r.width -= 18; // shrik for boxed contents.. which unity doesn't seem to handle right..
tex = EditorGUI.ObjectField (r, content, tex, typeof (Texture2D), false) as Texture2D;
if (EditorGUI.EndChangeCheck ())
{
EditorUtility.SetDirty (t);
MicroSplatObject.SyncAll ();
}
}
static Dictionary<string, bool> rolloutStates = new Dictionary<string, bool>();
static GUIStyle rolloutStyle;
public static bool DrawRollup(string text, bool defaultState = true, bool inset = false)
{
if (rolloutStyle == null)
{
rolloutStyle = new GUIStyle(GUI.skin.box);
rolloutStyle.normal.textColor = EditorGUIUtility.isProSkin ? Color.white : Color.black;
}
var oldColor = GUI.contentColor;
GUI.contentColor = EditorGUIUtility.isProSkin ? Color.white : Color.black;
if (inset == true)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.GetControlRect(GUILayout.Width(40));
}
if (!rolloutStates.ContainsKey(text))
{
rolloutStates[text] = defaultState;
string key = "MicroSplat_" + text;
if (EditorPrefs.HasKey(key))
{
rolloutStates [text] = EditorPrefs.GetBool (key);
}
}
if (GUILayout.Button(text, rolloutStyle, new GUILayoutOption[]{GUILayout.ExpandWidth(true), GUILayout.Height(20)}))
{
rolloutStates[text] = !rolloutStates[text];
string key = "MicroSplat_" + text;
EditorPrefs.SetBool (key, rolloutStates [text]);
}
if (inset == true)
{
EditorGUILayout.GetControlRect(GUILayout.Width(40));
EditorGUILayout.EndHorizontal();
}
GUI.contentColor = oldColor;
return rolloutStates[text];
}
public static void DrawSeparator()
{
EditorGUILayout.Separator();
GUILayout.Box("", new GUILayoutOption[] { GUILayout.ExpandWidth(true), GUILayout.Height(1) });
EditorGUILayout.Separator();
}
static List<TextureArrayPreviewCache> previewCache = new List<TextureArrayPreviewCache>(32);
static Texture2D FindInPreviewCache(int hash)
{
for (int i = 0; i < previewCache.Count; ++i)
{
if (previewCache[i].hash == hash)
return previewCache[i].texture;
}
return null;
}
public static void ClearPreviewCache()
{
for (int i = 0; i < previewCache.Count; ++i)
{
if (previewCache[i].texture != null)
{
GameObject.DestroyImmediate(previewCache[i].texture);
}
}
previewCache.Clear();
}
// for caching previews
public class TextureArrayPreviewCache
{
public int hash;
public Texture2D texture;
}
// workaround for unity's editor bug w/ linear. Blit texture into linear render buffer,
// readback int linear texture, write into PNG, read back from PNG. Cause Unity..
public static void FixUnityEditorLinearBug(ref Texture2D tex, int width = 128, int height = 128)
{
RenderTexture rt = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Graphics.Blit(tex, rt);
GameObject.DestroyImmediate(tex);
var tempTex = new Texture2D(width, height, TextureFormat.RGBA32, true, true);
RenderTexture.active = rt;
tempTex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
RenderTexture.active = null;
tex = new Texture2D(2, 2, TextureFormat.RGBA32, true, true);
tex.LoadImage(tempTex.EncodeToPNG());
GameObject.DestroyImmediate(tempTex);
}
static Texture2DArray cachedArray = null;
static TextureArrayConfig cachedConfig;
public static int DrawTextureSelector(int textureIndex, Texture2DArray ta, bool compact = false)
{
if (ta == null)
return textureIndex;
int count = ta.depth;
if (cachedArray != ta)
{
cachedArray = ta;
var path = AssetDatabase.GetAssetPath (ta);
path = path.Replace ("_diff_tarray", "");
cachedConfig = AssetDatabase.LoadAssetAtPath<TextureArrayConfig> (path);
}
Texture2D disp = Texture2D.blackTexture;
if (ta != null)
{
int hash = ta.GetHashCode() * (textureIndex + 7);
Texture2D hashed = FindInPreviewCache(hash);
if (hashed == null)
{
hashed = new Texture2D(ta.width, ta.height, ta.format, false);
Graphics.CopyTexture(ta, textureIndex, 0, hashed, 0, 0);
hashed.Apply(false, false);
//FixUnityEditorLinearBug(ref hashed);
var hd = new TextureArrayPreviewCache();
hd.hash = hash;
hd.texture = hashed;
previewCache.Add(hd);
if (previewCache.Count > 20)
{
hd = previewCache[0];
previewCache.RemoveAt(0);
if (hd.texture != null)
{
GameObject.DestroyImmediate(hd.texture);
}
}
}
disp = hashed;
}
if (compact)
{
EditorGUILayout.BeginVertical();
EditorGUI.DrawPreviewTexture(EditorGUILayout.GetControlRect(GUILayout.Width(110), GUILayout.Height(96)), disp);
textureIndex = EditorGUILayout.IntSlider(textureIndex, 0, count - 1, GUILayout.Width(120));
EditorGUILayout.EndVertical();
}
else
{
string name = "";
if (cachedConfig != null)
{
if (textureIndex < cachedConfig.sourceTextures.Count && textureIndex >= 0)
{
var conf = cachedConfig.sourceTextures [textureIndex];
if (conf != null && conf.terrainLayer != null)
{
if (conf.terrainLayer != null)
name = conf.terrainLayer.name;
else if (conf.diffuse != null)
name = conf.diffuse.name;
}
}
}
Rect r = EditorGUILayout.GetControlRect (false, GUILayout.Height (128));
r.width -= 2;
float width = r.width;
r.width = 128;
EditorGUI.DrawPreviewTexture(r, disp);
r.height = 20;
r.width = width - 148;
r.x += 148;
r.y += 36;
Rect ridx = r;
ridx.y += 20;
ridx.width -= 44;
textureIndex = EditorGUI.IntSlider (ridx, textureIndex, 0, count - 1);
if (textureIndex <= 0)
{
textureIndex = 0;
GUI.enabled = false;
}
else
{
GUI.enabled = true;
}
ridx.x += ridx.width;
ridx.x += 4;
ridx.width = 22;
if (GUI.Button (ridx, "<"))
{
textureIndex -= 1;
}
if (textureIndex >= count - 1)
{
textureIndex = count - 1;
GUI.enabled = false;
}
else
{
GUI.enabled = true;
}
ridx.x += 22;
if (GUI.Button (ridx, ">"))
{
textureIndex += 1;
}
GUI.enabled = true;
EditorGUI.LabelField (r, name);
}
return textureIndex;
}
public static void WarnLinear(Texture2D tex)
{
if (tex != null)
{
AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(tex));
if (ai != null)
{
TextureImporter ti = ai as TextureImporter;
if (ti != null && ti.sRGBTexture != false)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.HelpBox("Texture should be linear", MessageType.Error);
if (GUILayout.Button("Fix"))
{
ti.sRGBTexture = false;
ti.SaveAndReimport();
}
EditorGUILayout.EndHorizontal();
}
}
}
}
public static string RelativePathFromAsset(UnityEngine.Object o)
{
string path = null;
if (o != null)
{
path = AssetDatabase.GetAssetPath(o);
}
if (string.IsNullOrEmpty(path))
{
string selectionPath = AssetDatabase.GetAssetPath (Selection.activeObject);
if (!string.IsNullOrEmpty(selectionPath))
{
path = selectionPath;
}
}
if (string.IsNullOrEmpty(path))
{
path = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().path;
}
if (string.IsNullOrEmpty(path))
{
path = "Assets";
}
path = path.Replace("\\", "/");
if (path.Contains("/"))
{
path = path.Substring(0, path.LastIndexOf("/"));
}
if (!path.EndsWith ("/MicroSplatData"))
{
path += "/MicroSplatData";
}
if (!System.IO.Directory.Exists(path))
{
System.IO.Directory.CreateDirectory(path);
}
return path;
}
// REPLACEMENT FOR SELECTION GRID cause it's crap
static Texture2D selectedTex = null;
static Texture2D labelBackgroundTex = null;
static void SetupSelectionGrid()
{
if (selectedTex == null)
{
selectedTex = new Texture2D(128, 128, TextureFormat.ARGB32, false);
for (int x = 0; x < 128; ++x)
{
for (int y = 0; y < 128; ++y)
{
if (x < 4 || x > 123 || y < 4 || y > 123)
{
selectedTex.SetPixel(x, y, new Color(0, 0, 128));
}
else
{
selectedTex.SetPixel(x, y, new Color(0, 0, 0, 0));
}
}
}
selectedTex.Apply();
}
if (labelBackgroundTex == null)
{
labelBackgroundTex = new Texture2D(1, 1);
labelBackgroundTex.SetPixel(0, 0, new Color(0.0f, 0.0f, 0.0f, 0.5f));
labelBackgroundTex.Apply();
}
}
static int DrawSelectionElement(Rect r, int i, int index, Texture2D image, string label, int elemSize)
{
if (GUI.Button(r, "", GUI.skin.box))
{
index = i;
}
GUI.DrawTexture(r, image != null ? image : Texture2D.blackTexture, ScaleMode.ScaleToFit, false);
if (i == index)
{
GUI.DrawTexture(r, selectedTex, ScaleMode.ScaleToFit, true);
}
if (!string.IsNullOrEmpty(label))
{
r.height = 18;
var v = r.center;
v.y += elemSize - 18;
r.center = v;
Color contentColor = GUI.contentColor;
GUI.DrawTexture(r, labelBackgroundTex, ScaleMode.StretchToFill);
GUI.contentColor = EditorGUIUtility.isProSkin ? Color.white : Color.black;
GUI.Box(r, label);
GUI.contentColor = contentColor;
}
return index;
}
static Dictionary<int, Texture2D> cachedSelectionImages = new Dictionary<int, Texture2D>();
static int DrawSelectionElement(Rect r, int i, int index, Texture2DArray texArray, string label, int elemSize)
{
if (GUI.Button(r, "", GUI.skin.box))
{
index = i;
}
int hash = texArray.GetHashCode() * 7 + i * 31;
Texture2D image = null;
bool found = cachedSelectionImages.TryGetValue(hash, out image);
if (!found || image == null)
{
var tmp = new Texture2D(texArray.width, texArray.height, texArray.format, false, false);
Graphics.CopyTexture(texArray, i, 0, tmp, 0, 0);
tmp.Apply();
RenderTexture rt = RenderTexture.GetTemporary(128, 128, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
Graphics.Blit(tmp, rt);
RenderTexture.active = rt;
image = new Texture2D(128, 128, TextureFormat.RGBA32, true, false);
image.ReadPixels(new Rect(0,0,128,128), 0, 0);
image.Apply();
cachedSelectionImages[hash] = image;
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(rt);
}
GUI.DrawTexture(r, image, ScaleMode.ScaleToFit, false);
if (i == index)
{
GUI.DrawTexture(r, selectedTex, ScaleMode.ScaleToFit, true);
}
if (!string.IsNullOrEmpty(label))
{
r.height = 18;
var v = r.center;
v.y += elemSize - 18;
r.center = v;
Color contentColor = GUI.contentColor;
GUI.DrawTexture(r, labelBackgroundTex, ScaleMode.StretchToFill);
GUI.contentColor = EditorGUIUtility.isProSkin ? Color.white : Color.black;
GUI.Box(r, label);
GUI.contentColor = contentColor;
}
return index;
}
public static int SelectionGrid(int index, Texture2D[] contents, int elemSize)
{
SetupSelectionGrid();
int width = Mathf.Max(1, (int)EditorGUIUtility.currentViewWidth / (elemSize + 3));
EditorGUILayout.BeginVertical(GUILayout.Height((contents.Length / width + 1) * elemSize + 5));
int w = 0;
EditorGUILayout.BeginHorizontal();
for (int i = 0; i < contents.Length; ++i)
{
Rect r = EditorGUILayout.GetControlRect(GUILayout.Width(elemSize), GUILayout.Height(elemSize));
index = DrawSelectionElement(r, i, index, contents[i], contents[i].name, elemSize);
w++;
if (w >= width)
{
EditorGUILayout.EndHorizontal();
w = 0;
EditorGUILayout.BeginHorizontal();
}
}
var e = Event.current;
if (e.isKey && e.type == EventType.KeyDown)
{
if (e.keyCode == KeyCode.LeftArrow)
{
index--;
e.Use();
}
if (e.keyCode == KeyCode.RightArrow)
{
index++;
e.Use();
}
if (e.keyCode == KeyCode.DownArrow)
{
index += width;
e.Use();
}
if (e.keyCode == KeyCode.UpArrow)
{
index -= width;
e.Use();
}
}
index = Mathf.Clamp(index, 0, contents.Length - 1);
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
return index;
}
public static int SelectionGrid(int index, Texture2DArray contents, int elemSize)
{
if (contents == null)
return index;
SetupSelectionGrid();
int width = Mathf.Max(1, (int)EditorGUIUtility.currentViewWidth / (elemSize + 5));
EditorGUILayout.BeginVertical(GUILayout.Height((contents.depth / width + 1) * elemSize + 10));
int w = 0;
EditorGUILayout.BeginHorizontal();
for (int i = 0; i < contents.depth; ++i)
{
Rect r = EditorGUILayout.GetControlRect(GUILayout.Width(elemSize), GUILayout.Height(elemSize));
index = DrawSelectionElement(r, i, index, contents, "", elemSize);
w++;
if (w >= width)
{
EditorGUILayout.EndHorizontal();
w = 0;
EditorGUILayout.BeginHorizontal();
}
}
var e = Event.current;
if (e.isKey && e.type == EventType.KeyDown)
{
if (e.keyCode == KeyCode.LeftArrow)
{
index--;
e.Use();
}
if (e.keyCode == KeyCode.RightArrow)
{
index++;
e.Use();
}
if (e.keyCode == KeyCode.DownArrow)
{
index += width;
e.Use();
}
if (e.keyCode == KeyCode.UpArrow)
{
index -= width;
e.Use();
}
}
index = Mathf.Clamp(index, 0, contents.depth - 1);
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
return index;
}
//
#if __MICROSPLAT_DIGGER__
public static Shader GetDiggerShader (MicroSplatTerrain mst)
{
if (mst.templateMaterial == null)
{
Debug.LogError ("Digger: Template Material is not assigned to MicroSplatTerrain component");
return null;
}
var path = AssetDatabase.GetAssetPath (mst.templateMaterial.shader);
string diggerPath = path.Replace (".shader", "_Digger.shader");
diggerPath = diggerPath.Replace(".surfshader", "_Diger.surfshader");
return AssetDatabase.LoadAssetAtPath<Shader> (diggerPath);
}
#endif
static GUIStyle msBoxStyle;
public static GUIStyle boxStyle
{
get
{
if (msBoxStyle == null)
{
msBoxStyle = new GUIStyle (EditorStyles.helpBox);
msBoxStyle.normal.textColor = GUI.skin.label.normal.textColor;
msBoxStyle.fontStyle = FontStyle.Bold;
msBoxStyle.fontSize = 11;
msBoxStyle.alignment = TextAnchor.UpperLeft;
}
return msBoxStyle;
}
}
}
}

View File

@@ -0,0 +1,69 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using UnityEditor;
#if UNITY_2019_3_OR_NEWER
// installs defines for render pipelines, so we can #if USING_HDRP and do stuff. Can't believe Unity doesn't provide this crap, they
// really go out of their way to make it hard to work across pipelines.
namespace JBooth.MicroSplat
{
public static class RenderPipelineDefine
{
private const string HDRP_PACKAGE = "HDRenderPipelineAsset";
private const string URP_PACKAGE = "UniversalRenderPipelineAsset";
public static bool IsHDRP { get; private set; }
public static bool IsURP { get; private set; }
public static bool IsStandardRP { get; private set; }
[UnityEditor.Callbacks.DidReloadScripts]
private static void OnScriptsReloaded()
{
IsHDRP = DoesTypeExist(HDRP_PACKAGE);
IsURP = DoesTypeExist(URP_PACKAGE);
if (!(IsHDRP || IsURP))
{
IsStandardRP = true;
}
}
public static bool DoesTypeExist(string className)
{
var foundType = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in GetTypesSafe(assembly)
where type.Name == className
select type).FirstOrDefault();
return foundType != null;
}
public static IEnumerable<Type> GetTypesSafe(System.Reflection.Assembly assembly)
{
Type[] types;
try
{
types = assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
types = e.Types;
}
return types.Where(x => x != null);
}
}
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -0,0 +1,403 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using UnityEditor;
using System.Text;
using System.Linq;
namespace JBooth.MicroSplat
{
public class SurfaceShaderRenderLoopAdapter : IRenderLoopAdapter
{
public string GetDisplayName()
{
return "Standard";
}
public string GetShaderExtension()
{
return "shader";
}
public string GetRenderLoopKeyword()
{
return "_MSRENDERLOOP_SURFACESHADER";
}
public string GetVersion()
{
return MicroSplatShaderGUI.MicroSplatVersion;
}
MicroSplatGenerator gen;
TextAsset templateStandard;
TextAsset templatePassForward;
TextAsset templatePassForwardAdd;
TextAsset templatePassGBuffer;
TextAsset templatePassShadow;
TextAsset templatePassMeta;
TextAsset templateShared;
TextAsset templateShaderDesc;
TextAsset templateCommonHLSL;
TextAsset templateChain;
TextAsset templateTess;
public void Init(string[] paths)
{
if (gen == null)
{
gen = new MicroSplatGenerator();
}
gen.Init(paths);
foreach (var p in paths)
{
if (p.EndsWith("microsplat_template_standard.txt"))
{
templateStandard = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_func_tess2.txt"))
{
templateTess = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_chain.txt"))
{
templateChain = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_standard_passforward.txt"))
{
templatePassForward = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_standard_passforwardadd.txt"))
{
templatePassForwardAdd = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_standard_passgbuffer.txt"))
{
templatePassGBuffer = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_standard_passmeta.txt"))
{
templatePassMeta = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_standard_passshadow.txt"))
{
templatePassShadow = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_standard_commonHLSL.txt"))
{
templateCommonHLSL = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_shaderdesc.txt"))
{
templateShaderDesc = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
else if (p.EndsWith("microsplat_template_shared.txt"))
{
templateShared = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
}
}
if (templateStandard == null)
{
Debug.LogError ("Cannot find microsplat_template_standard.txt, will not be able to compile valid shaders");
}
if (templateChain == null)
{
Debug.LogError ("Cannot find microsplat_template_chain.txt, will not be able to compile valid shaders");
}
if (templatePassForward == null)
{
Debug.LogError ("Cannot find template for pass forward, will not be able to compile valid shaders");
}
if (templatePassForwardAdd == null)
{
Debug.LogError ("Cannot find template for pass forward add, will not be able to compile valid shaders");
}
if (templatePassGBuffer == null)
{
Debug.LogError ("Cannot find template for pass gbuffer, will not be able to compile valid shaders");
}
if (templatePassShadow == null)
{
Debug.LogError ("Cannot find template for pass shadow, will not be able to compile valid shaders");
}
if (templatePassMeta == null)
{
Debug.LogError ("Cannot find template for pass meta, will not be able to compile valid shaders");
}
if (templateShaderDesc == null)
{
Debug.LogError ("Cannot find template for shader desc, will not be able to compile valid shaders");
}
if (templateShared == null)
{
Debug.LogError ("Cannot find template for template shared, will not be able to compile valid shaders");
}
if (templateCommonHLSL == null)
{
Debug.LogError ("Cannot find template for template common HLSL, will not be able to compile valid shaders");
}
}
public static StringBuilder Strip(string code, StringBuilder template, string param, string[] keys)
{
bool contains = false;
foreach (var k in keys)
{
if (code.Contains(k))
{
contains = true;
break;
}
}
if (contains)
{
return template.Replace(param, "");
}
else
{
return template.Replace(param, "//");
}
}
public static StringBuilder Strip(string code, StringBuilder template, string param, string key)
{
if (code.Contains(key))
{
return template.Replace(param, "");
}
else
{
return template.Replace(param, "//");
}
}
public static StringBuilder Strip(string code, StringBuilder template)
{
template = Strip(code, template, "%SCREENPOS%", new string[] { ".screenPos", ".screenUV" });
template = Strip(code, template, "%UV0%", ".texcoord0");
template = Strip(code, template, "%UV1%", ".texcoord1");
template = Strip(code, template, "%UV2%", ".texcoord2");
template = Strip(code, template, "%UV3%", ".texcoord3");
template = Strip(code, template, "%V2FUV0%", ".texcoord0");
template = Strip(code, template, "%V2FUV1%", ".texcoord1");
template = Strip(code, template, "%V2FUV2%", ".texcoord2");
template = Strip(code, template, "%V2FUV3%", ".texcoord3");
template = Strip(code, template, "%LOCALSPACEPOSITION%", ".localSpacePosition");
template = Strip(code, template, "%LOCALSPACENORMAL%", ".localSpaceNormal");
template = Strip(code, template, "%LOCALSPACETANGENT%", ".localSpaceTangent");
template = Strip(code, template, "%VERTEXCOLOR%", ".vertexColor");
template = Strip(code, template, "%V2FVERTEXCOLOR%", ".vertexColor");
template = Strip(code, template, "%EXTRAV2F0%", ".extraV2F0");
template = Strip(code, template, "%EXTRAV2F1%", ".extraV2F1");
template = Strip(code, template, "%EXTRAV2F2%", ".extraV2F2");
template = Strip(code, template, "%EXTRAV2F3%", ".extraV2F3");
template = Strip(code, template, "%EXTRAV2F4%", ".extraV2F4");
template = Strip(code, template, "%EXTRAV2F5%", ".extraV2F5");
template = Strip(code, template, "%EXTRAV2F6%", ".extraV2F6");
template = Strip(code, template, "%EXTRAV2F7%", ".extraV2F7");
template = Strip(code, template, "%VERTEXID%", ".vertexID");
template = template.Replace("ChainSurfaceFunction", "SurfaceFunction");
return template;
}
public static StringBuilder ReplaceOrRemove(StringBuilder template, string key, string tag, string option)
{
if (!string.IsNullOrEmpty(option))
{
return template.Replace(key, tag + " \"" + option + "\"");
}
else
{
return template.Replace(key, "");
}
}
public static string GetTags(string options)
{
string[] lines = options.ToLines();
foreach (var l in lines)
{
string s = l.Trim();
if (s.Contains("Tags {"))
{
return l;
}
}
return "";
}
public static string GetOption(string options, string name)
{
string[] lines = options.ToLines();
foreach (var l in lines)
{
string s = l.Trim();
if (s.Contains(name))
{
return s.Replace(name, "").Replace("\"", "").Trim();
}
}
return "";
}
StringBuilder BuildTemplate(Blocks blocks, string fallbackOverride = null)
{
var template = new StringBuilder(100000);
template.Append(templateStandard.text);
var passforward = new StringBuilder(templatePassForward.text);
var passforwardAdd = new StringBuilder(templatePassForwardAdd.text);
var passGBuffer = new StringBuilder(templatePassGBuffer.text);
var passShadow = new StringBuilder(templatePassShadow.text);
var passMeta = new StringBuilder(templatePassMeta.text);
if (blocks.defines.Contains("_BDRF3") || blocks.defines.Contains("_BDRFLAMBERT"))
{
passGBuffer.Length = 0;
}
if (blocks.options.Contains("Unlit"))
{
passGBuffer.Length = 0;
passforwardAdd.Length = 0;
}
if (blocks.options.Contains("Alpha"))
{
passGBuffer.Length = 0;
passShadow.Length = 0;
passforward = passforward.Replace("%FORWARDBASEBLEND%", "Blend SrcAlpha OneMinusSrcAlpha");
passforwardAdd = passforwardAdd.Replace("%FORWARDADDBLEND%", "Blend SrcAlpha One");
blocks.defines += "\n #define _ALPHABLEND_ON 1";
passforward = passforward.Insert(0, "\nZWrite Off ColorMask RGB\n\n");
}
else
{
passforward = passforward.Replace("%FORWARDBASEBLEND%", "");
passforwardAdd = passforwardAdd.Replace("%FORWARDADDBLEND%", "");
}
template = template.Replace("%PASSGBUFFER%", passGBuffer.ToString());
template = template.Replace("%PASSMETA%", passMeta.ToString());
template = template.Replace("%PASSFORWARD%", passforward.ToString());
template = template.Replace("%PASSFORWARDADD%", passforwardAdd.ToString());
template = template.Replace("%PASSSHADOW%", passShadow.ToString());
StringBuilder header = new StringBuilder();
header.AppendLine("////////////////////////////////////////");
header.AppendLine("// MicroSplat");
header.AppendLine("// Copyright (c) Jason Booth");
header.AppendLine("//");
header.AppendLine("// Auto-generated shader code, don't hand edit!");
header.AppendLine("//");
header.AppendLine("// Unity Version: " + Application.unityVersion);
header.AppendLine("// MicroSplat Version: " + MicroSplatShaderGUI.MicroSplatVersion);
header.AppendLine("// Render Pipeline: Standard");
header.AppendLine("// Platform: " + Application.platform);
header.AppendLine("////////////////////////////////////////\n\n");
template = template.Insert(0, header);
template = template.Replace("%TAGS%", GetTags(blocks.options));
template = template.Replace("%TEMPLATE_SHARED%", templateShared.text);
template = ReplaceOrRemove(template, "%CUSTOMEDITOR%", "CustomEditor", GetOption(blocks.options, "CustomEditor"));
if (fallbackOverride != null)
{
template = template.Replace("%FALLBACK%", "Fallback \"" + fallbackOverride + "\"");
template = ReplaceOrRemove(template, "%DEPENDENCY%", "Dependency \"BaseMapShader\" = ", fallbackOverride);
}
else
{
template = ReplaceOrRemove(template, "%FALLBACK%", "Fallback", "");
template = ReplaceOrRemove(template, "%DEPENDENCY%", "Dependency", "");
}
return template;
}
public StringBuilder WriteShader(string[] features,
MicroSplatShaderGUI.MicroSplatCompiler compiler,
MicroSplatShaderGUI.MicroSplatCompiler.AuxShader auxShader,
string name,
string baseName)
{
StringBuilder defines = new StringBuilder();
var blocks = gen.GetShaderBlocks(features, compiler, auxShader);
var shader = BuildTemplate(blocks, baseName);
defines.AppendLine(blocks.defines);
defines.AppendLine("\n #define _STANDARD 1");
string shaderTarget = "3.0";
if (features.Contains("_TESSDISTANCE") || features.Contains("_FORCEMODEL46"))
{
shaderTarget = "4.6";
}
else if (features.Contains("_FORCEMODEL50"))
{
shaderTarget = "5.0";
}
if (features.Contains("_TESSDISTANCE") || features.Contains("_TESSEDGE"))
{
defines.AppendLine("\n #define _TESSELLATION_ON 1");
shader = shader.Replace("%TESSELLATION%", templateTess.text);
shader = shader.Replace("%PRAGMAS%", " #pragma hull Hull\n #pragma domain Domain\n #pragma vertex TessVert\n #pragma fragment Frag\n #pragma require tesshw\n");
}
else
{
shader = shader.Replace("%PRAGMAS%", " #pragma vertex Vert\n #pragma fragment Frag");
shader = shader.Replace("%TESSELLATION%", "");
}
shader = shader.Replace("%SHADERTARGET%", shaderTarget);
if (features.Contains("_USESPECULARWORKFLOW"))
{
defines.AppendLine("\n#define _USESPECULAR 1");
defines.AppendLine("#define _MATERIAL_FEATURE_SPECULAR_COLOR 1");
}
defines.AppendLine();
defines.AppendLine(templateCommonHLSL.text);
string shaderDesk = templateShaderDesc.text;
shaderDesk += templateChain.text;
shader = shader.Replace("%SHADERDESC%", shaderDesk);
shader = shader.Replace("%SHADERNAME%", name);
shader = shader.Replace("%PROPERTIES%", blocks.properties);
shader = shader.Replace("%CODE%", blocks.code);
shader = shader.Replace("%DEFINES%", defines.ToString());
shader = shader.Replace("%CBUFFER%", blocks.cbuffer);
string codeNoComments = blocks.code.StripComments();
shader = Strip(codeNoComments, shader);
return shader;
}
}
}

View File

@@ -0,0 +1,119 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace JBooth.MicroSplat
{
#if __MICROSPLAT__ && (__MICROSPLAT_STREAMS__ || __MICROSPLAT_GLOBALTEXTURE__ || __MICROSPLAT_SNOW__ || __MICROSPLAT_SCATTER__ || __MICROSPLAT_PROCTEX__ || __MICROSPLAT_MEGA__)
public class TerrainPaintJob : ScriptableObject
{
public Terrain terrain;
public Texture2D streamTex;
public Texture2D tintTex;
public Texture2D snowTex;
public Texture2D scatterTex;
public Texture2D biomeMask;
public Texture2D biomeMask2;
public Texture2D megaSplat;
public Collider collider;
public byte [] streamBuffer;
public byte [] tintBuffer;
public byte [] snowBuffer;
public byte [] scatterBuffer;
public byte [] biomeMaskBuffer;
public byte [] biomeMaskBuffer2;
public byte [] megaBuffer;
public void RegisterUndo()
{
bool undo = false;
if (streamTex != null)
{
streamBuffer = streamTex.GetRawTextureData();
undo = true;
}
if (tintTex != null)
{
tintBuffer = tintTex.GetRawTextureData ();
undo = true;
}
if (snowTex != null)
{
snowBuffer = snowTex.GetRawTextureData ();
undo = true;
}
if (scatterTex != null)
{
scatterBuffer = scatterTex.GetRawTextureData ();
undo = true;
}
if (biomeMask != null)
{
biomeMaskBuffer = biomeMask.GetRawTextureData ();
undo = true;
}
if (biomeMask2 != null)
{
biomeMaskBuffer2 = biomeMask2.GetRawTextureData ();
undo = true;
}
if (megaSplat != null)
{
megaBuffer = megaSplat.GetRawTextureData();
undo = true;
}
if (undo)
{
UnityEditor.Undo.RegisterCompleteObjectUndo(this, "Terrain Edit");
}
}
public void RestoreUndo()
{
if (streamBuffer != null && streamBuffer.Length > 0)
{
streamTex.LoadRawTextureData(streamBuffer);
streamTex.Apply();
}
if (tintTex != null && tintBuffer.Length > 0)
{
tintTex.LoadRawTextureData (tintBuffer);
tintTex.Apply ();
}
if (snowBuffer != null && snowBuffer.Length > 0)
{
snowTex.LoadRawTextureData (streamBuffer);
snowTex.Apply ();
}
if (scatterBuffer != null && scatterBuffer.Length > 0)
{
scatterTex.LoadRawTextureData (scatterBuffer);
scatterTex.Apply ();
}
if (biomeMask != null && biomeMaskBuffer.Length > 0)
{
biomeMask.LoadRawTextureData (biomeMaskBuffer);
biomeMask.Apply ();
}
if (biomeMask2 != null && biomeMaskBuffer2.Length > 0)
{
biomeMask.LoadRawTextureData (biomeMaskBuffer2);
biomeMask.Apply ();
}
if (megaSplat != null && megaBuffer.Length > 0)
{
megaSplat.LoadRawTextureData(megaBuffer);
megaSplat.Apply();
}
}
}
#endif
}

View File

@@ -0,0 +1,278 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
namespace JBooth.MicroSplat
{
#if __MICROSPLAT__ && (__MICROSPLAT_STREAMS__ || __MICROSPLAT_GLOBALTEXTURE__ || __MICROSPLAT_SNOW__ || __MICROSPLAT_SCATTER__ || __MICROSPLAT_PROCTEX__ || __MICROSPLAT_MEGA__)
public partial class TerrainPainterWindow : EditorWindow
{
[MenuItem("Window/MicroSplat/Terrain FX Painter")]
public static void ShowWindow()
{
var window = GetWindow<JBooth.MicroSplat.TerrainPainterWindow>();
window.InitTerrains();
window.Show();
}
bool enabled = true;
TerrainPaintJob[] terrains;
bool[] jobEdits;
TerrainPaintJob FindJob (Terrain t)
{
if (terrains == null || t == null)
return null;
for (int i = 0; i < terrains.Length; ++i)
{
if (terrains[i] != null && terrains[i].terrain == t)
return terrains[i];
}
return null;
}
List<Terrain> rawTerrains = new List<Terrain>();
void InitTerrains()
{
Object[] objs = Selection.GetFiltered(typeof(Terrain), SelectionMode.Editable | SelectionMode.Deep);
List<TerrainPaintJob> ts = new List<TerrainPaintJob> ();
rawTerrains.Clear();
for (int i = 0; i < objs.Length; ++i)
{
Terrain t = objs[i] as Terrain;
MicroSplatTerrain mst = t.GetComponent<MicroSplatTerrain>();
if (mst == null)
continue;
rawTerrains.Add(t);
if (t.materialTemplate != null)
{
bool hasStream = t.materialTemplate.HasProperty ("_StreamControl");
bool hasSnow = t.materialTemplate.HasProperty ("_SnowMask");
bool hasTint = t.materialTemplate.HasProperty ("_GlobalTintTex");
bool hasScatter = t.materialTemplate.HasProperty ("_ScatterControl");
bool hasBiome = t.materialTemplate.HasProperty ("_ProcTexBiomeMask");
bool hasBiome2 = t.materialTemplate.HasProperty ("_ProcTexBiomeMask2");
bool hasMega = t.materialTemplate.HasProperty("_MegaSplatTexture");
if (!hasSnow && !hasStream && !hasTint && !hasScatter && !hasBiome && !hasBiome2 && !hasMega)
continue;
#if __MICROSPLAT_STREAMS__
if (hasStream && mst.streamTexture == null)
{
mst.streamTexture = CreateTexture(t, mst.streamTexture, "_stream_data", new Color(0,0,0,0), true);
}
#endif
#if __MICROSPLAT_SNOW__
if (hasSnow && mst.snowMaskOverride == null)
{
mst.snowMaskOverride = CreateTexture (t, mst.snowMaskOverride, "_snowmask", new Color(1,0,0,1), true);
}
#endif
#if __MICROSPLAT_SCATTER__
if (hasScatter && mst.scatterMapOverride == null)
{
mst.scatterMapOverride = CreateTexture (t, mst.scatterMapOverride, "_scatter", new Color (0, 0, 0, 1), true);
}
#endif
#if __MICROSPLAT_MEGA__
if (hasMega && mst.megaSplatMap == null)
{
mst.megaSplatMap = CreateTexture(t, mst.megaSplatMap, "_megasplat", new Color(0, 0, 0, 0), true, false);
}
#endif
var tj = FindJob(t);
if (tj != null)
{
tj.collider = t.GetComponent<Collider>();
#if __MICROSPLAT_STREAMS__
tj.streamTex = mst.streamTexture;
#endif
#if __MICROSPLAT_GLOBALTEXTURE__
tj.tintTex = mst.tintMapOverride;
#endif
#if __MICROSPLAT_SNOW__
tj.snowTex = mst.snowMaskOverride;
#endif
#if __MICROSPLAT_SCATTER__
tj.scatterTex = mst.scatterMapOverride;
#endif
#if __MICROSPLAT_MEGA__
tj.megaSplat = mst.megaSplatMap;
#endif
#if __MICROSPLAT_PROCTEX__
tj.biomeMask = mst.procBiomeMask;
tj.biomeMask2 = mst.procBiomeMask2;
#endif
ts.Add(tj);
}
else
{
tj = TerrainPaintJob.CreateInstance<TerrainPaintJob> ();
tj.terrain = t;
tj.collider = t.GetComponent<Collider>();
#if __MICROSPLAT_STREAMS__
tj.streamTex = mst.streamTexture;
#endif
#if __MICROSPLAT_GLOBALTEXTURE__
tj.tintTex = mst.tintMapOverride;
#endif
#if __MICROSPLAT_SNOW__
tj.snowTex = mst.snowMaskOverride;
#endif
#if __MICROSPLAT_SCATTER__
tj.scatterTex = mst.scatterMapOverride;
#endif
#if __MICROSPLAT_MEGA__
tj.megaSplat = mst.megaSplatMap;
#endif
#if __MICROSPLAT_PROCTEX__
tj.biomeMask = mst.procBiomeMask;
tj.biomeMask2 = mst.procBiomeMask2;
#endif
ts.Add (tj);
}
}
}
if (terrains != null)
{
// clear out old terrains
for (int i = 0; i < terrains.Length; ++i)
{
if (!ts.Contains(terrains[i]))
{
DestroyImmediate(terrains[i]);
}
}
}
terrains = ts.ToArray();
jobEdits = new bool[ts.Count];
}
void OnSelectionChange()
{
InitTerrains();
this.Repaint();
}
void OnFocus()
{
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui -= this.OnSceneGUI;
SceneView.duringSceneGui += this.OnSceneGUI;
#else
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
SceneView.onSceneGUIDelegate += this.OnSceneGUI;
#endif
Undo.undoRedoPerformed -= this.OnUndo;
Undo.undoRedoPerformed += this.OnUndo;
UnityEditor.SceneManagement.EditorSceneManager.sceneSaving -= OnSceneSaving;
UnityEditor.SceneManagement.EditorSceneManager.sceneSaving += OnSceneSaving;
this.titleContent = new GUIContent("MicroSplat Terrain FX Painter");
InitTerrains();
Repaint();
}
public static void SaveTexture (Texture2D tex)
{
if (tex != null)
{
string path = AssetDatabase.GetAssetPath (tex);
if (path.EndsWith(".tga"))
{
AssetImporter ai = AssetImporter.GetAtPath(path);
TextureImporter ti = ai as TextureImporter;
if (ti.textureCompression == TextureImporterCompression.Uncompressed &&
ti.isReadable)
{
var bytes = tex.EncodeToTGA();
System.IO.File.WriteAllBytes(path, bytes);
}
}
else
{
Debug.LogError(path + " is not a TGA file");
}
}
}
void SaveAll ()
{
if (terrains == null)
return;
for (int i = 0; i < terrains.Length; ++i)
{
SaveTexture (terrains [i].streamTex);
SaveTexture (terrains [i].tintTex);
SaveTexture (terrains [i].snowTex);
SaveTexture (terrains [i].scatterTex);
SaveTexture (terrains [i].biomeMask);
SaveTexture (terrains [i].biomeMask2);
SaveTexture (terrains [i].megaSplat);
}
AssetDatabase.Refresh ();
}
void OnSceneSaving (UnityEngine.SceneManagement.Scene scene, string path)
{
SaveAll ();
}
void OnUndo()
{
if (terrains == null)
return;
for (int i = 0; i < terrains.Length; ++i)
{
if (terrains[i] != null)
{
terrains[i].RestoreUndo();
}
}
Repaint();
}
void OnInspectorUpdate()
{
// unfortunate...
Repaint ();
}
void OnDestroy()
{
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui -= this.OnSceneGUI;
#else
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
#endif
terrains = null;
}
}
#endif
}

View File

@@ -0,0 +1,484 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace JBooth.MicroSplat
{
#if __MICROSPLAT__ && (__MICROSPLAT_STREAMS__ || __MICROSPLAT_GLOBALTEXTURE__ || __MICROSPLAT_SNOW__ || __MICROSPLAT_SCATTER__ || __MICROSPLAT_PROCTEX__ || __MICROSPLAT_MEGA__)
public partial class TerrainPainterWindow : EditorWindow
{
double deltaTime = 0;
double lastTime = 0;
bool painting = false;
public Vector3 oldpos = Vector3.zero;
public float brushSize = 1;
public float brushFlow = 8;
public float brushFalloff = 1; // linear
public Color paintColor = Color.grey;
public float paintValue = 1;
public int biomeChannel = 0;
[System.Serializable]
public enum MegaLayerMode
{
Bottom,
Top,
Auto
}
[System.Serializable]
public enum MegaBrushMode
{
Single,
Cluster,
Blend
}
[System.Serializable]
public class ClusterBrush
{
public List<int> textures = new List<int>();
public float frequency = 1;
public float offset;
}
public int megaIndex = 0;
public MegaLayerMode megaLayerMode = MegaLayerMode.Auto;
public MegaBrushMode megaBrushMode = MegaBrushMode.Single;
public ClusterBrush clusterBrush = new ClusterBrush();
public System.Action<TerrainPaintJob []> OnBeginStroke;
public System.Action<TerrainPaintJob, bool> OnStokeModified; // bool is true when doing a fill or other non-bounded opperation
public System.Action OnEndStroke;
public enum BrushVisualization
{
Sphere,
Disk
}
public BrushVisualization brushVisualization = BrushVisualization.Sphere;
public Vector2 lastMousePosition;
void OnSceneGUI(SceneView sceneView)
{
deltaTime = EditorApplication.timeSinceStartup - lastTime;
lastTime = EditorApplication.timeSinceStartup;
if (terrains == null || terrains.Length == 0 && Selection.activeGameObject != null)
{
InitTerrains();
}
if (!enabled || terrains.Length == 0 || Selection.activeGameObject == null)
{
return;
}
RaycastHit hit;
float distance = float.MaxValue;
Vector3 mousePosition = Event.current.mousePosition;
// So, in 5.4, Unity added this value, which is basically a scale to mouse coordinates for retna monitors.
// Not all monitors, just some of them.
// What I don't get is why the fuck they don't just pass me the correct fucking value instead. I spent hours
// finding this, and even the paid Unity support my company pays many thousands of dollars for had no idea
// after several weeks of back and forth. If your going to fake the coordinates for some reason, please do
// it everywhere to not just randomly break things everywhere you don't multiply some new value in.
float mult = EditorGUIUtility.pixelsPerPoint;
mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y * mult;
mousePosition.x *= mult;
Vector3 fakeMP = mousePosition;
fakeMP.z = 20;
Vector3 point = sceneView.camera.ScreenToWorldPoint(fakeMP);
Vector3 normal = Vector3.forward;
Ray ray = sceneView.camera.ScreenPointToRay(mousePosition);
bool registerUndo = (Event.current.type == EventType.MouseDown && Event.current.button == 0 && Event.current.alt == false);
for (int i = 0; i < terrains.Length; ++i)
{
if (terrains[i] == null)
continue;
// Early out if we're not in the area..
var cld = terrains[i].collider;
Bounds b = cld.bounds;
b.Expand(brushSize*2);
if (!b.IntersectRay(ray))
{
continue;
}
if (registerUndo)
{
painting = true;
for (int x = 0; x < jobEdits.Length; ++x)
{
jobEdits[x] = false;
}
if (i == 0 && OnBeginStroke != null)
{
OnBeginStroke(terrains);
}
}
if (cld.Raycast(ray, out hit, float.MaxValue))
{
if (Event.current.shift == false)
{
if (hit.distance < distance)
{
distance = hit.distance;
point = hit.point;
normal = hit.normal;
}
}
else
{
point = oldpos;
}
}
else
{
if (Event.current.shift == true)
{
point = oldpos;
}
}
}
if (Event.current.type == EventType.MouseMove && Event.current.shift)
{
brushSize += Event.current.delta.x * (float)deltaTime * 6.0f;
brushFalloff -= Event.current.delta.y * (float)deltaTime * 48.0f;
}
if (Event.current.rawType == EventType.MouseUp)
{
EndStroke();
}
if (Event.current.type == EventType.MouseMove && Event.current.alt)
{
brushSize += Event.current.delta.y * (float)deltaTime;
}
if (brushVisualization == BrushVisualization.Sphere)
{
Handles.SphereHandleCap(0, point, Quaternion.identity, brushSize * 2, EventType.Repaint);
}
else
{
Handles.color = new Color(0.8f, 0, 0, 1.0f);
float r = Mathf.Pow(0.5f, brushFalloff);
Handles.DrawWireDisc(point, normal, brushSize * r);
Handles.color = new Color(0.9f, 0, 0, 0.8f);
Handles.DrawWireDisc(point, normal, brushSize);
}
// eat current event if mouse event and we're painting
if (Event.current.isMouse && painting)
{
Event.current.Use();
}
if (Event.current.type == EventType.Layout)
{
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(GetHashCode(), FocusType.Passive));
}
// only paint once per frame
if (Event.current.type != EventType.Repaint)
{
return;
}
if (terrains.Length > 0 && painting)
{
for (int i = 0; i < terrains.Length; ++i)
{
Bounds b = terrains[i].collider.bounds;
b.Expand(brushSize * 2);
if (!b.IntersectRay(ray))
{
continue;
}
if (jobEdits[i] == false)
{
jobEdits[i] = true;
terrains[i].RegisterUndo();
}
PaintTerrain(terrains[i], point);
if (OnStokeModified != null)
{
OnStokeModified(terrains[i], false);
}
}
}
lastMousePosition = Event.current.mousePosition;
// update views
sceneView.Repaint();
HandleUtility.Repaint();
}
void EndStroke()
{
painting = false;
if (OnEndStroke != null)
{
OnEndStroke();
}
}
public static Vector3 WorldToTerrain(Terrain ter, Vector3 point, Texture2D splatControl)
{
float x = (point.x / ter.terrainData.size.x) * splatControl.width;
float z = (point.z / ter.terrainData.size.z) * splatControl.height;
float y = ter.terrainData.GetHeight((int)x, (int)z);
return new Vector3(x, y, z);
}
public static Vector3 TerrainToWorld(Terrain ter, int x, int y, Texture2D splatControl)
{
Vector3 wp = new Vector3(x, 0, y);
wp.x *= ter.terrainData.size.x / (float)splatControl.width;
wp.y = ter.terrainData.GetHeight(x, y);
wp.z *= ter.terrainData.size.z / (float)splatControl.height;
wp += ter.transform.position;
return wp;
}
void MegaBrush(Texture2D tex, int x, int y, float finalStr)
{
Color data = tex.GetPixel(x, y);
int index0 = Mathf.RoundToInt(data.r * 256);
int index1 = Mathf.RoundToInt(data.g * 256);
float weight = data.b;
if (megaBrushMode != MegaBrushMode.Blend)
{
if (megaBrushMode == MegaBrushMode.Cluster)
{
float px = (float)x / (tex.width) * 128;
float py = (float)y / (tex.height) * 128;
float v = Mathf.PerlinNoise(px * clusterBrush.frequency + clusterBrush.offset, py * clusterBrush.frequency - clusterBrush.offset);
v = Mathf.Clamp01(v);
int idx = (int)(v * clusterBrush.textures.Count);
if (idx >= clusterBrush.textures.Count)
{
idx = clusterBrush.textures.Count - 1;
}
megaIndex = clusterBrush.textures[idx];
}
if (megaLayerMode == MegaLayerMode.Auto)
{
// replace lowest weight if we're new.
if (megaIndex != index0 && megaIndex != index1)
{
if (weight >= 0.5f)
{
index0 = megaIndex;
}
else
{
index1 = megaIndex;
}
}
if (megaIndex == index0)
{
weight -= finalStr;
}
else
{
weight += finalStr;
}
}
else if (megaLayerMode == MegaLayerMode.Bottom)
{
index0 = megaIndex;
weight -= finalStr;
}
else
{
index1 = megaIndex;
weight += finalStr;
}
}
else if (megaBrushMode == MegaBrushMode.Blend)
{
weight = Mathf.Lerp(weight, paintValue, finalStr);
}
weight = Mathf.Clamp01(weight);
data.r = (float)index0 / 255.0f;
data.g = (float)index1 / 255.0f;
data.b = weight;
tex.SetPixel(x, y, data);
}
void PaintTerrain(TerrainPaintJob tj, Vector3 worldPoint)
{
if (tj == null)
return;
// convert point into local space, so we don't have to convert every point
var mtx = Matrix4x4.TRS(tj.terrain.transform.position, tj.terrain.transform.rotation, Vector3.one).inverse;
Vector3 localPoint = mtx.MultiplyPoint3x4(worldPoint);
float bz = brushSize;
float pressure = Event.current.pressure > 0 ? Event.current.pressure : 1.0f;
Texture2D tex = null;
int channel = -1;
GetTexAndChannel (tj, out tex, out channel);
if (tex == null)
{
return;
}
Vector3 terPoint = WorldToTerrain(tj.terrain, localPoint, tex);
if (terPoint.x >= 0 && terPoint.z >= 0 && terPoint.x < tex.width || terPoint.z < tex.height)
{
// scale brush into texture space
Vector3 offsetPnt = localPoint - new Vector3(bz, 0, bz);
Vector3 beginTerPnt = WorldToTerrain(tj.terrain, offsetPnt, tex);
beginTerPnt.x = Mathf.Clamp(beginTerPnt.x, 0, tex.width);
beginTerPnt.z = Mathf.Clamp(beginTerPnt.z, 0, tex.height);
Vector3 offset = terPoint - beginTerPnt;
int pbx = (int)beginTerPnt.x;
int pby = (int)beginTerPnt.z;
int pex = (int)(terPoint.x + offset.x * 2.0f);
int pey = (int)(terPoint.z + offset.z * 2.0f);
pex = Mathf.Clamp(pex, 0, tex.width);
pey = Mathf.Clamp(pey, 0, tex.height);
for (int x = pbx; x < pex; ++x)
{
for (int y = pby; y < pey; ++y)
{
float h = tj.terrain.terrainData.GetHeight(x, y);
float d = Vector3.Distance(terPoint, new Vector3(x, h, y));
float str = 1.0f - d / bz;
str = Mathf.Pow(str, brushFalloff);
float finalStr = str * (float)deltaTime * brushFlow * pressure;
if (finalStr > 0)
{
Vector3 normal = tj.terrain.terrainData.GetInterpolatedNormal((float)x / tex.width, (float)y / tex.height);
float dt = Vector3.Dot(normal, Vector3.up);
dt = 1 - Mathf.Clamp01(dt);
bool filtered = dt < slopeRange.x || dt > slopeRange.y;
if (tab == Tab.Scatter)
{
filtered = false;
}
if (!filtered)
{
if (tab == Tab.TintMap)
{
Color c = tex.GetPixel (x, y);
c.r = Mathf.Lerp (c.r, paintColor.r, finalStr);
c.g = Mathf.Lerp (c.g, paintColor.g, finalStr);
c.b = Mathf.Lerp (c.b, paintColor.b, finalStr);
tex.SetPixel (x, y, c);
}
else if (tab == Tab.SnowMin)
{
Color c = tex.GetPixel (x, y);
c.g = Mathf.Lerp (c.g, paintValue, finalStr);
tex.SetPixel (x, y, c);
}
else if (tab == Tab.SnowMax)
{
Color c = tex.GetPixel (x, y);
c.r = Mathf.Lerp (c.r, paintValue, finalStr);
tex.SetPixel (x, y, c);
}
else if (tab == Tab.Wetness)
{
Color c = tex.GetPixel(x, y);
c.r = Mathf.Lerp(c.r, paintValue, finalStr);
tex.SetPixel(x, y, c);
}
else if (tab == Tab.Puddles)
{
Color c = tex.GetPixel(x, y);
c.g = Mathf.Lerp(c.g, paintValue, finalStr);
tex.SetPixel(x, y, c);
}
else if (tab == Tab.Streams)
{
Color c = tex.GetPixel(x, y);
c.b = Mathf.Lerp(c.b, paintValue, finalStr);
tex.SetPixel(x, y, c);
}
else if (tab == Tab.Lava)
{
Color c = tex.GetPixel(x, y);
c.a = Mathf.Lerp(c.a, paintValue, finalStr);
tex.SetPixel(x, y, c);
}
else if (tab == Tab.Scatter)
{
Color c = tex.GetPixel (x, y);
#if __MICROSPLAT_SCATTER__
if (scatterLayer == ScatterLayer.First)
{
c.r = (float)(scatterIndex + 1) / 64.0f;
c.g = Mathf.Lerp (c.g, paintValue, finalStr);
if (c.g <= 0)
{
c.r = 0;
}
}
else
{
c.a = (float)(scatterIndex + 1) / 64.0f;
c.b = Mathf.Lerp (c.b, paintValue, finalStr);
if (c.b <= 0)
{
c.a = 0;
}
}
#endif
tex.SetPixel (x, y, c);
}
else if (tab == Tab.Biome)
{
Color c = tex.GetPixel (x, y);
int ch = biomeChannel;
if (ch > 3)
ch -= 4;
c[ch] = Mathf.Lerp(c[ch], paintValue, finalStr);
tex.SetPixel (x, y, c);
}
else if (tab == Tab.Mega)
{
MegaBrush(tex, x, y, finalStr);
}
}
}
}
}
tex.Apply();
}
}
}
#endif
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,156 @@
//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Build;
namespace JBooth.MicroSplat
{
[InitializeOnLoad]
public class TextureArrayActiveBuildTargetListener : IActiveBuildTargetChanged
{
public int callbackOrder => 0;
public void OnActiveBuildTargetChanged(BuildTarget previousTarget, BuildTarget newTarget)
{
var guids = AssetDatabase.FindAssets("t: TextureArrayConfig");
foreach (var guid in guids)
{
AssetDatabase.ImportAsset(AssetDatabase.GUIDToAssetPath(guid));
}
}
}
class TextureArrayPreProcessor : AssetPostprocessor
{
// this is a shitty hash, but good enough for unity versions..
static int HashString(string str)
{
unchecked
{
int h = 0;
int[] hashPrimes = { 3, 5, 7, 11, 13, 17, 23, 27 };
int pidx = 0;
foreach (char c in str)
{
h += (int)c * hashPrimes[pidx % hashPrimes.Length];
pidx++;
}
return h;
}
}
static int GetNewHash(TextureArrayConfig cfg)
{
unchecked
{
var settings = TextureArrayConfigEditor.GetSettingsGroup(cfg, UnityEditor.EditorUserBuildSettings.activeBuildTarget);
int h = 17;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.diffuseSettings.compression, settings.diffuseSettings.compressionQuality) * 7;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.normalSettings.compression, settings.normalSettings.compressionQuality) * 13;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.emissiveSettings.compression, settings.emissiveSettings.compressionQuality) * 17;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.antiTileSettings.compression, settings.antiTileSettings.compressionQuality) * 23;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.smoothSettings.compression, settings.smoothSettings.compressionQuality) * 31;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.traxDiffuseSettings.compression, settings.traxDiffuseSettings.compressionQuality) * 37;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.traxNormalSettings.compression, settings.traxNormalSettings.compressionQuality) * 41;
h = h * (int)TextureArrayConfigEditor.GetTextureFormat(cfg, settings.decalSplatSettings.compression, settings.decalSplatSettings.compressionQuality) * 43;
h = h * HashString(Application.unityVersion) * 51;
//h = h * EditorUserBuildSettings.activeBuildTarget.GetHashCode () * 47;
return h;
}
}
public static bool sIsPostProcessing = false;
static void OnPostprocessAllAssets (string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
var updates = new HashSet<TextureArrayConfig>();
AddChangedConfigsToHashSet(updates, importedAssets);
AddChangedConfigsToHashSet(updates, movedAssets);
AddChangedConfigsToHashSet(updates, movedFromAssetPaths);
// this block allows users to not include the texture arrays in
// source control, and MS will regenerate them if they are missing.
bool needsSync = false;
var guids = AssetDatabase.FindAssets("t: TextureArrayConfig");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var diffuseArrayPath = path.Replace(".asset", "_diff_tarray.asset");
if (!System.IO.File.Exists(diffuseArrayPath))
{
var cfg = AssetDatabase.LoadAssetAtPath<TextureArrayConfig>(path);
sIsPostProcessing = true;
TextureArrayConfigEditor.CompileConfig(cfg);
needsSync = true;
sIsPostProcessing = false;
}
}
if (needsSync)
{
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
MicroSplatTerrain.SyncAll();
}
foreach (var updatedConfig in updates)
{
CheckConfigForUpdates(updatedConfig);
}
}
private static void AddChangedConfigsToHashSet(HashSet<TextureArrayConfig> hashSet, string[] paths)
{
for (int i = 0; i < paths.Length; i++)
{
var type = AssetDatabase.GetMainAssetTypeAtPath(paths[i]);
if (type != typeof(TextureArrayConfig))
{
continue;
}
var cfg = AssetDatabase.LoadAssetAtPath<TextureArrayConfig>(paths[i]);
if (cfg != null)
{
hashSet.Add(cfg);
}
else
{
Debug.LogWarning($"Unexpectedly failed to load ${nameof(TextureArrayConfig)} at path ${paths[i]}");
}
}
}
private static void CheckConfigForUpdates(TextureArrayConfig cfg)
{
int hash = GetNewHash(cfg);
if (hash != cfg.hash)
{
cfg.hash = hash;
EditorUtility.SetDirty(cfg);
try
{
sIsPostProcessing = true;
TextureArrayConfigEditor.CompileConfig(cfg);
}
finally
{
sIsPostProcessing = false;
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
MicroSplatTerrain.SyncAll();
}
}
}
}
}