498 lines
15 KiB
C#
498 lines
15 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Reflection;
|
|||
|
|
using System.Text;
|
|||
|
|
using UnityEditor;
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
using UnityEditor.Build;
|
|||
|
|
#endif
|
|||
|
|
using UnityEditor.PackageManager;
|
|||
|
|
using UnityEditor.PackageManager.Requests;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.Rendering;
|
|||
|
|
|
|||
|
|
#if HDRP_OUTLINE
|
|||
|
|
using UnityEngine.Rendering.HighDefinition;
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
namespace EPOOutline
|
|||
|
|
{
|
|||
|
|
public class EPOSetuper : EditorWindow
|
|||
|
|
{
|
|||
|
|
private static readonly string SRPShownID = "EasyPerformantOutlineWasShownAndCanceled";
|
|||
|
|
private static bool UPRWasFound = false;
|
|||
|
|
private static bool HDRPWasFound = false;
|
|||
|
|
|
|||
|
|
private static ListRequest request;
|
|||
|
|
private static AddRequest addRequest;
|
|||
|
|
|
|||
|
|
private Texture2D logoImage;
|
|||
|
|
|
|||
|
|
public enum SetupType
|
|||
|
|
{
|
|||
|
|
BuiltIn,
|
|||
|
|
URP,
|
|||
|
|
HDRP
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[SerializeField]
|
|||
|
|
private Vector2 scroll;
|
|||
|
|
|
|||
|
|
public static bool ShouldShow
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return PlayerPrefs.GetInt(SRPShownID, 0) == 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
PlayerPrefs.SetInt(SRPShownID, value ? 0 : 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
List<NamedBuildTarget>
|
|||
|
|
#else
|
|||
|
|
List<BuildTargetGroup>
|
|||
|
|
#endif
|
|||
|
|
GetApplicableGroups()
|
|||
|
|
{
|
|||
|
|
var groups = new List<BuildTargetGroup>();
|
|||
|
|
var type = typeof(BuildTargetGroup);
|
|||
|
|
|
|||
|
|
var values = type.GetFields(BindingFlags.Public | BindingFlags.Static);
|
|||
|
|
|
|||
|
|
foreach (var value in values)
|
|||
|
|
{
|
|||
|
|
if (value.GetCustomAttribute<ObsoleteAttribute>() != null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var targetValue = (BuildTargetGroup) value.GetValue(null);
|
|||
|
|
if (targetValue == BuildTargetGroup.Unknown)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
groups.Add(targetValue);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
return groups.ConvertAll(NamedBuildTarget.FromBuildTargetGroup);
|
|||
|
|
#else
|
|||
|
|
return groups;
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool CheckHasDefinition(string definition)
|
|||
|
|
{
|
|||
|
|
var targets = GetApplicableGroups();
|
|||
|
|
foreach (var buildTargetGroup in targets)
|
|||
|
|
{
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
var definitions = PlayerSettings.GetScriptingDefineSymbols(buildTargetGroup);
|
|||
|
|
#else
|
|||
|
|
var definitions = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup);
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
var splited = definitions.Split(';');
|
|||
|
|
|
|||
|
|
if (Array.Find(splited, x => x == definition) == null)
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if URP_OUTLINE
|
|||
|
|
private static bool CheckShouldFixFeature()
|
|||
|
|
{
|
|||
|
|
var activeAssets = PipelineAssetUtility.ActiveAssets;
|
|||
|
|
|
|||
|
|
foreach (var asset in activeAssets)
|
|||
|
|
{
|
|||
|
|
if (!PipelineAssetUtility.IsURP(asset))
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
if (!PipelineAssetUtility.DoesAssetContainsSRPOutlineFeature(asset))
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if URP_OUTLINE || HDRP_OUTLINE
|
|||
|
|
public enum ExpectedAssetType
|
|||
|
|
{
|
|||
|
|
URP,
|
|||
|
|
HDRP
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool CheckHasActiveRenderAssets(ExpectedAssetType type)
|
|||
|
|
{
|
|||
|
|
foreach (var asset in PipelineAssetUtility.ActiveAssets)
|
|||
|
|
{
|
|||
|
|
switch (type)
|
|||
|
|
{
|
|||
|
|
case ExpectedAssetType.URP:
|
|||
|
|
if (PipelineAssetUtility.IsURP(asset))
|
|||
|
|
return true;
|
|||
|
|
break;
|
|||
|
|
case ExpectedAssetType.HDRP:
|
|||
|
|
if (PipelineAssetUtility.IsHDRP(asset))
|
|||
|
|
return true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
private void OnEnable()
|
|||
|
|
{
|
|||
|
|
EditorApplication.update += Check;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnDisable()
|
|||
|
|
{
|
|||
|
|
EditorApplication.update -= Check;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void RemoveDefinition(string definition, Func<bool> check)
|
|||
|
|
{
|
|||
|
|
if (!check())
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var group = EditorUserBuildSettings.selectedBuildTargetGroup;
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
var definitions = PlayerSettings.GetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(group));
|
|||
|
|
#else
|
|||
|
|
var definitions = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
var splited = definitions.Split(';');
|
|||
|
|
|
|||
|
|
var builder = new StringBuilder();
|
|||
|
|
|
|||
|
|
var addedCount = 0;
|
|||
|
|
foreach (var item in splited)
|
|||
|
|
{
|
|||
|
|
if (item == definition)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
builder.Append(item);
|
|||
|
|
builder.Append(';');
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (addedCount != 0)
|
|||
|
|
builder.Remove(builder.Length - 1, 1);
|
|||
|
|
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
PlayerSettings.SetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(group), builder.ToString());
|
|||
|
|
#else
|
|||
|
|
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, builder.ToString());
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void Check()
|
|||
|
|
{
|
|||
|
|
if (EditorApplication.isPlaying)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (request == null || request.Error != null)
|
|||
|
|
{
|
|||
|
|
request = Client.List();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!request.IsCompleted)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
UPRWasFound = HasURP(request.Result);
|
|||
|
|
HDRPWasFound = HasHDRP(request.Result);
|
|||
|
|
|
|||
|
|
request = Client.List();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool HasHDRP(PackageCollection result)
|
|||
|
|
{
|
|||
|
|
return HasPackage(result, "com.unity.render-pipelines.high-definition");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool HasURP(PackageCollection result)
|
|||
|
|
{
|
|||
|
|
return HasPackage(result, "com.unity.render-pipelines.universal");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool HasPackage(PackageCollection result, string packageName)
|
|||
|
|
{
|
|||
|
|
if (result == null)
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
var found = false;
|
|||
|
|
var name = packageName;
|
|||
|
|
foreach (var item in result)
|
|||
|
|
{
|
|||
|
|
if (item.name == name)
|
|||
|
|
{
|
|||
|
|
found = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return found;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MenuItem("Tools/Easy performant outline/Setup")]
|
|||
|
|
private static void ForceShowWindow()
|
|||
|
|
{
|
|||
|
|
ShouldShow = true;
|
|||
|
|
ShowWindow();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MenuItem("Tools/Easy performant outline/Online docs")]
|
|||
|
|
private static void ShowDocs()
|
|||
|
|
{
|
|||
|
|
Application.OpenURL("https://docs.google.com/document/d/17GvzvXNEjpEQ8DShRrVHKQ4I6s2tTVtwX6NzCZZ28AQ");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void ShowWindow()
|
|||
|
|
{
|
|||
|
|
if (!ShouldShow)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var window = GetWindow<EPOSetuper>(true, "EPO Setuper", false);
|
|||
|
|
window.maxSize = new Vector2(500, 500);
|
|||
|
|
window.minSize = new Vector2(500, 500);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void OnGUI()
|
|||
|
|
{
|
|||
|
|
if (logoImage == null)
|
|||
|
|
logoImage = Resources.Load<Texture2D>("Easy performant outline/EP Outline logo");
|
|||
|
|
|
|||
|
|
var height = 180;
|
|||
|
|
GUILayout.Space(height);
|
|||
|
|
|
|||
|
|
var imagePosition = new Rect(Vector2.zero, new Vector2(position.width, height));
|
|||
|
|
|
|||
|
|
GUI.DrawTexture(imagePosition, logoImage, ScaleMode.ScaleAndCrop, true);
|
|||
|
|
|
|||
|
|
GUILayout.Space(10);
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Open documentation"))
|
|||
|
|
ShowDocs();
|
|||
|
|
|
|||
|
|
scroll = GUILayout.BeginScrollView(scroll);
|
|||
|
|
|
|||
|
|
if (EditorApplication.isPlaying)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox("Please stop running the app to start setup process", MessageType.Info);
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Stop"))
|
|||
|
|
EditorApplication.isPlaying = false;
|
|||
|
|
|
|||
|
|
GUILayout.EndScrollView();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (EditorApplication.isCompiling)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("Compiling... please wait."));
|
|||
|
|
GUILayout.EndScrollView();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
EditorGUILayout.HelpBox("In order to make DOTween work, please enable it on the DOTween setup panel", MessageType.Warning);
|
|||
|
|
|
|||
|
|
EditorGUI.indentLevel = 0;
|
|||
|
|
|
|||
|
|
EditorPrefs.SetInt("SetupType", (int)(SetupType)EditorGUILayout.EnumPopup("Setup type", (SetupType)EditorPrefs.GetInt("SetupType")));
|
|||
|
|
|
|||
|
|
switch ((SetupType)EditorPrefs.GetInt("SetupType"))
|
|||
|
|
{
|
|||
|
|
case SetupType.BuiltIn:
|
|||
|
|
DrawBuiltInSetup();
|
|||
|
|
break;
|
|||
|
|
case SetupType.URP:
|
|||
|
|
DrawURPSetup();
|
|||
|
|
break;
|
|||
|
|
case SetupType.HDRP:
|
|||
|
|
DrawHDRPSetup();
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
GUILayout.EndScrollView();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DrawBuiltInSetup()
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("There is no need in making any changes to work with Built-In renderer. Just add Outliner to camera and Outlinable to the object you wish to highlight"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DrawHDRPSetup()
|
|||
|
|
{
|
|||
|
|
if (addRequest != null && !addRequest.IsCompleted)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("Adding package..."));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var packageName = "com.unity.render-pipelines.high-definition";
|
|||
|
|
|
|||
|
|
if (!HDRPWasFound)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("There is no package added. Chick 'Add' to add the pipeline package."));
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Add"))
|
|||
|
|
addRequest = Client.Add(packageName);
|
|||
|
|
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("Pipeline asset has been found in packages"));
|
|||
|
|
|
|||
|
|
#if HDRP_OUTLINE
|
|||
|
|
if (!CheckHasActiveRenderAssets(ExpectedAssetType.HDRP))
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("There is no renderer asset set up. Create one?"));
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Create"))
|
|||
|
|
{
|
|||
|
|
var path = EditorUtility.SaveFilePanelInProject("Asset location", "Rendering asset", "asset", "Select the folder to save rendering asset");
|
|||
|
|
if (string.IsNullOrEmpty(path))
|
|||
|
|
{
|
|||
|
|
GUILayout.EndScrollView();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var pathNoExt = Path.ChangeExtension(path, string.Empty);
|
|||
|
|
pathNoExt = pathNoExt.Substring(0, pathNoExt.Length - 1);
|
|||
|
|
|
|||
|
|
var asset = PipelineAssetUtility.CreateHDRPAsset();
|
|||
|
|
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
GraphicsSettings.defaultRenderPipeline = asset;
|
|||
|
|
#else
|
|||
|
|
GraphicsSettings.renderPipelineAsset = asset;
|
|||
|
|
#endif
|
|||
|
|
AssetDatabase.CreateAsset(asset, path);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("At least one renderer asset is set up"));
|
|||
|
|
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
var volume = FindAnyObjectByType<CustomPassVolume>();
|
|||
|
|
#else
|
|||
|
|
var volume = FindObjectOfType<CustomPassVolume>();
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
if (volume == null)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("There is no custom pass volume in the scene. Click Add to fix it."));
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Add"))
|
|||
|
|
{
|
|||
|
|
var go = new GameObject("Custom volume");
|
|||
|
|
go.AddComponent<CustomPassVolume>();
|
|||
|
|
|
|||
|
|
EditorUtility.SetDirty(go);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("The scene has custom pass volume."));
|
|||
|
|
|
|||
|
|
if (volume.customPasses.Find(x => x is EPOOutline.OutlineCustomPass) == null)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("The volume doesn't have custom pass. Click Add to fix it."));
|
|||
|
|
if (GUILayout.Button("Add"))
|
|||
|
|
{
|
|||
|
|
volume.AddPassOfType(typeof(EPOOutline.OutlineCustomPass));
|
|||
|
|
|
|||
|
|
EditorUtility.SetDirty(volume);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("The custom volume is set up"));
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DrawURPSetup()
|
|||
|
|
{
|
|||
|
|
if (addRequest != null && !addRequest.IsCompleted)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("Adding package..."));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var packageName = "com.unity.render-pipelines.universal";
|
|||
|
|
if (!UPRWasFound)
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("There is no package added. Chick 'Add' to add the pipeline package."));
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Add"))
|
|||
|
|
addRequest = Client.Add(packageName);
|
|||
|
|
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("Pipeline asset has been found in packages"));
|
|||
|
|
|
|||
|
|
#if URP_OUTLINE
|
|||
|
|
if (!CheckHasActiveRenderAssets(ExpectedAssetType.URP))
|
|||
|
|
{
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("There is no renderer asset set up. Create one?"));
|
|||
|
|
|
|||
|
|
if (GUILayout.Button("Create"))
|
|||
|
|
{
|
|||
|
|
var path = EditorUtility.SaveFilePanelInProject("Asset location", "Rendering asset", "asset", "Select the folder to save rendering asset");
|
|||
|
|
if (string.IsNullOrEmpty(path))
|
|||
|
|
{
|
|||
|
|
GUILayout.EndScrollView();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var pathNoExt = Path.ChangeExtension(path, string.Empty);
|
|||
|
|
pathNoExt = pathNoExt.Substring(0, pathNoExt.Length - 1);
|
|||
|
|
|
|||
|
|
var asset = PipelineAssetUtility.CreateAsset(pathNoExt);
|
|||
|
|
#if UNITY_6000_0_OR_NEWER
|
|||
|
|
GraphicsSettings.defaultRenderPipeline = asset;
|
|||
|
|
#else
|
|||
|
|
GraphicsSettings.renderPipelineAsset = asset;
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("At least one renderer asset is set up"));
|
|||
|
|
|
|||
|
|
if (CheckShouldFixFeature())
|
|||
|
|
{
|
|||
|
|
if (!GUILayout.Button("Add render features to all assets"))
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var assets = PipelineAssetUtility.ActiveAssets;
|
|||
|
|
foreach (var asset in assets)
|
|||
|
|
PipelineAssetUtility.AddRenderFeature(asset);
|
|||
|
|
|
|||
|
|
AssetDatabase.SaveAssets();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
EditorGUILayout.HelpBox(new GUIContent("Feature is added for all renderers in use"));
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void OnDestroy()
|
|||
|
|
{
|
|||
|
|
ShouldShow = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|