├── .github └── workflows │ └── main.yml ├── .idea └── .idea.shadergraphenhancer.dir │ └── .idea │ ├── .gitignore │ ├── encodings.xml │ ├── indexLayout.xml │ └── vcs.xml ├── Editor.meta ├── Editor ├── KeyboardShortcut.cs ├── KeyboardShortcut.cs.meta ├── PackageCacheOverrider.cs ├── PackageCacheOverrider.cs.meta ├── Seonghwan.ShaderGraph.Enhancer.Editor.asmdef └── Seonghwan.ShaderGraph.Enhancer.Editor.asmdef.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── package.json └── package.json.meta /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Publish to npm 2 | 3 | # Controls when the action will run. 4 | # In this case, I'm saying on each release event when it's specifically a new release publish, the types: [published] is required here, 5 | # since releases could also be updated or deleted, we only want to publish to npm when a new release is created (published). 6 | on: 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 15 | - uses: actions/checkout@v2 16 | #Install Node.js, with the version 12 and using the registry URL of npm, this could be changed to a custom registry or the GitHub registry. 17 | - uses: actions/setup-node@v1 18 | with: 19 | node-version: 12 20 | registry-url: https://registry.npmjs.org/ 21 | 22 | # Command to install the package dependencies 23 | - run: yarn install 24 | 25 | # Publish to npm 26 | - run: npm publish --access public 27 | env: 28 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 29 | -------------------------------------------------------------------------------- /.idea/.idea.shadergraphenhancer.dir/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /modules.xml 6 | /contentModel.xml 7 | /projectSettingsUpdater.xml 8 | /.idea.shadergraphenhancer.iml 9 | # Datasource local storage ignored files 10 | /dataSources/ 11 | /dataSources.local.xml 12 | # Editor-based HTTP Client requests 13 | /httpRequests/ 14 | -------------------------------------------------------------------------------- /.idea/.idea.shadergraphenhancer.dir/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/.idea.shadergraphenhancer.dir/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/.idea.shadergraphenhancer.dir/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30a0af1b4f1035543a062a3f9ca9a6e7 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/KeyboardShortcut.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | using System.Runtime.CompilerServices; 6 | using UnityEditor; 7 | using UnityEngine; 8 | using UnityEngine.Assertions; 9 | using UnityEngine.UIElements; 10 | 11 | namespace ShaderGraphEnhancer 12 | { 13 | #if SGE_INSTALLED 14 | using UnityEditor.ShaderGraph; 15 | using UnityEditor.ShaderGraph.Drawing; 16 | #else 17 | public class MaterialGraphView {} 18 | public class GraphData { } 19 | 20 | public class AbstractMaterialNode { } 21 | #endif 22 | 23 | internal class KeyboardShortcutHelper 24 | { 25 | private MaterialGraphView GraphView { get; } 26 | private GraphData GraphData { get; } 27 | 28 | public KeyboardShortcutHelper(MaterialGraphView graphView, GraphData graph) 29 | { 30 | #if SGE_INSTALLED 31 | GraphView = graphView; 32 | GraphData = graph; 33 | 34 | GraphView.RegisterCallback(OnKeyDown); 35 | #endif 36 | } 37 | 38 | void OnKeyDown(KeyDownEvent evt) { 39 | #if SGE_INSTALLED 40 | 41 | 42 | if (GraphData == null) return; 43 | 44 | Vector2 pos = evt.originalMousePosition; 45 | 46 | switch (evt.keyCode) { 47 | case KeyCode.O: 48 | CreateNode(() => new OneMinusNode(), pos); 49 | break; 50 | case KeyCode.M: 51 | CreateNode(() => new MultiplyNode(), pos); 52 | break; 53 | case KeyCode.L: 54 | CreateNode(() => new LerpNode(), pos); 55 | break; 56 | case KeyCode.D: 57 | CreateNode(() => new DivideNode(), pos); 58 | break; 59 | case KeyCode.S: 60 | CreateNode(() => new SubtractNode(), pos); 61 | break; 62 | case KeyCode.A: 63 | CreateNode(() => new AddNode(), pos); 64 | break; 65 | case KeyCode.F: 66 | CreateNode(() => new FractionNode(), pos); 67 | break; 68 | case KeyCode.Alpha1: 69 | CreateNode(() => new Vector1Node(), pos); 70 | break; 71 | case KeyCode.Alpha2: 72 | CreateNode(() => new Vector2Node(), pos); 73 | break; 74 | case KeyCode.Alpha3: 75 | CreateNode(() => new Vector3Node(), pos); 76 | break; 77 | case KeyCode.Alpha4: 78 | CreateNode(() => new Vector4Node(), pos); 79 | break; 80 | case KeyCode.Alpha5: 81 | CreateNode(() => new ColorNode(), pos); 82 | break; 83 | } 84 | #endif 85 | } 86 | 87 | private void CreateNode(Func createNode, Vector2 pos) { 88 | #if SGE_INSTALLED 89 | AbstractMaterialNode multiplyNode = createNode(); 90 | 91 | var drawState = multiplyNode.drawState; 92 | Vector2 p = GraphView.contentViewContainer.WorldToLocal(pos); 93 | 94 | drawState.position = new Rect(p.x, p.y, drawState.position.width, drawState.position.height); 95 | multiplyNode.drawState = drawState; 96 | GraphData.AddNode(multiplyNode); 97 | #endif 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /Editor/KeyboardShortcut.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3b5538db0b4781c4da1fe30e06739089 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/PackageCacheOverrider.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using UnityEditor; 4 | using UnityEngine; 5 | using UnityEngine.Assertions; 6 | using UnityEngine.Rendering; 7 | 8 | namespace ShaderGraphEnhancer 9 | { 10 | public class PackageCacheOverrider 11 | { 12 | const string pre = 13 | "[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(\"Seonghwan.ShaderGraph.Enhancer.Editor\")]"; 14 | 15 | [InitializeOnLoadMethod] 16 | private static void Override() 17 | { 18 | string packageCachePath = Application.dataPath.Replace("/Assets", "/Library/PackageCache"); 19 | DirectoryInfo packageCache = new DirectoryInfo(packageCachePath); 20 | 21 | DirectoryInfo shaderGraphPackage = null; 22 | foreach (DirectoryInfo directoryInfo in packageCache.GetDirectories()) 23 | { 24 | if (directoryInfo.Name.Contains("com.unity.shadergraph@")) 25 | { 26 | shaderGraphPackage = directoryInfo; 27 | break; 28 | } 29 | else 30 | { 31 | // Debug.Log(directoryInfo.Name); 32 | } 33 | } 34 | 35 | Assert.IsNotNull(shaderGraphPackage); 36 | 37 | string filePath = shaderGraphPackage.FullName + "/Editor/Drawing/Views/GraphEditorView.cs"; 38 | FileInfo file = new FileInfo(filePath); 39 | 40 | Assert.IsTrue(file.Exists); 41 | 42 | var defines = 43 | PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings 44 | .selectedBuildTargetGroup); 45 | 46 | string text = File.ReadAllText(filePath); 47 | string[] lines = File.ReadAllLines(filePath); 48 | if (text.Contains(pre)) 49 | { 50 | Run(); 51 | } 52 | else 53 | { 54 | var removed = defines.Replace("SGE_INSTALLED", ""); 55 | PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, 56 | removed); 57 | 58 | Install(); 59 | } 60 | 61 | 62 | if(!defines.Contains("SGE_INSTALLED")) 63 | PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, 64 | defines + ";SGE_INSTALLED"); 65 | 66 | EditorApplication.wantsToQuit += () => 67 | { 68 | var defines = 69 | PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings 70 | .selectedBuildTargetGroup); 71 | var removed = defines.Replace("SGE_INSTALLED", ""); 72 | PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, 73 | removed); 74 | 75 | return true; 76 | }; 77 | 78 | void Install() 79 | { 80 | StringBuilder sb = new StringBuilder(text.Length + 256); 81 | sb.AppendLine("#define SG_HOOKED"); 82 | for (int i = 0; i < lines.Length; i++) 83 | { 84 | if (lines[i].StartsWith("using")) { } 85 | } 86 | 87 | text = text.Replace("namespace UnityEditor.ShaderGraph.Drawing", 88 | $"{pre}\n\nnamespace UnityEditor.ShaderGraph.Drawing") 89 | .Replace("MaterialGraphView m_GraphView;", 90 | "public static Action installedCallback;\n\t\t\t\tMaterialGraphView m_GraphView;") 91 | .Replace("m_InspectorView.InitializeGraphSettings();", 92 | "m_InspectorView.InitializeGraphSettings();\n\t\t\t\tinstalledCallback?.Invoke(graphView, graph);") 93 | ; 94 | File.WriteAllText(filePath, text); 95 | 96 | var defines = 97 | PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings 98 | .selectedBuildTargetGroup); 99 | 100 | if(!defines.Contains("SGE_INSTALLED")) 101 | PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, 102 | defines + ";SGE_INSTALLED"); 103 | } 104 | 105 | void Run() 106 | { 107 | #if SGE_INSTALLED 108 | UnityEditor.ShaderGraph.Drawing.GraphEditorView.installedCallback += (v, g) => 109 | { 110 | KeyboardShortcutHelper keyboardShortcutHelper = new KeyboardShortcutHelper(v, g); 111 | }; 112 | #endif 113 | } 114 | } 115 | 116 | [MenuItem("Tools/ShaderGraph Key2Node/Force Resolve")] 117 | static void ForceInstall() 118 | { 119 | bool yes = EditorUtility.DisplayDialog("Force Resolve", "Resolve ShaderGraph Enhancer", 120 | "Resolve", "Cancel"); 121 | 122 | if (!yes) return; 123 | 124 | string packageCachePath = Application.dataPath.Replace("/Assets", "/Library/PackageCache"); 125 | DirectoryInfo packageCache = new DirectoryInfo(packageCachePath); 126 | 127 | DirectoryInfo shaderGraphPackage = null; 128 | foreach (DirectoryInfo directoryInfo in packageCache.GetDirectories()) 129 | { 130 | if (directoryInfo.Name.Contains("com.unity.shadergraph@")) 131 | { 132 | shaderGraphPackage = directoryInfo; 133 | break; 134 | } 135 | else 136 | { 137 | Debug.Log(directoryInfo.Name); 138 | } 139 | } 140 | 141 | Assert.IsNotNull(shaderGraphPackage); 142 | 143 | string filePath = shaderGraphPackage.FullName + "/Editor/Drawing/Views/GraphEditorView.cs"; 144 | 145 | string text = File.ReadAllText(filePath); 146 | string[] lines = File.ReadAllLines(filePath); 147 | 148 | StringBuilder sb = new StringBuilder(text.Length + 256); 149 | sb.AppendLine("#define SG_HOOKED"); 150 | for (int i = 0; i < lines.Length; i++) 151 | { 152 | if (lines[i].StartsWith("using")) { } 153 | } 154 | 155 | text = text.Replace("namespace UnityEditor.ShaderGraph.Drawing", 156 | $"{pre}\n\nnamespace UnityEditor.ShaderGraph.Drawing") 157 | .Replace("MaterialGraphView m_GraphView;", 158 | "public static Action installedCallback;\n\t\t\t\tMaterialGraphView m_GraphView;") 159 | .Replace("m_InspectorView.InitializeGraphSettings();", 160 | "m_InspectorView.InitializeGraphSettings();\n\t\t\t\tinstalledCallback?.Invoke(graphView, graph);") 161 | ; 162 | File.WriteAllText(filePath, text); 163 | 164 | var defines = 165 | PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings 166 | .selectedBuildTargetGroup); 167 | if(!defines.Contains("SGE_INSTALLED")) 168 | PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, 169 | defines + ";SGE_INSTALLED"); 170 | 171 | EditorApplication.wantsToQuit += () => 172 | { 173 | var defines = 174 | PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings 175 | .selectedBuildTargetGroup); 176 | var removed = defines.Replace("SGE_INSTALLED", ""); 177 | PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, 178 | removed); 179 | 180 | return true; 181 | }; 182 | } 183 | 184 | [MenuItem("Tools/ShaderGraph Key2Node/Clean Uninstall")] 185 | static void CleanRemove() 186 | { 187 | bool yes = EditorUtility.DisplayDialog("Uninstall", "Uninstalling ShaderGraph Enhancer package", 188 | "Uninstall", "Cancel"); 189 | 190 | if (!yes) return; 191 | 192 | var defines = 193 | PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings 194 | .selectedBuildTargetGroup); 195 | var removed = defines.Replace("SGE_INSTALLED", ""); 196 | PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, 197 | removed); 198 | UnityEditor.PackageManager.Client.Remove("com.unity.seonghwan.shadergraph-enhancer"); 199 | } 200 | } 201 | } -------------------------------------------------------------------------------- /Editor/PackageCacheOverrider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d92b52974c1c4af8bf7230c659d6b299 3 | timeCreated: 1620037281 -------------------------------------------------------------------------------- /Editor/Seonghwan.ShaderGraph.Enhancer.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Seonghwan.ShaderGraph.Enhancer.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:ade7125e800904674ba0c115208f7ed5", 6 | "GUID:be0903cd8e1546f498710afdc59db5eb", 7 | "GUID:df380645f10b7bc4b97d4f5eb6303d95", 8 | "GUID:3eae0364be2026648bf74846acb8a731", 9 | "GUID:de1079f91ae5b43499b7c7e4f1269884" 10 | ], 11 | "includePlatforms": [ 12 | "Editor" 13 | ], 14 | "excludePlatforms": [], 15 | "allowUnsafeCode": false, 16 | "overrideReferences": false, 17 | "precompiledReferences": [], 18 | "autoReferenced": true, 19 | "defineConstraints": [], 20 | "versionDefines": [], 21 | "noEngineReferences": false 22 | } -------------------------------------------------------------------------------- /Editor/Seonghwan.ShaderGraph.Enhancer.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 41edab38ebfb92441a3b24991f9e7cd6 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 seonghwan-dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: db1596a08e63b4c4aad23d6d5a0ad39f 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShaderGraph Key2Node 2 | Simple shortcut system into unity shader graph without modifying package by hacking style. 3 | 4 | Reference - [Unity Forum Thread](https://forum.unity.com/threads/keyboard-shortcuts-are-an-essential-and-missing-feature.852154/) by DigitalSalmon. 5 | 6 | ### Installation 7 | #### way 1. unity-package 8 | this repo can be imported as open unity package. 9 | add https://github.com/seonghwan-dev/shadergraph-key2node.git in package manager. 10 | #### way 2. npmjs 11 | in `Packages/manifest.json` ... 12 | 13 | ```json 14 | { 15 | "dependencies": { 16 | "kr.seonghwan.shadergraph-key2node": "1.0.0" 17 | } 18 | } 19 | ``` 20 | 21 | ```json 22 | { 23 | "scopedRegistries": [ 24 | { 25 | "name": "NPM", 26 | "url": "https://registry.npmjs.org", 27 | "scopes": [ 28 | "kr.seonghwan" 29 | ] 30 | } 31 | ] 32 | } 33 | ``` 34 | 35 | 36 | 37 | ### Uninstallation 38 | use menuitem `Tools/ShaderGraph Key2Node/Clean Uninstall` 39 | 40 | ### Trouble shooting 41 | run menuitem `Tools/ShaderGraph Key2Node/Force Resolve` 42 | 43 | ### Why should I use in this way? 44 | You don't have to. this is an expedient to avoid modifying the Shader Graph packge in every project. We can mofidy the original package caches in appdata, but it's local way so its not fit. I tried to implement this feature without editing original package by hooking several functions in loaded assembly on unity editor but it requires lots of things to consider. 45 | 46 | ### How this works? 47 | 1. script installs static delegates at the source file in `ProjectRoot`/Library/PackageCache/com.unity.shadergraph@x.x.x/editor/drawing/views/grapheditorview.cs 48 | 2. add callbacks to installed delegate when domain reloaded. (`[InitializedOnLoad]`) 49 | 3. when constructor of grapheditorview is called, installed delegates invoked. 50 | 51 | ### Warning 52 | - takes more time after compilation because of InitializeOnLoad 53 | - takes more time in starting unity editor because of installing delegates and callbacks every unity editor instance. 54 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9d5342a50bbf8864d9f38c924df09389 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kr.seonghwan.shadergraph-key2node", 3 | "displayName": "Shader Graph Key To Node", 4 | "version": "1.0.0", 5 | "unity": "2019.3", 6 | "description": "Foundation package allows to use node shorcut in hacky-style.", 7 | "homepage": "https://github.com/seonghwan-dev/shadergraph-key2node", 8 | "bugs": { 9 | "url": "https://github.com/seonghwan-dev/shadergraph-key2node/issues" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "ssh://git@github.com:seonghwan-dev/shadergraph-key2node.git" 14 | }, 15 | "license":"MIT", 16 | "author": { 17 | "name": "Seonghwan", 18 | "url": "https://github.com/seonghwan-dev" 19 | }, 20 | "keywords": [ 21 | "shadergraph", 22 | "shortcut", 23 | "utility", 24 | "unity" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: be877fdde47c01047b65f8330c03a8b8 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------