123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834 |
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityEditor.Experimental.GraphView;
- using UnityEngine.UIElements;
- using System;
- using UnityEditor.Graphing;
- using UnityEditor.ShaderGraph.Internal;
- using GraphDataStore = UnityEditor.ShaderGraph.DataStore<UnityEditor.ShaderGraph.GraphData>;
- using BlackboardItem = UnityEditor.ShaderGraph.Internal.ShaderInput;
-
- namespace UnityEditor.ShaderGraph.Drawing
- {
- struct BlackboardShaderInputOrder
- {
- public bool isKeyword;
- public bool isDropdown;
- public KeywordType keywordType;
- public ShaderKeyword builtInKeyword;
- public string deprecatedPropertyName;
- public int version;
- }
- class BlackboardShaderInputFactory
- {
- static public ShaderInput GetShaderInput(BlackboardShaderInputOrder order)
- {
- ShaderInput output;
- if (order.isKeyword)
- {
- if (order.builtInKeyword == null)
- {
- output = new ShaderKeyword(order.keywordType);
- }
- else
- {
- output = order.builtInKeyword;
- }
- }
- else if (order.isDropdown)
- {
- output = new ShaderDropdown();
- }
- else
- {
- switch (order.deprecatedPropertyName)
- {
- case "Color":
- output = new ColorShaderProperty(order.version);
- break;
- default:
- output = null;
- AssertHelpers.Fail("BlackboardShaderInputFactory: Unknown deprecated property type.");
- break;
- }
- }
-
- return output;
- }
- }
- class AddShaderInputAction : IGraphDataAction
- {
- public enum AddActionSource
- {
- Default,
- AddMenu
- }
-
- void AddShaderInput(GraphData graphData)
- {
- AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddShaderInputAction");
-
- // If type property is valid, create instance of that type
- if (blackboardItemType != null && blackboardItemType.IsSubclassOf(typeof(BlackboardItem)))
- {
- shaderInputReference = (BlackboardItem)Activator.CreateInstance(blackboardItemType, true);
- }
- else if (m_ShaderInputReferenceGetter != null)
- {
- shaderInputReference = m_ShaderInputReferenceGetter();
- }
- // If type is null a direct override object must have been provided or else we are in an error-state
- else if (shaderInputReference == null)
- {
- AssertHelpers.Fail("BlackboardController: Unable to complete Add Shader Input action.");
- return;
- }
-
- shaderInputReference.generatePropertyBlock = shaderInputReference.isExposable;
-
- if (graphData.owner != null)
- graphData.owner.RegisterCompleteObjectUndo("Add Shader Input");
- else
- AssertHelpers.Fail("GraphObject is null while carrying out AddShaderInputAction");
-
- graphData.AddGraphInput(shaderInputReference);
-
- // If no categoryToAddItemToGuid is provided, add the input to the default category
- if (categoryToAddItemToGuid == String.Empty)
- {
- var defaultCategory = graphData.categories.FirstOrDefault();
- AssertHelpers.IsNotNull(defaultCategory, "Default category reference is null.");
- if (defaultCategory != null)
- {
- var addItemToCategoryAction = new AddItemToCategoryAction();
- addItemToCategoryAction.categoryGuid = defaultCategory.categoryGuid;
- addItemToCategoryAction.itemToAdd = shaderInputReference;
- graphData.owner.graphDataStore.Dispatch(addItemToCategoryAction);
- }
- }
- else
- {
- var addItemToCategoryAction = new AddItemToCategoryAction();
- addItemToCategoryAction.categoryGuid = categoryToAddItemToGuid;
- addItemToCategoryAction.itemToAdd = shaderInputReference;
- graphData.owner.graphDataStore.Dispatch(addItemToCategoryAction);
- }
- }
-
- public static AddShaderInputAction AddDeprecatedPropertyAction(BlackboardShaderInputOrder order)
- {
- return new() { shaderInputReference = BlackboardShaderInputFactory.GetShaderInput(order), addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
- }
-
- public static AddShaderInputAction AddDropdownAction(BlackboardShaderInputOrder order)
- {
- return new() { shaderInputReference = BlackboardShaderInputFactory.GetShaderInput(order), addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
- }
-
- public static AddShaderInputAction AddKeywordAction(BlackboardShaderInputOrder order)
- {
- return new() { shaderInputReference = BlackboardShaderInputFactory.GetShaderInput(order), addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
- }
-
- public static AddShaderInputAction AddPropertyAction(Type shaderInputType)
- {
- return new() { blackboardItemType = shaderInputType, addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
- }
-
- public Action<GraphData> modifyGraphDataAction => AddShaderInput;
- // If this is a subclass of ShaderInput and is not null, then an object of this type is created to add to blackboard
- // If the type field above is null and this is provided, then it is directly used as the item to add to blackboard
- public BlackboardItem shaderInputReference { get; set; }
- public AddActionSource addInputActionType { get; set; }
- public string categoryToAddItemToGuid { get; set; } = String.Empty;
-
- Type blackboardItemType { get; set; }
-
- Func<BlackboardItem> m_ShaderInputReferenceGetter = null;
- }
-
- class ChangeGraphPathAction : IGraphDataAction
- {
- void ChangeGraphPath(GraphData graphData)
- {
- AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeGraphPathAction");
- graphData.path = NewGraphPath;
- }
-
- public Action<GraphData> modifyGraphDataAction => ChangeGraphPath;
-
- public string NewGraphPath { get; set; }
- }
-
- class CopyShaderInputAction : IGraphDataAction
- {
- void CopyShaderInput(GraphData graphData)
- {
- AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out CopyShaderInputAction");
- AssertHelpers.IsNotNull(shaderInputToCopy, "ShaderInputToCopy is null while carrying out CopyShaderInputAction");
-
- // Don't handle undo here as there are different contexts in which this action is used, that define the undo action
- // TODO: Perhaps a sign that each of those need to be made their own actions instead of conflating intent into a single action
-
- switch (shaderInputToCopy)
- {
- case AbstractShaderProperty property:
-
- insertIndex = Mathf.Clamp(insertIndex, -1, graphData.properties.Count() - 1);
- var copiedProperty = (AbstractShaderProperty)graphData.AddCopyOfShaderInput(property, insertIndex);
- if (copiedProperty != null) // some property types cannot be duplicated (unknown types)
- {
- // Update the property nodes that depends on the copied node
- foreach (var node in dependentNodeList)
- {
- if (node is PropertyNode propertyNode)
- {
- propertyNode.owner = graphData;
- propertyNode.property = copiedProperty;
- }
- }
- }
-
-
- copiedShaderInput = copiedProperty;
- break;
-
- case ShaderKeyword shaderKeyword:
- // InsertIndex gets passed in relative to the blackboard position of an item overall,
- // and not relative to the array sizes of the properties/keywords/dropdowns
- var keywordInsertIndex = insertIndex - graphData.properties.Count();
-
- keywordInsertIndex = Mathf.Clamp(keywordInsertIndex, -1, graphData.keywords.Count() - 1);
-
- // Don't duplicate built-in keywords within the same graph
- if (shaderKeyword.isBuiltIn && graphData.keywords.Any(p => p.referenceName == shaderInputToCopy.referenceName))
- return;
-
- var copiedKeyword = (ShaderKeyword)graphData.AddCopyOfShaderInput(shaderKeyword, keywordInsertIndex);
-
- // Update the keyword nodes that depends on the copied node
- foreach (var node in dependentNodeList)
- {
- if (node is KeywordNode propertyNode)
- {
- propertyNode.owner = graphData;
- propertyNode.keyword = copiedKeyword;
- }
- }
-
- copiedShaderInput = copiedKeyword;
- break;
-
- case ShaderDropdown shaderDropdown:
- // InsertIndex gets passed in relative to the blackboard position of an item overall,
- // and not relative to the array sizes of the properties/keywords/dropdowns
- var dropdownInsertIndex = insertIndex - graphData.properties.Count() - graphData.keywords.Count();
-
- dropdownInsertIndex = Mathf.Clamp(dropdownInsertIndex, -1, graphData.dropdowns.Count() - 1);
-
- var copiedDropdown = (ShaderDropdown)graphData.AddCopyOfShaderInput(shaderDropdown, dropdownInsertIndex);
-
- // Update the dropdown nodes that depends on the copied node
- foreach (var node in dependentNodeList)
- {
- if (node is DropdownNode propertyNode)
- {
- propertyNode.owner = graphData;
- propertyNode.dropdown = copiedDropdown;
- }
- }
-
- copiedShaderInput = copiedDropdown;
- break;
-
- default:
- throw new ArgumentOutOfRangeException();
- }
-
- if (copiedShaderInput != null)
- {
- // If specific category to copy to is provided, find and use it
- foreach (var category in graphData.categories)
- {
- if (category.categoryGuid == containingCategoryGuid)
- {
- // Ensures that the new item gets added after the item it was duplicated from
- insertIndex += 1;
- // If the source item was already the last item in list, just add to end of list
- if (insertIndex >= category.childCount)
- insertIndex = -1;
- graphData.InsertItemIntoCategory(category.objectId, copiedShaderInput, insertIndex);
- return;
- }
- }
-
- // Else, add to default category
- graphData.categories.First().InsertItemIntoCategory(copiedShaderInput);
- }
- }
-
- public Action<GraphData> modifyGraphDataAction => CopyShaderInput;
-
- public IEnumerable<AbstractMaterialNode> dependentNodeList { get; set; } = new List<AbstractMaterialNode>();
-
- public BlackboardItem shaderInputToCopy { get; set; }
-
- public BlackboardItem copiedShaderInput { get; set; }
-
- public string containingCategoryGuid { get; set; }
-
- public int insertIndex { get; set; } = -1;
- }
-
- class AddCategoryAction : IGraphDataAction
- {
- void AddCategory(GraphData graphData)
- {
- AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddCategoryAction");
- graphData.owner.RegisterCompleteObjectUndo("Add Category");
- // If categoryDataReference is not null, directly add it to graphData
- if (categoryDataReference == null)
- categoryDataReference = new CategoryData(categoryName, childObjects);
- graphData.AddCategory(categoryDataReference);
- }
-
- public Action<GraphData> modifyGraphDataAction => AddCategory;
-
- // Direct reference to the categoryData to use if it is specified
- public CategoryData categoryDataReference { get; set; }
- public string categoryName { get; set; } = String.Empty;
- public List<ShaderInput> childObjects { get; set; }
- }
-
- class MoveCategoryAction : IGraphDataAction
- {
- void MoveCategory(GraphData graphData)
- {
- AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out MoveCategoryAction");
- graphData.owner.RegisterCompleteObjectUndo("Move Category");
- // Handling for out of range moves is slightly different, but otherwise we need to reverse for insertion order.
- var guids = newIndexValue >= graphData.categories.Count() ? categoryGuids : categoryGuids.Reverse<string>();
- foreach (var guid in categoryGuids)
- {
- var cat = graphData.categories.FirstOrDefault(c => c.categoryGuid == guid);
- graphData.MoveCategory(cat, newIndexValue);
- }
- }
-
- public Action<GraphData> modifyGraphDataAction => MoveCategory;
-
- // Reference to the shader input being modified
- internal List<string> categoryGuids { get; set; }
-
- internal int newIndexValue { get; set; }
- }
-
- class AddItemToCategoryAction : IGraphDataAction
- {
- public enum AddActionSource
- {
- Default,
- DragDrop
- }
-
- void AddItemsToCategory(GraphData graphData)
- {
- AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddItemToCategoryAction");
- graphData.owner.RegisterCompleteObjectUndo("Add Item to Category");
- graphData.InsertItemIntoCategory(categoryGuid, itemToAdd, indexToAddItemAt);
- }
-
- public Action<GraphData> modifyGraphDataAction => AddItemsToCategory;
-
- public string categoryGuid { get; set; }
-
- public ShaderInput itemToAdd { get; set; }
-
- // By default an item is always added to the end of a category, if this value is set to something other than -1, will insert the item at that position within the category
- public int indexToAddItemAt { get; set; } = -1;
-
- public AddActionSource addActionSource { get; set; }
- }
-
- class CopyCategoryAction : IGraphDataAction
- {
- void CopyCategory(GraphData graphData)
- {
- AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out CopyCategoryAction");
- AssertHelpers.IsNotNull(categoryToCopyReference, "CategoryToCopyReference is null while carrying out CopyCategoryAction");
-
- // This is called by MaterialGraphView currently, no need to repeat it here, though ideally it would live here
- //graphData.owner.RegisterCompleteObjectUndo("Copy Category");
-
- newCategoryDataReference = graphData.CopyCategory(categoryToCopyReference);
- }
-
- // Reference to the new category created as a copy
- public CategoryData newCategoryDataReference { get; set; }
-
- // After category has been copied, store reference to it
- public CategoryData categoryToCopyReference { get; set; }
-
- public Action<GraphData> modifyGraphDataAction => CopyCategory;
- }
-
- class ShaderVariantLimitAction : IGraphDataAction
- {
- public int currentVariantCount { get; set; } = 0;
- public int maxVariantCount { get; set; } = 0;
-
- public ShaderVariantLimitAction(int currentVariantCount, int maxVariantCount)
- {
- this.maxVariantCount = maxVariantCount;
- this.currentVariantCount = currentVariantCount;
- }
-
- // There's no action actually performed on the graph, but we need to implement this as a valid function
- public Action<GraphData> modifyGraphDataAction => Empty;
-
- void Empty(GraphData graphData)
- {
- }
- }
-
- class BlackboardController : SGViewController<GraphData, BlackboardViewModel>
- {
- // Type changes (adds/removes of Types) only happen after a full assembly reload so its safe to make this static
- static IList<Type> s_ShaderInputTypes;
-
- static BlackboardController()
- {
- var shaderInputTypes = TypeCache.GetTypesWithAttribute<BlackboardInputInfo>().ToList();
- // Sort the ShaderInput by priority using the BlackboardInputInfo attribute
- shaderInputTypes.Sort((s1, s2) =>
- {
- var info1 = Attribute.GetCustomAttribute(s1, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
- var info2 = Attribute.GetCustomAttribute(s2, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
-
- if (info1.priority == info2.priority)
- return (info1.name ?? s1.Name).CompareTo(info2.name ?? s2.Name);
- else
- return info1.priority.CompareTo(info2.priority);
- });
-
- s_ShaderInputTypes = shaderInputTypes.ToList();
- }
-
- BlackboardCategoryController m_DefaultCategoryController = null;
- Dictionary<string, BlackboardCategoryController> m_BlackboardCategoryControllers = new Dictionary<string, BlackboardCategoryController>();
-
- protected SGBlackboard m_Blackboard;
-
- internal SGBlackboard blackboard
- {
- get => m_Blackboard;
- private set => m_Blackboard = value;
- }
- public string GetFirstSelectedCategoryGuid()
- {
- if (m_Blackboard == null)
- {
- return string.Empty;
- }
- var copiedSelectionList = new List<ISelectable>(m_Blackboard.selection);
- var selectedCategories = new List<SGBlackboardCategory>();
- var selectedCategoryGuid = String.Empty;
- for (int i = 0; i < copiedSelectionList.Count; i++)
- {
- var selectable = copiedSelectionList[i];
- if (selectable is SGBlackboardCategory category)
- {
- selectedCategories.Add(selectable as SGBlackboardCategory);
- }
- }
- if (selectedCategories.Any())
- {
- selectedCategoryGuid = selectedCategories[0].viewModel.associatedCategoryGuid;
- }
- return selectedCategoryGuid;
- }
-
- void InitializeViewModel(bool useDropdowns)
- {
- // Clear the view model
- ViewModel.ResetViewModelData();
- ViewModel.subtitle = BlackboardUtils.FormatPath(Model.path);
- BlackboardShaderInputOrder propertyTypesOrder = new BlackboardShaderInputOrder();
-
- // Property data first
- foreach (var shaderInputType in s_ShaderInputTypes)
- {
- if (shaderInputType.IsAbstract)
- continue;
-
- var info = Attribute.GetCustomAttribute(shaderInputType, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
- string name = info?.name ?? ObjectNames.NicifyVariableName(shaderInputType.Name.Replace("ShaderProperty", ""));
-
- // QUICK FIX TO DEAL WITH DEPRECATED COLOR PROPERTY
- if (name.Equals("Color", StringComparison.InvariantCultureIgnoreCase) && ShaderGraphPreferences.allowDeprecatedBehaviors)
- {
- propertyTypesOrder.isKeyword = false;
- propertyTypesOrder.deprecatedPropertyName = name;
- propertyTypesOrder.version = ColorShaderProperty.deprecatedVersion;
- ViewModel.propertyNameToAddActionMap.Add($"Color (Legacy v0)", AddShaderInputAction.AddDeprecatedPropertyAction(propertyTypesOrder));
- ViewModel.propertyNameToAddActionMap.Add(name, AddShaderInputAction.AddPropertyAction(shaderInputType));
- }
- else
- ViewModel.propertyNameToAddActionMap.Add(name, AddShaderInputAction.AddPropertyAction(shaderInputType));
- }
-
- // Default Keywords next
- BlackboardShaderInputOrder keywordTypesOrder = new BlackboardShaderInputOrder();
- keywordTypesOrder.isKeyword = true;
- keywordTypesOrder.keywordType = KeywordType.Boolean;
- ViewModel.defaultKeywordNameToAddActionMap.Add("Boolean", AddShaderInputAction.AddKeywordAction(keywordTypesOrder));
- keywordTypesOrder.keywordType = KeywordType.Enum;
- ViewModel.defaultKeywordNameToAddActionMap.Add("Enum", AddShaderInputAction.AddKeywordAction(keywordTypesOrder));
-
- // Built-In Keywords after that
- foreach (var builtinKeywordDescriptor in KeywordUtil.GetBuiltinKeywordDescriptors())
- {
- var keyword = ShaderKeyword.CreateBuiltInKeyword(builtinKeywordDescriptor);
- // Do not allow user to add built-in keywords that conflict with user-made keywords that have the same reference name or display name
- if (Model.keywords.Any(x => x.referenceName == keyword.referenceName || x.displayName == keyword.displayName))
- {
- ViewModel.disabledKeywordNameList.Add(keyword.displayName);
- }
- else
- {
- keywordTypesOrder.builtInKeyword = (ShaderKeyword)keyword.Copy();
- ViewModel.builtInKeywordNameToAddActionMap.Add(keyword.displayName, AddShaderInputAction.AddKeywordAction(keywordTypesOrder));
- }
- }
-
- if (useDropdowns)
- {
- BlackboardShaderInputOrder dropdownsOrder = new BlackboardShaderInputOrder();
- dropdownsOrder.isDropdown = true;
- ViewModel.defaultDropdownNameToAdd = new Tuple<string, IGraphDataAction>("Dropdown", AddShaderInputAction.AddDropdownAction(dropdownsOrder));
- }
-
- // Category data last
- var defaultNewCategoryReference = new CategoryData("Category");
- ViewModel.addCategoryAction = new AddCategoryAction() { categoryDataReference = defaultNewCategoryReference };
-
- ViewModel.requestModelChangeAction = this.RequestModelChange;
- ViewModel.categoryInfoList.AddRange(DataStore.State.categories.ToList());
- }
-
- internal BlackboardController(GraphData model, BlackboardViewModel inViewModel, GraphDataStore graphDataStore)
- : base(model, inViewModel, graphDataStore)
- {
- // TODO: hide this more generically for category types.
- bool useDropdowns = model.isSubGraph;
- InitializeViewModel(useDropdowns);
-
- blackboard = new SGBlackboard(ViewModel, this);
-
- // Add default category at the top of the blackboard (create it if it doesn't exist already)
- var existingDefaultCategory = DataStore.State.categories.FirstOrDefault();
- if (existingDefaultCategory != null && existingDefaultCategory.IsNamedCategory() == false)
- {
- AddBlackboardCategory(graphDataStore, existingDefaultCategory);
- }
- else
- {
- // Any properties that don't already have a category (for example, if this graph is being loaded from an older version that doesn't have category data)
- var uncategorizedBlackboardItems = new List<ShaderInput>();
- foreach (var shaderProperty in DataStore.State.properties)
- if (IsInputUncategorized(shaderProperty))
- uncategorizedBlackboardItems.Add(shaderProperty);
-
- foreach (var shaderKeyword in DataStore.State.keywords)
- if (IsInputUncategorized(shaderKeyword))
- uncategorizedBlackboardItems.Add(shaderKeyword);
-
- if (useDropdowns)
- {
- foreach (var shaderDropdown in DataStore.State.dropdowns)
- if (IsInputUncategorized(shaderDropdown))
- uncategorizedBlackboardItems.Add(shaderDropdown);
- }
-
- var addCategoryAction = new AddCategoryAction();
- addCategoryAction.categoryDataReference = CategoryData.DefaultCategory(uncategorizedBlackboardItems);
- graphDataStore.Dispatch(addCategoryAction);
- }
-
- // Get the reference to default category controller after its been added
- m_DefaultCategoryController = m_BlackboardCategoryControllers.Values.FirstOrDefault();
- AssertHelpers.IsNotNull(m_DefaultCategoryController, "Failed to instantiate default category.");
-
- // Handle loaded-in categories from graph first, skipping the first/default category
- foreach (var categoryData in ViewModel.categoryInfoList.Skip(1))
- {
- AddBlackboardCategory(graphDataStore, categoryData);
- }
- }
-
- internal string editorPrefsBaseKey => "unity.shadergraph." + DataStore.State.objectId;
-
- BlackboardCategoryController AddBlackboardCategory(GraphDataStore graphDataStore, CategoryData categoryInfo)
- {
- var blackboardCategoryViewModel = new BlackboardCategoryViewModel();
- blackboardCategoryViewModel.parentView = blackboard;
- blackboardCategoryViewModel.requestModelChangeAction = ViewModel.requestModelChangeAction;
- blackboardCategoryViewModel.name = categoryInfo.name;
- blackboardCategoryViewModel.associatedCategoryGuid = categoryInfo.categoryGuid;
- blackboardCategoryViewModel.isExpanded = EditorPrefs.GetBool($"{editorPrefsBaseKey}.{categoryInfo.categoryGuid}.{ChangeCategoryIsExpandedAction.kEditorPrefKey}", true);
-
- var blackboardCategoryController = new BlackboardCategoryController(categoryInfo, blackboardCategoryViewModel, graphDataStore);
- if (m_BlackboardCategoryControllers.ContainsKey(categoryInfo.categoryGuid) == false)
- {
- m_BlackboardCategoryControllers.Add(categoryInfo.categoryGuid, blackboardCategoryController);
- m_DefaultCategoryController = m_BlackboardCategoryControllers.Values.FirstOrDefault();
- }
- else
- {
- AssertHelpers.Fail("Failed to add category controller due to category with same GUID already having been added.");
- return null;
- }
- return blackboardCategoryController;
- }
-
- // Creates controller, view and view model for a blackboard item and adds the view to the specified index in the category
- SGBlackboardRow InsertBlackboardRow(BlackboardItem shaderInput, int insertionIndex = -1)
- {
- return m_DefaultCategoryController.InsertBlackboardRow(shaderInput, insertionIndex);
- }
-
- public void UpdateBlackboardTitle(string newTitle)
- {
- ViewModel.title = newTitle;
- blackboard.title = ViewModel.title;
- }
-
- protected override void RequestModelChange(IGraphDataAction changeAction)
- {
- DataStore.Dispatch(changeAction);
- }
-
- // Called by GraphDataStore.Subscribe after the model has been changed
- protected override void ModelChanged(GraphData graphData, IGraphDataAction changeAction)
- {
- // Reconstruct view-model first
- // TODO: hide this more generically for category types.
- bool useDropdowns = graphData.isSubGraph;
- InitializeViewModel(useDropdowns);
-
- var graphView = ViewModel.parentView as MaterialGraphView;
-
- switch (changeAction)
- {
- // If newly added input doesn't belong to any of the user-made categories, add it to the default category at top of blackboard
- case AddShaderInputAction addBlackboardItemAction:
- if (IsInputUncategorized(addBlackboardItemAction.shaderInputReference))
- {
- var blackboardRow = InsertBlackboardRow(addBlackboardItemAction.shaderInputReference);
- if (blackboardRow != null)
- {
- var propertyView = blackboardRow.Q<SGBlackboardField>();
- if (addBlackboardItemAction.addInputActionType == AddShaderInputAction.AddActionSource.AddMenu)
- propertyView.OpenTextEditor();
- }
- }
- break;
- // Need to handle deletion of shader inputs here as opposed to BlackboardCategoryController, as currently,
- // once removed from the categories there is no way to associate an input with the category that owns it
- case DeleteShaderInputAction deleteShaderInputAction:
- foreach (var shaderInput in deleteShaderInputAction.shaderInputsToDelete)
- RemoveInputFromBlackboard(shaderInput);
- break;
-
- case HandleUndoRedoAction handleUndoRedoAction:
- ClearBlackboardCategories();
-
- foreach (var categoryData in graphData.addedCategories)
- AddBlackboardCategory(DataStore, categoryData);
-
- m_DefaultCategoryController = m_BlackboardCategoryControllers.Values.FirstOrDefault();
-
- break;
- case CopyShaderInputAction copyShaderInputAction:
- // In the specific case of only-one keywords like Material Quality and Raytracing, they can get copied, but because only one can exist, the output copied value is null
- if (copyShaderInputAction.copiedShaderInput != null && IsInputUncategorized(copyShaderInputAction.copiedShaderInput))
- {
- var blackboardRow = InsertBlackboardRow(copyShaderInputAction.copiedShaderInput, copyShaderInputAction.insertIndex);
- var propertyView = blackboardRow.Q<SGBlackboardField>();
- graphView?.AddToSelectionNoUndoRecord(propertyView);
- }
-
- break;
-
- case AddCategoryAction addCategoryAction:
- AddBlackboardCategory(DataStore, addCategoryAction.categoryDataReference);
- // Iterate through anything that is selected currently
- foreach (var selectedElement in blackboard.selection.ToList())
- {
- if (selectedElement is SGBlackboardField { userData: ShaderInput shaderInput })
- {
- // If a blackboard item is selected, first remove it from the blackboard
- RemoveInputFromBlackboard(shaderInput);
-
- // Then add input to the new category
- var addItemToCategoryAction = new AddItemToCategoryAction();
- addItemToCategoryAction.categoryGuid = addCategoryAction.categoryDataReference.categoryGuid;
- addItemToCategoryAction.itemToAdd = shaderInput;
- DataStore.Dispatch(addItemToCategoryAction);
- }
- }
- break;
-
- case DeleteCategoryAction deleteCategoryAction:
- // Clean up deleted categories
- foreach (var categoryGUID in deleteCategoryAction.categoriesToRemoveGuids)
- {
- RemoveBlackboardCategory(categoryGUID);
- }
- break;
-
- case MoveCategoryAction moveCategoryAction:
- ClearBlackboardCategories();
- foreach (var categoryData in ViewModel.categoryInfoList)
- AddBlackboardCategory(graphData.owner.graphDataStore, categoryData);
- break;
-
- case CopyCategoryAction copyCategoryAction:
- var blackboardCategory = AddBlackboardCategory(graphData.owner.graphDataStore, copyCategoryAction.newCategoryDataReference);
- if (blackboardCategory != null)
- graphView?.AddToSelectionNoUndoRecord(blackboardCategory.blackboardCategoryView);
- break;
- case ShaderVariantLimitAction shaderVariantLimitAction:
- blackboard.SetCurrentVariantUsage(shaderVariantLimitAction.currentVariantCount, shaderVariantLimitAction.maxVariantCount);
- break;
- }
-
- // Lets all event handlers this controller owns/manages know that the model has changed
- // Usually this is to update views and make them reconstruct themself from updated view-model
- //NotifyChange(changeAction);
-
- // Let child controllers know about changes to this controller so they may update themselves in turn
- //ApplyChanges();
- }
-
- void RemoveInputFromBlackboard(ShaderInput shaderInput)
- {
- // Check if input is in one of the categories
- foreach (var controller in m_BlackboardCategoryControllers.Values)
- {
- var blackboardRow = controller.FindBlackboardRow(shaderInput);
- if (blackboardRow != null)
- {
- controller.RemoveBlackboardRow(shaderInput);
- return;
- }
- }
- }
-
- bool IsInputUncategorized(ShaderInput shaderInput)
- {
- // Skip the first category controller as that is guaranteed to be the default category
- foreach (var categoryController in m_BlackboardCategoryControllers.Values.Skip(1))
- {
- if (categoryController.IsInputInCategory(shaderInput))
- return false;
- }
-
- return true;
- }
-
- public SGBlackboardCategory GetBlackboardCategory(string inputGuid)
- {
- foreach (var categoryController in m_BlackboardCategoryControllers.Values)
- {
- if (categoryController.Model.categoryGuid == inputGuid)
- return categoryController.blackboardCategoryView;
- }
-
- return null;
- }
-
- public SGBlackboardRow GetBlackboardRow(ShaderInput blackboardItem)
- {
- foreach (var categoryController in m_BlackboardCategoryControllers.Values)
- {
- var blackboardRow = categoryController.FindBlackboardRow(blackboardItem);
- if (blackboardRow != null)
- return blackboardRow;
- }
-
- return null;
- }
-
- int numberOfCategories => m_BlackboardCategoryControllers.Count;
-
- // Gets the index after the currently selected shader input for pasting properties into this graph
- internal int GetInsertionIndexForPaste()
- {
- if (blackboard?.selection == null || blackboard.selection.Count == 0)
- {
- return 0;
- }
-
- foreach (ISelectable selection in blackboard.selection)
- {
- if (selection is SGBlackboardField blackboardPropertyView)
- {
- SGBlackboardRow row = blackboardPropertyView.GetFirstAncestorOfType<SGBlackboardRow>();
- SGBlackboardCategory category = blackboardPropertyView.GetFirstAncestorOfType<SGBlackboardCategory>();
- if (row == null || category == null)
- continue;
- int blackboardFieldIndex = category.IndexOf(row);
-
- return blackboardFieldIndex;
- }
- }
-
- return 0;
- }
-
- void RemoveBlackboardCategory(string categoryGUID)
- {
- m_BlackboardCategoryControllers.TryGetValue(categoryGUID, out var blackboardCategoryController);
- if (blackboardCategoryController != null)
- {
- blackboardCategoryController.Dispose();
- m_BlackboardCategoryControllers.Remove(categoryGUID);
- }
- else
- AssertHelpers.Fail("Tried to remove a category that doesn't exist. ");
- }
-
- public override void Dispose()
- {
- if (m_Blackboard == null)
- return;
-
- base.Dispose();
- m_DefaultCategoryController = null;
- ClearBlackboardCategories();
-
- m_Blackboard?.Dispose();
- m_Blackboard = null;
- }
-
- void ClearBlackboardCategories()
- {
- foreach (var categoryController in m_BlackboardCategoryControllers.Values)
- {
- categoryController.Dispose();
- }
- m_BlackboardCategoryControllers.Clear();
- }
-
- // Meant to be used by UI testing in order to clear blackboard state
- internal void ResetBlackboardState()
- {
- ClearBlackboardCategories();
- var addCategoryAction = new AddCategoryAction();
- addCategoryAction.categoryDataReference = CategoryData.DefaultCategory();
- DataStore.Dispatch(addCategoryAction);
- }
- }
- }
|