diff options
Diffstat (limited to 'VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors')
43 files changed, 3595 insertions, 0 deletions
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components.meta new file mode 100644 index 00000000..1352cc98 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13fa33428f9f4a84b8e3dc4bfedd6e64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs new file mode 100644 index 00000000..101adaa6 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs @@ -0,0 +1,651 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; +using Poiyomi.ModularShaderSystem.UI; + +namespace Poiyomi.ModularShaderSystem.Debug +{ + public class FunctionTimeline : IModularShaderDebuggerTab + { + public VisualElement TabContainer { get; set; } + public string TabName { get; set; } + + public FunctionTimeline() + { + TabName = "Function Timeline"; + TabContainer = new VisualElement(); + + var styleSheet = Resources.Load<StyleSheet>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/FunctionTimelineStyle"); + TabContainer.styleSheets.Add(styleSheet); + TabContainer.style.flexGrow = 1; + } + + public void UpdateTab(ModularShader shader) + { + TabContainer.Clear(); + if (shader == null) return; + TabContainer.Add(new TimelineContainer(shader)); + } + } + + internal class FunctionItem : VisualElement + { + public ShaderFunction Function { get; } + public TimelineRow Row { get; set; } + public int Size { get; } + public int Offset { get; } + + private TimelineRoot _root; + + public FunctionItem(TimelineRoot root, ShaderFunction function, int size, int offset) + { + _root = root; + Function = function; + Size = size; + Offset = offset; + + style.left = Offset; + style.width = Size; + + var label = new Label(Function.Name); + label.AddToClassList("function-header-name"); + var queue = new Label("" + Function.Queue); + queue.AddToClassList("function-header-queue"); + Add(label); + Add(queue); + + RegisterCallback<MouseUpEvent>(evt => + { + if (evt.button != 0) return; + _root.SelectedFunction = this; + }); + } + } + + internal class TimelineRow : VisualElement + { + public ShaderModule Module { get; } + + public List<FunctionItem> Functions { get; set; } + + private VisualElement _title; + public VisualElement _content; + public List<VisualElement> _contentChilden; + + public TimelineRow(ShaderModule module) + { + Module = module; + + Functions = new List<FunctionItem>(); + _contentChilden = new List<VisualElement>(); + + _title = new VisualElement(); + _content = new VisualElement(); + _title.AddToClassList("timeline-title"); + _content.AddToClassList("timeline-content"); + + _title.Add(new Label(Module.Name)); + + Add(_content); + Add(_title); + } + + public void SetContentSize(int size) + { + _content.style.width = size; + } + + public void ApplyFunctionsToTimeline() + { + _content.Clear(); + _contentChilden.Clear(); + + for (int index = Functions.Count - 1; index >= 0; index--) + { + FunctionItem function = Functions[index]; + int counter = 0; + bool found = false; + var functionStart = function.Offset; + var functionEnd = function.Offset + function.Size; + while (counter < _contentChilden.Count) + { + if (!TimelineBusyAt(_contentChilden[counter], functionStart, functionEnd)) + { + _contentChilden[counter].Add(function); + function.Row = this; + found = true; + break; + } + counter++; + } + if (found) continue; + var newRow = new VisualElement(); + newRow.AddToClassList("timeline-content-row"); + _contentChilden.Add(newRow); + _content.Add(newRow); + newRow.Add(function); + function.Row = this; + } + } + + private static bool TimelineBusyAt(VisualElement content, int functionStart, int functionEnd) + { + return content.Children().Cast<FunctionItem>().Any(x => functionStart < x.Offset + x.Size && functionStart > x.Offset && functionEnd <= x.Offset + x.Size); + } + + public void SetRowsHeight() + { + foreach (VisualElement element in _contentChilden) + element.style.height = element[0].resolvedStyle.height + 8; + } + } + + internal class TimelineRoot : VisualElement + { + public List<TimelineRow> Rows { get; set; } + public List<FunctionItem> Functions { get; set; } + + public FunctionItem SelectedFunction + { + get => _selectedFunction; + set + { + _selectedFunction = value; + ResetSelectedClass(); + OnSelectedFunctionChanged?.Invoke(_selectedFunction); + } + } + + public string Keyword { get; } + + public Action<FunctionItem> OnSelectedFunctionChanged { get; set; } + public Action OnTimelineFirstDispatch { get; set; } + + private readonly Dictionary<ShaderFunction, ShaderModule> _moduleByFunction; + private FunctionItem _selectedFunction; + + public TimelineRoot(Dictionary<ShaderFunction, ShaderModule> moduleByFunction, List<ShaderFunction> allFunctions, string rootKeyword) + { + Keyword = rootKeyword; + Rows = new List<TimelineRow>(); + Functions = new List<FunctionItem>(); + int offset = 0; + _moduleByFunction = moduleByFunction; + foreach (var fn in allFunctions.Where(x => x.AppendAfter.Equals(rootKeyword)).OrderBy(x => x.Queue)) + { + offset += CreateFunctionHierarchy(allFunctions, fn, offset); + } + + foreach (TimelineRow row in Rows) + { + Add(row); + row.SetContentSize(offset + 30); + row.ApplyFunctionsToTimeline(); + } + + RegisterCallback<GeometryChangedEvent>(GeometryChangedCallback); + } + + private void GeometryChangedCallback(GeometryChangedEvent evt) + { + UnregisterCallback<GeometryChangedEvent>(GeometryChangedCallback); + + foreach (TimelineRow row in Rows) + { + row.SetRowsHeight(); + } + + OnTimelineFirstDispatch?.Invoke(); + } + + private int CreateFunctionHierarchy(List<ShaderFunction> functions, ShaderFunction function, int offset) + { + int size = 0; + var module = _moduleByFunction[function]; + var row = Rows.FirstOrDefault(x => x.Module == module); + if (row == null) + { + row = new TimelineRow(module); + Rows.Add(row); + } + + foreach (var fn in functions.Where(x => x.AppendAfter.Equals(function.Name)).OrderBy(x => x.Queue)) + size += CreateFunctionHierarchy(functions, fn, offset + size + 30); + + if (size == 0) size = 224; + else size += 30; + + var functionItem = new FunctionItem(this, function, size - 4, offset); + row.Functions.Add(functionItem); + Functions.Add(functionItem); + + return size; + } + + public float GetScrollAdjustment() + { + float factor = 1.1f; + if (Rows.Count <= 0) return factor; + + var width = Rows[0]._content.style.width.value.value; + var screenWidth = Rows[0]._content.resolvedStyle.width; + factor = screenWidth / width; + + return factor; + } + + public void Scroll(float f) + { + foreach (TimelineRow row in Rows) + { + var width = row._content.style.width.value.value; + var screenWidth = row._content.resolvedStyle.width; + if(width > screenWidth) + row._content.style.left = -((width- screenWidth) * f); + } + } + + public void ResetSelectedClass() + { + foreach (FunctionItem function in Functions) + { + if (function == SelectedFunction && !function.ClassListContains("selected-function")) + function.AddToClassList("selected-function"); + else if (function != SelectedFunction && function.ClassListContains("selected-function")) + function.RemoveFromClassList("selected-function"); + } + } + } + + internal class VariablesViewer : VisualElement + { + public Action<Variable> OnVariableSelected { get; set; } + + private List<VariableField> _variables; + + public VariablesViewer(ModularShader shader) + { + var variables = shader.BaseModules.Concat(shader.AdditionalModules).SelectMany(x => x.Functions).SelectMany(x => x.UsedVariables).Distinct().OrderBy(x => x.Type).ThenBy(x => x.Name); + + var title = new Label("Variables List"); + title.AddToClassList("area-title"); + var content = new ScrollView(ScrollViewMode.Vertical); + content.AddToClassList("area-content"); + + _variables = new List<VariableField>(); + + foreach (Variable variable in variables) + { + var element = new VariableField(variable); + _variables.Add(element); + content.Add(element); + + element.RegisterCallback<MouseUpEvent>(evt => + { + if (evt.button != 0) return; + foreach (VariableField field in _variables) + { + if (field.ClassListContains("selected-variable-global")) + field.RemoveFromClassList("selected-variable-global"); + } + + element.AddToClassList("selected-variable-global"); + OnVariableSelected?.Invoke(element.Variable); + }); + } + + Add(title); + Add(content); + } + } + + internal class FunctionViewer : VisualElement + { + public ShaderFunction SelectedItem + { + get => _selectedItem; + set + { + _selectedItem = value; + _appendAfter.Value = _selectedItem?.AppendAfter; + _name.Value = _selectedItem?.Name; + _queue.Value = "" + _selectedItem?.Queue; + + _variables.Clear(); + _variablesFoldout.Clear(); + _variableKeywordsFoldout.Clear(); + _codeKeywordsFoldout.Clear(); + if (_selectedItem == null) + { + OnVariableSelected?.Invoke(null); + return; + } + + foreach (Variable variable in _selectedItem.UsedVariables) + { + var element = new VariableField(variable); + _variables.Add(element); + _variablesFoldout.Add(element); + + element.RegisterCallback<MouseUpEvent>(evt => + { + if (evt.button != 0) return; + foreach (VariableField field in _variables) + { + if (field.ClassListContains("selected-variable")) + field.RemoveFromClassList("selected-variable"); + } + element.AddToClassList("selected-variable"); + OnVariableSelected?.Invoke(element.Variable); + }); + } + + + foreach (string keyword in _selectedItem.VariableKeywords) + _variableKeywordsFoldout.Add(new Label(keyword)); + if(_variableKeywordsFoldout.childCount == 0) + _variableKeywordsFoldout.Add(new Label("None")); + + foreach (string keyword in _selectedItem.CodeKeywords) + _codeKeywordsFoldout.Add(new Label(keyword)); + if(_codeKeywordsFoldout.childCount == 0) + _codeKeywordsFoldout.Add(new Label("None")); + } + } + + public Action<Variable> OnVariableSelected { get; set; } + + private LabelField _appendAfter; + private LabelField _name; + private LabelField _queue; + private ShaderFunction _selectedItem; + private Foldout _variablesFoldout; + private List<VariableField> _variables; + private readonly Foldout _variableKeywordsFoldout; + private readonly Foldout _codeKeywordsFoldout; + + public FunctionViewer() + { + var title = new Label("Selected function information"); + title.AddToClassList("area-title"); + var content = new ScrollView(ScrollViewMode.Vertical); + content.AddToClassList("area-content"); + + _name = new LabelField("Name", ""); + _appendAfter = new LabelField("Append After", ""); + _queue = new LabelField("Queue", ""); + + _variablesFoldout = new Foldout(); + _variablesFoldout.text = "Variables"; + _variables = new List<VariableField>(); + + _variableKeywordsFoldout = new Foldout(); + _variableKeywordsFoldout.text = "Variable Keywords"; + + _codeKeywordsFoldout = new Foldout(); + _codeKeywordsFoldout.text = "Code Keywords"; + + Add(title); + Add(content); + content.Add(_name); + content.Add(_appendAfter); + content.Add(_queue); + content.Add(_variablesFoldout); + content.Add(_variableKeywordsFoldout); + content.Add(_codeKeywordsFoldout); + } + } + + internal class ModuleViewer : VisualElement + { + public ShaderModule SelectedItem + { + get => _selectedItem; + set + { + _selectedItem = value; + if (_selectedItem == null) + { + _name.Value = null; + _id.Value = null; + _description.Value =null; + _author.Value = null; + _version.Value = null; + if(_content.Contains(_selectButton)) _content.Remove(_selectButton); + return; + } + _name.Value = _selectedItem.Name; + _id.Value = _selectedItem.Id; + _description.Value = _selectedItem.Description; + _author.Value = _selectedItem.Author; + _version.Value = _selectedItem.Version; + + if(!_content.Contains(_selectButton)) _content.Add(_selectButton); + } + } + + private ShaderModule _selectedItem; + private readonly LabelField _name; + private readonly LabelField _id; + private readonly LabelField _author; + private readonly LabelField _description; + private readonly LabelField _version; + private Button _selectButton; + private ScrollView _content; + + public ModuleViewer() + { + var title = new Label("Function's module base info"); + title.AddToClassList("area-title"); + var content = new ScrollView(ScrollViewMode.Vertical); + _content = content; + _content.AddToClassList("area-content"); + + _name = new LabelField("Name", ""); + _id = new LabelField("Id", ""); + _author = new LabelField("Author", ""); + _description = new LabelField("Description", ""); + _version = new LabelField("Version", ""); + + _selectButton = new Button(() => + { + if (_selectedItem == null) return; + Selection.SetActiveObjectWithContext(_selectedItem,_selectedItem); + }); + + _selectButton.text = "Select module in inspector"; + + Add(title); + Add(_content); + + _content.Add(_name); + _content.Add(_id); + _content.Add(_author); + _content.Add(_description); + _content.Add(_version); + } + } + + internal class FunctionTemplateViewer : VisualElement + { + public string SelectedItem + { + get => _selectedItem; + set + { + _selectedItem = value; + _viewer.Text = _selectedItem; + } + } + + private CodeViewElement _viewer; + private string _selectedItem; + + public FunctionTemplateViewer() + { + _viewer = new CodeViewElement(); + + var title = new Label("Function code template"); + title.AddToClassList("area-title"); + + Add(title); + Add(_viewer); + } + } + + internal class TimelineContainer : VisualElement + { + private List<TimelineRoot> _roots; + private PopupField<TimelineRoot> _popup; + + public TimelineContainer(ModularShader shader) + { + var left = new VisualElement(); + var right = new VisualElement(); + var bot = new VisualElement(); + var templateViewer = new FunctionTemplateViewer(); + var variablesViewer = new VariablesViewer(shader); + var functionViewer = new FunctionViewer(); + var moduleViewer = new ModuleViewer(); + + left.style.flexShrink = 0; + left.style.width = new StyleLength(Length.Percent(70)); + right.style.width = new StyleLength(Length.Percent(30)); + bot.style.height = new StyleLength(Length.Percent(50)); + bot.style.flexDirection = FlexDirection.Row; + style.flexDirection = FlexDirection.Row; + Add(left); + Add(right); + + var scroller = new Scroller(0,1,f => + { + _popup.value.Scroll(f); + }, SliderDirection.Horizontal); + + _roots = new List<TimelineRoot>(); + var functions = new List<ShaderFunction>(); + Dictionary<ShaderFunction, ShaderModule> moduleByFunction = new Dictionary<ShaderFunction, ShaderModule>(); + + foreach (var module in shader.BaseModules) + { + functions.AddRange(module.Functions); + foreach (var function in module.Functions) + if (!moduleByFunction.ContainsKey(function)) + moduleByFunction.Add(function, module); + } + + foreach (var module in shader.AdditionalModules) + { + functions.AddRange(module.Functions); + foreach (var function in module.Functions) + if (!moduleByFunction.ContainsKey(function)) + moduleByFunction.Add(function, module); + } + + foreach (var keyword in functions.Select(x => x.AppendAfter).Distinct().Where(x => x.StartsWith("#K#"))) + { + var root = new TimelineRoot(moduleByFunction, functions, keyword); + root.OnSelectedFunctionChanged = item => + { + functionViewer.SelectedItem = item.Function; + moduleViewer.SelectedItem = item.Row.Module; + templateViewer.SelectedItem = item.Function.ShaderFunctionCode == null ? null : item.Function.ShaderFunctionCode.Template; + + variablesViewer.OnVariableSelected = variable => + { + foreach (FunctionItem f in root.Functions) + { + bool toHighlight = f.Function.UsedVariables.Any(x => x == variable); + + if(toHighlight && !f.ClassListContains("contains-variable-global")) + f.AddToClassList("contains-variable-global"); + if(!toHighlight && f.ClassListContains("contains-variable-global")) + f.RemoveFromClassList("contains-variable-global"); + } + }; + + functionViewer.OnVariableSelected = variable => + { + foreach (FunctionItem f in root.Functions) + { + bool toHighlight = f.Function.UsedVariables.Any(x => x == variable); + + if(toHighlight && !f.ClassListContains("contains-variable")) + f.AddToClassList("contains-variable"); + if(!toHighlight && f.ClassListContains("contains-variable")) + f.RemoveFromClassList("contains-variable"); + } + }; + + foreach (FunctionItem f in root.Functions) + { + if(f.ClassListContains("contains-variable")) + f.RemoveFromClassList("contains-variable"); + } + }; + root.OnTimelineFirstDispatch = () => + { + scroller.Adjust(root.GetScrollAdjustment()); + }; + _roots.Add(root); + } + + var timelineContent = new VisualElement(); + if (_roots.Count == 0) + { + Label label = new Label("No roots found"); + left.Add(label); + return; + } + timelineContent.Add(_roots[0]); + + variablesViewer.OnVariableSelected = variable => + { + foreach (FunctionItem f in _roots[0].Functions) + { + bool toHighlight = f.Function.UsedVariables.Any(x => x == variable); + + if(toHighlight && !f.ClassListContains("contains-variable-global")) + f.AddToClassList("contains-variable-global"); + if(!toHighlight && f.ClassListContains("contains-variable-global")) + f.RemoveFromClassList("contains-variable-global"); + } + }; + + var timelineScroll = new ScrollView(ScrollViewMode.Vertical); + timelineScroll.AddToClassList("timeline"); + timelineScroll.style.flexGrow = 1; + timelineScroll.Add(timelineContent); + + _popup = new PopupField<TimelineRoot>("Root keywords", _roots, 0, root => { return root.Keyword; }, root => { return root.Keyword; }); + _popup.RegisterValueChangedCallback(evt => + { + timelineContent.Clear(); + timelineContent.Add(evt.newValue); + functionViewer.SelectedItem = evt.newValue?.SelectedFunction?.Function; + moduleViewer.SelectedItem = evt.newValue?.SelectedFunction?.Row.Module; + templateViewer.SelectedItem = evt.newValue?.SelectedFunction?.Function.ShaderFunctionCode.Template; + scroller.Adjust(evt.newValue.GetScrollAdjustment()); + scroller.value = 0; + evt.newValue.Scroll(0); + }); + + scroller.Adjust(_popup.value.GetScrollAdjustment()); + + timelineScroll.Add(timelineContent); + + left.Add(_popup); + left.Add(timelineScroll); + left.Add(scroller); + left.Add(bot); + right.Add(templateViewer); + bot.Add(variablesViewer); + bot.Add(functionViewer); + bot.Add(moduleViewer); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs.meta new file mode 100644 index 00000000..0a9be293 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c2e7f0b3ef37bf24bb90cfc0bb1c6de0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs new file mode 100644 index 00000000..9bf8b430 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.Experimental.GraphView; +using UnityEngine; +using UnityEngine.UIElements; +using Poiyomi.ModularShaderSystem.UI; + + +namespace Poiyomi.ModularShaderSystem.Debug +{ + public class TemplateGraph : IModularShaderDebuggerTab + { + public VisualElement TabContainer { get; set; } + public string TabName { get; set; } + + internal TemplateGraphView _graph; + + public TemplateGraph() + { + TabName = "Template graph"; + TabContainer = new VisualElement(); + TabContainer.StretchToParentSize(); + var styleSheet = Resources.Load<StyleSheet>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/TemplateGraphStyle"); + TabContainer.styleSheets.Add(styleSheet); + } + + public void UpdateTab(ModularShader shader) + { + TabContainer.Clear(); + if (shader == null) return; + _graph = new TemplateGraphView(shader); + + + + TabContainer.Add(_graph); + } + } + + internal class TemplateGraphView : GraphView + { + public List<TemplateNode> Nodes; + public List<TemplateNode> BaseNodes; + + private List<ShaderModule> _modules; + private ModularShader _shader; + + private static TextPopup _popup; + + public TemplateGraphView(ModularShader shader) + { + Nodes = new List<TemplateNode>(); + BaseNodes = new List<TemplateNode>(); + + _modules = shader.BaseModules.Concat(shader.AdditionalModules).ToList(); + _shader = shader; + + SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale); + this.AddManipulator(new ContentDragger()); + var grid = new GridBackground(); + + Insert(0, grid); + grid.StretchToParentSize(); + this.StretchToParentSize(); + + AddBaseTemplateNode("Shader", _shader.ShaderTemplate); + + if (_shader.UseTemplatesForProperties) + { + var keywords = new []{"#K#" + MSSConstants.TEMPLATE_PROPERTIES_KEYWORD}; + AddBaseTemplateNode("ShaderPropertiesRoot", new TemplateAsset{ Template = "", Keywords = keywords, name = "Properties Template Root"}); + if (_shader.ShaderPropertiesTemplate != null) AddTemplateNode("ShaderPropertiesTemplate", _shader.ShaderTemplate, keywords); + + } + + var moduleByTemplate = new Dictionary<ModuleTemplate, ShaderModule>(); + foreach (var module in _shader.BaseModules.Concat(_shader.AdditionalModules)) + foreach (var template in module.Templates) + moduleByTemplate.Add(template, module); + + foreach (var template in _shader.BaseModules.Concat(_shader.AdditionalModules).SelectMany(x => x.Templates).OrderBy(x => x.Queue)) + { + if (template.Template == null) continue; + var module = moduleByTemplate[template]; + AddTemplateNode(module.Id, template); + } + + ScheduleNodesPositionReset(); + } + + public void AddBaseTemplateNode(string moduleId, TemplateAsset template) + { + var baseNode = new TemplateNode(moduleId, template, ""); + AddElement(baseNode); + Nodes.Add(baseNode); + BaseNodes.Add(baseNode); + } + + public void AddTemplateNode(string moduleId, ModuleTemplate template) + { + + var tempList = new List<TemplateNode>(); + foreach ((TemplateNode parent, string key) in Nodes.SelectMany(item => template.Keywords.Where(y => IsKeywordValid(moduleId, item, y)).Select(y => (item, y))).Where(x => !string.IsNullOrEmpty(x.Item2))) + { + var node = new TemplateNode(moduleId, template, key); + AddElement(node); + tempList.Add(node); + LinkTemplateNodes(parent, node, key); + } + Nodes.AddRange(tempList); + } + + public void AddTemplateNode(string moduleId, TemplateAsset template, string[] keywords) + { + var tempList = new List<TemplateNode>(); + foreach ((TemplateNode parent, string key) in Nodes.SelectMany(item => keywords.Where(y => IsKeywordValid(moduleId, item, y)).Select(y => (item, y))).Where(x => !string.IsNullOrEmpty(x.Item2))) + { + var node = new TemplateNode(moduleId, template, key); + AddElement(node); + tempList.Add(node); + LinkTemplateNodes(parent, node, key); + } + Nodes.AddRange(tempList); + } + + public void ScheduleNodesPositionReset() + { + RegisterCallback<GeometryChangedEvent>(GeometryChangedCallback); + } + + public override void BuildContextualMenu(ContextualMenuPopulateEvent evt) + { + var items = evt.menu.MenuItems(); + for (int i = 0; i < items.Count; i++) + evt.menu.RemoveItemAt(0); + + if (evt.target is TemplateNode node) + { + evt.menu.InsertAction(0,"View template code", action => + { + if (_popup != null) _popup.Close(); + _popup = ScriptableObject.CreateInstance<TextPopup>(); + _popup.Text = node.TemplateAsset.Template; + var position = GUIUtility.GUIToScreenRect(node.worldBound); + int lineCount = _popup.Text == null ? 5 : _popup.Text.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).Length; + _popup.ShowAsDropDown(position, new Vector2(600, Math.Min(lineCount * 16, 800))); + }); + if (node.ModuleId.Equals("Shader") || node.ModuleId.Equals("ShaderPropertiesRoot")) + { + evt.menu.InsertAction(1, "Select relative modular shader asset", action => + { + Selection.SetActiveObjectWithContext(_shader, _shader); + }); + } + else + { + evt.menu.InsertAction(1, "Select relative module asset", action => + { + var module = _modules.Find(x => x.Id.Equals(node.ModuleId)); + if(module != null) + Selection.SetActiveObjectWithContext(module, module); + }); + } + + } + } + + private void GeometryChangedCallback(GeometryChangedEvent evt) + { + UnregisterCallback<GeometryChangedEvent>(GeometryChangedCallback); + + if(BaseNodes.Count == 0) return; + + float top = 0; + foreach (TemplateNode node in BaseNodes) + top += node.Reposition(2, top, 100*(Nodes.Count/70 + 1)); + + var newPosition = new Vector3(BaseNodes[0].style.left.value.value + viewport.resolvedStyle.width/2 - BaseNodes[0].resolvedStyle.width/2, + -BaseNodes[0].style.top.value.value + viewport.resolvedStyle.height/2 - BaseNodes[0].resolvedStyle.height/2, + viewTransform.position.z); + viewTransform.position = newPosition; + } + + private static bool IsKeywordValid(string moduleId, TemplateNode item, string y) + { + if (item.ContainsKeyword("#K#"+y)) return true; + return item.ContainsKeyword("#KI#"+y) && moduleId.Equals(item.ModuleId); + } + + private void LinkTemplateNodes(TemplateNode left, TemplateNode right, string key) + { + var edge = new Edge + { + output = left.Outputs[key], + input = right.Input + }; + + edge.input.Connect(edge); + edge.output.Connect(edge); + edge.SetEnabled(false); + Add(edge); + } + } + + internal sealed class TemplateNode : Node + { + public TemplateAsset TemplateAsset { get; set; } + public string ModuleId { get; set; } + public Port Input { get; set; } + public Dictionary<string, Port> Outputs { get; set; } + + private ModuleTemplate _template; + + public TemplateNode(string moduleId, ModuleTemplate template, string key) : this (moduleId, template.Template, key) + { + _template = template; + + if (_template == null) return; + var priority = new Label("" +_template.Queue); + priority.AddToClassList("node-header-queue"); + titleContainer.Add(priority); + } + + public TemplateNode(string moduleId, TemplateAsset templateAsset, string key) + { + ModuleId = moduleId; + TemplateAsset = templateAsset; + title = TemplateAsset.name; + var idLabel = new Label($"({ModuleId})"); + idLabel.AddToClassList("node-header-id"); + titleContainer.Insert(1, idLabel); + Outputs = new Dictionary<string, Port>(); + + if (!string.IsNullOrEmpty(key)) + { + Input = InstantiatePort(Orientation.Horizontal, Direction.Input, Port.Capacity.Multi, typeof(string)); + Input.portName = key; + Input.portColor = Color.cyan; + Input.edgeConnector.activators.Clear(); + inputContainer.Add(Input); + } + + foreach (string keyword in TemplateAsset.Keywords) + { + var port = InstantiatePort(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(string)); + + string sanitizedKeyword = keyword.Replace("#K#", "").Replace("#KI#", ""); + bool isInternal = keyword.StartsWith("#KI#"); + port.portName = sanitizedKeyword + (isInternal ? "(i)" : ""); + port.portColor = Color.cyan; + port.edgeConnector.activators.Clear(); + Outputs.Add(sanitizedKeyword, port); + outputContainer.Add(port); + } + + RefreshExpandedState(); + RefreshPorts(); + } + + public float Reposition(float right, float top, float offset) + { + float width = resolvedStyle.width; + float height = resolvedStyle.height; + + float childrenHeight = 0; + float newTop = top; + + foreach (var output in Outputs.Values) + { + foreach (var edge in output.connections) + { + var node = edge.input.node as TemplateNode; + if (node != null) + { + childrenHeight += node.Reposition(right + width + offset, newTop, offset); + newTop = top + childrenHeight; + } + } + } + SetPosition(new Rect(right, top + Math.Max((childrenHeight - height)/2, 0), 100, 100)); + RefreshExpandedState(); + RefreshPorts(); + + return Math.Max(height, childrenHeight) + 4; + } + + public bool ContainsKeyword(string keyword) + { + return TemplateAsset.Keywords.Contains(keyword); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs.meta new file mode 100644 index 00000000..e4084c2c --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a58025042953bc1429b3e1a8684be12f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers.meta new file mode 100644 index 00000000..8c8efc2c --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1a80f0c817257bb43bc624af1073b13f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs new file mode 100644 index 00000000..f33a10cb --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs @@ -0,0 +1,30 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + [CustomPropertyDrawer(typeof(EnableProperty))] + public class EnablePropertyDrawer : PropertyDrawer + { + private VisualElement _root; + + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + _root = new VisualElement(); + + var visualTree = Resources.Load<VisualTreeAsset>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/EnablePropertyDrawer"); + VisualElement template = visualTree.CloneTree(); + var foldout = new Foldout(); + foldout.text = property.displayName; + foldout.RegisterValueChangedCallback((e) => property.isExpanded = e.newValue); + foldout.value = property.isExpanded; + + foldout.Add(template); + _root.Add(foldout); + + return _root; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs.meta new file mode 100644 index 00000000..5f11f5c6 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ffc75a109a78eb846a6b3708786e1a50 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs new file mode 100644 index 00000000..d84eb1e4 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs @@ -0,0 +1,32 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + [CustomPropertyDrawer(typeof(ShaderFunction))] + public class FunctionPropertyDrawer : PropertyDrawer + { + private VisualElement _root; + + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + _root = new VisualElement(); + + var visualTree = Resources.Load<VisualTreeAsset>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/FunctionPropertyDrawer"); + VisualElement template = visualTree.CloneTree(); + var foldout = new Foldout(); + foldout.text = property.displayName; + foldout.RegisterValueChangedCallback((e) => property.isExpanded = e.newValue); + foldout.value = property.isExpanded; + + var nameField = template.Q<TextField>("Name"); + nameField.RegisterValueChangedCallback(evt => foldout.text = evt.newValue); + + foldout.Add(template); + _root.Add(foldout); + + return _root; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs.meta new file mode 100644 index 00000000..e8fb2bf6 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24f1782728098f349aee99d57e62f501 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs new file mode 100644 index 00000000..2d2c77b8 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs @@ -0,0 +1,29 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + [CustomPropertyDrawer(typeof(ModuleTemplate))] + public class ModuleTemplatePropertyDrawer : PropertyDrawer + { + private VisualElement _root; + + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + _root = new VisualElement(); + + var visualTree = Resources.Load<VisualTreeAsset>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/ModuleTemplatePropertyDrawer"); + VisualElement template = visualTree.CloneTree(); + var foldout = new Foldout(); + foldout.text = property.displayName; + foldout.RegisterValueChangedCallback((e) => property.isExpanded = e.newValue); + foldout.value = property.isExpanded; + foldout.Add(template); + _root.Add(foldout); + + return _root; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs.meta new file mode 100644 index 00000000..4f655a68 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21bc47d4c53206845ba7ea3ecdada7d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs new file mode 100644 index 00000000..2221cd03 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs @@ -0,0 +1,52 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + public class PropertyAttributeAttribute : PropertyAttribute + { + } + [CustomPropertyDrawer(typeof(PropertyAttributeAttribute))] + public class PropertyAttributeDrawer : PropertyDrawer + { + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + var root = new VisualElement(); + + var value = new TextField(); + value.SetValueWithoutNotify(property.stringValue); + root.Add(value); + + /*value.RegisterValueChangedCallback(evt => + { + property.stringValue = evt.newValue; + property.serializedObject.ApplyModifiedProperties(); + });*/ + value.RegisterCallback<FocusOutEvent>(evt => + { + string v = value.value; + if (v[v.Length - 1] == ']') + { + v = v.Remove(v.Length - 1, 1); + } + if (v[0] == '[') + { + v = v.Remove(0, 1); + } + property.stringValue = v; + value.SetValueWithoutNotify(property.stringValue); + property.serializedObject.ApplyModifiedProperties(); + }); + + /*customTypeField.style.display = ((VariableType)typeField.value) == VariableType.Custom ? DisplayStyle.Flex : DisplayStyle.None; + + typeField.RegisterValueChangedCallback(e => + { + customTypeField.style.display = ((VariableType)e.newValue) == VariableType.Custom ? DisplayStyle.Flex : DisplayStyle.None; + });*/ + + return root; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs.meta new file mode 100644 index 00000000..105176a5 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3a66ad23d96a7a849ad93d53ea328d0e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs new file mode 100644 index 00000000..a41154a4 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs @@ -0,0 +1,289 @@ +using System; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; +using UnityEngine.Analytics; + +namespace Poiyomi.ModularShaderSystem.UI +{ + public enum DefaultTextureValue + { + White, + Black, + Gray, + Bump, + } + + [CustomPropertyDrawer(typeof(Property))] + public class ShaderPropertyDrawer : PropertyDrawer + { + private VisualElement _root; + + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + _root = new VisualElement(); + + var visualTree = Resources.Load<VisualTreeAsset>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/ShaderPropertyDrawer"); + VisualElement template = visualTree.CloneTree(); + var foldout = new Foldout(); + foldout.text = property.displayName; + foldout.RegisterValueChangedCallback((e) => property.isExpanded = e.newValue); + foldout.value = property.isExpanded; + + var nameField = template.Q<TextField>("Name"); + nameField.RegisterValueChangedCallback(evt => foldout.text = evt.newValue); + var enumField = template.Q<EnumField>("TypeField"); + var valueContainer = template.Q<VisualElement>("ValueContainer"); + + var type = property.FindPropertyRelative("Type"); + var defaultValue = property.FindPropertyRelative("DefaultValue"); + + var propType = GetPropertyTypeFromSerializedProperty(type.stringValue); + + enumField.Init(propType); + + enumField.value = propType; + + enumField.RegisterValueChangedCallback(e => + { + SetPropType(type, (PropertyType)e.newValue); + SetPropDefaultValue(defaultValue,""); + UpdateValueContainer(property, defaultValue, type, (PropertyType)e.newValue, type.stringValue, defaultValue.stringValue, valueContainer); + }); + + UpdateValueContainer(property, defaultValue, type, propType, type.stringValue, defaultValue.stringValue, valueContainer); + + foldout.Add(template); + _root.Add(foldout); + return _root; + } + + private void SetPropDefaultValue(SerializedProperty defaultValue, string v) + { + defaultValue.stringValue = v; + defaultValue.serializedObject.ApplyModifiedProperties(); + } + + private void SetPropType(SerializedProperty type, string v) + { + type.stringValue = v; + type.serializedObject.ApplyModifiedProperties(); + } + + private void SetPropType(SerializedProperty propType, PropertyType type) + { + string typeString = ""; + switch (type) + { + case PropertyType.Int: typeString = "Int"; break; + case PropertyType.Float: typeString = "Float"; break; + case PropertyType.Range: typeString = "Range(0, 1)"; break; + case PropertyType.Vector: typeString = "Vector"; break; + case PropertyType.Color: typeString = "Color"; break; + case PropertyType.Texture2D: typeString = "2D"; break; + case PropertyType.Texture2DArray: typeString = "2DArray"; break; + case PropertyType.Cube: typeString = "Cube"; break; + case PropertyType.CubeArray: typeString = "CubeArray"; break; + case PropertyType.Texture3D: typeString = "3d"; break; + } + + propType.stringValue = typeString; + propType.serializedObject.ApplyModifiedProperties(); + } + + private void UpdateValueContainer(SerializedProperty property, SerializedProperty defaultValue, SerializedProperty type, PropertyType propType, string propTypeString, string propValue, VisualElement element) + { + VisualElement field = null; + switch (propType) + { + case PropertyType.Float: + float floatValue = 0; + if (float.TryParse(propValue, out float f)) floatValue = f; + else SetPropDefaultValue(defaultValue,"" + floatValue); + var flfield = new FloatField{ value = floatValue, label = "Default value"}; + flfield.RegisterValueChangedCallback(e => SetPropDefaultValue(defaultValue, "" + e.newValue)); + field = flfield; + break; + case PropertyType.Range: + + field = new VisualElement(); + var rangeLimits = new Vector2(0, 1); + float rangeValue = 0; + + string[] prt = propTypeString.Replace("Range(","").Replace(")","").Split(','); + float[] prv = new float[2]; + bool pfi = true; + for (int i = 0; i < 2; i++) + { + if (float.TryParse(prt[i], out float v)) + { + prv[i] = v; + } + else + { + pfi = false; + break; + } + } + if (pfi) rangeLimits = new Vector2(prv[0], prv[1]); + else SetPropType(type, $"Range({prv[0]}, {prv[1]})"); + + var limits = new Vector2Field { label = "Range limits", value = rangeLimits }; + + if (float.TryParse(propValue, out float r)) rangeValue = r; + else SetPropDefaultValue(defaultValue,"" + rangeValue); + var horizontalElement = new VisualElement(); + horizontalElement.style.flexDirection = FlexDirection.Row; + + var valueSlider = new Slider + { + value = rangeValue, + lowValue = Math.Min(rangeLimits[0], rangeLimits[1]), + highValue = Math.Max(rangeLimits[0], rangeLimits[1]), + + label = "Default value" + }; + valueSlider.style.flexGrow = 1; + var valueField = new FloatField { value = rangeValue }; + valueField.style.width = 30; + + limits.RegisterValueChangedCallback(e => + { + valueSlider.lowValue = Math.Min(e.newValue[0], e.newValue[1]); + valueSlider.highValue = Math.Max(e.newValue[0], e.newValue[1]); + SetPropType(type,$"Range({valueSlider.lowValue}, {valueSlider.highValue})"); + }); + + valueField.RegisterValueChangedCallback(e => + { + if (e.newValue > valueSlider.highValue || e.newValue < valueSlider.lowValue) + { + e.StopImmediatePropagation(); + e.PreventDefault(); + valueField.SetValueWithoutNotify(e.previousValue); + return; + } + valueSlider.SetValueWithoutNotify(e.newValue); + SetPropDefaultValue(defaultValue,"" + e.newValue); + }); + valueSlider.RegisterValueChangedCallback(e => + { + valueField.SetValueWithoutNotify(e.newValue); + SetPropDefaultValue(defaultValue,"" + e.newValue); + }); + + field.Add(limits); + horizontalElement.Add(valueSlider); + horizontalElement.Add(valueField); + field.Add(horizontalElement); + + break; + case PropertyType.Int: + int intValue = 0; + if (int.TryParse(propValue, out int iv)) intValue = iv; + else SetPropDefaultValue(defaultValue,"" + intValue); + var ivfield = new IntegerField{ value = intValue, label = "Default value"}; + field = ivfield; + ivfield.RegisterValueChangedCallback(e => SetPropDefaultValue(defaultValue,"" + e.newValue)); + break; + case PropertyType.Color: + Color colorValue = Color.white; + string[] clvl = propValue.Replace("(","").Replace(")","").Split(','); + float[] fv = new float[4]; + bool vfi = true; + for (int i = 0; i < 4; i++) + { + if (float.TryParse(clvl[i], out float v)) + { + fv[i] = v; + } + else + { + vfi = false; + break; + } + } + + if (vfi) colorValue = new Color(fv[0], fv[1], fv[2], fv[3]); + else SetPropDefaultValue(defaultValue,$"({colorValue[0]}, {colorValue[1]}, {colorValue[2]}, {colorValue[3]})"); + var clfield = new ColorField { value = colorValue, label = "Default value" }; + field = clfield; + clfield.RegisterValueChangedCallback(e => SetPropDefaultValue(defaultValue,$"({e.newValue[0]}, {e.newValue[1]}, {e.newValue[2]}, {e.newValue[3]})")); + break; + case PropertyType.Vector: + Vector4 vectorValue = Vector4.zero; + string[] vvl = propValue.Replace("(","").Replace(")","").Split(','); + float[] vv = new float[4]; + bool vvi = true; + for (int i = 0; i < 4; i++) + { + if (float.TryParse(vvl[i], out float v)) + { + vv[i] = v; + } + else + { + vvi = false; + break; + } + } + if (vvi) vectorValue = new Vector4(vv[0], vv[1], vv[2] ,vv[3]); + else SetPropDefaultValue(defaultValue,$"({vv[0]}, {vv[1]}, {vv[2]}, {vv[3]})"); + var vlfield = new Vector4Field{ value = vectorValue, label = "Default value" }; + field = vlfield; + vlfield.RegisterValueChangedCallback(e => SetPropDefaultValue(defaultValue,$"({e.newValue[0]}, {e.newValue[1]}, {e.newValue[2]}, {e.newValue[3]})")); + break; + case PropertyType.Texture2D: + var texValue = DefaultTextureValue.White; + if (propValue.Contains("white")) texValue = DefaultTextureValue.White; + if (propValue.Contains("gray")) texValue = DefaultTextureValue.Gray; + if (propValue.Contains("black")) texValue = DefaultTextureValue.Black; + if (propValue.Contains("bump")) texValue = DefaultTextureValue.Bump; + SetPropDefaultValue(defaultValue,$"\"{Enum.GetName(typeof(DefaultTextureValue), texValue)?.ToLower()}\" {{}}"); + var txfield = new EnumField { label = "Default value" }; + txfield.Init(texValue); + var textureAsset = new PropertyField(property.FindPropertyRelative("DefaultTextureAsset"), "Texture Override"); + textureAsset.Bind(property.serializedObject); + var vl = new VisualElement(); + vl.Add(txfield); + vl.Add(textureAsset); + field = vl; + txfield.RegisterValueChangedCallback(e => SetPropDefaultValue(defaultValue,$"\"{Enum.GetName(typeof(DefaultTextureValue), e.newValue)?.ToLower()}\" {{}}")); + break; + case PropertyType.Texture2DArray: + case PropertyType.CubeArray: + case PropertyType.Texture3D: + SetPropDefaultValue(defaultValue,"\"\"{}"); + break; + case PropertyType.Cube: + SetPropDefaultValue(defaultValue,"\"\"{}"); + var textureCubeAsset = new PropertyField(property.FindPropertyRelative("DefaultTextureAsset"), "Texture Override"); + textureCubeAsset.Bind(property.serializedObject); + field = textureCubeAsset; + break; + } + + element.Clear(); + if (field != null) element.Add(field); + } + + private static PropertyType GetPropertyTypeFromSerializedProperty(string propType) + { + switch (propType.Trim()) + { + case "Float": return PropertyType.Float; + case "Int": return PropertyType.Int; + case "Color": return PropertyType.Color; + case "Vector": return PropertyType.Vector; + case "2D": return PropertyType.Texture2D; + case "3D": return PropertyType.Texture3D; + case "Cube": return PropertyType.Cube; + case "2DArray": return PropertyType.Texture2DArray; + case "CubeArray": return PropertyType.CubeArray; + default: return propType.Trim().StartsWith("Range") ? PropertyType.Range : PropertyType.Float; + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs.meta new file mode 100644 index 00000000..a638df90 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eef114f99927e5d4b98ba893786d3838 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs new file mode 100644 index 00000000..3bdfd831 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs @@ -0,0 +1,43 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + [CustomPropertyDrawer(typeof(Variable))] + public class VariablePropertyDrawer : PropertyDrawer + { + private VisualElement _root; + + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + _root = new VisualElement(); + + var visualTree = Resources.Load<VisualTreeAsset>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/VariablePropertyDrawer"); + VisualElement template = visualTree.CloneTree(); + var foldout = new Foldout(); + foldout.text = property.displayName; + foldout.RegisterValueChangedCallback((e) => property.isExpanded = e.newValue); + foldout.value = property.isExpanded; + foldout.Add(template); + _root.Add(foldout); + + var nameField = template.Q<TextField>("Name"); + nameField.RegisterValueChangedCallback(evt => foldout.text = evt.newValue); + var typeField = template.Q<EnumField>("Type"); + var customTypeField = template.Q<VisualElement>("CustomType"); + + typeField.Init(VariableType.Float); + + customTypeField.style.display = ((VariableType)typeField.value) == VariableType.Custom ? DisplayStyle.Flex : DisplayStyle.None; + + typeField.RegisterValueChangedCallback(e => + { + customTypeField.style.display = ((VariableType)e.newValue) == VariableType.Custom ? DisplayStyle.Flex : DisplayStyle.None; + }); + + return _root; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs.meta new file mode 100644 index 00000000..b6b5f0fa --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d92791b337f5ca4984e6d33748e19a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements.meta new file mode 100644 index 00000000..ec87ef6f --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 593f19fde7643b4429fd5f6a8503214b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs new file mode 100644 index 00000000..3ad05abf --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs @@ -0,0 +1,104 @@ +using System; +using System.Linq; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + public class CodeViewElement : VisualElement + { + private class LineItem : VisualElement + { + private static Font TextFont + { + get + { + if (_font == null) + _font = Resources.Load<Font>(MSSConstants.RESOURCES_FOLDER + "/RobotoMono-Regular"); + return _font; + } + } + + private static Font _font; + private Label _lineNumber; + private Label _line; + public string Text { get; } + + public LineItem() : this(0, "") {} + + public LineItem(int number, string text, int digits = 0) + { + Text = text; + _lineNumber = new Label("" + number); + _lineNumber.style.color = Color.gray; + _lineNumber.style.width = digits == 0 ? 30 : digits * 8; + _lineNumber.style.unityTextAlign = TextAnchor.MiddleRight; + _lineNumber.style.unityFont = TextFont; + _lineNumber.style.marginRight = 4; + _lineNumber.style.marginLeft = 4; + + _line = new Label(text); + _line.style.flexGrow = 1; + _line.style.unityFont = TextFont; + + style.flexDirection = FlexDirection.Row; + Add(_lineNumber); + Add(_line); + } + + public void SetText(int i, string textLine, int digits) + { + _lineNumber.text = "" + i; + _lineNumber.style.width = digits == 0 ? 30 : digits * 8; + _line.text = textLine; + _line.MeasureTextSize(textLine, 0, MeasureMode.Exactly, 0, MeasureMode.Exactly); + } + } + + public string Text + { + get => string.Join("\n", _textLines); + set + { + _textLines = value == null ? Array.Empty<string>() : value.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + _digits = (int)Math.Floor(Math.Log10(_textLines.Length) + 1); + _listView.itemsSource = _textLines; + + float width =((_textLines.Length == 0 ? 0 : _textLines.Max(x => x.Length)) + _digits + 1) * 10; + _listView.contentContainer.style.width = width; + } + } + + public int LineCount => _textLines.Length; + + private Label _templateLabel; + private string[] _textLines; + private ListView _listView; + private int _digits; + + public CodeViewElement() + { + ScrollView s = new ScrollView(ScrollViewMode.Horizontal); + _listView = new ListView(); + _listView.itemHeight = 15; + _listView.AddToClassList("unity-base-text-field__input"); + _listView.AddToClassList("unity-text-field__input"); + _listView.AddToClassList("unity-base-field__input"); + _listView.style.flexGrow = 1; + _listView.contentContainer.style.flexGrow = 1; + + Func<VisualElement> makeItem = () => new LineItem(); + Action<VisualElement, int> bindItem = (e, i) => (e as LineItem).SetText(i+1, _textLines[i], _digits); + + _listView.makeItem = makeItem; + _listView.bindItem = bindItem; + _listView.selectionType = SelectionType.None; + s.Add(_listView); + Add(s); + s.style.flexGrow = 1; + s.contentContainer.style.flexGrow = 1; + + style.flexGrow = 1; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs.meta new file mode 100644 index 00000000..69417247 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 248ba9991fe7ba44d8ed907de6a5e4d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs new file mode 100644 index 00000000..60eff417 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs @@ -0,0 +1,324 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + public interface IInspectorList + { + InspectorListItem draggedElement { get; set; } + void HighlightDrops(); + void DeHighlightDrops(); + } + + public class InspectorList : BindableElement, IInspectorList + { + Foldout _listContainer; + Button _addButton; + SerializedProperty _array; + private bool _showElementsButtons; + + private bool _hasFoldingBeenForced; + + public InspectorListItem draggedElement { get; set; } + public bool _highlightDrops; + + private List<VisualElement> _drops; + + private VisualElement _currentDrop; + + public InspectorList() + { + _drops = new List<VisualElement>(); + _listContainer = new Foldout(); + _listContainer.text = "Unbound List"; + _listContainer.contentContainer.AddToClassList("inspector-list-container"); + _listContainer.value = false; + _listContainer.RegisterCallback<MouseUpEvent>(e => Drop()); + _listContainer.RegisterCallback<MouseLeaveEvent>(e => Drop()); + + _addButton = new Button(AddItem); + _addButton.text = "Add"; + _addButton.AddToClassList("inspector-list-add-button"); + Add(_listContainer); + if (enabledSelf) + _listContainer.Add(_addButton); + _listContainer.RegisterValueChangedCallback((e) => _array.isExpanded = e.newValue); + var styleSheet = Resources.Load<StyleSheet>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/InspectorList"); + styleSheets.Add(styleSheet); + } + + private void Drop() + { + if (draggedElement == null) return; + draggedElement.RemoveFromClassList("inspector-list-drag-enabled"); + + if (_highlightDrops) + { + DeHighlightDrops(); + int dropIndex = _drops.IndexOf(_currentDrop); + + if (dropIndex == -1) + { + draggedElement = null; + return; + } + + if (dropIndex > draggedElement.index) dropIndex--; + _array.MoveArrayElement(draggedElement.index, dropIndex); + bool expanded = _array.GetArrayElementAtIndex(dropIndex).isExpanded; + _array.GetArrayElementAtIndex(dropIndex).isExpanded = _array.GetArrayElementAtIndex(draggedElement.index).isExpanded; + _array.GetArrayElementAtIndex(draggedElement.index).isExpanded = expanded; + _array.serializedObject.ApplyModifiedProperties(); + UpdateList(); + } + draggedElement = null; + } + + public void HighlightDrops() + { + foreach (var item in _drops) + item.AddToClassList("inspector-list-drop-area-highlight"); + + _highlightDrops = true; + } + + public void DeHighlightDrops() + { + foreach (var item in _drops) + item.RemoveFromClassList("inspector-list-drop-area-highlight"); + + _highlightDrops = false; + } + + public override void HandleEvent(EventBase evt) + { + var type = evt.GetType(); //SerializedObjectBindEvent is internal, so need to use reflection here + if ((type.Name == "SerializedPropertyBindEvent") && !string.IsNullOrWhiteSpace(bindingPath)) + { + var obj = type.GetProperty("bindProperty")?.GetValue(evt) as SerializedProperty; + _array = obj; + if (obj != null) + { + if (_hasFoldingBeenForced) obj.isExpanded = _listContainer.value; + else _listContainer.value = obj.isExpanded; + } + UpdateList(); + } + base.HandleEvent(evt); + } + + public void UpdateList() + { + _listContainer.Clear(); + _drops.Clear(); + + if (_array == null) + return; + _listContainer.text = _array.displayName; + CreateDrop(); + for (int i = 0; i < _array.arraySize; i++) + { + int index = i; + var element = new PropertyField(_array.GetArrayElementAtIndex(index)); + element.Bind(_array.GetArrayElementAtIndex(index).serializedObject); + var item = new InspectorListItem(this, element, _array, index, _showElementsButtons); + item.removeButton.RegisterCallback<PointerUpEvent>((evt) => RemoveItem(index)); + item.upButton.RegisterCallback<PointerUpEvent>((evt) => MoveUpItem(index)); + item.downButton.RegisterCallback<PointerUpEvent>((evt) => MoveDownItem(index)); + _listContainer.Add(item); + CreateDrop(); + } + if (enabledSelf) + _listContainer.Add(_addButton); + } + + private void CreateDrop() + { + VisualElement dropArea = new VisualElement(); + dropArea.AddToClassList("inspector-list-drop-area"); + dropArea.RegisterCallback<MouseEnterEvent>(e => + { + if (_highlightDrops) + { + dropArea.AddToClassList("inspector-list-drop-area-selected"); + _currentDrop = dropArea; + } + }); + dropArea.RegisterCallback<MouseLeaveEvent>(e => + { + if (_highlightDrops) + { + dropArea.RemoveFromClassList("inspector-list-drop-area-selected"); + if (_currentDrop == dropArea) _currentDrop = null; + } + }); + + _listContainer.Add(dropArea); + _drops.Add(dropArea); + } + + public void RemoveItem(int index) + { + if (_array != null) + { + if (index < _array.arraySize - 1) + _array.GetArrayElementAtIndex(index).isExpanded = _array.GetArrayElementAtIndex(index + 1).isExpanded; + _array.DeleteArrayElementAtIndex(index); + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void MoveUpItem(int index) + { + if (_array != null && index > 0) + { + _array.MoveArrayElement(index, index - 1); + bool expanded = _array.GetArrayElementAtIndex(index).isExpanded; + _array.GetArrayElementAtIndex(index).isExpanded = _array.GetArrayElementAtIndex(index - 1).isExpanded; + _array.GetArrayElementAtIndex(index - 1).isExpanded = expanded; + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void MoveDownItem(int index) + { + if (_array != null && index < _array.arraySize - 1) + { + _array.MoveArrayElement(index, index + 1); + bool expanded = _array.GetArrayElementAtIndex(index).isExpanded; + _array.GetArrayElementAtIndex(index).isExpanded = _array.GetArrayElementAtIndex(index + 1).isExpanded; + _array.GetArrayElementAtIndex(index + 1).isExpanded = expanded; + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void AddItem() + { + if (_array != null) + { + _array.InsertArrayElementAtIndex(_array.arraySize); + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void SetFoldingState(bool open) + { + _listContainer.value = open; + if (_array != null) _array.isExpanded = open; + else _hasFoldingBeenForced = true; + } + + public new class UxmlFactory : UxmlFactory<InspectorList, UxmlTraits> { } + + public new class UxmlTraits : BindableElement.UxmlTraits + { + UxmlBoolAttributeDescription showElements = + new UxmlBoolAttributeDescription { name = "show-elements-text", defaultValue = true }; + + public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) + { + base.Init(ve, bag, cc); + + if (ve is InspectorList ate) ate._showElementsButtons = showElements.GetValueFromBag(bag, cc); + } + } + + } + + public class InspectorListItem : VisualElement + { + public Button removeButton; + public Button upButton; + public Button downButton; + + public VisualElement dragArea; + + public Vector2 startPosition; + + public int index; + + private IInspectorList _list; + public InspectorListItem(IInspectorList list, VisualElement element, SerializedProperty array, int index, bool showButtonsText) + { + this.index = index; + _list = list; + AddToClassList("inspector-list-item-container"); + + dragArea = new VisualElement(); + dragArea.AddToClassList("inspector-list-drag-handle"); + + dragArea.RegisterCallback<MouseDownEvent>(e => + { + if (_list.draggedElement == this) + { + e.StopImmediatePropagation(); + return; + } + + _list.draggedElement = this; + _list.HighlightDrops(); + AddToClassList("inspector-list-drag-enabled"); + }); + + VisualElement buttonsArea = new VisualElement(); + + RegisterCallback<GeometryChangedEvent>(e => + { + buttonsArea.ClearClassList(); + if (e.newRect.height > 60) + { + buttonsArea.AddToClassList("inspector-list-buttons-container-vertical"); + buttonsArea.Add(removeButton); + buttonsArea.Add(upButton); + buttonsArea.Add(downButton); + } + else + { + buttonsArea.AddToClassList("inspector-list-buttons-container-horizontal"); + buttonsArea.Add(upButton); + buttonsArea.Add(downButton); + buttonsArea.Add(removeButton); + } + }); + + upButton = new Button(); + upButton.name = "UpInspectorListItem"; + upButton.AddToClassList("inspector-list-up-button"); + if (index == 0) + upButton.SetEnabled(false); + downButton = new Button(); + downButton.name = "DownInspectorListItem"; + downButton.AddToClassList("inspector-list-down-button"); + if (index >= array.arraySize - 1) + downButton.SetEnabled(false); + removeButton = new Button(); + removeButton.name = "RemoveInspectorListItem"; + removeButton.AddToClassList("inspector-list-remove-button"); + + if (showButtonsText) + { + upButton.text = "up"; + downButton.text = "down"; + removeButton.text = "-"; + } + + var property = array.GetArrayElementAtIndex(index); + element.AddToClassList("inspector-list-item"); + + Add(dragArea); + Add(element); + Add(buttonsArea); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs.meta new file mode 100644 index 00000000..5839937e --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e402c0aaa42fe944802ceaf7d55be05 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs new file mode 100644 index 00000000..6f2f9955 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs @@ -0,0 +1,56 @@ +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + public class LabelField : VisualElement + { + public string Label + { + get => _label; + set + { + _label = value; + _labelField.text = _label; + } + } + + public string Value + { + get => _value; + set + { + _value = value; + _valueField.text = _value; + } + } + + private Label _labelField; + private Label _valueField; + private string _label; + private string _value; + + public LabelField(string label, string value) + { + _label = label; + _value = value; + _labelField = new Label(label); + _valueField = new Label(value); + + AddToClassList("unity-base-field"); + AddToClassList("unity-base-text-field"); + AddToClassList("unity-text-field"); + + _labelField.AddToClassList("unity-text-element"); + _labelField.AddToClassList("unity-label"); + _labelField.AddToClassList("unity-base-field__label"); + _labelField.AddToClassList("unity-base-text-field__label"); + _labelField.AddToClassList("unity-text-field__label"); + _labelField.AddToClassList("label-field-title"); + _valueField.AddToClassList("label-field-value"); + + Add(_labelField); + Add(_valueField); + + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs.meta new file mode 100644 index 00000000..f7dffca5 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: af8a36edfa0c0a44d971881746edc521 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs new file mode 100644 index 00000000..7a80493e --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs @@ -0,0 +1,315 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + + public class ModuleInspectorList : BindableElement, IInspectorList + { + Foldout _listContainer; + Button _addButton; + SerializedProperty _array; + private bool _showElementsButtons; + private List<string> _loadedModules; + + private bool _hasFoldingBeenForced; + + public InspectorListItem draggedElement { get; set; } + public bool _highlightDrops; + + private List<VisualElement> _drops; + + private VisualElement _currentDrop; + + public ModuleInspectorList() + { + _drops = new List<VisualElement>(); + _listContainer = new Foldout(); + _listContainer.text = "Unbound List"; + _listContainer.contentContainer.AddToClassList("inspector-list-container"); + _listContainer.value = false; + _listContainer.RegisterCallback<MouseUpEvent>(e => Drop()); + _listContainer.RegisterCallback<MouseLeaveEvent>(e => Drop()); + + _addButton = new Button(AddItem); + _addButton.text = "Add"; + _addButton.AddToClassList("inspector-list-add-button"); + Add(_listContainer); + if (enabledSelf) + _listContainer.Add(_addButton); + _listContainer.RegisterValueChangedCallback((e) => _array.isExpanded = e.newValue); + var styleSheet = Resources.Load<StyleSheet>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/InspectorList"); + styleSheets.Add(styleSheet); + } + + private void Drop() + { + if (draggedElement == null) return; + draggedElement.RemoveFromClassList("inspector-list-drag-enabled"); + + if (_highlightDrops) + { + DeHighlightDrops(); + int dropIndex = _drops.IndexOf(_currentDrop); + + if (dropIndex == -1) + { + draggedElement = null; + return; + } + + if (dropIndex > draggedElement.index) dropIndex--; + _array.MoveArrayElement(draggedElement.index, dropIndex); + bool expanded = _array.GetArrayElementAtIndex(dropIndex).isExpanded; + _array.GetArrayElementAtIndex(dropIndex).isExpanded = _array.GetArrayElementAtIndex(draggedElement.index).isExpanded; + _array.GetArrayElementAtIndex(draggedElement.index).isExpanded = expanded; + _array.serializedObject.ApplyModifiedProperties(); + UpdateList(); + } + draggedElement = null; + } + + public void HighlightDrops() + { + foreach (var item in _drops) + item.AddToClassList("inspector-list-drop-area-highlight"); + + _highlightDrops = true; + } + + public void DeHighlightDrops() + { + foreach (var item in _drops) + item.RemoveFromClassList("inspector-list-drop-area-highlight"); + + _highlightDrops = false; + } + + public override void HandleEvent(EventBase evt) + { + var type = evt.GetType(); //SerializedObjectBindEvent is internal, so need to use reflection here + if ((type.Name == "SerializedPropertyBindEvent") && !string.IsNullOrWhiteSpace(bindingPath)) + { + var obj = type.GetProperty("bindProperty")?.GetValue(evt) as SerializedProperty; + _array = obj; + if (obj != null) + { + if (_hasFoldingBeenForced) obj.isExpanded = _listContainer.value; + else _listContainer.value = obj.isExpanded; + } + UpdateList(); + } + base.HandleEvent(evt); + } + + public void UpdateList() + { + _listContainer.Clear(); + _drops.Clear(); + + if (_array == null) + return; + _listContainer.text = _array.displayName; + CreateDrop(); + + _loadedModules = new List<string>(); + for (int i = 0; i < _array.arraySize; i++) + { + if (_array.GetArrayElementAtIndex(i).objectReferenceValue != null) + _loadedModules.Add(((ShaderModule)_array.GetArrayElementAtIndex(i).objectReferenceValue)?.Id); + } + + + + for (int i = 0; i < _array.arraySize; i++) + { + int index = i; + + var moduleItem = new VisualElement(); + var objectField = new ObjectField();//_array.GetArrayElementAtIndex(index)); + + SerializedProperty propertyValue = _array.GetArrayElementAtIndex(index); + + objectField.objectType = typeof(ShaderModule); + objectField.bindingPath = propertyValue.propertyPath; + objectField.Bind(propertyValue.serializedObject); + var infoLabel = new Label(); + moduleItem.Add(objectField); + moduleItem.Add(infoLabel); + + objectField.RegisterCallback<ChangeEvent<Object>>(x => + { + var newValue = (ShaderModule)x.newValue; + var oldValue = (ShaderModule)x.previousValue; + + if (oldValue != null) + _loadedModules.Remove(oldValue.Id); + if (newValue != null) + _loadedModules.Add(newValue.Id); + + for (int j = 0; j < _array.arraySize; j++) + { + var element = ((ObjectField)x.target).parent.parent.parent.ElementAt(j*2+1).ElementAt(1); + Label label = element.ElementAt(1) as Label; + if (index == j) + CheckModuleValidity(newValue, label, element); + else + CheckModuleValidity((ShaderModule)_array.GetArrayElementAtIndex(j).objectReferenceValue, label, element); + } + }); + + var item = new InspectorListItem(this, moduleItem, _array, index, _showElementsButtons); + item.removeButton.RegisterCallback<PointerUpEvent>((evt) => RemoveItem(index)); + item.upButton.RegisterCallback<PointerUpEvent>((evt) => MoveUpItem(index)); + item.downButton.RegisterCallback<PointerUpEvent>((evt) => MoveDownItem(index)); + _listContainer.Add(item); + CreateDrop(); + + CheckModuleValidity((ShaderModule)propertyValue.objectReferenceValue, infoLabel, moduleItem); + } + if (enabledSelf) + _listContainer.Add(_addButton); + } + + private void CreateDrop() + { + VisualElement dropArea = new VisualElement(); + dropArea.AddToClassList("inspector-list-drop-area"); + dropArea.RegisterCallback<MouseEnterEvent>(e => + { + if (_highlightDrops) + { + dropArea.AddToClassList("inspector-list-drop-area-selected"); + _currentDrop = dropArea; + } + }); + dropArea.RegisterCallback<MouseLeaveEvent>(e => + { + if (_highlightDrops) + { + dropArea.RemoveFromClassList("inspector-list-drop-area-selected"); + if (_currentDrop == dropArea) _currentDrop = null; + } + }); + + _listContainer.Add(dropArea); + _drops.Add(dropArea); + } + + private void CheckModuleValidity(ShaderModule newValue, Label infoLabel, VisualElement moduleItem) + { + + List<string> problems = new List<string>(); + + if (newValue != null) + { + var moduleId = newValue.Id; + if (_loadedModules.Count(y => y.Equals(moduleId)) > 1) + problems.Add("The module is duplicate"); + + List<string> missingDependencies = newValue.ModuleDependencies.Where(dependency => _loadedModules.Count(y => y.Equals(dependency)) == 0).ToList(); + List<string> incompatibilities = newValue.IncompatibleWith.Where(dependency => _loadedModules.Count(y => y.Equals(dependency)) > 0).ToList(); + + if (missingDependencies.Count > 0) + problems.Add("Missing dependencies: " + string.Join(", ", missingDependencies)); + + if (incompatibilities.Count > 0) + problems.Add("These incompatible modules are installed: " + string.Join(", ", incompatibilities)); + } + + infoLabel.text = string.Join("\n", problems); + + if (!string.IsNullOrWhiteSpace(infoLabel.text)) + { + moduleItem.AddToClassList("error-background"); + infoLabel.visible = true; + } + else + { + moduleItem.RemoveFromClassList("error-background"); + infoLabel.visible = false; + } + } + + public void RemoveItem(int index) + { + if (_array != null) + { + if (index < _array.arraySize - 1) + _array.GetArrayElementAtIndex(index).isExpanded = _array.GetArrayElementAtIndex(index + 1).isExpanded; + var elementProperty = _array.GetArrayElementAtIndex(index); + if (elementProperty.objectReferenceValue != null) + elementProperty.objectReferenceValue = null; + _array.DeleteArrayElementAtIndex(index); + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void MoveUpItem(int index) + { + if (_array != null && index > 0) + { + _array.MoveArrayElement(index, index - 1); + bool expanded = _array.GetArrayElementAtIndex(index).isExpanded; + _array.GetArrayElementAtIndex(index).isExpanded = _array.GetArrayElementAtIndex(index - 1).isExpanded; + _array.GetArrayElementAtIndex(index - 1).isExpanded = expanded; + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void MoveDownItem(int index) + { + if (_array != null && index < _array.arraySize - 1) + { + _array.MoveArrayElement(index, index + 1); + bool expanded = _array.GetArrayElementAtIndex(index).isExpanded; + _array.GetArrayElementAtIndex(index).isExpanded = _array.GetArrayElementAtIndex(index + 1).isExpanded; + _array.GetArrayElementAtIndex(index + 1).isExpanded = expanded; + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void AddItem() + { + if (_array != null) + { + _array.InsertArrayElementAtIndex(_array.arraySize); + _array.serializedObject.ApplyModifiedProperties(); + } + + UpdateList(); + } + + public void SetFoldingState(bool open) + { + _listContainer.value = open; + if (_array != null) _array.isExpanded = open; + else _hasFoldingBeenForced = true; + } + + public new class UxmlFactory : UxmlFactory<ModuleInspectorList, UxmlTraits> { } + + public new class UxmlTraits : BindableElement.UxmlTraits + { + UxmlBoolAttributeDescription showElements = + new UxmlBoolAttributeDescription { name = "show-elements-text", defaultValue = true }; + + public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) + { + base.Init(ve, bag, cc); + + if (ve is ModuleInspectorList ate) ate._showElementsButtons = showElements.GetValueFromBag(bag, cc); + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs.meta new file mode 100644 index 00000000..aab66df8 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9210556d98393149bad9819fb127c5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs new file mode 100644 index 00000000..73079bd3 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs @@ -0,0 +1,30 @@ +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + public class VariableField : VisualElement + { + public Variable Variable { get; set; } + + private string _type; + + public VariableField(Variable variable) + { + Variable = variable; + if(variable.Type == VariableType.Custom) + _type = variable.CustomType; + else + _type = variable.Type.ToString(); + var nameField = new Label(variable.Name); + var typeField = new Label(_type); + + nameField.style.flexGrow = 1; + typeField.AddToClassList("variable-type-text"); + Add(nameField); + Add(typeField); + + style.flexDirection = FlexDirection.Row; + + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs.meta new file mode 100644 index 00000000..476b82a9 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d7f89d7e489068458d4c125179d6061 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors.meta new file mode 100644 index 00000000..6a6b0b87 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e5f27652ec52b1e4abcf40a28dfa2e9c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs new file mode 100644 index 00000000..4da90e07 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs @@ -0,0 +1,68 @@ +using System; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + [CustomEditor(typeof(ModularShader))] + public class ModularShaderEditor : Editor + { + private VisualElement _root; + private ModularShader _shader; + + public override VisualElement CreateInspectorGUI() + { + _root = new VisualElement(); + _shader = (ModularShader)serializedObject.targetObject; + var visualTree = Resources.Load<VisualTreeAsset>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/ModularShaderEditor"); + VisualElement template = visualTree.CloneTree(); + + _root.Add(template); + + var baseModulesField = _root.Q<ModuleInspectorList>("BaseModulesField"); + bool areModulesEditable = !_shader.LockBaseModules; + bool checkForProperties = _shader.UseTemplatesForProperties; + if(!areModulesEditable) + baseModulesField.SetFoldingState(true); + baseModulesField.SetEnabled(areModulesEditable); + + var templateField = _root.Q<ObjectField>("ShaderTemplateField"); + templateField.objectType = typeof(TemplateAsset); + + var propertiesTemplateField = _root.Q<ObjectField>("ShaderPropertiesTemplateField"); + propertiesTemplateField.objectType = typeof(TemplateAsset); + propertiesTemplateField.style.display = checkForProperties ? DisplayStyle.Flex : DisplayStyle.None; + + var useTemplatesField = _root.Q<Toggle>("UseTemplatesForPropertiesField"); + useTemplatesField.RegisterValueChangedCallback(x => + { + propertiesTemplateField.style.display = x.newValue ? DisplayStyle.Flex : DisplayStyle.None; + }); + + var generateButton = _root.Q<Button>("RegenerateShaderButton"); + + generateButton.clicked += () => + { + var _issues = ShaderGenerator.CheckShaderIssues(_shader); + if (_issues.Count > 0) + { + EditorUtility.DisplayDialog("Error", $"The modular shader has issues that must be resolved before generating the shader:\n {string.Join("\n ", _issues)}", "Ok"); + return; + } + + string path = EditorUtility.OpenFolderPanel("Select folder", "Assets", ""); + if (path.Length == 0) + return; + + string localPath = Environment.CurrentDirectory; + localPath = localPath.Replace('\\', '/'); + path = path.Replace(localPath + "/", ""); + ShaderGenerator.GenerateShader(path, _shader); + }; + + return _root; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs.meta new file mode 100644 index 00000000..0b65b770 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac6f0c46be6e0b34b81bc6cb1d45b9f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs new file mode 100644 index 00000000..1160ac15 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + [CustomEditor(typeof(ShaderModule))] + public class ShaderModuleEditor : Editor + { + private VisualElement _root; + + public override VisualElement CreateInspectorGUI() + { + _root = new VisualElement(); + + var visualTree = Resources.Load<VisualTreeAsset>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/ShaderModuleEditor"); + VisualElement template = visualTree.CloneTree(); + _root.Add(template); + + return _root; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs.meta new file mode 100644 index 00000000..4b45e29f --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c77ebb1fa33e27c44ac0cb2e2cadf4fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs new file mode 100644 index 00000000..a9278df6 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs @@ -0,0 +1,20 @@ +using System; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Poiyomi.ModularShaderSystem.UI +{ + [CustomEditor(typeof(TemplateAsset))] + public class TemplateAssetEditor : Editor + { + public override VisualElement CreateInspectorGUI() + { + CodeViewElement element = new CodeViewElement(); + element.Text = serializedObject.FindProperty("Template").stringValue; + element.style.minHeight = 600; + return element; + } + + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs.meta new file mode 100644 index 00000000..e37143ba --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c1a6131e05e6c54e9503e1a9d4529eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows.meta new file mode 100644 index 00000000..ef76597f --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 37d69e6ef5507b4498f9edc7b4d9bcd5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs new file mode 100644 index 00000000..9389c1c0 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs @@ -0,0 +1,814 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using Object = UnityEngine.Object; + +namespace Poiyomi.ModularShaderSystem +{ + [Serializable] + public class MigratedAssets + { + public List<MigratedTemplate> templates; + public List<MigratedCollection> templateCollections; + public List<MigratedShaderModule> shaderModules; + public List<MigratedModularShader> modularShaders; + + public MigratedAssets() + { + templates = new List<MigratedTemplate>(); + templateCollections = new List<MigratedCollection>(); + shaderModules = new List<MigratedShaderModule>(); + modularShaders = new List<MigratedModularShader>(); + } + } + + [Serializable] + public class MigratedTemplate + { + public long id; + public string path; + public string content; + } + + [Serializable] + public class MigratedCollection + { + public long id; + public string path; + public string content; + } + + [Serializable] + public class MigratedShaderModule + { + public long id; + public string path; + public string moduleId; + public string name; + public string version; + public string author; + public string description; + public List<EnableProperty> enableProperties; + public List<Property> properties; + public List<string> moduleDependencies; + public List<string> incompatibleWith; + public List<MigratedModuleTemplate> templates; + public List<MigratedShaderFunction> functions; + public string additionalSerializedData; + } + + [Serializable] + public class MigratedModularShader + { + public long id; + public string path; + public string shaderId; + public string name; + public string version; + public string author; + public string description; + public bool useTemplatesForProperties; + public long propertiesTemplateReference; + public string propertiesCollectionSubId; + public string shaderPath; + public long shaderTemplateReference; + public string shaderCollectionSubId; + public string customEditor; + public List<Property> properties; + public List<long> baseModules; + public List<long> additionalModules; + public bool lockBaseModules; + public List<Shader> lastGeneratedShaders; + public string additionalSerializedData; + } + + [Serializable] + public class MigratedModuleTemplate + { + public long templateReference; + public string collectionSubId; + public List<string> keywords; + public bool needsVariant; + public int queue; + } + + [Serializable] + public class MigratedShaderFunction + { + public string name; + public string appendAfter; + public short queue; + public long templateReference; + public string collectionSubId; + public List<Variable> usedVariables; + public List<string> variableKeywords; + public List<string> codeKeywords; + } + + public class MigratedItemElement<T> : VisualElement + { + private bool _isSelected; + + public bool IsSelected + { + get => _isSelected; + set + { + _isSelected = value; + _toggle.SetValueWithoutNotify(_isSelected); + } + } + + + private Migrator _window; + public T ToggledItem; + private string _name; + private readonly Toggle _toggle; + + public MigratedItemElement(Migrator window, T toggledItem, string name) + { + _window = window; + _name = name; + ToggledItem = toggledItem; + + style.flexDirection = FlexDirection.Row; + + _toggle = new Toggle(); + _toggle.RegisterValueChangedCallback(evt => + { + IsSelected = evt.newValue; + _window.CheckRelationshipSelection(toggledItem, IsSelected); + }); + Add(_toggle); + Add(new Label(_name)); + } + } + + public class Migrator : EditorWindow + { + [MenuItem(MSSConstants.WINDOW_PATH + "/Tools/Migrator")] + private static void ShowWindow() + { + var window = GetWindow<Migrator>(); + window.titleContent = new GUIContent("Migrator"); + window.Show(); + } + + private List<MigratedItemElement<ModularShader>> _shaderElements; + private List<MigratedItemElement<ShaderModule>> _moduleElements; + private List<MigratedItemElement<TemplateAsset>> _templateElements; + private List<MigratedItemElement<TemplateCollectionAsset>> _collectionElements; + + private void CreateGUI() + { + VisualElement root = rootVisualElement; + + var styleSheet = Resources.Load<StyleSheet>(MSSConstants.RESOURCES_FOLDER + (EditorGUIUtility.isProSkin ? "/MSSUIElements/MigratorDark" : "/MSSUIElements/MigratorLight")); + root.styleSheets.Add(styleSheet); + + var buttonRow = new VisualElement(); + buttonRow.AddToClassList("button-tab-area"); + + var selectedTab = new VisualElement(); + selectedTab.style.flexGrow = 1; + + VisualElement exportRoot = new VisualElement(); + exportRoot.style.flexGrow = 1; + SetupExport(exportRoot); + + VisualElement importRoot = new VisualElement(); + importRoot.style.flexGrow = 1; + SetupImport(importRoot); + + var tabButton = new Button(); + tabButton.text = "Export"; + tabButton.AddToClassList("button-tab"); + tabButton.clicked += () => + { + foreach (var button in buttonRow.Children()) + if(button.ClassListContains("button-tab-selected")) + button.RemoveFromClassList("button-tab-selected"); + + tabButton.AddToClassList("button-tab-selected"); + + selectedTab.Clear(); + selectedTab.Add(exportRoot); + }; + buttonRow.Add(tabButton); + + var secondTabButton = new Button(); + secondTabButton.text = "Import"; + secondTabButton.AddToClassList("button-tab"); + secondTabButton.clicked += () => + { + foreach (var button in buttonRow.Children()) + if(button.ClassListContains("button-tab-selected")) + button.RemoveFromClassList("button-tab-selected"); + + secondTabButton.AddToClassList("button-tab-selected"); + + selectedTab.Clear(); + selectedTab.Add(importRoot); + }; + + buttonRow.Add(secondTabButton); + + selectedTab.Add(exportRoot); + tabButton.AddToClassList("button-tab-selected"); + root.Add(buttonRow); + root.Add(selectedTab); + } + + private void SetupImport(VisualElement importRoot) + { + MigratedAssets assets = null; + + var scrollView = new ScrollView(ScrollViewMode.Vertical); + scrollView.style.flexGrow = 1; + + var importButton = new Button(); + importButton.text = "Import"; + importButton.SetEnabled(false); + importButton.clicked += () => Import(assets); + + var loadButton = new Button(); + loadButton.text = "Load file"; + loadButton.clicked += () => + { + string assetPath = EditorUtility.OpenFilePanel("Export", "Assets", "json"); + if (string.IsNullOrWhiteSpace(assetPath)) return; + + assets = JsonUtility.FromJson<MigratedAssets>(File.ReadAllText(assetPath)); + + importButton.SetEnabled(true); + + scrollView.Clear(); + var label = new Label("Modular Shaders"); + label.AddToClassList("title"); + scrollView.Add(label); + + foreach (var shader in assets.modularShaders) + scrollView.Add(new Label($"{shader.name} ({shader.shaderId})")); + + label = new Label("Shader Modules"); + label.AddToClassList("title"); + scrollView.Add(label); + + foreach (var module in assets.shaderModules) + scrollView.Add(new Label($"{module.name} ({module.moduleId})")); + + label = new Label("Template Assets"); + label.AddToClassList("title"); + scrollView.Add(label); + + foreach (var template in assets.templates) + scrollView.Add(new Label($"{Path.GetFileNameWithoutExtension(template.path)}")); + + label = new Label("Template Collection Assets"); + label.AddToClassList("title"); + scrollView.Add(label); + + foreach (var collection in assets.templateCollections) + scrollView.Add(new Label($"{Path.GetFileNameWithoutExtension(collection.path)}")); + }; + + importRoot.Add(loadButton); + importRoot.Add(scrollView); + importRoot.Add(importButton); + } + + private static void Import(MigratedAssets assets) + { + foreach (var asset in assets.templates) + { + Directory.CreateDirectory(Path.GetDirectoryName(asset.path)); + File.WriteAllText(Path.ChangeExtension(asset.path, MSSConstants.TEMPLATE_EXTENSION) ?? string.Empty, asset.content); + if(File.Exists(asset.path)) + File.Delete(asset.path); + } + + foreach (var asset in assets.templateCollections) + { + Directory.CreateDirectory(Path.GetDirectoryName(asset.path)); + File.WriteAllText(Path.ChangeExtension(asset.path, MSSConstants.TEMPLATE_COLLECTION_EXTENSION) ?? string.Empty, asset.content); + if(File.Exists(asset.path)) + File.Delete(asset.path); + } + + AssetDatabase.Refresh(); + + foreach (var asset in assets.shaderModules) + { + var module = CreateInstance<ShaderModule>(); + module.Id = asset.moduleId; + module.Name = asset.name; + module.Description = asset.description; + module.Version = asset.version; + module.Author = asset.author; + module.ModuleDependencies = new List<string>(asset.moduleDependencies); + module.IncompatibleWith = new List<string>(asset.incompatibleWith); + module.AdditionalSerializedData = asset.additionalSerializedData; + module.EnableProperties = new List<EnableProperty>(asset.enableProperties); + module.Properties = new List<Property>(asset.properties); + module.Templates = asset.templates.Select(x => + { + var template = new ModuleTemplate + { + Keywords = new List<string>(x.keywords), + Queue = x.queue, + NeedsVariant = x.needsVariant + }; + + if (string.IsNullOrWhiteSpace(x.collectionSubId)) + { + var t = assets.templates.Find(y => y.id == x.templateReference); + if (t == null) return template; + var st = AssetDatabase.LoadAssetAtPath<TemplateAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_EXTENSION)); + template.Template = st; + } + else + { + var t = assets.templateCollections.Find(y => y.id == x.templateReference); + if (t == null) return template; + var st = AssetDatabase.LoadAssetAtPath<TemplateCollectionAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_COLLECTION_EXTENSION)); + template.Template = st.Templates.Find(y => x.collectionSubId.Equals(y.name)); + } + + return template; + }).ToList(); + module.Functions = asset.functions.Select(x => + { + var function = new ShaderFunction + { + Name = x.name, + Queue = x.queue, + AppendAfter = x.appendAfter, + CodeKeywords = new List<string>(x.codeKeywords), + UsedVariables = new List<Variable>(x.usedVariables), + VariableKeywords = new List<string>(x.variableKeywords) + }; + + if (string.IsNullOrWhiteSpace(x.collectionSubId)) + { + var t = assets.templates.Find(y => y.id == x.templateReference); + if (t == null) return function; + var st = AssetDatabase.LoadAssetAtPath<TemplateAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_EXTENSION)); + function.ShaderFunctionCode = st; + } + else + { + var t = assets.templateCollections.Find(y => y.id == x.templateReference); + if (t == null) return function; + var st = AssetDatabase.LoadAssetAtPath<TemplateCollectionAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_COLLECTION_EXTENSION)); + function.ShaderFunctionCode = st.Templates.Find(y => x.collectionSubId.Equals(y.name)); + } + + return function; + }).ToList(); + Directory.CreateDirectory(Path.GetDirectoryName(asset.path)); + AssetDatabase.CreateAsset(module, asset.path); + } + + AssetDatabase.Refresh(); + + foreach (var asset in assets.modularShaders) + { + var shader = CreateInstance<ModularShader>(); + shader.Id = asset.shaderId; + shader.Name = asset.name; + shader.Description = asset.description; + shader.Version = asset.version; + shader.Author = asset.author; + shader.ShaderPath = asset.shaderPath; + shader.CustomEditor = asset.customEditor; + shader.LockBaseModules = asset.lockBaseModules; + shader.UseTemplatesForProperties = asset.useTemplatesForProperties; + shader.Properties = new List<Property>(asset.properties); + shader.BaseModules = asset.baseModules.Select(x => { return AssetDatabase.LoadAssetAtPath<ShaderModule>(assets.shaderModules.Find(y => y.id == x).path); }).ToList(); + + shader.AdditionalModules = asset.additionalModules.Select(x => { return AssetDatabase.LoadAssetAtPath<ShaderModule>(assets.shaderModules.Find(y => y.id == x).path); }).ToList(); + + if (string.IsNullOrWhiteSpace(asset.shaderCollectionSubId)) + { + var t = assets.templates.Find(y => y.id == asset.shaderTemplateReference); + if (t != null) + { + var st = AssetDatabase.LoadAssetAtPath<TemplateAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_EXTENSION)); + shader.ShaderTemplate = st; + } + } + else + { + var t = assets.templateCollections.Find(y => y.id == asset.shaderTemplateReference); + if (t != null) + { + var st = AssetDatabase.LoadAssetAtPath<TemplateCollectionAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_COLLECTION_EXTENSION)); + shader.ShaderTemplate = st.Templates.Find(y => asset.shaderCollectionSubId.Equals(y.name)); + } + } + + if (string.IsNullOrWhiteSpace(asset.propertiesCollectionSubId)) + { + var t = assets.templates.Find(y => y.id == asset.propertiesTemplateReference); + if (t != null) + { + var st = AssetDatabase.LoadAssetAtPath<TemplateAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_EXTENSION)); + shader.ShaderPropertiesTemplate = st; + } + } + else + { + var t = assets.templateCollections.Find(y => y.id == asset.propertiesTemplateReference); + if (t != null) + { + var st = AssetDatabase.LoadAssetAtPath<TemplateCollectionAsset>(Path.ChangeExtension(t.path, MSSConstants.TEMPLATE_COLLECTION_EXTENSION)); + shader.ShaderPropertiesTemplate = st.Templates.Find(y => asset.propertiesCollectionSubId.Equals(y.name)); + } + } + + Directory.CreateDirectory(Path.GetDirectoryName(asset.path)); + AssetDatabase.CreateAsset(shader, asset.path); + } + + AssetDatabase.Refresh(); + } + + private void SetupExport(VisualElement exportRoot) + { + _shaderElements = new List<MigratedItemElement<ModularShader>>(); + _moduleElements = new List<MigratedItemElement<ShaderModule>>(); + _templateElements = new List<MigratedItemElement<TemplateAsset>>(); + _collectionElements = new List<MigratedItemElement<TemplateCollectionAsset>>(); + + var templateAssets = FindAssetsByType<TemplateAsset>().Where(x => !AssetDatabase.IsSubAsset(x)).ToArray(); + var collectionAssets = FindAssetsByType<TemplateCollectionAsset>(); + var shaderModules = FindAssetsByType<ShaderModule>(); + var modularShaders = FindAssetsByType<ModularShader>(); + + var scrollView = new ScrollView(ScrollViewMode.Vertical); + scrollView.style.flexGrow = 1; + + Foldout shadersFoldout = new Foldout(); + shadersFoldout.text = "Modular Shaders"; + foreach (var modularShader in modularShaders) + { + var element = new MigratedItemElement<ModularShader>(this, modularShader, $"{modularShader.Name} ({modularShader.name})"); + shadersFoldout.Add(element); + _shaderElements.Add(element); + } + + Foldout modulesFoldout = new Foldout(); + modulesFoldout.text = "Shader Modules"; + foreach (var shaderModule in shaderModules) + { + var element = new MigratedItemElement<ShaderModule>(this, shaderModule, $"{shaderModule.Name} ({shaderModule.name})"); + modulesFoldout.Add(element); + _moduleElements.Add(element); + } + + Foldout templatesFoldout = new Foldout(); + templatesFoldout.text = "Template Assets"; + foreach (var template in templateAssets) + { + var element = new MigratedItemElement<TemplateAsset>(this, template, template.name); + templatesFoldout.Add(element); + _templateElements.Add(element); + } + + Foldout collectionsFoldout = new Foldout(); + collectionsFoldout.text = "Template Collection Assets"; + foreach (var collection in collectionAssets) + { + var element = new MigratedItemElement<TemplateCollectionAsset>(this, collection, collection.name); + collectionsFoldout.Add(element); + _collectionElements.Add(element); + } + + var bottomRow = new VisualElement(); + bottomRow.style.flexDirection = FlexDirection.Row; + + var b = new Button(); + b.text = "Select all"; + b.clicked += () => + { + _shaderElements.ForEach(x => x.IsSelected = true); + _moduleElements.ForEach(x => x.IsSelected = true); + _templateElements.ForEach(x => x.IsSelected = true); + _collectionElements.ForEach(x => x.IsSelected = true); + }; + bottomRow.Add(b); + + b = new Button(); + b.text = "Unselect all"; + b.clicked += () => + { + _shaderElements.ForEach(x => x.IsSelected = false); + _moduleElements.ForEach(x => x.IsSelected = false); + _templateElements.ForEach(x => x.IsSelected = false); + _collectionElements.ForEach(x => x.IsSelected = false); + }; + bottomRow.Add(b); + var v = new VisualElement(); + v.style.flexGrow = 1; + bottomRow.Add(v); + b = new Button(); + b.text = "Save"; + b.clicked += ExportSelected; + bottomRow.Add(b); + + + + scrollView.Add(shadersFoldout); + scrollView.Add(modulesFoldout); + scrollView.Add(templatesFoldout); + scrollView.Add(collectionsFoldout); + exportRoot.Add(scrollView); + exportRoot.Add(bottomRow); + } + + private void ExportSelected() + { + var assets = new MigratedAssets(); + + string finalPath = EditorUtility.SaveFilePanel("Export", "Assets", "migrationData", "json"); + if (string.IsNullOrWhiteSpace(finalPath)) return; + + var idByTemplate = new Dictionary<TemplateAsset, long>(); + var idByCollection = new Dictionary<TemplateAsset, long>(); + var idByModule = new Dictionary<ShaderModule, long>(); + + long currentId = 1; + foreach (var asset in _templateElements.Where(x => x.IsSelected).Select(x => x.ToggledItem)) + { + idByTemplate.Add(asset, currentId); + string path = AssetDatabase.GetAssetPath(asset); + assets.templates.Add(new MigratedTemplate + { + id = currentId, + content = File.ReadAllText(path), + path = path + }); + currentId++; + } + foreach (var asset in _collectionElements.Where(x => x.IsSelected).Select(x => x.ToggledItem)) + { + foreach (var tmp in asset.Templates) + { + idByCollection.Add(tmp, currentId); + } + string path = AssetDatabase.GetAssetPath(asset); + assets.templateCollections.Add(new MigratedCollection + { + id = currentId, + content = File.ReadAllText(path), + path = path + }); + currentId++; + } + foreach (var asset in _moduleElements.Where(x => x.IsSelected).Select(x => x.ToggledItem)) + { + idByModule.Add(asset, currentId); + string path = AssetDatabase.GetAssetPath(asset); + assets.shaderModules.Add(new MigratedShaderModule + { + id = currentId, + path = path, + moduleId = asset.Id, + name = asset.Name, + version = asset.Version, + author = asset.Author, + description = asset.Description, + enableProperties = asset.EnableProperties, + properties = new List<Property>(asset.Properties), + moduleDependencies = new List<string>(asset.ModuleDependencies), + incompatibleWith = new List<string>(asset.IncompatibleWith), + additionalSerializedData = asset.AdditionalSerializedData, + templates = new List<MigratedModuleTemplate>(asset.Templates.Select(x => FromModuleTemplate(x, idByTemplate, idByCollection))), + functions = new List<MigratedShaderFunction>(asset.Functions.Select(x => FromModuleFunction(x, idByTemplate, idByCollection))) + }); + currentId++; + } + foreach (var asset in _shaderElements.Where(x => x.IsSelected).Select(x => x.ToggledItem)) + { + string path = AssetDatabase.GetAssetPath(asset); + var shader = new MigratedModularShader + { + id = currentId, + path = path, + shaderId = asset.Id, + name = asset.Name, + version = asset.Version, + author = asset.Author, + description = asset.Description, + properties = new List<Property>(asset.Properties), + additionalSerializedData = asset.AdditionalSerializedData, + customEditor = asset.CustomEditor, + shaderPath = asset.ShaderPath, + lockBaseModules = asset.LockBaseModules, + useTemplatesForProperties = asset.UseTemplatesForProperties, + baseModules = asset.BaseModules.Select(x => idByModule[x]).ToList(), + additionalModules = asset.AdditionalModules.Select(x => idByModule[x]).ToList() + + }; + + if (asset.ShaderTemplate == null) shader.shaderTemplateReference = 0; + else if (idByTemplate.ContainsKey(asset.ShaderTemplate)) + { + shader.shaderTemplateReference = idByTemplate[asset.ShaderTemplate]; + } + else if (idByCollection.ContainsKey(asset.ShaderTemplate)) + { + shader.shaderTemplateReference = idByTemplate[asset.ShaderTemplate]; + shader.shaderCollectionSubId = asset.ShaderTemplate.name; + } + + if (asset.ShaderPropertiesTemplate == null) shader.propertiesTemplateReference = 0; + else if (idByTemplate.ContainsKey(asset.ShaderPropertiesTemplate)) + { + shader.propertiesTemplateReference = idByTemplate[asset.ShaderPropertiesTemplate]; + } + else if (idByCollection.ContainsKey(asset.ShaderPropertiesTemplate)) + { + shader.propertiesTemplateReference = idByTemplate[asset.ShaderPropertiesTemplate]; + shader.propertiesCollectionSubId = asset.ShaderPropertiesTemplate.name; + } + + assets.modularShaders.Add(shader); + + currentId++; + } + + File.WriteAllText(finalPath, JsonUtility.ToJson(assets)); + } + + private static MigratedModuleTemplate FromModuleTemplate(ModuleTemplate original, Dictionary<TemplateAsset, long> idByTemplate, Dictionary<TemplateAsset, long> idByCollection) + { + + var template = new MigratedModuleTemplate + { + keywords = new List<string>(original.Keywords), + queue = original.Queue, + needsVariant = original.NeedsVariant + }; + + if (original.Template == null) return template; + + if (idByTemplate.ContainsKey(original.Template)) + { + template.templateReference = idByTemplate[original.Template]; + } + else if (idByCollection.ContainsKey(original.Template)) + { + template.templateReference = idByCollection[original.Template]; + template.collectionSubId = original.Template.name; + } + + return template; + } + + private static MigratedShaderFunction FromModuleFunction(ShaderFunction original, Dictionary<TemplateAsset, long> idByTemplate, Dictionary<TemplateAsset, long> idByCollection) + { + + var template = new MigratedShaderFunction + { + name = original.Name, + queue = original.Queue, + appendAfter = original.AppendAfter, + usedVariables = original.UsedVariables, + codeKeywords = original.CodeKeywords, + variableKeywords = original.VariableKeywords, + }; + if (original.ShaderFunctionCode == null) return template; + + if (idByTemplate.ContainsKey(original.ShaderFunctionCode)) + { + template.templateReference = idByTemplate[original.ShaderFunctionCode]; + } + else if (idByCollection.ContainsKey(original.ShaderFunctionCode)) + { + template.templateReference = idByCollection[original.ShaderFunctionCode]; + template.collectionSubId = original.ShaderFunctionCode.name; + } + + return template; + } + + internal void CheckRelationshipSelection<T>(T item, bool isToggled) + { + switch (item) + { + case ModularShader s: + ToggleShaderDependencies(s, isToggled); + break; + case ShaderModule m: + ToggleModuleDependencies(m, isToggled); + break; + case TemplateAsset t: + ToggleTemplateDependencies(t, isToggled); + break; + case TemplateCollectionAsset c: + ToggleCollectionDependencies(c, isToggled); + break; + } + } + + private void ToggleShaderDependencies(ModularShader shader, bool isToggled) + { + if (!isToggled) return; + + foreach (var element in _moduleElements.Where(x => shader.BaseModules.Concat(shader.AdditionalModules).Contains(x.ToggledItem))) + { + element.IsSelected = true; + ToggleModuleDependencies(element.ToggledItem, true); + } + + foreach (var element in _templateElements.Where(x => shader.ShaderTemplate == x.ToggledItem || shader.ShaderPropertiesTemplate == x.ToggledItem)) + { + element.IsSelected = true; + } + + foreach (var element in _collectionElements.Where(x => x.ToggledItem.Templates.Contains(shader.ShaderTemplate) || x.ToggledItem.Templates.Contains(shader.ShaderPropertiesTemplate))) + { + element.IsSelected = true; + } + } + + private void ToggleModuleDependencies(ShaderModule module, bool isToggled) + { + if (isToggled) + { + foreach (var element in _templateElements.Where(x => + module.Templates.Select(y => y.Template).Concat(module.Functions.Select(z => z.ShaderFunctionCode)).Contains(x.ToggledItem))) + { + element.IsSelected = true; + } + + foreach (var element in _collectionElements.Where(x => + module.Templates.Select(y => y.Template).Concat(module.Functions.Select(z => z.ShaderFunctionCode)).Any(y => x.ToggledItem.Templates.Contains(y)))) + { + element.IsSelected = true; + } + } + else + { + foreach (var element in _shaderElements.Where(x => x.ToggledItem.BaseModules.Concat(x.ToggledItem.AdditionalModules).Contains(module))) + { + element.IsSelected = false; + } + } + } + + private void ToggleTemplateDependencies(TemplateAsset template, bool isToggled) + { + if (isToggled) return; + + foreach (var element in _moduleElements.Where(x => + x.ToggledItem.Templates.Select(y => y.Template).Concat(x.ToggledItem.Functions.Select(z => z.ShaderFunctionCode)).Contains(template))) + { + element.IsSelected = false; + ToggleModuleDependencies(element.ToggledItem, false); + } + + foreach (var element in _shaderElements.Where(x => x.ToggledItem.ShaderTemplate == template || x.ToggledItem.ShaderPropertiesTemplate == template)) + { + element.IsSelected = false; + } + } + + private void ToggleCollectionDependencies(TemplateCollectionAsset template, bool isToggled) + { + if (isToggled) return; + + foreach (var element in _moduleElements.Where(x => + x.ToggledItem.Templates.Select(y => y.Template).Concat(x.ToggledItem.Functions.Select(z => z.ShaderFunctionCode)).Any(y => template.Templates.Contains(y)))) + { + element.IsSelected = false; + ToggleModuleDependencies(element.ToggledItem, false); + } + + foreach (var element in _shaderElements.Where(x => template.Templates.Contains(x.ToggledItem.ShaderTemplate) ||template.Templates.Contains(x.ToggledItem.ShaderPropertiesTemplate))) + { + element.IsSelected = false; + } + } + + private static T[] FindAssetsByType<T>() where T : Object + { + List<T> assets = new List<T>(); + AssetDatabase.Refresh(); + string[] guids = AssetDatabase.FindAssets($"t:{typeof(T).ToString().Replace("UnityEngine.", "")}"); + for (int i = 0; i < guids.Length; i++) + { + string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]); + T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath); + if (asset != null) + assets.Add(asset); + } + return assets.ToArray(); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs.meta new file mode 100644 index 00000000..c7276d4a --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 905f1d7d180a43f46900ed1f09f40b2e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs new file mode 100644 index 00000000..377b1bef --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; + +namespace Poiyomi.ModularShaderSystem.Debug +{ + public interface IModularShaderDebuggerTab + { + VisualElement TabContainer { get; set; } + + string TabName { get; set; } + + void UpdateTab(ModularShader shader); + } + + public class ModularShaderDebugger : EditorWindow + { + [MenuItem(MSSConstants.WINDOW_PATH + "/Modular Shader Debugger")] + public static void ShowExample() + { + ModularShaderDebugger wnd = GetWindow<ModularShaderDebugger>(); + wnd.titleContent = new GUIContent("Modular Shader Debugger"); + + if (wnd.position.width < 400 || wnd.position.height < 400) + { + Rect size = wnd.position; + size.width = 1280; + size.height = 720; + wnd.position = size; + } + + wnd.Show(); + } + + private ObjectField _modularShaderField; + private ModularShader _modularShader; + private VisualElement _selectedTab; + + private List<IModularShaderDebuggerTab> _tabs; + + public void CreateGUI() + { + VisualElement root = rootVisualElement; + var styleSheet = Resources.Load<StyleSheet>(MSSConstants.RESOURCES_FOLDER + "/MSSUIElements/ModularShaderDebuggerStyle"); + var darkThemeStyleSheet = EditorGUIUtility.Load("StyleSheets/Generated/DefaultCommonDark_inter.uss.asset") as StyleSheet; + root.styleSheets.Add(darkThemeStyleSheet); + root.styleSheets.Add(styleSheet); + root.style.backgroundColor = new Color(0.2196079f, 0.2196079f, 0.2196079f, 1); + _modularShaderField = new ObjectField("Shader"); + _modularShaderField.style.flexGrow = 1; + _modularShaderField.objectType = typeof(ModularShader); + _modularShaderField.RegisterCallback<ChangeEvent<UnityEngine.Object>>(e => + { + if (_modularShaderField.value != null) + _modularShader = (ModularShader)_modularShaderField.value; + else + _modularShader = null; + + UpdateTabs(); + }); + + _tabs = new List<IModularShaderDebuggerTab>(); + + var topArea = new VisualElement(); + topArea.AddToClassList("top-area"); + var refreshButton = new Button(); + refreshButton.AddToClassList("refresh-button"); + refreshButton.clicked += () => UpdateTabs(); + + var buttonRow = new VisualElement(); + buttonRow.AddToClassList("button-tab-area"); + + _selectedTab = new VisualElement(); + _selectedTab.style.flexGrow = 1; + + topArea.Add(_modularShaderField); + topArea.Add(refreshButton); + + root.Add(topArea); + root.Add(buttonRow); + root.Add(_selectedTab); + + var tabTypes = AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(x => x.GetTypes()) + .Where(x => x.GetInterface(typeof(IModularShaderDebuggerTab).FullName) != null) + .OrderBy(x => x.Name) + .ToList(); + + foreach (var type in tabTypes) + { + var tab = Activator.CreateInstance(type) as IModularShaderDebuggerTab; + + var tabButton = new Button(); + tabButton.text = tab?.TabName; + tabButton.AddToClassList("button-tab"); + + tabButton.clicked += () => + { + foreach (var button in buttonRow.Children()) + if(button.ClassListContains("button-tab-selected")) + button.RemoveFromClassList("button-tab-selected"); + + tabButton.AddToClassList("button-tab-selected"); + + _selectedTab.Clear(); + _selectedTab.Add(tab.TabContainer); + }; + + buttonRow.Add(tabButton); + _tabs.Add(tab); + } + + if (_tabs.Count == 0) return; + var graph = _tabs.FirstOrDefault(x => x.GetType() == typeof(TemplateGraph)); + var timeline = _tabs.FirstOrDefault(x => x.GetType() == typeof(FunctionTimeline)); + + if (timeline != null) + { + var index = _tabs.IndexOf(timeline); + var button = buttonRow[index]; + _tabs.RemoveAt(index); + buttonRow.RemoveAt(index); + _tabs.Insert(0, timeline); + buttonRow.Insert(0, button); + } + if (graph != null) + { + var index = _tabs.IndexOf(graph); + var button = buttonRow[index]; + _tabs.RemoveAt(index); + buttonRow.RemoveAt(index); + _tabs.Insert(0, graph); + buttonRow.Insert(0, button); + } + + buttonRow[0].AddToClassList("button-tab-selected"); + _selectedTab.Add(_tabs[0].TabContainer); + } + + private void UpdateTabs() + { + foreach (IModularShaderDebuggerTab tab in _tabs) + { + tab.UpdateTab(_modularShader); + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs.meta new file mode 100644 index 00000000..7f5ba831 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da3cfc39b3d4bbe45baa0af3e383d910 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs new file mode 100644 index 00000000..b59a3434 --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs @@ -0,0 +1,20 @@ +using UnityEditor; +using UnityEngine.UIElements; +using Poiyomi.ModularShaderSystem.Debug; + +namespace Poiyomi.ModularShaderSystem.UI +{ + public class TextPopup : EditorWindow + { + public string Text; + private void CreateGUI() + { + var viewer = new CodeViewElement(); + viewer.Text = Text; + viewer.StretchToParentSize(); + var darkThemeStyleSheet = EditorGUIUtility.Load("StyleSheets/Generated/DefaultCommonDark_inter.uss.asset") as StyleSheet; + rootVisualElement.styleSheets.Add(darkThemeStyleSheet); + rootVisualElement.Add(viewer); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs.meta new file mode 100644 index 00000000..35db26ed --- /dev/null +++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1bc7c24875ba994fbef21ccbc464432 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |