Files
Cielonos/Assets/PotaToon/Editor/Scripts/PotaToonEditor.cs
SoulliesOfficial f7af60351b 阶段性完成
2025-12-08 05:27:53 -05:00

1053 lines
50 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
namespace PotaToon.Editor
{
#if UNITY_2021_3
[VolumeComponentEditor(typeof(PotaToon))]
#else
[CustomEditor(typeof(PotaToon))]
#endif
public class PotaToonEditor : VolumeComponentEditor
{
SerializedDataParameter m_Mode;
SerializedDataParameter m_TransparentShadow;
SerializedDataParameter m_CharShadowDirOffsetMode;
SerializedDataParameter m_CharShadowDirOffset;
SerializedDataParameter m_FollowLayerMask;
SerializedDataParameter m_MaxToonBrightness;
SerializedDataParameter m_Bias;
SerializedDataParameter m_NormalBias;
SerializedDataParameter m_Quality;
SerializedDataParameter m_TextureScale;
SerializedDataParameter m_TransparentTextureScale;
SerializedDataParameter m_ShadowCullingDistance;
SerializedDataParameter m_OIT;
SerializedDataParameter m_OITMode;
SerializedDataParameter m_CharPostProcess;
SerializedDataParameter m_CharScreenOutline;
SerializedDataParameter m_CharScreenOutlineExcludeInnerLines;
SerializedDataParameter m_CharScreenOutlineColor;
SerializedDataParameter m_CharScreenOutlineThickness;
SerializedDataParameter m_CharScreenOutlineEdgeStrength;
SerializedDataParameter m_CharPostExposure;
SerializedDataParameter m_CharGammaAdjust;
SerializedDataParameter m_CharToneMapping;
SerializedDataParameter m_CharContrast;
SerializedDataParameter m_CharColorFilter;
SerializedDataParameter m_CharHueShift;
SerializedDataParameter m_CharSaturation;
SerializedDataParameter m_CharWhiteBalanceTemperature;
SerializedDataParameter m_CharWhiteBalanceTint;
SerializedDataParameter m_ScreenRimWidth;
SerializedDataParameter m_ScreenRimColor;
SerializedDataParameter m_EnvPostProcess;
SerializedDataParameter m_EnvToneMapping;
SerializedDataParameter m_EnvPostExposure;
SerializedDataParameter m_CharToeStrength;
SerializedDataParameter m_CharToeLength;
SerializedDataParameter m_CharShoulderStrength;
SerializedDataParameter m_CharShoulderLength;
SerializedDataParameter m_CharShoulderAngle;
SerializedDataParameter m_CharGamma;
SerializedDataParameter m_EnvToeStrength;
SerializedDataParameter m_EnvToeLength;
SerializedDataParameter m_EnvShoulderStrength;
SerializedDataParameter m_EnvShoulderLength;
SerializedDataParameter m_EnvShoulderAngle;
SerializedDataParameter m_EnvGamma;
SerializedDataParameter m_EnvContrast;
SerializedDataParameter m_EnvColorFilter;
SerializedDataParameter m_EnvHueShift;
SerializedDataParameter m_EnvSaturation;
SerializedDataParameter m_EnvWhiteBalanceTemperature;
SerializedDataParameter m_EnvWhiteBalanceTint;
SerializedDataParameter m_CharBloom;
SerializedDataParameter m_CharBloomThreshold;
SerializedDataParameter m_CharBloomIntensity;
SerializedDataParameter m_CharBloomScatter;
SerializedDataParameter m_CharBloomClamp;
SerializedDataParameter m_CharBloomTint;
SerializedDataParameter m_CharBloomHQ;
SerializedDataParameter m_CharBloomDownscale;
SerializedDataParameter m_CharBloomMaxIterations;
SerializedDataParameter m_CharBloomDirtTexture;
SerializedDataParameter m_CharBloomDirtIntensity;
SerializedDataParameter m_EnvBloom;
SerializedDataParameter m_EnvBloomThreshold;
SerializedDataParameter m_EnvBloomIntensity;
SerializedDataParameter m_EnvBloomScatter;
SerializedDataParameter m_EnvBloomClamp;
SerializedDataParameter m_EnvBloomTint;
SerializedDataParameter m_EnvBloomHQ;
SerializedDataParameter m_EnvBloomDownscale;
SerializedDataParameter m_EnvBloomMaxIterations;
SerializedDataParameter m_EnvBloomDirtTexture;
SerializedDataParameter m_EnvBloomDirtIntensity;
private static bool s_ShadowSettingsFoldout;
private static bool s_CharScreenOutlineFoldout;
private static bool s_CharColorGradingFoldout;
private static bool s_CharScreenRimFoldout;
private static bool s_CharBloomFoldout;
private static bool s_EnvColorGradingFoldout;
private static bool s_EnvBloomFoldout;
// Curve drawing utilities
private readonly HableCurve m_HableCurve = new HableCurve();
private Rect m_CurveRect;
private Material m_CurveMaterial;
private RenderTexture m_CurveTex;
// Preset
private static VolumeComponent s_CopyBuffer;
private static bool s_PresetFoldout = true;
private Vector2 m_ScrollPos = Vector2.zero;
private int m_SelectedIndex = -1;
private List<PotaToonVolumePreset> m_Presets;
private Color m_SelectedPresetColor = new Color(0.4f, 0.8f, 0.3f) ;
private Texture2D m_PresetIcon;
public override void OnEnable()
{
PotaToonGUIUtility.LoadAdvancedSettingUnlocked();
InitializePresetsAndIcons();
var o = new PropertyFetcher<PotaToon>(serializedObject);
m_Mode = Unpack(o.Find(x => x.mode));
m_TransparentShadow = Unpack(o.Find(x => x.transparentShadow));
m_CharShadowDirOffsetMode = Unpack(o.Find(x => x.charShadowDirOffsetMode));
m_CharShadowDirOffset = Unpack(o.Find(x => x.charShadowDirOffset));
m_FollowLayerMask = Unpack(o.Find(x => x.followLayerMask));
m_MaxToonBrightness = Unpack(o.Find(x => x.maxToonBrightness));
m_Bias = Unpack(o.Find(x => x.bias));
m_NormalBias = Unpack(o.Find(x => x.normalBias));
m_Quality = Unpack(o.Find(x => x.quality));
m_TextureScale = Unpack(o.Find(x => x.textureScale));
m_TransparentTextureScale = Unpack(o.Find(x => x.transparentTextureScale));
m_ShadowCullingDistance = Unpack(o.Find(x => x.shadowCullingDistance));
m_OIT = Unpack(o.Find(x => x.oit));
m_OITMode = Unpack(o.Find(x => x.oitMode));
m_CharPostProcess = Unpack(o.Find(x => x.charPostProcessing));
m_CharScreenOutline = Unpack(o.Find(x => x.charScreenOutline));
m_CharScreenOutlineExcludeInnerLines = Unpack(o.Find(x => x.charScreenOutlineExcludeInnerLines));
m_CharScreenOutlineColor = Unpack(o.Find(x => x.charScreenOutlineColor));
m_CharScreenOutlineThickness = Unpack(o.Find(x => x.charScreenOutlineThickness));
m_CharScreenOutlineEdgeStrength = Unpack(o.Find(x => x.charScreenOutlineEdgeStrength));
m_CharPostExposure = Unpack(o.Find(x => x.charPostExposure));
m_CharGammaAdjust = Unpack(o.Find(x => x.charGammaAdjust));
m_CharToneMapping = Unpack(o.Find(x => x.charToneMapping));
m_CharContrast = Unpack(o.Find(x => x.charContrast));
m_CharColorFilter = Unpack(o.Find(x => x.charColorFilter));
m_CharHueShift = Unpack(o.Find(x => x.charHueShift));
m_CharSaturation = Unpack(o.Find(x => x.charSaturation));
m_CharWhiteBalanceTemperature = Unpack(o.Find(x => x.whiteBalanceTemperature));
m_CharWhiteBalanceTint = Unpack(o.Find(x => x.whiteBalanceTint));
m_ScreenRimWidth = Unpack(o.Find(x => x.screenRimWidth));
m_ScreenRimColor = Unpack(o.Find(x => x.screenRimColor));
m_EnvPostProcess = Unpack(o.Find(x => x.envPostProcessing));
m_EnvToneMapping = Unpack(o.Find(x => x.envToneMapping));
m_EnvPostExposure = Unpack(o.Find(x => x.envPostExposure));
m_EnvContrast = Unpack(o.Find(x => x.envContrast));
m_EnvColorFilter = Unpack(o.Find(x => x.envColorFilter));
m_EnvHueShift = Unpack(o.Find(x => x.envHueShift));
m_EnvSaturation = Unpack(o.Find(x => x.envSaturation));
m_EnvWhiteBalanceTemperature = Unpack(o.Find(x => x.envWhiteBalanceTemperature));
m_EnvWhiteBalanceTint = Unpack(o.Find(x => x.envWhiteBalanceTint));
m_CharToeStrength = Unpack(o.Find(x => x.charToeStrength));
m_CharToeLength = Unpack(o.Find(x => x.charToeLength));
m_CharShoulderStrength = Unpack(o.Find(x => x.charShoulderStrength));
m_CharShoulderLength = Unpack(o.Find(x => x.charShoulderLength));
m_CharShoulderAngle = Unpack(o.Find(x => x.charShoulderAngle));
m_CharGamma = Unpack(o.Find(x => x.charGamma));
m_EnvToeStrength = Unpack(o.Find(x => x.envToeStrength));
m_EnvToeLength = Unpack(o.Find(x => x.envToeLength));
m_EnvShoulderStrength = Unpack(o.Find(x => x.envShoulderStrength));
m_EnvShoulderLength = Unpack(o.Find(x => x.envShoulderLength));
m_EnvShoulderAngle = Unpack(o.Find(x => x.envShoulderAngle));
m_EnvGamma = Unpack(o.Find(x => x.envGamma));
m_CharBloom = Unpack(o.Find(x => x.charBloom));
m_CharBloomThreshold = Unpack(o.Find(x => x.threshold));
m_CharBloomIntensity = Unpack(o.Find(x => x.intensity));
m_CharBloomScatter = Unpack(o.Find(x => x.scatter));
m_CharBloomClamp = Unpack(o.Find(x => x.clamp));
m_CharBloomTint = Unpack(o.Find(x => x.tint));
m_CharBloomHQ = Unpack(o.Find(x => x.highQualityFiltering));
m_CharBloomDownscale = Unpack(o.Find(x => x.downscale));
m_CharBloomMaxIterations = Unpack(o.Find(x => x.maxIterations));
m_CharBloomDirtTexture = Unpack(o.Find(x => x.dirtTexture));
m_CharBloomDirtIntensity = Unpack(o.Find(x => x.dirtIntensity));
m_EnvBloom = Unpack(o.Find(x => x.envBloom));
m_EnvBloomThreshold = Unpack(o.Find(x => x.envBloomThreshold));
m_EnvBloomIntensity = Unpack(o.Find(x => x.envBloomIntensity));
m_EnvBloomScatter = Unpack(o.Find(x => x.envBloomScatter));
m_EnvBloomClamp = Unpack(o.Find(x => x.envBloomClamp));
m_EnvBloomTint = Unpack(o.Find(x => x.envBloomTint));
m_EnvBloomHQ = Unpack(o.Find(x => x.envBloomHighQualityFiltering));
m_EnvBloomDownscale = Unpack(o.Find(x => x.envBloomDownscale));
m_EnvBloomMaxIterations = Unpack(o.Find(x => x.envBloomMaxIterations));
m_EnvBloomDirtTexture = Unpack(o.Find(x => x.envBloomDirtTexture));
m_EnvBloomDirtIntensity = Unpack(o.Find(x => x.envBloomDirtIntensity));
m_CurveMaterial = new Material(Shader.Find("Hidden/PotaToon/Editor/Custom Tonemapper Curve"));
}
public override void OnDisable()
{
CoreUtils.Destroy(m_CurveMaterial);
m_CurveMaterial = null;
CoreUtils.Destroy(m_CurveTex);
m_CurveTex = null;
}
public override void OnInspectorGUI()
{
var originalColor = GUI.backgroundColor;
var subGroupBoxStyle = new GUIStyle(GUI.skin.box) { padding = new RectOffset(10, 10, 5, 5), margin = new RectOffset(5, 5, 5, 5) };
// Copy/Paste Buttons
const float iconSize = 32f;
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(EditorGUIUtility.IconContent("Clipboard", "|Copy settings"), GUILayout.Width(iconSize), GUILayout.Height(iconSize)))
CopyComponent();
if (GUILayout.Button(EditorGUIUtility.IconContent("d_SaveAs", "|Paste settings"), GUILayout.Width(iconSize), GUILayout.Height(iconSize)))
PasteComponent();
GUI.backgroundColor = s_PresetFoldout ? new Color(0.8f, 0.8f, 1f) : originalColor;
var presetIconContent = m_PresetIcon != null ? new GUIContent(m_PresetIcon, "Preset") : EditorGUIUtility.IconContent("d_Preset.Context", "|Preset");
if (GUILayout.Button(presetIconContent, GUILayout.Width(iconSize), GUILayout.Height(iconSize)))
{
s_PresetFoldout = !s_PresetFoldout;
}
GUI.backgroundColor = originalColor;
EditorGUILayout.EndHorizontal();
DrawPresetField();
EditorGUILayout.LabelField("MAIN SETTINGS", EditorStyles.boldLabel);
// Mode
PropertyField(m_Mode);
bool isConcertMode = m_Mode.value.enumValueIndex == (int)PotaToonMode.Concert;
// Mode Settings
{
EditorGUI.indentLevel++;
EditorGUILayout.BeginVertical(subGroupBoxStyle);
if (!isConcertMode)
{
EditorGUILayout.LabelField("Shadow Direction Control");
PropertyField(m_CharShadowDirOffsetMode, new GUIContent("Mode"));
var text = m_CharShadowDirOffsetMode.value.enumValueIndex == 0 ? "Offset" : "Fixed Direction";
PropertyField(m_CharShadowDirOffset, new GUIContent(text));
}
else
{
PropertyField(m_FollowLayerMask);
}
EditorGUILayout.EndVertical();
EditorGUI.indentLevel--;
}
// Shadow
PropertyField(m_TransparentShadow);
EditorGUILayout.Space();
// Quality
PropertyField(m_Quality);
{
EditorGUI.indentLevel++;
EditorGUI.BeginDisabledGroup(m_Quality.value.enumValueIndex != (int)PotaToonQuality.Custom);
switch (m_Quality.value.enumValueIndex)
{
case (int)PotaToonQuality.Low:
m_TextureScale.value.enumValueIndex = 0; // X2
m_TransparentTextureScale.value.enumValueIndex = 0; // X2
m_ShadowCullingDistance.value.floatValue = 1.0f;
break;
case (int)PotaToonQuality.Medium:
m_TextureScale.value.enumValueIndex = 1; // X4
m_TransparentTextureScale.value.enumValueIndex = 1; // X4
m_ShadowCullingDistance.value.floatValue = 1.2f;
break;
case (int)PotaToonQuality.High:
m_TextureScale.value.enumValueIndex = 2; // X8
m_TransparentTextureScale.value.enumValueIndex = 2; // X8
m_ShadowCullingDistance.value.floatValue = 1.5f;
break;
case (int)PotaToonQuality.Cinematic:
m_TextureScale.value.enumValueIndex = 3; // X16
m_TransparentTextureScale.value.enumValueIndex = 3; // X16
m_ShadowCullingDistance.value.floatValue = 2.0f;
break;
}
if (m_Quality.value.enumValueIndex == (int)PotaToonQuality.Custom)
{
PropertyField(m_TextureScale);
PropertyField(m_TransparentTextureScale);
PropertyField(m_ShadowCullingDistance, new GUIContent("Culling Distance"));
}
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel--;
}
// Max Brightness
PropertyField(m_MaxToonBrightness, new GUIContent("Max Material Brightness"));
// Post Processing
var postProcessLabelStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter, fontStyle = FontStyle.Bold, fontSize = 13 };
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
EditorGUILayout.LabelField("POST PROCESSING", EditorStyles.boldLabel);
PropertyField(m_CharPostProcess);
if (m_CharPostProcess.value.boolValue)
{
EditorGUI.indentLevel++;
s_CharScreenOutlineFoldout = EditorGUILayout.Foldout(s_CharScreenOutlineFoldout, "[Screen Outline]", true, EditorStyles.foldoutHeader);
if (s_CharScreenOutlineFoldout)
{
EditorGUILayout.BeginVertical(subGroupBoxStyle);
PropertyField(m_CharScreenOutline, new GUIContent("Screen Outline"));
if (m_CharScreenOutline.value.boolValue)
{
EditorGUI.indentLevel++;
PropertyField(m_CharScreenOutlineExcludeInnerLines, new GUIContent("Exclude Inner Outline"));
PropertyField(m_CharScreenOutlineColor, new GUIContent("Color"));
PropertyField(m_CharScreenOutlineThickness, new GUIContent("Thickness"));
PropertyField(m_CharScreenOutlineEdgeStrength, new GUIContent("Edge Strength"));
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
}
s_CharColorGradingFoldout = EditorGUILayout.Foldout(s_CharColorGradingFoldout, "[Color Grading]", true, EditorStyles.foldoutHeader);
if (s_CharColorGradingFoldout)
{
EditorGUILayout.BeginVertical(subGroupBoxStyle);
EditorGUILayout.LabelField("Tone Mapping", postProcessLabelStyle);
PropertyField(m_CharToneMapping, new GUIContent("Mode"));
if (m_CharToneMapping.value.intValue == (int)PotaToonToneMapping.Custom)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space();
// Reserve GUI space
m_CurveRect = GUILayoutUtility.GetRect(128, 80);
m_CurveRect.xMin += EditorGUI.indentLevel * 15f;
if (Event.current.type == EventType.Repaint)
{
// Prepare curve data
float toeStrength = m_CharToeStrength.value.floatValue;
float toeLength = m_CharToeLength.value.floatValue;
float shoulderStrength = m_CharShoulderStrength.value.floatValue;
float shoulderLength = m_CharShoulderLength.value.floatValue;
float shoulderAngle = m_CharShoulderAngle.value.floatValue;
float gamma = m_CharGamma.value.floatValue;
m_HableCurve.Init(
toeStrength,
toeLength,
shoulderStrength,
shoulderLength,
shoulderAngle,
gamma
);
float alpha = GUI.enabled ? 1f : 0.5f;
m_CurveMaterial.SetVector(ShaderIDs._CustomToneCurve, m_HableCurve.uniforms.curve);
m_CurveMaterial.SetVector(ShaderIDs._ToeSegmentA, m_HableCurve.uniforms.toeSegmentA);
m_CurveMaterial.SetVector(ShaderIDs._ToeSegmentB, m_HableCurve.uniforms.toeSegmentB);
m_CurveMaterial.SetVector(ShaderIDs._MidSegmentA, m_HableCurve.uniforms.midSegmentA);
m_CurveMaterial.SetVector(ShaderIDs._MidSegmentB, m_HableCurve.uniforms.midSegmentB);
m_CurveMaterial.SetVector(ShaderIDs._ShoSegmentA, m_HableCurve.uniforms.shoSegmentA);
m_CurveMaterial.SetVector(ShaderIDs._ShoSegmentB, m_HableCurve.uniforms.shoSegmentB);
m_CurveMaterial.SetVector("_Variants", new Vector4(alpha, m_HableCurve.whitePoint, 0f, 0f));
CheckCurveRT((int)m_CurveRect.width, (int)m_CurveRect.height);
var oldRt = RenderTexture.active;
Graphics.Blit(null, m_CurveTex, m_CurveMaterial, EditorGUIUtility.isProSkin ? 0 : 1);
RenderTexture.active = oldRt;
GUI.DrawTexture(m_CurveRect, m_CurveTex);
Handles.DrawSolidRectangleWithOutline(m_CurveRect, Color.clear, Color.white * 0.4f);
}
PropertyField(m_CharToeStrength, new GUIContent("Toe Strength"));
PropertyField(m_CharToeLength, new GUIContent("Toe Length"));
PropertyField(m_CharShoulderStrength, new GUIContent("Shoulder Strength"));
PropertyField(m_CharShoulderLength, new GUIContent("Shoulder Length"));
PropertyField(m_CharShoulderAngle, new GUIContent("Shoulder Angle"));
PropertyField(m_CharGamma, new GUIContent("Gamma"));
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space(1f);
EditorGUILayout.BeginVertical(subGroupBoxStyle);
EditorGUILayout.LabelField("Color Adjustments", postProcessLabelStyle);
PropertyField(m_CharPostExposure, new GUIContent("Post Brightness"));
PropertyField(m_CharGammaAdjust, new GUIContent("Gamma Adjust"));
PropertyField(m_CharContrast, new GUIContent("Contrast"));
PropertyField(m_CharColorFilter, new GUIContent("Color Filter"));
PropertyField(m_CharHueShift, new GUIContent("Hue Shift"));
PropertyField(m_CharSaturation, new GUIContent("Saturation"));
EditorGUILayout.EndVertical();
EditorGUILayout.Space(1f);
EditorGUILayout.BeginVertical(subGroupBoxStyle);
EditorGUILayout.LabelField("White Balance", postProcessLabelStyle);
PropertyField(m_CharWhiteBalanceTemperature, new GUIContent("Temperature"));
PropertyField(m_CharWhiteBalanceTint, new GUIContent("Tint"));
EditorGUILayout.EndVertical();
}
s_CharScreenRimFoldout = EditorGUILayout.Foldout(s_CharScreenRimFoldout, "[Screen Rim]", true, EditorStyles.foldoutHeader);
if (s_CharScreenRimFoldout)
{
EditorGUILayout.BeginVertical(subGroupBoxStyle);
PropertyField(m_ScreenRimWidth);
PropertyField(m_ScreenRimColor);
EditorGUILayout.EndVertical();
}
s_CharBloomFoldout = EditorGUILayout.Foldout(s_CharBloomFoldout, "[Bloom]", true, EditorStyles.foldoutHeader);
if (s_CharBloomFoldout)
{
EditorGUILayout.BeginVertical(subGroupBoxStyle);
PropertyField(m_CharBloom);
if (m_CharBloom.value.boolValue)
{
EditorGUI.indentLevel++;
PropertyField(m_CharBloomThreshold);
PropertyField(m_CharBloomIntensity);
PropertyField(m_CharBloomScatter);
PropertyField(m_CharBloomClamp);
PropertyField(m_CharBloomTint);
PropertyField(m_CharBloomHQ);
PropertyField(m_CharBloomDownscale);
PropertyField(m_CharBloomMaxIterations);
PropertyField(m_CharBloomDirtTexture);
PropertyField(m_CharBloomDirtIntensity);
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
}
EditorGUI.indentLevel--;
}
PropertyField(m_EnvPostProcess);
if (m_EnvPostProcess.value.boolValue)
{
EditorGUI.indentLevel++;
s_EnvColorGradingFoldout = EditorGUILayout.Foldout(s_EnvColorGradingFoldout, "[Color Grading]", true, EditorStyles.foldoutHeader);
if (s_EnvColorGradingFoldout)
{
EditorGUILayout.BeginVertical(subGroupBoxStyle);
EditorGUILayout.LabelField("Tone Mapping", postProcessLabelStyle);
PropertyField(m_EnvToneMapping, new GUIContent("Mode"));
if (m_EnvToneMapping.value.intValue == (int)PotaToonToneMapping.Custom)
{
EditorGUI.indentLevel++;
EditorGUILayout.Space();
// Reserve GUI space
m_CurveRect = GUILayoutUtility.GetRect(128, 80);
m_CurveRect.xMin += EditorGUI.indentLevel * 15f;
if (Event.current.type == EventType.Repaint)
{
// Prepare curve data
float toeStrength = m_EnvToeStrength.value.floatValue;
float toeLength = m_EnvToeLength.value.floatValue;
float shoulderStrength = m_EnvShoulderStrength.value.floatValue;
float shoulderLength = m_EnvShoulderLength.value.floatValue;
float shoulderAngle = m_EnvShoulderAngle.value.floatValue;
float gamma = m_EnvGamma.value.floatValue;
m_HableCurve.Init(
toeStrength,
toeLength,
shoulderStrength,
shoulderLength,
shoulderAngle,
gamma
);
float alpha = GUI.enabled ? 1f : 0.5f;
m_CurveMaterial.SetVector(ShaderIDs._CustomToneCurve, m_HableCurve.uniforms.curve);
m_CurveMaterial.SetVector(ShaderIDs._ToeSegmentA, m_HableCurve.uniforms.toeSegmentA);
m_CurveMaterial.SetVector(ShaderIDs._ToeSegmentB, m_HableCurve.uniforms.toeSegmentB);
m_CurveMaterial.SetVector(ShaderIDs._MidSegmentA, m_HableCurve.uniforms.midSegmentA);
m_CurveMaterial.SetVector(ShaderIDs._MidSegmentB, m_HableCurve.uniforms.midSegmentB);
m_CurveMaterial.SetVector(ShaderIDs._ShoSegmentA, m_HableCurve.uniforms.shoSegmentA);
m_CurveMaterial.SetVector(ShaderIDs._ShoSegmentB, m_HableCurve.uniforms.shoSegmentB);
m_CurveMaterial.SetVector("_Variants", new Vector4(alpha, m_HableCurve.whitePoint, 0f, 0f));
CheckCurveRT((int)m_CurveRect.width, (int)m_CurveRect.height);
var oldRt = RenderTexture.active;
Graphics.Blit(null, m_CurveTex, m_CurveMaterial, EditorGUIUtility.isProSkin ? 0 : 1);
RenderTexture.active = oldRt;
GUI.DrawTexture(m_CurveRect, m_CurveTex);
Handles.DrawSolidRectangleWithOutline(m_CurveRect, Color.clear, Color.white * 0.4f);
}
PropertyField(m_EnvToeStrength, new GUIContent("Toe Strength"));
PropertyField(m_EnvToeLength, new GUIContent("Toe Length"));
PropertyField(m_EnvShoulderStrength, new GUIContent("Shoulder Strength"));
PropertyField(m_EnvShoulderLength, new GUIContent("Shoulder Length"));
PropertyField(m_EnvShoulderAngle, new GUIContent("Shoulder Angle"));
PropertyField(m_EnvGamma, new GUIContent("Gamma"));
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space(1f);
EditorGUILayout.BeginVertical(subGroupBoxStyle);
EditorGUILayout.LabelField("Color Adjustments", postProcessLabelStyle);
PropertyField(m_EnvPostExposure, new GUIContent("Post Brightness"));
PropertyField(m_EnvContrast, new GUIContent("Contrast"));
PropertyField(m_EnvColorFilter, new GUIContent("Color Filter"));
PropertyField(m_EnvHueShift, new GUIContent("Hue Shift"));
PropertyField(m_EnvSaturation, new GUIContent("Saturation"));
EditorGUILayout.EndVertical();
EditorGUILayout.Space(1f);
EditorGUILayout.BeginVertical(subGroupBoxStyle);
EditorGUILayout.LabelField("White Balance", postProcessLabelStyle);
PropertyField(m_EnvWhiteBalanceTemperature, new GUIContent("Temperature"));
PropertyField(m_EnvWhiteBalanceTint, new GUIContent("Tint"));
EditorGUILayout.EndVertical();
}
s_EnvBloomFoldout = EditorGUILayout.Foldout(s_EnvBloomFoldout, "[Bloom]", true, EditorStyles.foldoutHeader);
if (s_EnvBloomFoldout)
{
EditorGUILayout.BeginVertical(subGroupBoxStyle);
PropertyField(m_EnvBloom);
if (m_EnvBloom.value.boolValue)
{
EditorGUI.indentLevel++;
PropertyField(m_EnvBloomThreshold, new GUIContent("Threshold"));
PropertyField(m_EnvBloomIntensity, new GUIContent("Intensity"));
PropertyField(m_EnvBloomScatter, new GUIContent("Scatter"));
PropertyField(m_EnvBloomClamp, new GUIContent("Clamp"));
PropertyField(m_EnvBloomTint, new GUIContent("Tint"));
PropertyField(m_EnvBloomHQ, new GUIContent("High Quality Filtering"));
PropertyField(m_EnvBloomDownscale, new GUIContent("DownScale"));
PropertyField(m_EnvBloomMaxIterations, new GUIContent("Max Iterations"));
PropertyField(m_EnvBloomDirtTexture, new GUIContent("Dirt Texture"));
PropertyField(m_EnvBloomDirtIntensity, new GUIContent("Dirt Intensity"));
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
}
EditorGUI.indentLevel--;
}
// Advanced Settings
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
EditorGUILayout.LabelField("ADVANCED SETTINGS", EditorStyles.boldLabel);
EditorGUILayout.LabelField("[PLEASE READ THE TOOLTIP]");
var originalAdvancedSettingsUnlocked = PotaToonGUIUtility.advancedSettingsUnlocked;
PotaToonGUIUtility.DrawAdvancedSettingsButton();
if (PotaToonGUIUtility.advancedSettingsUnlocked)
{
s_ShadowSettingsFoldout = EditorGUILayout.BeginFoldoutHeaderGroup(s_ShadowSettingsFoldout, "Global Char Shadow Settings");
if (s_ShadowSettingsFoldout)
{
PropertyField(m_Bias, new GUIContent("Depth Bias"));
PropertyField(m_NormalBias);
}
EditorGUILayout.EndFoldoutHeaderGroup();
EditorGUI.indentLevel++;
EditorGUILayout.LabelField("OIT is only available in Windows. (DX 11/12)");
EditorGUI.indentLevel--;
PropertyField(m_OIT, new GUIContent("OIT"));
PropertyField(m_OITMode, new GUIContent("OIT Mode"));
}
GUI.backgroundColor = originalColor;
if (PotaToonGUIUtility.advancedSettingsUnlocked != originalAdvancedSettingsUnlocked)
PotaToonGUIUtility.SaveAdvancedSettingUnlocked();
}
private void CopyComponent()
{
var comp = target as VolumeComponent;
if (comp == null)
return;
s_CopyBuffer = ScriptableObject.CreateInstance(comp.GetType()) as VolumeComponent;
EditorUtility.CopySerialized(comp, s_CopyBuffer);
PotaToonGUIUtility.ShowNotification("Copied!");
}
private void PasteComponent()
{
var comp = target as VolumeComponent;
if (comp == null || s_CopyBuffer == null)
return;
Undo.RecordObject(comp, "Paste VolumeComponent Settings");
EditorUtility.CopySerialized(s_CopyBuffer, comp);
EditorUtility.SetDirty(comp);
PotaToonGUIUtility.ShowNotification("Pasted!");
}
private void CheckCurveRT(int width, int height)
{
if (m_CurveTex == null || !m_CurveTex.IsCreated() || m_CurveTex.width != width || m_CurveTex.height != height)
{
CoreUtils.Destroy(m_CurveTex);
m_CurveTex = new RenderTexture(width, height, 0, GraphicsFormat.R8G8B8A8_SRGB);
m_CurveTex.hideFlags = HideFlags.HideAndDontSave;
}
}
#region Preset
private void DrawPresetField()
{
if (!s_PresetFoldout)
return;
const float scrollHeight = 50f;
const float itemWidth = 70f;
const float itemHeight = 30f;
GUIStyle presetHeaderStyle = new GUIStyle(EditorStyles.boldLabel)
{
alignment = TextAnchor.MiddleCenter,
};
GUIStyle itemStyle = new GUIStyle(GUI.skin.button)
{
fontSize = 10,
alignment = TextAnchor.MiddleCenter,
wordWrap = true
};
GUIStyle boxStyle = new GUIStyle(GUI.skin.box)
{
padding = new RectOffset(10, 10, 5, 5),
margin = new RectOffset(5, 5, 5, 5)
};
// Header
EditorGUILayout.BeginVertical(GUI.skin.box);
EditorGUILayout.LabelField("[ PRESET ]", presetHeaderStyle);
EditorGUILayout.HelpBox("Right-click to edit preset.", MessageType.Info);
var originalBackgroundColor = GUI.backgroundColor;
var evt = Event.current;
EditorGUILayout.BeginVertical(boxStyle);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Plus", "|Create preset"), GUILayout.Height(20f)))
{
if (CreateAndAddPreset() != null)
{
evt.Use();
PopupWindow.Show(new Rect(30f, 200f, 0, 0),
new VolumePresetContextMenu(m_Presets, m_Presets.Count - 1, target as PotaToon,
() =>
{
m_SelectedIndex = Mathf.Clamp(m_SelectedIndex, 0, m_Presets.Count - 1);
}));
}
}
if (GUILayout.Button(EditorGUIUtility.IconContent("Import", "|Import preset"), GUILayout.Height(20f)))
{
ImportPreset();
GUIUtility.ExitGUI();
}
EditorGUILayout.EndHorizontal();
// Items
HandleKeyboardNavigation(itemWidth);
m_ScrollPos = EditorGUILayout.BeginScrollView( m_ScrollPos, true, false,
GUI.skin.horizontalScrollbar, GUIStyle.none, GUI.skin.box,
GUILayout.Height(scrollHeight), GUILayout.ExpandWidth(true));
EditorGUILayout.BeginHorizontal();
for (int i = 0; i < m_Presets.Count; i++)
{
GUI.backgroundColor = i == m_SelectedIndex ? m_SelectedPresetColor : originalBackgroundColor;
var assetPath = AssetDatabase.GetAssetPath(m_Presets[i]);
var fileName = Path.GetFileNameWithoutExtension(assetPath);
if (GUILayout.Button(fileName, itemStyle, GUILayout.Width(itemWidth), GUILayout.Height(itemHeight)))
{
if (evt.button == 0)
{
m_SelectedIndex = i;
ApplyPreset(i);
}
else if (evt.button == 1)
{
evt.Use();
PopupWindow.Show(new Rect(0, 0, 0, 0),
new VolumePresetContextMenu(m_Presets, i, target as PotaToon,
() =>
{
m_SelectedIndex = Mathf.Clamp(m_SelectedIndex, 0, m_Presets.Count - 1);
}));
}
}
}
GUI.backgroundColor = originalBackgroundColor;
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
EditorGUILayout.EndVertical();
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
}
private void HandleKeyboardNavigation(float itemWidth)
{
if (EditorGUIUtility.editingTextField)
return;
Event e = Event.current;
if (e.type == EventType.KeyDown)
{
if (e.keyCode == KeyCode.RightArrow || e.keyCode == KeyCode.DownArrow)
{
m_SelectedIndex = Mathf.Min(m_SelectedIndex + 1, m_Presets.Count - 1);
m_ScrollPos.x = m_SelectedIndex * itemWidth;
Repaint();
e.Use();
ApplyPreset(m_SelectedIndex);
}
else if (e.keyCode == KeyCode.LeftArrow || e.keyCode == KeyCode.UpArrow)
{
m_SelectedIndex = Mathf.Max(m_SelectedIndex - 1, 0);
m_ScrollPos.x = m_SelectedIndex * itemWidth;
Repaint();
e.Use();
ApplyPreset(m_SelectedIndex);
}
}
}
private static MonoScript FindMonoScriptFor<T>()
{
var guids = AssetDatabase.FindAssets($"t:MonoScript {typeof(T).Name}");
for (int i = 0; i < guids.Length; ++i)
{
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
var ms = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
if (ms != null && ms.GetClass() == typeof(T))
return ms;
}
return null;
}
private void InitializePresetsAndIcons()
{
#if UNITY_2021_3
var monoScript = FindMonoScriptFor<PotaToonEditor>();
#else
var monoScript = MonoScript.FromScriptableObject(this);
#endif
var scriptPath = AssetDatabase.GetAssetPath(monoScript);
var editorDir = Path.GetDirectoryName(scriptPath).Replace("\\Scripts", "/");
var volumeDir = $"{editorDir}/Presets/Volume";
m_Presets = new List<PotaToonVolumePreset>();
m_SelectedIndex = -1;
m_PresetIcon = AssetDatabase.LoadAssetAtPath<Texture2D>($"{editorDir}/Textures/potatoon_icon.png");
if (AssetDatabase.IsValidFolder(volumeDir))
{
var guids = AssetDatabase.FindAssets("t:PotaToonVolumePreset", new[] { volumeDir });
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var preset = AssetDatabase.LoadAssetAtPath<PotaToonVolumePreset>(path);
if (preset != null)
m_Presets.Add(preset);
}
}
}
private void ApplyPreset(int index)
{
var component = target as PotaToon;
if (component != null)
{
Undo.RecordObject(component, "Apply PotaToonVolumePreset");
m_Presets[index].ApplyTo(component);
EditorUtility.SetDirty(component);
AssetDatabase.SaveAssets();
PotaToonGUIUtility.ShowNotification($"Applied preset: [{m_Presets[index].name}]");
}
}
private PotaToonVolumePreset CreateAndAddPreset()
{
#if UNITY_2021_3
var monoScript = FindMonoScriptFor<PotaToonEditor>();
#else
var monoScript = MonoScript.FromScriptableObject(this);
#endif
var scriptPath = AssetDatabase.GetAssetPath(monoScript);
var parentDir = Path.GetDirectoryName(scriptPath).Replace("\\Scripts", "/");
var presetsBase = $"{parentDir}/Presets";
var volumeBase = $"{presetsBase}/Volume";
var presetsDir = $"{parentDir}/Presets/Volume";
if (!AssetDatabase.IsValidFolder(presetsBase))
AssetDatabase.CreateFolder(parentDir, "Presets");
if (!AssetDatabase.IsValidFolder(volumeBase))
AssetDatabase.CreateFolder(presetsBase, "Volume");
var assetPath = AssetDatabase.GenerateUniqueAssetPath($"{presetsDir}/New Preset.asset");
var newPreset = ScriptableObject.CreateInstance<PotaToonVolumePreset>();
AssetDatabase.CreateAsset(newPreset, assetPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
m_Presets.Add(newPreset);
m_SelectedIndex = m_Presets.Count - 1;
m_ScrollPos.x = m_SelectedIndex * 20f;
return newPreset;
}
private void ImportPreset()
{
var absPath = EditorUtility.OpenFilePanel("Import PotaToonVolumePreset", "", "asset");
if (string.IsNullOrEmpty(absPath))
return;
#if UNITY_2021_3
var monoScript = FindMonoScriptFor<PotaToonEditor>();
#else
var monoScript = MonoScript.FromScriptableObject(this);
#endif
var scriptPath = AssetDatabase.GetAssetPath(monoScript);
var editorDir = Path.GetDirectoryName(scriptPath).Replace("\\Scripts", "/");
var presetsBase = $"{editorDir}/Presets";
var volumeDir = $"{presetsBase}/Volume";
if (!AssetDatabase.IsValidFolder(presetsBase))
AssetDatabase.CreateFolder(editorDir, "Presets");
if (!AssetDatabase.IsValidFolder(volumeDir))
AssetDatabase.CreateFolder(presetsBase, "Volume");
var fileName = Path.GetFileName(absPath);
var destPath = AssetDatabase.GenerateUniqueAssetPath($"{volumeDir}/{fileName}");
File.Copy(absPath, destPath, overwrite: false);
AssetDatabase.ImportAsset(destPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
var imported = AssetDatabase.LoadAssetAtPath<PotaToonVolumePreset>(destPath);
if (imported == null)
{
EditorUtility.DisplayDialog("Invalid Preset",
"The selected file is not a PotaToonVolumePreset asset.", "OK");
AssetDatabase.DeleteAsset(destPath);
AssetDatabase.SaveAssets();
return;
}
PotaToonGUIUtility.ShowNotification($"Imported {imported.name}.");
m_Presets.Add(imported);
}
internal class VolumePresetContextMenu : PopupWindowContent
{
private readonly Action m_OnDelete;
private List<PotaToonVolumePreset> m_Presets;
private string m_TempName;
private int m_Index;
private PotaToon m_Target;
public VolumePresetContextMenu(List<PotaToonVolumePreset> presets, int idx, PotaToon volume, Action onDelete)
{
m_Presets = presets;
m_TempName = m_Presets[idx].name;
m_Index = idx;
m_Target = volume;
m_OnDelete = onDelete;
}
public override Vector2 GetWindowSize() => new Vector2(200, 205);
public override void OnGUI(Rect rect)
{
var preset = m_Presets[m_Index];
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Edit Preset", EditorStyles.boldLabel, GUILayout.Width(100f), GUILayout.Height(20f));
GUILayout.FlexibleSpace();
if (GUILayout.Button("X", GUILayout.Width(20f)))
{
editorWindow.Close();
}
EditorGUILayout.EndHorizontal();
var textfieldStyle = new GUIStyle(GUI.skin.textField)
{
alignment = TextAnchor.MiddleLeft,
fontSize = 13
};
m_TempName = GUILayout.TextField(m_TempName, textfieldStyle, GUILayout.Height(30f));
if (GUILayout.Button("Rename", GUILayout.Height(30f)))
{
if (!preset.name.Equals(m_TempName, StringComparison.Ordinal))
{
var oldPath = AssetDatabase.GetAssetPath(preset);
var newNameNoExt = Path.GetFileNameWithoutExtension(m_TempName);
AssetDatabase.RenameAsset(oldPath, newNameNoExt);
AssetDatabase.SaveAssets();
PotaToonGUIUtility.ShowNotification("Renamed.");
}
}
GUILayout.FlexibleSpace();
if (GUILayout.Button("Find Preset in Project", GUILayout.Height(25f)))
{
EditorUtility.FocusProjectWindow();
EditorGUIUtility.PingObject(preset);
}
if (GUILayout.Button("Export Preset", GUILayout.Height(25f)))
{
ExportPreset(m_Index);
}
if (GUILayout.Button("Save (Override)", GUILayout.Height(30f)))
{
SavePreset(m_Index);
}
if (GUILayout.Button("Delete", GUILayout.Height(30f)))
{
if (RemovePreset(m_Index))
{
m_OnDelete();
}
editorWindow.Close();
}
}
private bool RemovePreset(int index)
{
var preset = m_Presets[index];
var path = AssetDatabase.GetAssetPath(preset);
if (!EditorUtility.DisplayDialog("Remove Preset", $"Are you sure you want to remove preset '{preset.name}'? This operation can't be undone.", "Remove", "Cancel"))
return false;
AssetDatabase.DeleteAsset(path);
AssetDatabase.SaveAssets();
m_Presets.RemoveAt(index);
return true;
}
private void SavePreset(int index)
{
if (m_Target == null)
return;
var preset = m_Presets[index];
// Rename if needed
if (!preset.name.Equals(m_TempName, StringComparison.Ordinal))
{
var oldPath = AssetDatabase.GetAssetPath(preset);
var newNameNoExt = Path.GetFileNameWithoutExtension(m_TempName);
AssetDatabase.RenameAsset(oldPath, newNameNoExt);
}
preset.SaveFrom(m_Target);
Undo.RecordObject(preset, "Save PotaToonVolumePreset");
EditorUtility.SetDirty(preset);
AssetDatabase.SaveAssets();
PotaToonGUIUtility.ShowNotification("Saved.");
}
private void ExportPreset(int index)
{
// Get source asset path
var preset = m_Presets[index];
var sourcePath = AssetDatabase.GetAssetPath(preset);
if (string.IsNullOrEmpty(sourcePath))
{
EditorUtility.DisplayDialog("Export Failed", "Could not find the preset asset path.", "OK");
return;
}
// Ask user for target save path (anywhere)
var defaultName = preset.name + ".asset";
var absTarget = EditorUtility.SaveFilePanel(
"Export Volume Preset",
"", // default folder
defaultName,
"asset"
);
if (string.IsNullOrEmpty(absTarget))
return;
// Convert source to absolute path
var absSource = Path.GetFullPath(sourcePath).Replace("\\", "/");
// Copy file
try
{
// Notify and refresh
System.IO.File.Copy(absSource, absTarget, overwrite: true);
EditorUtility.RevealInFinder(absTarget);
var win = EditorWindow.focusedWindow;
if (win != null)
win.ShowNotification(new GUIContent("Preset exported!"));
}
catch (System.Exception ex)
{
PotaToonEditorUtility.PotaToonLog($"Error exporting preset: {ex.Message}", true);
EditorUtility.DisplayDialog("Export Failed", ex.Message, "OK");
}
}
}
#endregion
}
}