summaryrefslogtreecommitdiff
path: root/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-12-27 00:56:58 -0500
committerFreya Murphy <freya@freyacat.org>2024-12-27 00:58:02 -0500
commit799e6680d40119dc9c2a9e0b320054a40324bebe (patch)
treedbcd308d59eb6e4f937a5547dd77d9f91d4fec20 /VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors
parentmove to self host (diff)
downloadunityprojects-799e6680d40119dc9c2a9e0b320054a40324bebe.tar.gz
unityprojects-799e6680d40119dc9c2a9e0b320054a40324bebe.tar.bz2
unityprojects-799e6680d40119dc9c2a9e0b320054a40324bebe.zip
VRCSDK3Avatars found!
Diffstat (limited to 'VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors')
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs651
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/FunctionTimeline.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs293
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Components/TemplateGraph.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs30
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/EnablePropertyDrawer.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs32
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/FunctionPropertyDrawer.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs29
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ModuleTemplatePropertyDrawer.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs52
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/PropertyAttributeDrawer.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs289
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/ShaderPropertyDrawer.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs43
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Drawers/VariablePropertyDrawer.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs104
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/CodeViewElement.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs324
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/InspectorList.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs56
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/LabelField.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs315
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/ModuleInspectorList.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs30
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Elements/VariableField.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs68
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ModularShaderEditor.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs23
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/ShaderModuleEditor.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs20
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Inspectors/TemplateAssetEditor.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs814
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/Migrator.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs153
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/ModularShaderDebugger.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs20
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ModularShaderSystem/Editors/Windows/TextPopup.cs.meta11
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: