using System.Collections.Generic; using System; using PlasticGui; using UnityEngine; using UnityEngine.UIElements; 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 ChildResetItems = new List(); public Action ResetCallBack; public Action OnValueChangedCallBack; public Func CheckHasModifyOnValueChange; public Func 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 checkHasModifyOnValueChange,Func 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 checkHasModifyOnValueChange,Func 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 checkHasModifyOnValueChange,Func 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; } 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; MaterialProperty.PropType propertyType = property.type; if (pack.property.type == MaterialProperty.PropType.Texture && vectorValeType != VectorValeType.Undefine) { propertyType = MaterialProperty.PropType.Vector;//Tilling or Offset } switch (propertyType) { case MaterialProperty.PropType.Color: Vector4 colorValue = _shader.GetPropertyDefaultVectorValue(pack.index); property.colorValue = new Color(colorValue.x, colorValue.y, colorValue.z, colorValue.x); break; case MaterialProperty.PropType.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; property.textureScaleAndOffset = 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; property.textureScaleAndOffset = 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 MaterialProperty.PropType.Float or MaterialProperty.PropType.Range: float value = _shader.GetPropertyDefaultFloatValue(pack.index); property.floatValue = value; break; case MaterialProperty.PropType.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; MaterialProperty.PropType propertyType = property.type; if (pack.property.type == MaterialProperty.PropType.Texture && vectorValeType != VectorValeType.Undefine) { propertyType = MaterialProperty.PropType.Vector;//Tilling or Offset } switch (propertyType) { case MaterialProperty.PropType.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 MaterialProperty.PropType.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; } 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 MaterialProperty.PropType.Float or MaterialProperty.PropType.Range: return Mathf.Approximately(property.floatValue, _shader.GetPropertyDefaultFloatValue(pack.index)) ? false : true; case MaterialProperty.PropType.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; } } } }