You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
10 KiB
243 lines
10 KiB
using UnityEditor.SceneManagement;
|
|
using UnityEditor.ShaderGraph;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.HighDefinition;
|
|
using UnityEngine.Rendering.HighDefinition.Compositor;
|
|
|
|
namespace UnityEditor.Rendering.HighDefinition.Compositor
|
|
{
|
|
[HDRPHelpURLAttribute("understand-the-graphics-compositor")]
|
|
internal class CompositorWindow : EditorWindowWithHelpButton
|
|
{
|
|
static class Styles
|
|
{
|
|
static public GUIContent windowTitle { get; } = EditorGUIUtility.TrTextContent("Graphics Compositor");
|
|
static public GUIContent enableCompositor { get; } = EditorGUIUtility.TrTextContent("Enable Compositor", "Enabled the compositor and creates a default composition profile.");
|
|
static public GUIContent removeCompositor { get; } = EditorGUIUtility.TrTextContent("Remove compositor from scene", "Removes the compositor and any composition settings from the scene.");
|
|
}
|
|
|
|
static CompositorWindow s_Window;
|
|
|
|
// Remember the last selected layer
|
|
static int s_SelectionIndex = -1;
|
|
|
|
CompositionManagerEditor m_Editor;
|
|
Vector2 m_ScrollPosition = Vector2.zero;
|
|
bool m_RequiresRedraw = false;
|
|
float m_TimeSinceLastRepaint = 0;
|
|
|
|
[MenuItem("Window/Rendering/Graphics Compositor", false, 10400)]
|
|
static void Init()
|
|
{
|
|
// Get existing open window or if none, make a new one:
|
|
s_Window = GetWindow(typeof(CompositorWindow)) as CompositorWindow;
|
|
if (s_Window == null)
|
|
return;
|
|
|
|
s_Window.titleContent = Styles.windowTitle;
|
|
s_Window.Show();
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
// Register a custom undo callback
|
|
Undo.undoRedoPerformed += UndoCallback;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
m_TimeSinceLastRepaint += Time.deltaTime;
|
|
|
|
// This ensures that layer thumbnails are updated at least 4 times per second (redrawing the UI on every frame is too CPU intensive)
|
|
const float timeThreshold = 0.25f;
|
|
if (m_TimeSinceLastRepaint > timeThreshold)
|
|
{
|
|
Repaint();
|
|
|
|
// [case 1266216] Ensure the game view gets repainted a few times per second even when we are not in play mode.
|
|
// This ensures that we will not always display the first frame, which might have some artifacts for effects that require temporal data
|
|
if (!Application.isPlaying)
|
|
{
|
|
CompositionManager compositor = CompositionManager.GetInstance();
|
|
if (compositor && compositor.enableOutput)
|
|
{
|
|
compositor.timeSinceLastRepaint += Time.deltaTime;
|
|
// The Editor will repaint the game view if the scene view is also visible (side-by-side) and
|
|
// "always refresh" is enabled so we call manually repaint only if enough time has passed
|
|
if (compositor.timeSinceLastRepaint > timeThreshold)
|
|
{
|
|
compositor.Repaint();
|
|
#if UNITY_2021_1_OR_NEWER
|
|
// [case 1290622] For version 2021.1 we have to explicitly request an update of the gameview with the following call
|
|
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
m_TimeSinceLastRepaint = 0;
|
|
CompositionManager compositor = CompositionManager.GetInstance();
|
|
bool enableCompositor = false;
|
|
if (compositor)
|
|
{
|
|
enableCompositor = compositor.enableInternal;
|
|
}
|
|
|
|
bool enableCompositorCached = enableCompositor;
|
|
enableCompositor = EditorGUILayout.Toggle(Styles.enableCompositor, enableCompositor);
|
|
|
|
// Track if the user changed the compositor enable state and mark the scene dirty if necessary
|
|
if (enableCompositorCached != enableCompositor && compositor != null)
|
|
{
|
|
EditorUtility.SetDirty(compositor);
|
|
}
|
|
|
|
if (compositor == null && enableCompositor)
|
|
{
|
|
Debug.Log("The scene does not have a compositor. Creating a new one with the default configuration.");
|
|
GameObject go = new GameObject("HDRP Compositor") { hideFlags = HideFlags.HideInHierarchy };
|
|
compositor = go.AddComponent<CompositionManager>();
|
|
|
|
// Mark as dirty, so if the user closes the scene right away, the change will be saved.
|
|
EditorUtility.SetDirty(compositor);
|
|
|
|
// Now add the default configuration
|
|
CompositionUtils.LoadDefaultCompositionGraph(compositor);
|
|
CompositionUtils.LoadOrCreateCompositionProfileAsset(compositor);
|
|
compositor.SetupCompositionMaterial();
|
|
CompositionUtils.SetDefaultCamera(compositor);
|
|
CompositionUtils.SetDefaultLayers(compositor);
|
|
|
|
Undo.RegisterCreatedObjectUndo(compositor.outputCamera.gameObject, "Create Compositor");
|
|
Undo.RegisterCreatedObjectUndo(go, "Create Compositor");
|
|
}
|
|
else if (compositor && (compositor.enableInternal != enableCompositor))
|
|
{
|
|
string message = enableCompositor ? "Enable Compositor" : "Disable Compositor";
|
|
Undo.RecordObject(compositor, message);
|
|
compositor.enableInternal = enableCompositor;
|
|
}
|
|
else if (!compositor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (compositor && !compositor.enableInternal)
|
|
{
|
|
if (GUILayout.Button(new GUIContent(Styles.removeCompositor)))
|
|
{
|
|
if (compositor.outputCamera)
|
|
{
|
|
if (compositor.outputCamera.name == CompositionUtils.k_DefaultCameraName)
|
|
{
|
|
var cameraData = compositor.outputCamera.GetComponent<HDAdditionalCameraData>();
|
|
if (cameraData != null)
|
|
{
|
|
CoreUtils.Destroy(cameraData);
|
|
}
|
|
CoreUtils.Destroy(compositor.outputCamera.gameObject);
|
|
CoreUtils.Destroy(compositor.outputCamera);
|
|
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
|
}
|
|
}
|
|
|
|
CoreUtils.Destroy(compositor);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// keep track of shader graph changes: when the user saves a graph, we should load/reflect any new shader properties
|
|
GraphData.onSaveGraph += MarkShaderAsDirty;
|
|
|
|
if (compositor.profile == null)
|
|
{
|
|
// The compositor was loaded, but there was no profile (someone deleted the asset from disk?), so create a new one
|
|
CompositionUtils.LoadOrCreateCompositionProfileAsset(compositor);
|
|
compositor.SetupCompositionMaterial();
|
|
m_RequiresRedraw = true;
|
|
}
|
|
|
|
if (m_Editor == null || m_Editor.target == null || m_Editor.isDirty || m_RequiresRedraw)
|
|
{
|
|
if (m_Editor != null)
|
|
{
|
|
// Remember the previously selected layer when recreating the Editor
|
|
s_SelectionIndex = m_Editor.selectionIndex;
|
|
}
|
|
m_Editor = (CompositionManagerEditor)Editor.CreateEditor(compositor);
|
|
m_RequiresRedraw = false;
|
|
m_Editor.defaultSelection = s_SelectionIndex;
|
|
}
|
|
|
|
m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition);
|
|
using (new EditorGUI.DisabledScope(!compositor.enableInternal))
|
|
{
|
|
if (m_Editor)
|
|
{
|
|
m_Editor.OnInspectorGUI();
|
|
|
|
// Remember which layer was selected / drawn in the last draw call
|
|
s_SelectionIndex = m_Editor.selectionIndex;
|
|
}
|
|
}
|
|
GUILayout.EndScrollView();
|
|
}
|
|
|
|
void MarkShaderAsDirty(Shader shader, object context)
|
|
{
|
|
CompositionManager compositor = CompositionManager.GetInstance();
|
|
if (compositor)
|
|
{
|
|
compositor.shaderPropertiesAreDirty = true;
|
|
m_RequiresRedraw = true;
|
|
|
|
EditorUtility.SetDirty(compositor);
|
|
EditorUtility.SetDirty(compositor.profile);
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
GraphData.onSaveGraph -= MarkShaderAsDirty;
|
|
|
|
Undo.undoRedoPerformed -= UndoCallback;
|
|
s_SelectionIndex = m_Editor ? m_Editor.selectionIndex : -1;
|
|
}
|
|
|
|
void UndoCallback()
|
|
{
|
|
// Undo-redo might change the layer order, so we need to redraw the compositor UI and also refresh the layer setup
|
|
if (!m_Editor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_Editor.CacheSerializedObjects();
|
|
m_RequiresRedraw = true;
|
|
|
|
// After undo, set the selection index to the last shown layer, because the Unity Editor resets the value to the last layer in the list
|
|
m_Editor.defaultSelection = s_SelectionIndex;
|
|
m_Editor.selectionIndex = s_SelectionIndex;
|
|
|
|
|
|
CompositionManager compositor = CompositionManager.GetInstance();
|
|
// The compositor might be null even if the CompositionManagerEditor is not (in case the user switches from a scene with a compositor to a scene without one)
|
|
if (compositor)
|
|
{
|
|
// Some properties were changed, mark the profile as dirty so it can be saved if the user saves the scene
|
|
EditorUtility.SetDirty(compositor);
|
|
EditorUtility.SetDirty(compositor.profile);
|
|
|
|
// Clean-up existing cameras after undo, we will re-allocate the layer resources
|
|
CompositorCameraRegistry.GetInstance().CleanUpCameraOrphans(compositor.layers);
|
|
compositor.DeleteLayerRTs();
|
|
compositor.UpdateLayerSetup();
|
|
}
|
|
}
|
|
}
|
|
}
|