VolumetricFogEditor.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System.IO;
  4. using UnityEngine.Rendering;
  5. namespace VolumetricFogAndMist2 {
  6. [CustomEditor(typeof(VolumetricFog))]
  7. public partial class VolumetricFogEditor : Editor {
  8. VolumetricFogProfile cachedProfile;
  9. Editor cachedProfileEditor;
  10. SerializedProperty profile;
  11. SerializedProperty maskEditorEnabled, maskBrushMode, maskBrushColor, maskBrushWidth, maskBrushFuzziness, maskBrushOpacity;
  12. SerializedProperty enablePointLights, enableNativeLights;
  13. SerializedProperty enableVoids;
  14. SerializedProperty enableFogOfWar, fogOfWarCenter, fogOfWarIsLocal, fogOfWarSize, fogOfWarShowCoverage, fogOfWarTextureSize, fogOfWarRestoreDelay, fogOfWarRestoreDuration, fogOfWarSmoothness, fogOfWarBlur;
  15. SerializedProperty enableFollow, followTarget, followMode, followOffset;
  16. SerializedProperty enableFade, fadeDistance, fadeOut, fadeController, enableSubVolumes, subVolumes;
  17. SerializedProperty enableUpdateModeOptions, updateMode, updateModeCamera, updateModeBounds;
  18. SerializedProperty showBoundary;
  19. static GUIStyle boxStyle;
  20. VolumetricFog fog;
  21. public static VolumetricFog lastEditingFog;
  22. void OnEnable() {
  23. profile = serializedObject.FindProperty("profile");
  24. enablePointLights = serializedObject.FindProperty("enablePointLights");
  25. enableNativeLights = serializedObject.FindProperty("enableNativeLights");
  26. enableVoids = serializedObject.FindProperty("enableVoids");
  27. enableFogOfWar = serializedObject.FindProperty("enableFogOfWar");
  28. fogOfWarCenter = serializedObject.FindProperty("fogOfWarCenter");
  29. fogOfWarIsLocal = serializedObject.FindProperty("fogOfWarIsLocal");
  30. fogOfWarSize = serializedObject.FindProperty("fogOfWarSize");
  31. fogOfWarShowCoverage = serializedObject.FindProperty("fogOfWarShowCoverage");
  32. fogOfWarTextureSize = serializedObject.FindProperty("fogOfWarTextureSize");
  33. fogOfWarRestoreDelay = serializedObject.FindProperty("fogOfWarRestoreDelay");
  34. fogOfWarRestoreDuration = serializedObject.FindProperty("fogOfWarRestoreDuration");
  35. fogOfWarSmoothness = serializedObject.FindProperty("fogOfWarSmoothness");
  36. fogOfWarBlur = serializedObject.FindProperty("fogOfWarBlur");
  37. maskEditorEnabled = serializedObject.FindProperty("maskEditorEnabled");
  38. maskBrushColor = serializedObject.FindProperty("maskBrushColor");
  39. maskBrushMode = serializedObject.FindProperty("maskBrushMode");
  40. maskBrushWidth = serializedObject.FindProperty("maskBrushWidth");
  41. maskBrushFuzziness = serializedObject.FindProperty("maskBrushFuzziness");
  42. maskBrushOpacity = serializedObject.FindProperty("maskBrushOpacity");
  43. enableFollow = serializedObject.FindProperty("enableFollow");
  44. followTarget = serializedObject.FindProperty("followTarget");
  45. followMode = serializedObject.FindProperty("followMode");
  46. followOffset = serializedObject.FindProperty("followOffset");
  47. enableFade = serializedObject.FindProperty("enableFade");
  48. fadeDistance = serializedObject.FindProperty("fadeDistance");
  49. fadeOut = serializedObject.FindProperty("fadeOut");
  50. fadeController = serializedObject.FindProperty("fadeController");
  51. enableSubVolumes = serializedObject.FindProperty("enableSubVolumes");
  52. subVolumes = serializedObject.FindProperty("subVolumes");
  53. enableUpdateModeOptions = serializedObject.FindProperty("enableUpdateModeOptions");
  54. updateMode = serializedObject.FindProperty("updateMode");
  55. updateModeCamera = serializedObject.FindProperty("updateModeCamera");
  56. updateModeBounds = serializedObject.FindProperty("updateModeBounds");
  57. showBoundary = serializedObject.FindProperty("showBoundary");
  58. fog = (VolumetricFog)target;
  59. lastEditingFog = fog;
  60. }
  61. public override void OnInspectorGUI() {
  62. var pipe = GraphicsSettings.currentRenderPipeline as UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset;
  63. if (pipe == null) {
  64. EditorGUILayout.HelpBox("Universal Rendering Pipeline asset is not set in Project Settings / Graphics !", MessageType.Error);
  65. return;
  66. }
  67. if (!pipe.supportsCameraDepthTexture) {
  68. EditorGUILayout.HelpBox("Depth Texture option is required in Universal Rendering Pipeline asset!", MessageType.Error);
  69. if (GUILayout.Button("Go to Universal Rendering Pipeline Asset")) {
  70. Selection.activeObject = pipe;
  71. }
  72. EditorGUILayout.Separator();
  73. GUI.enabled = false;
  74. }
  75. if (boxStyle == null) {
  76. boxStyle = new GUIStyle(GUI.skin.box);
  77. boxStyle.padding = new RectOffset(15, 10, 5, 5);
  78. }
  79. serializedObject.Update();
  80. EditorGUILayout.PropertyField(profile);
  81. if (profile.objectReferenceValue != null) {
  82. if (cachedProfile != profile.objectReferenceValue) {
  83. cachedProfile = null;
  84. }
  85. if (cachedProfile == null) {
  86. cachedProfile = (VolumetricFogProfile)profile.objectReferenceValue;
  87. cachedProfileEditor = CreateEditor(profile.objectReferenceValue);
  88. }
  89. // Drawing the profile editor
  90. EditorGUILayout.BeginVertical(boxStyle);
  91. cachedProfileEditor.OnInspectorGUI();
  92. EditorGUILayout.EndVertical();
  93. } else {
  94. EditorGUILayout.HelpBox("Create or assign a fog profile.", MessageType.Info);
  95. if (GUILayout.Button("New Fog Profile")) {
  96. CreateFogProfile();
  97. }
  98. }
  99. EditorGUILayout.PropertyField(enableNativeLights);
  100. GUI.enabled = !enableNativeLights.boolValue;
  101. EditorGUILayout.BeginHorizontal();
  102. EditorGUILayout.PropertyField(enablePointLights);
  103. if (GUILayout.Button("Point Light Manager", GUILayout.Width(200))) {
  104. Selection.activeGameObject = VolumetricFogManager.pointLightManager.gameObject;
  105. }
  106. EditorGUILayout.EndHorizontal();
  107. GUI.enabled = true;
  108. EditorGUILayout.BeginHorizontal();
  109. EditorGUILayout.PropertyField(enableVoids);
  110. if (GUILayout.Button("Void Manager", GUILayout.Width(200))) {
  111. Selection.activeGameObject = VolumetricFogManager.fogVoidManager.gameObject;
  112. }
  113. EditorGUILayout.EndHorizontal();
  114. EditorGUILayout.PropertyField(enableFollow);
  115. if (enableFollow.boolValue) {
  116. EditorGUI.indentLevel++;
  117. EditorGUILayout.PropertyField(followTarget, new GUIContent("Target"));
  118. EditorGUILayout.PropertyField(followMode, new GUIContent("Mode"));
  119. EditorGUILayout.PropertyField(followOffset, new GUIContent("Offset"));
  120. EditorGUI.indentLevel--;
  121. }
  122. EditorGUILayout.PropertyField(enableFade);
  123. if (enableFade.boolValue) {
  124. EditorGUI.indentLevel++;
  125. EditorGUILayout.PropertyField(fadeDistance);
  126. EditorGUILayout.PropertyField(fadeOut);
  127. EditorGUILayout.PropertyField(fadeController);
  128. EditorGUI.indentLevel--;
  129. }
  130. EditorGUILayout.PropertyField(enableSubVolumes);
  131. if (enableSubVolumes.boolValue) {
  132. EditorGUI.indentLevel++;
  133. EditorGUILayout.HelpBox("If no sub-volumes are specified below, any sub-volume in the scene will be used.", MessageType.Info);
  134. EditorGUILayout.PropertyField(fadeController, new GUIContent("Character Controller"));
  135. EditorGUILayout.PropertyField(subVolumes);
  136. EditorGUI.indentLevel--;
  137. }
  138. bool requiresFogOfWarTextureReload = false;
  139. EditorGUILayout.PropertyField(enableFogOfWar);
  140. if (enableFogOfWar.boolValue) {
  141. EditorGUI.indentLevel++;
  142. EditorGUILayout.PropertyField(fogOfWarCenter, new GUIContent("World Center"));
  143. EditorGUILayout.PropertyField(fogOfWarIsLocal, new GUIContent("Is Local", "Enable if fog of war center is local to the fog volume"));
  144. EditorGUILayout.PropertyField(fogOfWarSize, new GUIContent("World Coverage"));
  145. EditorGUILayout.PropertyField(fogOfWarShowCoverage, new GUIContent("Show Coverage Bounds"));
  146. EditorGUILayout.PropertyField(fogOfWarTextureSize, new GUIContent("Texture Size"));
  147. EditorGUILayout.PropertyField(fogOfWarRestoreDelay, new GUIContent("Restore Delay"));
  148. EditorGUILayout.PropertyField(fogOfWarRestoreDuration, new GUIContent("Restore Duration"));
  149. EditorGUILayout.PropertyField(fogOfWarSmoothness, new GUIContent("Border Smoothness"));
  150. EditorGUILayout.PropertyField(fogOfWarBlur, new GUIContent("Blur"));
  151. EditorGUILayout.Separator();
  152. EditorGUILayout.PropertyField(maskEditorEnabled, new GUIContent("Fog Of War Editor", "Activates terrain brush to paint/remove fog of war at custom locations."));
  153. if (maskEditorEnabled.boolValue) {
  154. if (GUILayout.Button("Create New Mask Texture")) {
  155. if (EditorUtility.DisplayDialog("Create Mask Texture", "A texture asset will be created with the size specified in current profile (" + fog.fogOfWarTextureSize + "x" + fog.fogOfWarTextureSize + ").\n\nContinue?", "Ok", "Cancel")) {
  156. CreateNewMaskTexture();
  157. GUIUtility.ExitGUI();
  158. }
  159. }
  160. EditorGUI.BeginChangeCheck();
  161. 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);
  162. if (EditorGUI.EndChangeCheck()) {
  163. requiresFogOfWarTextureReload = true;
  164. }
  165. Texture2D tex = fog.fogOfWarTexture;
  166. if (tex != null) {
  167. EditorGUILayout.LabelField(" Texture Size", tex.width.ToString());
  168. string path = AssetDatabase.GetAssetPath(tex);
  169. if (string.IsNullOrEmpty(path)) {
  170. path = "(Temporary texture)";
  171. }
  172. EditorGUILayout.LabelField(" Texture Path", path);
  173. }
  174. if (tex != null) {
  175. EditorGUILayout.Separator();
  176. EditorGUILayout.BeginVertical(GUI.skin.box);
  177. EditorGUILayout.BeginHorizontal();
  178. EditorGUILayout.PropertyField(maskBrushMode, new GUIContent("Brush Mode", "Select brush operation mode."));
  179. if (GUILayout.Button("Toggle", GUILayout.Width(70))) {
  180. maskBrushMode.intValue = maskBrushMode.intValue == 0 ? 1 : 0;
  181. }
  182. EditorGUILayout.EndHorizontal();
  183. if (maskBrushMode.intValue == (int)MASK_TEXTURE_BRUSH_MODE.ColorFog) {
  184. EditorGUILayout.PropertyField(maskBrushColor, new GUIContent(" Color", "Brush color."));
  185. }
  186. EditorGUILayout.PropertyField(maskBrushWidth, new GUIContent(" Width", "Width of the snow editor brush."));
  187. EditorGUILayout.PropertyField(maskBrushFuzziness, new GUIContent(" Fuzziness", "Solid vs spray brush."));
  188. EditorGUILayout.PropertyField(maskBrushOpacity, new GUIContent(" Opacity", "Stroke opacity."));
  189. EditorGUILayout.BeginHorizontal();
  190. if (tex == null) GUI.enabled = false;
  191. if (GUILayout.Button("Fill Mask")) {
  192. fog.ResetFogOfWar(1f);
  193. maskBrushMode.intValue = (int)MASK_TEXTURE_BRUSH_MODE.RemoveFog;
  194. }
  195. if (GUILayout.Button("Clear Mask")) {
  196. fog.ResetFogOfWar(0);
  197. maskBrushMode.intValue = (int)MASK_TEXTURE_BRUSH_MODE.AddFog;
  198. }
  199. GUI.enabled = true;
  200. EditorGUILayout.EndHorizontal();
  201. EditorGUILayout.EndVertical();
  202. }
  203. }
  204. EditorGUI.indentLevel--;
  205. }
  206. EditorGUILayout.PropertyField(enableUpdateModeOptions);
  207. if (enableUpdateModeOptions.boolValue) {
  208. EditorGUI.indentLevel++;
  209. EditorGUILayout.PropertyField(updateMode);
  210. EditorGUILayout.PropertyField(updateModeCamera, new GUIContent("Camera"));
  211. if (updateMode.intValue == (int)VolumetricFogUpdateMode.WhenCameraIsInsideArea) {
  212. EditorGUILayout.PropertyField(updateModeBounds, new GUIContent("Bounds"));
  213. }
  214. EditorGUI.indentLevel--;
  215. }
  216. EditorGUILayout.PropertyField(showBoundary);
  217. EditorGUILayout.Separator();
  218. if (GUILayout.Button("Show Volumetric Fog Manager Settings")) {
  219. Selection.activeObject = VolumetricFogManager.instance;
  220. }
  221. serializedObject.ApplyModifiedProperties();
  222. if (requiresFogOfWarTextureReload) {
  223. fog.ReloadFogOfWarTexture();
  224. }
  225. }
  226. void CreateFogProfile() {
  227. // Find directional light and adjusts brightness to avoid excessive bright fog
  228. float brightness = 1f;
  229. Light[] lights = FindObjectsOfType<Light>();
  230. if (lights != null) {
  231. foreach (Light light in lights) {
  232. if (light.type == LightType.Directional) {
  233. brightness /= light.intensity;
  234. break;
  235. }
  236. }
  237. }
  238. string path = "Assets";
  239. foreach (Object obj in Selection.GetFiltered(typeof(Object), SelectionMode.Assets)) {
  240. path = AssetDatabase.GetAssetPath(obj);
  241. if (File.Exists(path)) {
  242. path = Path.GetDirectoryName(path);
  243. }
  244. break;
  245. }
  246. VolumetricFogProfile fp = CreateInstance<VolumetricFogProfile>();
  247. fp.name = "New Volumetric Fog Profile";
  248. fp.brightness = brightness;
  249. AssetDatabase.CreateAsset(fp, path + "/" + fp.name + ".asset");
  250. AssetDatabase.SaveAssets();
  251. profile.objectReferenceValue = fp;
  252. EditorGUIUtility.PingObject(fp);
  253. }
  254. }
  255. }