using System; using System.Collections.Generic; using System.Linq; using UnityEditor.AnimatedValues; // using UnityEditor.Rendering.Universal.ShaderGUI; using UnityEngine; using Random = UnityEngine.Random; using System.Reflection; using UnityEngine.UIElements; using UnityEditor; namespace NBShaderEditor { /* 多选材质面板的原则记录: 0、多选后 同时只会有1个matEditor。每个属性只会有一个MaterialProperty。但会有多个Mat以及Mat属性值。会有多个ShaderFlag以及ShaderFlag值。 1、当多选后,属性处于Mixed状态时。对propety.Value进行设置是不合法的。只有非Mixed状态才会完全设置。包括这样对property.Value值进行判断也是非法的。如果在不确定Mixed的情况下,应该通过遍历Mats[i].Get或Set进行操作。Flag亦同理。 2、drawBlock应该都传Property,让DrawBlock内能知道Mixed状态。然后在Block内需要判断状态是否明确才进行相关的操作。 3、drawBlock一般放需要绘制的行为。对数值修改的行为统一放到OnValueChangeCheckBlock。 4、所有在GUI上存储,并需要后续判断的状态,需要可以标记mixed状态。如果是枚举,需要有一个指定为-1的UnKnownOrMixed枚举。如果是bool,应该改为int值,并规定-1为UnKnownOrMixed状态。 5、对于属性值的更改设定,都应该在OnValueChange的情况下进行。 6、对于Toggle作用的方法,均应该有EditorOnly的Property进行标记Toggle储存。Keywords或者Flag应该是设定结果,而非Toggle标记。 7、DrawVectorComponent这种多个GUI公用一个property属性的情况,需要有手动的提供各个Component是否是Mixed的方案。 */ public class ShaderGUIHelper { public ShaderGUIHelper() { Undo.undoRedoPerformed += OnUndoRedoPerformed; } private void OnUndoRedoPerformed()//定义一个Undo回调 { if(!shader) return;//会有一些空的Helper对象 DrawGradientUndoPerformed(); ResetTool?.NeedUpdate(); } public class ShaderPropertyPack { public MaterialProperty property; public string name; public int index; } public List mats; public MaterialEditor matEditor; public Shader shader; // public List ShaderPropertyPacks = new List(); public Dictionary ShaderPropertyPacksDic = new Dictionary(); public ShaderFlagsBase[] shaderFlags = null; public bool isClearUnUsedTexture = false; Color defaultBackgroundColor; Color animatedBackgroundColor=>Color.red; public void Init(MaterialEditor materialEditor, MaterialProperty[] properties, ShaderFlagsBase[] shaderFlags_in = null, List mats_in = null) { defaultBackgroundColor = GUI.backgroundColor; shaderFlags = shaderFlags_in; // ShaderPropertyPacks.Clear(); matEditor = materialEditor; mats = mats_in; if (mats[0].shader != shader) { shader = mats[0].shader; ShaderPropertyPacksDic.Clear(); for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); i++) { ShaderPropertyPack pack = new ShaderPropertyPack(); pack.name = ShaderUtil.GetPropertyName(shader, i); for (int index = 0; index < properties.Length; ++index) { if (properties[index] != null && properties[index].name == pack.name) { pack.property = properties[index]; pack.index = index; break; } else { if (index == properties.Length - 1) { Debug.LogError(pack.name + "找不到Properties"); } } } // ShaderPropertyPacks.Add(pack); ShaderPropertyPacksDic.Add(pack.name,pack); } } else { for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); i++) { string propertyName = ShaderUtil.GetPropertyName(shader, i); ShaderPropertyPacksDic[propertyName].property = properties[i]; } } if (ResetTool == null) { ResetTool = new ShaderGUIResetTool(this); } else { ResetTool.EndInit(); ResetTool.Update(); } isClearUnUsedTexture = false; } private ShaderGUIToolBar _toolBar; public ShaderGUIResetTool ResetTool; public List renderersUsingThisMaterial = new List(); public bool IsPropertyAnimated(string propertyName,params string[] componentNames) { if (AnimationMode.InAnimationMode()) { string propertyPath = "material." + propertyName; foreach (var r in renderersUsingThisMaterial) { if (componentNames.Length > 0) { foreach (var component in componentNames) { string propertyPathWithComponent = propertyPath +"." +component; if (AnimationMode.IsPropertyAnimated(r, propertyPathWithComponent)) { return true; } } } else { if (AnimationMode.IsPropertyAnimated(r, propertyPath)) { // Debug.Log(propertyName); return true; } } } } return false; } public void DrawToolBar() { if (_toolBar == null) _toolBar = new ShaderGUIToolBar(this); _toolBar.DrawToolbar(); } public void DrawToggleFoldOut(int foldOutFlagBit,int foldOutFlagIndex, int animBoolIndex,string label, string propertyName, int flagBitsName = 0, int flagIndex = 0, string shaderKeyword = null, string shaderPassName = null, bool isIndentBlock = true, FontStyle fontStyle = FontStyle.Normal, Action drawBlock = null,Action drawEndChangeCheck = null,bool isSharedGlobalParent = false) { bool foldOutState = shaderFlags[0].CheckFlagBits(foldOutFlagBit, index: foldOutFlagIndex); AnimBool animBool = GetAnimBool(foldOutFlagBit, animBoolIndex, foldOutFlagIndex); //foldOut里的第一组。 animBool.target = foldOutState; DrawToggleFoldOut(ref animBool, label, propertyName, flagBitsName, flagIndex, shaderKeyword, shaderPassName, isIndentBlock, fontStyle, drawBlock, drawEndChangeCheck,isSharedGlobalParent:isSharedGlobalParent); foldOutState = animBool.target; if (foldOutState) { shaderFlags[0].SetFlagBits(foldOutFlagBit, index: foldOutFlagIndex); } else { shaderFlags[0].ClearFlagBits(foldOutFlagBit, index: foldOutFlagIndex); } } public void ColorProperty(string label, string propertyName,bool showAlpha = true) { EditorGUILayout.BeginHorizontal(); ShaderPropertyPack shaderPropertyPack = ShaderPropertyPacksDic[propertyName]; MaterialProperty prop = shaderPropertyPack.property; Color color = shaderPropertyPack.property.colorValue; Rect position = EditorGUILayout.GetControlRect(); // MaterialEditor.BeginProperty(position, prop); EditorGUI.BeginChangeCheck(); bool hdr = (prop.propertyFlags & UnityEngine.Rendering.ShaderPropertyFlags.HDR) != 0; if (IsPropertyAnimated(propertyName,"r","g","b","a")) { GUI.backgroundColor = animatedBackgroundColor; } color = EditorGUI.ColorField(position, new GUIContent(label), prop.colorValue, true, showAlpha, hdr); GUI.backgroundColor = defaultBackgroundColor; Action onEndChaneChange = () => { prop.colorValue = color; ResetTool.CheckOnValueChange((label,propertyName)); }; if (EditorGUI.EndChangeCheck()) { onEndChaneChange(); } ResetTool.DrawResetModifyButton(new Rect(),(label,shaderPropertyPack.name),shaderPropertyPack,resetAction: () => { color = prop.colorValue; },onValueChangedCallBack: onEndChaneChange); // MaterialEditor.EndProperty(); // matEditor.ColorProperty(GetProperty(propertyName), label); EditorGUILayout.EndHorizontal(); ResetTool.EndResetModifyButtonScope(); } public void DrawBigBlockFoldOut(int foldOutFlagBit,int foldOutFlagIndex,int animBoolIndex ,string label, Action drawBlock,bool isResetButtonBias = true) { EditorGUILayout.Space(); DrawFoldOut(foldOutFlagBit,foldOutFlagIndex,animBoolIndex, label,FontStyle.Bold, drawBlock,isResetButtonBias); GuiLine(); } private AnimBool[] animBoolArr = new AnimBool[96];//先假定有3组。和存好的bit一一对应。 public AnimBool GetAnimBool(int flagBit, int AnimBoolIndex,int flagIndex) { int bitPos = 0; for (int i = 0; i < 32; i++) { if ((flagBit & (1 << i)) > 0) { bitPos = i; break; } } int arrIndex = AnimBoolIndex * 32 + bitPos; // Debug.Log(arrIndex.ToString() +"---"+ animBoolArr[arrIndex]); if (animBoolArr[arrIndex] == null) { animBoolArr[arrIndex] = new AnimBool(shaderFlags[0].CheckFlagBits(flagBit,index:flagIndex)); } return animBoolArr[arrIndex]; } public void DrawFoldOut(int foldOutFlagBit,int foldOutFlagIndex,int animBoolIndex, String label,FontStyle fontStyle = FontStyle.Normal, Action drawBlock = null,bool isResetButtonBias = true) { bool foldOutState = shaderFlags[0].CheckFlagBits(foldOutFlagBit, index: foldOutFlagIndex); AnimBool animBool = GetAnimBool(foldOutFlagBit, animBoolIndex, foldOutFlagIndex); animBool.target = foldOutState; EditorGUILayout.BeginHorizontal(); var rect = EditorGUILayout.GetControlRect(); var foldoutRect = new Rect(rect.x, rect.y, rect.width, rect.height); foldoutRect.width -= 2 * ResetTool.ResetButtonSize; var labelRect = new Rect(rect.x + 2f, rect.y, rect.width - 2f, rect.height); var resetRect = rect; resetRect.x = rect.x + rect.width - ResetTool.ResetButtonSize ; if (isResetButtonBias) resetRect.x -= 3f; resetRect.width = ResetTool.ResetButtonSize; animBool.target = EditorGUI.Foldout(foldoutRect, animBool.target, string.Empty, true); FontStyle origFontStyle = EditorStyles.label.fontStyle; EditorStyles.label.fontStyle = fontStyle; EditorGUI.LabelField(labelRect, label); EditorStyles.label.fontStyle = origFontStyle; ResetTool.DrawResetModifyButton(resetRect,label); EditorGUILayout.EndHorizontal(); float faded = animBool.faded; if (faded == 0) faded = 0.00001f; EditorGUILayout.BeginFadeGroup(faded); EditorGUI.indentLevel++; drawBlock?.Invoke(); EditorGUI.indentLevel--; EditorGUILayout.EndFadeGroup(); foldOutState = animBool.target; if (foldOutState) { shaderFlags[0].SetFlagBits(foldOutFlagBit, index: foldOutFlagIndex); } else { shaderFlags[0].ClearFlagBits(foldOutFlagBit, index: foldOutFlagIndex); } ResetTool.EndResetModifyButtonScope(); } public void DrawBigBlockWithToggle(String label, string propertyName, int flagBitsName = 0, int flagIndex = 0, string shaderKeyword = null, string shaderPassName = null, string shaderPassName2 = null, Action drawBlock = null) { DrawToggle(label, propertyName, flagBitsName, flagIndex, shaderKeyword, shaderPassName, shaderPassName2, isIndentBlock: true, FontStyle.Bold, drawBlock: drawBlock); GuiLine(); } public void DrawToggleFoldOut(ref AnimBool foldOutAnimBool, String label, string propertyName, int flagBitsName = 0, int flagIndex = 0, string shaderKeyword = null, string shaderPassName = null, bool isIndentBlock = true, FontStyle fontStyle = FontStyle.Normal, Action drawBlock = null, Action drawEndChangeCheck = null,bool isSharedGlobalParent = false) { ShaderPropertyPack propertyPack = ShaderPropertyPacksDic[propertyName]; MaterialProperty toggleProp = GetProperty(propertyName); if (fontStyle == FontStyle.Bold) { EditorGUILayout.Space(); } EditorGUILayout.BeginHorizontal(); var rect = EditorGUILayout.GetControlRect(); var toggleRect = rect; toggleRect.x += EditorGUIUtility.labelWidth; toggleRect.width -= EditorGUIUtility.labelWidth; var foldoutRect = new Rect(rect.x, rect.y, rect.width, rect.height); foldoutRect.width = toggleRect.x - foldoutRect.x; var labelRect = new Rect(rect.x , rect.y, rect.width , rect.height); // bool isToggle = false; // 必须先画Toggle,不然按钮会被FoldOut和Label覆盖。 DrawToggle(string.Empty, propertyName, flagBitsName, flagIndex, shaderKeyword, shaderPassName, isIndentBlock: false, fontStyle: FontStyle.Normal, rect: toggleRect, drawEndChangeCheck: drawEndChangeCheck,isSharedGlobalParent:isSharedGlobalParent); foldOutAnimBool.target = EditorGUI.Foldout(foldoutRect, foldOutAnimBool.target, string.Empty, true); var origFontStyle = EditorStyles.label.fontStyle; EditorStyles.label.fontStyle = fontStyle; EditorGUI.LabelField(labelRect, label); EditorStyles.label.fontStyle = origFontStyle; EditorGUILayout.EndHorizontal(); if (isIndentBlock) EditorGUI.indentLevel++; float faded = foldOutAnimBool.faded; if (faded == 0) faded = 0.00001f; //用于欺骗FadeGroup,不要让他真的关闭了。这样会藏不住相关的GUI。我们的目的是,GUI藏住,但是逻辑还是在跑。drawBlock要执行。 EditorGUILayout.BeginFadeGroup(faded); { bool isDisabledGroup = toggleProp.hasMixedValue || toggleProp.floatValue < 0.5f; EditorGUI.BeginDisabledGroup(isDisabledGroup); drawBlock?.Invoke(toggleProp); EditorGUI.EndDisabledGroup(); } EditorGUILayout.EndFadeGroup(); if (isIndentBlock) EditorGUI.indentLevel--; ResetTool.EndResetModifyButtonScope();//开始是在DrawToggle里开始的 } public void DrawToggle(String label, string propertyName, int flagBitsName = 0, int flagIndex = 0, string shaderKeyword = null, string shaderPassName = null, string shaderPassName2 = null, bool isIndentBlock = true, FontStyle fontStyle = FontStyle.Normal, Rect rect = new Rect(), Action drawBlock = null, Action drawEndChangeCheck = null,bool isSharedGlobalParent = false) { if (GetProperty(propertyName) == null) return; if (fontStyle == FontStyle.Bold) { EditorGUILayout.Space(); } MaterialProperty toggleProperty = GetProperty(propertyName); ShaderPropertyPack propertyPack = ShaderPropertyPacksDic[propertyName]; EditorGUI.showMixedValue = toggleProperty.hasMixedValue; EditorGUI.BeginChangeCheck(); bool isToggle = toggleProperty.floatValue > 0.5f ? true : false; var origFontStyle = EditorStyles.label.fontStyle; EditorStyles.label.fontStyle = fontStyle; Rect resetButtonRect = rect; if (label.Length == 0) //给FoldOut功能使用。 { rect.x += 2f; isToggle = EditorGUI.Toggle(rect, isToggle, EditorStyles.toggle); resetButtonRect.x = resetButtonRect.x + resetButtonRect.width - ResetTool.ResetButtonSize; resetButtonRect.width = ResetTool.ResetButtonSize; } else { EditorGUILayout.BeginHorizontal(); isToggle = EditorGUILayout.Toggle(label, isToggle); } EditorStyles.label.fontStyle = origFontStyle; Action onEndChangeCheck = () => { toggleProperty.floatValue = isToggle ? 1.0f : 0.0f; ResetTool.CheckOnValueChange((label,propertyName)); if (!toggleProperty.hasMixedValue) { for (int i = 0; i < mats.Count; i++) { if (isToggle) { mats[i].SetFloat(propertyName,1); if (flagBitsName != 0 && shaderFlags[i] != null) { shaderFlags[i].SetFlagBits(flagBitsName, index: flagIndex); } if (shaderKeyword != null) { mats[i].EnableKeyword(shaderKeyword); } if (shaderPassName != null) { mats[i].SetShaderPassEnabled(shaderPassName, true); } if (shaderPassName2 != null) { mats[i].SetShaderPassEnabled(shaderPassName2, true); } } else { mats[i].SetFloat(propertyName,0); if (flagBitsName != 0 && shaderFlags[i] != null) { shaderFlags[i].ClearFlagBits(flagBitsName, index: flagIndex); } if (shaderKeyword != null) { mats[i].DisableKeyword(shaderKeyword); } if (shaderPassName != null) { mats[i].SetShaderPassEnabled(shaderPassName, false); } if (shaderPassName2 != null) { mats[i].SetShaderPassEnabled(shaderPassName2, false); } } } } drawEndChangeCheck?.Invoke(toggleProperty); }; if (EditorGUI.EndChangeCheck()) { onEndChangeCheck(); } ResetTool.DrawResetModifyButton(resetButtonRect,(label,propertyPack.name),propertyPack,resetAction: () => { isToggle = propertyPack.property.floatValue > 0.5f ? true : false; },onValueChangedCallBack:onEndChangeCheck,isSharedGlobalParent:isSharedGlobalParent); if (label.Length > 0) { EditorGUILayout.EndHorizontal(); } if (isIndentBlock) EditorGUI.indentLevel++; drawBlock?.Invoke(toggleProperty); if (isIndentBlock) EditorGUI.indentLevel--; EditorGUI.showMixedValue = false; if (rect.width <= 0) { ResetTool.EndResetModifyButtonScope();//如果是DrawFoldOut,需要在DrawFoldOut里去结束。 } } void RangeVecHasMixedValue(string rangePropertyName, out bool minValueHasMixed, out bool maxValueHasMixed) { minValueHasMixed = false; maxValueHasMixed = false; if (mats.Count > 1) { MaterialProperty rangeProperty = GetProperty(rangePropertyName); float minValue = 0; float maxValue = 0; for (int i = 0; i < mats.Count; i++) { Vector4 rangeVec = 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(string rangePropertyName) { MaterialProperty rangeProperty = GetProperty(rangePropertyName); return mats[0].GetVector(rangePropertyName) == shader.GetPropertyDefaultVectorValue(ShaderPropertyPacksDic[rangePropertyName].index) && !rangeProperty.hasMixedValue; } void DrawSlider(string label, ref float value,ref float min,ref float max,bool isValueMixed,string rangePropertyName = null,bool propIsAnimated = false) { if (rangePropertyName == null) { EditorGUI.showMixedValue = isValueMixed; if (propIsAnimated) GUI.backgroundColor = animatedBackgroundColor; value = EditorGUILayout.Slider(label,value,min,max); GUI.backgroundColor = defaultBackgroundColor; EditorGUI.showMixedValue = false; } else { Rect rect = EditorGUILayout.GetControlRect(); Rect labelRect = rect; labelRect.width = EditorGUIUtility.labelWidth; Rect valueRect = rect; valueRect.x += EditorGUIUtility.labelWidth; valueRect.width -= EditorGUIUtility.labelWidth; // EditorGUI.DrawRect(valueRect,Color.red); float rangeWidth = 50f; Rect minRect = valueRect; minRect.width = rangeWidth; minRect.x -= indent; minRect.width += indent; // EditorGUI.DrawRect(minRect,Color.green); Rect maxRect = valueRect; maxRect.x += valueRect.width; maxRect.x -= rangeWidth; maxRect.width = rangeWidth; maxRect.x -= indent; maxRect.width += indent; valueRect.x += rangeWidth; valueRect.width -= 2*rangeWidth; valueRect.x -= indent; valueRect.width += indent; valueRect.x += 4f; valueRect.width -= 8f; EditorGUI.LabelField(labelRect, label); RangeVecHasMixedValue(rangePropertyName,out bool minValueHasMixed,out bool maxValueHasMixed); EditorGUI.showMixedValue = minValueHasMixed; min = EditorGUI.FloatField(minRect, GUIContent.none, min); EditorGUI.showMixedValue = maxValueHasMixed; max = EditorGUI.FloatField(maxRect, GUIContent.none, max); EditorGUI.showMixedValue = isValueMixed; if (propIsAnimated) { GUI.backgroundColor = animatedBackgroundColor; } value = EditorGUI.Slider(valueRect, value, min, max); GUI.backgroundColor = defaultBackgroundColor; EditorGUI.showMixedValue = false; } } public void DrawSlider(string label, string propertyName,float min = 0,float max = 1,string rangePropertyName = null, Action drawBlock = null) { bool hasMixedValue = GetProperty(propertyName).hasMixedValue; float f = GetProperty(propertyName).floatValue; bool customedRange = rangePropertyName != null; Vector4 rangeVec = Vector4.zero; if (customedRange) { rangeVec = GetProperty(rangePropertyName).vectorValue; } EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); if (IsPropertyAnimated(propertyName)) { GUI.backgroundColor = animatedBackgroundColor; } bool isPropAnimated = IsPropertyAnimated(propertyName); if (customedRange) { DrawSlider(label, ref f, ref rangeVec.x,ref rangeVec.y,hasMixedValue,rangePropertyName,isPropAnimated); } else { DrawSlider(label, ref f, ref min,ref max,hasMixedValue,propIsAnimated:isPropAnimated); } GUI.backgroundColor = defaultBackgroundColor; Action endChangCallBack= () => { GetProperty(propertyName).floatValue = f; if (customedRange) { GetProperty(rangePropertyName).vectorValue = rangeVec; } ResetTool.CheckOnValueChange((label,propertyName)); }; EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { endChangCallBack.Invoke(); } ResetTool.DrawResetModifyButton(new Rect(),(label,propertyName),resetCallBack: () => { f = shader.GetPropertyDefaultFloatValue(ShaderPropertyPacksDic[propertyName].index); if (customedRange) { rangeVec = shader.GetPropertyDefaultVectorValue(ShaderPropertyPacksDic[rangePropertyName].index); } },onValueChangedCallBack: () => { endChangCallBack(); } ,checkHasModifyOnValueChange: () => { bool isModify = !Mathf.Approximately(mats[0].GetFloat(propertyName),shader.GetPropertyDefaultFloatValue(ShaderPropertyPacksDic[propertyName].index)); if (customedRange) { isModify |= !RangePropIsDefaultValue(rangePropertyName); } return isModify; },checkHasMixedValueOnValueChange: () => { bool hasMixedValue = false; hasMixedValue |= GetProperty(propertyName).hasMixedValue; if (customedRange) { RangeVecHasMixedValue(rangePropertyName,out bool minValueHasMixed,out bool maxValueHasMixed); hasMixedValue |= minValueHasMixed; hasMixedValue |= maxValueHasMixed; } return hasMixedValue; }); EditorGUILayout.EndHorizontal(); drawBlock?.Invoke(f); ResetTool.EndResetModifyButtonScope(); } public void DrawFloat(string label, string propertyName, bool isReciprocal = false, Action drawBlock = null) { EditorGUI.showMixedValue = GetProperty(propertyName).hasMixedValue; MaterialProperty floatProperty = GetProperty(propertyName); float f = floatProperty.floatValue; if (isReciprocal) f = 1 / f; EditorGUI.BeginChangeCheck(); Action endChangeCallback = () => { floatProperty.floatValue = f; ResetTool.CheckOnValueChange((label,propertyName)); }; EditorGUILayout.BeginHorizontal(); if (IsPropertyAnimated(propertyName)) { GUI.backgroundColor = animatedBackgroundColor; } f = EditorGUILayout.FloatField(label, f); GUI.backgroundColor = defaultBackgroundColor; if (isReciprocal) f = 1 / f; if (EditorGUI.EndChangeCheck()) { endChangeCallback.Invoke(); } ResetTool.DrawResetModifyButton(new Rect(),(label,propertyName),ShaderPropertyPacksDic[propertyName],resetAction: () => { f = floatProperty.floatValue; },onValueChangedCallBack:endChangeCallback); EditorGUILayout.EndHorizontal(); drawBlock?.Invoke(floatProperty); EditorGUI.showMixedValue = false; ResetTool.EndResetModifyButtonScope(); } Vector2 GetVec2InVec4(Vector4 vec4,bool isFirstLine) { if (isFirstLine) { return new Vector2(vec4.x, vec4.y); } else { return new Vector2(vec4.z, vec4.w); } } Vector4 SetVec2InVec4(Vector4 vec4,bool isFirstLine,Vector2 vec2Value) { if (isFirstLine) { vec4.x = vec2Value.x; vec4.y = vec2Value.y; return vec4; } else { vec4.z = vec2Value.x; vec4.w = vec2Value.y; return vec4; } } Vector2 GetVecInTwoLineDefaultValue(string propertyName, bool isFirstLine) { ShaderPropertyPack propertyPack = ShaderPropertyPacksDic[propertyName]; Vector4 defaultVector = shader.GetPropertyDefaultVectorValue(propertyPack.index); if (isFirstLine) { return new Vector2(defaultVector.x, defaultVector.y); } else { return new Vector2(defaultVector.z, defaultVector.w); } } bool Vector4In2LineHasMixedValue(string propertyName, bool isFirstLine) { MaterialProperty matProp = GetProperty(propertyName); if (matProp.hasMixedValue) { Vector2 val = Vector2.zero; for (int i = 0; i < mats.Count; i++) { Vector2 matValue = GetVec2InVec4(mats[i].GetVector(propertyName), isFirstLine); if (i == 0) { val = matValue; } else { if (!val.Equals(matValue) ) { return true; } } } } return false; } public void DrawVector4In2Line(string propertyName, string label , bool isFirstLine, Action drawBlock = null) { EditorGUI.showMixedValue = Vector4In2LineHasMixedValue(propertyName, isFirstLine); MaterialProperty property = GetProperty(propertyName); (string,string) nameTuple = (label,propertyName); EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); Vector2 vec2 = GetVec2InVec4(property.vectorValue, isFirstLine); // vec2 = EditorGUILayout.Vector2Field(label, vec2); Rect rect = EditorGUILayout.GetControlRect(true); Rect labelRect = rect; labelRect.width = EditorGUIUtility.labelWidth - indent; EditorGUI.LabelField(labelRect, label); Rect vec2Rect = rect; vec2Rect.x += labelRect.width; vec2Rect.width -= labelRect.width; if (isFirstLine) { if (IsPropertyAnimated(propertyName, "x", "y")) { GUI.backgroundColor = animatedBackgroundColor; } } else { if (IsPropertyAnimated(propertyName, "z", "w")) { GUI.backgroundColor = animatedBackgroundColor; } } vec2 = EditorGUI.Vector2Field(vec2Rect,"", vec2); GUI.backgroundColor = defaultBackgroundColor; Action vec2OnEndChangeCheck = () => { int shaderID = Shader.PropertyToID(propertyName); for (int i = 0; i < mats.Count; i++) { Vector4 vec4 = mats[i].GetVector(shaderID); vec4 = SetVec2InVec4(vec4, isFirstLine, vec2); if (mats.Count == 1) { GetProperty(propertyName).vectorValue = vec4; //为了K动画,多选不能K动画。 } else { mats[i].SetVector(shaderID, vec4); } } ResetTool.CheckOnValueChange(nameTuple); }; if (EditorGUI.EndChangeCheck()) { vec2OnEndChangeCheck(); } EditorGUI.showMixedValue = false; ResetTool.DrawResetModifyButton(new Rect(),nameTuple, resetCallBack:()=> { vec2 = GetVecInTwoLineDefaultValue(propertyName, isFirstLine); vec2OnEndChangeCheck(); }, onValueChangedCallBack:vec2OnEndChangeCheck, checkHasModifyOnValueChange: () => GetVec2InVec4(mats[0].GetVector(propertyName), isFirstLine) != GetVecInTwoLineDefaultValue(propertyName, isFirstLine), checkHasMixedValueOnValueChange:()=>Vector4In2LineHasMixedValue(propertyName, isFirstLine)); drawBlock?.Invoke(); ResetTool.EndResetModifyButtonScope(); EditorGUILayout.EndHorizontal(); } float GetCompInVec4(Vector4 vec, string comp) { float f = 0; switch (comp) { case "x": f = vec.x; break; case "y": f = vec.y; break; case "z": f = vec.z; break; case "w": f = vec.w; break; } return f; } float GetCompDefaultValueInVec4(string propertyName, string comp) { ShaderPropertyPack propertyPack = ShaderPropertyPacksDic[propertyName]; Vector4 defaultValue = shader.GetPropertyDefaultVectorValue(propertyPack.index); return GetCompInVec4(defaultValue, comp); } Vector4 SetCompInVec4(Vector4 vec, string comp, float value) { switch (comp) { case "x": vec.x = value; break; case "y": vec.y = value; break; case "z": vec.z = value; break; case "w": vec.w = value; break; } return vec; } bool Vector4ComponentHasMixedValue(string propertyName, string channel) { MaterialProperty property = GetProperty(propertyName); if (property.hasMixedValue) { float val = 0; for (int i = 0; i < mats.Count; i++) { if (i == 0) { val = GetCompInVec4(mats[i].GetVector(propertyName),channel) ; } else { if (!val.Equals(GetCompInVec4(mats[i].GetVector(propertyName),channel)) ) { return true; } } } } return false; } public void DrawVector4Component(string label, string propertyName, string channel, bool isSlider,float minValue = 0,float maxValue = 1, string rangeVecPropName = null, float powerSlider = 1, float multiplier = 1, bool isReciprocal = false, Action drawBlock = null, Action drawEndChangeCheckBlock = null) { bool hasMixedValue = Vector4ComponentHasMixedValue(propertyName, channel); (string, string) nameTuple = (label, propertyName); Vector4 vec = GetProperty(propertyName).vectorValue; float f = GetCompInVec4(vec, channel); Vector4 rangeVec = Vector4.zero; bool isCustomedRange = rangeVecPropName != null; if (isCustomedRange) { rangeVec = GetProperty(rangeVecPropName).vectorValue; } f *= multiplier; if (isReciprocal) f = 1 / f; EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); bool propIsAnimated = IsPropertyAnimated(propertyName, channel); if (isSlider) { if (powerSlider > 1) { if (propIsAnimated) GUI.backgroundColor = animatedBackgroundColor; f = PowerSlider(EditorGUILayout.GetControlRect(new GUILayoutOption[] { GUILayout.Height(18) }), new GUIContent(label), f, minValue, maxValue, powerSlider); GUI.backgroundColor = defaultBackgroundColor; } else { if (isCustomedRange) { DrawSlider(label,ref f,ref rangeVec.x,ref rangeVec.y,hasMixedValue,rangeVecPropName,propIsAnimated); } else { DrawSlider(label,ref f,ref minValue,ref maxValue,hasMixedValue,propIsAnimated:propIsAnimated); } } } else { EditorGUI.showMixedValue = hasMixedValue; if(propIsAnimated) GUI.backgroundColor = animatedBackgroundColor; f = EditorGUILayout.FloatField(label, f); GUI.backgroundColor = defaultBackgroundColor; EditorGUI.showMixedValue = false; } if (isReciprocal) f = 1 / f; f /= multiplier; Action floatVecEndChangeCheck = () => { int id= Shader.PropertyToID(propertyName); for (int i = 0; i < mats.Count; i++) { Vector4 val = mats[i].GetVector(id); val = SetCompInVec4(val, channel, f); if (mats.Count == 1) { GetProperty(propertyName).vectorValue = val;//为了K动画,多选不能K动画。 } else { mats[i].SetVector(id, val); } if (isCustomedRange) { mats[i].SetVector(rangeVecPropName, rangeVec); } } drawEndChangeCheckBlock?.Invoke(f, hasMixedValue); ResetTool.CheckOnValueChange(nameTuple); }; if (EditorGUI.EndChangeCheck()) { floatVecEndChangeCheck(); } ResetTool.DrawResetModifyButton(new Rect(),nameTuple, resetCallBack:()=> { f = GetCompDefaultValueInVec4(propertyName, channel); if (isCustomedRange) { rangeVec = shader.GetPropertyDefaultVectorValue(ShaderPropertyPacksDic[rangeVecPropName].index); } floatVecEndChangeCheck(); },onValueChangedCallBack:floatVecEndChangeCheck, checkHasModifyOnValueChange: () => { bool isEqual = Mathf.Approximately(GetCompInVec4(mats[0].GetVector(propertyName), channel), GetCompDefaultValueInVec4(propertyName, channel)); if (isCustomedRange) { isEqual &= RangePropIsDefaultValue(rangeVecPropName); } return !isEqual; }, checkHasMixedValueOnValueChange: () => { bool hasMixedValue = Vector4ComponentHasMixedValue(propertyName, channel); if (isCustomedRange) { RangeVecHasMixedValue(rangeVecPropName, out bool minValueHasMixed, out bool maxValueHasMixed); hasMixedValue |= minValueHasMixed; hasMixedValue |= maxValueHasMixed; } return hasMixedValue; }); EditorGUILayout.EndHorizontal(); drawBlock?.Invoke(f,hasMixedValue); ResetTool.EndResetModifyButtonScope(); EditorGUI.showMixedValue = false; } public void DrawVector4XYZComponet(string label, string propertyName, Action drawBlock = null) { EditorGUI.showMixedValue = GetProperty(propertyName).hasMixedValue; (string, string) nameTuple = (label, propertyName); Vector4 originVec = GetProperty(propertyName).vectorValue; Vector3 vec = originVec; EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); vec = EditorGUILayout.Vector3Field(label, vec); EditorGUI.showMixedValue = false; Action drawEndChangeCheck = () => { GetProperty(propertyName).vectorValue = new Vector4(vec.x, vec.y, vec.z, originVec.w); ResetTool.CheckOnValueChange(nameTuple); }; if (EditorGUI.EndChangeCheck()) { drawEndChangeCheck(); } ResetTool.DrawResetModifyButton(new Rect(),nameTuple,ShaderPropertyPacksDic[propertyName],resetAction: () => { vec = GetProperty(propertyName).vectorValue; },onValueChangedCallBack:drawEndChangeCheck,vectorValeType:VectorValeType.XYZ); EditorGUILayout.EndHorizontal(); drawBlock?.Invoke(vec); ResetTool.EndResetModifyButtonScope(); } public enum SamplerWarpMode { Repeat, Clamp, RepeatX_ClampY, ClampX_RepeatY } public Rect GetRectAfterLabelWidth(Rect rect, bool ignoreIndent = false) { Rect rectAfterLabelWidth = MaterialEditor.GetRectAfterLabelWidth(rect); //右边缘是准的。 Rect leftAlignedFieldRect = MaterialEditor.GetLeftAlignedFieldRect(rect); //左边缘是准的,实际有2f空隙。 float x = leftAlignedFieldRect.x + 2f; float width = rectAfterLabelWidth.x + rectAfterLabelWidth.width - x; var newRec = new Rect(x, rectAfterLabelWidth.y, width, rectAfterLabelWidth.height); if (ignoreIndent) { float indent = (float)EditorGUI.indentLevel * 15f; newRec.x -= indent; newRec.width += indent; } return newRec; } public void DrawTextureFoldOut(int foldOutFlagBit,int foldOutFlagIndex,int animBoolIndex,string label, string texturePropertyName, string colorPropertyName = null, bool drawScaleOffset = true, bool drawWrapMode = false, int flagBitsName = 0, int flagIndex = 2, Action drawBlock = null) { bool foldOutState = shaderFlags[0].CheckFlagBits(foldOutFlagBit, index: foldOutFlagIndex); AnimBool animBool = GetAnimBool(foldOutFlagBit, animBoolIndex, foldOutFlagIndex); animBool.target = foldOutState; DrawTextureFoldOut(ref animBool, label, texturePropertyName, colorPropertyName, drawScaleOffset, drawWrapMode, flagBitsName, flagIndex, drawBlock); foldOutState = animBool.target; if (foldOutState) { shaderFlags[0].SetFlagBits(foldOutFlagBit, index: foldOutFlagIndex); } else { shaderFlags[0].ClearFlagBits(foldOutFlagBit, index: foldOutFlagIndex); } } public void DrawTextureFoldOut(ref AnimBool foldOutAnimBool, string label, string texturePropertyName, string colorPropertyName = null, bool drawScaleOffset = true, bool drawWrapMode = false, int wrapModeFlagBitsName = 0, int flagIndex = 2, Action drawBlock = null) { // EditorGUILayout.BeginHorizontal(); // var rect = EditorGUILayout.GetControlRect(false,68f);//MaterialEditor.GetTextureFieldHeight() => 64f; // var foldoutRect = new Rect(rect.x, rect.y, rect.width , rect.height); // var textureThumbnialRect = new Rect(rect.x , rect.y, rect.width, rect.height); // Texture texture = matEditor.TextureProperty(textureThumbnialRect,GetProperty(texturePropertyName), label, drawScaleOffset); Texture texture = TextureProperty(GetProperty(texturePropertyName), label, drawScaleOffset); // EditorGUILayout.EndHorizontal(); if (colorPropertyName != null) { // Rect colorPropRect = GetRectAfterLabelWidth(rect, true); // colorPropRect.x -= EditorGUI.indentLevel // EditorGUI.indentLevel++; // Rect colorPropRect = EditorGUILayout.GetControlRect(false); // Color color = matEditor.ColorProperty(colorPropRect, GetProperty(colorPropertyName), ""); ColorProperty("",colorPropertyName,true); // EditorGUI.indentLevel--; } var foldoutRect = EditorGUILayout.GetControlRect(false);//MaterialEditor.GetTextureFieldHeight() => 64f; Rect labelRect = foldoutRect; labelRect.width = EditorGUIUtility.labelWidth; EditorGUI.LabelField(labelRect, label+"相关功能", EditorStyles.boldLabel); foldOutAnimBool.target = EditorGUI.Foldout(foldoutRect, foldOutAnimBool.target, "", true); float faded = foldOutAnimBool.faded; if (faded == 0) faded = 0.00001f; EditorGUILayout.BeginFadeGroup(faded); EditorGUI.BeginDisabledGroup(texture == null); DrawAfterTexture(true, label, texturePropertyName, drawWrapMode, wrapModeFlagBitsName, flagIndex, drawBlock); EditorGUI.EndDisabledGroup(); EditorGUILayout.EndFadeGroup(); } public void DrawTexture(string label, string texturePropertyName, string colorPropertyName = null, bool drawScaleOffset = true, bool drawWrapMode = false, int wrapModeFlagBitsName = 0, int flagIndex = 2, Action drawBlock = null) { bool hasTexture = mats[0].GetTexture(texturePropertyName) != null; Texture texture = TextureProperty(GetProperty(texturePropertyName), label, drawScaleOffset); if (colorPropertyName != null) { ColorProperty("",colorPropertyName,true); } DrawAfterTexture(hasTexture, label, texturePropertyName, drawWrapMode, wrapModeFlagBitsName, flagIndex, drawBlock); } Texture TextureProperty(MaterialProperty textureProperty, string label, bool drawScaleOffset) { ShaderPropertyPack texturePropertyPack = ShaderPropertyPacksDic[textureProperty.name]; if (!GUI.enabled && isClearUnUsedTexture&&textureProperty.textureValue) { Debug.Log("清除掉贴图:"+textureProperty.name,textureProperty.textureValue); texturePropertyPack.property.textureValue = null; } float indentWidth = 15f; float currentIndentWidth = EditorGUI.indentLevel * indentWidth; float singleLineHeight = EditorGUIUtility.singleLineHeight; EditorGUILayout.BeginHorizontal(); EditorGUILayout.GetControlRect(GUILayout.Width(EditorGUI.indentLevel * indentWidth)); float textureFieldHeight = 3*singleLineHeight; var textureRect = EditorGUILayout.GetControlRect(GUILayout.Height(textureFieldHeight),GUILayout.Width(textureFieldHeight)); EditorGUILayout.BeginVertical(); var textureLabelRect = EditorGUILayout.GetControlRect(); textureLabelRect.x -= currentIndentWidth; textureLabelRect.width += currentIndentWidth; EditorGUI.LabelField(textureLabelRect,label,EditorStyles.boldLabel); var textureResetButtonRect = textureLabelRect; textureResetButtonRect.x += textureResetButtonRect.width; textureResetButtonRect.x -= ResetTool.ResetButtonSize; textureResetButtonRect.width = ResetTool.ResetButtonSize; float tillingOffsetLabelWidth = 30f; Rect tillingRect = EditorGUILayout.GetControlRect(); Rect tillingVec2Rect = tillingRect; tillingVec2Rect.x += tillingOffsetLabelWidth; tillingVec2Rect.width -= tillingOffsetLabelWidth; tillingVec2Rect.width -= ResetTool.ResetButtonSize; tillingVec2Rect.width -= 2f; Rect tillingResetButtonRect = tillingRect; tillingResetButtonRect.x = tillingResetButtonRect.x + tillingResetButtonRect.width - ResetTool.ResetButtonSize; tillingResetButtonRect.width = ResetTool.ResetButtonSize; Rect offsetRect = EditorGUILayout.GetControlRect(); Rect offsetVec2Rect = offsetRect; offsetVec2Rect.x += tillingOffsetLabelWidth; offsetVec2Rect.width -= tillingOffsetLabelWidth; offsetVec2Rect.width -= ResetTool.ResetButtonSize; offsetVec2Rect.width -= 2f; Rect offsetResetButtonRect = offsetRect; offsetResetButtonRect.x = offsetResetButtonRect.x + offsetResetButtonRect.width - ResetTool.ResetButtonSize; offsetResetButtonRect.width = ResetTool.ResetButtonSize; EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); Texture texture = textureProperty.textureValue; Action drawTextureEndChangeCheck = () => { textureProperty.textureValue = texture; ResetTool.CheckOnValueChange((label,texturePropertyPack.property.name)); }; EditorGUI.BeginChangeCheck(); texture = (Texture)EditorGUI.ObjectField(textureRect,texture,typeof(Texture2D)); if (EditorGUI.EndChangeCheck()) { drawTextureEndChangeCheck(); } ResetTool.DrawResetModifyButton(textureResetButtonRect,(label,texturePropertyPack.name),texturePropertyPack,resetAction: () => { texture = null; },onValueChangedCallBack:drawTextureEndChangeCheck); ResetTool.EndResetModifyButtonScope(); if (drawScaleOffset) { DrawScaleOffset(texturePropertyPack, tillingRect, tillingVec2Rect, tillingResetButtonRect, offsetRect, offsetVec2Rect, offsetResetButtonRect); } return texture; } static float indent => (float) EditorGUI.indentLevel * 15f; public void TextureScaleOffsetProperty(string texturePropertyName) { ShaderPropertyPack texturePropertyPack = ShaderPropertyPacksDic[texturePropertyName]; // EditorGUILayout.BeginHorizontal(); Rect tillingRect = EditorGUILayout.GetControlRect(); // EditorGUILayout.EndHorizontal(); Rect tillingLabelRect = new Rect(tillingRect.x + indent, tillingRect.y, EditorGUIUtility.labelWidth - indent, tillingRect.height); tillingLabelRect.width = EditorGUIUtility.labelWidth; Rect tillingVec2Rect = tillingRect; tillingVec2Rect.x += tillingLabelRect.width; tillingVec2Rect.x -= indent; tillingVec2Rect.width -= 3f; tillingVec2Rect.width -= tillingLabelRect.width; tillingVec2Rect.width += indent; tillingVec2Rect.width -= ResetTool.ResetButtonSize; Rect tillingResetButtonRect = tillingRect; tillingResetButtonRect.x = tillingResetButtonRect.x + tillingResetButtonRect.width - ResetTool.ResetButtonSize; tillingResetButtonRect.width = ResetTool.ResetButtonSize; Rect offsetRect = EditorGUILayout.GetControlRect(); Rect offsetLabelRect = new Rect(offsetRect.x + indent, offsetRect.y, EditorGUIUtility.labelWidth - indent, offsetRect.height); offsetLabelRect.width = EditorGUIUtility.labelWidth; Rect offsetVec2Rect = offsetRect; offsetVec2Rect.x += offsetLabelRect.width; offsetVec2Rect.x -= indent; offsetVec2Rect.width -= 3f; offsetVec2Rect.width -= offsetLabelRect.width; offsetVec2Rect.width += indent; offsetVec2Rect.width -= ResetTool.ResetButtonSize; Rect offsetResetButtonRect = offsetRect; offsetResetButtonRect.x = offsetResetButtonRect.x + offsetResetButtonRect.width - ResetTool.ResetButtonSize; offsetResetButtonRect.width = ResetTool.ResetButtonSize; DrawScaleOffset(texturePropertyPack,tillingLabelRect,tillingVec2Rect,tillingResetButtonRect,offsetLabelRect,offsetVec2Rect,offsetResetButtonRect); } public void DrawScaleOffset(ShaderPropertyPack texturePropertyPack, Rect tillingRect, Rect tillingVec2Rect, Rect tillingResetButtonRect, Rect offsetRect, Rect offsetVec2Rect, Rect offsetResetButtonRect) { MaterialProperty textureProperty = texturePropertyPack.property; Vector4 tillingOffset = textureProperty.textureScaleAndOffset; string tillingLabel = "Tilling"; var tillingTuple = (tillingLabel, textureProperty.name + "_ST"); GUI.Label(tillingRect,tillingLabel); Vector2 tilling = new Vector2(tillingOffset.x, tillingOffset.y); Action drawTillingEndChangeCheck = () => { tillingOffset.x = tilling.x; tillingOffset.y = tilling.y; textureProperty.textureScaleAndOffset = tillingOffset; ResetTool.CheckOnValueChange(tillingTuple); }; EditorGUI.BeginChangeCheck(); if (IsPropertyAnimated(tillingTuple.Item2, "x","y") ) { GUI.backgroundColor = animatedBackgroundColor; } tilling = EditorGUI.Vector2Field(tillingVec2Rect, "", tilling); GUI.backgroundColor = defaultBackgroundColor; if (EditorGUI.EndChangeCheck()) { drawTillingEndChangeCheck(); } ResetTool.DrawResetModifyButton(tillingResetButtonRect,tillingTuple,texturePropertyPack,resetAction: () => { tilling = Vector2.one; },onValueChangedCallBack:drawTillingEndChangeCheck,VectorValeType.Tilling); ResetTool.EndResetModifyButtonScope(); string offsetLabel = "Offset"; var offsetTuple = (offsetLabel, textureProperty.name + "_ST"); GUI.Label(offsetRect,offsetLabel); Vector2 offset = new Vector2(tillingOffset.z, tillingOffset.w); Action drawOffsetEndChangeCheck = () => { tillingOffset.z = offset.x; tillingOffset.w = offset.y; textureProperty.textureScaleAndOffset = tillingOffset; ResetTool.CheckOnValueChange(offsetTuple); }; EditorGUI.BeginChangeCheck(); if (IsPropertyAnimated(offsetTuple.Item2, "z","w")) { GUI.backgroundColor = animatedBackgroundColor; } offset = EditorGUI.Vector2Field(offsetVec2Rect, "", offset); GUI.backgroundColor = defaultBackgroundColor; if (EditorGUI.EndChangeCheck()) { drawOffsetEndChangeCheck(); } ResetTool.DrawResetModifyButton(offsetResetButtonRect,offsetTuple,texturePropertyPack,resetAction: () => { offset = Vector2.zero; },onValueChangedCallBack:drawOffsetEndChangeCheck,VectorValeType.Offset); ResetTool.EndResetModifyButtonScope(); } bool WrapModeFlagHasMixedValue(int wrapModeFlagBitsName, int flagIndex) { int tmpWrapMode = 0; for (int i = 0; i < shaderFlags.Length; i++) { if (i == 0) { tmpWrapMode = GetWrapModeFlagValue(wrapModeFlagBitsName,flagIndex,shaderFlags[i]); } else { if (!tmpWrapMode.Equals(GetWrapModeFlagValue(wrapModeFlagBitsName, flagIndex, shaderFlags[i]))) { return true; } } } return false; } int GetWrapModeFlagValue(int wrapModeFlagBitsName, int flagIndex,ShaderFlagsBase shaderFlag) { int tmpWrapMode = shaderFlag.CheckFlagBits(wrapModeFlagBitsName, index: flagIndex) ? 1 : 0; tmpWrapMode = shaderFlag.CheckFlagBits(wrapModeFlagBitsName << 16, index: flagIndex) ? tmpWrapMode + 2 : tmpWrapMode; return tmpWrapMode; } void SetWrapModeFlagValue(int wrapModeFlagBitsName, int flagIndex,int wrapModeValue) { for (int i = 0; i < shaderFlags.Length; i++) { switch (wrapModeValue) { case 0: shaderFlags[i].ClearFlagBits(wrapModeFlagBitsName, index: flagIndex); shaderFlags[i].ClearFlagBits(wrapModeFlagBitsName << 16, index: flagIndex); break; case 1: shaderFlags[i].SetFlagBits(wrapModeFlagBitsName, index: flagIndex); shaderFlags[i].ClearFlagBits(wrapModeFlagBitsName << 16, index: flagIndex); break; case 2: shaderFlags[i].ClearFlagBits(wrapModeFlagBitsName, index: flagIndex); shaderFlags[i].SetFlagBits(wrapModeFlagBitsName << 16, index: flagIndex); break; case 3: shaderFlags[i].SetFlagBits(wrapModeFlagBitsName, index: flagIndex); shaderFlags[i].SetFlagBits(wrapModeFlagBitsName << 16, index: flagIndex); break; } } } public void DrawWrapMode(string texturelabel,int wrapModeFlagBitsName = 0, int flagIndex = 2) { bool hasMixedValue = WrapModeFlagHasMixedValue(wrapModeFlagBitsName, flagIndex); EditorGUI.showMixedValue = hasMixedValue; string wrapLabel = texturelabel + "循环模式"; (string, string) wrapNameTuple = (wrapLabel, ""); int tmpWrapMode = GetWrapModeFlagValue(wrapModeFlagBitsName, flagIndex,shaderFlags[0]); Action onWrapModeEndChangeCheck = () => { SetWrapModeFlagValue(wrapModeFlagBitsName, flagIndex, tmpWrapMode); hasMixedValue = WrapModeFlagHasMixedValue(wrapModeFlagBitsName, flagIndex); ResetTool.CheckOnValueChange(wrapNameTuple); }; EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); tmpWrapMode = EditorGUILayout.Popup(new GUIContent(wrapLabel), tmpWrapMode, Enum.GetNames(typeof(SamplerWarpMode))); if (EditorGUI.EndChangeCheck()) { onWrapModeEndChangeCheck(); } ResetTool.DrawResetModifyButton(new Rect(),wrapNameTuple,resetCallBack: () => { tmpWrapMode = 0; },onValueChangedCallBack:onWrapModeEndChangeCheck,() => { bool hasModified = GetWrapModeFlagValue(wrapModeFlagBitsName, flagIndex,shaderFlags[0]) != 0 ? true : false; return hasModified; }, () => WrapModeFlagHasMixedValue(wrapModeFlagBitsName, flagIndex)); EditorGUILayout.EndHorizontal(); ResetTool.EndResetModifyButtonScope(); } public void DrawAfterTexture(bool hasTexture, string label, string texturePropertyName, bool drawWrapMode = false, int wrapModeFlagBitsName = 0, int flagIndex = 2, Action drawBlock = null) { // EditorGUI.indentLevel++; EditorGUI.BeginDisabledGroup(!hasTexture); if (drawWrapMode) { DrawWrapMode(label, wrapModeFlagBitsName,flagIndex); } drawBlock?.Invoke(GetProperty(texturePropertyName)); // EditorGUI.indentLevel--; EditorGUI.EndDisabledGroup(); } public void DrawPopUp(string label, string propertyName, string[] options, string[] toolTips = null, Action drawBlock = null,Action drawOnValueChangedBlock = null,bool isSharedGlobalParent = false) { MaterialProperty property = GetProperty(propertyName); if (property == null) return; EditorGUI.showMixedValue = property.hasMixedValue; float mode = property.floatValue; EditorGUI.BeginChangeCheck(); GUIContent[] optionGUIContents = new GUIContent[options.Length]; for (int i = 0; i < optionGUIContents.Length; i++) { if (toolTips != null && toolTips.Length == options.Length) { optionGUIContents[i] = new GUIContent(options[i], toolTips[i]); } else { optionGUIContents[i] = new GUIContent(options[i]); } } Action drawOnValueChanged = () => { property.floatValue = mode; drawOnValueChangedBlock?.Invoke(property); ResetTool.CheckOnValueChange((label,propertyName)); }; EditorGUILayout.BeginHorizontal(); mode = EditorGUILayout.Popup(new GUIContent(label), (int)mode, optionGUIContents); if (EditorGUI.EndChangeCheck()) { drawOnValueChanged.Invoke(); } ResetTool.DrawResetModifyButton(new Rect(),(label,propertyName),ShaderPropertyPacksDic[propertyName],resetAction: () => { mode = property.floatValue; },onValueChangedCallBack:drawOnValueChanged); EditorGUILayout.EndHorizontal(); drawBlock?.Invoke(property); ResetTool.EndResetModifyButtonScope(); EditorGUI.showMixedValue = false; } public MaterialProperty GetProperty(string propertyName) { // foreach (ShaderPropertyPack pack in ShaderPropertyPacks) // { // if (pack.name == propertyName) // { // return pack.property; // } // } if (ShaderPropertyPacksDic.ContainsKey(propertyName)) { return ShaderPropertyPacksDic[propertyName].property; } // Debug.LogError("材质球" + mat.name + "找不到属性" + propertyName, mat); return null; } bool RenderQueueHasMixedValue() { int queue = 0; for (int i = 0; i < mats.Count; i++) { if (i == 0) { queue = mats[i].renderQueue; } else { if (queue != mats[i].renderQueue) { return true; } } } return false; } public void DrawRenderQueue(MaterialProperty queueBiasProp) { Rect rect = EditorGUILayout.GetControlRect(); Rect labelRect = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height); int queueLabelWidth = 55; Rect queueLabelRect = new Rect(labelRect.x , labelRect.y, queueLabelWidth, rect.height); Rect queueNumberRect = new Rect(queueLabelRect.x + queueLabelWidth , queueLabelRect.y,EditorGUIUtility.labelWidth - queueLabelWidth, rect.height); EditorGUI.LabelField(queueLabelRect, "Queue:" ); EditorGUI.showMixedValue = RenderQueueHasMixedValue(); EditorGUI.BeginDisabledGroup(true); EditorGUI.TextField(queueNumberRect, mats[0].renderQueue.ToString()); EditorGUI.EndDisabledGroup(); EditorGUI.showMixedValue = false; Rect afterLabelRect = GetRectAfterLabelWidth(rect); int QueueBias = (int)queueBiasProp.floatValue; EditorGUI.showMixedValue = queueBiasProp.hasMixedValue; EditorGUI.BeginChangeCheck(); QueueBias = EditorGUI.IntField(afterLabelRect, "QueueBias:", QueueBias); if (EditorGUI.EndChangeCheck()) { queueBiasProp.floatValue = QueueBias; } EditorGUI.showMixedValue = false; } void GuiLine(int i_height = 1) { Rect rect = EditorGUILayout.GetControlRect(false, i_height); rect.height = i_height; EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f)); } public static float PowerSlider(Rect position, GUIContent label, float value, float leftValue, float rightValue, float power) { var editorGuiType = typeof(EditorGUI); var methodInfo = editorGuiType.GetMethod( "PowerSlider", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static, null, new[] { typeof(Rect), typeof(GUIContent), typeof(float), typeof(float), typeof(float), typeof(float) }, null); if (methodInfo != null) { return (float)methodInfo.Invoke(null, new object[] { position, label, value, leftValue, rightValue, power }); } return leftValue; } private GradientAlphaKey[] defaultGradientAlphaKey = new GradientAlphaKey[] { new GradientAlphaKey(1, 0), new GradientAlphaKey(1, 1) }; float RGBLuminance(Color color) { return 0.2126f* color.r + 0.7152f * color.g + 0.0722f * color.b; } private bool isUpateGradientPickerCache = false; public void DrawGradientUndoPerformed() { isUpateGradientPickerCache = true; } Dictionary gradientsUpdateDic = new Dictionary(); void GetGradientKeyCount(MaterialProperty countProperty, MaterialProperty[] colorProperties, MaterialProperty[] alphaProperties,out int countPropertyIntValue,out int colorKeysCount,out int alphaKeysCount) { countPropertyIntValue = countProperty.intValue; if (colorProperties != null && alphaProperties != null) { colorKeysCount = countPropertyIntValue & 0xFFFF; alphaKeysCount = countPropertyIntValue >> 16; } else { colorKeysCount = countPropertyIntValue; alphaKeysCount = 2; } } bool GradientPropertyHasMixedValue(MaterialProperty countProperty, MaterialProperty[] colorProperties = null, MaterialProperty[] alphaProperties = null) { GetGradientKeyCount(countProperty, colorProperties, alphaProperties,out int countPropertyIntValue, out int colorKeysCount,out int alphaKeysCount); bool hasMixedValue = false; hasMixedValue |= countProperty.hasMixedValue; if (colorProperties != null) { for(int i = 0; i < colorKeysCount; i++) { hasMixedValue |= colorProperties[i].hasMixedValue; } } if (alphaProperties != null) { for (int i = 0; i < Mathf.CeilToInt(alphaKeysCount/2f); i++) { hasMixedValue |= alphaProperties[i].hasMixedValue; } } return hasMixedValue; } void GetGradientConditionBool(MaterialProperty[] colorProperties, MaterialProperty[] alphaProperties, out bool isBlackAndWhiteGradient, out bool isNoAlphaColorGradient) { isBlackAndWhiteGradient = colorProperties == null && alphaProperties != null; isNoAlphaColorGradient = colorProperties != null && alphaProperties == null; } void SetGradientByProperty(Gradient gradient, MaterialProperty countProperty, MaterialProperty[] colorProperties = null, MaterialProperty[] alphaProperties = null) { GetGradientConditionBool(colorProperties,alphaProperties,out bool isBlackAndWhiteGradient,out bool isNoAlphaColorGradient); GetGradientKeyCount(countProperty, colorProperties, alphaProperties,out int countPropertyIntValue, out int colorKeysCount,out int alphaKeysCount); if (colorProperties != null || isBlackAndWhiteGradient) { GradientColorKey[] colorKeys; if (gradient.colorKeys.Length != colorKeysCount) { colorKeys = new GradientColorKey[colorKeysCount]; } else { colorKeys = gradient.colorKeys; } for (int i = 0; i < colorKeysCount; i++) { if (isBlackAndWhiteGradient) { Vector4 vec = alphaProperties[i / 2].vectorValue; Color c = Color.white; if (i % 2 == 0) { c.r = vec.x; c.g = vec.x; c.b = vec.x; } else { c.r = vec.z; c.g = vec.z; c.b = vec.z; } // Debug.Log(i); // Debug.Log(c); colorKeys[i].color = c; colorKeys[i].time = i % 2 == 0 ? vec.y : vec.w; } else { Color c = colorProperties[i].colorValue; colorKeys[i].color = c; colorKeys[i].time = c.a; } } gradient.colorKeys = colorKeys; } if (isBlackAndWhiteGradient || isNoAlphaColorGradient || alphaProperties == null) { gradient.alphaKeys = defaultGradientAlphaKey; } else { GradientAlphaKey[] alphaKeys; if (gradient.alphaKeys.Length != alphaKeysCount) { alphaKeys = new GradientAlphaKey[alphaKeysCount]; } else { alphaKeys = gradient.alphaKeys; } for (int i = 0; i < alphaKeysCount ; i++) { Vector4 vec = alphaProperties[i / 2].vectorValue; if (i % 2 == 0) { alphaKeys[i].alpha = vec.x; alphaKeys[i].time = vec.y; } else { alphaKeys[i].alpha = vec.z; alphaKeys[i].time = vec.w; } } gradient.alphaKeys = alphaKeys; } } //如果是黑白Gradient,则取Gradient的颜色的黑白值(这样在面板上可视化比较好) //如果既有颜色,也有Alpha。则在CountProperty上采取前16位和后16位编码。 //原则:gradient对象只是一个操作中介。取值应该直接在MatProperty上去,Set值也应该在验证合法后才能Set,不合法应该提出警告。 public void DrawGradient(Gradient gradient,bool hdr,ColorSpace colorSpace,string label,int maxCount,string countPropertyName,MaterialProperty[] colorProperties = null,MaterialProperty[] alphaProperties = null) { (string,string) nameTuple = (label, countPropertyName); if (isUpateGradientPickerCache) { foreach (var keys in gradientsUpdateDic.Keys.ToList()) { gradientsUpdateDic[keys] = true; } isUpateGradientPickerCache = false; } MaterialProperty countProperty = GetProperty(countPropertyName); Rect rect = EditorGUILayout.GetControlRect(); // var labelRect = new Rect(rect.x + 2f, rect.y, rect.width - 2f, rect.height); // EditorGUI.LabelField(labelRect,label); var gradientRect = rect; gradientRect.width -= ResetTool.ResetButtonSize; gradientRect.width -= 2f; var gradientResetButtonRect = rect; gradientResetButtonRect.x = gradientResetButtonRect.x + gradientResetButtonRect.width - ResetTool.ResetButtonSize; gradientResetButtonRect.width = ResetTool.ResetButtonSize; if (gradient == null||gradientsUpdateDic[gradient]) { if (gradient == null) { gradient = new Gradient(); // gradient.colorSpace = ColorSpace.Gamma; gradientsUpdateDic.Add(gradient,false); } gradientsUpdateDic[gradient] = false; SetGradientByProperty(gradient,countProperty, colorProperties, alphaProperties); GradientReflectionHelper.RefreshGradientData(); // Debug.Log("----------------SetCurrentGradient------------------"); } EditorGUI.showMixedValue = GradientPropertyHasMixedValue(countProperty, colorProperties, alphaProperties); GetGradientKeyCount(countProperty, colorProperties, alphaProperties,out int countPropertyIntValue, out int colorKeysCount,out int alphaKeysCount); GetGradientConditionBool(colorProperties, alphaProperties,out bool isBlackAndWhiteGradient,out bool isNoAlphaColorGradient); EditorGUI.BeginChangeCheck(); gradient = EditorGUI.GradientField(gradientRect, new GUIContent(label),gradient,hdr,colorSpace); Action onGradientEndChangeCheck = () => { gradientsUpdateDic[gradient] = true; int countPropertyValue = countPropertyIntValue; if (colorProperties != null || isBlackAndWhiteGradient) { int finalColorKeysCount = gradient.colorKeys.Length; if (finalColorKeysCount <= maxCount) { if (isBlackAndWhiteGradient) { int floatPropertyCount = Mathf.CeilToInt((float)finalColorKeysCount / (2f)); for (int i = 0; i < floatPropertyCount; i++) { Vector4 vec = Vector4.zero; vec.x = gradient.colorKeys[i*2].color.r; vec.y = gradient.colorKeys[i*2].time; if (i * 2 + 1 < gradient.colorKeys.Length) { vec.z = gradient.colorKeys[i * 2 + 1].color.r; vec.w = gradient.colorKeys[i * 2 + 1].time; } alphaProperties[i].vectorValue = vec; } } else { for (int i = 0; i < finalColorKeysCount; i++) { Color c = gradient.colorKeys[i].color; c.a = gradient.colorKeys[i].time; colorProperties[i].colorValue = c; } } countPropertyValue &= (0xFFFF <<16); countPropertyValue |= finalColorKeysCount; } } if (!(isBlackAndWhiteGradient || isNoAlphaColorGradient || alphaProperties == null)) { int finalAlphaKeysCount = gradient.alphaKeys.Length; if (finalAlphaKeysCount <= maxCount) { int floatPropertyCount = Mathf.CeilToInt((float)finalAlphaKeysCount / (2f)); for (int i = 0; i < floatPropertyCount; i++) { Vector4 vec = Vector4.zero; vec.x = gradient.alphaKeys[i*2].alpha; vec.y = gradient.alphaKeys[i*2].time; if (i * 2 + 1 < gradient.alphaKeys.Length) { vec.z = gradient.alphaKeys[i * 2 + 1].alpha; vec.w = gradient.alphaKeys[i * 2 + 1].time; } alphaProperties[i].vectorValue = vec; } countPropertyValue &= (0xFFFF); int alphaCount = finalAlphaKeysCount << 16; countPropertyValue |= alphaCount; } } countProperty.intValue = countPropertyValue; ResetTool.CheckOnValueChange(nameTuple); }; if (EditorGUI.EndChangeCheck()) { onGradientEndChangeCheck(); } ResetTool.DrawResetModifyButton(gradientResetButtonRect,nameTuple,resetCallBack: () => { ShaderPropertyPack countPropPack = ShaderPropertyPacksDic[countPropertyName]; countPropPack.property.intValue = 2; if (colorProperties != null) { foreach (var colorProp in colorProperties) { ShaderPropertyPack colorPropPack = ShaderPropertyPacksDic[colorProp.name]; colorPropPack.property.colorValue = shader.GetPropertyDefaultVectorValue(colorPropPack.index); } } if (alphaProperties != null) { foreach (var alphaProps in alphaProperties) { ShaderPropertyPack alphaPropPack = ShaderPropertyPacksDic[alphaProps.name]; alphaPropPack.property.vectorValue = shader.GetPropertyDefaultVectorValue(alphaPropPack.index); } } SetGradientByProperty(gradient,countProperty, colorProperties, alphaProperties); },onValueChangedCallBack:onGradientEndChangeCheck, checkHasMixedValueOnValueChange: () => GradientPropertyHasMixedValue(countProperty, colorProperties, alphaProperties), checkHasModifyOnValueChange: () => { bool hasModified = false; ShaderPropertyPack countPropPack = ShaderPropertyPacksDic[countPropertyName]; bool hasCountModified = !Mathf.Approximately(mats[0].GetInteger(countPropPack.name),2); hasModified |= hasCountModified; if (colorProperties != null) { foreach (var colorProp in colorProperties) { ShaderPropertyPack colorPropPack = ShaderPropertyPacksDic[colorProp.name]; hasModified |= mats[0].GetVector(colorPropPack.name) != shader.GetPropertyDefaultVectorValue(colorPropPack.index); } } if (alphaProperties != null) { foreach (var alphaProps in alphaProperties) { ShaderPropertyPack alphaPropPack = ShaderPropertyPacksDic[alphaProps.name]; Vector4 vec = mats[0].GetVector(alphaPropPack.name); Vector4 defaultVec = shader.GetPropertyDefaultVectorValue(alphaPropPack.index); hasModified |= vec != defaultVec; } } return hasModified; }); ResetTool.EndResetModifyButtonScope(); } } public enum VectorValeType { X,Y,Z,W,XY,ZW,XYZ,XYZW,Tilling,Offset,Undefine } }