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.
 
 
 
 

426 lines
14 KiB

#define USE_EXIT_WORKAROUND_FOGBUGZ_1062258
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using UnityEngine;
using UnityEngine.VFX;
namespace UnityEditor.VFX.UI
{
[Serializable]
class VFXViewWindow : EditorWindow
{
private static Dictionary<Tuple<Type, bool>, string> vfxIconMap = new()
{
{ new Tuple<Type, bool>(typeof(VisualEffectSubgraphOperator), true), "d_subgraph-operator.png" },
{ new Tuple<Type, bool>(typeof(VisualEffectSubgraphBlock), true), "d_subgraph-block.png" },
{ new Tuple<Type, bool>(typeof(VFXGraph), true), "vfx_graph_icon_gray_dark.png" },
{ new Tuple<Type, bool>(typeof(VisualEffectSubgraphOperator), false), "subgraph-operator.png" },
{ new Tuple<Type, bool>(typeof(VisualEffectSubgraphBlock), false), "subgraph-block.png" },
{ new Tuple<Type, bool>(typeof(VFXGraph), false), "vfx_graph_icon_gray_light.png" },
};
static List<VFXViewWindow> s_VFXWindows = new();
VisualEffect m_pendingAttachment;
static VFXViewWindow()
{
EditorApplication.wantsToQuit += OnQuitting;
}
static bool OnQuitting()
{
VFXAnalytics.GetInstance().OnQuitApplication();
return true;
}
void OnEnable()
{
if (this.m_DisplayedResource == null && TryGetNoAssetWindow(out _))
{
this.Close();
}
else
{
UnityEditor.UIElements.AssetMonitoringUtilities.SetResetPanelRenderingOnAssetChange(this, false);
s_VFXWindows.Add(this);
DisableViewDataPersistence();
}
}
[MenuItem("Window/Visual Effects/Visual Effect Graph", false, 3011)]
public static void ShowWindow()
{
VFXLibrary.LogUnsupportedSRP();
GetWindow((VisualEffectResource)null, true);
}
public static VFXViewWindow GetWindow(VisualEffectAsset vfxAsset, bool createIfNeeded = false)
{
return GetWindowLambda(x => x.displayedResource?.asset == vfxAsset, createIfNeeded, true);
}
public static VFXViewWindow GetWindow(VFXGraph vfxGraph, bool createIfNeeded = false, bool show = true)
{
return GetWindowLambda(
x => x.displayedResource == (vfxGraph != null ? vfxGraph.visualEffectResource : null),
createIfNeeded,
show);
}
public static VFXViewWindow GetWindow(VisualEffectResource resource, bool createIfNeeded = false, bool show = true)
{
return GetWindowLambda(x => x.graphView?.controller?.graph.visualEffectResource == resource, createIfNeeded, show);
}
public static VFXViewWindow GetWindow(VFXParameter vfxParameter, bool createIfNeeded = false)
{
return GetWindowLambda(
x => x.graphView?.controller?.parameterControllers.Any(y => y.model == vfxParameter) == true,
createIfNeeded,
true);
}
static VFXViewWindow GetWindowLambda(Func<VFXViewWindow, bool> func, bool createIfNeeded, bool show)
{
var window = s_VFXWindows.SingleOrDefault(func);
if (window == null)
{
// Get the empty VFX window if it's opened
TryGetNoAssetWindow(out window);
}
if (window == null && createIfNeeded)
{
window = CreateWindow();
}
if (window != null && show)
{
window.Show(true);
window.Focus();
}
return window;
}
public static VFXViewWindow GetWindowNoShow(VFXView vfxView) => GetWindow(vfxView.controller?.graph, false, false);
public static VFXViewWindow GetWindow(VFXView vfxView) => GetWindow(vfxView.controller?.graph);
public static ReadOnlyCollection<VFXViewWindow> GetAllWindows() => s_VFXWindows.AsReadOnly();
public static bool CloseIfNotLast(VFXView vfxView)
{
var noAssetWindows = s_VFXWindows.Where(x => x.graphView?.controller?.graph == null).ToArray();
if (noAssetWindows.Length > 1)
{
var window = noAssetWindows.Single(x => x.graphView == vfxView);
window.Close();
return true;
}
return false;
}
public VFXView graphView { get; private set; }
public VisualEffectResource displayedResource => m_DisplayedResource;
public void UpdateTitle(string assetPath)
{
titleContent.text = Path.GetFileNameWithoutExtension(assetPath);
}
public void UpdateHistory()
{
m_ResourceHistory.RemoveAll(x => x == null);
if (graphView != null)
graphView.UpdateIsSubgraph();
}
public void LoadAsset(VisualEffectAsset asset, VisualEffect effectToAttach)
{
VFXLibrary.LogUnsupportedSRP();
string assetPath = AssetDatabase.GetAssetPath(asset);
VisualEffectResource resource = VisualEffectResource.GetResourceAtPath(assetPath);
//Transitionning code
if (resource == null)
{
resource = new VisualEffectResource();
resource.SetAssetPath(AssetDatabase.GetAssetPath(asset));
}
LoadResource(resource, effectToAttach);
}
public void LoadResource(VisualEffectResource resource, VisualEffect effectToAttach = null)
{
if (graphView?.controller == null || graphView.controller.model != resource)
{
InternalLoadResource(resource);
}
var asset = effectToAttach == null ? m_pendingAttachment : effectToAttach;
graphView?.TryAttachTo(asset, true);
titleContent.text = resource.name;
UpdateIcon(resource);
}
VisualEffect GetVisualEffectFromID(int id) => EditorUtility.InstanceIDToObject(id) as VisualEffect;
internal void AttachTo(VisualEffect visualEffect)
{
if (graphView != null && !graphView.locked)
{
graphView.TryAttachTo(visualEffect, true);
SaveChanges();
}
else if (visualEffect.visualEffectAsset == m_DisplayedResource.asset)
{
m_pendingAttachment = visualEffect;
}
}
internal void DetachIfDeleted()
{
if (graphView != null && graphView.attachedComponent == null)
{
graphView.Detach();
SaveChanges();
}
}
List<VisualEffectResource> m_ResourceHistory = new();
public IEnumerable<VisualEffectResource> resourceHistory
{
get { return m_ResourceHistory; }
}
public void PushResource(VisualEffectResource resource)
{
if (graphView.controller == null || graphView.controller.model != resource)
{
m_ResourceHistory.Add(m_DisplayedResource);
InternalLoadResource(resource);
}
}
void InternalLoadResource(VisualEffectResource resource)
{
m_DisplayedResource = resource;
graphView.controller = VFXViewController.GetController(resource, true);
graphView.UpdateGlobalSelection();
graphView.FrameNewController();
graphView.UpdateIsSubgraph();
UpdateIcon(resource);
}
void UpdateIcon(VisualEffectResource resource)
{
var iconFilePath = vfxIconMap[new Tuple<Type, bool>(resource.isSubgraph ? resource.subgraph.GetType() : resource.graph.GetType(), EditorGUIUtility.isProSkin)];
var icon = AssetDatabase.LoadAssetAtPath<Texture2D>($"{VisualEffectAssetEditorUtility.editorResourcesPath}/VFX/{iconFilePath}");
titleContent.image = icon;
}
public bool CanPopResource()
{
return m_ResourceHistory.Any();
}
public void PopResource()
{
if (CanPopResource())
{
var resource = m_ResourceHistory.Last();
if (resource != null)
{
var window = VFXViewWindow.GetWindow(resource);
if (window != null)
{
window.Focus();
}
else
{
LoadResource(resource);
m_ResourceHistory.Remove(resource);
graphView.UpdateIsSubgraph();
}
}
}
}
protected void CreateGUI()
{
VFXManagerEditor.CheckVFXManager();
graphView = new VFXView();
rootVisualElement.Add(graphView);
autoCompile = true;
autoReinit = true;
if (graphView?.controller == null && m_DisplayedResource != null)
{
LoadResource(m_DisplayedResource);
}
if (titleContent.image == null)
{
var icon = AssetDatabase.LoadAssetAtPath<Texture2D>(VisualEffectAssetEditorUtility.editorResourcesPath + "/VFX/"
+ (EditorGUIUtility.isProSkin ? "vfx_graph_icon_gray_dark.png" : "vfx_graph_icon_gray_light.png"));
titleContent.image = icon;
}
graphView?.OnFocus();
}
protected void OnDestroy()
{
s_VFXWindows.Remove(this);
if (graphView != null)
{
if (graphView.controller != null)
VFXAnalytics.GetInstance().OnGraphClosed(graphView);
graphView.Dispose();
graphView = null;
}
}
static VFXViewWindow CreateWindow()
{
var lastVFXWindow = s_VFXWindows.LastOrDefault();
var window = CreateInstance<VFXViewWindow>();
if (!TryToTabNextTo(lastVFXWindow, window))
{
TryToTabNextTo(GetWindowDontShow<SceneView>(), window);
}
return window;
}
static bool TryGetNoAssetWindow(out VFXViewWindow noAssetWindow)
{
var noAssetWindows = s_VFXWindows.Where(x => x.m_DisplayedResource == null);
noAssetWindow = noAssetWindows.FirstOrDefault();
return noAssetWindow != null;
}
static bool TryToTabNextTo(EditorWindow nextToWindow, EditorWindow window)
{
if (nextToWindow != null && nextToWindow.m_Parent is DockArea dockArea)
{
var index = dockArea.m_Panes.IndexOf(nextToWindow);
dockArea.AddTab(index + 1, window);
return true;
}
return false;
}
void OnFocus()
{
if (graphView != null) // OnFocus can be somehow called before OnEnable
graphView.OnFocus();
}
public void OnVisualEffectComponentChanged(IEnumerable<VisualEffect> componentChanged)
{
if (graphView != null)
graphView.OnVisualEffectComponentChanged(componentChanged);
}
public bool autoCompile { get; set; }
public bool autoReinit { get; set; }
public float autoReinitPrewarmTime { get; set; }
void Update()
{
if (graphView == null && m_DisplayedResource == null)
return;
VFXViewController controller = graphView?.controller;
var filename = "No Asset";
if (controller != null)
{
controller.NotifyUpdate();
if (controller.model != null)
{
var graph = controller.graph;
if (graph != null)
{
filename = controller.name;
if (EditorUtility.IsDirty(graph))
{
filename += "*";
}
if (autoCompile && graph.IsExpressionGraphDirty() && !graph.GetResource().isSubgraph)
{
graph.errorManager.RefreshCompilationReport();
VFXGraph.explicitCompile = true;
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(graphView.controller.model));
// As are implemented subgraph now, compiling dependents chain can reset dirty flag on used subgraphs, which will make an infinite loop, this is bad!
graph.SetExpressionGraphDirty(false);
VFXGraph.explicitCompile = false;
}
else
graph.RecompileIfNeeded(true, true);
if (graph.IsCustomAttributeDirty())
{
graphView.blackboard.Update(true);
graph.SetCustomAttributeDirty(false);
}
bool wasDirty = graph.IsExpressionGraphDirty();
controller.RecompileExpressionGraphIfNeeded();
// Hack to avoid infinite recompilation due to UI triggering a recompile TODO: Fix problematic cases that trigger that error
if (!wasDirty && graph.IsExpressionGraphDirty())
{
Debug.LogError(
"Expression graph was marked as dirty after compiling context for UI. Discard to avoid infinite compilation loop.");
graph.SetExpressionGraphDirty(false);
}
graphView.UpdateBadges(graph.errorManager.errorReporter);
graphView.UpdateBadges(graph.errorManager.compileReporter);
}
}
else
{
m_DisplayedResource = null;
}
}
if (VFXViewModificationProcessor.assetMoved)
{
graphView.AssetMoved();
VFXViewModificationProcessor.assetMoved = false;
}
titleContent.text = filename;
}
[SerializeField]
VisualEffectResource m_DisplayedResource;
}
}