2025-06-08 00:39:11 +09:00

403 lines
21 KiB
C#

using UnityEngine;
using UnityEditor;
using System.IO;
using UnityEngine.Rendering;
using System.Reflection;
using UnityEditor.IMGUI.Controls;
namespace VolumetricFogAndMist2 {
[CustomEditor(typeof(VolumetricFog))]
public partial class VolumetricFogEditor : Editor {
VolumetricFogProfile cachedProfile;
Editor cachedProfileEditor;
SerializedProperty profile;
SerializedProperty maskEditorEnabled, maskBrushMode, maskBrushColor, maskBrushWidth, maskBrushFuzziness, maskBrushOpacity;
SerializedProperty enablePointLights, enableNativeLights, nativeLightsMultiplier, enableAPV, apvIntensityMultiplier;
SerializedProperty enableVoids;
SerializedProperty enableFogOfWar, fogOfWarCenter, fogOfWarIsLocal, fogOfWarSize, fogOfWarShowCoverage, fogOfWarTextureWidth, fogOfWarTextureHeight, fogOfWarRestoreDelay, fogOfWarRestoreDuration, fogOfWarSmoothness, fogOfWarBlur;
SerializedProperty enableFollow, followTarget, followMode, followOffset, followIncludeDistantFog;
SerializedProperty enableFade, fadeDistance, fadeOut, fadeController, enableSubVolumes, subVolumes;
SerializedProperty enableUpdateModeOptions, updateMode, updateModeCamera, updateModeBounds;
SerializedProperty showBoundary;
static GUIStyle boxStyle;
VolumetricFog fog;
public static VolumetricFog lastEditingFog;
void OnEnable () {
profile = serializedObject.FindProperty("profile");
enablePointLights = serializedObject.FindProperty("enablePointLights");
enableNativeLights = serializedObject.FindProperty("enableNativeLights");
nativeLightsMultiplier = serializedObject.FindProperty("nativeLightsMultiplier");
enableAPV = serializedObject.FindProperty("enableAPV");
apvIntensityMultiplier = serializedObject.FindProperty("apvIntensityMultiplier");
enableVoids = serializedObject.FindProperty("enableVoids");
enableFogOfWar = serializedObject.FindProperty("enableFogOfWar");
fogOfWarCenter = serializedObject.FindProperty("fogOfWarCenter");
fogOfWarIsLocal = serializedObject.FindProperty("fogOfWarIsLocal");
fogOfWarSize = serializedObject.FindProperty("fogOfWarSize");
fogOfWarShowCoverage = serializedObject.FindProperty("fogOfWarShowCoverage");
fogOfWarTextureWidth = serializedObject.FindProperty("fogOfWarTextureWidth");
fogOfWarTextureHeight = serializedObject.FindProperty("fogOfWarTextureHeight");
fogOfWarRestoreDelay = serializedObject.FindProperty("fogOfWarRestoreDelay");
fogOfWarRestoreDuration = serializedObject.FindProperty("fogOfWarRestoreDuration");
fogOfWarSmoothness = serializedObject.FindProperty("fogOfWarSmoothness");
fogOfWarBlur = serializedObject.FindProperty("fogOfWarBlur");
maskEditorEnabled = serializedObject.FindProperty("maskEditorEnabled");
maskBrushColor = serializedObject.FindProperty("maskBrushColor");
maskBrushMode = serializedObject.FindProperty("maskBrushMode");
maskBrushWidth = serializedObject.FindProperty("maskBrushWidth");
maskBrushFuzziness = serializedObject.FindProperty("maskBrushFuzziness");
maskBrushOpacity = serializedObject.FindProperty("maskBrushOpacity");
enableFollow = serializedObject.FindProperty("enableFollow");
followTarget = serializedObject.FindProperty("followTarget");
followMode = serializedObject.FindProperty("followMode");
followOffset = serializedObject.FindProperty("followOffset");
followIncludeDistantFog = serializedObject.FindProperty("followIncludeDistantFog");
enableFade = serializedObject.FindProperty("enableFade");
fadeDistance = serializedObject.FindProperty("fadeDistance");
fadeOut = serializedObject.FindProperty("fadeOut");
fadeController = serializedObject.FindProperty("fadeController");
enableSubVolumes = serializedObject.FindProperty("enableSubVolumes");
subVolumes = serializedObject.FindProperty("subVolumes");
enableUpdateModeOptions = serializedObject.FindProperty("enableUpdateModeOptions");
updateMode = serializedObject.FindProperty("updateMode");
updateModeCamera = serializedObject.FindProperty("updateModeCamera");
updateModeBounds = serializedObject.FindProperty("updateModeBounds");
showBoundary = serializedObject.FindProperty("showBoundary");
fog = (VolumetricFog)target;
lastEditingFog = fog;
}
public override void OnInspectorGUI () {
var pipe = GraphicsSettings.currentRenderPipeline as UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset;
if (pipe == null) {
EditorGUILayout.HelpBox("Universal Rendering Pipeline asset is not set in Project Settings / Graphics !", MessageType.Error);
return;
}
if (!pipe.supportsCameraDepthTexture) {
EditorGUILayout.HelpBox("Depth Texture option is required in Universal Rendering Pipeline asset!", MessageType.Error);
if (GUILayout.Button("Go to Universal Rendering Pipeline Asset")) {
Selection.activeObject = pipe;
}
EditorGUILayout.Separator();
GUI.enabled = false;
}
// Check depth texture mode
FieldInfo renderers = pipe.GetType().GetField("m_RendererDataList", BindingFlags.NonPublic | BindingFlags.Instance);
if (renderers == null) return;
foreach (var renderer in (object[])renderers.GetValue(pipe)) {
if (renderer == null) continue;
FieldInfo depthTextureModeField = renderer.GetType().GetField("m_CopyDepthMode", BindingFlags.NonPublic | BindingFlags.Instance);
if (depthTextureModeField != null) {
int depthTextureMode = (int)depthTextureModeField.GetValue(renderer);
if (depthTextureMode == 1) { // transparent copy depth mode
EditorGUILayout.HelpBox("Depth Texture Mode in URP asset must be set to 'After Opaques' or 'Force Prepass'.", MessageType.Warning);
if (GUILayout.Button("Show Pipeline Asset")) {
Selection.activeObject = (Object)renderer;
GUIUtility.ExitGUI();
}
EditorGUILayout.Separator();
}
}
}
if (boxStyle == null) {
boxStyle = new GUIStyle(GUI.skin.box);
boxStyle.padding = new RectOffset(15, 10, 5, 5);
}
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(profile);
if (profile.objectReferenceValue != null) {
if (cachedProfile != profile.objectReferenceValue) {
cachedProfile = null;
}
if (cachedProfile == null) {
cachedProfile = (VolumetricFogProfile)profile.objectReferenceValue;
cachedProfileEditor = CreateEditor(profile.objectReferenceValue);
}
// Drawing the profile editor
EditorGUILayout.BeginVertical(boxStyle);
cachedProfileEditor.OnInspectorGUI();
EditorGUILayout.EndVertical();
} else {
EditorGUILayout.HelpBox("Create or assign a fog profile.", MessageType.Info);
if (GUILayout.Button("New Fog Profile")) {
CreateFogProfile();
}
}
if (EditorGUI.EndChangeCheck()) {
if (!Application.isPlaying) {
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene());
}
}
EditorGUIUtility.labelWidth = 170;
EditorGUILayout.PropertyField(enableNativeLights);
if (enableNativeLights.boolValue) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(nativeLightsMultiplier, new GUIContent("Intensity Multiplier"));
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(enableAPV, new GUIContent("Enable APV (Probe Volumes)"));
if (enableAPV.boolValue) {
EditorGUI.indentLevel++;
#if !UNITY_2023_1_OR_NEWER
EditorGUILayout.HelpBox("Adaptative Probe Volumes are only available in Unity 2023.", MessageType.Warning);
#endif
EditorGUILayout.PropertyField(apvIntensityMultiplier, new GUIContent("Intensity Multiplier"));
EditorGUI.indentLevel--;
}
GUI.enabled = !enableNativeLights.boolValue;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(enablePointLights);
if (GUILayout.Button("Point Light Manager", GUILayout.Width(180))) {
Selection.activeGameObject = VolumetricFogManager.pointLightManager.gameObject;
}
EditorGUILayout.EndHorizontal();
GUI.enabled = true;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(enableVoids);
if (GUILayout.Button("Void Manager", GUILayout.Width(180))) {
Selection.activeGameObject = VolumetricFogManager.fogVoidManager.gameObject;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(enableFollow);
if (enableFollow.boolValue) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(followTarget, new GUIContent("Target"));
EditorGUILayout.PropertyField(followMode, new GUIContent("Mode"));
if ((VolumetricFogFollowMode)followMode.intValue == VolumetricFogFollowMode.FullXYZ) {
EditorGUILayout.PropertyField(followIncludeDistantFog, new GUIContent("Include Distant Fog", "Also adjusts distant fog base altitude to the followed object altitude."));
}
EditorGUILayout.PropertyField(followOffset, new GUIContent("Offset"));
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(enableFade);
if (enableFade.boolValue) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(fadeDistance);
EditorGUILayout.PropertyField(fadeOut);
EditorGUILayout.PropertyField(fadeController);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(enableSubVolumes);
if (enableSubVolumes.boolValue) {
EditorGUI.indentLevel++;
EditorGUILayout.HelpBox("If no sub-volumes are specified below, any sub-volume in the scene will be used.", MessageType.Info);
EditorGUILayout.PropertyField(fadeController, new GUIContent("Character Controller"));
EditorGUILayout.PropertyField(subVolumes);
EditorGUI.indentLevel--;
}
bool requiresFogOfWarTextureReload = false;
EditorGUILayout.PropertyField(enableFogOfWar);
if (enableFogOfWar.boolValue) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(fogOfWarCenter, new GUIContent("World Center"));
EditorGUILayout.PropertyField(fogOfWarIsLocal, new GUIContent("Is Local", "Enable if fog of war center is local to the fog volume"));
EditorGUILayout.PropertyField(fogOfWarSize, new GUIContent("World Coverage"));
EditorGUILayout.PropertyField(fogOfWarShowCoverage, new GUIContent("Show Coverage Bounds"));
EditorGUILayout.PropertyField(fogOfWarTextureWidth, new GUIContent("Texture Width"));
EditorGUILayout.PropertyField(fogOfWarTextureHeight, new GUIContent("Texture Height"));
EditorGUILayout.PropertyField(fogOfWarRestoreDelay, new GUIContent("Restore Delay"));
EditorGUILayout.PropertyField(fogOfWarRestoreDuration, new GUIContent("Restore Duration"));
EditorGUILayout.PropertyField(fogOfWarSmoothness, new GUIContent("Border Smoothness"));
EditorGUILayout.PropertyField(fogOfWarBlur, new GUIContent("Blur"));
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(maskEditorEnabled, new GUIContent("Fog Of War Editor", "Activates terrain brush to paint/remove fog of war at custom locations."));
if (maskEditorEnabled.boolValue) {
if (GUILayout.Button("Create New Mask Texture")) {
if (EditorUtility.DisplayDialog("Create Mask Texture", "A texture asset will be created with the size specified in current profile (" + fog.fogOfWarTextureWidth + "x" + fog.fogOfWarTextureHeight + ").\n\nContinue?", "Ok", "Cancel")) {
CreateNewMaskTexture();
GUIUtility.ExitGUI();
}
}
EditorGUI.BeginChangeCheck();
fog.fogOfWarTexture = (Texture2D)EditorGUILayout.ObjectField(new GUIContent("Coverage Texture", "Fog of war coverage mask. A value of alpha of zero means no fog."), fog.fogOfWarTexture, typeof(Texture2D), false);
Texture2D tex = fog.fogOfWarTexture;
if (EditorGUI.EndChangeCheck()) {
requiresFogOfWarTextureReload = true;
if (tex != null) {
string assetPath = AssetDatabase.GetAssetPath(tex);
if (!string.IsNullOrEmpty(assetPath)) {
TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
if (importer != null) {
bool settingsChanged = false;
if (!importer.isReadable) {
importer.isReadable = true;
settingsChanged = true;
}
if (importer.textureCompression != TextureImporterCompression.Uncompressed) {
importer.textureCompression = TextureImporterCompression.Uncompressed;
settingsChanged = true;
}
if (settingsChanged) {
importer.SaveAndReimport();
GUIUtility.ExitGUI();
}
}
}
}
}
if (tex != null) {
EditorGUILayout.LabelField(" Texture Width", tex.width.ToString());
EditorGUILayout.LabelField(" Texture Height", tex.height.ToString());
string path = AssetDatabase.GetAssetPath(tex);
if (string.IsNullOrEmpty(path)) {
path = "(Temporary texture)";
}
EditorGUILayout.LabelField(" Texture Path", path);
}
if (tex != null) {
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(GUI.skin.box);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(maskBrushMode, new GUIContent("Brush Mode", "Select brush operation mode."));
if (GUILayout.Button("Toggle", GUILayout.Width(70))) {
maskBrushMode.intValue = maskBrushMode.intValue == 0 ? 1 : 0;
}
EditorGUILayout.EndHorizontal();
if (maskBrushMode.intValue == (int)MaskTextureBrushMode.ColorFog) {
EditorGUILayout.PropertyField(maskBrushColor, new GUIContent(" Color", "Brush color."));
}
EditorGUILayout.PropertyField(maskBrushWidth, new GUIContent(" Width", "Width of the snow editor brush."));
EditorGUILayout.PropertyField(maskBrushFuzziness, new GUIContent(" Fuzziness", "Solid vs spray brush."));
EditorGUILayout.PropertyField(maskBrushOpacity, new GUIContent(" Opacity", "Stroke opacity."));
EditorGUILayout.BeginHorizontal();
if (tex == null) GUI.enabled = false;
if (GUILayout.Button("Fill Mask")) {
fog.ResetFogOfWar(1f);
maskBrushMode.intValue = (int)MaskTextureBrushMode.RemoveFog;
}
if (GUILayout.Button("Clear Mask")) {
fog.ResetFogOfWar(0);
maskBrushMode.intValue = (int)MaskTextureBrushMode.AddFog;
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
}
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(enableUpdateModeOptions);
if (enableUpdateModeOptions.boolValue) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(updateMode);
EditorGUILayout.PropertyField(updateModeCamera, new GUIContent("Camera"));
if (updateMode.intValue == (int)VolumetricFogUpdateMode.WhenCameraIsInsideArea) {
EditorGUILayout.PropertyField(updateModeBounds, new GUIContent("Bounds"));
}
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(showBoundary);
EditorGUILayout.Separator();
if (GUILayout.Button("Show Volumetric Fog Manager Settings")) {
Selection.activeObject = VolumetricFogManager.instance;
}
serializedObject.ApplyModifiedProperties();
if (requiresFogOfWarTextureReload) {
fog.ReloadFogOfWarTexture();
}
}
void CreateFogProfile () {
// Find directional light and adjusts brightness to avoid excessive bright fog
float brightness = 1f;
#if UNITY_2023_2_OR_NEWER
Light[] lights = FindObjectsByType<Light>(FindObjectsSortMode.None);
#else
Light[] lights = FindObjectsOfType<Light>();
#endif
if (lights != null) {
foreach (Light light in lights) {
if (light.type == LightType.Directional) {
brightness /= light.intensity;
break;
}
}
}
string path = "Assets";
foreach (Object obj in Selection.GetFiltered(typeof(Object), SelectionMode.Assets)) {
path = AssetDatabase.GetAssetPath(obj);
if (File.Exists(path)) {
path = Path.GetDirectoryName(path);
}
break;
}
VolumetricFogProfile fp = CreateInstance<VolumetricFogProfile>();
fp.name = "New Volumetric Fog Profile";
fp.brightness = brightness;
AssetDatabase.CreateAsset(fp, path + "/" + fp.name + ".asset");
AssetDatabase.SaveAssets();
profile.objectReferenceValue = fp;
EditorGUIUtility.PingObject(fp);
}
void OnSceneGUI () {
OnSceneGUI_FogOfWar();
OnSceneGUI_TransformHandle();
}
private readonly BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle();
void OnSceneGUI_TransformHandle () {
if (fog == null) return;
Bounds bounds = fog.GetBounds();
m_BoundsHandle.center = bounds.center;
m_BoundsHandle.size = bounds.size;
// draw the handle
EditorGUI.BeginChangeCheck();
m_BoundsHandle.DrawHandle();
if (EditorGUI.EndChangeCheck()) {
// record the target object before setting new values so changes can be undone/redone
Undo.RecordObject(fog, "Change Bounds");
// copy the handle's updated data back to the target object
Bounds newBounds = new Bounds();
newBounds.center = m_BoundsHandle.center;
newBounds.size = m_BoundsHandle.size;
fog.SetBounds(newBounds);
}
}
}
}