This commit is contained in:
SoulliesOfficial
2026-07-17 17:46:16 -04:00
parent b00ac27e3a
commit 40fa80cd70
1178 changed files with 51090 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fd758a702a9040fcb8614b232817cff9
timeCreated: 1721290722

View File

@@ -0,0 +1,146 @@
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(AnimationSheetHelper))]
public class AnimationSheetHelperEditor : Editor
{
SerializedProperty isParticleBaseShader;
SerializedProperty isPostProcessShader;
SerializedProperty postProcessingController;
SerializedProperty propertyName;
SerializedProperty xSize;
SerializedProperty ySize;
SerializedProperty manualPlay;
SerializedProperty manualPlayePos;
SerializedProperty speed;
SerializedProperty scaleOffset;
SerializedProperty mat;
SerializedProperty frameIndex;
SerializedProperty frameCount;
private AnimationSheetHelper _target;
void OnEnable()
{
_target = (AnimationSheetHelper)target;
isParticleBaseShader = serializedObject.FindProperty("isParticleBaseShader");
// isPostProcessShader = serializedObject.FindProperty("isPostProcessShader");
// postProcessingController = serializedObject.FindProperty("postProcessingController");
propertyName = serializedObject.FindProperty("propertyName");
xSize = serializedObject.FindProperty("xSize");
ySize = serializedObject.FindProperty("ySize");
manualPlay = serializedObject.FindProperty("manualPlay");
manualPlayePos = serializedObject.FindProperty("manualPlayePos");
speed = serializedObject.FindProperty("speed");
scaleOffset = serializedObject.FindProperty("scaleOffset");
mat = serializedObject.FindProperty("mat");
frameIndex = serializedObject.FindProperty("frameIndex");
frameCount = serializedObject.FindProperty("frameCount");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawShaderSection();
EditorGUILayout.Space(5);
DrawAnimationSection();
EditorGUILayout.Space(5);
DrawRuntimeInfo();
EditorGUILayout.Space(8);
DrawInitButton();
serializedObject.ApplyModifiedProperties();
}
#region Sections
void DrawShaderSection()
{
EditorGUILayout.LabelField("Shader 设置", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(isParticleBaseShader, new GUIContent("使用 NB Shader"));
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
_target.InitParticleBaseShaderToggle();
EditorUtility.SetDirty(_target);
}
// EditorGUI.BeginChangeCheck();
// EditorGUILayout.PropertyField(isPostProcessShader, new GUIContent("使用 后处理扭曲"));
// if (EditorGUI.EndChangeCheck())
// {
// serializedObject.ApplyModifiedProperties();
// // _target.InitPostProcessToggle();
// EditorUtility.SetDirty(_target);
// }
// if (isPostProcessShader.boolValue)
// {
// GUI.enabled = false;
// EditorGUILayout.PropertyField(postProcessingController);
// GUI.enabled = true;
// }
EditorGUILayout.PropertyField(propertyName, new GUIContent("Shader 属性名"));
}
void DrawAnimationSection()
{
EditorGUILayout.LabelField("序列帧设置", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(xSize, new GUIContent("横向帧数量"));
EditorGUILayout.PropertyField(ySize, new GUIContent("纵向帧数量"));
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
_target.Init();
EditorUtility.SetDirty(_target);
}
EditorGUILayout.Space(4);
EditorGUILayout.PropertyField(manualPlay, new GUIContent("手动控制播放"));
if (manualPlay.boolValue)
{
EditorGUILayout.PropertyField(manualPlayePos, new GUIContent("手动播放位置"));
}
else
{
EditorGUILayout.PropertyField(speed, new GUIContent("播放速度 fps"));
}
}
void DrawRuntimeInfo()
{
EditorGUILayout.LabelField("运行时信息", EditorStyles.boldLabel);
GUI.enabled = false;
EditorGUILayout.PropertyField(scaleOffset, new GUIContent("Tiling Offset"));
EditorGUILayout.PropertyField(mat, new GUIContent("Material"));
EditorGUILayout.PropertyField(frameIndex);
EditorGUILayout.PropertyField(frameCount);
GUI.enabled = true;
}
void DrawInitButton()
{
if (GUILayout.Button("初始化", GUILayout.Height(28)))
{
_target.Init();
EditorUtility.SetDirty(_target);
}
}
#endregion
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2e1a5d6748bd4515869cb9c6ebd2b548
timeCreated: 1769260616

View File

@@ -0,0 +1,103 @@
using UnityEditor;
using UnityEngine;
using System;
namespace NBShaderEditor
{
public class BinaryIntAttribute : PropertyAttribute
{
public int binaryBits;
public bool showInputFiled;
public int tabNums;
public BinaryIntAttribute(int Bits = 32, bool showInput = false, int tab = 0)
{
binaryBits = Bits;
showInputFiled = showInput;
tabNums = tab;
}
}
[CustomPropertyDrawer(typeof(BinaryIntAttribute))]
public class BinaryIntDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUIStyle richTextStyle = EditorStyles.label;
richTextStyle.richText = true;
BinaryIntAttribute binaryIntAttribute = (BinaryIntAttribute)attribute;
// int value = property.intValue;
// int largestBit = 0;
// for (int i = 0; i < 32; i++)
// {
// if ((~value & (1 << i)) == 0)
// {
// largestBit = i;
// }
// }
//
// int addZeroCount = 0;
// if (largestBit < binaryIntAttribute.binaryBits)
// {
// addZeroCount = binaryIntAttribute.binaryBits - largestBit - 1;
// }
//
// string addZeroString = "";
// for (int i = 0; i < addZeroCount; i++)
// {
// addZeroString += "0";
// }
// string binary = addZeroString+Convert.ToString(property.intValue, 2);
string binary = DrawBinaryInt(property.intValue, binaryIntAttribute.binaryBits);
string tabs = "";
for (int i = 0; i < binaryIntAttribute.tabNums; i++)
{
tabs += "\t";
}
if (binaryIntAttribute.showInputFiled)
{
string labelText = property.displayName + tabs + "<mspace=1em>" + binary + "</mspace>";
Rect intRect = EditorGUI.PrefixLabel(position, new GUIContent(labelText), richTextStyle);
property.intValue = EditorGUI.IntField(intRect, property.intValue);
}
else
{
EditorGUILayout.LabelField(property.displayName + tabs);
EditorGUILayout.LabelField(binary);
}
}
public static string DrawBinaryInt(int value, int binaryBits)
{
int largestBit = 0;
for (int i = 0; i < 32; i++)
{
if ((~value & (1 << i)) == 0)
{
largestBit = i;
}
}
int addZeroCount = 0;
if (largestBit < binaryBits)
{
addZeroCount = binaryBits - largestBit - 1;
}
string addZeroString = "";
for (int i = 0; i < addZeroCount; i++)
{
addZeroString += "0";
}
string binary = addZeroString + Convert.ToString(value, 2);
return binary;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4517deaf5f31a5848b6a58304d1f516f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,253 @@
using System;
using System.Reflection;
using UnityEngine;
namespace NBShaderEditor
{
//因为GradientPicker窗口在窗口打开时使用的是Gradient对象的Cache。
//如果有Undo操作导致MaterialProperty更新了但Gradient却更新不到ColorPicer里。所以在这里进行强制更新。
//后续需要强制更新Picker的时候也可以用这里。
//DeepSeek写的。谨慎用。
public static class GradientReflectionHelper
{
#region GradientPicker
private static Type _gradientPickerType;
// SetCurrentGradient 方法
private static MethodInfo _setGradientMethod;
private static Action<Gradient> _setGradientDelegate;
// RefreshGradientData 方法
private static MethodInfo _refreshMethod;
private static Action _refreshDelegate;
#endregion
#region GradientPreviewCache
private static Type _gradientCacheType;
private static MethodInfo _clearCacheMethod;
private static Action _clearCacheDelegate;
#endregion
// 初始化状态
private static bool _initialized;
private static bool _isValid;
private static readonly object _initLock = new object();
#region API
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void SetCurrentGradient(Gradient gradient)
{
ExecuteAction(() =>
{
if (_setGradientDelegate != null)
_setGradientDelegate(gradient);
else if (_setGradientMethod != null)
_setGradientMethod.Invoke(null, new object[] { gradient });
}, "SetCurrentGradient");
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void RefreshGradientData()
{
ExecuteAction(() =>
{
if (_refreshDelegate != null)
_refreshDelegate();
else if (_refreshMethod != null)
_refreshMethod.Invoke(null, null);
}, "RefreshGradientData");
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void ClearGradientCache()
{
ExecuteAction(() =>
{
if (_clearCacheDelegate != null)
_clearCacheDelegate();
else if (_clearCacheMethod != null)
_clearCacheMethod.Invoke(null, null);
}, "ClearGradientCache");
}
#endregion
#region
private static void ExecuteAction(Action action, string methodName)
{
if (!Application.isEditor) return;
EnsureInitialized();
if (!_isValid) return;
try
{
action?.Invoke();
}
catch (Exception ex)
{
Debug.LogError($"[GradientReflection] {methodName} failed: {ex.Message}");
// 可选择性地禁用后续调用
// _isValid = false;
}
}
private static void EnsureInitialized()
{
if (_initialized) return;
lock (_initLock)
{
if (_initialized) return;
try
{
// 1. 初始化 GradientPicker 类型
_gradientPickerType = Type.GetType("UnityEditor.GradientPicker, UnityEditor");
if (_gradientPickerType != null)
{
// 初始化 SetCurrentGradient
_setGradientMethod = GetStaticMethod(
_gradientPickerType,
"SetCurrentGradient",
new[] { typeof(Gradient) }
);
if (_setGradientMethod != null)
{
_setGradientDelegate = CreateActionDelegate<Gradient>(_setGradientMethod);
}
// 初始化 RefreshGradientData
_refreshMethod = GetStaticMethod(
_gradientPickerType,
"RefreshGradientData",
Type.EmptyTypes
);
if (_refreshMethod != null)
{
_refreshDelegate = CreateActionDelegate(_refreshMethod);
}
}
else
{
Debug.LogWarning("[GradientReflection] GradientPicker type not found");
}
// 2. 初始化 GradientPreviewCache
_gradientCacheType = Type.GetType("UnityEditorInternal.GradientPreviewCache, UnityEditor");
if (_gradientCacheType != null)
{
_clearCacheMethod = GetStaticMethod(
_gradientCacheType,
"ClearCache",
Type.EmptyTypes
);
if (_clearCacheMethod != null)
{
_clearCacheDelegate = CreateActionDelegate(_clearCacheMethod);
}
}
else
{
Debug.LogWarning("[GradientReflection] GradientPreviewCache type not found");
}
// 3. 验证初始化状态
_isValid = (_setGradientMethod != null || _refreshMethod != null) &&
_clearCacheMethod != null;
if (!_isValid)
{
Debug.LogWarning($"[GradientReflection] Initialization incomplete. " +
$"SetCurrentGradient: {(_setGradientMethod != null ? "OK" : "Missing")}, " +
$"RefreshGradientData: {(_refreshMethod != null ? "OK" : "Missing")}, " +
$"ClearCache: {(_clearCacheMethod != null ? "OK" : "Missing")}");
}
else
{
// Debug.Log("[GradientReflection] Initialized successfully");
}
}
catch (Exception e)
{
Debug.LogError($"[GradientReflection] Initialization failed: {e}");
_isValid = false;
}
finally
{
_initialized = true;
}
}
}
private static MethodInfo GetStaticMethod(Type type, string methodName, Type[] parameterTypes)
{
if (type == null) return null;
return type.GetMethod(
methodName,
BindingFlags.Public | BindingFlags.Static,
null,
parameterTypes,
null
);
}
private static Action CreateActionDelegate(MethodInfo method)
{
try
{
return (Action)Delegate.CreateDelegate(typeof(Action), method);
}
catch (Exception ex)
{
Debug.LogWarning($"[GradientReflection] Failed to create delegate for {method.Name}: {ex.Message}");
return null;
}
}
private static Action<T> CreateActionDelegate<T>(MethodInfo method)
{
try
{
return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), method);
}
catch (Exception ex)
{
Debug.LogWarning($"[GradientReflection] Failed to create delegate for {method.Name}: {ex.Message}");
return null;
}
}
#endregion
#region
/// <summary>
/// 检查是否所有反射方法都可用
/// </summary>
public static bool IsFullySupported => _isValid;
/// <summary>
/// 获取反射初始化状态
/// </summary>
public static string GetReflectionStatus()
{
if (!_initialized) return "Not Initialized";
return $"GradientPicker: {(_gradientPickerType != null ? "Found" : "Missing")}\n" +
$"- SetCurrentGradient: {(_setGradientMethod != null ? "OK" : "Missing")}\n" +
$"- RefreshGradientData: {(_refreshMethod != null ? "OK" : "Missing")}\n" +
$"GradientPreviewCache: {(_gradientCacheType != null ? "Found" : "Missing")}\n" +
$"- ClearCache: {(_clearCacheMethod != null ? "OK" : "Missing")}";
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ebe6cf7ae30f4357bae940f8ce409628
timeCreated: 1753589587

View File

@@ -0,0 +1,261 @@
using UnityEditor;
using UnityEngine;
using UnityEditor.Experimental.GraphView;
using System;
using NBShader;
namespace NBShaderEditor
{
// #if UNITY_EDITOR
[CustomEditor(typeof(MaterialPropertyAgent))]
public class MaterialPropertyAgentInspector : UnityEditor.Editor
{
private MaterialPropertyAgent agent;
private void OnEnable()
{
agent = (MaterialPropertyAgent)target;
}
private GUIContent materialIndexContent = new GUIContent("材质序号:", "只有模型模式下需要使用,谨慎修改");
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
if (!agent.isGetByComponet)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("customRenderer"),
new GUIContent("指定Renderer"));
}
if (agent.isRendererMode)
{
EditorGUILayout.BeginHorizontal();
int lastIndex = agent.materialIndex;
agent.materialIndex = EditorGUILayout.IntField(materialIndexContent, agent.materialIndex);
if (lastIndex != agent.materialIndex)
{
agent.initMatAndShaderByMaterialIndexChange();
}
EditorGUILayout.LabelField("材质名:" + agent.mat.name + "\t" + "Shader名" + agent.shader.name);
EditorGUILayout.EndHorizontal();
}
DrawPropertyData(ref agent.data0, "Data0", serializedObject.FindProperty("data0"));
DrawPropertyData(ref agent.data1, "Data1", serializedObject.FindProperty("data1"));
DrawPropertyData(ref agent.data2, "Data2", serializedObject.FindProperty("data2"));
DrawPropertyData(ref agent.data3, "Data3", serializedObject.FindProperty("data3"));
DrawPropertyData(ref agent.data4, "Data4", serializedObject.FindProperty("data4"));
DrawPropertyData(ref agent.data5, "Data5", serializedObject.FindProperty("data5"));
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("添加"))
{
agent.addProperteData();
}
if (GUILayout.Button("全部删除"))
{
agent.removeAllProperty();
}
EditorGUILayout.EndHorizontal();
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
PrefabUtility.RecordPrefabInstancePropertyModifications(agent);
if (!agent.isGetByComponet && agent.mat)
{
if (matEditor) DestroyImmediate(matEditor);
matEditor = (MaterialEditor)CreateEditor(agent.mat);
}
}
if (!agent.isGetByComponet && agent.mat)
{
DrawMaterialInspector(matEditor, agent.mat);
}
}
void DrawPropertyData(ref MaterialPropertyAgent.PropertyData data, string dataLabel,
SerializedProperty property)
{
EditorGUI.BeginProperty(EditorGUILayout.GetControlRect(false, 0f), GUIContent.none, property);
if (data.isActive)
{
EditorGUILayout.LabelField(dataLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
float originLabelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 80;
// data.index = EditorGUILayout.Popup("属性名:", data.index, agent.shaderPropNameArr);
// data.index = EditorGUILayout.Popup("属性名:", data.index, agent.shaderPropDescripArr);
// string dataDesript = data.descripName;
if (GUILayout.Button(data.descripName, EditorStyles.popup))
{
int dataIndexInAgent = data.dataIndexInAgent;
StringListSerchProvider provider = ScriptableObject.CreateInstance<StringListSerchProvider>();
provider.Initialize(agent.shaderPropDescripsForSerch, (x) =>
{
if (x != null)
{
AfterShaderPropSerch(dataIndexInAgent, x);
}
});
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)),
provider);
// SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)),
// new StringListSerchProvider(agent.shaderPropDescripsForSerch, (x) =>
// {
// if (x != null)
// {
// AfterShaderPropSerch(dataIndexInAgent,x);
// }
// }));
}
EditorGUILayout.LabelField("属性类型:", data.type.ToString());
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = originLabelWidth;
switch (data.type)
{
case MaterialPropertyAgent.shaderPropertyType.Color:
SerializedProperty colorProp = property.FindPropertyRelative("colorValue");
if (data.descripName.ToLower().Contains("hdr"))
{
colorProp.colorValue = EditorGUILayout.ColorField(new GUIContent(data.propName + " :"),
colorProp.colorValue, true, true, true);
}
else
{
colorProp.colorValue =
EditorGUILayout.ColorField(data.propName + " :", colorProp.colorValue);
}
break;
case MaterialPropertyAgent.shaderPropertyType.Vector:
case MaterialPropertyAgent.shaderPropertyType.TexEnv:
SerializedProperty vecProp = property.FindPropertyRelative("vecValue");
if (data.type == MaterialPropertyAgent.shaderPropertyType.Vector)
{
vecProp.vector4Value =
EditorGUILayout.Vector4Field(data.propName + " :", vecProp.vector4Value);
}
else if (data.type == MaterialPropertyAgent.shaderPropertyType.TexEnv)
{
vecProp.vector4Value =
EditorGUILayout.Vector4Field(data.propName + ":", vecProp.vector4Value);
}
break;
case MaterialPropertyAgent.shaderPropertyType.Float:
case MaterialPropertyAgent.shaderPropertyType.Range:
SerializedProperty floatProp = property.FindPropertyRelative("floatValue");
if (data.type == MaterialPropertyAgent.shaderPropertyType.Float)
{
floatProp.floatValue =
EditorGUILayout.FloatField(data.propName + ":", floatProp.floatValue);
}
else if (data.type == MaterialPropertyAgent.shaderPropertyType.Range)
{
floatProp.floatValue = EditorGUILayout.Slider(data.propName + ":", floatProp.floatValue,
data.rangMin, data.rangMax);
}
break;
}
if (GUILayout.Button("删除", new[] { GUILayout.Width(200) }))
{
data.isActive = false;
}
EditorGUI.indentLevel--;
// EditorGUILayout.Space();
}
EditorGUI.EndProperty();
}
//因为Action不能有Ref。所以有了这个丑陋的HardCode
public void AfterShaderPropSerch(int dataIndexInAgent, string propertyDesrpt)
{
switch (dataIndexInAgent)
{
case 0:
AfterShaderPropSerch(ref agent.data0, propertyDesrpt);
break;
case 1:
AfterShaderPropSerch(ref agent.data1, propertyDesrpt);
break;
case 2:
AfterShaderPropSerch(ref agent.data2, propertyDesrpt);
break;
case 3:
AfterShaderPropSerch(ref agent.data3, propertyDesrpt);
break;
case 4:
AfterShaderPropSerch(ref agent.data4, propertyDesrpt);
break;
case 5:
AfterShaderPropSerch(ref agent.data5, propertyDesrpt);
break;
}
}
public void AfterShaderPropSerch(ref MaterialPropertyAgent.PropertyData data, string propertyDesrpt)
{
int preservedIndex = data.index;
// string propname = data.propName;
data.index = Array.FindIndex(agent.shaderPropDescripArr, x => x.Equals(propertyDesrpt));
// Debug.Log();
if (preservedIndex != data.index) //证明用户进行了更改
{
if (!agent.isCanUsedIndex(data.index))
{
//TODO给一个报错提示
Debug.LogError("材质属性已经存在:" + agent.shader.GetPropertyDescription(data.index));
data.index = agent.getCanUsedIndex();
}
//此处进行内容刷新
data.setValueByPropChange();
}
}
private MaterialEditor matEditor;
void DrawMaterialInspector(MaterialEditor editor, Material mat)
{
if (editor != null && mat != null)
{
// Draw the material's foldout and the material shader field
// Required to call _materialEditor.OnInspectorGUI ();
editor.DrawHeader();
// We need to prevent the user to edit Unity default materials
bool isDefaultMaterial = !AssetDatabase.GetAssetPath(mat).StartsWith("Assets");
using (new EditorGUI.DisabledGroupScope(isDefaultMaterial))
{
// Draw the material properties
// Works only if the foldout of _materialEditor.DrawHeader () is open
editor.OnInspectorGUI();
}
}
}
}
}
// #endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9544bec10c84ecca289bece4a630d65
timeCreated: 1702126025

View File

@@ -0,0 +1,383 @@
using Unity.Collections;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using NBShader;
namespace NBShaderEditor
{
[CustomEditor(typeof(MeshUI))]
[CanEditMultipleObjects]
public class MeshUIInspector : Editor
{
private SerializedProperty _meshProp;
private SerializedProperty _subMeshMaterialsProp;
private SerializedProperty _colorProp;
private SerializedProperty _raycastTargetProp;
private SerializedProperty _maskableProp;
private void OnEnable()
{
_meshProp = serializedObject.FindProperty("mesh");
_subMeshMaterialsProp = serializedObject.FindProperty("subMeshMaterials");
_colorProp = serializedObject.FindProperty("m_Color");
_raycastTargetProp = serializedObject.FindProperty("m_RaycastTarget");
_maskableProp = serializedObject.FindProperty("m_Maskable");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawMeshSection();
EditorGUILayout.Space();
DrawSubMeshMaterials();
EditorGUILayout.Space();
DrawUGUISection();
EditorGUILayout.Space();
DrawMeshInfo();
serializedObject.ApplyModifiedProperties();
}
private void DrawMeshSection()
{
EditorGUILayout.LabelField("Mesh", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(_meshProp, new GUIContent("模型"));
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
SyncSlotsToMesh();
serializedObject.Update();
}
DrawBakeMeshSection();
}
private void DrawSubMeshMaterials()
{
Mesh mesh = _meshProp.objectReferenceValue as Mesh;
int targetSize = mesh != null ? mesh.subMeshCount : _subMeshMaterialsProp.arraySize;
EditorGUILayout.LabelField("Mesh Materials", EditorStyles.boldLabel);
if (mesh != null && _subMeshMaterialsProp.arraySize != targetSize)
{
EditorGUILayout.HelpBox($"当前材质槽位数量为 {_subMeshMaterialsProp.arraySize}Mesh 的 SubMesh 数量为 {targetSize}。更换 Mesh 后会自动同步槽位。", MessageType.Warning);
}
for (int i = 0; i < _subMeshMaterialsProp.arraySize; i++)
{
SerializedProperty element = _subMeshMaterialsProp.GetArrayElementAtIndex(i);
string label = $"Sub Mesh {i}";
if (mesh != null && i < mesh.subMeshCount)
{
label += $" ({mesh.GetTopology(i)})";
}
EditorGUILayout.PropertyField(element, new GUIContent(label));
}
}
private void DrawUGUISection()
{
EditorGUILayout.LabelField("UGUI", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(_colorProp, new GUIContent("颜色"));
EditorGUILayout.PropertyField(_raycastTargetProp, new GUIContent("射线检测"));
EditorGUILayout.PropertyField(_maskableProp, new GUIContent("支持 Mask"));
}
private void DrawMeshInfo()
{
Mesh mesh = _meshProp.objectReferenceValue as Mesh;
EditorGUILayout.LabelField("信息", EditorStyles.boldLabel);
if (mesh == null)
{
EditorGUILayout.HelpBox("未指定 Mesh。请先指定一个包含三角形拓扑的模型。", MessageType.Warning);
return;
}
int subMeshCount = mesh.subMeshCount;
int triangleSubMeshCount = GetTriangleSubMeshCount(mesh);
int vertexCount = mesh.vertexCount;
int indexCount = 0;
for (int i = 0; i < subMeshCount; i++)
{
if (mesh.GetTopology(i) == MeshTopology.Triangles)
{
indexCount += (int)mesh.GetIndexCount(i);
}
}
string renderMode = GetRenderModeDescription(mesh);
string info =
$"可读性: {(mesh.isReadable ? "" : "")}\n" +
$"顶点数: {vertexCount}\n" +
$"SubMesh 数量: {subMeshCount}\n" +
$"三角形 SubMesh 数量: {triangleSubMeshCount}\n" +
$"三角形索引数: {indexCount}\n" +
$"运行时模式: {renderMode}";
EditorGUILayout.HelpBox(info, MessageType.Info);
if (triangleSubMeshCount == 0)
{
EditorGUILayout.HelpBox("当前 Mesh 不包含可渲染的三角形 SubMesh。", MessageType.Error);
}
else if (triangleSubMeshCount != subMeshCount)
{
EditorGUILayout.HelpBox("当前 Mesh 中存在非三角形拓扑的 SubMesh运行时会生成 Mesh并且只渲染三角形 SubMesh。", MessageType.Warning);
}
}
private string GetRenderModeDescription(Mesh mesh)
{
if (!mesh.isReadable)
{
return "当前 Mesh 不可读,无法渲染";
}
if (GetTriangleSubMeshCount(mesh) != mesh.subMeshCount)
{
return "将生成运行时 Mesh原因存在非三角形 SubMesh";
}
Color color = _colorProp.colorValue;
if (color != Color.white)
{
return "将生成运行时 Mesh原因需要应用 UGUI 颜色)";
}
return "将直接使用原始 Mesh";
}
private void DrawBakeMeshSection()
{
Mesh mesh = _meshProp.objectReferenceValue as Mesh;
if (mesh == null || mesh.isReadable)
{
return;
}
EditorGUILayout.HelpBox("当前 Mesh 没有开启 Read/WriteMeshUI 无法直接通过 CanvasRenderer 渲染。请先烘焙出一个可读的 Mesh。", MessageType.Warning);
if (GUILayout.Button("烘焙只读Mesh"))
{
BakeMeshForTargets(mesh);
}
}
private int GetTriangleSubMeshCount(Mesh mesh)
{
int count = 0;
for (int i = 0; i < mesh.subMeshCount; i++)
{
if (mesh.GetTopology(i) == MeshTopology.Triangles)
{
count++;
}
}
return count;
}
private void BakeMeshForTargets(Mesh sourceMesh)
{
string defaultDirectory = GetDefaultSaveDirectory(sourceMesh);
string defaultName = $"{sourceMesh.name}_MeshUI";
string assetPath = EditorUtility.SaveFilePanelInProject(
"烘焙 MeshUI Mesh",
defaultName,
"asset",
"请选择烘焙后 Mesh 的保存位置。",
defaultDirectory);
if (string.IsNullOrEmpty(assetPath))
{
return;
}
Mesh bakedMesh = CreateReadableMeshCopy(sourceMesh, System.IO.Path.GetFileNameWithoutExtension(assetPath));
if (bakedMesh == null)
{
return;
}
string uniqueAssetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
AssetDatabase.CreateAsset(bakedMesh, uniqueAssetPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Mesh bakedAsset = AssetDatabase.LoadAssetAtPath<Mesh>(uniqueAssetPath);
if (bakedAsset == null)
{
Debug.LogError($"无法加载已烘焙的 Mesh 资源: '{uniqueAssetPath}'");
return;
}
Undo.RecordObjects(targets, "烘焙 MeshUI Mesh");
foreach (Object editorTarget in targets)
{
MeshUI meshUI = editorTarget as MeshUI;
if (meshUI == null)
{
continue;
}
meshUI.Mesh = bakedAsset;
EditorUtility.SetDirty(meshUI);
PrefabUtility.RecordPrefabInstancePropertyModifications(meshUI);
}
serializedObject.Update();
SyncSlotsToMesh();
}
private string GetDefaultSaveDirectory(Mesh sourceMesh)
{
string sourcePath = AssetDatabase.GetAssetPath(sourceMesh);
if (string.IsNullOrEmpty(sourcePath))
{
return "Assets";
}
string directory = System.IO.Path.GetDirectoryName(sourcePath);
if (string.IsNullOrEmpty(directory))
{
return "Assets";
}
return directory.Replace('\\', '/');
}
private static Mesh CreateReadableMeshCopy(Mesh sourceMesh, string newMeshName)
{
using (Mesh.MeshDataArray meshDataArray = MeshUtility.AcquireReadOnlyMeshData(sourceMesh))
{
if (meshDataArray.Length == 0)
{
Debug.LogError($"无法读取 Mesh 数据: {sourceMesh.name}");
return null;
}
Mesh.MeshData meshData = meshDataArray[0];
Mesh bakedMesh = new Mesh
{
name = newMeshName,
indexFormat = meshData.indexFormat
};
int vertexCount = meshData.vertexCount;
if (vertexCount <= 0)
{
Debug.LogError($"Mesh '{sourceMesh.name}' 不包含任何顶点。");
Object.DestroyImmediate(bakedMesh);
return null;
}
using (NativeArray<Vector3> vertices = new NativeArray<Vector3>(vertexCount, Allocator.Temp))
{
meshData.GetVertices(vertices);
bakedMesh.SetVertices(vertices);
}
if (meshData.HasVertexAttribute(VertexAttribute.Normal))
{
using (NativeArray<Vector3> normals = new NativeArray<Vector3>(vertexCount, Allocator.Temp))
{
meshData.GetNormals(normals);
bakedMesh.SetNormals(normals);
}
}
if (meshData.HasVertexAttribute(VertexAttribute.Tangent))
{
using (NativeArray<Vector4> tangents = new NativeArray<Vector4>(vertexCount, Allocator.Temp))
{
meshData.GetTangents(tangents);
bakedMesh.SetTangents(tangents);
}
}
if (meshData.HasVertexAttribute(VertexAttribute.TexCoord0))
{
using (NativeArray<Vector2> uv0 = new NativeArray<Vector2>(vertexCount, Allocator.Temp))
{
meshData.GetUVs(0, uv0);
bakedMesh.SetUVs(0, uv0);
}
}
int subMeshCount = sourceMesh.subMeshCount;
bakedMesh.subMeshCount = subMeshCount;
bool hasTriangleSubMesh = false;
for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
{
SubMeshDescriptor subMesh = meshData.GetSubMesh(subMeshIndex);
if (subMesh.topology == MeshTopology.Triangles && subMesh.indexCount > 0)
{
hasTriangleSubMesh = true;
if (meshData.indexFormat == IndexFormat.UInt16)
{
using (NativeArray<ushort> indices = new NativeArray<ushort>((int)subMesh.indexCount, Allocator.Temp))
{
meshData.GetIndices(indices, subMeshIndex, true);
bakedMesh.SetIndices(indices, MeshTopology.Triangles, subMeshIndex, false);
}
}
else
{
using (NativeArray<int> indices = new NativeArray<int>((int)subMesh.indexCount, Allocator.Temp))
{
meshData.GetIndices(indices, subMeshIndex, true);
bakedMesh.SetIndices(indices, MeshTopology.Triangles, subMeshIndex, false);
}
}
}
else
{
bakedMesh.SetIndices(System.Array.Empty<int>(), MeshTopology.Triangles, subMeshIndex, false);
}
}
if (!hasTriangleSubMesh)
{
Debug.LogError($"Mesh '{sourceMesh.name}' 不包含可烘焙的三角形 SubMesh。");
Object.DestroyImmediate(bakedMesh);
return null;
}
bakedMesh.RecalculateBounds();
return bakedMesh;
}
}
private void SyncSlotsToMesh()
{
foreach (Object editorTarget in targets)
{
MeshUI meshUI = editorTarget as MeshUI;
if (meshUI == null)
{
continue;
}
SerializedObject targetObject = new SerializedObject(meshUI);
SerializedProperty meshProp = targetObject.FindProperty("mesh");
SerializedProperty materialsProp = targetObject.FindProperty("subMeshMaterials");
Mesh mesh = meshProp.objectReferenceValue as Mesh;
int targetSize = mesh != null ? mesh.subMeshCount : 0;
materialsProp.arraySize = Mathf.Max(0, targetSize);
targetObject.ApplyModifiedProperties();
EditorUtility.SetDirty(meshUI);
PrefabUtility.RecordPrefabInstancePropertyModifications(meshUI);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 10b17bb5e66542688383e3f64f44717c
timeCreated: 1774473600

View File

@@ -0,0 +1,31 @@
using UnityEditor;
using UnityEngine.Rendering;
namespace NBShaderEditor
{
public static class ShaderGUIUnityCompat
{
public static ShaderPropertyType GetPropertyType(MaterialProperty property)
{
#if UNITY_6000_1_OR_NEWER
return property.propertyType;
#else
#pragma warning disable 0618
return (ShaderPropertyType)property.type;
#pragma warning restore 0618
#endif
}
public static bool HasHdrFlag(MaterialProperty property)
{
#if UNITY_6000_1_OR_NEWER
return (property.propertyFlags & ShaderPropertyFlags.HDR) != 0;
#else
#pragma warning disable 0618
return (((ShaderPropertyFlags)property.flags) & ShaderPropertyFlags.HDR) != 0;
#pragma warning restore 0618
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab0b838b256742c994e033e50aca5be0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f6342c05c0b5437abd5d7332652ef16b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,320 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public enum NBFXLanguageMode
{
FollowEditor,
Chinese,
English
}
[FilePath("ProjectSettings/NB_FXSettings.asset", FilePathAttribute.Location.ProjectFolder)]
public sealed class NBFXProjectSettings : ScriptableSingleton<NBFXProjectSettings>
{
public const string SettingsPath = "Project/NB_FX";
private const string LocalizationTableName = "NBShader";
private const string LocalizationCsvAssetPath = "Packages/com.xuanxuan.nb.fx/NBShaders2/Editor/Localization/NBShaderInspectorLocalization.csv";
private const string FoldoutSessionPrefix = "NBFXProjectSettings.Foldout.";
private sealed class SettingsSection
{
public string id;
public Func<GUIContent> titleProvider;
public Action<string> guiHandler;
public List<string> keywords;
public int order;
public GUIContent titleContent;
public string titleLanguage;
}
private static readonly string[] LanguageOptionsChineseFallback =
{
"跟随编辑器",
"中文",
"英文"
};
private static readonly string[] LanguageOptionsEnglishFallback =
{
"Follow Editor",
"Chinese",
"English"
};
private static readonly List<SettingsSection> s_Sections = new List<SettingsSection>();
private static readonly Dictionary<string, GUIContent> s_ProjectSettingsContentCache =
new Dictionary<string, GUIContent>(StringComparer.Ordinal);
private static GUIStyle s_FoldoutHeaderStyle;
private static string s_ProjectSettingsEditorLanguage;
private static string[] s_ProjectSettingsLanguageOptions;
[SerializeField]
private NBFXLanguageMode languageMode = NBFXLanguageMode.FollowEditor;
public static NBFXLanguageMode LanguageMode => instance.languageMode;
public static void SetLanguageMode(NBFXLanguageMode mode)
{
if (instance.languageMode == mode)
{
return;
}
instance.languageMode = mode;
instance.Save(true);
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
}
public static void RegisterSettingsSection(
string id,
Func<GUIContent> titleProvider,
Action<string> guiHandler,
IEnumerable<string> keywords = null,
int order = 0)
{
if (string.IsNullOrEmpty(id) || guiHandler == null)
{
return;
}
var section = new SettingsSection
{
id = id,
titleProvider = titleProvider,
guiHandler = guiHandler,
keywords = keywords == null ? new List<string>() : new List<string>(keywords),
order = order
};
for (int i = 0; i < s_Sections.Count; i++)
{
if (!string.Equals(s_Sections[i].id, id, StringComparison.Ordinal))
{
continue;
}
s_Sections[i] = section;
SortSections();
return;
}
s_Sections.Add(section);
SortSections();
}
[SettingsProvider]
public static SettingsProvider CreateSettingsProvider()
{
var provider = new SettingsProvider(SettingsPath, SettingsScope.Project)
{
label = "NB_FX",
guiHandler = DrawSettingsGUI,
keywords = BuildKeywords()
};
return provider;
}
private static void DrawSettingsGUI(string searchContext)
{
EnsureProjectSettingsContentCacheForEditorLanguage();
if (DrawSectionFoldout(
"General",
ProjectSettingsContent("projectSettings.generalSection", "基础设置", "General Settings")))
{
EditorGUI.BeginChangeCheck();
var selectedMode = (NBFXLanguageMode)EditorGUILayout.Popup(
ProjectSettingsContent("projectSettings.defaultLanguage", "默认语言", "Default Language"),
(int)LanguageMode,
ProjectSettingsLanguageOptions());
if (EditorGUI.EndChangeCheck())
{
SetLanguageMode(selectedMode);
}
}
DrawRegisteredSections(searchContext);
}
private static void DrawRegisteredSections(string searchContext)
{
for (int i = 0; i < s_Sections.Count; i++)
{
SettingsSection section = s_Sections[i];
EditorGUILayout.Space(10f);
if (DrawSectionFoldout(section.id, GetSectionTitleContent(section)))
{
section.guiHandler(searchContext);
}
}
}
private static bool DrawSectionFoldout(string id, GUIContent content)
{
string sessionKey = FoldoutSessionPrefix + id;
bool expanded = SessionState.GetBool(sessionKey, true);
EditorGUI.BeginChangeCheck();
bool nextExpanded = EditorGUILayout.Foldout(expanded, content, true, FoldoutHeaderStyle);
if (EditorGUI.EndChangeCheck())
{
SessionState.SetBool(sessionKey, nextExpanded);
}
return nextExpanded;
}
private static GUIStyle FoldoutHeaderStyle
{
get
{
if (s_FoldoutHeaderStyle == null)
{
s_FoldoutHeaderStyle = new GUIStyle(EditorStyles.foldout)
{
fontStyle = FontStyle.Bold
};
}
return s_FoldoutHeaderStyle;
}
}
private static GUIContent ProjectSettingsContent(string key, string chineseFallback, string englishFallback)
{
EnsureProjectSettingsContentCacheForEditorLanguage();
GUIContent content;
if (s_ProjectSettingsContentCache.TryGetValue(key, out content))
return content;
var text = ProjectSettingsText(key, chineseFallback, englishFallback, s_ProjectSettingsEditorLanguage);
content = new GUIContent(text, text);
s_ProjectSettingsContentCache.Add(key, content);
return content;
}
private static string ProjectSettingsText(
string key,
string chineseFallback,
string englishFallback,
string editorLanguage)
{
string fallback = IsEnglishLanguage(editorLanguage) ? englishFallback : chineseFallback;
return ShaderGUILocalization.GetInspectorText(LocalizationTableName, key, fallback, editorLanguage);
}
private static string[] ProjectSettingsLanguageOptions()
{
EnsureProjectSettingsContentCacheForEditorLanguage();
if (s_ProjectSettingsLanguageOptions != null)
return s_ProjectSettingsLanguageOptions;
string[] fallback = IsEnglishLanguage(s_ProjectSettingsEditorLanguage)
? LanguageOptionsEnglishFallback
: LanguageOptionsChineseFallback;
s_ProjectSettingsLanguageOptions = ShaderGUILocalization.GetInspectorOptions(
LocalizationTableName,
"projectSettings.languageMode",
fallback,
s_ProjectSettingsEditorLanguage);
return s_ProjectSettingsLanguageOptions;
}
private static GUIContent GetSectionTitleContent(SettingsSection section)
{
if (section.titleProvider != null)
{
string language = ShaderGUILocalization.GetCurrentLanguage(LocalizationTableName);
if (section.titleContent != null &&
string.Equals(section.titleLanguage, language, StringComparison.Ordinal))
{
return section.titleContent;
}
section.titleLanguage = language;
section.titleContent = section.titleProvider();
return section.titleContent;
}
GUIContent content;
if (s_ProjectSettingsContentCache.TryGetValue(section.id, out content))
return content;
content = new GUIContent(section.id, section.id);
s_ProjectSettingsContentCache.Add(section.id, content);
return content;
}
private static void EnsureProjectSettingsContentCacheForEditorLanguage()
{
RegisterLocalization();
string editorLanguage = ShaderGUILocalization.GetEditorLanguageName();
if (string.Equals(s_ProjectSettingsEditorLanguage, editorLanguage, StringComparison.Ordinal))
return;
s_ProjectSettingsEditorLanguage = editorLanguage;
s_ProjectSettingsContentCache.Clear();
s_ProjectSettingsLanguageOptions = null;
}
private static bool IsEnglishLanguage(string language)
{
return string.Equals(language, ShaderGUILocalization.EnglishLanguage, StringComparison.OrdinalIgnoreCase);
}
private static void RegisterLocalization()
{
ShaderGUILocalization.RegisterCsv(LocalizationTableName, LocalizationCsvAssetPath);
}
private static HashSet<string> BuildKeywords()
{
var keywords = new HashSet<string>
{
"NB_FX",
"Language",
"语言",
"默认语言",
"Default Language",
"General Settings",
"基础设置",
"NBShader"
};
for (int i = 0; i < s_Sections.Count; i++)
{
List<string> sectionKeywords = s_Sections[i].keywords;
if (sectionKeywords == null)
{
continue;
}
for (int keywordIndex = 0; keywordIndex < sectionKeywords.Count; keywordIndex++)
{
keywords.Add(sectionKeywords[keywordIndex]);
}
}
return keywords;
}
private static void SortSections()
{
s_Sections.Sort((a, b) =>
{
int orderCompare = a.order.CompareTo(b.order);
if (orderCompare != 0)
{
return orderCompare;
}
return string.CompareOrdinal(a.id, b.id);
});
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5f19e24bcae4dcc8e06c4550b9b1029
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9279ba81938dc1548b9e9d8297bdcd57
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 947243301cab4804d8df47a42b220c4e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class BlockItem : ShaderGUIItem
{
protected readonly ShaderGUIRootItem SharedRootItem;
private readonly ShaderGUIFoldOutHelper _foldOutHelper;
private readonly Func<GUIContent> _contentProvider;
protected virtual GUIStyle TitleStyle => EditorStyles.label;
protected virtual bool DrawSeparatorLine => false;
public string FoldOutPropertyName { get; }
public BlockItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string foldOutPropertyName,
Func<GUIContent> contentProvider) : base(rootItem, parentItem)
{
SharedRootItem = rootItem;
FoldOutPropertyName = foldOutPropertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
GuiContent = _contentProvider();
_foldOutHelper = new ShaderGUIFoldOutHelper(rootItem, foldOutPropertyName);
}
public override void OnGUI()
{
DrawLeadingSpace();
GetRect();
_foldOutHelper.DrawFoldOut(LabelRect);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent, TitleStyle);
}
DrawResetButton();
EditorGUI.indentLevel++;
if (_foldOutHelper.BeginFadeGroup())
{
DrawBlock();
}
_foldOutHelper.EndFadedGroup();
EditorGUI.indentLevel--;
if (DrawSeparatorLine)
{
Rect rect = ApplyGlobalRectCompensation(LayoutRect(1));
EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f));
}
}
protected virtual void DrawLeadingSpace()
{
}
public override void DrawBlock()
{
for (int i = 0; i < ChildrenItemList.Count; i++)
{
ChildrenItemList[i].OnGUI();
}
}
}
public class BigBlockItem : BlockItem
{
protected override GUIStyle TitleStyle => EditorStyles.boldLabel;
protected override bool DrawSeparatorLine => true;
public BigBlockItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string foldOutPropertyName,
Func<GUIContent> contentProvider) : base(rootItem, parentItem, foldOutPropertyName, contentProvider)
{
}
protected override void DrawLeadingSpace()
{
LayoutSpace();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d07eceb7cee2d8b46ab0f6f62c4f82e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,190 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ColorItem : ShaderGUIItem
{
private const float ColorFieldLeftInset = EditorGUIIndentWidth;
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public ColorItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect(false);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
Color color = PropertyInfo.Property.colorValue;
bool hdr = ShaderGUIUnityCompat.HasHdrFlag(PropertyInfo.Property);
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Rect colorRect = GetLabeledColorFieldRect(ControlRect);
bool animatedScope = BeginAnimatedPropertyBackground(colorRect, PropertyInfo.Property);
using (new EditorGUIIndentLevelScope(0))
{
color = EditorGUI.ColorField(colorRect, GUIContent.none, color, true, true, hdr);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
PropertyInfo.Property.colorValue = color;
OnEndChange();
}
}
DrawResetButton();
DrawBlock();
}
internal static Rect GetNoLabelColorFieldRect(Rect rect)
{
rect.x += ColorFieldLeftInset;
rect.width = Mathf.Max(0f, rect.width - ColorFieldLeftInset);
return rect;
}
internal static Rect GetLabeledColorFieldRect(Rect rect)
{
float leftPadding = EditorStyles.colorField.padding.left + ColorFieldLeftInset +1f;
rect.x -= leftPadding;
rect.width += leftPadding;
return rect;
}
}
public class ColorLineItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly bool _showLabel;
private readonly Func<bool> _isVisible;
public ColorLineItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
bool showLabel,
Func<GUIContent> contentProvider = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_showLabel = showLabel;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _showLabel ? _contentProvider() : GUIContent.none;
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
Draw(ApplyGlobalRectCompensation(LayoutRect()));
DrawBlock();
}
public void Draw(Rect rect)
{
BaseRect = rect;
if (_showLabel)
{
SplitLineRect(rect, out LabelRect, out ControlRect, out ResetRect, false);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
}
else
{
LabelRect = new Rect(rect.x, rect.y, 0f, rect.height);
SplitControlAndResetRect(rect, out ControlRect, out ResetRect, false);
}
MaterialProperty property = PropertyInfo.Property;
Color color = property.colorValue;
bool hdr = ShaderGUIUnityCompat.HasHdrFlag(property);
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Rect colorRect = _showLabel
? ColorItem.GetLabeledColorFieldRect(ControlRect)
: ColorItem.GetNoLabelColorFieldRect(ControlRect);
bool animatedScope = BeginAnimatedPropertyBackground(colorRect, property);
using (new EditorGUIIndentLevelScope(0))
{
color = EditorGUI.ColorField(colorRect, GUIContent.none, color, true, true, hdr);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
property.colorValue = color;
OnEndChange();
}
}
DrawResetButton();
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = true;
if (PropertyInfo != null)
{
Vector4 defaultColor = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
isDefaultValue = !PropertyInfo.Property.hasMixedValue &&
Approximately(PropertyInfo.Property.colorValue, defaultColor);
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 31b44f82d6184d46b683b144f9b5f517
timeCreated: 1777032000

View File

@@ -0,0 +1,533 @@
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
namespace NBShaderEditor
{
public class GradientItem : ShaderGUIItem
{
private static bool s_UndoRedoRegistered;
private static int s_UndoRedoVersion;
private readonly Func<GUIContent> _contentProvider;
private readonly string[] _colorPropertyNames;
private readonly string[] _alphaPropertyNames;
private readonly int _maxCount;
private readonly bool _hdr;
private readonly ColorSpace _colorSpace;
private readonly Func<bool> _isVisible;
private ShaderPropertyInfo[] _colorPropertyInfos;
private ShaderPropertyInfo[] _alphaPropertyInfos;
private readonly Gradient _gradient = new Gradient();
private GradientColorKey[] _colorKeys = Array.Empty<GradientColorKey>();
private GradientAlphaKey[] _alphaKeys = Array.Empty<GradientAlphaKey>();
private bool _gradientCacheDirty = true;
private int _lastUndoRedoVersion = -1;
public GradientItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string countPropertyName,
int maxCount,
string[] colorPropertyNames,
string[] alphaPropertyNames,
Func<GUIContent> contentProvider,
bool hdr = false,
ColorSpace colorSpace = ColorSpace.Gamma,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = countPropertyName;
_maxCount = Mathf.Max(2, maxCount);
_colorPropertyNames = colorPropertyNames ?? Array.Empty<string>();
_alphaPropertyNames = alphaPropertyNames ?? Array.Empty<string>();
_contentProvider = contentProvider ?? (() => GUIContent.none);
_hdr = hdr;
_colorSpace = colorSpace;
_isVisible = isVisible;
GuiContent = _contentProvider();
EnsureUndoRedoRegistered();
CacheProperties();
InitTriggerByChild();
}
private static void EnsureUndoRedoRegistered()
{
if (s_UndoRedoRegistered)
{
return;
}
Undo.undoRedoPerformed += OnUndoRedoPerformed;
s_UndoRedoRegistered = true;
}
private static void OnUndoRedoPerformed()
{
s_UndoRedoVersion++;
}
private void CacheProperties()
{
_colorPropertyInfos = new ShaderPropertyInfo[_colorPropertyNames.Length];
for (int i = 0; i < _colorPropertyNames.Length; i++)
{
if (RootItem.PropertyInfoDic.TryGetValue(_colorPropertyNames[i], out ShaderPropertyInfo info))
{
_colorPropertyInfos[i] = info;
}
}
_alphaPropertyInfos = new ShaderPropertyInfo[_alphaPropertyNames.Length];
for (int i = 0; i < _alphaPropertyNames.Length; i++)
{
if (RootItem.PropertyInfoDic.TryGetValue(_alphaPropertyNames[i], out ShaderPropertyInfo info))
{
_alphaPropertyInfos[i] = info;
}
}
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
Gradient gradient = GetCachedGradient();
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = GradientPropertyHasMixedValue();
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, PropertyInfo.Property);
using (new EditorGUIIndentLevelScope(0))
{
gradient = EditorGUI.GradientField(ControlRect, GUIContent.none, gradient, _hdr, _colorSpace);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
WriteGradient(gradient);
MarkGradientCacheDirty();
OnEndChange();
}
}
DrawResetButton();
}
private Gradient GetCachedGradient()
{
if (_lastUndoRedoVersion != s_UndoRedoVersion)
{
_lastUndoRedoVersion = s_UndoRedoVersion;
MarkGradientCacheDirty();
}
if (_gradientCacheDirty)
{
ReadGradient();
_gradientCacheDirty = false;
GradientReflectionHelper.RefreshGradientData();
}
return _gradient;
}
private void MarkGradientCacheDirty()
{
_gradientCacheDirty = true;
}
private void ReadGradient()
{
bool hasColorProperties = _colorPropertyInfos.Length > 0;
bool hasAlphaProperties = _alphaPropertyInfos.Length > 0;
bool isBlackAndWhiteGradient = !hasColorProperties && hasAlphaProperties;
GetGradientKeyCounts(out int colorKeyCount, out int alphaKeyCount);
if (_colorKeys.Length != colorKeyCount)
{
_colorKeys = new GradientColorKey[colorKeyCount];
}
if (_alphaKeys.Length != alphaKeyCount)
{
_alphaKeys = new GradientAlphaKey[alphaKeyCount];
}
for (int i = 0; i < colorKeyCount; i++)
{
Color color = Color.white;
float colorTime = colorKeyCount == 1 ? 0f : i / (float)(colorKeyCount - 1);
if (isBlackAndWhiteGradient)
{
TryReadBlackAndWhiteKey(i, ref color, ref colorTime);
}
else if (i < _colorPropertyInfos.Length && _colorPropertyInfos[i] != null)
{
Color packed = _colorPropertyInfos[i].Property.colorValue;
color = packed;
colorTime = packed.a;
}
_colorKeys[i] = new GradientColorKey(color, colorTime);
}
for (int i = 0; i < alphaKeyCount; i++)
{
float alpha = 1f;
float alphaTime = alphaKeyCount == 1 ? 0f : i / (float)(alphaKeyCount - 1);
if (!isBlackAndWhiteGradient && hasAlphaProperties)
{
TryReadAlphaKey(i, ref alpha, ref alphaTime);
}
_alphaKeys[i] = new GradientAlphaKey(alpha, alphaTime);
}
_gradient.SetKeys(_colorKeys, _alphaKeys);
}
private void GetGradientKeyCounts(out int colorKeyCount, out int alphaKeyCount)
{
int countValue = PropertyInfo.Property.intValue;
bool hasColorProperties = _colorPropertyInfos.Length > 0;
bool hasAlphaProperties = _alphaPropertyInfos.Length > 0;
if (hasColorProperties && hasAlphaProperties)
{
colorKeyCount = countValue & 0xFFFF;
alphaKeyCount = countValue >> 16;
}
else
{
colorKeyCount = countValue;
alphaKeyCount = 2;
}
}
private void TryReadBlackAndWhiteKey(int index, ref Color color, ref float time)
{
int vectorIndex = index / 2;
int componentIndex = index % 2;
if (vectorIndex >= _alphaPropertyInfos.Length || _alphaPropertyInfos[vectorIndex] == null)
{
return;
}
Vector4 packed = _alphaPropertyInfos[vectorIndex].Property.vectorValue;
float value = componentIndex == 0 ? packed.x : packed.z;
time = componentIndex == 0 ? packed.y : packed.w;
color = new Color(value, value, value, 1f);
}
private void TryReadAlphaKey(int index, ref float alpha, ref float time)
{
int vectorIndex = index / 2;
int componentIndex = index % 2;
if (vectorIndex >= _alphaPropertyInfos.Length || _alphaPropertyInfos[vectorIndex] == null)
{
return;
}
Vector4 packed = _alphaPropertyInfos[vectorIndex].Property.vectorValue;
if (componentIndex == 0)
{
alpha = packed.x;
time = packed.y;
}
else
{
alpha = packed.z;
time = packed.w;
}
}
private void WriteGradient(Gradient gradient)
{
bool hasColorProperties = _colorPropertyInfos.Length > 0;
bool hasAlphaProperties = _alphaPropertyInfos.Length > 0;
bool isBlackAndWhiteGradient = !hasColorProperties && hasAlphaProperties;
int countPropertyValue = PropertyInfo.Property.intValue;
if (isBlackAndWhiteGradient)
{
int finalColorKeyCount = gradient.colorKeys.Length;
if (finalColorKeyCount <= _maxCount)
{
WriteBlackAndWhiteGradient(gradient, finalColorKeyCount);
PropertyInfo.Property.intValue = finalColorKeyCount;
}
return;
}
if (hasColorProperties)
{
int finalColorKeyCount = gradient.colorKeys.Length;
if (finalColorKeyCount <= _maxCount)
{
WriteColorKeys(gradient, finalColorKeyCount);
countPropertyValue &= 0xFFFF << 16;
countPropertyValue |= finalColorKeyCount;
}
}
if (!isBlackAndWhiteGradient && hasAlphaProperties)
{
int finalAlphaKeyCount = gradient.alphaKeys.Length;
if (finalAlphaKeyCount <= _maxCount)
{
WriteAlphaKeys(gradient, finalAlphaKeyCount);
countPropertyValue &= 0xFFFF;
countPropertyValue |= finalAlphaKeyCount << 16;
}
}
PropertyInfo.Property.intValue = countPropertyValue;
}
private void WriteColorKeys(Gradient gradient, int colorKeyCount)
{
for (int i = 0; i < colorKeyCount && i < _colorPropertyInfos.Length; i++)
{
if (_colorPropertyInfos[i] == null)
{
continue;
}
GradientColorKey key = gradient.colorKeys[i];
_colorPropertyInfos[i].Property.colorValue = new Color(key.color.r, key.color.g, key.color.b, key.time);
}
}
private void WriteBlackAndWhiteGradient(Gradient gradient, int colorKeyCount)
{
int vectorCount = Mathf.CeilToInt(colorKeyCount / 2f);
for (int i = 0; i < vectorCount && i < _alphaPropertyInfos.Length; i++)
{
if (_alphaPropertyInfos[i] == null)
{
continue;
}
Vector4 value = Vector4.zero;
GradientColorKey key0 = gradient.colorKeys[i * 2];
value.x = key0.color.r;
value.y = key0.time;
if (i * 2 + 1 < colorKeyCount)
{
GradientColorKey key1 = gradient.colorKeys[i * 2 + 1];
value.z = key1.color.r;
value.w = key1.time;
}
_alphaPropertyInfos[i].Property.vectorValue = value;
}
}
private void WriteAlphaKeys(Gradient gradient, int alphaKeyCount)
{
int vectorCount = Mathf.CeilToInt(alphaKeyCount / 2f);
for (int i = 0; i < vectorCount && i < _alphaPropertyInfos.Length; i++)
{
if (_alphaPropertyInfos[i] == null)
{
continue;
}
Vector4 value = Vector4.zero;
GradientAlphaKey key0 = gradient.alphaKeys[i * 2];
value.x = key0.alpha;
value.y = key0.time;
if (i * 2 + 1 < alphaKeyCount)
{
GradientAlphaKey key1 = gradient.alphaKeys[i * 2 + 1];
value.z = key1.alpha;
value.w = key1.time;
}
_alphaPropertyInfos[i].Property.vectorValue = value;
}
}
private bool GradientPropertyHasMixedValue()
{
if (PropertyInfo == null || PropertyInfo.Property == null)
{
return false;
}
GetGradientKeyCounts(out int colorKeyCount, out int alphaKeyCount);
bool hasMixedValue = PropertyInfo.Property.hasMixedValue;
for (int i = 0; i < colorKeyCount && i < _colorPropertyInfos.Length; i++)
{
hasMixedValue |= _colorPropertyInfos[i]?.Property.hasMixedValue == true;
}
int alphaPropertyCount = Mathf.CeilToInt(alphaKeyCount / 2f);
for (int i = 0; i < alphaPropertyCount && i < _alphaPropertyInfos.Length; i++)
{
hasMixedValue |= _alphaPropertyInfos[i]?.Property.hasMixedValue == true;
}
return hasMixedValue;
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = IsCountDefault();
for (int i = 0; i < _colorPropertyInfos.Length; i++)
{
isDefaultValue &= IsDefault(_colorPropertyInfos[i]);
}
for (int i = 0; i < _alphaPropertyInfos.Length; i++)
{
isDefaultValue &= IsDefault(_alphaPropertyInfos[i]);
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
ResetCountProperty();
for (int i = 0; i < _colorPropertyInfos.Length; i++)
{
ResetProperty(_colorPropertyInfos[i]);
}
for (int i = 0; i < _alphaPropertyInfos.Length; i++)
{
ResetProperty(_alphaPropertyInfos[i]);
}
PropertyIsDefaultValue = true;
HasModified = false;
MarkGradientCacheDirty();
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
private bool IsCountDefault()
{
if (PropertyInfo == null || PropertyInfo.Property == null || PropertyInfo.Property.hasMixedValue)
{
return false;
}
return PropertyInfo.Property.intValue == GetDefaultInt(PropertyInfo);
}
private void ResetCountProperty()
{
if (PropertyInfo?.Property == null)
{
return;
}
PropertyInfo.Property.intValue = GetDefaultInt(PropertyInfo);
}
private bool IsDefault(ShaderPropertyInfo info)
{
if (info == null || info.Property == null || info.Property.hasMixedValue)
{
return false;
}
switch (ShaderGUIUnityCompat.GetPropertyType(info.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
return Mathf.Approximately(info.Property.floatValue, RootItem.Shader.GetPropertyDefaultFloatValue(info.Index));
case ShaderPropertyType.Int:
return info.Property.intValue == GetDefaultInt(info);
case ShaderPropertyType.Color:
return Approximately(info.Property.colorValue, RootItem.Shader.GetPropertyDefaultVectorValue(info.Index));
case ShaderPropertyType.Vector:
return Approximately(info.Property.vectorValue, RootItem.Shader.GetPropertyDefaultVectorValue(info.Index));
default:
return Mathf.Approximately(info.Property.floatValue, GetDefaultScalar(info));
}
}
private void ResetProperty(ShaderPropertyInfo info)
{
if (info == null || info.Property == null)
{
return;
}
switch (ShaderGUIUnityCompat.GetPropertyType(info.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
info.Property.floatValue = RootItem.Shader.GetPropertyDefaultFloatValue(info.Index);
break;
case ShaderPropertyType.Int:
info.Property.intValue = GetDefaultInt(info);
break;
case ShaderPropertyType.Color:
info.Property.colorValue = RootItem.Shader.GetPropertyDefaultVectorValue(info.Index);
break;
case ShaderPropertyType.Vector:
info.Property.vectorValue = RootItem.Shader.GetPropertyDefaultVectorValue(info.Index);
break;
default:
info.Property.floatValue = GetDefaultScalar(info);
break;
}
}
private float GetDefaultScalar(ShaderPropertyInfo info)
{
try
{
return RootItem.Shader.GetPropertyDefaultFloatValue(info.Index);
}
catch (ArgumentException)
{
return 2f;
}
}
private int GetDefaultInt(ShaderPropertyInfo info)
{
try
{
return GetShaderPropertyDefaultIntValue(RootItem.Shader, info.Index);
}
catch (ArgumentException)
{
return Mathf.RoundToInt(GetDefaultScalar(info));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17810b41ec32488faa9fbceaaf8b8fd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
using UnityEditor;
namespace NBShaderEditor
{
public class HelpBoxItem : ShaderGUIItem
{
private readonly MessageType _messageType;
private readonly Func<string> _messageProvider;
private readonly Func<bool> _isVisible;
public HelpBoxItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
Func<string> messageProvider,
MessageType messageType = MessageType.Info,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
_messageProvider = messageProvider ?? (() => string.Empty);
_messageType = messageType;
_isVisible = isVisible;
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
using (ParentControlDisabledScope())
{
DrawLayoutHelpBox(_messageProvider(), _messageType);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6c516e0fb3014df8b96bce4e0925cb45
timeCreated: 1777032000

View File

@@ -0,0 +1,51 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class RenderQueueItem : ShaderGUIFloatItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Action _onQueueChanged;
private readonly Func<bool> _isVisible;
public RenderQueueItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Action onQueueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_onQueueChanged = onQueueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void OnEndChange()
{
base.OnEndChange();
_onQueueChanged?.Invoke();
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
_onQueueChanged?.Invoke();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5caca15eb8794602ae6029d798cb5f1f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class SectionLabelItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public SectionLabelItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
LayoutSpace();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(ApplyGlobalRectCompensation(LayoutRect()), GuiContent, EditorStyles.boldLabel);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75c41d424c5e4203992222069d060bc6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,394 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ShaderGUIFloatItem:ShaderGUIItem
{
private readonly Func<bool> _isVisible;
public ShaderGUIFloatItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem, Func<bool> isVisible = null) :
base(rootItem, parentItem: parentItem)
{
_isVisible = isVisible;
base.InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void DrawController()
{
float value = DraggableLabelFloat.Handle(LabelRect, PropertyInfo.Property.floatValue, sensitivity: -1f);//拖动Label控件可以操作Float参数
value = EditorGUI.FloatField(ControlRect, value);
SetFloatIfDifferent(PropertyInfo.Property, value);
}
}
public class ShaderGUISliderItem:ShaderGUIItem
{
public float Min = 0;
public float Max = 1;
public string RangePropertyName;
ShaderPropertyInfo _rangePropertyInfo;
private readonly Func<bool> _isVisible;
private const float SliderFrontGap = 5f;
private const float SliderBackGap = 2f;
private const float RangeFieldWidth = 30f;
public override void InitTriggerByChild()
{
if (RangePropertyName != null)
{
_rangePropertyInfo = RootItem.PropertyInfoDic[RangePropertyName];
Min = _rangePropertyInfo.Property.vectorValue.x;
Max = _rangePropertyInfo.Property.vectorValue.y;
}
base.InitTriggerByChild();
}
public ShaderGUISliderItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem, Func<bool> isVisible = null) :
base(rootItem, parentItem: parentItem)
{
_isVisible = isVisible;
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
if (_rangePropertyInfo == null)
{
base.OnGUI();
return;
}
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
using (new EditorGUIIndentLevelScope(0))
{
DrawController();
}
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
OnEndChange();
}
}
DrawResetButton();
DrawBlock();
}
public override void DrawController()
{
if (_rangePropertyInfo != null)
{
Rect minRect = ControlRect;
minRect.width = RangeFieldWidth;
Rect maxRect = ControlRect;
maxRect.x = ControlRect.xMax - RangeFieldWidth;
maxRect.width = RangeFieldWidth;
Rect sliderRect = ControlRect;
sliderRect.x = minRect.xMax + SliderFrontGap;
sliderRect.width = Mathf.Max(0f, maxRect.x - sliderRect.x - SliderBackGap + EditorGUI.indentLevel * UnityEditorGUIIndentWidth);
RangeVecHasMixedValue(out bool minValueHasMixed,out bool maxValueHasMixed);
Vector4 rangeVector = _rangePropertyInfo.Property.vectorValue;
float min = rangeVector.x;
float max = rangeVector.y;
EditorGUI.showMixedValue = minValueHasMixed;
bool minAnimatedScope = BeginAnimatedPropertyBackground(minRect, _rangePropertyInfo.Property);
min = EditorGUI.FloatField(minRect, min);
EndAnimatedPropertyBackground(minAnimatedScope);
EditorGUI.showMixedValue = maxValueHasMixed;
bool maxAnimatedScope = BeginAnimatedPropertyBackground(maxRect, _rangePropertyInfo.Property);
max = EditorGUI.FloatField(maxRect, max);
EndAnimatedPropertyBackground(maxAnimatedScope);
rangeVector.x = min;
rangeVector.y = max;
SetVectorIfDifferent(_rangePropertyInfo.Property, rangeVector);
float sliderMin = Mathf.Min(min, max);
float sliderMax = Mathf.Max(min, max);
float value = DraggableLabelFloat.Handle(
LabelRect,
PropertyInfo.Property.floatValue,
DraggableLabelFloat.GetSensitivityByRange(sliderMin, sliderMax),
sliderMin,
sliderMax);
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
bool sliderAnimatedScope = BeginAnimatedPropertyBackground(sliderRect, PropertyInfo.Property);
value = SliderNoIndent(sliderRect, value, sliderMin, sliderMax);
EndAnimatedPropertyBackground(sliderAnimatedScope);
SetFloatIfDifferent(PropertyInfo.Property, Mathf.Clamp(value, sliderMin, sliderMax));
EditorGUI.showMixedValue = false;
}
else
{
float value = DraggableLabelFloat.Handle(
LabelRect,
PropertyInfo.Property.floatValue,
DraggableLabelFloat.GetSensitivityByRange(Min, Max),
Min,
Max);
value = EditorGUI.Slider(ControlRect, value,Min,Max);
SetFloatIfDifferent(PropertyInfo.Property, value);
}
}
private static float SliderNoIndent(Rect rect, float value, float min, float max)
{
int indentLevel = EditorGUI.indentLevel;
try
{
EditorGUI.indentLevel = 0;
return EditorGUI.Slider(rect, value, min, max);
}
finally
{
EditorGUI.indentLevel = indentLevel;
}
}
void RangeVecHasMixedValue( out bool minValueHasMixed, out bool maxValueHasMixed)
{
minValueHasMixed = false;
maxValueHasMixed = false;
if (RootItem.Mats.Count > 1)
{
MaterialProperty rangeProperty = _rangePropertyInfo.Property;
float minValue = 0;
float maxValue = 0;
for (int i = 0; i < RootItem.Mats.Count; i++)
{
Vector4 rangeVec = RootItem.Mats[i].GetVector(RangePropertyName);
if (i == 0)
{
minValue = rangeVec.x;
maxValue = rangeVec.y;
}
else
{
if (!Mathf.Approximately(minValue, rangeVec.x))
{
minValueHasMixed = true;
}
if (!Mathf.Approximately(maxValue, rangeVec.y))
{
maxValueHasMixed = true;
}
}
}
}
}
bool RangePropIsDefaultValue()
{
MaterialProperty rangeProperty = _rangePropertyInfo.Property;
return rangeProperty.vectorValue == RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index) && !rangeProperty.hasMixedValue;
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (RangePropertyName != null)
{
if (!isCallByChild) //只有自身Reset了才需要查询自己的状态是否正确
{
bool isDefaultValue = true;
float defaultValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
isDefaultValue = Mathf.Approximately(PropertyInfo.Property.floatValue, defaultValue);
Vector4 defaultRangeVec = RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
isDefaultValue &= _rangePropertyInfo.Property.vectorValue == defaultRangeVec;
base.CheckIsPropertyModified(isCallByChild);
if (isDefaultValue == PropertyIsDefaultValue)
{
return; //如果状态没有改变,就不需要做任何操作
}
else
{
PropertyIsDefaultValue = isDefaultValue;
}
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
else
{
base.CheckIsPropertyModified(isCallByChild);
}
}
public override void ExecuteReset(bool isCallByParent = false)
{
if (RangePropertyName != null)
{
PropertyInfo.Property.floatValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
_rangePropertyInfo.Property.vectorValue =
RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
PropertyIsDefaultValue = true;
foreach (var childItem in ChildrenItemList)
{
childItem.ExecuteReset(true);
}
HasModified = false;
if (!isCallByParent) //直接由用户触发重置
{
ParentItem?.CheckIsPropertyModified(true);
}
}
else
{
base.ExecuteReset(isCallByParent);
}
}
}
//用于滑动控制Label的方案。
// DraggableLabelFloat.cs
public static class DraggableLabelFloat
{
// 为每个控件实例缓存一次拖拽状态
private static readonly System.Collections.Generic.Dictionary<int, DragState> s_Drag =
new System.Collections.Generic.Dictionary<int, DragState>();
private class DragState
{
public float startValue;
public float dragX; // 累计水平位移(单位:像素)
}
public static float Handle(Rect labelRect, float value, float sensitivity = -1f, float? min = null, float? max = null)
{
if (!GUI.enabled)
{
return value;
}
EditorGUIUtility.AddCursorRect(labelRect, MouseCursor.SlideArrow);
int id = GUIUtility.GetControlID(FocusType.Passive, labelRect);
Event e = Event.current;
switch (e.GetTypeForControl(id))
{
case EventType.MouseDown:
if (labelRect.Contains(e.mousePosition) && e.button == 0)
{
GUIUtility.hotControl = id;
e.Use();
EditorGUIUtility.SetWantsMouseJumping(1);
s_Drag[id] = new DragState
{
startValue = value,
dragX = 0f
};
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id && s_Drag.TryGetValue(id, out var st))
{
// 关键:使用 Event.delta 累加位移,兼容越界/跳回
st.dragX += e.delta.x;
float baseSens = sensitivity > 0f
? sensitivity
: 0.003f * Mathf.Max(1f, Mathf.Abs(st.startValue));
float modifier = 1f;
if (e.shift) modifier *= 0.1f;
if (e.control || e.command) modifier *= 10f;
float accel = 1f + 0.15f * Mathf.Clamp01(Mathf.Abs(st.dragX) / 50f);
float newValue = st.startValue + st.dragX * baseSens * modifier * accel;
if (min.HasValue) newValue = Mathf.Max(min.Value, newValue);
if (max.HasValue) newValue = Mathf.Min(max.Value, newValue);
if (!float.IsNaN(newValue) && !float.IsInfinity(newValue) && !Mathf.Approximately(newValue, value))
{
GUI.changed = true;
value = newValue;
}
e.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
GUIUtility.hotControl = 0;
EditorGUIUtility.SetWantsMouseJumping(0);
s_Drag.Remove(id);
e.Use();
}
break;
case EventType.KeyDown:
// 可选ESC 取消拖拽并还原
if (GUIUtility.hotControl == id && e.keyCode == KeyCode.Escape && s_Drag.TryGetValue(id, out var st2))
{
value = st2.startValue;
GUI.changed = true;
GUIUtility.hotControl = 0;
EditorGUIUtility.SetWantsMouseJumping(0);
s_Drag.Remove(id);
e.Use();
}
break;
}
return value;
}
public static float GetSensitivityByRange(float min, float max)
{
float range = Mathf.Abs(max - min);
if (float.IsNaN(range) || float.IsInfinity(range) || Mathf.Approximately(range, 0f))
{
return -1f;
}
return range * 0.003f;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b82b6bb5065f449f98cdba68daa4c16f
timeCreated: 1758462240

View File

@@ -0,0 +1,127 @@
using UnityEditor;
using UnityEngine;
using UnityEditor.AnimatedValues;
namespace NBShaderEditor
{
public class ShaderGUIFoldOutHelper
{
private const float FoldOutAnimationSpeed = 10f;
public ShaderGUIFoldOutHelper(ShaderGUIRootItem rootItem, string foldOutPropertyName)
{
_rootItem = rootItem;
_propertyName = foldOutPropertyName;
_propertyInfo = _rootItem.PropertyInfoDic[_propertyName];
bool isOpen = _propertyInfo.Property.floatValue > 0.5f;
animBool.value = isOpen;
animBool.target = isOpen;
animBool.valueChanged.AddListener(rootItem.MatEditor.Repaint);
animBool.speed = FoldOutAnimationSpeed;
}
private ShaderGUIRootItem _rootItem;
private string _propertyName;
private AnimBool animBool = new AnimBool();
private ShaderPropertyInfo _propertyInfo;
public bool BeginFadedGroup(Rect labelRect)
{
DrawFoldOut(labelRect);
return BeginFadeGroup();
}
public bool BeginFadeGroup()
{
return animBool.faded > 0.0001f;
}
public bool DrawFoldOut(Rect labelRect)
{
Rect foldOutRect = MakeFoldOutRect(labelRect);
bool isOpen = _propertyInfo.Property.floatValue > 0.5f;
isOpen = GUI.Toggle(foldOutRect, isOpen, GUIContent.none, EditorStyles.foldout);
SetOpen(isOpen);
return isOpen;
}
public bool DrawFoldOutLabel(Rect labelRect, GUIContent content, GUIStyle style)
{
Rect foldOutRect = MakeFoldOutRect(labelRect);
bool isOpen = _propertyInfo.Property.floatValue > 0.5f;
isOpen = GUI.Toggle(foldOutRect, isOpen, content, style ?? EditorStyles.foldout);
SetOpen(isOpen);
return isOpen;
}
public void SetOpen(bool isOpen)
{
if (animBool.target != isOpen)
{
animBool.target = isOpen;
}
float value = isOpen ? 1f : 0f;
if (!Mathf.Approximately(_propertyInfo.Property.floatValue, value))
{
_propertyInfo.Property.floatValue = value;
}
}
private static Rect MakeFoldOutRect(Rect labelRect)
{
Rect foldOutRect = labelRect;
foldOutRect.x = ShaderGUIItem.GetEditorLabelTextX(labelRect) - ShaderGUIItem.FoldOutArrowWidth;
foldOutRect.width = Mathf.Max(0f, labelRect.xMax - foldOutRect.x);
return foldOutRect;
}
public void EndFadedGroup()
{
}
}
public class ShaderGUIBigBlockItem:ShaderGUIItem
{
ShaderGUIFoldOutHelper _foldOutHelper;
GUIStyle _boldStyle = new GUIStyle(EditorStyles.boldLabel);
public string FoldOutPropertyName { get; set; }
public override void InitTriggerByChild()
{
_foldOutHelper = new ShaderGUIFoldOutHelper(RootItem, FoldOutPropertyName);
base.InitTriggerByChild();
}
public ShaderGUIBigBlockItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) :
base(rootItem, parentItem: parentItem)
{
}
public override void OnGUI()//完全覆写
{
LayoutSpace();
GetRect();
DrawResetButton();
bool isOpen = _foldOutHelper.BeginFadedGroup(LabelRect);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent,_boldStyle);
}
EditorGUI.indentLevel++;
if (isOpen)
{
DrawBlock();
}
_foldOutHelper.EndFadedGroup();
EditorGUI.indentLevel--;
Rect rect = ApplyGlobalRectCompensation(LayoutRect(1));
EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6228baa804b041658eaebea96655b431
timeCreated: 1758373869

View File

@@ -0,0 +1,517 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
namespace NBShaderEditor
{
public enum MixedBool
{
False = 0,
True = 1,
Mixed = -1
}
public class ShaderGUIItem
{
public const float MinLabelWidth = 120f;
public const float LabelWidthRatio = 0.4f;
public const float GlobalRectXOffset = -15f;
public const float GlobalRectWidthExpansion = 15f;
public const float UnityEditorGUIIndentWidth = 15f;
public const float EditorGUIIndentWidth = 10f;
public const float FoldOutArrowWidth = UnityEditorGUIIndentWidth;
public const float ControlResetGap = 3f;
public const float ControlIndentCompensation = 10f;
public ShaderPropertyInfo PropertyInfo;
public ShaderGUIItem ParentItem;
public List<ShaderGUIItem> ChildrenItemList = new List<ShaderGUIItem>();
public ShaderGUIRootItem RootItem;
public string PropertyName;
public GUIContent GuiContent;
private static int _parentControlDisabledDepth;
public virtual void InitTriggerByChild()//根Child一定要做这个事情
{
if ( PropertyInfo == null && PropertyName != null)
{
PropertyInfo = RootItem.PropertyInfoDic[PropertyName];
}
CheckIsPropertyModified();
}
public ShaderGUIItem(ShaderGUIRootItem rtItem,ShaderGUIItem parentItem=null)
{
RootItem = rtItem;
if (parentItem != null)
{
ParentItem = parentItem;
if (!parentItem.ChildrenItemList.Contains(this))
{
ParentItem.ChildrenItemList.Add(this);
}
}
}
public Rect BaseRect;
public Rect LabelRect;
public Rect ControlRect;
public Rect ResetRect;
private static readonly GUIContent TempHelpContent = new GUIContent();
public static float ResetButtonSize => EditorGUIUtility.singleLineHeight;
#if !UNITY_2022_1_OR_NEWER
private static Material s_ShaderDefaultIntValueMaterial;
#endif
public static int GetShaderPropertyDefaultIntValue(Shader shader, int propertyIndex)
{
#if UNITY_2022_1_OR_NEWER
return shader.GetPropertyDefaultIntValue(propertyIndex);
#else
if (s_ShaderDefaultIntValueMaterial == null ||
s_ShaderDefaultIntValueMaterial.shader != shader)
{
if (s_ShaderDefaultIntValueMaterial != null)
{
UnityEngine.Object.DestroyImmediate(s_ShaderDefaultIntValueMaterial);
}
// Unity 2021.3 has no int default API; use one hidden material as the shader default-value reader.
s_ShaderDefaultIntValueMaterial = new Material(shader)
{
hideFlags = HideFlags.HideAndDontSave
};
}
return s_ShaderDefaultIntValueMaterial.GetInteger(shader.GetPropertyName(propertyIndex));
#endif
}
public virtual void GetRect(bool applyControlIndentCompensation = true)
{
BaseRect = ApplyGlobalRectCompensation(RootItem.GetControlRect());
SplitLineRect(BaseRect, out LabelRect, out ControlRect, out ResetRect, applyControlIndentCompensation);
}
public static Rect ApplyGlobalRectCompensation(Rect rect)
{
rect.x += GlobalRectXOffset;
rect.width = Mathf.Max(0f, rect.width + GlobalRectWidthExpansion);
return rect;
}
public static Rect ApplyLabelIndentWidth(Rect rect)
{
float indentDelta = EditorGUI.indentLevel * (EditorGUIIndentWidth - UnityEditorGUIIndentWidth);
rect.x += indentDelta;
rect.width = Mathf.Max(0f, rect.width - indentDelta);
return rect;
}
public static Rect ApplyDirectLabelIndentWidth(Rect rect)
{
float indentOffset = EditorGUI.indentLevel * EditorGUIIndentWidth;
rect.x += indentOffset;
rect.width = Mathf.Max(0f, rect.width - indentOffset);
return rect;
}
public static float GetEditorLabelTextX(Rect labelRect)
{
return labelRect.x + EditorGUI.indentLevel * UnityEditorGUIIndentWidth;
}
public static float GetLabelWidth(Rect baseRect)
{
return Mathf.Max(MinLabelWidth, baseRect.width * LabelWidthRatio);
}
public static void SplitLineRect(
Rect baseRect,
out Rect labelRect,
out Rect controlRect,
out Rect resetRect,
bool applyControlIndentCompensation = true)
{
float labelWidth = GetLabelWidth(baseRect);
labelRect = baseRect;
labelRect.width = labelWidth;
labelRect = ApplyLabelIndentWidth(labelRect);
Rect controlAndResetRect = baseRect;
controlAndResetRect.x += labelWidth;
controlAndResetRect.width = Mathf.Max(0f, controlAndResetRect.width - labelWidth);
SplitControlAndResetRect(controlAndResetRect, out controlRect, out resetRect, applyControlIndentCompensation);
}
protected static GUIContent LocalizedContent(
string tableName,
string labelKey,
string labelFallback,
string tooltipKey = null,
string tooltipFallback = "")
{
return ShaderGUILocalization.MakeContent(tableName, labelKey, labelFallback, tooltipKey, tooltipFallback);
}
protected static GUIContent LocalizedInspectorContent(string tableName, string key, string fallback, string tip = "")
{
return ShaderGUILocalization.MakeInspectorContent(tableName, key, fallback, tip);
}
protected static string LocalizedText(string tableName, string key, string fallback = "")
{
return ShaderGUILocalization.GetInspectorText(tableName, key, fallback);
}
protected static string[] LocalizedOptions(string tableName, string key, string[] fallback)
{
return ShaderGUILocalization.GetInspectorOptions(tableName, key, fallback);
}
public static void SplitControlAndResetRect(
Rect baseRect,
out Rect controlRect,
out Rect resetRect,
bool applyControlIndentCompensation = true)
{
resetRect = baseRect;
resetRect.x = baseRect.xMax - ResetButtonSize;
resetRect.width = ResetButtonSize;
float controlIndentCompensation = applyControlIndentCompensation ? ControlIndentCompensation : 0f;
controlRect = baseRect;
controlRect.x -= controlIndentCompensation;
controlRect.width = Mathf.Max(0f, baseRect.width - ResetButtonSize - ControlResetGap + controlIndentCompensation);
}
protected Rect LayoutRect(float height = -1f)
{
return RootItem.GetControlRect(height);
}
protected void LayoutSpace(float height = -1f)
{
RootItem.Space(height);
}
protected void DrawLayoutHelpBox(string message, MessageType messageType)
{
TempHelpContent.text = message;
float width = Mathf.Max(1f, EditorGUIUtility.currentViewWidth + GlobalRectWidthExpansion + GlobalRectXOffset);
float height = Mathf.Max(
EditorGUIUtility.singleLineHeight * 2f,
EditorStyles.helpBox.CalcHeight(TempHelpContent, width));
Rect rect = ApplyGlobalRectCompensation(LayoutRect(height));
EditorGUI.HelpBox(rect, message, messageType);
TempHelpContent.text = string.Empty;
}
public virtual void OnGUI()
{
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
bool animatedPropertyScope = BeginAnimatedPropertyBackground(BaseRect, PropertyInfo.Property);
using (new EditorGUIIndentLevelScope(0))
{
DrawController();
}
EndAnimatedPropertyBackground(animatedPropertyScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
OnEndChange();
}
}
DrawResetButton();
DrawBlock();
}
public void DrawResetButton()//如果重写OnGUI一定要记得调用DrawResetButton
{
CheckIsPropertyModified();
_resetButtonContent.text = HasModified ? "R" : "";
_resetButtonStyle = HasModified ? GUI.skin.button : GUI.skin.label;
using (ParentControlDisabledScope())
{
if (GUI.Button(ResetRect, _resetButtonContent, _resetButtonStyle))
{
ExecuteReset();
}
}
}
public virtual void DrawController()
{
}
protected static bool SetFloatIfDifferent(MaterialProperty property, float value)
{
if (property == null || Mathf.Approximately(property.floatValue, value))
{
return false;
}
property.floatValue = value;
return true;
}
protected static bool SetVectorIfDifferent(MaterialProperty property, Vector4 value)
{
if (property == null || Approximately(property.vectorValue, value))
{
return false;
}
property.vectorValue = value;
return true;
}
protected static bool SetColorIfDifferent(MaterialProperty property, Color value)
{
if (property == null)
{
return false;
}
Color current = property.colorValue;
if (Mathf.Approximately(current.r, value.r) &&
Mathf.Approximately(current.g, value.g) &&
Mathf.Approximately(current.b, value.b) &&
Mathf.Approximately(current.a, value.a))
{
return false;
}
property.colorValue = value;
return true;
}
protected readonly struct EditorGUIIndentLevelScope : System.IDisposable
{
private readonly int _indentLevel;
public EditorGUIIndentLevelScope(int indentLevel)
{
_indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = indentLevel;
}
public void Dispose()
{
EditorGUI.indentLevel = _indentLevel;
}
}
protected bool IsParentControlDisabled => _parentControlDisabledDepth > 0;
protected EditorGUI.DisabledScope ParentControlDisabledScope(bool disabled = false)
{
return new EditorGUI.DisabledScope(IsParentControlDisabled || disabled);
}
protected readonly struct InheritedControlDisabledScope : System.IDisposable
{
private readonly bool _disabled;
public InheritedControlDisabledScope(bool disabled)
{
_disabled = disabled;
if (_disabled)
{
_parentControlDisabledDepth++;
}
}
public void Dispose()
{
if (_disabled)
{
_parentControlDisabledDepth = Mathf.Max(0, _parentControlDisabledDepth - 1);
}
}
}
public virtual void DrawBlock()
{
}
public virtual void OnEndChange()
{
CheckIsPropertyModified();
}
private readonly Dictionary<string, string> _animationPropertyPathDic = new Dictionary<string, string>();
protected bool BeginAnimatedPropertyBackground(Rect totalPosition, MaterialProperty property)
{
if (property == null || RootItem?.MatEditor == null)
{
return false;
}
RootItem.MatEditor.BeginAnimatedCheck(totalPosition, property);
return true;
}
protected void EndAnimatedPropertyBackground(bool scopeActive)
{
if (scopeActive && RootItem?.MatEditor != null)
{
RootItem.MatEditor.EndAnimatedCheck();
}
}
public bool IsPropertyAnimated(string propertyName, params string[] componentNames)
{
if (propertyName == null) return false;
if (AnimationMode.InAnimationMode())
{
foreach (var r in RootItem.RenderersUsingThisMaterial)
{
if (componentNames != null && componentNames.Length > 0)
{
foreach (string componentName in componentNames)
{
if (AnimationMode.IsPropertyAnimated(r, GetAnimationPropertyPath(propertyName, componentName)))
{
return true;
}
}
}
else if (AnimationMode.IsPropertyAnimated(r, GetAnimationPropertyPath(propertyName, string.Empty)))
{
return true;
}
}
}
return false;
}
private string GetAnimationPropertyPath(string propertyName, string componentName)
{
string key = string.IsNullOrEmpty(componentName) ? propertyName : propertyName + "." + componentName;
if (!_animationPropertyPathDic.TryGetValue(key, out string propertyPath))
{
propertyPath = string.IsNullOrEmpty(componentName)
? "material." + propertyName
: "material." + propertyName + "." + componentName;
_animationPropertyPathDic.Add(key, propertyPath);
}
return propertyPath;
}
#region ResetLogic
private GUIContent _resetButtonContent = new GUIContent("R","重置当前属性及子集属性(如有)");
private GUIStyle _resetButtonStyle;
public bool HasModified = false;
public bool PropertyIsDefaultValue = true;
public virtual void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)//只有自身Reset了才需要查询自己的状态是否正确
{
bool isDefaultValue = true;
if (PropertyInfo != null)
{
switch (ShaderGUIUnityCompat.GetPropertyType(PropertyInfo.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
float defaultValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
isDefaultValue = Mathf.Approximately(PropertyInfo.Property.floatValue, defaultValue);
break;
case ShaderPropertyType.Color:
Vector4 defaultColor = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
isDefaultValue = Approximately(PropertyInfo.Property.colorValue, defaultColor);
break;
case ShaderPropertyType.Vector:
Vector4 defaultVector = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
isDefaultValue = Approximately(PropertyInfo.Property.vectorValue, defaultVector);
break;
}
}
else
{
isDefaultValue = true;//如果没有Property则是看子集的情况
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;//如果状态没有改变,就不需要做任何操作
}
else
{
PropertyIsDefaultValue = isDefaultValue;
}
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public virtual void ExecuteReset(bool isCallByParent = false)
{
if (PropertyInfo != null)
{
switch (ShaderGUIUnityCompat.GetPropertyType(PropertyInfo.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
float defaultValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
PropertyInfo.Property.floatValue = defaultValue;
break;
case ShaderPropertyType.Color:
PropertyInfo.Property.colorValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
break;
case ShaderPropertyType.Vector:
PropertyInfo.Property.vectorValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
break;
}
PropertyIsDefaultValue = true;
}
foreach (var childItem in ChildrenItemList)
{
childItem.ExecuteReset(true);
}
HasModified = false;
if (!isCallByParent)//直接由用户触发重置
{
ParentItem?.CheckIsPropertyModified(true);
}
}
protected static bool Approximately(Vector4 a, Vector4 b)
{
return Mathf.Approximately(a.x, b.x) &&
Mathf.Approximately(a.y, b.y) &&
Mathf.Approximately(a.z, b.z) &&
Mathf.Approximately(a.w, b.w);
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f948e090bae4a558a52cb56d0b6c9fc
timeCreated: 1758127003

View File

@@ -0,0 +1,894 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public static class ShaderGUILocalization
{
public const string DefaultLanguage = "zh-CN";
public const string EnglishLanguage = "en-US";
private const string TooltipColumnSuffix = "-tip";
private const string TooltipColumnAliasSuffix = "-tooltip";
private static readonly Dictionary<string, LocalizationTable> Tables =
new Dictionary<string, LocalizationTable>(StringComparer.OrdinalIgnoreCase);
private static NBFXLanguageMode s_CachedLanguageMode;
private static string s_CachedPreferredLanguage;
private static bool s_CachedPreferredLanguageValid;
public static void RegisterCsv(string tableName, string csvAssetPath)
{
tableName = NormalizeTableName(tableName);
if (Tables.TryGetValue(tableName, out LocalizationTable table))
{
if (string.Equals(table.CsvAssetPath, csvAssetPath, StringComparison.OrdinalIgnoreCase))
{
return;
}
table.CsvAssetPath = csvAssetPath;
table.Reload();
return;
}
Tables[tableName] = new LocalizationTable(csvAssetPath);
}
public static string GetCurrentLanguage(string tableName)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetCurrentLanguage(table);
}
public static string GetEditorLanguageName()
{
return GetLanguageFromEditor();
}
private static string GetCurrentLanguage(LocalizationTable table)
{
string preferred = GetPreferredLanguageName();
foreach (string language in table.Languages)
{
if (string.Equals(language, preferred, StringComparison.OrdinalIgnoreCase))
{
return language;
}
}
return DefaultLanguage;
}
public static GUIContent MakeContent(
string tableName,
string labelKey,
string labelFallback,
string tooltipKey = null,
string tooltipFallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
var cacheKey = new ContentCacheKey(language, labelKey, labelFallback, tooltipKey, tooltipFallback);
if (table.ContentCache.TryGetValue(cacheKey, out GUIContent cachedContent))
{
return cachedContent;
}
var content = new GUIContent(
GetLocalizedValue(table, labelKey, labelFallback, language),
GetTooltip(table, labelKey, tooltipKey, tooltipFallback, language));
table.ContentCache[cacheKey] = content;
return content;
}
public static GUIContent MakeInspectorContent(string tableName, string key, string fallback, string tip = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
var cacheKey = new InspectorContentCacheKey(language, key, fallback, tip);
if (table.InspectorContentCache.TryGetValue(cacheKey, out GUIContent cachedContent))
{
return cachedContent;
}
string labelKey = "inspector." + key + ".label";
string tooltipKey = "inspector." + key + ".tip";
var content = new GUIContent(
GetLocalizedValue(table, labelKey, fallback, language),
GetTooltip(table, labelKey, tooltipKey, tip, language));
table.InspectorContentCache[cacheKey] = content;
return content;
}
public static string GetInspectorText(string tableName, string key, string fallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
return GetInspectorText(table, key, fallback, language);
}
public static string GetInspectorText(string tableName, string key, string fallback, string language)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetInspectorText(table, key, fallback, language);
}
private static string GetInspectorText(LocalizationTable table, string key, string fallback, string language)
{
var cacheKey = new InspectorTextCacheKey(language, key, fallback);
if (table.InspectorTextCache.TryGetValue(cacheKey, out string cachedText))
{
return cachedText;
}
cachedText = GetLocalizedValue(table, "inspector." + key, fallback, language);
table.InspectorTextCache[cacheKey] = cachedText;
return cachedText;
}
public static string[] GetInspectorOptions(string tableName, string key, string[] fallback)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
return GetInspectorOptions(table, key, fallback, language);
}
public static string[] GetInspectorOptions(string tableName, string key, string[] fallback, string language)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetInspectorOptions(table, key, fallback, language);
}
private static string[] GetInspectorOptions(LocalizationTable table, string key, string[] fallback, string language)
{
if (fallback == null)
{
return Array.Empty<string>();
}
var cacheKey = new OptionsCacheKey(language, key, fallback);
if (table.InspectorOptionsCache.TryGetValue(cacheKey, out string[] cachedValues))
{
return cachedValues;
}
string keyPrefix = "inspector." + key + ".option";
string[] values = new string[fallback.Length];
for (int i = 0; i < fallback.Length; i++)
{
values[i] = GetLocalizedValue(table, keyPrefix + "." + i, fallback[i], language);
}
table.InspectorOptionsCache[cacheKey] = values;
return values;
}
public static string[] GetOptions(string tableName, string keyPrefix, string[] fallback)
{
if (fallback == null)
{
return Array.Empty<string>();
}
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
var cacheKey = new OptionsCacheKey(language, keyPrefix, fallback);
if (table.OptionsCache.TryGetValue(cacheKey, out string[] cachedValues))
{
return cachedValues;
}
string[] values = new string[fallback.Length];
for (int i = 0; i < fallback.Length; i++)
{
values[i] = GetLocalizedValue(table, keyPrefix + "." + i, fallback[i], language);
}
table.OptionsCache[cacheKey] = values;
return values;
}
public static string Get(string tableName, string key, string fallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetLocalizedValue(table, key, fallback, GetCurrentLanguage(table));
}
private static string GetLocalizedValue(LocalizationTable table, string key, string fallback, string language)
{
if (string.IsNullOrEmpty(key))
{
return fallback;
}
if (table.Rows.TryGetValue(key, out Dictionary<string, string> row))
{
if (row.TryGetValue(language, out string localizedValue) && !string.IsNullOrEmpty(localizedValue))
{
return localizedValue;
}
if (row.TryGetValue(DefaultLanguage, out string defaultValue) && !string.IsNullOrEmpty(defaultValue))
{
return defaultValue;
}
}
return fallback;
}
public static string GetTooltip(string tableName, string labelKey, string tooltipKey = null, string fallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetTooltip(table, labelKey, tooltipKey, fallback, GetCurrentLanguage(table));
}
private static string GetTooltip(
LocalizationTable table,
string labelKey,
string tooltipKey,
string fallback,
string language)
{
if (!string.IsNullOrEmpty(labelKey) &&
table.Rows.TryGetValue(labelKey, out Dictionary<string, string> row))
{
if (TryGetTooltip(row, language, out string localizedTooltip))
{
return localizedTooltip;
}
if (TryGetTooltip(row, DefaultLanguage, out string defaultTooltip))
{
return defaultTooltip;
}
}
if (!string.IsNullOrEmpty(tooltipKey))
{
return GetLocalizedValue(table, tooltipKey, fallback, language);
}
return fallback;
}
public static void Reload(string tableName = null)
{
if (string.IsNullOrEmpty(tableName))
{
foreach (LocalizationTable table in Tables.Values)
{
table.Reload();
}
InvalidatePreferredLanguageCache();
return;
}
GetTable(tableName).Reload();
InvalidatePreferredLanguageCache();
}
private static void InvalidatePreferredLanguageCache()
{
s_CachedPreferredLanguageValid = false;
}
private static LocalizationTable GetTable(string tableName)
{
tableName = NormalizeTableName(tableName);
if (!Tables.TryGetValue(tableName, out LocalizationTable table))
{
table = new LocalizationTable(string.Empty);
Tables[tableName] = table;
}
return table;
}
private static string NormalizeTableName(string tableName)
{
return string.IsNullOrEmpty(tableName) ? "default" : tableName;
}
private static bool IsTooltipColumn(string header)
{
return !string.IsNullOrEmpty(header) &&
(header.EndsWith(TooltipColumnSuffix, StringComparison.OrdinalIgnoreCase) ||
header.EndsWith(TooltipColumnAliasSuffix, StringComparison.OrdinalIgnoreCase));
}
private static bool TryGetTooltip(Dictionary<string, string> row, string language, out string tooltip)
{
tooltip = string.Empty;
if (string.IsNullOrEmpty(language))
{
return false;
}
if (row.TryGetValue(language + TooltipColumnSuffix, out tooltip) && !string.IsNullOrEmpty(tooltip))
{
return true;
}
if (row.TryGetValue(language + TooltipColumnAliasSuffix, out tooltip) && !string.IsNullOrEmpty(tooltip))
{
return true;
}
return false;
}
private static string GetPreferredLanguageName()
{
NBFXLanguageMode languageMode = NBFXProjectSettings.LanguageMode;
if (s_CachedPreferredLanguageValid && s_CachedLanguageMode == languageMode)
{
return s_CachedPreferredLanguage;
}
string language;
switch (languageMode)
{
case NBFXLanguageMode.Chinese:
language = DefaultLanguage;
break;
case NBFXLanguageMode.English:
language = EnglishLanguage;
break;
case NBFXLanguageMode.FollowEditor:
default:
language = GetLanguageFromEditor();
break;
}
s_CachedLanguageMode = languageMode;
s_CachedPreferredLanguage = language;
s_CachedPreferredLanguageValid = true;
return language;
}
private static string GetLanguageFromEditor()
{
if (TryGetEditorLanguageName(out string language))
{
return language;
}
return GetSystemLanguageName();
}
private static bool TryGetEditorLanguageName(out string language)
{
language = string.Empty;
Type localizationDatabaseType = typeof(Editor).Assembly.GetType("UnityEditor.LocalizationDatabase");
if (localizationDatabaseType == null)
{
return false;
}
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
string[] memberNames =
{
"currentEditorLanguage",
"CurrentEditorLanguage"
};
foreach (string memberName in memberNames)
{
PropertyInfo propertyInfo = localizationDatabaseType.GetProperty(memberName, flags);
if (propertyInfo != null && TryGetEditorLanguageValue(() => propertyInfo.GetValue(null, null), out language))
{
return true;
}
FieldInfo fieldInfo = localizationDatabaseType.GetField(memberName, flags);
if (fieldInfo != null && TryGetEditorLanguageValue(() => fieldInfo.GetValue(null), out language))
{
return true;
}
}
return false;
}
private static bool TryGetEditorLanguageValue(Func<object> valueGetter, out string language)
{
language = string.Empty;
try
{
return TryMapLanguageValue(valueGetter(), out language);
}
catch
{
return false;
}
}
private static bool TryMapLanguageValue(object value, out string language)
{
language = string.Empty;
if (value == null)
{
return false;
}
if (value is SystemLanguage systemLanguage)
{
language = MapSystemLanguage(systemLanguage);
return true;
}
string languageName = value.ToString();
if (string.IsNullOrEmpty(languageName))
{
return false;
}
string normalizedLanguageName = languageName.Replace("_", "-").ToLowerInvariant();
if (normalizedLanguageName.StartsWith("en", StringComparison.Ordinal) ||
normalizedLanguageName.Contains("english"))
{
language = EnglishLanguage;
return true;
}
if (normalizedLanguageName.StartsWith("zh", StringComparison.Ordinal) ||
normalizedLanguageName.Contains("chinese"))
{
language = DefaultLanguage;
return true;
}
language = DefaultLanguage;
return true;
}
private static string GetSystemLanguageName()
{
return MapSystemLanguage(Application.systemLanguage);
}
private static string MapSystemLanguage(SystemLanguage systemLanguage)
{
switch (systemLanguage)
{
case SystemLanguage.English:
return EnglishLanguage;
case SystemLanguage.Chinese:
case SystemLanguage.ChineseSimplified:
case SystemLanguage.ChineseTraditional:
return DefaultLanguage;
default:
return DefaultLanguage;
}
}
private readonly struct ContentCacheKey : IEquatable<ContentCacheKey>
{
private readonly string _language;
private readonly string _labelKey;
private readonly string _labelFallback;
private readonly string _tooltipKey;
private readonly string _tooltipFallback;
public ContentCacheKey(
string language,
string labelKey,
string labelFallback,
string tooltipKey,
string tooltipFallback)
{
_language = language;
_labelKey = labelKey;
_labelFallback = labelFallback;
_tooltipKey = tooltipKey;
_tooltipFallback = tooltipFallback;
}
public bool Equals(ContentCacheKey other)
{
return StringEquals(_language, other._language) &&
StringEquals(_labelKey, other._labelKey) &&
StringEquals(_labelFallback, other._labelFallback) &&
StringEquals(_tooltipKey, other._tooltipKey) &&
StringEquals(_tooltipFallback, other._tooltipFallback);
}
public override bool Equals(object obj)
{
return obj is ContentCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_labelKey);
hash = hash * 31 + StringHash(_labelFallback);
hash = hash * 31 + StringHash(_tooltipKey);
hash = hash * 31 + StringHash(_tooltipFallback);
return hash;
}
}
}
private readonly struct InspectorContentCacheKey : IEquatable<InspectorContentCacheKey>
{
private readonly string _language;
private readonly string _key;
private readonly string _fallback;
private readonly string _tip;
public InspectorContentCacheKey(string language, string key, string fallback, string tip)
{
_language = language;
_key = key;
_fallback = fallback;
_tip = tip;
}
public bool Equals(InspectorContentCacheKey other)
{
return StringEquals(_language, other._language) &&
StringEquals(_key, other._key) &&
StringEquals(_fallback, other._fallback) &&
StringEquals(_tip, other._tip);
}
public override bool Equals(object obj)
{
return obj is InspectorContentCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_key);
hash = hash * 31 + StringHash(_fallback);
hash = hash * 31 + StringHash(_tip);
return hash;
}
}
}
private readonly struct OptionsCacheKey : IEquatable<OptionsCacheKey>
{
private readonly string _language;
private readonly string _keyPrefix;
private readonly string[] _fallback;
private readonly int _fallbackLength;
public OptionsCacheKey(string language, string keyPrefix, string[] fallback)
{
_language = language;
_keyPrefix = keyPrefix;
_fallback = fallback;
_fallbackLength = fallback != null ? fallback.Length : 0;
}
public bool Equals(OptionsCacheKey other)
{
if (!StringEquals(_language, other._language) ||
!StringEquals(_keyPrefix, other._keyPrefix) ||
_fallbackLength != other._fallbackLength)
{
return false;
}
for (int i = 0; i < _fallbackLength; i++)
{
if (!StringEquals(_fallback[i], other._fallback[i]))
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
return obj is OptionsCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_keyPrefix);
hash = hash * 31 + _fallbackLength;
for (int i = 0; i < _fallbackLength; i++)
{
hash = hash * 31 + StringHash(_fallback[i]);
}
return hash;
}
}
}
private readonly struct InspectorTextCacheKey : IEquatable<InspectorTextCacheKey>
{
private readonly string _language;
private readonly string _key;
private readonly string _fallback;
public InspectorTextCacheKey(string language, string key, string fallback)
{
_language = language;
_key = key;
_fallback = fallback;
}
public bool Equals(InspectorTextCacheKey other)
{
return StringEquals(_language, other._language) &&
StringEquals(_key, other._key) &&
StringEquals(_fallback, other._fallback);
}
public override bool Equals(object obj)
{
return obj is InspectorTextCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_key);
hash = hash * 31 + StringHash(_fallback);
return hash;
}
}
}
private static bool StringEquals(string a, string b)
{
return string.Equals(a, b, StringComparison.Ordinal);
}
private static int StringHash(string value)
{
return value == null ? 0 : StringComparer.Ordinal.GetHashCode(value);
}
private sealed class LocalizationTable
{
private bool _loaded;
private bool _warningLogged;
public LocalizationTable(string csvAssetPath)
{
CsvAssetPath = csvAssetPath;
Rows = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
Languages = Array.Empty<string>();
ContentCache = new Dictionary<ContentCacheKey, GUIContent>();
InspectorContentCache = new Dictionary<InspectorContentCacheKey, GUIContent>();
InspectorTextCache = new Dictionary<InspectorTextCacheKey, string>();
OptionsCache = new Dictionary<OptionsCacheKey, string[]>();
InspectorOptionsCache = new Dictionary<OptionsCacheKey, string[]>();
}
public string CsvAssetPath { get; set; }
public Dictionary<string, Dictionary<string, string>> Rows { get; private set; }
public string[] Languages { get; private set; }
public Dictionary<ContentCacheKey, GUIContent> ContentCache { get; }
public Dictionary<InspectorContentCacheKey, GUIContent> InspectorContentCache { get; }
public Dictionary<InspectorTextCacheKey, string> InspectorTextCache { get; }
public Dictionary<OptionsCacheKey, string[]> OptionsCache { get; }
public Dictionary<OptionsCacheKey, string[]> InspectorOptionsCache { get; }
public void Reload()
{
_loaded = false;
_warningLogged = false;
Rows = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
Languages = Array.Empty<string>();
ContentCache.Clear();
InspectorContentCache.Clear();
InspectorTextCache.Clear();
OptionsCache.Clear();
InspectorOptionsCache.Clear();
}
public void EnsureLoaded()
{
if (_loaded)
{
return;
}
_loaded = true;
Rows = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
Languages = Array.Empty<string>();
if (string.IsNullOrEmpty(CsvAssetPath))
{
LogWarningOnce("ShaderGUI localization table is not registered.");
return;
}
TextAsset csvAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(CsvAssetPath);
if (csvAsset == null)
{
LogWarningOnce($"ShaderGUI localization csv not found at '{CsvAssetPath}'.");
return;
}
ParseCsv(csvAsset.text);
}
private void ParseCsv(string csvText)
{
List<string> rows = SplitRows(csvText);
if (rows.Count == 0)
{
return;
}
List<string> headers = ParseCsvRow(rows[0]);
if (headers.Count < 2)
{
return;
}
var languages = new List<string>();
for (int i = 1; i < headers.Count; i++)
{
if (!IsTooltipColumn(headers[i]))
{
languages.Add(headers[i]);
}
}
Languages = languages.ToArray();
for (int rowIndex = 1; rowIndex < rows.Count; rowIndex++)
{
List<string> values = ParseCsvRow(rows[rowIndex]);
if (values.Count == 0 || string.IsNullOrWhiteSpace(values[0]))
{
continue;
}
var languageMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
for (int i = 1; i < headers.Count; i++)
{
languageMap[headers[i]] = i < values.Count ? values[i] : string.Empty;
}
Rows[values[0]] = languageMap;
}
}
private static List<string> SplitRows(string csvText)
{
var rows = new List<string>();
if (string.IsNullOrEmpty(csvText))
{
return rows;
}
var builder = new StringBuilder();
bool inQuotes = false;
for (int i = 0; i < csvText.Length; i++)
{
char current = csvText[i];
if (current == '"')
{
if (inQuotes && i + 1 < csvText.Length && csvText[i + 1] == '"')
{
builder.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
continue;
}
if (!inQuotes && (current == '\n' || current == '\r'))
{
if (builder.Length > 0)
{
rows.Add(builder.ToString());
builder.Length = 0;
}
if (current == '\r' && i + 1 < csvText.Length && csvText[i + 1] == '\n')
{
i++;
}
continue;
}
builder.Append(current);
}
if (builder.Length > 0)
{
rows.Add(builder.ToString());
}
return rows;
}
private static List<string> ParseCsvRow(string rowText)
{
var values = new List<string>();
var builder = new StringBuilder();
bool inQuotes = false;
for (int i = 0; i < rowText.Length; i++)
{
char current = rowText[i];
if (current == '"')
{
if (inQuotes && i + 1 < rowText.Length && rowText[i + 1] == '"')
{
builder.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
continue;
}
if (!inQuotes && current == ',')
{
values.Add(builder.ToString());
builder.Length = 0;
continue;
}
builder.Append(current);
}
values.Add(builder.ToString());
return values;
}
private void LogWarningOnce(string message)
{
if (_warningLogged)
{
return;
}
_warningLogged = true;
Debug.LogWarning(message);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5429c73ee5e4590bbbbccee3d5a3a58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,158 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ShaderGUIPopUpItem:ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<string[]> _popupNamesProvider;
private readonly Action<MaterialProperty> _onValueChanged;
private readonly Func<bool> _isVisible;
public ShaderGUIPopUpItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) :
base(rootItem, parentItem: parentItem)
{
// base.InitTriggerByChild();
}
public ShaderGUIPopUpItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Func<string[]> popupNamesProvider,
Action<MaterialProperty> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_popupNamesProvider = popupNamesProvider;
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
PopUpNames = _popupNamesProvider?.Invoke();
InitTriggerByChild();
}
public string[] PopUpNames;
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void DrawController()
{
int value = EditorGUI.Popup(ControlRect, (int)PropertyInfo.Property.floatValue, PopUpNames);
SetFloatIfDifferent(PropertyInfo.Property, value);
}
public override void OnEndChange()
{
base.OnEndChange();
_onValueChanged?.Invoke(PropertyInfo.Property);
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
OnEndChange();
}
}
public class ShaderGUIBitMaskItem : ShaderGUIItem
{
private static readonly string[] EmptyMaskNames = new string[0];
private readonly Func<GUIContent> _contentProvider;
private readonly Func<string[]> _maskNamesProvider;
private readonly Action<MaterialProperty> _onValueChanged;
private readonly Func<bool> _isVisible;
public ShaderGUIBitMaskItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem)
: base(rootItem, parentItem: parentItem)
{
}
public ShaderGUIBitMaskItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Func<string[]> maskNamesProvider,
Action<MaterialProperty> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_maskNamesProvider = maskNamesProvider;
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
MaskNames = _maskNamesProvider?.Invoke();
InitTriggerByChild();
}
public string[] MaskNames;
public int ValidMask = -1;
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
if (_contentProvider != null)
{
GuiContent = _contentProvider();
}
if (_maskNamesProvider != null)
{
MaskNames = _maskNamesProvider();
}
base.OnGUI();
}
public override void DrawController()
{
string[] maskNames = MaskNames ?? EmptyMaskNames;
int validMask = GetValidMask(maskNames);
int mask = Mathf.RoundToInt(PropertyInfo.Property.floatValue) & validMask;
int value = EditorGUI.MaskField(ControlRect, mask, maskNames) & validMask;
SetFloatIfDifferent(PropertyInfo.Property, value);
}
public override void OnEndChange()
{
base.OnEndChange();
_onValueChanged?.Invoke(PropertyInfo.Property);
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
OnEndChange();
}
private int GetValidMask(string[] maskNames)
{
if (ValidMask >= 0)
{
return ValidMask;
}
int optionCount = Mathf.Clamp(maskNames.Length, 0, 30);
return optionCount == 0 ? 0 : (1 << optionCount) - 1;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a4147e85b43d4bcaab43dedf111c41fb
timeCreated: 1758444036

View File

@@ -0,0 +1,288 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using NBShader;
namespace NBShaderEditor
{
public class ShaderPropertyInfo
{
public MaterialProperty Property;
public string Name;
public int Index;
}
public class ShaderGUIRootItem
{
private static int s_RendererCacheVersion;
private static double s_RendererCacheFirstDirtyTime = -1d;
private static double s_RendererCacheDirtyTime;
private const double RendererCacheHierarchyRefreshDelay = 0.35d;
private const double RendererCacheMaxHierarchyRefreshDelay = 2d;
private const float DefaultManualLayoutHeight = 1200f;
public Shader Shader;
public MaterialEditor MatEditor;
public List<Material> Mats;
public Dictionary<string,ShaderPropertyInfo> PropertyInfoDic = new Dictionary<string,ShaderPropertyInfo>();
public List<ShaderFlagsBase> ShaderFlags;//各个继承类各自初始化
public bool IsInit = true;
public Color DefaultBackgroundColor;
public bool ClearUnusedTextureReferencesRequested { get; private set; }
public List<Renderer> RenderersUsingThisMaterial = new List<Renderer>();
public List<ParticleSystemRenderer> ParticleRenderersUsingThisMaterial = new List<ParticleSystemRenderer>();
public bool IsUsedByParticleSystem { get; private set; }
private int _rendererCacheVersion = -1;
private Material _rendererCacheMaterial;
private readonly List<Material> _rendererMaterialBuffer = new List<Material>();
private bool _manualLayoutActive;
private Rect _manualLayoutRect;
private float _manualLayoutY;
private float _manualLayoutUsedHeight;
private float _manualLayoutHeight = DefaultManualLayoutHeight;
static ShaderGUIRootItem()
{
EditorApplication.hierarchyChanged += OnHierarchyChanged;
}
private static void OnHierarchyChanged()
{
double now = EditorApplication.timeSinceStartup;
s_RendererCacheVersion++;
if (s_RendererCacheFirstDirtyTime < 0d)
{
s_RendererCacheFirstDirtyTime = now;
}
s_RendererCacheDirtyTime = now;
}
public virtual void OnGUI(MaterialEditor editor,MaterialProperty[] props)
{
MatEditor = editor;
DefaultBackgroundColor = GUI.backgroundColor;
if (IsInit || ShouldRebuildMaterialTargets(editor.targets))
{
IsInit = true;
Mats = new List<Material>();
foreach (var obj in editor.targets)
{
Mats.Add(obj as Material);
}
InitFlags(Mats);
Shader = Mats[0].shader;
RefreshRendererCacheIfNeeded(true);
}
else
{
RefreshRendererCacheIfNeeded(false);
}
if (ShouldRebuildPropertyInfo(props))
{
PropertyInfoDic.Clear();
for (int i = 0; i < props.Length; i++)
{
ShaderPropertyInfo propInfo = new ShaderPropertyInfo();
propInfo.Property = props[i];
propInfo.Name = props[i].name;
propInfo.Index = i;
PropertyInfoDic.Add(propInfo.Name, propInfo);
}
}
else
{
for (int i = 0; i < props.Length; i++)
{
ShaderPropertyInfo propInfo = PropertyInfoDic[props[i].name];
propInfo.Property = props[i];
propInfo.Index = i;
}
}
try
{
BeginManualLayout();
OnChildOnGUI();
}
finally
{
EndManualLayout();
ClearUnusedTextureReferencesRequested = false;
}
RepaintWhenAnimationModeActive();
IsInit = false;
}
public void RequestClearUnusedTextureReferences()
{
ClearUnusedTextureReferencesRequested = true;
}
public Rect GetControlRect(float height = -1f)
{
float rectHeight = height >= 0f ? height : EditorGUIUtility.singleLineHeight;
if (!_manualLayoutActive)
{
return EditorGUILayout.GetControlRect(false, rectHeight);
}
Rect rect = new Rect(_manualLayoutRect.x, _manualLayoutY, _manualLayoutRect.width, rectHeight);
_manualLayoutY += rectHeight + EditorGUIUtility.standardVerticalSpacing;
_manualLayoutUsedHeight = Mathf.Max(
_manualLayoutUsedHeight,
_manualLayoutY - _manualLayoutRect.y - EditorGUIUtility.standardVerticalSpacing);
return rect;
}
public void Space(float height = -1f)
{
float spaceHeight = height >= 0f ? height : EditorGUIUtility.standardVerticalSpacing;
if (!_manualLayoutActive)
{
EditorGUILayout.Space();
return;
}
_manualLayoutY += spaceHeight;
_manualLayoutUsedHeight = Mathf.Max(_manualLayoutUsedHeight, _manualLayoutY - _manualLayoutRect.y);
}
private void BeginManualLayout()
{
float height = Mathf.Max(EditorGUIUtility.singleLineHeight, _manualLayoutHeight);
_manualLayoutRect = EditorGUILayout.GetControlRect(false, height);
_manualLayoutY = _manualLayoutRect.y;
_manualLayoutUsedHeight = 0f;
_manualLayoutActive = true;
}
private void EndManualLayout()
{
if (!_manualLayoutActive)
{
return;
}
_manualLayoutActive = false;
float usedHeight = Mathf.Max(EditorGUIUtility.singleLineHeight, _manualLayoutUsedHeight);
if (!Mathf.Approximately(_manualLayoutHeight, usedHeight))
{
_manualLayoutHeight = usedHeight;
MatEditor?.Repaint();
}
}
private bool ShouldRebuildPropertyInfo(MaterialProperty[] props)
{
if (PropertyInfoDic.Count != props.Length)
{
return true;
}
for (int i = 0; i < props.Length; i++)
{
if (!PropertyInfoDic.ContainsKey(props[i].name))
{
return true;
}
}
return false;
}
void RepaintWhenAnimationModeActive()
{
if (AnimationMode.InAnimationMode())
{
MatEditor.Repaint();
}
}
private bool ShouldRebuildMaterialTargets(UnityEngine.Object[] targets)
{
if (Mats == null || targets == null || Mats.Count != targets.Length)
{
return true;
}
for (int i = 0; i < targets.Length; i++)
{
if (Mats[i] != (targets[i] as Material))
{
return true;
}
}
return false;
}
private void RefreshRendererCacheIfNeeded(bool force)
{
Material material = Mats != null && Mats.Count > 0 ? Mats[0] : null;
if (!force && _rendererCacheMaterial == material && _rendererCacheVersion == s_RendererCacheVersion)
{
return;
}
double now = EditorApplication.timeSinceStartup;
bool waitingForQuietHierarchy = now - s_RendererCacheDirtyTime < RendererCacheHierarchyRefreshDelay;
bool withinMaxDelay = s_RendererCacheFirstDirtyTime >= 0d &&
now - s_RendererCacheFirstDirtyTime < RendererCacheMaxHierarchyRefreshDelay;
if (!force &&
_rendererCacheMaterial == material &&
waitingForQuietHierarchy &&
withinMaxDelay)
{
return;
}
CacheRenderersUsingThisMaterial(material);
_rendererCacheMaterial = material;
_rendererCacheVersion = s_RendererCacheVersion;
s_RendererCacheFirstDirtyTime = -1d;
}
void CacheRenderersUsingThisMaterial(Material material)
{
Renderer[] renderers = UnityObjectFindCompat.FindAll<Renderer>();
RenderersUsingThisMaterial.Clear();
ParticleRenderersUsingThisMaterial.Clear();
IsUsedByParticleSystem = false;
if (material == null || renderers == null)
{
return;
}
foreach (Renderer renderer in renderers)
{
if (renderer is ParticleSystemRenderer psr &&
(psr.sharedMaterial == material || psr.trailMaterial == material))
{
IsUsedByParticleSystem = true;
ParticleRenderersUsingThisMaterial.Add(psr);
}
_rendererMaterialBuffer.Clear();
renderer.GetSharedMaterials(_rendererMaterialBuffer);
for (int i = 0; i < _rendererMaterialBuffer.Count; i++)
{
if (_rendererMaterialBuffer[i] == material)
{
RenderersUsingThisMaterial.Add(renderer);
break;
}
}
}
_rendererMaterialBuffer.Clear();
}
public virtual void InitFlags(List<Material> mats) { } //各个子类各自实现
public virtual void OnChildOnGUI()
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e57b381204754ae780ab305e5feff475
timeCreated: 1758206205

View File

@@ -0,0 +1,498 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class TextureItem : ShaderGUIItem
{
private readonly string _texturePropertyName;
private readonly TexturePropertyGroupItem _groupItem;
private readonly Func<bool> _isVisible;
private readonly Action<MaterialProperty> _afterDraw;
public TextureItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string texturePropertyName,
Func<GUIContent> contentProvider,
string colorPropertyName = null,
bool drawScaleOffset = true,
Action<MaterialProperty> afterDraw = null,
Func<bool> isVisible = null,
Func<GUIContent> tillingContentProvider = null,
Func<GUIContent> offsetContentProvider = null) : base(rootItem, parentItem)
{
_texturePropertyName = texturePropertyName;
_isVisible = isVisible;
_afterDraw = afterDraw;
_groupItem = new TexturePropertyGroupItem(
rootItem,
this,
texturePropertyName,
colorPropertyName,
contentProvider,
drawScaleOffset,
tillingContentProvider: tillingContentProvider,
offsetContentProvider: offsetContentProvider);
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
_groupItem.ClearTextureIfRequested();
return;
}
_groupItem.OnGUI();
if (_afterDraw != null && RootItem.PropertyInfoDic.TryGetValue(_texturePropertyName, out ShaderPropertyInfo texturePropertyInfo))
{
_afterDraw(texturePropertyInfo.Property);
}
}
}
public class TexturePropertyGroupItem : ShaderGUIItem
{
private readonly TextureObjectItem _textureItem;
private readonly ColorLineItem _colorItem;
private readonly TextureScaleOffsetItem _scaleOffsetItem;
private readonly Func<bool> _isVisible;
public TexturePropertyGroupItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string texturePropertyName,
string colorPropertyName,
Func<GUIContent> contentProvider,
bool drawScaleOffset = true,
Func<bool> isVisible = null,
Func<GUIContent> tillingContentProvider = null,
Func<GUIContent> offsetContentProvider = null) : base(rootItem, parentItem)
{
_isVisible = isVisible;
_textureItem = new TextureObjectItem(rootItem, this, texturePropertyName, contentProvider);
_colorItem = string.IsNullOrEmpty(colorPropertyName)
? null
: new ColorLineItem(rootItem, this, colorPropertyName, false, contentProvider);
_scaleOffsetItem = drawScaleOffset
? new TextureScaleOffsetItem(
rootItem,
this,
texturePropertyName,
false,
tillingContentProvider: tillingContentProvider,
offsetContentProvider: offsetContentProvider)
: null;
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
_textureItem.ClearTextureIfRequested();
return;
}
float singleLineHeight = EditorGUIUtility.singleLineHeight;
float rowGap = EditorGUIUtility.standardVerticalSpacing;
float textureFieldHeight = 3f * singleLineHeight + 2f * rowGap;
float textureContentGap = EditorGUIUtility.standardVerticalSpacing;
Rect textureGroupRect = ApplyGlobalRectCompensation(LayoutRect(textureFieldHeight));
Rect textureLabelRow = new Rect(textureGroupRect.x, textureGroupRect.y, textureGroupRect.width, singleLineHeight);
Rect tillingRow = new Rect(textureGroupRect.x, textureGroupRect.y + singleLineHeight + rowGap, textureGroupRect.width, singleLineHeight);
Rect offsetRow = new Rect(textureGroupRect.x, tillingRow.y + singleLineHeight + rowGap, textureGroupRect.width, singleLineHeight);
Rect indentedTextureLabelRow = ApplyDirectLabelIndentWidth(textureLabelRow);
Rect textureRect = new Rect(indentedTextureLabelRow.x + 2f, textureLabelRow.y, textureFieldHeight, textureFieldHeight);
textureRect.x -= 2f;
textureRect.y += 2f;
float contentX = textureRect.x + textureRect.width + textureContentGap;
Rect textureLabelRect = MoveRectLeftKeepingRight(textureLabelRow, contentX);
SplitLineRect(tillingRow, out _, out Rect tillingVec2Rect, out Rect tillingResetRect, false);
Rect tillingLabelRect = MakeLabelRect(tillingRow, contentX, tillingVec2Rect.x);
tillingVec2Rect = ApplyControlIndentCompensation(tillingVec2Rect);
SplitLineRect(offsetRow, out _, out Rect offsetVec2Rect, out Rect offsetResetRect, false);
Rect offsetLabelRect = MakeLabelRect(offsetRow, contentX, offsetVec2Rect.x);
offsetVec2Rect = ApplyControlIndentCompensation(offsetVec2Rect);
_textureItem.Draw(textureRect, textureLabelRect);
_scaleOffsetItem?.Draw(tillingLabelRect, tillingVec2Rect, tillingResetRect, offsetLabelRect, offsetVec2Rect, offsetResetRect);
_colorItem?.OnGUI();
}
public void ClearTextureIfRequested()
{
_textureItem.ClearTextureIfRequested();
}
private static Rect MoveRectLeftKeepingRight(Rect rect, float x)
{
float xMax = rect.xMax;
rect.x = Mathf.Min(x, xMax);
rect.width = xMax - rect.x;
return rect;
}
private static Rect MakeLabelRect(Rect rowRect, float labelX, float controlX)
{
Rect labelRect = rowRect;
labelRect.x = Mathf.Min(labelX, controlX);
labelRect.width = Mathf.Max(0f, controlX - labelRect.x);
return labelRect;
}
private static Rect ApplyControlIndentCompensation(Rect rect)
{
rect.x -= ControlIndentCompensation;
rect.width += ControlIndentCompensation;
return rect;
}
}
public class TextureObjectItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public TextureObjectItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string texturePropertyName,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = texturePropertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
ClearTextureIfRequested();
return;
}
GetRect();
Draw(ControlRect, LabelRect, ResetRect);
DrawBlock();
}
public void Draw(Rect textureRect, Rect labelAndResetRect)
{
SplitControlAndResetRect(labelAndResetRect, out Rect labelRect, out Rect resetRect, false);
Draw(textureRect, labelRect, resetRect);
}
public void Draw(Rect textureRect, Rect labelRect, Rect resetRect)
{
ControlRect = textureRect;
LabelRect = labelRect;
ResetRect = resetRect;
MaterialProperty property = PropertyInfo.Property;
if (RootItem.ClearUnusedTextureReferencesRequested && (IsParentControlDisabled || !GUI.enabled))
{
ClearTextureIfRequested();
}
using (ParentControlDisabledScope())
{
GUI.Label(LabelRect, GuiContent, EditorStyles.boldLabel);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
Texture texture;
using (new EditorGUIIndentLevelScope(0))
{
texture = (Texture)EditorGUI.ObjectField(ControlRect, property.textureValue, typeof(Texture2D), false);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
property.textureValue = texture;
OnEndChange();
}
}
DrawResetButton();
}
public void ClearTextureIfRequested()
{
if (!RootItem.ClearUnusedTextureReferencesRequested || PropertyInfo == null)
{
return;
}
MaterialProperty property = PropertyInfo.Property;
if (property == null || property.textureValue == null)
{
return;
}
property.textureValue = null;
OnEndChange();
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = PropertyInfo == null ||
(!PropertyInfo.Property.hasMixedValue && PropertyInfo.Property.textureValue == null);
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
if (PropertyInfo != null)
{
PropertyInfo.Property.textureValue = null;
PropertyIsDefaultValue = true;
}
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class TextureScaleOffsetItem : ShaderGUIItem
{
private static readonly GUIContent TillingContent = new GUIContent("Tilling");
private static readonly GUIContent OffsetContent = new GUIContent("Offset");
private static readonly Vector4 DefaultScaleOffset = new Vector4(1f, 1f, 0f, 0f);
private readonly bool _isVectorProperty;
private readonly Func<bool> _isVisible;
private readonly Func<GUIContent> _tillingContentProvider;
private readonly Func<GUIContent> _offsetContentProvider;
private readonly GUIContent _tillingContent;
private readonly GUIContent _offsetContent;
public TextureScaleOffsetItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
bool isVectorProperty,
Func<bool> isVisible = null,
Func<GUIContent> tillingContentProvider = null,
Func<GUIContent> offsetContentProvider = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_isVectorProperty = isVectorProperty;
_isVisible = isVisible;
_tillingContentProvider = tillingContentProvider ?? (() => TillingContent);
_offsetContentProvider = offsetContentProvider ?? (() => OffsetContent);
_tillingContent = _tillingContentProvider();
_offsetContent = _offsetContentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
Rect tillingLabelRect = LabelRect;
Rect tillingVec2Rect = ControlRect;
Rect tillingResetRect = ResetRect;
GetRect();
Rect offsetLabelRect = LabelRect;
Rect offsetVec2Rect = ControlRect;
Rect offsetResetRect = ResetRect;
Draw(tillingLabelRect, tillingVec2Rect, tillingResetRect, offsetLabelRect, offsetVec2Rect, offsetResetRect, true);
DrawBlock();
}
public void Draw(
Rect tillingLabelRect,
Rect tillingVec2Rect,
Rect tillingResetRect,
Rect offsetLabelRect,
Rect offsetVec2Rect,
Rect offsetResetRect,
bool useEditorLabelField = false)
{
MaterialProperty property = PropertyInfo.Property;
Vector4 scaleOffset = GetScaleOffset(property);
DrawLabel(tillingLabelRect, _tillingContent, useEditorLabelField);
using (ParentControlDisabledScope())
{
Vector2 tilling = new Vector2(scaleOffset.x, scaleOffset.y);
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
bool tillingAnimatedScope = BeginAnimatedPropertyBackground(tillingVec2Rect, property);
using (new EditorGUIIndentLevelScope(0))
{
tilling = EditorGUI.Vector2Field(tillingVec2Rect, GUIContent.none, tilling);
}
EndAnimatedPropertyBackground(tillingAnimatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
scaleOffset.x = tilling.x;
scaleOffset.y = tilling.y;
Apply(scaleOffset);
}
bool tillingModified = property.hasMixedValue || tilling != Vector2.one;
if (GUI.Button(tillingResetRect, tillingModified ? "R" : string.Empty, tillingModified ? GUI.skin.button : GUI.skin.label))
{
scaleOffset.x = 1f;
scaleOffset.y = 1f;
Apply(scaleOffset);
}
}
DrawLabel(offsetLabelRect, _offsetContent, useEditorLabelField);
using (ParentControlDisabledScope())
{
Vector2 offset = new Vector2(scaleOffset.z, scaleOffset.w);
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
bool offsetAnimatedScope = BeginAnimatedPropertyBackground(offsetVec2Rect, property);
using (new EditorGUIIndentLevelScope(0))
{
offset = EditorGUI.Vector2Field(offsetVec2Rect, GUIContent.none, offset);
}
EndAnimatedPropertyBackground(offsetAnimatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
scaleOffset.z = offset.x;
scaleOffset.w = offset.y;
Apply(scaleOffset);
}
bool offsetModified = property.hasMixedValue || offset != Vector2.zero;
if (GUI.Button(offsetResetRect, offsetModified ? "R" : string.Empty, offsetModified ? GUI.skin.button : GUI.skin.label))
{
scaleOffset.z = 0f;
scaleOffset.w = 0f;
Apply(scaleOffset);
}
}
}
private void DrawLabel(Rect rect, GUIContent content, bool useEditorLabelField)
{
using (ParentControlDisabledScope())
{
if (useEditorLabelField)
{
EditorGUI.LabelField(rect, content);
return;
}
GUI.Label(rect, content);
}
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = true;
if (PropertyInfo != null)
{
MaterialProperty property = PropertyInfo.Property;
isDefaultValue = !property.hasMixedValue && Approximately(GetScaleOffset(property), DefaultScaleOffset);
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
if (PropertyInfo != null)
{
SetScaleOffset(PropertyInfo.Property, DefaultScaleOffset);
PropertyIsDefaultValue = true;
}
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
private Vector4 GetScaleOffset(MaterialProperty property)
{
return _isVectorProperty ? property.vectorValue : property.textureScaleAndOffset;
}
private void Apply(Vector4 scaleOffset)
{
SetScaleOffset(PropertyInfo.Property, scaleOffset);
OnEndChange();
}
private void SetScaleOffset(MaterialProperty property, Vector4 scaleOffset)
{
if (_isVectorProperty)
{
property.vectorValue = scaleOffset;
}
else
{
property.textureScaleAndOffset = scaleOffset;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7f026fa752b843a0b469f9306b2ff461
timeCreated: 1777032000

View File

@@ -0,0 +1,58 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ToggleItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
private readonly Action<bool> _onValueChanged;
public ToggleItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Action<bool> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void DrawController()
{
bool value = PropertyInfo.Property.floatValue > 0.5f;
value = EditorGUI.Toggle(ControlRect, value);
SetFloatIfDifferent(PropertyInfo.Property, value ? 1f : 0f);
}
public override void OnEndChange()
{
base.OnEndChange();
_onValueChanged?.Invoke(PropertyInfo.Property.floatValue > 0.5f);
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
_onValueChanged?.Invoke(PropertyInfo.Property.floatValue > 0.5f);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: afba064c4c70401bbb7133de835d2d35
timeCreated: 1777032000

View File

@@ -0,0 +1,610 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class Vector2LineItem : ShaderGUIItem
{
private readonly bool _firstLine;
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public Vector2LineItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
bool firstLine,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_firstLine = firstLine;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
MaterialProperty property = PropertyInfo.Property;
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Vector4 vector = property.vectorValue;
Vector2 value = _firstLine ? new Vector2(vector.x, vector.y) : new Vector2(vector.z, vector.w);
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.Vector2Field(ControlRect, GUIContent.none, value);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
if (_firstLine)
{
vector.x = value.x;
vector.y = value.y;
}
else
{
vector.z = value.x;
vector.w = value.y;
}
property.vectorValue = vector;
OnEndChange();
}
}
DrawResetButton();
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (PropertyInfo == null)
{
base.CheckIsPropertyModified(isCallByChild);
return;
}
if (!isCallByChild)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
Vector4 currentValue = PropertyInfo.Property.vectorValue;
bool isDefaultValue = _firstLine
? Mathf.Approximately(currentValue.x, defaultValue.x) && Mathf.Approximately(currentValue.y, defaultValue.y)
: Mathf.Approximately(currentValue.z, defaultValue.z) && Mathf.Approximately(currentValue.w, defaultValue.w);
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
Vector4 currentValue = PropertyInfo.Property.vectorValue;
if (_firstLine)
{
currentValue.x = defaultValue.x;
currentValue.y = defaultValue.y;
}
else
{
currentValue.z = defaultValue.z;
currentValue.w = defaultValue.w;
}
PropertyInfo.Property.vectorValue = currentValue;
PropertyIsDefaultValue = true;
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class VectorComponentItem : ShaderGUIItem
{
private readonly int _componentIndex;
private readonly bool _isSlider;
private readonly float _min;
private readonly float _max;
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public VectorComponentItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
int componentIndex,
Func<GUIContent> contentProvider,
bool isSlider,
float min = 0f,
float max = 1f,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_componentIndex = componentIndex;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isSlider = isSlider;
_min = min;
_max = max;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
MaterialProperty property = PropertyInfo.Property;
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Vector4 vector = property.vectorValue;
float value = GetValue(vector);
value = _isSlider
? DraggableLabelFloat.Handle(
LabelRect,
value,
DraggableLabelFloat.GetSensitivityByRange(_min, _max),
_min,
_max)
: DraggableLabelFloat.Handle(LabelRect, value);
if (_isSlider)
{
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.Slider(ControlRect, value, _min, _max);
}
EndAnimatedPropertyBackground(animatedScope);
}
else
{
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.FloatField(ControlRect, value);
}
EndAnimatedPropertyBackground(animatedScope);
}
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
SetValue(ref vector, value);
property.vectorValue = vector;
OnEndChange();
}
}
DrawResetButton();
}
private float GetValue(Vector4 vector)
{
switch (_componentIndex)
{
case 0: return vector.x;
case 1: return vector.y;
case 2: return vector.z;
case 3: return vector.w;
default: return vector.x;
}
}
private void SetValue(ref Vector4 vector, float value)
{
switch (_componentIndex)
{
case 0:
vector.x = value;
break;
case 1:
vector.y = value;
break;
case 2:
vector.z = value;
break;
case 3:
vector.w = value;
break;
}
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (PropertyInfo == null)
{
base.CheckIsPropertyModified(isCallByChild);
return;
}
if (!isCallByChild)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
bool isDefaultValue = Mathf.Approximately(GetValue(PropertyInfo.Property.vectorValue), GetValue(defaultValue));
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
Vector4 vector = PropertyInfo.Property.vectorValue;
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
SetValue(ref vector, GetValue(defaultValue));
PropertyInfo.Property.vectorValue = vector;
PropertyIsDefaultValue = true;
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class VectorComponentRangeSliderItem : ShaderGUIItem
{
private readonly int _componentIndex;
private readonly Func<GUIContent> _contentProvider;
private readonly string _rangePropertyName;
private readonly Func<bool> _isVisible;
private ShaderPropertyInfo _rangePropertyInfo;
private const float SliderFrontGap = 5f;
private const float SliderBackGap = 2f;
public VectorComponentRangeSliderItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
int componentIndex,
string rangePropertyName,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_componentIndex = componentIndex;
_rangePropertyName = rangePropertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void InitTriggerByChild()
{
if (!string.IsNullOrEmpty(_rangePropertyName))
{
_rangePropertyInfo = RootItem.PropertyInfoDic[_rangePropertyName];
}
base.InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
using (new EditorGUIIndentLevelScope(0))
{
DrawController();
}
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
OnEndChange();
}
}
DrawResetButton();
}
public override void DrawController()
{
Vector4 vector = PropertyInfo.Property.vectorValue;
Vector4 range = _rangePropertyInfo.Property.vectorValue;
float min = range.x;
float max = range.y;
float sliderMin = Mathf.Min(min, max);
float sliderMax = Mathf.Max(min, max);
float value = GetValue(vector);
float rangeFieldWidth = EditorGUIUtility.fieldWidth;
Rect minRect = ControlRect;
minRect.width = rangeFieldWidth;
Rect maxRect = ControlRect;
maxRect.x = ControlRect.xMax - rangeFieldWidth;
maxRect.width = rangeFieldWidth;
Rect sliderRect = ControlRect;
sliderRect.x = minRect.xMax + SliderFrontGap;
sliderRect.width = Mathf.Max(0f, maxRect.x - sliderRect.x - SliderBackGap + EditorGUI.indentLevel * UnityEditorGUIIndentWidth);
RangeVecHasMixedValue(out bool minValueHasMixed, out bool maxValueHasMixed);
EditorGUI.showMixedValue = minValueHasMixed;
bool minAnimatedScope = BeginAnimatedPropertyBackground(minRect, _rangePropertyInfo.Property);
min = EditorGUI.FloatField(minRect, min);
EndAnimatedPropertyBackground(minAnimatedScope);
EditorGUI.showMixedValue = maxValueHasMixed;
bool maxAnimatedScope = BeginAnimatedPropertyBackground(maxRect, _rangePropertyInfo.Property);
max = EditorGUI.FloatField(maxRect, max);
EndAnimatedPropertyBackground(maxAnimatedScope);
range.x = min;
range.y = max;
SetVectorIfDifferent(_rangePropertyInfo.Property, range);
sliderMin = Mathf.Min(min, max);
sliderMax = Mathf.Max(min, max);
value = DraggableLabelFloat.Handle(
LabelRect,
value,
DraggableLabelFloat.GetSensitivityByRange(sliderMin, sliderMax),
sliderMin,
sliderMax);
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
bool sliderAnimatedScope = BeginAnimatedPropertyBackground(sliderRect, PropertyInfo.Property);
value = SliderNoIndent(sliderRect, value, sliderMin, sliderMax);
EndAnimatedPropertyBackground(sliderAnimatedScope);
SetValue(ref vector, Mathf.Clamp(value, sliderMin, sliderMax));
SetVectorIfDifferent(PropertyInfo.Property, vector);
EditorGUI.showMixedValue = false;
}
private static float SliderNoIndent(Rect rect, float value, float min, float max)
{
int indentLevel = EditorGUI.indentLevel;
try
{
EditorGUI.indentLevel = 0;
return EditorGUI.Slider(rect, value, min, max);
}
finally
{
EditorGUI.indentLevel = indentLevel;
}
}
private void RangeVecHasMixedValue(out bool minValueHasMixed, out bool maxValueHasMixed)
{
minValueHasMixed = false;
maxValueHasMixed = false;
if (RootItem.Mats.Count <= 1)
{
return;
}
float firstMin = 0f;
float firstMax = 0f;
for (int i = 0; i < RootItem.Mats.Count; i++)
{
Vector4 range = RootItem.Mats[i].GetVector(_rangePropertyName);
if (i == 0)
{
firstMin = range.x;
firstMax = range.y;
continue;
}
if (!Mathf.Approximately(firstMin, range.x))
{
minValueHasMixed = true;
}
if (!Mathf.Approximately(firstMax, range.y))
{
maxValueHasMixed = true;
}
}
}
private float GetValue(Vector4 vector)
{
switch (_componentIndex)
{
case 0: return vector.x;
case 1: return vector.y;
case 2: return vector.z;
case 3: return vector.w;
default: return vector.x;
}
}
private void SetValue(ref Vector4 vector, float value)
{
switch (_componentIndex)
{
case 0:
vector.x = value;
break;
case 1:
vector.y = value;
break;
case 2:
vector.z = value;
break;
case 3:
vector.w = value;
break;
}
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
Vector4 defaultRange = RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
bool isDefaultValue = Mathf.Approximately(GetValue(PropertyInfo.Property.vectorValue), GetValue(defaultValue)) &&
_rangePropertyInfo.Property.vectorValue == defaultRange;
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
Vector4 vector = PropertyInfo.Property.vectorValue;
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
SetValue(ref vector, GetValue(defaultValue));
PropertyInfo.Property.vectorValue = vector;
_rangePropertyInfo.Property.vectorValue = RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
PropertyIsDefaultValue = true;
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class Vector3Item : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
private readonly Action<Vector3> _onValueChanged;
public Vector3Item(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Action<Vector3> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
MaterialProperty property = PropertyInfo.Property;
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Vector4 vector = property.vectorValue;
Vector3 value = new Vector3(vector.x, vector.y, vector.z);
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.Vector3Field(ControlRect, GUIContent.none, value);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
vector.x = value.x;
vector.y = value.y;
vector.z = value.z;
property.vectorValue = vector;
OnEndChange();
_onValueChanged?.Invoke(value);
}
}
DrawResetButton();
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
Vector4 vector = PropertyInfo.Property.vectorValue;
_onValueChanged?.Invoke(new Vector3(vector.x, vector.y, vector.z));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 20a960b448954fc29c9197c2357e89ec
timeCreated: 1777032000

View File

@@ -0,0 +1,426 @@
using System.Collections.Generic;
using System;
using UnityEngine;
using UnityEngine.Rendering;
using ShaderPropertyPack = NBShaderEditor.ShaderGUIHelper.ShaderPropertyPack;
using UnityEditor;
namespace NBShaderEditor
{
public class ShaderGUIResetTool
{
private ShaderGUIHelper _helper;
private Shader _shader;
public bool IsInitResetData = false;
private Stack<(string,string)> _scopeContextStack = new Stack<(string,string)>();
public void CheckAllModifyOnValueChange()
{
foreach (var item in ResetItemDict.Values)
{
item.HasModified = item.CheckHasModifyOnValueChange();
}
}
public void Init(ShaderGUIHelper helper)
{
_helper = helper;
_shader = helper.shader;
IsInitResetData = true;
ResetItemDict.Clear();
_scopeContextStack.Clear();
}
public void EndInit()
{
IsInitResetData = false;
}
public void Update()
{
if (_needUpdate)
{
_needUpdate = false;
CheckAllModifyOnValueChange();
}
}
private bool _needUpdate = false;
public void NeedUpdate()
{
_needUpdate = true;
}
public ShaderGUIResetTool(ShaderGUIHelper helper)
{
Init(helper);
}
public Dictionary<(string, string), ResetItem> ResetItemDict = new Dictionary<(string, string), ResetItem>();
public class ResetItem
{
public ResetItem Parent;
public List<ResetItem> ChildResetItems = new List<ResetItem>();
public Action ResetCallBack;
public Action OnValueChangedCallBack;
public Func<bool> CheckHasModifyOnValueChange;
public Func<bool> CheckHasMixedValueOnValueChange;
public (string, string) NameTuple;
public bool HasModified = false;
public bool HasMixedValue =false;
public bool ChildHasModified = false;
public bool ChildHasMixedValue = false;
public void Init((string, string) nameTuple,Action resetCallBack,Action onValueChangedCallBack,Func<bool> checkHasModifyOnValueChange,Func<bool> checkHasMixedValueOnValueChange)
{
NameTuple = nameTuple;
ResetCallBack = resetCallBack;
OnValueChangedCallBack = onValueChangedCallBack;
CheckHasModifyOnValueChange = checkHasModifyOnValueChange;
CheckHasMixedValueOnValueChange = checkHasMixedValueOnValueChange;
}
public void Execute(bool isParentCall = false)
{
ResetCallBack?.Invoke();
OnValueChangedCallBack?.Invoke();
HasModified = CheckHasModifyOnValueChange();
HasMixedValue = CheckHasMixedValueOnValueChange();
CheckOnValueChange(isParentCall);
foreach (var item in ChildResetItems)
{
item.Execute(true);
}
}
public void CheckOnValueChange(bool isParentCall = false)
{
HasModified = CheckHasModifyOnValueChange();
HasMixedValue = CheckHasMixedValueOnValueChange();
foreach (var childItem in ChildResetItems)
{
HasMixedValue |= childItem.HasMixedValue;
HasModified |= childItem.HasModified;
}
if (!isParentCall && Parent != null)
{
Parent.CheckOnValueChange();
}
}
}
public void CheckOnValueChange((string,string) nameTuple)
{
if (ResetItemDict.ContainsKey(nameTuple))
{
ResetItemDict[nameTuple].CheckOnValueChange();
}
else
{
Debug.LogError("不包含ResetItemDict:"+nameTuple);
}
}
public void DrawResetModifyButton(Rect rect,(string, string) nameTuple, Action resetCallBack,
Action onValueChangedCallBack,Func<bool> checkHasModifyOnValueChange,Func<bool> checkHasMixedValueOnValueChange,bool isSharedGlobalParent = false)
{
ConstructResetItem(nameTuple,resetCallBack,onValueChangedCallBack,checkHasModifyOnValueChange,checkHasMixedValueOnValueChange,isSharedGlobalParent);
DrawResetModifyButtonFinal(rect,nameTuple);
}
public void DrawResetModifyButton(Rect rect,(string,string)nameTuple,ShaderPropertyPack pack, Action resetAction,Action onValueChangedCallBack,VectorValeType vectorValeType = VectorValeType.Undefine,bool isSharedGlobalParent = false)
{
// (string, string) nameTuple = (label, pack.property.name);
ConstructResetItem(nameTuple,
resetAction: ()=>{
SetPropertyToDefaultValue(pack,vectorValeType);
resetAction?.Invoke();
},onValueChangedCallBack:onValueChangedCallBack,
checkHasModifyOnValueChange: () => IsPropertyModified(pack,vectorValeType),
checkHasMixedValueOnValueChange: () => pack.property.hasMixedValue,
isSharedGlobalParent: isSharedGlobalParent
);
if (ResetItemDict.ContainsKey(nameTuple))
{
DrawResetModifyButtonFinal(rect,nameTuple);
}
}
public void DrawResetModifyButton(Rect rect, string label)
{
//大部分功能都是触发子类
ConstructResetItem((label, ""), resetAction: () => { },onValueChangedCallBack: () => { }, () => false, () => false);
DrawResetModifyButtonFinal(rect,(label,""));
}
//isSharedGlobalParent==>有些组件是公用的比如极坐标一类。这些是不会设置父Item的。
public void ConstructResetItem((string,string) nameTuple, Action resetAction,
Action onValueChangedCallBack,Func<bool> checkHasModifyOnValueChange,Func<bool> checkHasMixedValueOnValueChange,bool isSharedGlobalParent = false)
{
if(!IsInitResetData) return;
if (!ResetItemDict.ContainsKey(nameTuple))
{
ResetItem item = new ResetItem();
item.Init(nameTuple,resetAction,onValueChangedCallBack,checkHasModifyOnValueChange,checkHasMixedValueOnValueChange);
ResetItemDict.Add(nameTuple,item);
if (_scopeContextStack.Count > 0 && !isSharedGlobalParent)
{
var contextNameTuple = _scopeContextStack.Peek();
ResetItem parentItem = ResetItemDict[contextNameTuple];
parentItem.ChildResetItems.Add(item);
item.Parent = parentItem;
}
if (_helper.matEditor.targets.Length == 1)
{
onValueChangedCallBack?.Invoke();//属性值初始化
}
item.CheckOnValueChange();//Init
}
else
{
// Debug.LogError("ResetItem已经存在:"+nameTuple.ToString());
}
//就算是已经ContainsKey了也Push和Pop一下。没有作用但让写法更简单。
_scopeContextStack.Push(nameTuple);
}
public void EndResetModifyButtonScope()
{
if(!IsInitResetData) return;
if(_scopeContextStack.Count == 0) return;
_scopeContextStack.Pop();
}
public float ResetButtonSize => EditorGUIUtility.singleLineHeight;
private GUIContent resetIconContent = new GUIContent();
//仅仅只是Drawer
private void DrawResetModifyButtonFinal(Rect position, (string, string) nameTuple)
{
ResetItem item;
// GUILayout.FlexibleSpace();
if (ResetItemDict.ContainsKey(nameTuple))
{
item = ResetItemDict[nameTuple];
}
else
{
return;
}
float btnSize = ResetButtonSize;
string iconText;
bool isDisabled = true;
GUIStyle iconStyle;
if (item.HasModified || item.HasMixedValue)
{
isDisabled = false;
iconText = "R";
iconStyle = GUI.skin.button;
}
else
{
isDisabled = true;
iconText = "";
iconStyle = GUI.skin.label;
}
resetIconContent.text = iconText;
EditorGUI.BeginDisabledGroup(isDisabled);
// if (GUILayout.Button(iconTexture, GUILayout.Width(btnSize), GUILayout.Height(btnSize)))
if (position.width <= 0)
{
position = GUILayoutUtility.GetRect(resetIconContent, GUI.skin.button, GUILayout.Width(btnSize),
GUILayout.Height(btnSize));
}
if(GUI.Button(position,resetIconContent,iconStyle))
{
item.Execute();
}
EditorGUI.EndDisabledGroup();
}
public void SetPropertyToDefaultValue(ShaderPropertyPack pack,VectorValeType vectorValeType = VectorValeType.Undefine)
{
MaterialProperty property = pack.property;
ShaderPropertyType propertyType = ShaderGUIUnityCompat.GetPropertyType(property);
if (propertyType == ShaderPropertyType.Texture && vectorValeType != VectorValeType.Undefine)
{
propertyType = ShaderPropertyType.Vector;//Tilling or Offset
}
switch (propertyType)
{
case ShaderPropertyType.Color:
Vector4 colorValue = _shader.GetPropertyDefaultVectorValue(pack.index);
property.colorValue = new Color(colorValue.x, colorValue.y, colorValue.z, colorValue.x);
break;
case ShaderPropertyType.Vector:
Vector4 defaultVecValue;
Vector4 vecValue;
if (vectorValeType == VectorValeType.Tilling || vectorValeType == VectorValeType.Offset)
{
defaultVecValue = new Vector4(1f, 1f, 0f, 0f);
vecValue = property.textureScaleAndOffset;
}
else
{
defaultVecValue = _shader.GetPropertyDefaultVectorValue(pack.index);
vecValue = property.vectorValue;
}
switch (vectorValeType)
{
case VectorValeType.Undefine: Debug.LogError("VectorValeType is undefined"); break;
case VectorValeType.X: vecValue.x = defaultVecValue.x;property.vectorValue = vecValue;break;
case VectorValeType.Y: vecValue.y = defaultVecValue.y;property.vectorValue = vecValue;break;
case VectorValeType.Z: vecValue.z = defaultVecValue.z;property.vectorValue = vecValue;break;
case VectorValeType.W: vecValue.w = defaultVecValue.w;property.vectorValue = vecValue;break;
case VectorValeType.XY:vecValue.x = defaultVecValue.x; vecValue.y = defaultVecValue.y;
property.vectorValue = vecValue;break;
case VectorValeType.Tilling:vecValue.x = defaultVecValue.x; vecValue.y = defaultVecValue.y;
if (ShaderGUIUnityCompat.GetPropertyType(property) == ShaderPropertyType.Texture)
{
property.textureScaleAndOffset = vecValue;
}
else
{
property.vectorValue = vecValue;
}
break;
case VectorValeType.ZW:vecValue.z = defaultVecValue.z; vecValue.w = defaultVecValue.w;
property.vectorValue = vecValue;break;
case VectorValeType.Offset:vecValue.z = defaultVecValue.z; vecValue.w = defaultVecValue.w;
if (ShaderGUIUnityCompat.GetPropertyType(property) == ShaderPropertyType.Texture)
{
property.textureScaleAndOffset = vecValue;
}
else
{
property.vectorValue = vecValue;
}
break;
case VectorValeType.XYZ:vecValue.x = defaultVecValue.x; vecValue.y = defaultVecValue.y;
vecValue.z = defaultVecValue.z; property.vectorValue = vecValue;break;
case VectorValeType.XYZW: property.vectorValue = defaultVecValue;break;
}
break;
case ShaderPropertyType.Float or ShaderPropertyType.Range:
float value = _shader.GetPropertyDefaultFloatValue(pack.index);
property.floatValue = value;
break;
case ShaderPropertyType.Texture:
if (property.textureValue == null)
{
break;
}
else
{
property.textureValue = null;
break;
}
// return property.textureValue.name == shader.GetPropertyTextureDefaultName(pack.index) ? false : true;
default:
// 如果不属于上述类型,输出提示信息
Debug.Log($"{property.displayName} has no default value or unsupported type");
break;
}
}
public bool IsPropertyModified(ShaderPropertyPack pack,VectorValeType vectorValeType = VectorValeType.Undefine)
{
MaterialProperty property = pack.property;
ShaderPropertyType propertyType = ShaderGUIUnityCompat.GetPropertyType(property);
if (propertyType == ShaderPropertyType.Texture && vectorValeType != VectorValeType.Undefine)
{
propertyType = ShaderPropertyType.Vector;//Tilling or Offset
}
switch (propertyType)
{
case ShaderPropertyType.Color:
Vector4 colorValue = _shader.GetPropertyDefaultVectorValue(pack.index);
Color color = new Color(colorValue.x, colorValue.y, colorValue.z, colorValue.w);
return property.colorValue == color ? false : true;
case ShaderPropertyType.Vector:
Vector4 defaultVecValue;
Vector4 vecValue;
if (vectorValeType == VectorValeType.Tilling || vectorValeType == VectorValeType.Offset)
{
defaultVecValue = new Vector4(1f, 1f, 0f, 0f);
if (ShaderGUIUnityCompat.GetPropertyType(property) == ShaderPropertyType.Texture)
{
vecValue = property.textureScaleAndOffset;
}
else
{
vecValue = property.vectorValue;
}
}
else
{
defaultVecValue = _shader.GetPropertyDefaultVectorValue(pack.index);
vecValue = property.vectorValue;
}
Vector2 defaultVecXYValue = new Vector2(defaultVecValue.x, defaultVecValue.y);
Vector2 defaultVecZWValue = new Vector2(defaultVecValue.z, defaultVecValue.w);
Vector2 vecXYValue = new Vector2(vecValue.x, vecValue.y);
Vector2 vecZWValue = new Vector2(vecValue.z, vecValue.w);
Vector2 defaultVecXYZValue = new Vector3(defaultVecValue.x, defaultVecValue.y,defaultVecValue.z);
Vector2 vecXYZValue = new Vector3(vecValue.x, vecValue.y,vecValue.z);
bool isVecModified = false;
switch (vectorValeType)
{
case VectorValeType.Undefine: Debug.LogError("VectorValeType is undefined"); break;
case VectorValeType.X: isVecModified = Mathf.Approximately(vecValue.x,defaultVecValue.x) ? false : true;break;
case VectorValeType.Y: isVecModified = Mathf.Approximately(vecValue.y,defaultVecValue.y) ? false : true;break;
case VectorValeType.Z: isVecModified = Mathf.Approximately(vecValue.z,defaultVecValue.z) ? false : true;break;
case VectorValeType.W: isVecModified = Mathf.Approximately(vecValue.w,defaultVecValue.w) ? false : true;break;
case VectorValeType.XY:case VectorValeType.Tilling:
isVecModified = vecXYValue == defaultVecXYValue ? false : true;break;
case VectorValeType.ZW:case VectorValeType.Offset:
isVecModified = vecZWValue == defaultVecZWValue ? false : true;break;
case VectorValeType.XYZ:isVecModified = vecXYZValue == defaultVecXYZValue ? false : true ; break;
case VectorValeType.XYZW:isVecModified= vecValue == defaultVecValue? false : true ; break;
}
return isVecModified;
case ShaderPropertyType.Float or ShaderPropertyType.Range:
return Mathf.Approximately(property.floatValue, _shader.GetPropertyDefaultFloatValue(pack.index)) ? false : true;
case ShaderPropertyType.Texture:
if (property.textureValue == null)
{
return false;
}
else
{
return property.textureValue.name == "textureExternal" ? false : true;
}
// return property.textureValue.name == shader.GetPropertyTextureDefaultName(pack.index) ? false : true;
default:
// 如果不属于上述类型,输出提示信息
return false;
// Debug.Log($"{property.displayName} has no default value or unsupported type");
// break;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 73135babda09451c9e5798b36897637b
timeCreated: 1754727843

View File

@@ -0,0 +1,250 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.Rendering;
using System.Collections.Generic;
using NBShader;
namespace NBShaderEditor
{
public class ShaderGUIToolBar
{
public ShaderGUIHelper Helper;
private int viewModeIndex;
private readonly string[] viewModes = { "List", "Grid" };
// private string searchText = "";
private MaterialEditor _editor => Helper.matEditor;
public ShaderGUIToolBar(ShaderGUIHelper helper)
{
Helper = helper;
}
private static Material copiedMaterial;
private static Shader copiedShader;
// 帮助链接URL
private const string HELP_URL = "https://owejt9diz2c.feishu.cn/wiki/BHz8wHHSjiYJagk7WrmcAcconlb?from=from_copylink";
// private Vector2 imagePos;
// private Texture2D icon;
// private Texture2D image;
public void DrawToolbar()
{
float BtnWidth = 30f;
// 开始工具栏区域 (背景)
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
// 1. 选择当前材质
if (GUILayout.Button(EditorGUIUtility.IconContent("Material On Icon","跳到当前材质|跳到当前材质"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
{
EditorGUIUtility.PingObject(Helper.mats[0]);
}
if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Trash","清除没有使用的贴图|清除没有使用的贴图"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
{
foreach (var mat in Helper.mats)
{
CleanUnusedTextureProperties(Helper.mats[0]);//先清理不属于当前Shader的贴图
}
Helper.isClearUnUsedTexture = true;
}
if (GUILayout.Button(new GUIContent("C","复制材质属性"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
{
copiedMaterial = Helper.mats[0];
copiedShader = copiedMaterial.shader;
}
if (GUILayout.Button(new GUIContent("V","粘贴材质属性"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
{
if (copiedShader)
{
Helper.mats[0].shader = copiedShader;
}
if (copiedMaterial)
{
Helper.mats[0].CopyPropertiesFromMaterial(copiedMaterial);
}
}
if (GUILayout.Button(new GUIContent("R","特殊重置功能"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
{
ShowResetPopupMenu();
}
if (GUILayout.Button(EditorGUIUtility.IconContent("d_UnityEditor.HierarchyWindow","折叠所有控件|折叠所有控件"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
{
for (int i = 0;i<Helper.shaderFlags.Length;i++)
{
ShaderFlagsBase shaderFlags = Helper.shaderFlags[i];
for (int j = 3; j <= 5; j++)
{
Helper.mats[i].SetInteger(shaderFlags.GetShaderFlagsId(j),0);
}
}
}
// 2. 添加下拉菜单
// viewModeIndex = EditorGUILayout.Popup(viewModeIndex, viewModes, EditorStyles.toolbarPopup, GUILayout.Width(BtnWidth));
// 3. 添加搜索框
GUILayout.FlexibleSpace(); // 将搜索框推到中间
// // 搜索框样式
// GUIStyle searchField = new GUIStyle("SearchTextField");
// GUIStyle cancelButton = new GUIStyle("SearchCancelButton");
//
// EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(300));
// {
// EditorGUI.BeginChangeCheck();
// searchText = EditorGUILayout.TextField(searchText, searchField);
// if (EditorGUI.EndChangeCheck())
// {
// Helper.isSearchText = searchText.Length > 0;
// Helper.searchText = searchText;
// }
//
// // 清除搜索按钮
// if (GUILayout.Button("", cancelButton))
// {
// searchText = "";
// GUI.FocusControl(null); // 移除焦点
// }
// }
// EditorGUILayout.EndHorizontal();
// // 4. 右侧按钮组
// if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Refresh"), EditorStyles.toolbarButton))
// {
// Debug.Log("Refresh clicked");
// }
//
// // 选项菜单
// if (GUILayout.Button(EditorGUIUtility.IconContent("_Popup"), EditorStyles.toolbarButton))
// {
// // 创建下拉菜单
// GenericMenu menu = new GenericMenu();
// menu.AddItem(new GUIContent("Option 1"), false, () => Debug.Log("Option 1"));
// menu.AddItem(new GUIContent("Option 2"), false, () => Debug.Log("Option 2"));
// menu.ShowAsContext();
// }
// 贴图加载
// if (icon == null)
// icon = AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath("eaa39f504c2ce7646aece103ba9c4766"));
// if (image == null)
// image = AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath("cc6c30349a33a1d4c8242a9ef1a68830"));
// if (GUILayout.Button(new GUIContent(icon,"爸爸!"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
// {
// // 弹出 PopupWindow
// // 弹出浮动窗口
// FloatingImageWindow.ShowWindow(image);
// }
if (GUILayout.Button(EditorGUIUtility.IconContent("d__Help@2x","说明文档|说明文档"), EditorStyles.toolbarButton,GUILayout.Width(BtnWidth)))
{
// 打开浏览器跳转到帮助链接
Application.OpenURL(HELP_URL);
}
EditorGUILayout.EndHorizontal(); // 结束工具栏
}
private void ShowResetPopupMenu()
{
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("重置特殊UV通道"), false, () =>
{
Helper.ResetTool.ResetItemDict[("特殊UV通道选择","_SpecialUVChannelMode")].Execute();
});
menu.AddItem(new GUIContent("重置旋转扭曲"), false, () =>
{
Helper.ResetTool.ResetItemDict[("","_UTwirlEnabled")].Execute();
});
menu.AddItem(new GUIContent("重置极坐标"), false, () =>
{
Helper.ResetTool.ResetItemDict[("","_PolarCoordinatesEnabled")].Execute();
});
// 弹出位置可以用 Event.current.mousePosition
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
}
private void CleanUnusedTextureProperties(Material mat)
{
if (mat == null || mat.shader == null) return;
Shader shader = mat.shader;
// 收集 Shader 里声明过的贴图属性
var shaderTexProps = new HashSet<string>();
int count = shader.GetPropertyCount();
for (int i = 0; i < count; i++)
{
if (shader.GetPropertyType(i) == ShaderPropertyType.Texture)
{
shaderTexProps.Add(shader.GetPropertyName(i));
}
}
// 遍历材质所有贴图属性,找到 shader 不再声明的
var allProps = mat.GetTexturePropertyNames();
foreach (var propName in allProps)
{
if (!shaderTexProps.Contains(propName))
{
if (mat.GetTexture(propName) != null)
{
mat.SetTexture(propName, null);
Debug.Log($"清理 {mat.name} 的无效贴图属性: {propName}");
}
}
}
}
// EditorWindow 显示图片
public class FloatingImageWindow : EditorWindow
{
private Texture2D popupImage;
public static void ShowWindow(Texture2D image)
{
// 创建窗口
FloatingImageWindow window = CreateInstance<FloatingImageWindow>();
window.titleContent = new GUIContent("谢谢爸爸");
window.popupImage = image;
// 设置初始尺寸
if (image != null)
window.position = new Rect(Screen.width / 2f - image.width / 2f,
Screen.height / 2f - image.height / 2f,
image.width,
image.height);
else
window.position = new Rect(Screen.width / 2f - 100, Screen.height / 2f - 100, 200, 200);
window.ShowUtility(); // 浮动窗口
}
private void OnGUI()
{
if (popupImage != null)
{
// 绘制图片
Rect rect = new Rect(0, 0, position.width, position.height);
GUI.DrawTexture(rect, popupImage, ScaleMode.ScaleToFit);
}
// 可选:增加关闭按钮
if (GUI.Button(new Rect(position.width - 25, 5, 20, 20), "X"))
{
Close();
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: deccdbc6963a48b2ace3358cbba67689
timeCreated: 1754406958

View File

@@ -0,0 +1,165 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace NBShaderEditor
{
[Serializable]
public class StencilValues
{
public int DefaultQueue = 2000;
[BinaryInt(8, true, 2)] public int Ref = 0;
public CompareFunction Comp = CompareFunction.Always;
public StencilOp Pass = StencilOp.Keep;
public StencilOp Fail = StencilOp.Keep;
public StencilOp ZFail = StencilOp.Keep;
[BinaryInt(8, true, 1)] public int ReadMask = 255;
[BinaryInt(8, true, 1)] public int WriteMask = 255;
}
public static class StencilTestHelper
{
// private static StencilValuesConfig stencilValuesConfig;
public class StencilPropertyNames
{
public string stencil = "_Stencil";
public string stencilComp = "_StencilComp";
public string stencilOp = "_StencilOp";
public string stencilWriteMask = "_StencilWriteMask";
public string stencilReadMask = "_StencilReadMask";
public string stencilZFail = "_StencilZFail";
public string stencilFail = "_StencilFail";
public string stencilKexIndex = "_StencilKeyIndex";
public StencilPropertyNames()
{
}
public StencilPropertyNames(string stencilName, string stencilCompName, string stencilOpName,
string stencilWriteMaskName, string stencilReadMaskName, string stencilZFailName,
string stencilFailName, string stencilKexIndexName)
{
if (!string.IsNullOrEmpty(stencilName))
{
stencil = stencilName;
}
if (!string.IsNullOrEmpty(stencilCompName))
{
stencilComp = stencilCompName;
}
if (!string.IsNullOrEmpty(stencilOpName))
{
stencilOp = stencilOpName;
}
if (!string.IsNullOrEmpty(stencilWriteMaskName))
{
stencilWriteMask = stencilWriteMaskName;
}
if (!string.IsNullOrEmpty(stencilReadMaskName))
{
stencilReadMask = stencilReadMaskName;
}
if (!string.IsNullOrEmpty(stencilZFailName))
{
stencilZFail = stencilZFailName;
}
if (!string.IsNullOrEmpty(stencilFailName))
{
stencilFail = stencilFailName;
}
if (!string.IsNullOrEmpty(stencilKexIndexName))
{
stencilKexIndex = stencilKexIndexName;
}
}
}
private static StencilPropertyNames defaultStencilPropertyNames = new StencilPropertyNames();
// public static void SetMaterialStencil(Material mat,StencilValues stencilValues,out int defaultQueue)
public static void SetMaterialStencil(Material mat, string stencilConfigKey,
StencilValuesConfig stencilValuesConfig, out int defaultQueue,
StencilPropertyNames stencilPropertyNames = null)
{
if (stencilValuesConfig == null)
{
Debug.LogError(mat.name + ": 缺少模板预设,设置Stencil失败");
// stencilValuesConfig =
// AssetDatabase.LoadAssetAtPath<StencilValuesConfig>(
// "Assets/AddressableAssets/Shader/StencilConfig.asset");
}
if (stencilPropertyNames == null)
{
stencilPropertyNames = defaultStencilPropertyNames;
}
StencilValues stencilValues;
if (stencilValuesConfig.ContainsKey(stencilConfigKey))
{
stencilValues = stencilValuesConfig[stencilConfigKey];
if (!string.IsNullOrEmpty(stencilPropertyNames.stencil))
{
mat.SetFloat(stencilPropertyNames.stencil, stencilValues.Ref);
}
if (!string.IsNullOrEmpty(stencilPropertyNames.stencilComp))
{
mat.SetFloat(stencilPropertyNames.stencilComp, (float)stencilValues.Comp);
}
if (!string.IsNullOrEmpty(stencilPropertyNames.stencilOp))
{
mat.SetFloat(stencilPropertyNames.stencilOp, (float)stencilValues.Pass);
}
if (!string.IsNullOrEmpty(stencilPropertyNames.stencilWriteMask))
{
mat.SetFloat(stencilPropertyNames.stencilWriteMask, stencilValues.WriteMask);
}
if (!string.IsNullOrEmpty(stencilPropertyNames.stencilReadMask))
{
mat.SetFloat(stencilPropertyNames.stencilReadMask, stencilValues.ReadMask);
}
if (!string.IsNullOrEmpty(stencilPropertyNames.stencilZFail))
{
mat.SetFloat(stencilPropertyNames.stencilZFail, (float)stencilValues.ZFail);
}
if (!string.IsNullOrEmpty(stencilPropertyNames.stencilFail))
{
mat.SetFloat(stencilPropertyNames.stencilFail, (float)stencilValues.Fail);
}
if (!string.IsNullOrEmpty(stencilPropertyNames.stencilKexIndex))
{
mat.SetFloat(stencilPropertyNames.stencilKexIndex,
stencilValuesConfig.GetKeyIndex(stencilConfigKey));
}
defaultQueue = stencilValues.DefaultQueue;
}
else
{
Debug.LogError("无法设置材质模板参数,因为没有配置模板值", mat);
defaultQueue = mat.renderQueue;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7d6268a1ca7d824d8da2adf4cb8f74a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using NBShader;
namespace NBShaderEditor
{
public class StencilValuesConfig : ScriptableObject
{
// public Dictionary<string, StencilValues> Config = new Dictionary<string, StencilValues>();
// public StencilValuesConfigDictionary Config = new StencilValuesConfigDictionary();
[Serializable]
public class KeyStencilValues
{
public string key;
public StencilValues Values;
}
[SerializeField]
public List<KeyStencilValues> Config = new List<KeyStencilValues>();
public bool ContainsKey(string key)
{
if (GetStencilValues(key) != null)
{
return true;
}
else
{
return false;
}
}
public StencilValues GetStencilValues(string key)
{
foreach (var item in Config)
{
if (item.key == key)
{
return item.Values;
}
}
Debug.LogError("StencilValuesConfig: 不存在Key"+key);
return null;
}
public int GetKeyIndex(string key)
{
for (int i = 0; i < Config.Count; i++)
{
if (Config[i].key == key)
{
return i;
}
}
Debug.LogError("StencilValuesConfig: 不存在Key"+key);
return -1;
}
public string GetKeyByIndex(int index)
{
return Config[index].key;
}
public StencilValues this[string key]
{
get
{
return GetStencilValues(key);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9177edf81a5b2354490ec4d66c4c069d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEditor;
public class StringListSerchProvider : ScriptableObject , ISearchWindowProvider
// public class StringListSerchProvider
{
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
List<SearchTreeEntry> serchList = new List<SearchTreeEntry>();
serchList.Add(new SearchTreeGroupEntry(new GUIContent("List"),0));
List<string> listItemAfterSort = listItems.ToList();
sortListItems(ref listItemAfterSort);
List<string> groups = new List<string>();
foreach (var item in listItemAfterSort)
{
string[] entryTitle = item.Split("/");
string groupName = "";
for (int i = 0; i < entryTitle.Length - 1; i++)
{
groupName += entryTitle[i];
if (!groups.Contains(groupName))
{
serchList.Add(new SearchTreeGroupEntry(new GUIContent(entryTitle[i]),i+1));
}
groupName += "/";
}
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(entryTitle.Last()));
entry.level = entryTitle.Length;
entry.userData = item;//这里是serch操作点击最后的返回值是什么都可以的。
// Debug.Log(entry.userData);
serchList.Add(entry);
}
return serchList;
}
public bool OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context)
{
string data = (string)searchTreeEntry.userData;
onSetIndexCallback?.Invoke(data);
return true;
}
private string[] listItems;
private Action<string> onSetIndexCallback;
public StringListSerchProvider(string[] items, Action<string> callBack)
{
listItems = items;
onSetIndexCallback = callBack;
}
public void Initialize(string[] items, Action<string> callBack)
{
listItems = items;
onSetIndexCallback = callBack;
}
// private List<string> sortListItems;
void sortListItems(ref List<string> list)
{
// sortListItems = listItems.ToList();
list.Sort((a, b) =>
{
string[] splits1 = a.Split('/');
string[] splits2 = b.Split('/');
for (int i = 0; i < splits1.Length; i++)
{
if (i >= splits2.Length)
{
return 1;
}
int value = splits1[i].CompareTo(splits2[i]);
if (value != 0)
{
if (splits1.Length != splits2.Length && (i == splits1.Length - 1 || i == splits2.Length - 1))
{
return splits1.Length < splits2.Length ? 1 : -1;
}
return value;
}
}
return 0;
});
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aec459410f5254c4bb4d42e7ec8b4b5e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,124 @@
using System;
using System.IO;
using UnityEditor;
namespace NBShader.Editor
{
public sealed class VATTextureImporter : AssetPostprocessor
{
private const string VatKeyword = "vat";
private const string VatKeyword2 = "vertex_animation_textures";
private const string ExrExtension = ".exr";
private const string PngExtension = ".png";
private const string DefaultPlatformName = "DefaultTexturePlatform";
private const string StandalonePlatformName = "Standalone";
private const string AndroidPlatformName = "Android";
private const int AndroidDataTextureMaxSize = 8192;
private void OnPreprocessTexture()
{
if (!(assetImporter is TextureImporter textureImporter))
{
return;
}
if (!ShouldImport(assetPath))
{
return;
}
ApplyImportSettings(textureImporter);
}
internal static bool ShouldImport(string path)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
if (!HasSupportedExtension(path))
{
return false;
}
string fileName = Path.GetFileName(path);
if (string.IsNullOrEmpty(fileName))
{
return false;
}
bool shouldImport = false;
shouldImport |= fileName.IndexOf(VatKeyword, StringComparison.OrdinalIgnoreCase) >= 0;
shouldImport |= fileName.IndexOf(VatKeyword2, StringComparison.OrdinalIgnoreCase) >= 0;
return shouldImport;
}
private static bool HasSupportedExtension(string path)
{
string extension = Path.GetExtension(path);
if (string.IsNullOrEmpty(extension))
{
return false;
}
return extension.Equals(ExrExtension, StringComparison.OrdinalIgnoreCase) ||
extension.Equals(PngExtension, StringComparison.OrdinalIgnoreCase);
}
private static void ApplyImportSettings(TextureImporter textureImporter)
{
ApplyCommonDataSettings(textureImporter);
string extension = Path.GetExtension(textureImporter.assetPath);
if (extension.Equals(ExrExtension, StringComparison.OrdinalIgnoreCase))
{
ApplyPlatformSettings(textureImporter, TextureImporterFormat.RGBAHalf);
return;
}
if (extension.Equals(PngExtension, StringComparison.OrdinalIgnoreCase))
{
ApplyPlatformSettings(textureImporter, TextureImporterFormat.RGBA32);
}
}
private static void ApplyCommonDataSettings(TextureImporter textureImporter)
{
textureImporter.textureType = TextureImporterType.Default;
textureImporter.sRGBTexture = false;
// textureImporter.isReadable = true;
textureImporter.mipmapEnabled = false;
textureImporter.alphaIsTransparency = false;
textureImporter.textureCompression = TextureImporterCompression.Uncompressed;
}
private static void ApplyPlatformSettings(TextureImporter textureImporter, TextureImporterFormat format)
{
ApplyPlatformSettings(textureImporter, DefaultPlatformName, format);
ApplyPlatformSettings(textureImporter, StandalonePlatformName, format);
ApplyPlatformSettings(textureImporter, AndroidPlatformName, format);
}
private static void ApplyPlatformSettings(TextureImporter textureImporter, string platformName, TextureImporterFormat format)
{
TextureImporterPlatformSettings settings = textureImporter.GetPlatformTextureSettings(platformName);
settings.name = platformName;
settings.overridden = true;
settings.format = format;
settings.textureCompression = TextureImporterCompression.Uncompressed;
settings.compressionQuality = 100;
settings.crunchedCompression = false;
settings.allowsAlphaSplitting = false;
if (platformName.Equals(AndroidPlatformName, StringComparison.Ordinal) &&
settings.maxTextureSize < AndroidDataTextureMaxSize)
{
settings.maxTextureSize = AndroidDataTextureMaxSize;
}
textureImporter.SetPlatformTextureSettings(settings);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0eb1e10af3780b0499d3c7e1393a0749
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
{
"name": "com.xuanxuan.render.utility.Editor",
"rootNamespace": "",
"references": [
"GUID:8f9e4d586616f13449cfeb86c5f704c2",
"GUID:df380645f10b7bc4b97d4f5eb6303d95"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8495541fcd41b0c40b5b6e6e7a7639d1
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,188 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
namespace NBShaderEditor
{
public class RefreshMaterialInspectorWindow : EditorWindow
{
[MenuItem("ArtTools/刷新选中材质球面板")]
static void refershSelectMaterialInspector()
{
EditorWindow.GetWindow<RefreshMaterialInspectorWindow>("材质球刷新");
}
public static bool isRefreshing = false;
List<Material> materials = new List<Material>();
int i = 0;
private void OnGUI()
{
if (GUILayout.Button("开始刷新材质面板"))
{
materials.Clear();
foreach (UnityEngine.Object o in Selection.GetFiltered(typeof(Material), SelectionMode.DeepAssets))
{
string path = AssetDatabase.GetAssetPath(o);
string fileExtension = Path.GetExtension(path);
if (fileExtension == ".mat")
{
materials.Add((Material)o);
}
}
i = 0;
}
}
private void OnEnable()
{
Debug.Log("刷新材质打开");
isRefreshing = true;
}
private void Update()
{
if(materials.Count <= 0) return;
if (i < materials.Count)
{
// isRefreshing = true;
// SetNormalToUV1(materials[i]);
Selection.activeObject = materials[i];
Debug.Log(materials[i].name,materials[i]);
EditorUtility.DisplayProgressBar("材质球刷新", $"{i + 1}/{materials.Count}", (i + 1) / (float)materials.Count);
i++;
// isRefreshing = false;
}
if (i >= materials.Count)
{
EditorUtility.ClearProgressBar();
materials.Clear();
Debug.Log("材质刷新结束isRefreshing布尔值 = "+isRefreshing);
}
}
void OnDisable()
{
Debug.Log("刷新材质关闭");
isRefreshing = false;
}
// void SetNormalToUV1(Material mat)
// {
// if (mat.HasProperty("_NormalFrom"))
// {
// mat.SetInteger("_NormalFrom",1);
// }
// }
//
// // [MenuItem("Assets/测试刷新材质", priority = 1)]
// public static void TestRefeshMaterial()
// {
// W9ParticleShaderFlags flags = new W9ParticleShaderFlags();
// int id = Shader.PropertyToID("_Mask2_Toggle");
// foreach (UnityEngine.Object o in Selection.GetFiltered(typeof(Material), SelectionMode.DeepAssets))
// {
// Material mat = o as Material;
// if (mat.shader.name == "XuanXuan/Effects/Particle_NiuBi")
// {
// bool isToggle = mat.GetFloat(id) > 0.5f;
// if (isToggle)
// {
// mat.DisableKeyword("_MASKMAP2");
// flags.SetMaterial(mat);
// flags.SetFlagBits(W9ParticleShaderFlags.FLAG_BIT_PARTICLE_1_MASK_MAP2,index:1);
// }
// // flags.SetMaterial(mat);
// }
// }
// AssetDatabase.SaveAssets();
// }
//
// [MenuItem("Assets/Effect Tool/刷新选中文件夹内材质Wrap模式", priority = 1)]
// public static void SetSelectParticleMaterialWrapMode()
// {
// foreach (UnityEngine.Object o in Selection.GetFiltered(typeof(Material), SelectionMode.DeepAssets))
// {
// Material mat = (Material)o;
// // Debug.Log(mat.shader.name);
// SetParticleMaterialWrapMode((Material)o);
// }
// AssetDatabase.SaveAssets();
// }
//
// public static void SetParticleMaterialWrapMode(Material mat)
// {
//
// if (mat.shader.name == "XuanXuan/Effects/Particle_NiuBi")
// {
// W9ParticleShaderFlags flags = new W9ParticleShaderFlags(mat);
// SetWrapModeFlag(flags,mat,"_BaseMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_BASEMAP);
// SetWrapModeFlag(flags,mat,"_MaskMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_MASKMAP);
// SetWrapModeFlag(flags,mat,"_MaskMap2",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_MASKMAP2);
// SetWrapModeFlag(flags,mat,"_NoiseMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_NOISEMAP);
// SetWrapModeFlag(flags,mat,"_EmissionMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_EMISSIONMAP);
// SetWrapModeFlag(flags,mat,"_DissolveMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_DISSOLVE_MAP);
// SetWrapModeFlag(flags,mat,"_DissolveMaskMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_DISSOLVE_MASKMAP);
// SetWrapModeFlag(flags,mat,"_DissolveRampMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_DISSOLVE_RAMPMAP);
// SetWrapModeFlag(flags,mat,"_ColorBlendMap",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_COLORBLENDMAP);
// SetWrapModeFlag(flags,mat,"_VertexOffset_Map",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_VERTEXOFFSETMAP);
// SetWrapModeFlag(flags,mat,"_ParallaxMapping_Map",W9ParticleShaderFlags.FLAG_BIT_WRAPMODE_PARALLAXMAPPINGMAP);
// }
// }
//
// public static void SetParticleMaterialMaskStrenth(Material mat)
// {
// if (mat.shader.name == "XuanXuan/Effects/Particle_NiuBi")
// {
// Vector4 vec = mat.GetVector("_MaskMap3OffsetAnition");
// if (vec.z < 1)
// {
// Vector4 newVec = new Vector4(vec.x, vec.y, 1, vec.w);
// mat.SetVector("_MaskMap3OffsetAnition",newVec);
// }
// }
// }
//
// static void SetWrapModeFlag(W9ParticleShaderFlags flags, Material mat, string matTexPropertyName, int flagBit)
// {
// if (mat.GetTexture(matTexPropertyName) != null)
// {
// Texture tex = mat.GetTexture(matTexPropertyName);
// Debug.Log(tex.name);
// if (tex.wrapMode == TextureWrapMode.Clamp)
// {
// Debug.Log("This is Clamp");
// flags.SetFlagBits(flagBit,index:2);
// }
// else if (tex.wrapMode == TextureWrapMode.Repeat)
// {
// Debug.Log("This is Repeat");
// flags.ClearFlagBits(flagBit,index:2);
// }
// }
// }
// static IEnumerator refreshMaterialInspector(Material[] materials)
// {
// for (int i = 0; i < materials.Length; i++)
// {
// EditorUtility.DisplayProgressBar("材质球刷新", $"{i + 1}/{materials.Length}", (i + 1) / (float)materials.Length);
// Debug.Log(i);
// // Selection.activeObject = materials[i];
// yield return null;
// }
// EditorUtility.ClearProgressBar();
// // yield break;
// }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 317bb692129be184ebd7249bba265210
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 72e9a7523c1a5994c8fb317e7a9514e0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,370 @@
using System;
using UnityEngine;
// using Sirenix.OdinInspector;
// using Unity.Mathematics;
using UnityEngine.UI;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteInEditMode]
public class AnimationSheetHelper : MonoBehaviour,IMaterialModifier
{
public bool isParticleBaseShader = true;
public string propertyName = "_BaseMap_ST";
private int _propertyID;
private int _particleBaseAniBlendStPropertyID = Shader.PropertyToID("_BaseMap_AnimationSheetBlend_ST");
private int _particleBaseAniBlendIntensityPropertyID = Shader.PropertyToID("_AnimationSheetHelperBlendIntensity");
private static readonly int ParticleShaderFlagsId = Shader.PropertyToID("_W9ParticleShaderFlags");
private static readonly int ParticleShaderFlags1Id = Shader.PropertyToID("_W9ParticleShaderFlags1");
private static readonly int NBShaderFlagsId = Shader.PropertyToID("_NBShaderFlags");
private static readonly int NBShaderFlags1Id = Shader.PropertyToID("_NBShaderFlags1");
private const int FlagBitUIEffectOn = 1 << 14;
private const int FlagBitAnimationSheetHelper = 1 << 15;
private const int FlagBitUIEffectBaseMapMode = 1 << 22;
// [LabelText("序列帧图横向帧数量")]
// [OnValueChanged("Init")]
public int xSize = 4;
// [LabelText("序列帧图纵向帧数量")]
// [OnValueChanged("Init")]
public int ySize = 4;
// [LabelText("手动控制播放")]
public bool manualPlay = false;
// [ShowIf("manualPlay")]
// [LabelText("手动播放位置")] [Range(0, 1)]
public float manualPlayePos = 0;
// [LabelText("播放速度fps")]
// [HideIf("manualPlay")]
public float speed = 16;
// [ReadOnly]
// [LabelText("TillingOffset")]
public Vector4 scaleOffset;
// [ReadOnly]
public Material mat;
// [ReadOnly]
public int frameIndex;
// [ReadOnly]
public int frameCount;
private float _time;
private float _xScale;
private float _yScale;
private static List<Material> usedMaterialList = new List<Material>();
// // Start is called before the first frame update
// void Start()
// {
// Debug.Log( "ASUpadate_Start");
// Init();
// }
private void OnEnable()
{
// Debug.Log( "ASUpadate_OnEnable");
Init();
#if UNITY_EDITOR
EditorApplication.update += EditorUpdate;
#endif
}
private void OnDisable()
{
Init();
//清掉,避免参数保留。
if (isParticleBaseShader)
{
if (mat)
{
GetShaderFlagIds(mat, out _, out int flags1Id);
ClearFlagBits(mat, flags1Id, FlagBitAnimationSheetHelper);
}
}
if (usedMaterialList.Contains(mat))
{
usedMaterialList.Remove(mat);
}
#if UNITY_EDITOR
EditorApplication.update -= EditorUpdate;
#endif
}
// [Button("初始化")]
public void Init()
{
_time = 0;
if (xSize <= 0)
{
xSize = 1;
}
if (ySize <= 0)
{
ySize = 1;
}
frameCount = xSize * ySize;
_xScale = 1 / (float)xSize;
_yScale = 1 / (float)ySize;
// if (frameCount <= 0)
// {
// frameCount = 1;
// }
// scaleOffset = new Vector4(1f/(float)xSize,1f/ (float)ySize, 0, 0);
if (gameObject.TryGetComponent(out Graphic g))
{
if (Application.isPlaying)
{
mat = g.materialForRendering;
}
else
{
mat = g.material;
}
}
else if (gameObject.TryGetComponent(out Renderer r))
{
if (Application.isPlaying)
{
mat = r.material;
}
else
{
mat = r.sharedMaterial;
}
// Debug.Log("AS_GetRenderer:Mat--"+mat.name);
}
else
{
mat = null;
}
InitParticleBaseShaderToggle();
// InitPostProcessToggle();
_propertyID = Shader.PropertyToID(propertyName);
if (mat != null)
{
mat.SetVector(_propertyID,CalSt(0));
}
}
public void InitParticleBaseShaderToggle()
{
if (isParticleBaseShader)
{
GetShaderFlagIds(mat, out int flagsId, out int flags1Id);
// Debug.Log(CheckFlagBits(mat, ParticleShaderFlags1Id, FlagBitUIEffectBaseMapMode));
if(CheckFlagBits(mat, flagsId, FlagBitUIEffectOn) && !CheckFlagBits(mat, flags1Id, FlagBitUIEffectBaseMapMode))
{
propertyName = "_UI_MainTex_ST";
}
else
{
propertyName = "_BaseMap_ST";
}
if (mat)
{
SetFlagBits(mat, flags1Id, FlagBitAnimationSheetHelper);
}
}
// else
// {
// if (mat)
// {
// if (mat.shader.name == "Mh2/Effects/Particle_NiuBi")
// {
// _flags.SetMaterial(mat);
// _flags.ClearFlagBits(W9ParticleShaderFlags.FLAG_BIT_PARTICLE_1_ANIMATION_SHEET_HELPER,index:1);
// }
// }
// }
}
// public void InitPostProcessToggle()
// {
// if (isPostProcessShader)
// {
// TryGetComponent<PostProcessingController>(out postProcessingController);
// }
// else
// {
// postProcessingController = null;
// }
// }
// Update is called once per frame
private int _lastIndex;
private int _nextIndex = 1;
private float _blendLerp;
private void Update()
{
// if (!isPostProcessShader)//后处理控制不通过才知。
// {
// if(!mat || _propertyID==0) return;
// }
float frameIndexFloat;
if (manualPlay)
{
float playPos = Mathf.Repeat(manualPlayePos, 1f);
frameIndexFloat = playPos * frameCount;
}
else
{
if (!Application.isPlaying)
{
#if UNITY_EDITOR
_time += (float)editorDeltaTime * speed;
#endif
}
else
{
_time += Time.deltaTime * speed;
}
frameIndexFloat = _time % frameCount;
}
frameIndex = (int) frameIndexFloat;
if (frameIndex == frameCount)
{
frameIndex = frameCount - 1;
}
if (_lastIndex != frameIndex)
{
// if (isPostProcessShader)
// {
// postProcessingController.distortSpeedTexSt = CalSt(frameIndex);
// }
// else
// {
mat.SetVector(_propertyID,CalSt(frameIndex));
// }
_lastIndex = frameIndex;
_nextIndex = frameIndex + 1;
if (isParticleBaseShader)
{
mat.SetVector(_particleBaseAniBlendStPropertyID,CalSt(_nextIndex));
}
}
if (isParticleBaseShader)
{
_blendLerp = Mathf.Repeat(frameIndexFloat,1f);
mat.SetFloat(_particleBaseAniBlendIntensityPropertyID,_blendLerp);
}
}
public Material GetModifiedMaterial(Material baseMaterial)
{
if (usedMaterialList.Contains(baseMaterial))
{
//有可能打断UI合批烦请客户端判断
Material newMat = Instantiate(baseMaterial);
mat = newMat;
usedMaterialList.Add(newMat);
}
else
{
mat = baseMaterial;
usedMaterialList.Add(baseMaterial);
}
return mat;
}
private Vector4 CalSt(int index)
{
float xOffset = (index % xSize)*_xScale;
float yOffset = (ySize - index / xSize -1)*_yScale;
return new Vector4(_xScale, _yScale, xOffset, yOffset);
}
private static void SetFlagBits(Material material, int propertyId, int bits)
{
if (!material)
{
return;
}
material.SetInteger(propertyId, material.GetInteger(propertyId) | bits);
}
private static void ClearFlagBits(Material material, int propertyId, int bits)
{
if (!material)
{
return;
}
material.SetInteger(propertyId, material.GetInteger(propertyId) & ~bits);
}
private static bool CheckFlagBits(Material material, int propertyId, int bits)
{
return material && (material.GetInteger(propertyId) & bits) != 0;
}
private static void GetShaderFlagIds(Material material, out int flagsId, out int flags1Id)
{
if (material && (material.HasProperty(NBShaderFlagsId) || material.HasProperty(NBShaderFlags1Id)))
{
flagsId = NBShaderFlagsId;
flags1Id = NBShaderFlags1Id;
return;
}
flagsId = ParticleShaderFlagsId;
flags1Id = ParticleShaderFlags1Id;
}
#if UNITY_EDITOR
private double editorDeltaTime = 0;
private double lastEditorTime = 0;
void EditorUpdate()
{
if (lastEditorTime == 0)
{
lastEditorTime = EditorApplication.timeSinceStartup;
}
editorDeltaTime = EditorApplication.timeSinceStartup - lastEditorTime;
lastEditorTime = EditorApplication.timeSinceStartup;
if (!Application.isPlaying)
{
Update();
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf793623f3ea13b4db35460280cb6ded
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using System.Reflection;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace NBShader
{
public class InspectorButtonAttribute : PropertyAttribute
{
public string Label;
public string MethodName;
public InspectorButtonAttribute(string label, string methodName)
{
this.Label = label;
this.MethodName = methodName;
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(InspectorButtonAttribute))]
public class InspectorButtonPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
InspectorButtonAttribute buttonAttribute = (InspectorButtonAttribute)attribute;
// 绘制按钮
if (GUI.Button(position, buttonAttribute.Label))
{
// 获取包含该方法的对象
var targetObject = property.serializedObject.targetObject;
// 获取方法信息
var methodInfo = targetObject.GetType().GetMethod(buttonAttribute.MethodName,
BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (methodInfo != null)
{
// 调用方法
methodInfo.Invoke(targetObject, null);
}
else
{
Debug.LogError($"Method '{buttonAttribute.MethodName}' not found in {targetObject.name}.");
}
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.singleLineHeight; // 返回按钮的高度
}
}
#endif
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 45f557806bb54c4dbeaec7a2ded5be83
timeCreated: 1737792133

View File

@@ -0,0 +1,598 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
namespace NBShader
{
//TODO增加一键去重功能。测试排查BUG
[ExecuteInEditMode]
public class MaterialPropertyAgent : MonoBehaviour, IMaterialModifier
{
[System.Serializable]
public struct PropertyData
{
[HideInInspector] public int dataIndexInAgent;
[HideInInspector] public MaterialPropertyAgent agent;
[HideInInspector] public int id;
public int index;
[HideInInspector] public string propName;
public shaderPropertyType type;
public string descripName;
public Color colorValue;
public Vector4 vecValue;
public float floatValue;
[SerializeField] public IEnumerable propNameList;
[HideInInspector] public bool isActive;
[HideInInspector] public Shader shader;
[HideInInspector] public Material mat;
#if UNITY_EDITOR
public float rangMin { get; set; }
public float rangMax { get; set; }
public void setValueByPropChange()
{
agent.refreshShderPropNameList();
if (!agent.isCanUsedIndex(index))
{
index = agent.getCanUsedIndex();
}
string propertyName = shader.GetPropertyName(index);
id = Shader.PropertyToID(propertyName);
descripName = shader.GetPropertyDescription(index);
type = (shaderPropertyType)shader.GetPropertyType(index);
if (type == shaderPropertyType.TexEnv)
{
propName = propertyName + "_ST";
}
else
{
propName = propertyName;
}
switch (type)
{
case shaderPropertyType.Color:
colorValue = mat.GetColor(id);
break;
case shaderPropertyType.Float:
floatValue = mat.GetFloat(id);
break;
case shaderPropertyType.Range:
Vector2 rangeLimits = shader.GetPropertyRangeLimits(index);
rangMin = rangeLimits.x;
rangMax = rangeLimits.y;
floatValue = mat.GetFloat(id);
break;
case shaderPropertyType.Vector:
vecValue = mat.GetVector(id);
break;
case shaderPropertyType.TexEnv:
string stName = propertyName + "_ST";
vecValue = mat.GetVector(stName);
break;
}
}
public void inActivateThis()
{
isActive = false;
}
#endif
}
//因为ShaderUtil只是用于Editor所以复制一个枚举对属性类型进行识别。
public enum shaderPropertyType
{
//
// 摘要:
// Color Property.
Color = 0,
//
// 摘要:
// Vector Property.
Vector = 1,
//
// 摘要:
// Float Property.
Float = 2,
//
// 摘要:
// Range Property.
Range = 3,
//
// 摘要:
// Texture Property.
TexEnv = 4
}
public PropertyData data0 = new PropertyData();
public PropertyData data1 = new PropertyData();
public PropertyData data2 = new PropertyData();
public PropertyData data3 = new PropertyData();
public PropertyData data4 = new PropertyData();
public PropertyData data5 = new PropertyData();
public Shader shader;
public Material mat;
public int materialIndex = 0;
public Renderer customRenderer;
void initMatAndShader(bool initMat = false)
{
if (!initMat)
{
if (mat != null) return;
}
if (customRenderer || GetComponent<Renderer>())
{
Material[] materials;
Renderer r;
if (customRenderer)
{
r = customRenderer;
}
else
{
r = GetComponent<Renderer>();
}
if (Application.isPlaying)
{
materials = r.materials;
}
else
{
materials = r.sharedMaterials;
}
mat = materials[materialIndex];
shader = mat.shader;
}
else if (this.GetComponent<Graphic>())
{
Graphic graphic = this.GetComponent<Graphic>();
//测试要不要用IMaterialModifier来处理
// if (Application.isPlaying)
// {
// graphic.material = Material.Instantiate(graphic.material);
// }
mat = graphic.material;
shader = mat.shader;
}
else
{
Debug.LogError("MaterialPropertyAgent未找到材质", this.gameObject);
}
}
private void Start()
{
if (Application.isPlaying)
{
initMatAndShader(true);
}
else
{
initMatAndShader(false);
}
}
private void Update()
{
if (mat == null)
{
// initMatAndShader();
return;
}
updateData(data0);
updateData(data1);
updateData(data2);
updateData(data3);
updateData(data4);
updateData(data5);
}
void updateData(PropertyData data)
{
if (!data.isActive) return;
if (mat == null) return;
switch (data.type)
{
case shaderPropertyType.Color:
mat.SetColor(data.propName, data.colorValue);
break;
case shaderPropertyType.Vector:
case shaderPropertyType.TexEnv:
mat.SetVector(data.propName, data.vecValue);
break;
case shaderPropertyType.Float:
case shaderPropertyType.Range:
mat.SetFloat(data.propName, data.floatValue);
break;
}
}
//实际上是修改了graphic.materialForRendering
public Material GetModifiedMaterial(Material baseMaterial)
{
if (mat)
{
return mat;
}
else
{
return baseMaterial;
}
}
#if UNITY_EDITOR
private void OnRenderObject()
{
if (!UnityEditor.EditorApplication.isPlaying)
{
Update();
}
}
public void addProperteData()
{
refreshShderPropNameList();
if (!data0.isActive)
{
initData(ref data0, 0);
}
else if (!data1.isActive)
{
initData(ref data1, 1);
}
else if (!data2.isActive)
{
initData(ref data2, 2);
}
else if (!data3.isActive)
{
initData(ref data3, 3);
}
else if (!data4.isActive)
{
initData(ref data4, 4);
}
else if (!data5.isActive)
{
initData(ref data5, 5);
}
else
{
Debug.Log("已用掉可用的6个属性");
}
}
public void removeAllProperty()
{
data0.isActive = false;
data1.isActive = false;
data2.isActive = false;
data3.isActive = false;
data4.isActive = false;
data5.isActive = false;
}
public void initData(ref PropertyData data, int dataIndexInAgent)
{
data.dataIndexInAgent = dataIndexInAgent;
data.agent = this;
data.shader = shader;
data.mat = mat;
data.isActive = true;
data.index = getCanUsedIndex();
data.setValueByPropChange();
}
#region TODO自动排除已用Property
List<string> usedPropertyName = new List<string>();
void collectUsedPropName()
{
usedPropertyName.Clear();
if (data0.isActive)
{
usedPropertyName.Add(data0.propName);
}
if (data1.isActive)
{
usedPropertyName.Add(data1.propName);
}
if (data2.isActive)
{
usedPropertyName.Add(data2.propName);
}
if (data3.isActive)
{
usedPropertyName.Add(data3.propName);
}
if (data4.isActive)
{
usedPropertyName.Add(data4.propName);
}
if (data5.isActive)
{
usedPropertyName.Add(data5.propName);
}
}
public int getCanUsedIndex()
{
int index = -1;
collectUsedPropName();
for (int i = 0; i < shaderPropNameArr.Length; i++)
{
string propertyName;
if (shader.GetPropertyType(i) == ShaderPropertyType.Texture)
{
propertyName = shaderPropNameArr[i] + "_ST";
}
else
{
propertyName = shaderPropNameArr[i];
}
if (usedPropertyName.Contains(propertyName)) continue;
index = i;
break;
}
return index;
}
public bool isCanUsedIndex(int i)
{
string propertyName;
if (shader.GetPropertyType(i) == ShaderPropertyType.Texture)
{
propertyName = shaderPropNameArr[i] + "_ST";
}
else
{
propertyName = shaderPropNameArr[i];
}
collectUsedPropName();
if (usedPropertyName.Contains(propertyName))
{
return false;
}
else
{
return true;
}
}
#endregion
public string[] shaderPropNameArr;
public string[] shaderPropDescripArr;
public string[] shaderPropDescripsForSerch;
private void OnValidate()
{
// if (XLuaManager.Instance != null)
// {
// if (XLuaManager.Instance.HasGameStart)//判断是游戏运行状态才进行实例化
// {
// return;//游戏进行中不允许编辑
// }
// }
// Debug.Log("MaterialPropertyAgent : " + "OnValidate");
refreshShderPropNameList();
if (TryGetComponent<Renderer>(out Renderer r) || customRenderer)
{
isRendererMode = true;
}
else
{
isRendererMode = false;
}
if (TryGetComponent<Renderer>(out Renderer r2) || TryGetComponent<Graphic>(out Graphic g))
{
isGetByComponet = true;
}
else
{
isGetByComponet = false;
}
}
public bool isRendererMode = false;
public bool isGetByComponet = false;
public void initMatAndShaderByMaterialIndexChange()
{
initMatAndShader(true);
refreshShderPropNameList();
if (data0.isActive && data0.shader != mat.shader)
{
data0.shader = mat.shader;
data0.mat = mat;
}
if (data1.isActive && data1.shader != mat.shader)
{
data1.shader = mat.shader;
data1.mat = mat;
}
if (data2.isActive && data2.shader != mat.shader)
{
data2.shader = mat.shader;
data2.mat = mat;
}
if (data3.isActive && data3.shader != mat.shader)
{
data3.shader = mat.shader;
data3.mat = mat;
}
if (data4.isActive && data4.shader != mat.shader)
{
data4.shader = mat.shader;
data4.mat = mat;
}
if (data5.isActive && data5.shader != mat.shader)
{
data5.shader = mat.shader;
data5.mat = mat;
}
}
List<string> shaderPropNameList = new List<string>();
private List<string> shaderPropDescripList = new List<string>();
private List<string> shaderPropDescripListForSerch = new List<string>();
public void refreshShderPropNameList()
{
initMatAndShader();
if (shader == null) return;
shaderPropNameList.Clear();
shaderPropDescripList.Clear();
shaderPropDescripListForSerch.Clear();
for (int i = 0; i < shader.GetPropertyCount(); i++)
{
shaderPropNameList.Add(shader.GetPropertyName(i));
string descript = shader.GetPropertyDescription(i);
shaderPropDescripList.Add(descript);
string lowerDesc = descript.ToLower();
if (!(lowerDesc.Contains("ignore") || lowerDesc.Contains("mode") || lowerDesc.Contains("toggle") ||
lowerDesc.Contains("enable") || lowerDesc.Contains("flag")))
{
shaderPropDescripListForSerch.Add(descript);
}
}
shaderPropNameArr = shaderPropNameList.ToArray();
shaderPropDescripArr = shaderPropDescripList.ToArray();
shaderPropDescripsForSerch = shaderPropDescripListForSerch.ToArray();
}
#endif
}
/*
[CustomPropertyDrawer(typeof(MaterialPropertyAgent.PropertyData))]
public class PropertyAgentPropertyDataDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
var isActive = property.FindPropertyRelative("isActive");
if (isActive.boolValue)
{
EditorGUILayout.LabelField(label, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
var index = property.FindPropertyRelative("index");
MaterialPropertyAgent agent = property.FindPropertyRelative("agent").objectReferenceValue as MaterialPropertyAgent;
int preservedIndex = index.intValue;
float originLabelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 80;
index.intValue = EditorGUILayout.Popup("属性名:", index.intValue, agent.shaderPropNameArr);
if (preservedIndex != index.intValue)//证明用户进行了更改
{
if (!agent.isCanUsedIndex(index.intValue))
{
//TODO给一个报错提示
Debug.Log(agent.shader.GetPropertyDescription(index.intValue));
index.intValue = agent.getCanUsedIndex();
}
//此处进行内容刷新
data.setValueByPropChange();
}
EditorGUILayout.LabelField("属性类型:", data.type.ToString());
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = originLabelWidth;
switch (data.type)
{
case MaterialPropertyAgent.shaderPropertyType.Color:
data.colorValue = EditorGUILayout.ColorField(data.descripName + " :", data.colorValue);
break;
case MaterialPropertyAgent.shaderPropertyType.Vector:
data.vecValue = EditorGUILayout.Vector4Field(data.descripName + " :", data.vecValue);
break;
case MaterialPropertyAgent.shaderPropertyType.Float:
data.floatValue = EditorGUILayout.FloatField(data.descripName + ":", data.floatValue);
break;
case MaterialPropertyAgent.shaderPropertyType.Range:
data.floatValue = EditorGUILayout.Slider(data.descripName + ":", data.floatValue, data.rangMin, data.rangMax);
break;
case MaterialPropertyAgent.shaderPropertyType.TexEnv:
data.vecValue = EditorGUILayout.Vector4Field(data.propName + "_ST:", data.vecValue);
break;
}
if (GUILayout.Button("删除", new[] { GUILayout.Width(200) }))
{
data.isActive = false;
}
EditorGUI.indentLevel--;
EditorGUILayout.Space();
}
}
EditorGUI.EndProperty();
}
}
*/
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 781e80efab11a1d438f7c4d37603bdff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,337 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace NBShader
{
[ExecuteAlways]
[RequireComponent(typeof(CanvasRenderer))]
[AddComponentMenu("UI/Mesh UI")]
public class MeshUI : MaskableGraphic
{
[SerializeField] private Mesh mesh;
[SerializeField] private List<Material> subMeshMaterials = new List<Material>();
private readonly List<Vector3> _positions = new List<Vector3>();
private readonly List<Vector3> _normals = new List<Vector3>();
private readonly List<Vector4> _tangents = new List<Vector4>();
private readonly List<Vector2> _uv0S = new List<Vector2>();
private readonly List<Color32> _colors = new List<Color32>();
private readonly List<int[]> _subMeshIndices = new List<int[]>();
private readonly List<int> _renderSubMeshMap = new List<int>();
private Mesh _workingMesh;
private int _renderSubMeshCount;
private bool _hasLoggedUnreadableMesh;
public Mesh Mesh
{
get => mesh;
set
{
if (mesh == value)
{
return;
}
mesh = value;
_hasLoggedUnreadableMesh = false;
SetMaterialDirty();
SetVerticesDirty();
}
}
public List<Material> SubMeshMaterials => subMeshMaterials;
public override Texture mainTexture
{
get
{
Material currentMaterial = material;
if (currentMaterial != null && currentMaterial.mainTexture != null)
{
return currentMaterial.mainTexture;
}
return s_WhiteTexture;
}
}
protected override void OnEnable()
{
base.OnEnable();
SetAllDirty();
}
protected override void OnDisable()
{
canvasRenderer.Clear();
base.OnDisable();
}
protected override void OnDestroy()
{
ReleaseWorkingMesh();
base.OnDestroy();
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
}
protected override void UpdateGeometry()
{
if (canvasRenderer == null)
{
return;
}
if (!PrepareRenderMesh(out Mesh renderMesh))
{
canvasRenderer.Clear();
return;
}
canvasRenderer.SetMesh(renderMesh);
}
protected override void UpdateMaterial()
{
if (canvasRenderer == null)
{
return;
}
int materialCount = Mathf.Max(1, _renderSubMeshCount);
canvasRenderer.materialCount = materialCount;
for (int i = 0; i < materialCount; i++)
{
Material sourceMaterial = GetRenderMaterial(i);
Material modifiedMaterial = sourceMaterial != null ? GetModifiedMaterial(sourceMaterial) : defaultGraphicMaterial;
canvasRenderer.SetMaterial(modifiedMaterial, i);
}
canvasRenderer.SetTexture(mainTexture);
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
SetMaterialDirty();
SetVerticesDirty();
}
#endif
protected override void OnDidApplyAnimationProperties()
{
base.OnDidApplyAnimationProperties();
SetVerticesDirty();
}
private bool PrepareRenderMesh(out Mesh renderMesh)
{
renderMesh = null;
_renderSubMeshCount = 0;
if (mesh == null)
{
return false;
}
if (!mesh.isReadable)
{
if (!_hasLoggedUnreadableMesh)
{
Debug.LogWarning($"MeshUI requires a read/write enabled mesh. Bake a readable mesh in the inspector before using '{mesh.name}'.", this);
_hasLoggedUnreadableMesh = true;
}
return false;
}
if (mesh.vertexCount <= 0)
{
return false;
}
CollectRenderableSubMeshes(mesh, _subMeshIndices, _renderSubMeshMap);
if (_subMeshIndices.Count == 0)
{
return false;
}
_renderSubMeshCount = _subMeshIndices.Count;
if (ShouldUseSourceMesh())
{
renderMesh = mesh;
return true;
}
EnsureWorkingMesh();
BuildWorkingMesh();
renderMesh = _workingMesh;
return true;
}
private bool ShouldUseSourceMesh()
{
if (color != Color.white)
{
return false;
}
return _renderSubMeshCount == mesh.subMeshCount;
}
private void BuildWorkingMesh()
{
Vector3[] srcVertices = mesh.vertices;
int vertexCount = srcVertices.Length;
PrepareVertexLists(vertexCount);
Vector2[] srcUv0 = mesh.uv;
Vector3[] srcNormals = mesh.normals;
Vector4[] srcTangents = mesh.tangents;
Color32 vertexColor = color;
for (int i = 0; i < vertexCount; i++)
{
_positions.Add(srcVertices[i]);
_uv0S.Add(srcUv0 != null && i < srcUv0.Length ? srcUv0[i] : Vector2.zero);
_normals.Add(srcNormals != null && i < srcNormals.Length ? srcNormals[i] : Vector3.back);
_tangents.Add(srcTangents != null && i < srcTangents.Length ? srcTangents[i] : new Vector4(1f, 0f, 0f, 1f));
_colors.Add(vertexColor);
}
_workingMesh.Clear();
_workingMesh.indexFormat = mesh.indexFormat;
_workingMesh.SetVertices(_positions);
_workingMesh.SetNormals(_normals);
_workingMesh.SetTangents(_tangents);
_workingMesh.SetUVs(0, _uv0S);
_workingMesh.SetColors(_colors);
_workingMesh.subMeshCount = _renderSubMeshCount;
for (int i = 0; i < _renderSubMeshCount; i++)
{
_workingMesh.SetTriangles(_subMeshIndices[i], i, false);
}
_workingMesh.RecalculateBounds();
}
private static void CollectRenderableSubMeshes(Mesh sourceMesh, List<int[]> subMeshIndices, List<int> renderSubMeshMap)
{
subMeshIndices.Clear();
renderSubMeshMap.Clear();
int subMeshCount = Mathf.Max(1, sourceMesh.subMeshCount);
for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
{
if (sourceMesh.GetTopology(subMeshIndex) != MeshTopology.Triangles)
{
continue;
}
int[] indices = sourceMesh.GetTriangles(subMeshIndex);
if (indices == null || indices.Length == 0)
{
continue;
}
subMeshIndices.Add(indices);
renderSubMeshMap.Add(subMeshIndex);
}
}
private void PrepareVertexLists(int capacity)
{
_positions.Clear();
_normals.Clear();
_tangents.Clear();
_uv0S.Clear();
_colors.Clear();
if (_positions.Capacity < capacity)
{
_positions.Capacity = capacity;
}
if (_normals.Capacity < capacity)
{
_normals.Capacity = capacity;
}
if (_tangents.Capacity < capacity)
{
_tangents.Capacity = capacity;
}
if (_uv0S.Capacity < capacity)
{
_uv0S.Capacity = capacity;
}
if (_colors.Capacity < capacity)
{
_colors.Capacity = capacity;
}
}
private void EnsureWorkingMesh()
{
if (_workingMesh != null)
{
return;
}
_workingMesh = new Mesh
{
name = "MeshUI Generated Mesh"
};
_workingMesh.MarkDynamic();
}
private void ReleaseWorkingMesh()
{
if (_workingMesh == null)
{
return;
}
if (Application.isPlaying)
{
Destroy(_workingMesh);
}
else
{
DestroyImmediate(_workingMesh);
}
_workingMesh = null;
}
private Material GetRenderMaterial(int subMeshIndex)
{
int sourceSubMeshIndex = subMeshIndex;
if (subMeshIndex >= 0 && subMeshIndex < _renderSubMeshMap.Count)
{
sourceSubMeshIndex = _renderSubMeshMap[subMeshIndex];
}
if (subMeshMaterials != null && sourceSubMeshIndex >= 0 && sourceSubMeshIndex < subMeshMaterials.Count)
{
Material subMeshMaterial = subMeshMaterials[sourceSubMeshIndex];
if (subMeshMaterial != null)
{
return subMeshMaterial;
}
}
return material;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 96ee1f1d0f8f4cb3a4993f12831f42b7
timeCreated: 1774387200

View File

@@ -0,0 +1,29 @@
using UnityEngine;
namespace NBShader
{
public static class UnityObjectFindCompat
{
public static T FindAny<T>() where T : Object
{
#if UNITY_2023_1_OR_NEWER
return Object.FindAnyObjectByType<T>();
#else
#pragma warning disable 0618
return Object.FindObjectOfType<T>();
#pragma warning restore 0618
#endif
}
public static T[] FindAll<T>() where T : Object
{
#if UNITY_2023_1_OR_NEWER
return Object.FindObjectsByType<T>(FindObjectsSortMode.None);
#else
#pragma warning disable 0618
return Object.FindObjectsOfType<T>();
#pragma warning restore 0618
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6399d1dc93fb4e0c8a757240070a1313
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
using UnityEngine;
namespace NBShader
{
[ExecuteAlways]
[DisallowMultipleComponent]
[RequireComponent(typeof(ParticleSystem))]
public sealed class NBParticleLocalTransformHelper : MonoBehaviour
{
private const string NBShaderName = "Effects/NBShader";
private const string LegacyShaderName = "Effects/NBShader(Legacy)";
private const string CustomLocalTransformKeyword = "_CUSTOM_LOCAL_TRANSFORM";
private static readonly int CustomLocalTransformLocalToWorldId =
Shader.PropertyToID("_CustomLocalTransformLocalToWorld");
private static readonly int CustomLocalTransformWorldToLocalId =
Shader.PropertyToID("_CustomLocalTransformWorldToLocal");
private ParticleSystemRenderer _particleRenderer;
private Material _runtimeMaterial;
private Material _lastAppliedMaterial;
private string _lastWarning;
private void OnEnable()
{
CacheRenderer();
ApplyCustomTransform();
}
private void LateUpdate()
{
ApplyCustomTransform();
}
private void OnValidate()
{
CacheRenderer();
ApplyCustomTransform();
}
private void OnDisable()
{
SetCustomTransformKeyword(false);
}
private void OnDestroy()
{
SetCustomTransformKeyword(false);
}
private void CacheRenderer()
{
if (_particleRenderer == null)
{
TryGetComponent(out _particleRenderer);
}
}
private void ApplyCustomTransform()
{
if (!TryGetWritableMaterial(out Material material))
{
DisableLastAppliedKeyword();
return;
}
if (_lastAppliedMaterial != null && _lastAppliedMaterial != material)
{
_lastAppliedMaterial.DisableKeyword(CustomLocalTransformKeyword);
}
Matrix4x4 localToWorld = transform.localToWorldMatrix;
Matrix4x4 worldToLocal = transform.worldToLocalMatrix;
material.SetMatrix(CustomLocalTransformLocalToWorldId, localToWorld);
material.SetMatrix(CustomLocalTransformWorldToLocalId, worldToLocal);
material.EnableKeyword(CustomLocalTransformKeyword);
_lastAppliedMaterial = material;
_lastWarning = null;
}
private void SetCustomTransformKeyword(bool enabled)
{
Material material = GetExistingMaterial();
if (material == null)
{
return;
}
if (enabled)
{
material.EnableKeyword(CustomLocalTransformKeyword);
}
else
{
material.DisableKeyword(CustomLocalTransformKeyword);
if (material == _lastAppliedMaterial)
{
_lastAppliedMaterial = null;
}
}
}
private Material GetExistingMaterial()
{
if (_lastAppliedMaterial != null)
{
return _lastAppliedMaterial;
}
CacheRenderer();
if (_particleRenderer == null)
{
return null;
}
if (Application.isPlaying)
{
return _runtimeMaterial;
}
return _particleRenderer.sharedMaterial;
}
private bool TryGetWritableMaterial(out Material material)
{
CacheRenderer();
if (_particleRenderer == null)
{
material = null;
LogWarningOnce("NBParticleLocalTransformHelper requires a ParticleSystemRenderer on the same GameObject.");
return false;
}
material = Application.isPlaying ? GetRuntimeMaterial() : _particleRenderer.sharedMaterial;
if (material == null)
{
LogWarningOnce("NBParticleLocalTransformHelper could not find a material on the ParticleSystemRenderer.");
return false;
}
if (material.shader == null || !IsSupportedShader(material.shader))
{
material = null;
LogWarningOnce("NBParticleLocalTransformHelper only supports NBShader materials using shader '" +
NBShaderName + "' or '" + LegacyShaderName + "'.");
return false;
}
return true;
}
private static bool IsSupportedShader(Shader shader)
{
return shader.name == NBShaderName || shader.name == LegacyShaderName;
}
private Material GetRuntimeMaterial()
{
if (_runtimeMaterial == null && _particleRenderer != null)
{
_runtimeMaterial = _particleRenderer.material;
}
return _runtimeMaterial;
}
private void DisableLastAppliedKeyword()
{
if (_lastAppliedMaterial == null)
{
return;
}
_lastAppliedMaterial.DisableKeyword(CustomLocalTransformKeyword);
_lastAppliedMaterial = null;
}
private void LogWarningOnce(string message)
{
if (_lastWarning == message)
{
return;
}
_lastWarning = message;
Debug.LogWarning(message, this);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70a2d4dce4af4875b6c1cb93bf60442d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,99 @@
using UnityEngine;
using UnityEngine.UI;
// using Sirenix.OdinInspector;
namespace NBShader
{
[ExecuteAlways]
public class NBShaderSpriteHelper : MonoBehaviour
{
private const string NBShaderName = "Effects/NBShader";
private const string LegacyShaderName = "Effects/NBShader(Legacy)";
public Sprite sprite;
// [ReadOnly]
public SpriteRenderer spRenderer;
// [ReadOnly]
public Image image;
[InspectorButton("初始化", "Init")] public bool ButtomInspector;
private Material mat;
// Start is called before the first frame update
void Start()
{
Init();
}
void Init()
{
if (TryGetComponent<SpriteRenderer>(out SpriteRenderer spr))
{
spRenderer = spr;
sprite = spr.sprite;
}
else
{
if (TryGetComponent<Image>(out Image im))
{
image = im;
sprite = im.sprite;
}
}
if (sprite)
{
Texture texture = sprite.texture;
Rect rect = sprite.textureRect;
// sharedMaterial.SetVector("_BaseMapReverseST",CalScaleOffset(spRenderer.sprite.textureRect,spRenderer.sprite.texture));
if (spRenderer)
{
if (Application.isPlaying)
{
mat = spRenderer.material;
}
else
{
mat = spRenderer.sharedMaterial;
}
}
else if (image)
{
mat = image.material;
}
if (mat && IsSupportedShader(mat.shader))
{
mat.SetVector("_MainTex_Reverse_ST", CalScaleOffset(rect, texture));
Debug.Log(mat.name);
}
}
}
Vector4 CalScaleOffset(Rect textureRect, Texture texture)
{
//这是一个反向的scale offset。
//算法如果原scale offset。转换后会是 scaleAfter= 1 / scale , offsetAfter = - offset/scale;
Vector2 scaleAfter = new Vector2(texture.width / textureRect.width, texture.height / textureRect.height);
Vector2 offsetAfter = new Vector2(-(textureRect.x / texture.width) * scaleAfter.x,
-(textureRect.y / texture.height) * scaleAfter.y);
Vector4 scaleOffset = new Vector4(scaleAfter.x, scaleAfter.y, offsetAfter.x, offsetAfter.y);
return scaleOffset;
}
private static bool IsSupportedShader(Shader shader)
{
return shader && (shader.name == NBShaderName || shader.name == LegacyShaderName);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58ec7ff2f9b58f5459946ac86028eacf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,89 @@
using System;
using UnityEngine;
namespace NBShader
{
public abstract class ShaderFlagsBase
{
private Material _material;
public Material material
{
get { return _material; }
}
protected ShaderFlagsBase(Material material)
{
_material = material;
}
public void SetMaterial(Material material)
{
_material = material;
}
public Material GetMaterial()
{
return _material;
}
public abstract int GetShaderFlagsId(int index = 0);
protected abstract string GetShaderFlagsName(int index = 0);
private void SetIntValue(Material material, int flagBits, int index = 0)
{
#if UNITY_EDITOR
material.SetInteger(GetShaderFlagsId(index), flagBits);
#else
material.SetInteger(GetShaderFlagsId(index), flagBits);
#endif
}
public void SetFlagBits(int flagBits, MaterialPropertyBlock propertyBlock = null, int index = 0)
{
if (propertyBlock is null)
{
if (_material is null) return;
int flags = _material.GetInteger(GetShaderFlagsId(index));
SetIntValue(_material, flags | flagBits, index);
}
else
{
int flags = propertyBlock.GetInt(GetShaderFlagsId(index));
propertyBlock.SetInt(GetShaderFlagsId(index), flags | flagBits);
}
}
public void ClearFlagBits(int flagBits, MaterialPropertyBlock propertyBlock = null, int index = 0)
{
if (propertyBlock is null)
{
if (_material is null) return;
int flags = _material.GetInteger(GetShaderFlagsId(index));
SetIntValue(_material, flags & ~flagBits, index);
}
else
{
int flags = propertyBlock.GetInteger(GetShaderFlagsId(index));
propertyBlock.SetInteger(GetShaderFlagsId(index), flags & ~flagBits);
}
}
public bool CheckFlagBits(int flagBits, MaterialPropertyBlock propertyBlock = null, int index = 0)
{
int flags = 0;
if (propertyBlock is null)
{
if (_material is null) throw new NullReferenceException("material");
flags = _material.GetInteger(GetShaderFlagsId(index));
}
else
{
flags = propertyBlock.GetInteger(GetShaderFlagsId(index));
}
return (flags & flagBits) != 0;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3bcf591e67bd470e9375d72c63838643
timeCreated: 1655437380

View File

@@ -0,0 +1,16 @@
{
"name": "com.xuanxuan.render.utility",
"rootNamespace": "",
"references": [
"GUID:15fc0a57446b3144c949da3e2b9737a9"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8f9e4d586616f13449cfeb86c5f704c2
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 97647516635f92f43a6cd4807c3899d8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fa89a0fdb680e154f89c18f1a7fea2c6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,521 @@
#ifndef HOUDINI_VAT_INCLUDED
#define HOUDINI_VAT_INCLUDED
// ─────────────────────────────────────────────────────────────────────
// Houdini VAT 3.0 — 统一实现SoftBody / RigidBody / DynamicRemeshing / ParticleSprite
// 集成于 ParticleBase.shader 的粒子着色器管线
// ─────────────────────────────────────────────────────────────────────
// ── 纹理声明 ──────────────────────────────────────────────────────────
TEXTURE2D(_posTexture); SAMPLER(sampler_posTexture);
TEXTURE2D(_posTexture2); SAMPLER(sampler_posTexture2);
TEXTURE2D(_rotTexture); SAMPLER(sampler_rotTexture);
TEXTURE2D(_colTexture); SAMPLER(sampler_colTexture);
TEXTURE2D(_lookupTable); SAMPLER(sampler_lookupTable);
#if defined(_VAT) && defined(_VAT_HOUDINI) && \
!defined(_HOUDINI_VAT_SOFTBODY) && \
!defined(_HOUDINI_VAT_RIGIDBODY) && \
!defined(_HOUDINI_VAT_DYNAMIC_REMESH) && \
!defined(_HOUDINI_VAT_PARTICLE_SPRITE)
#define _HOUDINI_VAT_SOFTBODY
#endif
// ── extern 材质属性Unity 自动从材质中查找同名值) ───────────────────
// Playback
extern float _B_autoPlayback;
extern float _gameTimeAtFirstFrame;
extern float _playbackSpeed;
extern float _houdiniFPS;
extern float _displayFrame;
extern float _B_interpolate;
extern float _animateFirstFrame;
extern float _frameCount;
// Bounds
extern float _boundMinX, _boundMinY, _boundMinZ;
extern float _boundMaxX, _boundMaxY, _boundMaxZ;
// Scale
extern float _globalPscaleMul;
extern float _B_pscaleAreInPosA;
// Particle Sprite
extern float _widthBaseScale;
extern float _heightBaseScale;
extern float _B_hideOverlappingOrigin;
extern float _originRadius;
extern float _B_CAN_SPIN;
extern float _B_spinFromHeading;
extern float _spinPhase;
extern float _scaleByVelAmount;
extern float _particleTexUScale;
extern float _particleTexVScale;
// Flags
extern float _B_LOAD_POS_TWO_TEX;
extern float _B_UNLOAD_ROT_TEX;
extern float _B_LOAD_COL_TEX;
extern float _B_LOAD_LOOKUP_TABLE;
// ─────────────────────────────────────────────────────────────────────
// 工具函数
// ─────────────────────────────────────────────────────────────────────
// 用单位四元数旋转向量
// 公式: v' = v + 2 * cross(q.xyz, q.w*v + cross(q.xyz, v))
float3 HVAT_RotateByQuat(float3 v, float4 q)
{
float3 t = cross(q.xyz, v);
return v + cross(q.xyz, t + v * q.w) * 2.0;
}
// 从 3 个最小组件重建单位四元数RigidBody 专用)
// maxComp (0-3) 标识被省略的分量
float4 HVAT_DecodeQuaternion(float3 xyz, float maxComp)
{
float w = sqrt(saturate(1.0 - dot(xyz, xyz)));
float4 q = float4(xyz.x, xyz.y, xyz.z, w);
int mc = (int)maxComp;
if (mc == 1) q = float4( w, xyz.y, xyz.z, xyz.x);
else if (mc == 2) q = float4(xyz.x, -w, xyz.z, -xyz.y);
else if (mc == 3) q = float4(xyz.x, xyz.y, -w, -xyz.z);
return q;
}
// 从 posTexture.a 解码 5-bit spheremap 压缩法线
float3 HVAT_DecodeCompressedNormal(float posA)
{
float scaledA = posA * 1024.0;
float xIdx = floor(scaledA / 32.0);
float yRaw = scaledA - xIdx * 32.0;
float xNorm = xIdx / 31.5;
float yNorm = yRaw / 31.5;
float2 xy = float2(xNorm, yNorm) * 4.0 - 2.0;
float d = dot(xy, xy);
float sqrtF = sqrt(saturate(1.0 - d * 0.25));
return float3(-sqrtF * xy.x, 1.0 - d * 0.5, sqrtF * xy.y);
}
// Lookup table 解码:从 RGBA 得到高精度采样 UV
float2 HVAT_DecodeLookupUV(float4 lookupSample, float boundMinX)
{
float lookupHDR = (frac(-boundMinX * 10.0) >= 0.5) ? 1.0 : 0.0;
float divisor = lookupHDR ? 2048.0 : 255.0;
float lookupX = lookupSample.r + lookupSample.g / divisor;
float lookupY = 1.0 - (lookupSample.b + lookupSample.a / divisor);
return float2(lookupX, lookupY);
}
// 计算 VAT 采样 UV
float2 HVAT_VatUV(float selectedFrame, float uv_r, float uv_g,
float oneMinusBoundMaxR, float multiplyBoundMinB, float totalFrames)
{
float wrapped = fmod(selectedFrame - 1.0, totalFrames);
float vBase = (1.0 - uv_g) * oneMinusBoundMaxR
+ (wrapped / totalFrames) * oneMinusBoundMaxR;
return float2(multiplyBoundMinB, 1.0 - vBase);
}
// 2D hash 随机 [0, 1]
float HVAT_HashRandom2D(float2 seed)
{
return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);
}
// ─────────────────────────────────────────────────────────────────────
// 帧选择
// ─────────────────────────────────────────────────────────────────────
float2 HVAT_GetVatUV1(AttributesParticle input)
{
return CheckLocalFlags1(FLAG_BIT_PARTICLE_1_IS_PARTICLE_SYSTEM)
? input.texcoords.zw
: input.Custom1.xy;
}
void HVAT_ComputeFrameSelection(AttributesParticle input, out float selectedFrame, out float frameAlpha)
{
float totalFrames = _frameCount;
float animTime = (_Time.y - _gameTimeAtFirstFrame)
* (_houdiniFPS / (totalFrames - 0.01))
* _playbackSpeed;
float frameFloat = frac(animTime) * totalFrames;
selectedFrame = _B_autoPlayback
? floor(frameFloat) + 1.0
: floor(_displayFrame);
frameAlpha = frac(_B_autoPlayback ? frameFloat : _displayFrame);
if (CheckLocalFlags1(FLAG_BIT_PARTICLE_1_IS_PARTICLE_SYSTEM))
{
float frameCustomData = GetCustomData(NB_CUSTOM_DATA_FLAG_2, FLAGBIT_POS_2_CUSTOMDATA_VAT_FRAME, -1.0, input.Custom1, input.Custom2);
if (frameCustomData >= 0.0)
{
float customFrame = saturate(frameCustomData) * max(totalFrames - 1.0, 0.0) + 1.0;
selectedFrame = floor(customFrame);
frameAlpha = frac(customFrame);
}
}
}
// ─────────────────────────────────────────────────────────────────────
// 主函数ApplyHoudiniVAT
// ─────────────────────────────────────────────────────────────────────
void ApplyHoudiniVAT(AttributesParticle input, inout float4 positionOS, inout float3 normalOS)
{
// ── 共享 Bounds 常量(不依赖 UV 的部分) ──
float comparisonBoundMaxb = (frac(_boundMaxZ * 10.0) >= 0.5) ? 1.0 : 0.0;
float oneMinusBoundMaxR = 1.0 - frac(_boundMaxX * (-10.0));
float boundMinMul10z = _boundMinZ * 10.0;
float oneMinusBoundMinB = 1.0 - (ceil(boundMinMul10z) - boundMinMul10z);
float pscaleDenom = max(1.0 - frac(_boundMaxY * 10.0), 1e-5);
// ── 帧选择 ──
float selectedFrame, frameAlpha;
HVAT_ComputeFrameSelection(input, selectedFrame, frameAlpha);
float totalFrames = _frameCount;
float3 boundsMax = float3(_boundMaxX, _boundMaxY, _boundMaxZ);
float3 boundsMin = float3(_boundMinX, _boundMinY, _boundMinZ);
// ─────────────────────────────────────────────────────────────────
// Sub Mode 0: SoftBody — 加法位移 + 四元数法线 / 压缩法线
// ─────────────────────────────────────────────────────────────────
#if defined(_HOUDINI_VAT_SOFTBODY)
{
float2 vatUV1 = HVAT_GetVatUV1(input);
float uv1r = vatUV1.r;
float uv1g = vatUV1.g;
float multiplyBoundMinB = uv1r * oneMinusBoundMinB;
float2 texUV = HVAT_VatUV(selectedFrame, uv1r, uv1g,
oneMinusBoundMaxR, multiplyBoundMinB, totalFrames);
// 采样位置
float4 posSample = SAMPLE_TEXTURE2D_LOD(_posTexture, sampler_posTexture, texUV, 0);
float3 posRGB = posSample.rgb;
float posA = posSample.a;
// 双纹理高精度位置
if (_B_LOAD_POS_TWO_TEX > 0.5)
{
float4 pos2 = SAMPLE_TEXTURE2D_LOD(_posTexture2, sampler_posTexture2, texUV, 0);
posRGB += pos2.rgb * 0.01;
}
// 解码位移
float3 posDecoded = posRGB * (boundsMax - boundsMin) + boundsMin;
float3 displacement = comparisonBoundMaxb ? posRGB : posDecoded;
// SoftBody: 原始位置 + 位移
positionOS.xyz += displacement;
// 法线
if (_B_UNLOAD_ROT_TEX > 0.5)
{
normalOS = normalize(HVAT_DecodeCompressedNormal(posA));
}
else
{
float4 rotSample = SAMPLE_TEXTURE2D_LOD(_rotTexture, sampler_rotTexture, texUV, 0);
float4 rotFinal = comparisonBoundMaxb ? rotSample : (rotSample - 0.5) * 2.0;
normalOS = normalize(HVAT_RotateByQuat(float3(0.0, 1.0, 0.0), rotFinal));
}
return;
}
// ─────────────────────────────────────────────────────────────────
// Sub Mode 1: RigidBody — Pivot 旋转 + Pscale + 帧间插值
// ─────────────────────────────────────────────────────────────────
#elif defined(_HOUDINI_VAT_RIGIDBODY)
{
if (CheckLocalFlags1(FLAG_BIT_PARTICLE_1_IS_PARTICLE_SYSTEM))
{
return;
}
float2 vatUV1 = HVAT_GetVatUV1(input);
float uv1r = vatUV1.r;
float uv1g = vatUV1.g;
float multiplyBoundMinB = uv1r * oneMinusBoundMinB;
// 当前帧和下一帧 UV
float2 texUV = HVAT_VatUV(selectedFrame, uv1r, uv1g,
oneMinusBoundMaxR, multiplyBoundMinB, totalFrames);
float2 texUV_next = HVAT_VatUV(selectedFrame + 1.0, uv1r, uv1g,
oneMinusBoundMaxR, multiplyBoundMinB, totalFrames);
// 采样旋转
float4 rotSample = SAMPLE_TEXTURE2D_LOD(_rotTexture, sampler_rotTexture, texUV, 0);
float4 rotRemapped = (rotSample - 0.5) * 2.0;
float4 rotFinal = comparisonBoundMaxb ? rotSample : rotRemapped;
// 采样位置
float4 posSample = SAMPLE_TEXTURE2D_LOD(_posTexture, sampler_posTexture, texUV, 0);
float3 posRGB = posSample.rgb;
float posA = posSample.a;
// 四元数 maxComp 索引(从 posA 整数部分)
float quatMaxIdxScaled = posA * 4.0;
float quatMaxIdx = floor(quatMaxIdxScaled);
// 解码四元数
float4 q = HVAT_DecodeQuaternion(rotFinal.rgb, quatMaxIdx);
// 解码位置
float3 posRawForDecode = posRGB;
if (_B_LOAD_POS_TWO_TEX > 0.5)
{
float4 pos2 = SAMPLE_TEXTURE2D_LOD(_posTexture2, sampler_posTexture2, texUV, 0);
posRawForDecode = posRGB + pos2.rgb * 0.01;
}
float3 posDecoded = posRawForDecode * (boundsMax - boundsMin) + boundsMin;
float3 piecePos = comparisonBoundMaxb ? posRawForDecode : posDecoded;
// Pscale
float pscaleFromPosA = (1.0 - frac(quatMaxIdxScaled)) / pscaleDenom;
float pscale = (_B_pscaleAreInPosA > 0.5) ? pscaleFromPosA : 1.0;
float totalScale = _globalPscaleMul * pscale;
// Rest pivot 从 UV2/UV3
// Custom2 = TEXCOORD2 (uv2), vatTexcoord5 = TEXCOORD4 (uv3)
float3 restPivot = float3(-input.Custom2.r, input.vatTexcoord5.r, 1.0 - input.vatTexcoord5.g);
// 旋转局部偏移
float3 localOffset = positionOS.xyz - restPivot;
float3 rotatedLocal = HVAT_RotateByQuat(localOffset, q);
float3 scaledLocal = rotatedLocal * totalScale;
// 帧间插值
float3 finalPiecePos = piecePos;
if (_B_interpolate > 0.5)
{
float4 posSampleNext = SAMPLE_TEXTURE2D_LOD(_posTexture, sampler_posTexture, texUV_next, 0);
float3 posRGBNext = posSampleNext.rgb;
if (_B_LOAD_POS_TWO_TEX > 0.5)
{
float4 pos2Next = SAMPLE_TEXTURE2D_LOD(_posTexture2, sampler_posTexture2, texUV_next, 0);
posRGBNext += pos2Next.rgb * 0.01;
}
float3 posDecodedNext = posRGBNext * (boundsMax - boundsMin) + boundsMin;
float3 piecePosNext = comparisonBoundMaxb ? posRGBNext : posDecodedNext;
finalPiecePos = lerp(piecePos, piecePosNext, frameAlpha);
}
// 组装最终位置
float3 animatedPos = scaledLocal + finalPiecePos;
// 首帧复位
float wrappedForCheck = fmod(selectedFrame - 1.0, totalFrames);
bool isRestFrame = (wrappedForCheck < 0.5) && !(_animateFirstFrame > 0.5);
float3 finalPosOS = isRestFrame ? positionOS.xyz : animatedPos;
// 崩塌:无 piece 关联
finalPosOS = (uv1g <= 0.1) ? float3(0, 0, 0) : finalPosOS;
// 法线
float3 rotatedNormalOS = isRestFrame
? normalOS
: normalize(HVAT_RotateByQuat(normalOS, q));
normalOS = rotatedNormalOS;
positionOS.xyz = finalPosOS;
return;
}
// ─────────────────────────────────────────────────────────────────
// Sub Mode 2: DynamicRemeshing — Lookup Table → 绝对位置
// ─────────────────────────────────────────────────────────────────
#elif defined(_HOUDINI_VAT_DYNAMIC_REMESH)
{
// 使用 uv0 (texcoords.r/g) 作为 piece UV
float uv0r = input.texcoords.r;
float uv0g = input.texcoords.g;
float multiplyBoundMinB = uv0r * oneMinusBoundMinB;
float2 vatUV = HVAT_VatUV(selectedFrame, uv0r, uv0g,
oneMinusBoundMaxR, multiplyBoundMinB, totalFrames);
// Lookup Table
float4 lookupSample = SAMPLE_TEXTURE2D_LOD(_lookupTable, sampler_lookupTable, vatUV, 0);
float2 lookupUV = HVAT_DecodeLookupUV(lookupSample, _boundMinX);
// 采样位置(绝对坐标)
float4 posSample = SAMPLE_TEXTURE2D_LOD(_posTexture, sampler_posTexture, lookupUV, 0);
float3 posRGB = posSample.rgb;
float posA = posSample.a;
// 双纹理高精度位置
if (_B_LOAD_POS_TWO_TEX > 0.5)
{
float4 pos2 = SAMPLE_TEXTURE2D_LOD(_posTexture2, sampler_posTexture2, lookupUV, 0);
posRGB += pos2.rgb * 0.01;
}
// 解码绝对位置
float3 posDecoded = posRGB * (boundsMax - boundsMin) + boundsMin;
float3 finalPosOS = comparisonBoundMaxb ? posRGB : posDecoded;
// 无有效 piece 塌陷
finalPosOS = (uv0g <= 0.1) ? float3(0, 0, 0) : finalPosOS;
positionOS.xyz = finalPosOS;
// 法线
if (_B_UNLOAD_ROT_TEX > 0.5)
{
normalOS = normalize(HVAT_DecodeCompressedNormal(posA));
}
else
{
float4 rotSample = SAMPLE_TEXTURE2D_LOD(_rotTexture, sampler_rotTexture, lookupUV, 0);
float4 rotFinal = comparisonBoundMaxb ? rotSample : (rotSample - 0.5) * 2.0;
normalOS = normalize(HVAT_RotateByQuat(float3(0.0, 1.0, 0.0), rotFinal));
}
return;
}
// ─────────────────────────────────────────────────────────────────
#elif defined(_HOUDINI_VAT_PARTICLE_SPRITE)
// Sub Mode 3: ParticleSprite — Billboard + Spin + Origin Mask
// ─────────────────────────────────────────────────────────────────
{
float2 vatUV1 = HVAT_GetVatUV1(input);
float uv1r = vatUV1.r;
float uv1g = vatUV1.g;
float multiplyBoundMinB = uv1r * oneMinusBoundMinB;
// 当前帧 + 下一帧 UV
float2 vatUV = HVAT_VatUV(selectedFrame, uv1r, uv1g,
oneMinusBoundMaxR, multiplyBoundMinB, totalFrames);
float2 vatUV_next = HVAT_VatUV(selectedFrame + 1.0, uv1r, uv1g,
oneMinusBoundMaxR, multiplyBoundMinB, totalFrames);
// 采样位置
float4 posSample = SAMPLE_TEXTURE2D_LOD(_posTexture, sampler_posTexture, vatUV, 0);
float4 posSample_next = SAMPLE_TEXTURE2D_LOD(_posTexture, sampler_posTexture, vatUV_next, 0);
float3 posRGB = posSample.rgb;
float3 posRGB_next = posSample_next.rgb;
float posA = posSample.a;
float posA_next = posSample_next.a;
// 双纹理高精度位置
if (_B_LOAD_POS_TWO_TEX > 0.5)
{
float4 pos2 = SAMPLE_TEXTURE2D_LOD(_posTexture2, sampler_posTexture2, vatUV, 0);
float4 pos2_next = SAMPLE_TEXTURE2D_LOD(_posTexture2, sampler_posTexture2, vatUV_next, 0);
posRGB += pos2.rgb * 0.01;
posRGB_next += pos2_next.rgb * 0.01;
}
// 解码粒子中心
float3 posDecoded = posRGB * (boundsMax - boundsMin) + boundsMin;
float3 posDecoded_next = posRGB_next * (boundsMax - boundsMin) + boundsMin;
float3 particlePos = comparisonBoundMaxb ? posRGB : posDecoded;
float3 particlePos_next = comparisonBoundMaxb ? posRGB_next : posDecoded_next;
// 帧间插值
float3 particlePosF = (_B_interpolate > 0.5)
? lerp(particlePos, particlePos_next, frameAlpha)
: particlePos;
// Pscale
float posA_f = (_B_interpolate > 0.5) ? lerp(posA, posA_next, frameAlpha) : posA;
float pscaleRaw = posA_f / pscaleDenom;
// 每粒子随机缩放
float perParticleRandom = HVAT_HashRandom2D(float2(uv1r, uv1g));
float additionalPscaleMul = 1.0 + perParticleRandom;
// 原点遮挡 mask
float distThis = distance(particlePos, float3(0, 0, 0));
float distNext = distance(particlePos_next, float3(0, 0, 0));
float maskThis = saturate(sign(distThis - _originRadius));
float maskNext = saturate(sign(distNext - _originRadius));
float maskF = (_B_interpolate > 0.5) ? lerp(maskThis, maskNext, frameAlpha) : maskThis;
float pscaleFinal;
if (_B_pscaleAreInPosA > 0.5)
{
pscaleFinal = (_B_hideOverlappingOrigin > 0.5)
? pscaleRaw * _globalPscaleMul * additionalPscaleMul * maskF
: pscaleRaw * _globalPscaleMul * additionalPscaleMul;
}
else
{
pscaleFinal = (_B_hideOverlappingOrigin > 0.5)
? _globalPscaleMul * additionalPscaleMul * maskF
: _globalPscaleMul * additionalPscaleMul;
}
// Billboard 方向轴
float3 viewRight = float3(1, 0, 0);
float3 viewUp = float3(0, 1, 0);
float velStretch = 1.0;
float3 headingViewDir = float3(0, 0, 0);
if (_B_CAN_SPIN > 0.5)
{
// 从颜色通道读取 heading
if (_B_LOAD_COL_TEX > 0.5)
{
float4 colThis = SAMPLE_TEXTURE2D_LOD(_colTexture, sampler_colTexture, vatUV, 0);
headingViewDir = float3(-colThis.r, colThis.g, colThis.b);
}
if (_B_spinFromHeading > 0.5)
{
float2 hXY = headingViewDir.xy;
float hLen = length(hXY);
float2 hDir = (hLen > 1e-5) ? hXY / hLen : float2(1, 0);
viewRight = float3(hDir.x, hDir.y, 0);
viewUp = cross(viewRight, float3(0, 0, -1));
velStretch = _scaleByVelAmount * hLen;
}
else
{
float angle = frac(_spinPhase) * 6.283185;
float c = cos(angle);
float s = sin(angle);
viewRight = float3(c, s, 0);
viewUp = cross(viewRight, float3(0, 0, -1));
}
}
// 视图空间 → 世界空间 → 物体空间
float3 worldRight = mul((float3x3)UNITY_MATRIX_I_V, viewRight);
float3 worldUp = mul((float3x3)UNITY_MATRIX_I_V, viewUp);
float3 worldFwd = mul((float3x3)UNITY_MATRIX_I_V, float3(0, 0, 1));
float3 rightOS = normalize(TransformWorldToObjectDir(worldRight));
float3 upOS = normalize(TransformWorldToObjectDir(worldUp));
float3 normalOS_new = normalize(TransformWorldToObjectDir(worldFwd));
// Billboard 顶点偏移
float cornerX = input.texcoords.r - 0.5;
float cornerY = input.texcoords.g - 0.5;
float3 offsetX = rightOS * cornerX * _widthBaseScale * pscaleFinal;
float3 offsetY = upOS * cornerY * _heightBaseScale * pscaleFinal * velStretch;
float3 finalPosOS = particlePosF + offsetX + offsetY;
// 崩塌
finalPosOS = (uv1g <= 0.1) ? float3(0, 0, 0) : finalPosOS;
positionOS.xyz = finalPosOS;
normalOS = normalOS_new;
return;
}
#endif
}
#endif // HOUDINI_VAT_INCLUDED

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 802859f4f9ef96442bf245ade4894f0d
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,356 @@
#ifndef SIX_WAY_SMOKE_LIT_HLSL
#define SIX_WAY_SMOKE_LIT_HLSL
//这部分尽量借鉴 UnityEditor.VFX.HDRP.SixWaySmokeLit
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
// Generated from UnityEditor.VFX.HDRP.SixWaySmokeLit+BSDFData
// PackingRules = Exact
struct BSDFData
{
uint materialFeatures;
float absorptionRange;
real4 diffuseColor;
real3 fresnel0;
real ambientOcclusion;
float3 normalWS;
float4 tangentWS;
real3 geomNormalWS;
real3 rigRTBk;
real3 rigLBtF;
real3 bakeDiffuseLighting0;//rigRTBk.x
real3 bakeDiffuseLighting1;//rigRTBk.y
real3 bakeDiffuseLighting2;// bsdfData.tangentWS.w > 0.0f ? rigRTBk.z : rigLBtF.z
real3 backBakeDiffuseLighting0;//rigLBtF.x
real3 backBakeDiffuseLighting1;//rigLBtF.y
real3 backBakeDiffuseLighting2;// bsdfData.tangentWS.w > 0.0f ? rigLBtF.z : rigRTBk.z
//-----NBShaders-----
real3 emission;
real emissionInput;//NegativeTex.a
real alpha;//PositiveTex.a
};
#define ABSORPTION_EPSILON max(REAL_MIN, 1e-5)
real3 ComputeDensityScales(real3 absorptionColor)
{
absorptionColor.rgb = max(ABSORPTION_EPSILON, absorptionColor.rgb);
// Empirical value used to parametrize absorption from color
const real absorptionStrength = 0.2f;
return 1.0f + log2(absorptionColor.rgb) / log2(absorptionStrength);
}
real3 GetTransmissionWithAbsorption(real transmission, real4 absorptionColor, real absorptionRange)
{
#if defined(VFX_SIX_WAY_ABSORPTION)
real3 densityScales = ComputeDensityScales(absorptionColor.rgb);
#ifdef VFX_BLENDMODE_PREMULTIPLY
absorptionRange *= (absorptionColor.a > 0) ? absorptionColor.a : 1.0f;
#endif
// real3 outTransmission = GetTransmissionWithAbsorption(transmission, densityScales, absorptionRange);
real3 outTransmission = pow(saturate(transmission / absorptionRange), densityScales);
outTransmission *= absorptionRange;
return outTransmission;
#else
return transmission.xxx * absorptionColor.rgb; // simple multiply
#endif
}
void ModifyBakedDiffuseLighting(BSDFData bsdfData, inout float3 bakeDiffuseLighting)
{
bakeDiffuseLighting = 0;
// Scale to be energy conserving: Total energy = 4*pi; divided by 6 directions
float scale = 4.0f * PI / 6.0f;
float3 frontBakeDiffuseLighting = bsdfData.tangentWS.w > 0.0f ? bsdfData.bakeDiffuseLighting2 : bsdfData.backBakeDiffuseLighting2;
float3 backBakeDiffuseLighting = bsdfData.tangentWS.w > 0.0f ? bsdfData.backBakeDiffuseLighting2 : bsdfData.bakeDiffuseLighting2;
float3x3 bakeDiffuseLightingMat;
bakeDiffuseLightingMat[0] = bsdfData.bakeDiffuseLighting0;
bakeDiffuseLightingMat[1] = bsdfData.bakeDiffuseLighting1;
bakeDiffuseLightingMat[2] = frontBakeDiffuseLighting;
bakeDiffuseLighting += GetTransmissionWithAbsorption(bsdfData.rigRTBk.x, bsdfData.diffuseColor, bsdfData.absorptionRange) * bakeDiffuseLightingMat[0];
bakeDiffuseLighting += GetTransmissionWithAbsorption(bsdfData.rigRTBk.y, bsdfData.diffuseColor, bsdfData.absorptionRange) * bakeDiffuseLightingMat[1];
bakeDiffuseLighting += GetTransmissionWithAbsorption(bsdfData.rigRTBk.z, bsdfData.diffuseColor, bsdfData.absorptionRange) * bakeDiffuseLightingMat[2];
bakeDiffuseLightingMat[0] = bsdfData.backBakeDiffuseLighting0;
bakeDiffuseLightingMat[1] = bsdfData.backBakeDiffuseLighting1;
bakeDiffuseLightingMat[2] = backBakeDiffuseLighting;
bakeDiffuseLighting += GetTransmissionWithAbsorption(bsdfData.rigLBtF.x, bsdfData.diffuseColor, bsdfData.absorptionRange) * bakeDiffuseLightingMat[0];
bakeDiffuseLighting += GetTransmissionWithAbsorption(bsdfData.rigLBtF.y, bsdfData.diffuseColor, bsdfData.absorptionRange) * bakeDiffuseLightingMat[1];
bakeDiffuseLighting += GetTransmissionWithAbsorption(bsdfData.rigLBtF.z, bsdfData.diffuseColor, bsdfData.absorptionRange) * bakeDiffuseLightingMat[2];
bakeDiffuseLighting *= scale;
}
//世界空间到切线空间方向转换
float3 TransformToLocalFrame(float3 L, BSDFData bsdfData)
{
float3 zVec = -bsdfData.normalWS;
float3 xVec = bsdfData.tangentWS.xyz;
float3 yVec = -cross(zVec, xVec) * bsdfData.tangentWS.w;//原代码没有负值,实际测试需要负值
float3x3 tbn = float3x3(xVec, yVec, zVec);
return mul(tbn, L);
}
CBSDF EvaluateBSDF(float3 L, BSDFData bsdfData)
{
CBSDF cbsdf;
ZERO_INITIALIZE(CBSDF, cbsdf);
float3 dir = TransformToLocalFrame(L, bsdfData);
float3 weights = dir >= 0 ? bsdfData.rigRTBk.xyz : bsdfData.rigLBtF.xyz;
float3 sqrDir = dir*dir;
cbsdf.diffR = GetTransmissionWithAbsorption(dot(sqrDir, weights), bsdfData.diffuseColor, bsdfData.absorptionRange);
return cbsdf;
}
//这一步最好在面板上做完
half GetAbsorptionRange(float absorptionStrenth)
{
return INV_PI + saturate(absorptionStrenth) * (1 - INV_PI);
}
//---------NBShaderUtility-----------
//UseInVerTex
void GetSixWayBakeDiffuseLight(real3 normalWS,real3 tangentWS,real3 biTangentWS,
inout half3 bakeDiffuseLighting0,inout half3 bakeDiffuseLighting1,inout half3 bakeDiffuseLighting2,
inout half3 backBakeDiffuseLighting0,inout half3 backBakeDiffuseLighting1,inout half3 backBakeDiffuseLighting2)
{
bakeDiffuseLighting0 = SampleSHVertex(tangentWS);
bakeDiffuseLighting1 = SampleSHVertex(biTangentWS);
bakeDiffuseLighting2 = SampleSHVertex(-normalWS);
backBakeDiffuseLighting0 = SampleSHVertex(-tangentWS);
backBakeDiffuseLighting1 = SampleSHVertex(-biTangentWS);
backBakeDiffuseLighting2 = SampleSHVertex(normalWS);
}
LightingData CreateSixWayLightingData(InputData inputData, half3 emission)
{
LightingData lightingData;
lightingData.giColor = inputData.bakedGI;
lightingData.emissionColor = emission;
lightingData.vertexLightingColor = 0;
lightingData.mainLightColor = 0;
lightingData.additionalLightsColor = 0;
return lightingData;
}
void GetSixWayEmission(inout BSDFData bsdfData,Texture2D rampMap,half4 emissionColor,bool isRampMap)
{
float input = pow(bsdfData.emissionInput,_SixWayInfo.y);
half3 emission = emissionColor * emissionColor.a;
if (isRampMap)
{
half4 rampSample = rampMap.Sample(sampler_linear_clamp,half2(input,0.5));
emission = emission * rampSample * rampSample.a;
}
else
{
emission *= input;
}
bsdfData.emission = emission;
}
half3 LightingSixWay(Light light,InputData inputData, BSDFData bsdfData)
{
half3 cbsdf_R = EvaluateBSDF(light.direction,bsdfData).diffR;
half3 radiance = light.color * light.distanceAttenuation * light.shadowAttenuation;
return PI * cbsdf_R * radiance;
}
//光照流程--->原型为UniversalFragmentBlinnPhong
half4 UniversalFragmentSixWay(InputData inputData,BSDFData bsdfData)
{
// #if defined(DEBUG_DISPLAY)
// half4 debugColor;
//
// if (CanDebugOverrideOutputColor(inputData, surfaceData, debugColor))
// {
// return debugColor;
// }
// #endif
#ifdef _LIGHT_LAYERS
uint meshRenderingLayers = GetMeshRenderingLayer();
#endif
half4 shadowMask = CalculateShadowMask(inputData);
// AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
AmbientOcclusionFactor aoFactor;
aoFactor.directAmbientOcclusion = 1;
aoFactor.indirectAmbientOcclusion = 1;
Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
// MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, aoFactor);
// inputData.bakedGI *= surfaceData.albedo;
// LightingData lightingData = CreateLightingData(inputData, surfaceData);
LightingData lightingData = CreateSixWayLightingData(inputData,bsdfData.emission);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
#endif
{
lightingData.mainLightColor += LightingSixWay(mainLight, inputData, bsdfData);
}
#if defined(_ADDITIONAL_LIGHTS)
uint pixelLightCount = GetAdditionalLightsCount();
#if USE_FORWARD_PLUS
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
{
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingSixWay(light, inputData, bsdfData);
}
}
#endif
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingSixWay(light, inputData, bsdfData);
}
LIGHT_LOOP_END
#endif
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
lightingData.vertexLightingColor += inputData.vertexLighting * surfaceData.albedo;
#endif
return CalculateFinalColor(lightingData, bsdfData.alpha);
}
half3 LightingHalfLambert(half3 lightColor, half3 lightDir, half3 normal)
{
half NdotL = saturate(dot(normal, lightDir));
NdotL = NdotL*0.5 + 0.5;
return lightColor * NdotL;
}
half3 CalculateHalfLambertBlinnPhong(Light light, InputData inputData, SurfaceData surfaceData)
{
half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
half3 lightDiffuseColor = LightingHalfLambert(attenuatedLightColor, light.direction, inputData.normalWS);
half3 lightSpecularColor = half3(0,0,0);
#if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR)
half smoothness = exp2(10 * surfaceData.smoothness + 1);
lightSpecularColor += LightingSpecular(attenuatedLightColor, light.direction, inputData.normalWS, inputData.viewDirectionWS, half4(surfaceData.specular, 1), smoothness);
#endif
#if _ALPHAPREMULTIPLY_ON
return lightDiffuseColor * surfaceData.albedo * surfaceData.alpha + lightSpecularColor;
#else
return lightDiffuseColor * surfaceData.albedo + lightSpecularColor;
#endif
}
half4 UniversalFragmentHalfLambert(InputData inputData, half3 diffuse, half4 specularGloss, half smoothness, half3 emission, half alpha, half3 normalTS)
{
SurfaceData surfaceData;
surfaceData.albedo = diffuse;
surfaceData.alpha = alpha;
surfaceData.emission = emission;
surfaceData.metallic = 0;
surfaceData.occlusion = 1;
surfaceData.smoothness = smoothness;
surfaceData.specular = specularGloss.rgb;
surfaceData.clearCoatMask = 0;
surfaceData.clearCoatSmoothness = 1;
surfaceData.normalTS = normalTS;
// #if defined(DEBUG_DISPLAY)
// half4 debugColor;
//
// if (CanDebugOverrideOutputColor(inputData, surfaceData, debugColor))
// {
// return debugColor;
// }
// #endif
#ifdef _LIGHT_LAYERS
uint meshRenderingLayers = GetMeshRenderingLayer();
#endif
half4 shadowMask = CalculateShadowMask(inputData);
AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, aoFactor);
inputData.bakedGI *= surfaceData.albedo;
LightingData lightingData = CreateLightingData(inputData, surfaceData);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
#endif
{
lightingData.mainLightColor += CalculateHalfLambertBlinnPhong(mainLight, inputData, surfaceData);
}
#if defined(_ADDITIONAL_LIGHTS)
uint pixelLightCount = GetAdditionalLightsCount();
#if USE_FORWARD_PLUS
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
{
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += CalculateBlinnPhong(light, inputData, surfaceData);
}
}
#endif
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += CalculateHalfLambertBlinnPhong(light, inputData, surfaceData);
}
LIGHT_LOOP_END
#endif
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
lightingData.vertexLightingColor += inputData.vertexLighting * surfaceData.albedo;
#endif
return CalculateFinalColor(lightingData, surfaceData.alpha);
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 70e94bdedafe40bfb7f8e77cdc4bef0f
timeCreated: 1751704445

View File

@@ -0,0 +1,585 @@
#ifndef TYFLOW_VAT_INCLUDED
#define TYFLOW_VAT_INCLUDED
#define TYFLOW_VAT_SKIN_MAX_BONES 7
#if defined(_VAT) && defined(_VAT_TYFLOW) && \
!defined(_TYFLOW_VAT_ABSOLUTE) && \
!defined(_TYFLOW_VAT_RELATIVE) && \
!defined(_TYFLOW_VAT_SKIN_R) && \
!defined(_TYFLOW_VAT_SKIN_PR) && \
!defined(_TYFLOW_VAT_SKIN_PRSAVE) && \
!defined(_TYFLOW_VAT_SKIN_PRSXYZ)
#define _TYFLOW_VAT_ABSOLUTE
#endif
#if defined(_TYFLOW_VAT_SKIN_R) || \
defined(_TYFLOW_VAT_SKIN_PR) || \
defined(_TYFLOW_VAT_SKIN_PRSAVE) || \
defined(_TYFLOW_VAT_SKIN_PRSXYZ)
#define TYFLOW_VAT_SKIN_MODE
#endif
struct TyflowVatMatrix3
{
float3 row0;
float3 row1;
float3 row2;
float3 row3;
};
struct TyflowVatTMParts
{
float3 pos;
float4 rot;
float3 scale;
};
inline int TyflowVatUnpackIntRGBA(int4 bytes)
{
return ((bytes.x << 24) + (bytes.y << 16) + (bytes.z << 8) + (bytes.w << 0));
}
inline float TyflowVatUnpackFloatRGBA(int4 bytes)
{
int sign = (bytes.r & 128) > 0 ? -1 : 1;
int expR = (bytes.r & 127) << 1;
int expG = bytes.g >> 7;
int exponent = expR + expG;
int signifG = (bytes.g & 127) << 16;
int signifB = bytes.b << 8;
float significand = (signifG + signifB + bytes.a) / pow(2, 23);
significand += 1;
return sign * significand * pow(2, exponent - 127);
}
inline half TyflowVatUnpackHalfRGBA(int2 bytes)
{
uint value = (bytes.x << 8) | bytes.y;
uint sign = (value & 0x8000) > 0;
int exponent = (value & 0x7C00) >> 10;
uint mantissa = (value & 0x03FF);
if ((value & 0x7FFF) == 0)
{
return sign ? -0 : 0;
}
if (exponent == 0x001F)
{
if (mantissa == 0)
{
return sign ? -1e+28 : 1e+28;
}
return 1e+28;
}
if (exponent > 0)
{
float result = pow(2.0, exponent - 15) * (1 + mantissa * (1 / 1024.0f));
return sign ? -result : result;
}
float subnormal = pow(2.0, -24) * mantissa;
return sign ? -subnormal : subnormal;
}
float4 TyflowVatSampleTexel(uint x, uint y)
{
float2 uv = float2(
((float)x + 0.5f) * _VATTex_TexelSize.x,
1.0f - (((float)y + 0.5f) * _VATTex_TexelSize.y));
return SAMPLE_TEXTURE2D_LOD(_VATTex, sampler_point_clamp, uv, 0);
}
float4 TyflowVatSampleEncodedTexel(uint x, uint y)
{
float4 sample = TyflowVatSampleTexel(x, y);
if (_LinearToGamma > 0.5f)
{
sample.r = LinearToGammaSpaceExact(sample.r);
sample.g = LinearToGammaSpaceExact(sample.g);
sample.b = LinearToGammaSpaceExact(sample.b);
}
return sample;
}
int TyflowVatTex2DInt(int arrInx)
{
uint width = (uint)_VATTex_TexelSize.z;
uint x = (uint)arrInx % width;
uint y = (uint)arrInx / width;
float4 bytes = TyflowVatSampleEncodedTexel(x, y);
int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255));
return TyflowVatUnpackIntRGBA(byteInts.wzyx);
}
float TyflowVatTex2DFloat(int arrInx)
{
uint width = (uint)_VATTex_TexelSize.z;
uint x = (uint)arrInx % width;
uint y = (uint)arrInx / width;
float4 bytes = TyflowVatSampleEncodedTexel(x, y);
int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255));
return TyflowVatUnpackFloatRGBA(byteInts.wzyx);
}
half TyflowVatTex2DHalf(float arrInxF)
{
uint arrInx = (uint)floor(arrInxF + 0.1f);
uint width = (uint)_VATTex_TexelSize.z;
uint x = arrInx % width;
uint y = arrInx / width;
float4 bytes = TyflowVatSampleEncodedTexel(x, y);
int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255));
if (abs(arrInxF - round(arrInxF)) > 0.25f)
{
return TyflowVatUnpackHalfRGBA(byteInts.wz);
}
return TyflowVatUnpackHalfRGBA(byteInts.yx);
}
half2 TyflowVatTex2DHalfs2(float arrInxF)
{
uint arrInx = (uint)floor(arrInxF + 0.1f);
uint width = (uint)_VATTex_TexelSize.z;
uint x = arrInx % width;
uint y = arrInx / width;
float4 bytes = TyflowVatSampleEncodedTexel(x, y);
int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255));
return half2(TyflowVatUnpackHalfRGBA(byteInts.yx), TyflowVatUnpackHalfRGBA(byteInts.wz));
}
float3 TyflowVatMultiplyPosition(TyflowVatMatrix3 matrixValue, float3 pos)
{
return float3(
pos.x * matrixValue.row0[0] + pos.y * matrixValue.row1[0] + pos.z * matrixValue.row2[0] + matrixValue.row3[0],
pos.x * matrixValue.row0[1] + pos.y * matrixValue.row1[1] + pos.z * matrixValue.row2[1] + matrixValue.row3[1],
pos.x * matrixValue.row0[2] + pos.y * matrixValue.row1[2] + pos.z * matrixValue.row2[2] + matrixValue.row3[2]);
}
TyflowVatMatrix3 TyflowVatQuaternionToTM(float4 quat)
{
TyflowVatMatrix3 matrixValue;
float x = quat.x;
float y = quat.y;
float z = quat.z;
float w = quat.w;
matrixValue.row0[0] = 1 - 2 * (y * y + z * z);
matrixValue.row0[1] = 2 * (x * y + z * w);
matrixValue.row0[2] = 2 * (x * z - y * w);
matrixValue.row1[0] = 2 * (x * y - z * w);
matrixValue.row1[1] = 1 - 2 * (x * x + z * z);
matrixValue.row1[2] = 2 * (y * z + x * w);
matrixValue.row2[0] = 2 * (x * z + y * w);
matrixValue.row2[1] = 2 * (y * z - x * w);
matrixValue.row2[2] = 1 - 2 * (x * x + y * y);
matrixValue.row3 = float3(0, 0, 0);
return matrixValue;
}
TyflowVatMatrix3 TyflowVatScaleTM(TyflowVatMatrix3 matrixValue, float3 scale)
{
matrixValue.row0 *= scale.x;
matrixValue.row1 *= scale.y;
matrixValue.row2 *= scale.z;
return matrixValue;
}
TyflowVatMatrix3 TyflowVatTranslateTM(TyflowVatMatrix3 matrixValue, float3 translation)
{
matrixValue.row3 = translation;
return matrixValue;
}
float4 TyflowVatQlerp(float4 a, float4 b, float blend)
{
float s1 = 1.0f - blend;
float s2 = dot(a, b) < 0.0f ? -blend : blend;
return normalize(float4(
s1 * a.x + s2 * b.x,
s1 * a.y + s2 * b.y,
s1 * a.z + s2 * b.z,
s1 * a.w + s2 * b.w));
}
int TyflowVatGetMetaDataSize()
{
#if defined(_TYFLOW_VAT_SKIN_PRSXYZ)
return 12;
#else
if (_DeformingSkin > 0.5f)
{
return 12;
}
return 3;
#endif
}
float3 TyflowVatGetTMPos(float startIndex)
{
float3 result;
for (int i = 0; i < 3; i++)
{
result[i] = TyflowVatTex2DFloat(startIndex + i);
}
return result;
}
float4 TyflowVatGetTMRot(float startIndex)
{
half2 rotHalfs1 = TyflowVatTex2DHalfs2(startIndex);
half2 rotHalfs2 = TyflowVatTex2DHalfs2(startIndex + 1);
return float4(-rotHalfs1.x, -rotHalfs1.y, -rotHalfs2.x, rotHalfs2.y);
}
float3 TyflowVatGetTMScaleXYZ(float startIndex)
{
half2 scaleHalfs1 = TyflowVatTex2DHalfs2(startIndex);
half2 scaleHalfs2 = TyflowVatTex2DHalfs2(startIndex + 1);
return float3(scaleHalfs1.x, scaleHalfs1.y, scaleHalfs2.x);
}
float3 TyflowVatGetTMScaleAve(float startIndex)
{
half scaleHalf = TyflowVatTex2DHalf(startIndex);
return float3(scaleHalf, scaleHalf, scaleHalf);
}
TyflowVatMatrix3 TyflowVatTMFromParts(TyflowVatTMParts parts)
{
TyflowVatMatrix3 matrixValue = TyflowVatQuaternionToTM(parts.rot);
matrixValue = TyflowVatScaleTM(matrixValue, parts.scale);
matrixValue = TyflowVatTranslateTM(matrixValue, parts.pos);
return matrixValue;
}
TyflowVatMatrix3 TyflowVatGetVertexInvTM(int tmInx)
{
TyflowVatMatrix3 matrixValue;
int pixelsPerTM = TyflowVatGetMetaDataSize();
float tmRowInx = 2 + (pixelsPerTM * tmInx);
matrixValue.row0 = TyflowVatGetTMPos(tmRowInx);
tmRowInx += 3;
matrixValue.row1 = TyflowVatGetTMPos(tmRowInx);
tmRowInx += 3;
matrixValue.row2 = TyflowVatGetTMPos(tmRowInx);
tmRowInx += 3;
matrixValue.row3 = TyflowVatGetTMPos(tmRowInx);
return matrixValue;
}
TyflowVatTMParts TyflowVatGetVertexTMPartsAtFrame(int tmInx, int frame, int numTMs)
{
float4 rot = float4(0, 0, 0, 1);
float3 pos = float3(0, 0, 0);
float3 scale = float3(1, 1, 1);
float pixelsPerTM = 0;
#if defined(_TYFLOW_VAT_SKIN_R)
pixelsPerTM = 2;
#elif defined(_TYFLOW_VAT_SKIN_PR)
pixelsPerTM = 5;
#elif defined(_TYFLOW_VAT_SKIN_PRSXYZ)
pixelsPerTM = 7;
#elif defined(_TYFLOW_VAT_SKIN_PRSAVE)
pixelsPerTM = 6;
#endif
float frameTMInx = (2 + numTMs * TyflowVatGetMetaDataSize()) + (frame * numTMs * pixelsPerTM) + (pixelsPerTM * tmInx);
#if defined(_TYFLOW_VAT_SKIN_R)
pos = TyflowVatGetTMPos(2 + tmInx * TyflowVatGetMetaDataSize());
rot = TyflowVatGetTMRot(frameTMInx);
#elif defined(_TYFLOW_VAT_SKIN_PR)
pos = TyflowVatGetTMPos(frameTMInx);
frameTMInx += 3;
rot = TyflowVatGetTMRot(frameTMInx);
#elif defined(_TYFLOW_VAT_SKIN_PRSXYZ)
pos = TyflowVatGetTMPos(frameTMInx);
frameTMInx += 3;
rot = TyflowVatGetTMRot(frameTMInx);
frameTMInx += 2;
scale = TyflowVatGetTMScaleXYZ(frameTMInx);
#elif defined(_TYFLOW_VAT_SKIN_PRSAVE)
pos = TyflowVatGetTMPos(frameTMInx);
frameTMInx += 3;
rot = TyflowVatGetTMRot(frameTMInx);
frameTMInx += 2;
scale = TyflowVatGetTMScaleAve(frameTMInx);
#endif
TyflowVatTMParts parts;
parts.pos = pos;
parts.rot = rot;
parts.scale = scale;
return parts;
}
float4 TyflowVatGetVertexValueAtFrame(uint vertexIndex, int vertexCount, int frame, int frameOffset)
{
float4 result = float4(0, 0, 0, 0);
vertexIndex += (vertexCount * frame) + (vertexCount * frameOffset);
if (_RGBAEncoded > 0.5f)
{
if (_RGBAHalf > 0.5f)
{
vertexIndex *= 3;
for (int i = 0; i < 3; i++)
{
float arrInxF = (vertexIndex + i) * 0.5f;
result[i] = TyflowVatTex2DHalf(arrInxF);
}
}
else
{
vertexIndex *= 3;
for (int i = 0; i < 3; i++)
{
result[i] = TyflowVatTex2DFloat(vertexIndex + i);
}
}
}
else
{
uint width = (uint)_VATTex_TexelSize.z;
uint x = vertexIndex % width;
uint y = vertexIndex / width;
result = TyflowVatSampleTexel(x, y);
}
return result;
}
float3 TyflowVatGetLocalVertexPosFromTM(float3 pos, TyflowVatMatrix3 invTM)
{
float3 localPos = ((pos / _ImportScale) * float3(-1, 1, 1));
return TyflowVatMultiplyPosition(invTM, localPos);
}
float3 TyflowVatGetLocalVertexPosFromPos(float3 pos, float boneInx)
{
int tmInx = 2 + (int)round(boneInx) * TyflowVatGetMetaDataSize();
float3 tmPos = float3(0, 0, 0);
for (int i = 0; i < 3; i++)
{
tmPos[i] = TyflowVatTex2DFloat(tmInx + i);
}
return ((pos / _ImportScale) * float3(-1, 1, 1)) - tmPos;
}
float2 TyflowVatGetSkinTexcoord(AttributesParticle input, int index)
{
if (index == 0) return input.Custom1.xy;
if (index == 1) return input.Custom2.xy;
#if !defined(_FLIPBOOKBLENDING_ON)
if (index == 2) return input.vatTexcoord4;
#endif
if (index == 3) return input.vatTexcoord5;
if (index == 4) return input.vatTexcoord6;
if (index == 5) return input.vatTexcoord7;
if (index == 6) return input.vatTexcoord8;
return float2(0, 0);
}
void ApplyTyflowVAT(AttributesParticle input, inout float4 positionOS, inout float3 normalOS)
{
#if defined(SHADOWS_DEPTH)
if (_AffectsShadows < 0.5f)
{
return;
}
#endif
float frameBase = _Frame;
float frameCustomData = GetCustomData(NB_CUSTOM_DATA_FLAG_2, FLAGBIT_POS_2_CUSTOMDATA_VAT_FRAME, -1.0f, input.Custom1, input.Custom2);
if (frameCustomData >= 0.0f)
{
frameBase = saturate(frameCustomData) * max(_Frames - 1.0f, 0.0f);
}
float frame = abs(frameBase + ((_Autoplay > 0.5f) ? (time * 30.0f * _AutoplaySpeed) : 0.0f));
frame = (_Loop > 0.5f) ? fmod(frame, _Frames) : min(frame, _Frames - 1.0f);
if ((_Loop > 0.5f) && (_InterpolateLoop < 0.5f) && (frame >= _Frames - 1.0f))
{
frame = _Frames - 1.0f;
}
uint frame0 = (uint)floor(frame);
uint frame1 = (uint)ceil(frame) % (uint)_Frames;
float frameInterp = frame - frame0;
#if defined(TYFLOW_VAT_SKIN_MODE)
if (CheckLocalFlags1(FLAG_BIT_PARTICLE_1_IS_PARTICLE_SYSTEM))
{
return;
}
else
{
int numTMs = TyflowVatTex2DInt(1);
float3 combinedPos = float3(0, 0, 0);
float3 combinedNormal = float3(0, 0, 0);
int loopCount = (_DeformingSkin > 0.5f) ? (int)_SkinBoneCount : 1;
[unroll]
for (int i = 0; i < TYFLOW_VAT_SKIN_MAX_BONES; i++)
{
if (i >= loopCount)
{
break;
}
float weight = 1.0f;
float tmInx = round(input.Custom1.y);
if (_DeformingSkin > 0.5f)
{
float2 texcoord = TyflowVatGetSkinTexcoord(input, i);
tmInx = round(texcoord.x);
weight = texcoord.y;
}
TyflowVatTMParts tmParts0 = TyflowVatGetVertexTMPartsAtFrame((int)tmInx, frame0, numTMs);
if (_FrameInterpolation > 0.5f)
{
TyflowVatTMParts tmParts1 = TyflowVatGetVertexTMPartsAtFrame((int)tmInx, frame1, numTMs);
tmParts0.pos = lerp(tmParts0.pos, tmParts1.pos, frameInterp);
tmParts0.rot = TyflowVatQlerp(tmParts0.rot, tmParts1.rot, frameInterp);
tmParts0.scale = lerp(tmParts0.scale, tmParts1.scale, frameInterp);
}
TyflowVatMatrix3 tm = TyflowVatTMFromParts(tmParts0);
TyflowVatMatrix3 invStartTM;
#if defined(_TYFLOW_VAT_SKIN_PRSXYZ)
bool useInvStartTM = true;
#else
bool useInvStartTM = _DeformingSkin > 0.5f;
#endif
if (useInvStartTM)
{
invStartTM = TyflowVatGetVertexInvTM((int)tmInx);
}
else
{
invStartTM.row0 = float3(0, 0, 0);
invStartTM.row1 = float3(0, 0, 0);
invStartTM.row2 = float3(0, 0, 0);
invStartTM.row3 = float3(0, 0, 0);
}
float3 localPos;
if (useInvStartTM)
{
localPos = TyflowVatGetLocalVertexPosFromTM(positionOS.xyz, invStartTM);
}
else
{
localPos = TyflowVatGetLocalVertexPosFromPos(positionOS.xyz, tmInx);
}
float3 pos = TyflowVatMultiplyPosition(tm, localPos) * float3(-1, 1, 1);
combinedPos += (pos * _ImportScale) * weight;
#if !defined(SHADOWS_DEPTH)
{
float3 animatedNormal = normalOS;
tm.row3 = float3(0, 0, 0);
if (useInvStartTM)
{
invStartTM.row3 = float3(0, 0, 0);
float3 localNormal = TyflowVatGetLocalVertexPosFromTM(animatedNormal, invStartTM);
animatedNormal = normalize(TyflowVatMultiplyPosition(tm, localNormal)) * float3(-1, 1, 1);
}
else
{
tm = TyflowVatTranslateTM(tm, float3(0, 0, 0));
animatedNormal = normalize(TyflowVatMultiplyPosition(tm, animatedNormal * float3(-1, 1, 1))) * float3(-1, 1, 1);
}
combinedNormal += animatedNormal * weight;
}
#endif
}
positionOS.xyz = combinedPos;
normalOS = combinedNormal;
}
#elif defined(_TYFLOW_VAT_ABSOLUTE) || defined(_TYFLOW_VAT_RELATIVE)
{
float2 tyflowVatIndexData = CheckLocalFlags1(FLAG_BIT_PARTICLE_1_IS_PARTICLE_SYSTEM)
? input.texcoords.zw
: input.Custom1.xy;
uint vertexIndex = (uint)round(tyflowVatIndexData.x);
uint vertexCount = (uint)round(tyflowVatIndexData.y);
float4 vertexOffset0 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame0, 0);
float4 vertexOffset = vertexOffset0;
if (_FrameInterpolation > 0.5f)
{
float4 vertexOffset1 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame1, 0);
vertexOffset = lerp(vertexOffset0, vertexOffset1, frameInterp);
}
#if defined(_TYFLOW_VAT_RELATIVE)
positionOS.xyz += (vertexOffset * float4(-1, 1, 1, 1) * _ImportScale).xyz;
#else
positionOS.xyz = (vertexOffset * float4(-1, 1, 1, 1) * _ImportScale).xyz;
#endif
#if !defined(SHADOWS_DEPTH)
if (_VATIncludesNormals > 0.5f)
{
float4 normal0 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame0, (int)_Frames);
float4 animatedNormal = normal0;
if (_FrameInterpolation > 0.5f)
{
float4 normal1 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame1, (int)_Frames);
animatedNormal = lerp(normal0, normal1, frameInterp) * float4(-1, 1, 1, 1);
}
normalOS = normalize(animatedNormal.xyz);
}
#endif
}
#endif
}
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e5bde8a4dd9946d4ab49c8b1f3bc6d12
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
#ifndef PARTICLE_VAT_INCLUDED
#define PARTICLE_VAT_INCLUDED
#include "HoudiniVAT.hlsl"
#include "TyflowVAT.hlsl"
void ApplyVAT(AttributesParticle input, inout float4 positionOS, inout float3 normalOS)
{
#if defined(_VAT)
if (_VAT_Toggle < 0.5f)
{
return;
}
#if defined(_VAT_HOUDINI)
ApplyHoudiniVAT(input, positionOS, normalOS);
#elif defined(_VAT_TYFLOW)
ApplyTyflowVAT(input, positionOS, normalOS);
#endif
#endif
}
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a0126cd1b294e894f9ac10e46b7776d6
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,391 @@
#ifndef XUANXUAN_UTILITY
#define XUANXUAN_UTILITY
//引入自UnityCG.cginc
#define UNITY_PI 3.14159265359f
#define UNITY_TWO_PI 6.28318530718f
#define UNITY_FOUR_PI 12.56637061436f
#define UNITY_INV_PI 0.31830988618f
#define UNITY_INV_TWO_PI 0.15915494309f
#define UNITY_INV_FOUR_PI 0.07957747155f
#define UNITY_HALF_PI 1.57079632679f
#define UNITY_INV_HALF_PI 0.636619772367f
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
inline half NB_Remap(half x, half inMin, half inMax, half outMin, half outMax)
{
// x= clamp(x,inMin,inMax);
return saturate((x - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin;
}
inline half NB_Remap01(half x, half inMin, half inMax)
{
// x= clamp(x,inMin,inMax);
return saturate((x - inMin) / (inMax - inMin));
}
inline half NB_RemapNoClamp(half x, half inMin, half inMax, half outMin, half outMax)
{
// x= clamp(x,inMin,inMax);
return ((x - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin;
}
//原生SmoothStep开销很大https://zhuanlan.zhihu.com/p/34629262
//在很多时候可以用简单线性映射代替。
half SimpleSmoothstep(half min,half max,half interp)
{
return saturate((interp - min)/(max-min));
}
half4 TryLinearize(half4 color)
{
#if defined(UNITY_COLORSPACE_GAMMA)
return pow(color, 2.2f);
#else
return color;
#endif
}
half4 TryLinearizeWithoutAlpha(half4 color)
{
#if defined(UNITY_COLORSPACE_GAMMA)
return half4(pow(color.rgb, 2.2f), color.a);
#else
return color;
#endif
}
half3 TryLinearize(half3 color)
{
#if defined(UNITY_COLORSPACE_GAMMA)
return pow(color, 2.2f);
#else
return color;
#endif
}
half2 TryLinearize(half2 color)
{
#if defined(UNITY_COLORSPACE_GAMMA)
return pow(color, 2.2f);
#else
return color;
#endif
}
half TryLinearize(half color)
{
#if defined(UNITY_COLORSPACE_GAMMA)
return pow(color, 2.2f);
#else
return color;
#endif
}
half4 tex2D_TryLinearizeWithoutAlpha(sampler2D tex, float2 uv)
{
#if defined(UNITY_COLORSPACE_GAMMA)
return TryLinearizeWithoutAlpha(tex2D(tex, uv));
#else
return tex2D(tex, uv);
#endif
}
inline float LinearToGammaSpaceExact (float value)
{
if (value <= 0.0F)
return 0.0F;
else if (value <= 0.0031308F)
return 12.92F * value;
else if (value < 1.0F)
return 1.055F * pow(value, 0.4166667F) - 0.055F;
else
return pow(value, 0.45454545F);
}
// real FastSRGBToLinear(real c)
// {
// return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878);
// }
// float2 Rotate_Radians_float(float2 UV, float2 Center, float Rotation)
// {
// if(Rotation == 0)
// {
// return UV;
// }
//
// // Rotation = Rotation / 180 * 3.14; //从角度转为弧度。
// Rotation *= 0.01745329222; //从角度转为弧度。
// UV -= Center;
// float s = sin(Rotation);
// float c = cos(Rotation);
// float2x2 rMatrix = float2x2(c, -s, s, c);
// rMatrix *= 0.5;
// rMatrix += 0.5;
// rMatrix = rMatrix * 2 - 1;
// UV.xy = mul(UV.xy, rMatrix);
// UV += Center;
// return UV;
// }
float2 Rotate_Radians_float(float2 UV, float2 Center, float Rotation)
{
if(Rotation == 0)
{
return UV;
}
// Rotation = Rotation / 180 * 3.14; //从角度转为弧度。
Rotation *= 0.01745329222; //从角度转为弧度。
float s, c; // 如果 angle 是 uniform每帧或每物件算一次更省
sincos(Rotation, s, c); // HLSL 内置,一次算出 sin/cos
float2 d = UV - Center;
// 旋转 (x', y') = ( c*x - s*y, s*x + c*y )
float2 r = float2(dot(d, float2( c, -s)),
dot(d, float2( s, c)));
return r + Center;
}
inline half luminance(half3 color)
{
return dot(color, float3(0.2126f, 0.7152f, 0.0722f));
}
inline half DepthFactor(float Z, float near, float far)
{
Z = saturate((Z-near)/(far - near));
return Z;
}
//抽自Unity.cginc
inline half3 LinearToGammaSpace (half3 linRGB)
{
linRGB = max(linRGB, half3(0.h, 0.h, 0.h));
// An almost-perfect approximation from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return max(1.055h * pow(linRGB, 0.416666667h) - 0.055h, 0.h);
// Exact version, useful for debugging.
//return half3(LinearToGammaSpaceExact(linRGB.r), LinearToGammaSpaceExact(linRGB.g), LinearToGammaSpaceExact(linRGB.b));
}
//ASE
float2 voronoihash1( float2 p )
{
p = float2( dot( p, float2( 127.1, 311.7 ) ), dot( p, float2( 269.5, 183.3 ) ) );
return frac( sin( p ) *43758.5453);
}
//ASE
float voronoi1( float2 v, float time, inout float2 id, inout float2 mr, float smoothness )
{
float2 n = floor( v );
float2 f = frac( v );
float F1 = 8.0;
float F2 = 8.0; float2 mg = 0;
for ( int j = -1; j <= 1; j++ )
{
for ( int i = -1; i <= 1; i++ )
{
float2 g = float2( i, j );
float2 o = voronoihash1( n + g );
o = ( sin( time + o * 6.2831 ) * 0.5 + 0.5 ); float2 r = f - g - o;
float d = 0.5 * dot( r, r );
if( d<F1 ) {
F2 = F1;
F1 = d; mg = g; mr = r; id = o;
} else if( d<F2 ) {
F2 = d;
}
}
}
return F2 - F1;
}
void voroniForgraphfunc_half(half2 uv,half angle,half scale,out float outVoroni1 )
{
uv = uv*scale;
float2 id1 = 0;
float2 uv1 = 0;
outVoroni1 = voronoi1( uv, angle, id1, uv1, 0 );
}
inline float2 unity_voronoi_noise_randomVector (float2 UV, float offset)
{
float2x2 m = float2x2(15.27, 47.63, 99.41, 89.98);
UV = frac(sin(mul(UV, m)) * 46839.32);
return float2(sin(UV.y*+offset)*0.5+0.5, cos(UV.x*offset)*0.5+0.5);
}
void Unity_Voronoi_float(float2 UV, float AngleOffset, float CellDensity, out float Out, out float Cells)
{
float2 g = floor(UV * CellDensity);
float2 f = frac(UV * CellDensity);
float t = 8.0;
float3 res = float3(8.0, 0.0, 0.0);
for(int y=-1; y<=1; y++)
{
for(int x=-1; x<=1; x++)
{
float2 lattice = float2(x,y);
float2 offset = unity_voronoi_noise_randomVector(lattice + g, AngleOffset);
float d = distance(lattice + offset, f);
if(d < res.x)
{
res = float3(d, offset.x, offset.y);
Out = res.x;
Cells = res.y;
}
}
}
}
void Unity_Blend_Overlay_float4(float4 Base, float4 Blend, float Opacity, out float4 Out)
{
float4 result1 = 1.0 - 2.0 * (1.0 - Base) * (1.0 - Blend);
float4 result2 = 2.0 * Base * Blend;
float4 zeroOrOne = step(Base, 0.5);
Out = result2 * zeroOrOne + (1 - zeroOrOne) * result1;
Out = lerp(Base, Out, Opacity);
}
void Unity_Blend_HardLight_half(half Base, half Blend, half Opacity, out half Out)
{
half result1 = 1.0 - 2.0 * (1.0 - Base) * (1.0 - Blend);
half result2 = 2.0 * Base * Blend;
half zeroOrOne = step(Blend, 0.5);
Out = result2 * zeroOrOne + (1 - zeroOrOne) * result1;
Out = lerp(Base, Out, Opacity);
}
void Blend_HardLight_half(half Base, half Blend, out half Out)
{
half result1 = 1.0 - 2.0 * (1.0 - Base) * (1.0 - Blend);
half result2 = 2.0 * Base * Blend;
half zeroOrOne = step(Blend, 0.5);
Out = result2 * zeroOrOne + (1 - zeroOrOne) * result1;
}
float2 randomGradient(float2 p) {
p = p + 0.02;
float x = dot(p, float2(123.4, 234.5));
float y = dot(p, float2(234.5, 345.6));
float2 gradient = float2(x, y);
gradient = sin(gradient);
gradient = gradient * 43758.5453;
// part 4.5 - update noise function with time
// gradient = sin(gradient + u_time);
return gradient;
// gradient = sin(gradient);
// return gradient;
}
#include "./jp.keijiro.noiseshader/Shader/SimplexNoise3D.hlsl"
half SimplexNoise(float2 uv,float time)
{
return SimplexNoise(float3(uv,time))*0.5+0.5;
}
#include "./jp.keijiro.noiseshader/Shader/ClassicNoise3D.hlsl"
half PerlinNoise(float2 uv,float time)
{
return ClassicNoise(float3(uv,time))*0.5+0.5;
}
float2 PolarCoordinates(float2 UV, float2 _PCCenter)
{
// float2 uvsource = float2(0, 0);
float2 delta = UV - _PCCenter.xy;//校准UV到中心
float radius = length(delta) * 2;
float angle = atan2(delta.x, delta.y) * UNITY_INV_TWO_PI ;
return float2(angle, radius);//翻转可以调整横向和纵向。
}
float2 PolarCoordinatesStrengthAndST(float2 UVBeforPollarCoord,float2 UVAfterPolarCoord,float polarStrenth ,float4 tex_ST)
{
UVAfterPolarCoord = UVAfterPolarCoord*tex_ST.xy + tex_ST.zw;//利用相应功能贴图的坐标对ST进行修改。
UVAfterPolarCoord = lerp(UVBeforPollarCoord, UVAfterPolarCoord, polarStrenth);
return UVAfterPolarCoord;
}
//极坐标//transformation为极坐标强度
float2 PolarCoordinates(float2 UV, float3 _PCCenter, float4 tex_ST)
{
// float2 uvsource = float2(0, 0);
//
// float2 delta = UV - _PCCenter.xy;//校准UV到中心
// float radius = length(delta) * 2;
// float angle = atan2(delta.x, delta.y) * UNITY_INV_TWO_PI ;
// uvsource = float2(angle, radius);//翻转可以调整横向和纵向。
// uvsource = uvsource*tex_ST.xy + tex_ST.zw;//利用相应功能贴图的坐标对ST进行修改。
// uvsource = lerp(UV, uvsource, _PCCenter.z);
// return uvsource;
//拆分成两步第一步求极坐标太耗atan一般源UV都是一样的只是ST不同。
float2 uvsource = PolarCoordinates(UV,_PCCenter.xy);
uvsource = PolarCoordinatesStrengthAndST(UV,uvsource,_PCCenter.z,tex_ST);
return uvsource;
}
float2 CylinderCoordinate(float3 positionOS)
{
float angle = atan2(positionOS.x,positionOS.z)* UNITY_INV_TWO_PI ;
return float2(angle,positionOS.y);
}
float2 UVOffsetAnimaiton(float2 UV,half2 OffsetSpeed,float time)
{
float2 newUV = float2(OffsetSpeed.x*time+UV.x,OffsetSpeed.y*time+UV.y);
return newUV;
}
half3 rgb2hsv(half3 c)
{
half4 K = half4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
half4 p = lerp(half4(c.bg, K.wz), half4(c.gb, K.xy), step(c.b, c.g));
half4 q = lerp(half4(p.xyw, c.r), half4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
half3 hsv2rgb(half3 c)
{
half4 K = half4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
half3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
}
half3 ColorSaturate(half3 color, half colorSaturation)
{
half3 lum = luminance(color);
return max(0, lerp(lum, color, colorSaturation));
}
half2 Rotate(half2 v, half cos0, half sin0)
{
return half2(v.x * cos0 - v.y * sin0,
v.x * sin0 + v.y * cos0);
}
half SmoothStep01(half interval)//让01线性过渡变成SmoothStep过渡但是控制计算量。
{
interval = saturate(interval);
return interval * interval * ( 3 - 2 * interval );
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9f34986983f558c40a7e30a353dc3c34
timeCreated: 1684725413

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e95a8f1b5d23f4141b5121f98f90f10a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
Noise Shader Library for Unity
==============================
**NoiseShader** is a Unity package that provides 2D/3D gradient noise
functions written in the shader language. These functions are ported from the
[webgl-noise] library that is originally written by Stefan Gustavson and Ahima
Arts.
[webgl-noise]: https://github.com/ashima/webgl-noise
At the moment, it contains the following functions:
- Classic Perlin noise (2D/3D)
- Periodic Perlin noise (2D/3D)
- Simplex noise (2D/3D)
- Analytical derivatives of simplex noise (2D/3D)
How To Install
--------------
This package uses the [scoped registry] feature to resolve package dependencies.
Please add the following sections to the manifest file (Packages/manifest.json).
[scoped registry]: https://docs.unity3d.com/Manual/upm-scoped.html
To the `scopedRegistries` section:
```
{
"name": "Keijiro",
"url": "https://registry.npmjs.com",
"scopes": [ "jp.keijiro" ]
}
```
To the `dependencies` section:
```
"jp.keijiro.noiseshader": "2.0.0"
```
After changes, the manifest file should look like below:
```
{
"scopedRegistries": [
{
"name": "Keijiro",
"url": "https://registry.npmjs.com",
"scopes": [ "jp.keijiro" ]
}
],
"dependencies": {
"jp.keijiro.noiseshader": "2.0.0",
...
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b92baefe22663afa5ad0ab303b3dcc20
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 77b21af505211947eb730e678820eb8b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More