├── .gitignore
├── README.md
├── __init__.py
├── blender_manifest.toml
├── build.bat
├── build.py
├── images
├── Node Pie compositor.jpg
├── Node Pie geometry.jpg
├── Node Pie preferences.jpg
├── Node Pie serpens.jpg
└── Node Pie shader.jpg
└── node_pie
├── __init__.py
├── generate_node_layout.py
├── node_def_files
├── builtin
│ ├── CompositorNodeTree_1_0.jsonc
│ ├── CompositorNodeTree_4_1.jsonc
│ ├── GeometryNodeTree_1_0.jsonc
│ ├── GeometryNodeTree_3_5.jsonc
│ ├── GeometryNodeTree_3_6.jsonc
│ ├── GeometryNodeTree_4_0.jsonc
│ ├── GeometryNodeTree_4_1.jsonc
│ ├── GeometryNodeTree_4_2.jsonc
│ ├── GeometryNodeTree_4_3.jsonc
│ ├── GeometryNodeTree_4_4.jsonc
│ ├── ShaderNodeTree_1_0.jsonc
│ ├── ShaderNodeTree_4_0.jsonc
│ ├── ShaderNodeTree_4_1.jsonc
│ ├── ShaderNodeTree_4_2.jsonc
│ └── ShaderNodeTree_4_3.jsonc
├── node_def_base.jsonc
├── node_def_example.jsonc
├── sockets
│ ├── CompositorNodeTree_sockets_4_1.jsonc
│ ├── CompositorNodeTree_sockets_4_3.jsonc
│ ├── GeometryNodeTree_sockets_4_1.jsonc
│ ├── GeometryNodeTree_sockets_4_2.jsonc
│ ├── GeometryNodeTree_sockets_4_3.jsonc
│ ├── ShaderNodeTree_sockets_4_1.jsonc
│ ├── ShaderNodeTree_sockets_4_2.jsonc
│ └── ShaderNodeTree_sockets_4_3.jsonc
└── user
│ └── ScriptingNodesTree.jsonc
├── npie_btypes.py
├── npie_constants.py
├── npie_drawing.py
├── npie_helpers.py
├── npie_keymap.py
├── npie_menus.py
├── npie_node_def_file.py
├── npie_node_info.py
├── npie_prefs.py
├── npie_ui.py
├── operators
├── __init__.py
├── op_add_node.py
├── op_call_link_drag.py
├── op_call_node_pie.py
├── op_check_missing_nodes.py
├── op_copy_nodes_as_json.py
├── op_generate_socket_types_file.py
├── op_insert_node_pie.py
├── op_open_definition_file.py
├── op_reset_popularity.py
└── op_show_info.py
└── shaders
├── 2D_line_antialiased_frag.glsl
└── 2D_vert.glsl
/.gitignore:
--------------------------------------------------------------------------------
1 | shared/__pycache__
2 | *.pyc
3 | **/*.json
4 | !**/node_def_files/*.json
5 | **/npie_dev.py
6 | *.zip
7 | tokens.txt
8 | builds/
9 | .vscode
10 | .venv
11 | /docs_testing
12 | /node_pie/images
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Node Pie
2 |
3 | Node Pie is a Blender addon that converts the usual blender add node menu into a pie menu:
4 |
5 |
6 | https://github.com/strike-digital/node_pie/assets/59890307/98119501-9c5c-4795-9e5f-43f562525c28
7 |
8 |
9 |
10 | As well as that, the size of the buttons will change depending on how frequently you use each node; nodes you use a lot will have larger buttons, and nodes you don't use much will have smaller buttons:
11 |
12 |
13 |
14 |
15 |
16 |
17 | It also works in all Blender Node editors, and even in custom node editors created by python addons:
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | # Installation
26 | For automatic updates, install the addon from the Blender extensions platform, either from within Blender or from the website (Only for Blender 4.2 and above): https://extensions.blender.org/add-ons/nodepie/
27 |
28 | You can install also download the latest release here: https://github.com/strike-digital/node_pie/releases/latest
29 |
30 |
31 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 | import bpy
14 |
15 | bl_info = {
16 | "name": "Node Pie 1.2.42",
17 | "author": "Andrew Stevenson",
18 | "description": "Add nodes quickly with a pie menu",
19 | "blender": (4, 2, 0),
20 | "version": (1, 2, 42),
21 | "location": "Node editor > Shortcut",
22 | "doc_url": "https://github.com/strike-digital/node_pie/wiki",
23 | "tracker_url": "https://github.com/strike-digital/node_pie/issues",
24 | "category": "Node",
25 | }
26 |
27 |
28 | if not bpy.app.background:
29 | from .node_pie import npie_btypes
30 |
31 | npie_btypes.configure("node_pie", auto_register=True)
32 |
33 | def register():
34 | npie_btypes.register()
35 |
36 | def unregister():
37 | npie_btypes.unregister()
38 |
39 | else:
40 | register = unregister = lambda: None
41 |
--------------------------------------------------------------------------------
/blender_manifest.toml:
--------------------------------------------------------------------------------
1 | schema_version = "1.0.0"
2 |
3 | id = "NodePie"
4 | version = "1.2.42"
5 | name = "Node Pie"
6 | tagline = "Add nodes faster with a pie menu"
7 | maintainer = "Andrew Stevenson"
8 | type = "add-on"
9 | website = "https://blenderartists.org/t/node-pie-menu-free-addon/1402871"
10 | tags = ["User Interface", "Node"]
11 |
12 | blender_version_min = "4.2.0"
13 | license = ["SPDX:GPL-3.0-or-later"]
14 |
15 | [permissions]
16 | files = "Reading and writing config files"
--------------------------------------------------------------------------------
/build.bat:
--------------------------------------------------------------------------------
1 | "%~dp0.venv\Scripts\python.exe" -u build.py
2 | @REM python -u build.py
--------------------------------------------------------------------------------
/build.py:
--------------------------------------------------------------------------------
1 | if __name__ == "__main__":
2 | import requests
3 | import subprocess
4 | import argparse
5 | from zipfile import ZipFile
6 | from pathlib import Path
7 | import webbrowser
8 | from github import Github
9 | import click
10 | import re
11 |
12 | def update_init_file(init_file: Path, version: tuple):
13 | with open(init_file, "r") as file:
14 | text = file.read()
15 |
16 | version = tuple(version)
17 | str_version = [str(v) for v in version]
18 | matcher = '"name".*:.*,'
19 | name_match = re.findall(matcher, text)
20 | matcher = '"version".*:.*,'
21 | version_match = re.findall(matcher, text)
22 | text = text.replace(name_match[0], f'"name": "Node Pie {".".join(str_version)}",')
23 | text = text.replace(version_match[0], f'"version": {str(version)},')
24 |
25 | with open(init_file, "w") as file:
26 | file.write(text)
27 |
28 | def update_constants_file(constants_file, value):
29 | with open(constants_file, "r") as file:
30 | text = file.read()
31 |
32 | matcher = '__IS_DEV__.*=.*'
33 | name_match = re.findall(matcher, text)
34 | text = text.replace(name_match[0], f'__IS_DEV__ = {str(value)}')
35 |
36 | with open(constants_file, "w") as file:
37 | file.write(text)
38 |
39 | def update_manifest_file(manifest_file: Path, version: tuple):
40 | with open(manifest_file, 'r') as file:
41 | text = file.read()
42 |
43 | version = tuple(version)
44 | matcher = r'^version *= *"\d*.\d*.\d*"'
45 | # print(text)
46 | # matcher = '^version = '
47 | match = re.findall(matcher, text, flags=re.MULTILINE)
48 | str_version = [str(v) for v in version]
49 | text = text.replace(match[0], f'version = \"{".".join(str_version)}\"')
50 |
51 | with open(manifest_file, 'w') as file:
52 | file.write(text)
53 |
54 | # def multi_input(prompt=""):
55 | # """Get user input over multiple lines. Exit with Ctrl-Z"""
56 | # print(prompt)
57 | # contents = []
58 | # while True:
59 | # try:
60 | # line = input()
61 | # except EOFError:
62 | # break
63 | # contents.append(line)
64 | # return "\n".join(contents)
65 |
66 | # def multi_line_input(prompt):
67 | # dark = "#26242f"
68 | # win = tkinter.Tk()
69 |
70 | # w = h = 750
71 |
72 | # # get screen width and height
73 | # ws = win.winfo_screenwidth() # width of the screen
74 | # hs = win.winfo_screenheight() # height of the screen
75 |
76 | # # calculate x and y coordinates for the Tk root window
77 | # x = int((ws/2) - (w/2))
78 | # y = int((hs/2) - (h/2))
79 |
80 | # win.geometry(f"{w}x{h}+{x}+{y}")
81 | # win.config(bg=dark)
82 |
83 | # def confirm():
84 | # confirm.final_text = text.get("1.0", tkinter.END)
85 | # win.quit()
86 |
87 | # label = tkinter.Label(win, text=prompt)
88 | # label.configure(bg=dark, fg="white")
89 | # label.pack()
90 |
91 | # button = tkinter.Button(win, text="Confirm", width=20, command=confirm)
92 | # button.pack(side=tkinter.BOTTOM)
93 | # button.configure(bg=dark, fg="white")
94 |
95 | # text = tkinter.Text(win,)
96 | # scroll = tkinter.Scrollbar(win)
97 | # text.configure(yscrollcommand=scroll.set, bg=dark, fg="white")
98 | # text.focus_set()
99 | # text.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
100 |
101 | # scroll.config(command=text.yview)
102 | # scroll.pack(side=tkinter.RIGHT, fill=tkinter.BOTH)
103 |
104 | # win.mainloop()
105 | # return confirm.final_text
106 |
107 | # def multi_line_input(prompt):
108 | # ctk.set_appearance_mode("dark")
109 | # ctk.set_default_color_theme("dark-blue")
110 | # win = ctk.CTk()
111 |
112 | # w = h = 750
113 |
114 | # # get screen width and height
115 | # ws = win.winfo_screenwidth() # width of the screen
116 | # hs = win.winfo_screenheight() # height of the screen
117 |
118 | # # calculate x and y coordinates for the Tk root window
119 | # x = int((ws / 2) - (w / 2))
120 | # y = int((hs / 2) - (h / 2))
121 |
122 | # win.geometry(f"{w}x{h}+{x}+{y}")
123 |
124 | # # win.config(bg=dark)
125 |
126 | # def confirm():
127 | # confirm.final_text = textbox.textbox.get("1.0", ctk.END)
128 | # win.quit()
129 |
130 | # label = ctk.CTkLabel(win, text=prompt)
131 | # label.pack()
132 |
133 | # button = ctk.CTkButton(win, text="Confirm", width=20, command=confirm)
134 | # button.pack(side=ctk.BOTTOM)
135 | # # button.configure(bg=dark, fg="white")
136 |
137 | # textbox = ctk.CTkTextbox(win)
138 | # textbox.focus_set()
139 | # textbox.pack(fill=ctk.BOTH, side=ctk.LEFT)
140 | # # scroll = ctk.CTkScrollbar(win)
141 |
142 | # # textbox.configure(width=w - scroll.winfo_width() * 20)
143 | # textbox.configure(width=w)
144 |
145 | # # textbox.configure(yscrollcommand=scroll.set)
146 |
147 | # # scroll.configure(command=textbox.yview)
148 | # # scroll.pack(side=ctk.RIGHT, fill=ctk.BOTH)
149 |
150 | # win.mainloop()
151 | # return confirm.final_text
152 |
153 | def multi_line_input(prompt: str):
154 | return click.edit(text=prompt, editor="code -w -n", extension=".md")
155 |
156 | def main():
157 | parser = argparse.ArgumentParser()
158 | parser.add_argument(
159 | "-v",
160 | "--version",
161 | help="The version number to use, in the format '0.0.1'",
162 | default="",
163 | type=str,
164 | )
165 | args = parser.parse_args()
166 |
167 | ignore = ["images\\", "README", ".gitignore", "build.py", "build.bat", "vendor\\"]
168 | path = Path(__file__).parent
169 | files = [Path(f.decode("utf8")) for f in subprocess.check_output("git ls-files", shell=True).splitlines()]
170 | files = [f for f in files if not any(i in str(f) for i in ignore)]
171 | # pprint(files)
172 |
173 | # version
174 | if args.version:
175 | file_version = args.version.replace(".", "_")
176 | else:
177 | res = requests.get("https://api.github.com/repos/strike-digital/node_pie/releases").json()[0]
178 | latest_version: str = res["tag_name"]
179 | subversion = latest_version.split(".")[-1]
180 |
181 | file_version = "_".join(latest_version.split(".")[:-1] + [str(int(subversion) + 1)])
182 |
183 | print(file_version)
184 | version = tuple(int(f) for f in file_version.split("_"))
185 | update_init_file(path / "__init__.py", version)
186 | update_manifest_file(path / "blender_manifest.toml", version)
187 | # update_constants_file(constants_file, False)
188 |
189 | out_path = path / "builds" / f"node_pie_{file_version}.zip"
190 |
191 | print(f"Zipping {len(files)} files")
192 | with ZipFile(out_path, 'w') as z:
193 | # writing each file one by one
194 | for file in files:
195 | print(file, file.exists())
196 | # print(file)
197 | # z.write(file, arcname=str(file).replace("asset_bridge", f"asset_bridge_{file_version}"))
198 | z.write(file, arcname=str(f"node_pie_{file_version}" / file))
199 | print(f"Zipped: {out_path}")
200 |
201 | try:
202 | with open("tokens.txt", "r") as f:
203 | token = f.readlines()[0]
204 | except Exception:
205 | webbrowser.open(out_path.parent)
206 | return
207 |
208 | create_release = input("Do you want to create a release on github? (y/n) ")
209 |
210 | if create_release == "y":
211 | gh = Github(token)
212 | repo = gh.get_repo("strike-digital/node_pie")
213 | version = file_version.replace("_", ".")
214 | commit = repo.get_commits()[0]
215 |
216 | message = multi_line_input("Release message:")
217 | if message.rstrip().endswith("CANCEL"):
218 | print("Cancelled release")
219 | return
220 | release = repo.create_git_tag_and_release(
221 | tag=version,
222 | release_name="v" + version,
223 | release_message=message,
224 | tag_message=version,
225 | object=commit.sha,
226 | type="",
227 | )
228 | release.upload_asset(str(out_path))
229 | webbrowser.open(release.html_url)
230 | webbrowser.open(out_path.parent)
231 | webbrowser.open("https://extensions.blender.org/add-ons/nodepie/manage/versions/new/")
232 | print("FINISHED!")
233 |
234 | else:
235 | webbrowser.open(out_path.parent)
236 |
237 |
238 | if __name__ == "__main__":
239 | main()
240 |
--------------------------------------------------------------------------------
/images/Node Pie compositor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strike-digital/node_pie/48322b48c80347382c4b8292cb8eaf04610dcfdc/images/Node Pie compositor.jpg
--------------------------------------------------------------------------------
/images/Node Pie geometry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strike-digital/node_pie/48322b48c80347382c4b8292cb8eaf04610dcfdc/images/Node Pie geometry.jpg
--------------------------------------------------------------------------------
/images/Node Pie preferences.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strike-digital/node_pie/48322b48c80347382c4b8292cb8eaf04610dcfdc/images/Node Pie preferences.jpg
--------------------------------------------------------------------------------
/images/Node Pie serpens.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strike-digital/node_pie/48322b48c80347382c4b8292cb8eaf04610dcfdc/images/Node Pie serpens.jpg
--------------------------------------------------------------------------------
/images/Node Pie shader.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strike-digital/node_pie/48322b48c80347382c4b8292cb8eaf04610dcfdc/images/Node Pie shader.jpg
--------------------------------------------------------------------------------
/node_pie/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strike-digital/node_pie/48322b48c80347382c4b8292cb8eaf04610dcfdc/node_pie/__init__.py
--------------------------------------------------------------------------------
/node_pie/generate_node_layout.py:
--------------------------------------------------------------------------------
1 | import json
2 | from .npie_constants import NODE_DEF_DIR
3 |
4 | # File used to generate the initial node def file, doesn't actually do anything in the addon currently
5 |
6 | colours = {
7 | "Attribute": "attribute",
8 | "Color": "color",
9 | "Curve": "geometry",
10 | "Geometry": "geometry",
11 | "Input": "input",
12 | "Instances": "geometry",
13 | "Material": "geometry",
14 | "Mesh": "geometry",
15 | "Point": "geometry",
16 | "Curve Primitives": "geometry",
17 | "Mesh Primitives": "geometry",
18 | "Text": "geometry",
19 | "Texture": "texture",
20 | "Utilities": "converter",
21 | "Vector": "vector",
22 | "Volume": "geometry",
23 | "Layout": "layout",
24 | # 3.4+
25 | "Mesh Topology": "input",
26 | "Curve Topology": "input",
27 | "UV": "converter",
28 | }
29 | overrides = {}
30 | color_overrides = {
31 | "Input": "input",
32 | "FunctionNode": "converter",
33 | "GeometryNodeStringJoin": "converter",
34 | "ShaderNodeValToRGB": "converter",
35 | "ShaderNodeCombineXYZ": "converter",
36 | "ShaderNodeSeparateXYZ": "converter",
37 | "GeometryNodeSplineParameter": "input",
38 | "GeometryNodeSplineLength": "input",
39 | "GeometryNodeCurveHandleTypeSelection": "input",
40 | "GeometryNodeCurveEndpointSelection": "input",
41 | }
42 |
43 | data = {}
44 |
45 |
46 | def main():
47 |
48 | # for area in bpy.context.window.screen.areas:
49 | # if area.type == "NODE_EDITOR":
50 | # with bpy.context.temp_override(area=area):
51 | # cats = list(nodeitems_utils.node_categories_iter(bpy.context))
52 | # for cat in cats:
53 | # # if cat.name not in colours.keys():
54 | # # continue
55 | # items = []
56 | # for item in cat.items(bpy.context):
57 | # if not isinstance(item, nodeitems_utils.NodeItem):
58 | # continue
59 | # settings = item.settings
60 | # data_item = {"label": item.label, "identifier": item.nodetype}
61 | # if settings:
62 | # data_item["settings"] = settings
63 | # if item.nodetype in color_overrides:
64 | # data_item["color"] = color_overrides[item.nodetype]
65 | # items.append(data_item)
66 | # data[cat.identifier] = {"label": cat.name, "color": "input", "nodes": items}
67 |
68 | # selected_nodes = bpy.context.selected_nodes
69 | # items = []
70 | # for node in selected_nodes:
71 | # data_item = {"label": node.bl_label, "identifier": node.bl_idname}
72 | # items.append(data_item)
73 |
74 | # # Uncomment to write out the initial file from an old version that still has a working NodeItems api.
75 | # # Shouldn't need to be used now.
76 | # if data:
77 | # fpath = NODE_DEF_DIR / "CompositorNodeTree2.jsonc"
78 | # with open(fpath, "w") as f:
79 | # json.dump(data, f, indent=2)
80 |
81 | # print("Dumped!")
82 | # print(data)
83 |
84 | orig_file = NODE_DEF_DIR / "GeometryNodeTree.jsonc"
85 | new_file = NODE_DEF_DIR / "GeometryNodeTree_40 copy.jsonc"
86 |
87 | with open(orig_file, "r") as f:
88 | orig_data = json.load(f)
89 | with open(new_file, "r") as f:
90 | new_data = json.load(f)
91 |
92 | orig_categories = orig_data["categories"]
93 | new_categories = new_data["categories"]
94 | data = {}
95 |
96 | for orig_name, orig_cat in orig_categories.items():
97 | new_cat = new_categories[orig_name]
98 | nodes = []
99 | for node in new_cat["nodes"]:
100 | if node.get("identifier") not in {n.get("identifier") for n in orig_cat["nodes"]}:
101 | if node.get("separator"):
102 | nodes.append(node)
103 | if nodes:
104 | data[orig_name] = {"nodes": nodes}
105 |
106 | # print(dict(new_categories.items() - orig_categories.items()))
107 | data.update({k: v for k, v in new_categories.items() if k not in orig_categories})
108 |
109 | print(json.dumps(data, indent=2) + ",")
110 |
111 |
112 | # Uncomment to call main function
113 | # bpy.app.timers.register(main)
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/CompositorNodeTree_1_0.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "layout": {
3 | "left": [
4 | ["DISTORT", "LAYOUT"],
5 | ["CONVERTOR", "VECTOR"]
6 | ],
7 | "right": [["INPUT", "OUTPUT"], ["FILTER"]],
8 | "top": [["COLOR"]],
9 | "bottom": [["MATTE"]]
10 | },
11 | "categories": {
12 | "INPUT": {
13 | "label": "Input",
14 | "color": "input",
15 | "nodes": [
16 | { "identifier": "CompositorNodeBokehImage" },
17 | { "identifier": "CompositorNodeImage" },
18 | { "identifier": "CompositorNodeMask" },
19 | { "identifier": "CompositorNodeMovieClip" },
20 | { "identifier": "CompositorNodeRLayers" },
21 | { "identifier": "CompositorNodeRGB" },
22 | { "identifier": "CompositorNodeSceneTime" },
23 | { "identifier": "CompositorNodeTexture" },
24 | { "identifier": "CompositorNodeTime" },
25 | { "identifier": "CompositorNodeTrackPos" },
26 | { "identifier": "CompositorNodeValue" }
27 | ]
28 | },
29 | "OUTPUT": {
30 | "label": "Output",
31 | "color": "output",
32 | "nodes": [
33 | { "identifier": "CompositorNodeComposite" },
34 | { "identifier": "CompositorNodeOutputFile" },
35 | { "identifier": "CompositorNodeLevels" },
36 | { "identifier": "CompositorNodeSplitViewer" },
37 | { "identifier": "CompositorNodeViewer" }
38 | ]
39 | },
40 | "COLOR": {
41 | "label": "Color",
42 | "color": "color",
43 | "nodes": [
44 | { "identifier": "CompositorNodeAlphaOver" },
45 | { "identifier": "CompositorNodeBrightContrast" },
46 | { "identifier": "CompositorNodeColorBalance" },
47 | { "identifier": "CompositorNodeColorCorrection" },
48 | { "identifier": "CompositorNodeExposure" },
49 | { "identifier": "CompositorNodeGamma" },
50 | { "identifier": "CompositorNodeHueCorrect" },
51 | { "identifier": "CompositorNodeHueSat" },
52 | { "identifier": "CompositorNodeInvert" },
53 | {
54 | "identifier": "CompositorNodeMixRGB",
55 | "variants": {
56 | "Add": { "blend_type": "ADD" },
57 | "Subtract": { "blend_type": "SUBTRACT" },
58 | "Multiply": { "blend_type": "MULTIPLY" },
59 | "Divide": { "blend_type": "DIVIDE" },
60 | "Overlay": { "blend_type": "OVERLAY" },
61 | "Linear Light": { "blend_type": "LINEAR_LIGHT" }
62 | }
63 | },
64 | { "identifier": "CompositorNodePosterize" },
65 | { "identifier": "CompositorNodeCurveRGB" },
66 | { "identifier": "CompositorNodeTonemap" },
67 | { "identifier": "CompositorNodeZcombine" }
68 | ]
69 | },
70 | "CONVERTOR": {
71 | "label": "Converter",
72 | "color": "converter",
73 | "nodes": [
74 | { "identifier": "CompositorNodePremulKey" },
75 | { "identifier": "CompositorNodeConvertColorSpace" },
76 | { "identifier": "CompositorNodeValToRGB" },
77 | { "identifier": "CompositorNodeCombineColor" },
78 | { "identifier": "CompositorNodeCombineXYZ" },
79 | { "identifier": "CompositorNodeIDMask" },
80 | {
81 | "identifier": "CompositorNodeMath",
82 | "variants": {
83 | "Negate": {
84 | "operation": "MULTIPLY",
85 | "show_options": false,
86 | "label": "Negate",
87 | "inputs[1].hide": true,
88 | "inputs[1].default_value": -1
89 | },
90 | "One Minus": {
91 | "operation": "SUBTRACT",
92 | "show_options": false,
93 | "label": "One Minus",
94 | "inputs[0].hide": true,
95 | "inputs[0].default_value": 1
96 | },
97 | "One Over": {
98 | "operation": "DIVIDE",
99 | "show_options": false,
100 | "label": "One Over",
101 | "inputs[0].hide": true,
102 | "inputs[0].default_value": 1
103 | },
104 | "separator": true,
105 | "Multiply": { "operation": "MULTIPLY" },
106 | "Divide": { "operation": "DIVIDE" },
107 | "Subtract": { "operation": "SUBTRACT" },
108 | "Modulo": { "operation": "MODULO" },
109 | "Absolute": { "operation": "ABSOLUTE" },
110 | "Power": { "operation": "POWER" },
111 | "Sine": { "operation": "SINE" },
112 | "Cosine": { "operation": "COSINE" }
113 | }
114 | },
115 | { "identifier": "CompositorNodeRGBToBW" },
116 | { "identifier": "CompositorNodeSeparateColor" },
117 | { "identifier": "CompositorNodeSeparateXYZ" },
118 | { "identifier": "CompositorNodeSetAlpha" },
119 | { "identifier": "CompositorNodeSwitch" },
120 | { "identifier": "CompositorNodeSwitchView" }
121 | ]
122 | },
123 | "FILTER": {
124 | "label": "Filter",
125 | "color": "filter",
126 | "nodes": [
127 | { "identifier": "CompositorNodeAntiAliasing" },
128 | { "identifier": "CompositorNodeBilateralblur" },
129 | { "identifier": "CompositorNodeBlur" },
130 | { "identifier": "CompositorNodeBokehBlur" },
131 | { "identifier": "CompositorNodeDefocus" },
132 | { "identifier": "CompositorNodeDenoise" },
133 | { "identifier": "CompositorNodeDespeckle" },
134 | { "identifier": "CompositorNodeDilateErode" },
135 | { "identifier": "CompositorNodeDBlur" },
136 | { "identifier": "CompositorNodeFilter" },
137 | { "identifier": "CompositorNodeGlare" },
138 | { "identifier": "CompositorNodeInpaint" },
139 | { "identifier": "CompositorNodeKuwahara" },
140 | { "identifier": "CompositorNodePixelate" },
141 | { "identifier": "CompositorNodeSunBeams" },
142 | { "identifier": "CompositorNodeVecBlur" }
143 | ]
144 | },
145 | "VECTOR": {
146 | "label": "Vector",
147 | "color": "vector",
148 | "nodes": [
149 | { "identifier": "CompositorNodeMapRange" },
150 | { "identifier": "CompositorNodeMapValue" },
151 | { "identifier": "CompositorNodeNormal" },
152 | { "identifier": "CompositorNodeNormalize" },
153 | { "identifier": "CompositorNodeCurveVec" }
154 | ]
155 | },
156 | "MATTE": {
157 | "label": "Matte",
158 | "color": "matte",
159 | "nodes": [
160 | { "identifier": "CompositorNodeBoxMask" },
161 | { "identifier": "CompositorNodeChannelMatte" },
162 | { "identifier": "CompositorNodeChromaMatte" },
163 | { "identifier": "CompositorNodeColorMatte" },
164 | { "identifier": "CompositorNodeColorSpill" },
165 | { "identifier": "CompositorNodeCryptomatteV2" },
166 | { "identifier": "CompositorNodeCryptomatte" },
167 | { "identifier": "CompositorNodeDiffMatte" },
168 | { "identifier": "CompositorNodeDistanceMatte" },
169 | { "identifier": "CompositorNodeDoubleEdgeMask" },
170 | { "identifier": "CompositorNodeEllipseMask" },
171 | { "identifier": "CompositorNodeKeying" },
172 | { "identifier": "CompositorNodeKeyingScreen" },
173 | { "identifier": "CompositorNodeLumaMatte" }
174 | ]
175 | },
176 | "DISTORT": {
177 | "label": "Distort",
178 | "color": "distor",
179 | "nodes": [
180 | { "identifier": "CompositorNodeCornerPin" },
181 | { "identifier": "CompositorNodeCrop" },
182 | { "identifier": "CompositorNodeDisplace" },
183 | { "identifier": "CompositorNodeFlip" },
184 | { "identifier": "CompositorNodeLensdist" },
185 | { "identifier": "CompositorNodeMapUV" },
186 | { "identifier": "CompositorNodeMovieDistortion" },
187 | { "identifier": "CompositorNodePlaneTrackDeform" },
188 | { "identifier": "CompositorNodeRotate" },
189 | { "identifier": "CompositorNodeScale" },
190 | { "identifier": "CompositorNodeStabilize" },
191 | { "identifier": "CompositorNodeTransform" },
192 | { "identifier": "CompositorNodeTranslate" }
193 | ]
194 | },
195 | "LAYOUT": {
196 | "label": "Layout",
197 | "color": "layout",
198 | "nodes": [{ "identifier": "NodeFrame" }, { "identifier": "NodeReroute" }]
199 | }
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/CompositorNodeTree_4_1.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "blender_version": [4, 1, 0],
3 | "removals": {
4 | "nodes": ["CompositorNodeSplitViewer"]
5 | },
6 | "additions": {
7 | "categories": {
8 | "CONVERTOR": {
9 | "nodes": [
10 | {
11 | "identifier": "CompositorNodeSplit",
12 | "before_node": "CompositorNodeSwitch"
13 | }
14 | ]
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/GeometryNodeTree_3_5.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 3.5, compared to the base file.
2 | {
3 | "blender_version": [3, 5, 0],
4 | "additions": {
5 | "categories": {
6 | "ATTRIBUTE": {
7 | "nodes": [
8 | { "identifier": "GeometryNodeBlurAttribute", "after_node": "top" }
9 | ]
10 | },
11 | "INPUT": {
12 | "nodes": [
13 | {
14 | "identifier": "GeometryNodeInputImage",
15 | "after_node": "FunctionNodeInputColor"
16 | },
17 | {
18 | "identifier": "GeometryNodeImageInfo",
19 | "before_node": "GeometryNodeIsViewport"
20 | }
21 | ]
22 | },
23 | "CURVE": {
24 | "nodes": [
25 | {
26 | "identifier": "GeometryNodeInterpolateCurves",
27 | "after_node": "GeometryNodeTrimCurve"
28 | }
29 | ]
30 | },
31 | "MESH": {
32 | "nodes": [
33 | {
34 | "identifier": "GeometryNodeEdgesToFaceGroups",
35 | "after_node": "GeometryNodeMeshFaceSetBoundaries",
36 | "color": "input"
37 | }
38 | ]
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/GeometryNodeTree_3_6.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 3.6, compared to the base file.
2 | {
3 | "blender_version": [3, 6, 0],
4 | "additions": {
5 | "layout": {
6 | "right": [[], [], ["SIMULATION"]]
7 | },
8 | "categories": {
9 | "GEOMETRY": {
10 | "nodes": [
11 | {
12 | "identifier": "GeometryNodeIndexOfNearest",
13 | "after_node": "GeometryNodeSampleIndex"
14 | }
15 | ]
16 | },
17 | "SIMULATION": {
18 | "label": "Simulation",
19 | "color": "layout",
20 | "icon": "PHYSICS",
21 | "nodes": [
22 | {
23 | "label": "Simulation Zone",
24 | "operator": "node.add_simulation_zone",
25 | "settings": { "use_transform": true }
26 | }
27 | ]
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/GeometryNodeTree_4_0.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 4.0, compared to the base file.
2 | // Adds the new nodes, and also replaces the simulation category with one for all zones.
3 | // New tool specific nodes are also added, with poll conditions so that they only show up in the correct context.
4 | {
5 | "blender_version": [4, 0, 0],
6 | "removals": {
7 | "layout": {
8 | "right": [[], ["VOLUME"], ["SIMULATION"]]
9 | },
10 | "categories": {
11 | "SIMULATION": {}
12 | }
13 | },
14 | "additions": {
15 | "layout": {
16 | "right": [["ROTATION"], [], ["VOLUME", "ZONE"]]
17 | },
18 | "poll_types": {
19 | "is_tool": [
20 | {
21 | "context_path": "space_data.geometry_nodes_type",
22 | "operand": "equals",
23 | "value": "TOOL"
24 | }
25 | ]
26 | },
27 | "categories": {
28 | "INPUT": {
29 | "nodes": [
30 | {
31 | "identifier": "GeometryNodeInputEdgeSmooth",
32 | "after_node": "GeometryNodeInputIndex"
33 | },
34 | {
35 | "separator": true,
36 | "label": "Tool",
37 | "after_node": "bottom",
38 | "poll_type": "is_tool"
39 | },
40 | {
41 | "identifier": "GeometryNodeTool3DCursor",
42 | "poll_type": "is_tool"
43 | },
44 | {
45 | "identifier": "GeometryNodeToolFaceSet",
46 | "poll_type": "is_tool"
47 | },
48 | {
49 | "identifier": "GeometryNodeToolSelection",
50 | "poll_type": "is_tool"
51 | }
52 | ]
53 | },
54 | "ROTATION": {
55 | "label": "Rotation",
56 | "color": "converter",
57 | "icon": "CON_ROTLIKE",
58 | "nodes": [
59 | { "identifier": "FunctionNodeAxisAngleToRotation" },
60 | { "identifier": "FunctionNodeEulerToRotation" },
61 | { "identifier": "FunctionNodeInvertRotation" },
62 | {
63 | "identifier": "ShaderNodeMix",
64 | "label": "Mix Rotation",
65 | "settings": { "data_type": "ROTATION" }
66 | },
67 | { "identifier": "FunctionNodeQuaternionToRotation" },
68 | { "identifier": "FunctionNodeRotateVector" },
69 | { "identifier": "FunctionNodeRotationToAxisAngle" },
70 | { "identifier": "FunctionNodeRotationToEuler" },
71 | { "identifier": "FunctionNodeRotationToQuaternion" }
72 | ]
73 | },
74 | "GEOMETRY": {
75 | "nodes": [
76 | {
77 | "identifier": "GeometryNodeToolSetSelection",
78 | "poll_type": "is_tool"
79 | },
80 | {
81 | "identifier": "GeometryNodeToolSetFaceSet",
82 | "before_node": "GeometryNodeSetID",
83 | "poll_type": "is_tool"
84 | }
85 | ]
86 | },
87 | "MESH": {
88 | "nodes": [
89 | {
90 | "identifier": "GeometryNodeMeshToSDFVolume",
91 | "after_node": "GeometryNodeMeshToVolume"
92 | },
93 | {
94 | "identifier": "GeometryNodeInputEdgeSmooth",
95 | "before_node": "GeometryNodeInputMeshFaceIsPlanar",
96 | "color": "input"
97 | },
98 | {
99 | "identifier": "GeometryNodeCornersOfEdge",
100 | "color": "input",
101 | "before_node": "GeometryNodeCornersOfFace"
102 | }
103 | ]
104 | },
105 | "POINT": {
106 | "nodes": [
107 | {
108 | "identifier": "GeometryNodePointsToSDFVolume",
109 | "after_node": "GeometryNodePointsToVolume"
110 | }
111 | ]
112 | },
113 | "VOLUME": {
114 | "nodes": [
115 | { "separator": true },
116 | { "identifier": "GeometryNodeInputSignedDistance" },
117 | { "identifier": "GeometryNodeSampleVolume" },
118 | { "identifier": "GeometryNodeSDFVolumeSphere" },
119 | { "identifier": "GeometryNodeOffsetSDFVolume" },
120 | { "identifier": "GeometryNodeMeanFilterSDFVolume" }
121 | ]
122 | },
123 | "ZONE": {
124 | "label": "Zones",
125 | "color": "layout",
126 | "icon": "PHYSICS",
127 | "nodes": [
128 | {
129 | "label": "Simulation Zone",
130 | "identifier": "SimulationZone",
131 | "operator": "node.add_simulation_zone",
132 | "settings": { "use_transform": true }
133 | },
134 | {
135 | "label": "Repeat Zone",
136 | "identifier": "RepeatZone",
137 | "operator": "node.add_repeat_zone",
138 | "settings": { "use_transform": true }
139 | }
140 | ]
141 | }
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/GeometryNodeTree_4_1.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 3.5, compared to the base file.
2 | {
3 | "blender_version": [4, 1, 0],
4 | "removals": {
5 | "layout": {
6 | "left": [["UV"]]
7 | },
8 | "nodes": [
9 | "ShaderNodeTexMusgrave",
10 | "GeometryNodeSDFVolumeSphere",
11 | "GeometryNodeInputSignedDistance",
12 | "GeometryNodeOffsetSDFVolume",
13 | "GeometryNodeSampleVolume",
14 | "GeometryNodeMeanFilterSDFVolume",
15 | "GeometryNodeMeshToSDFVolume",
16 | "GeometryNodePointsToSDFVolume",
17 | "FunctionNodeRotateEuler"
18 | ]
19 | },
20 | "additions": {
21 | "layout": {
22 | "right": [[], ["UV"]]
23 | },
24 | "categories": {
25 | "GEOMETRY": {
26 | "nodes": [
27 | {
28 | "identifier": "GeometryNodeSplitToInstances",
29 | "after_node": "GeometryNodeSeparateGeometry"
30 | },
31 | {
32 | "identifier": "GeometryNodeSortElements",
33 | "before_node": "GeometryNodeSplitToInstances"
34 | }
35 | ]
36 | },
37 | "UTILITIES": {
38 | "nodes": [
39 | {
40 | "identifier": "GeometryNodeIndexSwitch",
41 | "after_node": "FunctionNodeFloatToInt"
42 | }
43 | ]
44 | },
45 | "INPUT": {
46 | "nodes": [
47 | {
48 | "identifier": "GeometryNodeInputActiveCamera",
49 | "before_node": "GeometryNodeCollectionInfo"
50 | }
51 | ]
52 | },
53 | "ROTATION": {
54 | "nodes": [
55 | { "identifier": "FunctionNodeRotateRotation" , "before_node": "FunctionNodeRotateVector"}
56 | ]
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/GeometryNodeTree_4_2.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 4.2, compared to the previous version.
2 | // This version added a lot of matrix nodes which requires the layout to be rearranged a lot
3 | // It also added some more tool nodes that need polls
4 | {
5 | "blender_version": [4, 2, 0],
6 | "removals": {
7 | "layout": {
8 | "left": [[], [], ["UTILITIES", "INSTANCE", "POINT"]],
9 | "right": [["INPUT", "COLOR", "ROTATION"], ["UV"]],
10 | "top": [["ATTRIBUTE"]]
11 | },
12 | "nodes": [
13 | "FunctionNodeAlignEulerToVector",
14 | "SepRotation",
15 | "GeometryNodeIndexSwitch",
16 | "GeometryNodeSwitch"
17 | ]
18 | },
19 | "additions": {
20 | "layout": {
21 | "left": [[], [], ["MATRIX", "UTILITIES", "ROTATION"]],
22 | "right": [["INSTANCE", "INPUT"], ["POINT"]],
23 | "top": [["COLOR", "ATTRIBUTE"]],
24 | "bottom": [["UV"]]
25 | },
26 | "categories": {
27 | "COLOR": {
28 | "nodes": [
29 | {
30 | "identifier": "ShaderNodeBlackbody",
31 | "before_node": "FunctionNodeCompare"
32 | }
33 | ]
34 | },
35 | "GEOMETRY": {
36 | "nodes": [
37 | {
38 | "identifier": "GeometryNodeBake",
39 | "before_node": "GeometryNodeBoundBox"
40 | }
41 | ]
42 | },
43 | "ROTATION": {
44 | "nodes": [
45 | {
46 | "identifier": "FunctionNodeAlignRotationToVector",
47 | "before_node": "FunctionNodeAxisAngleToRotation"
48 | }
49 | ]
50 | },
51 | "INPUT": {
52 | "nodes": [
53 | {
54 | "identifier": "GeometryNodeToolMousePosition",
55 | "after_node": "GeometryNodeToolFaceSet",
56 | "poll_type": "is_tool"
57 | },
58 | {
59 | "identifier": "GeometryNodeViewportTransform",
60 | "after_node": "bottom",
61 | "poll_type": "is_tool"
62 | },
63 | {
64 | "identifier": "FunctionNodeInputRotation",
65 | "before_node": "FunctionNodeInputString"
66 | }
67 | ]
68 | },
69 | "MATRIX": {
70 | "icon": "MESH_GRID",
71 | "label": "Matrix",
72 | "color": "converter",
73 | "nodes": [
74 | { "identifier": "FunctionNodeCombineMatrix" },
75 | { "identifier": "FunctionNodeCombineTransform" },
76 | { "identifier": "FunctionNodeInvertMatrix" },
77 | { "identifier": "FunctionNodeMatrixMultiply" },
78 | { "identifier": "FunctionNodeProjectPoint" },
79 | { "identifier": "FunctionNodeSeparateMatrix" },
80 | { "identifier": "FunctionNodeSeparateTransform" },
81 | { "identifier": "FunctionNodeTransformDirection" },
82 | { "identifier": "FunctionNodeTransformPoint" },
83 | { "identifier": "FunctionNodeTransposeMatrix" }
84 | ]
85 | },
86 | "POINT": {
87 | "nodes": [
88 | {
89 | "identifier": "GeometryNodePointsToCurves",
90 | "before_node": "GeometryNodePointsToVertices"
91 | }
92 | ]
93 | },
94 | "UTILITIES": {
95 | "nodes": [
96 | // Create a new "Switch" section
97 | { "separator": true, "label": "Switch", "after_node": "bottom" },
98 | { "identifier": "GeometryNodeIndexSwitch" },
99 | { "identifier": "GeometryNodeMenuSwitch" },
100 | { "identifier": "GeometryNodeSwitch" }
101 | ]
102 | },
103 | "INSTANCE": {
104 | "nodes": [
105 | {
106 | "identifier": "GeometryNodeSetInstanceTransform",
107 | "after_node": "GeometryNodeScaleInstances"
108 | },
109 | {
110 | "identifier": "GeometryNodeInstanceTransform",
111 | "after_node": "GeometryNodeInputInstanceScale",
112 | "color": "input"
113 | }
114 | ]
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/GeometryNodeTree_4_3.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 4.3, compared to the previous version.
2 | // Lots of misc nodes added, plus a new zone.
3 | {
4 | "blender_version": [4, 3, 0],
5 | "removals": {},
6 | "additions": {
7 | "layout": {
8 | "right": [["GIZMO"]]
9 | },
10 | "categories": {
11 | "GIZMO": {
12 | "label": "Gizmo",
13 | "color": "layout",
14 | "nodes": [
15 | { "identifier": "GeometryNodeGizmoDial" },
16 | { "identifier": "GeometryNodeGizmoLinear" },
17 | { "identifier": "GeometryNodeGizmoTransform" }
18 | ]
19 | },
20 | "INPUT": {
21 | "nodes": [
22 | {
23 | "identifier": "GeometryNodeInputNamedLayerSelection",
24 | "before_node": "GeometryNodeObjectInfo"
25 | },
26 | {
27 | "identifier": "GeometryNodeWarning",
28 | "after_node": "FunctionNodeInputVector"
29 | },
30 | {
31 | "identifier": "GeometryNodeToolActiveElement",
32 | "after_node": "GeometryNodeTool3DCursor"
33 | }
34 | ]
35 | },
36 | "CURVE": {
37 | "nodes": [
38 | {
39 | "identifier": "GeometryNodeCurvesToGreasePencil",
40 | "before_node": "GeometryNodeCurveToMesh"
41 | },
42 | {
43 | "identifier": "GeometryNodeMergeLayers",
44 | "after_node": "GeometryNodeFilletCurve"
45 | },
46 | {
47 | "identifier": "GeometryNodeGreasePencilToCurves",
48 | "after_node": "GeometryNodeFilletCurve"
49 | }
50 | ]
51 | },
52 | "GEOMETRY": {
53 | "nodes": [
54 | {
55 | "identifier": "GeometryNodeSetGeometryName",
56 | "before_node": "GeometryNodeSetID"
57 | }
58 | ]
59 | },
60 | "MATRIX": {
61 | "nodes": [
62 | {
63 | "identifier": "FunctionNodeMatrixDeterminant",
64 | "after_node": "FunctionNodeCombineTransform"
65 | }
66 | ]
67 | },
68 | "ROTATION": {
69 | "nodes": [
70 | {
71 | "identifier": "FunctionNodeAxesToRotation",
72 | "before_node": "FunctionNodeAxisAngleToRotation"
73 | }
74 | ]
75 | },
76 | "TEXTURE": {
77 | "nodes": [
78 | {
79 | "identifier": "ShaderNodeTexGabor",
80 | "before_node": "ShaderNodeTexGradient"
81 | }
82 | ]
83 | },
84 | "UTILITIES": {
85 | "nodes": [
86 | {
87 | "identifier": "FunctionNodeIntegerMath",
88 | "after_node": "FunctionNodeFloatToInt"
89 | },
90 | {
91 | "identifier": "FunctionNodeHashValue",
92 | "after_node": "FunctionNodeFloatToInt"
93 | }
94 | ]
95 | },
96 | "ZONE": {
97 | "nodes": [
98 | {
99 | "label": "For Each Element Zone",
100 | "identifier": "ForEachElementZone",
101 | "before_node": "SimulationZone",
102 | "operator": "node.add_foreach_geometry_element_zone",
103 | "settings": { "use_transform": true }
104 | }
105 | ]
106 | }
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/GeometryNodeTree_4_4.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 4.4, compared to the previous version.
2 | // Lots of misc nodes added, plus a new zone.
3 | {
4 | "blender_version": [4, 4, 0],
5 | "removals": {"layout": {
6 | "right": [["GIZMO"]]
7 | }},
8 | "additions": {
9 | "layout": {
10 | "left": [[], ["GIZMO"]]
11 | },
12 | "categories": {
13 | "INPUT": {
14 | "nodes": [
15 | { "identifier": "GeometryNodeInputObject", "after_node": "GeometryNodeInputMaterial" },
16 | { "identifier": "GeometryNodeInputCollection", "before_node": "FunctionNodeInputColor" }
17 | ]
18 | },
19 | "TEXT": {
20 | "nodes": [
21 | { "identifier": "FunctionNodeFindInString", "before_node": "GeometryNodeStringJoin" }
22 | ]
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/ShaderNodeTree_1_0.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "layout": {
3 | "left": [
4 | ["TEXTURE", "LAYOUT"],
5 | ["CONVERTOR", "SCRIPT"]
6 | ],
7 | "right": [["INPUT"], ["SHADER"]],
8 | "top": [["VECTOR"]],
9 | "bottom": [["COLOR"]]
10 | },
11 | "poll_types": {
12 | "is_world": [
13 | {
14 | "context_path": "space_data.shader_type",
15 | "operand": "equals",
16 | "value": "WORLD"
17 | }
18 | ],
19 | "is_not_world": [
20 | {
21 | "context_path": "space_data.shader_type",
22 | "operand": "not_equals",
23 | "value": "WORLD"
24 | }
25 | ]
26 | },
27 | "categories": {
28 | "INPUT": {
29 | "label": "Input",
30 | "color": "input",
31 | "nodes": [
32 | { "identifier": "ShaderNodeAmbientOcclusion" },
33 | { "identifier": "ShaderNodeAttribute" },
34 | { "identifier": "ShaderNodeBackground", "poll_type": "is_world" },
35 | { "identifier": "ShaderNodeCameraData" },
36 | { "identifier": "ShaderNodeVertexColor" },
37 | { "identifier": "ShaderNodeHairInfo" },
38 | { "identifier": "ShaderNodeFresnel" },
39 | { "identifier": "ShaderNodeNewGeometry" },
40 | { "identifier": "ShaderNodeLayerWeight" },
41 | { "identifier": "ShaderNodeLightPath" },
42 | { "identifier": "ShaderNodeObjectInfo" },
43 | { "identifier": "ShaderNodePointInfo" },
44 | { "identifier": "ShaderNodeRGB" },
45 | { "identifier": "ShaderNodeTangent" },
46 | { "identifier": "ShaderNodeTexCoord" },
47 | {
48 | "identifier": "ShaderNodeUVAlongStroke",
49 | "poll_conditions": [
50 | {
51 | "context_path": "space_data.shader_type",
52 | "operand": "equals",
53 | "value": "LINESTYLE"
54 | }
55 | ]
56 | },
57 | { "identifier": "ShaderNodeUVMap" },
58 | { "identifier": "ShaderNodeValue" },
59 | { "identifier": "ShaderNodeVolumeInfo" },
60 | { "identifier": "ShaderNodeWireframe" },
61 | { "separator": true, "label": "Cycles" },
62 | { "identifier": "ShaderNodeBevel" },
63 | { "identifier": "ShaderNodeParticleInfo" }
64 | ]
65 | },
66 | "OUTPUT": {
67 | "label": "Output",
68 | "color": "output",
69 | "nodes": [
70 | { "identifier": "ShaderNodeOutputAOV" },
71 | { "identifier": "ShaderNodeOutputLight" },
72 | { "identifier": "ShaderNodeOutputMaterial" }
73 | ]
74 | },
75 | "SHADER": {
76 | "label": "Shader",
77 | "color": "shader",
78 | "nodes": [
79 | // A lot of shader nodes are not available in the world context
80 | { "identifier": "ShaderNodeAddShader" },
81 | { "identifier": "ShaderNodeBsdfDiffuse", "poll_type": "is_not_world" },
82 | { "identifier": "ShaderNodeEmission" },
83 | { "identifier": "ShaderNodeBsdfGlass", "poll_type": "is_not_world" },
84 | { "identifier": "ShaderNodeBsdfGlossy", "poll_type": "is_not_world" },
85 | { "identifier": "ShaderNodeHoldout", "poll_type": "is_not_world" },
86 | { "identifier": "ShaderNodeMixShader" },
87 | { "identifier": "ShaderNodeBsdfPrincipled", "poll_type": "is_not_world" },
88 | { "identifier": "ShaderNodeVolumePrincipled" },
89 | { "identifier": "ShaderNodeBsdfRefraction", "poll_type": "is_not_world" },
90 | { "identifier": "ShaderNodeSubsurfaceScattering", "poll_type": "is_not_world" },
91 | { "identifier": "ShaderNodeBsdfTranslucent", "poll_type": "is_not_world" },
92 | { "identifier": "ShaderNodeBsdfTransparent", "poll_type": "is_not_world" },
93 | { "identifier": "ShaderNodeVolumeAbsorption" },
94 | { "identifier": "ShaderNodeVolumeScatter" },
95 | {
96 | "separator": true,
97 | "label": "Cycles",
98 | "poll_type": "is_not_world"
99 | },
100 | { "identifier": "ShaderNodeBsdfAnisotropic", "poll_type": "is_not_world" },
101 | { "identifier": "ShaderNodeBsdfToon", "poll_type": "is_not_world" },
102 | { "identifier": "ShaderNodeBsdfHair", "poll_type": "is_not_world" },
103 | { "identifier": "ShaderNodeBsdfHairPrincipled", "poll_type": "is_not_world" },
104 | { "identifier": "ShaderNodeBsdfVelvet", "poll_type": "is_not_world" },
105 | { "separator": true, "label": "Eevee", "poll_type": "is_not_world" },
106 | { "identifier": "ShaderNodeEeveeSpecular", "poll_type": "is_not_world" }
107 | ]
108 | },
109 | "TEXTURE": {
110 | "label": "Texture",
111 | "color": "texture",
112 | "nodes": [
113 | { "identifier": "ShaderNodeTexBrick" },
114 | { "identifier": "ShaderNodeTexChecker" },
115 | { "identifier": "ShaderNodeTexEnvironment" },
116 | { "identifier": "ShaderNodeTexGradient" },
117 | { "identifier": "ShaderNodeTexImage" },
118 | { "identifier": "ShaderNodeTexMagic" },
119 | { "identifier": "ShaderNodeTexMusgrave" },
120 | { "identifier": "ShaderNodeTexNoise" },
121 | { "identifier": "ShaderNodeTexSky" },
122 | { "identifier": "ShaderNodeTexVoronoi" },
123 | { "identifier": "ShaderNodeTexWave" },
124 | { "identifier": "ShaderNodeTexWhiteNoise" },
125 | { "separator": true, "label": "Cycles" },
126 | { "identifier": "ShaderNodeTexIES" },
127 | { "identifier": "ShaderNodeTexPointDensity" }
128 | ]
129 | },
130 | "COLOR": {
131 | "label": "Color",
132 | "color": "color",
133 | "nodes": [
134 | { "identifier": "ShaderNodeBrightContrast" },
135 | { "identifier": "ShaderNodeGamma" },
136 | { "identifier": "ShaderNodeHueSaturation" },
137 | { "identifier": "ShaderNodeInvert" },
138 | {
139 | "identifier": "ShaderNodeMix",
140 | "settings": { "data_type": "RGBA" },
141 | "color": "color",
142 | "variants": {
143 | "Add": { "blend_type": "ADD" },
144 | "Subtract": { "blend_type": "SUBTRACT" },
145 | "Multiply": { "blend_type": "MULTIPLY" },
146 | "Divide": { "blend_type": "DIVIDE" },
147 | "Overlay": { "blend_type": "OVERLAY" },
148 | "Linear Light": { "blend_type": "LINEAR_LIGHT" }
149 | }
150 | },
151 | { "identifier": "ShaderNodeRGBCurve" },
152 | { "separator": true, "label": "Cycles" },
153 | { "identifier": "ShaderNodeLightFalloff" }
154 | ]
155 | },
156 | "VECTOR": {
157 | "label": "Vector",
158 | "color": "vector",
159 | "nodes": [
160 | { "identifier": "ShaderNodeBump" },
161 | { "identifier": "ShaderNodeDisplacement" },
162 | { "identifier": "ShaderNodeMapping" },
163 | {
164 | "identifier": "ShaderNodeMix",
165 | "settings": { "data_type": "VECTOR" }
166 | },
167 | { "identifier": "ShaderNodeNormal" },
168 | { "identifier": "ShaderNodeNormalMap" },
169 | { "identifier": "ShaderNodeVectorCurve" },
170 | { "identifier": "ShaderNodeVectorDisplacement" },
171 | { "identifier": "ShaderNodeVectorRotate" },
172 | { "identifier": "ShaderNodeVectorTransform" },
173 | {
174 | "identifier": "ShaderNodeVectorMath",
175 | "variants": {
176 | "Negate": {
177 | "operation": "SCALE",
178 | "show_options": false,
179 | "label": "Negate",
180 | "inputs[3].hide": true,
181 | "inputs[3].default_value": -1
182 | },
183 | "One Minus": {
184 | "operation": "SUBTRACT",
185 | "show_options": false,
186 | "label": "One Minus",
187 | "inputs[0].hide": true,
188 | "inputs[0].default_value": [1, 1, 1]
189 | },
190 | "One Over": {
191 | "operation": "DIVIDE",
192 | "show_options": false,
193 | "label": "One Over",
194 | "inputs[0].hide": true,
195 | "inputs[0].default_value": [1, 1, 1]
196 | },
197 | "separator": true,
198 | "Multiply": { "operation": "MULTIPLY" },
199 | "Divide": { "operation": "DIVIDE" },
200 | "Subtract": { "operation": "SUBTRACT" },
201 | "Scale": { "operation": "SCALE" },
202 | "Dot Product": { "operation": "DOT_PRODUCT" },
203 | "Cross Product": { "operation": "CROSS_PRODUCT" },
204 | "Normalize": { "operation": "NORMALIZE" },
205 | "Absolute": { "operation": "ABSOLUTE" },
206 | "Distance": { "operation": "DISTANCE" },
207 | "Length": { "operation": "LENGTH" }
208 | }
209 | }
210 | ]
211 | },
212 | "CONVERTOR": {
213 | "label": "Converter",
214 | "color": "converter",
215 | "nodes": [
216 | { "identifier": "ShaderNodeBlackbody" },
217 | { "identifier": "ShaderNodeClamp" },
218 | { "identifier": "ShaderNodeValToRGB", "color": "converter" },
219 | { "identifier": "ShaderNodeCombineColor" },
220 | { "identifier": "ShaderNodeCombineXYZ", "color": "converter" },
221 | { "identifier": "ShaderNodeFloatCurve" },
222 | { "identifier": "ShaderNodeMapRange" },
223 | {
224 | "identifier": "ShaderNodeMath",
225 | "variants": {
226 | "Negate": {
227 | "operation": "MULTIPLY",
228 | "show_options": false,
229 | "label": "Negate",
230 | "inputs[1].hide": true,
231 | "inputs[1].default_value": -1
232 | },
233 | "One Minus": {
234 | "operation": "SUBTRACT",
235 | "show_options": false,
236 | "label": "One Minus",
237 | "inputs[0].hide": true,
238 | "inputs[0].default_value": 1
239 | },
240 | "One Over": {
241 | "operation": "DIVIDE",
242 | "show_options": false,
243 | "label": "One Over",
244 | "inputs[0].hide": true,
245 | "inputs[0].default_value": 1
246 | },
247 | "separator": true,
248 | "Multiply": { "operation": "MULTIPLY" },
249 | "Divide": { "operation": "DIVIDE" },
250 | "Subtract": { "operation": "SUBTRACT" },
251 | "Modulo": { "operation": "MODULO" },
252 | "Absolute": { "operation": "ABSOLUTE" },
253 | "Power": { "operation": "POWER" },
254 | "Sine": { "operation": "SINE" },
255 | "Cosine": { "operation": "COSINE" }
256 | }
257 | },
258 | { "identifier": "ShaderNodeMix" },
259 | { "identifier": "ShaderNodeRGBToBW" },
260 | { "identifier": "ShaderNodeSeparateColor" },
261 | {
262 | "identifier": "ShaderNodeSeparateXYZ",
263 | "color": "converter",
264 | "variants": {
265 | "X": {
266 | "outputs[1].hide": true,
267 | "outputs[2].hide": true,
268 | "label": "Get X"
269 | },
270 | "Y": {
271 | "outputs[0].hide": true,
272 | "outputs[2].hide": true,
273 | "label": "Get Y"
274 | },
275 | "Z": {
276 | "outputs[0].hide": true,
277 | "outputs[1].hide": true,
278 | "label": "Get Z"
279 | }
280 | }
281 | },
282 |
283 | { "identifier": "ShaderNodeWavelength" },
284 | { "separator": true, "label": "Eevee" },
285 | { "identifier": "ShaderNodeShaderToRGB" }
286 | ]
287 | },
288 | "SCRIPT": {
289 | "label": "Script",
290 | "color": "script",
291 | "nodes": [{ "identifier": "ShaderNodeScript" }]
292 | },
293 | "LAYOUT": {
294 | "label": "Layout",
295 | "color": "layout",
296 | "nodes": [{ "identifier": "NodeFrame" }, { "identifier": "NodeReroute" }]
297 | }
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/ShaderNodeTree_4_0.jsonc:
--------------------------------------------------------------------------------
1 | // This is a versioning file that only specifies the new nodes in 4.0, compared to the base file.
2 |
3 | // In this version, the glossy bsdf node was replaced by an improved version of the anisotropic bsdf.
4 | // However, while the display label is now "Glossy Bsdf", the identifier is still "ShaderNodeBsdfAnisotropic".
5 | // This just removes the old nodes and adds the anisotropic back in in the correct location
6 | {
7 | "blender_version": [4, 0, 0],
8 | "removals": {
9 | "categories": {
10 | "SHADER": {
11 | "nodes": [
12 | { "identifier": "ShaderNodeBsdfVelvet" },
13 | { "identifier": "ShaderNodeBsdfAnisotropic" },
14 | { "identifier": "ShaderNodeBsdfGlossy" }
15 | ]
16 | }
17 | }
18 | },
19 | "additions": {
20 | "categories": {
21 | "SHADER": {
22 | "nodes": [
23 | {
24 | "identifier": "ShaderNodeBsdfAnisotropic",
25 | "after_node": "ShaderNodeBsdfGlass",
26 | "poll_type": "is_not_world"
27 | }
28 | ]
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/ShaderNodeTree_4_1.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "blender_version": [4, 1, 0],
3 | "removals": {
4 | "nodes": ["ShaderNodeTexMusgrave"]
5 | }
6 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/ShaderNodeTree_4_2.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "blender_version": [4, 2, 0],
3 | "additions": {
4 | "categories": {
5 | "SHADER": {
6 | "nodes": [
7 | { "identifier": "ShaderNodeBsdfSheen", "before_node": "ShaderNodeSubsurfaceScattering", "poll_type": "is_not_world" }
8 | ]
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/builtin/ShaderNodeTree_4_3.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "blender_version": [4, 3, 0],
3 | "additions": {
4 | "categories": {
5 | "TEXTURE": {
6 | "nodes": [{ "identifier": "ShaderNodeTexGabor", "before_node": "ShaderNodeTexGradient" }]
7 | },
8 | "SHADER": {
9 | "nodes": [
10 | { "identifier": "ShaderNodeBsdfMetallic", "before_node": "ShaderNodeMixShader", "poll_type": "is_not_world" },
11 | { "identifier": "ShaderNodeBsdfRayPortal", "after_node": "ShaderNodeBsdfHairPrincipled", "poll_type": "is_not_world" }
12 | ]
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/node_def_base.jsonc:
--------------------------------------------------------------------------------
1 | // For more info about how to use this file, use the "Open Example File" operator in the right click menu of the node editor
2 | // You'll need Node Pie developer extras to be enabled in the preferences to see it.
3 |
4 | {
5 | "layout": {
6 | "left": [["CATEGORY_NAME"]],
7 | "right": [],
8 | "top": [],
9 | "bottom": []
10 | },
11 | "categories": {
12 | "CATEGORY_NAME": {
13 | "label": "Category name",
14 | "color": "converter",
15 | "nodes": [
16 | // Use the "Copy nodes as json" operator for this
17 | { "identifier": "NodeIdentifier" },
18 | { "separator": true }
19 | ]
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/node_def_example.jsonc:
--------------------------------------------------------------------------------
1 | // This example shows how to structure a node pie definition file (This one is for the Serpens addon)
2 | // For the best experience it is recommended to edit this using a code editor
3 | // with syntax highlighting such as vscode or notepad++.
4 |
5 | // The definition file will be reloaded automatically each time the pie men is called,
6 | // just make sure to save the file after making a change for it to be shown.
7 | {
8 | // This defines how each category is placed in the pie menu
9 | // The menu is divided into four sections (left, right, etc.)
10 | // Each section then contains a list of columns (the first set of brackets)
11 | // Then each column contains a list of rows (the second set of brackets)
12 | // Each row is just defined by the category identifier (defined below)
13 | // The layout below will create a pie menu with one column, containing two rows, on the left hand side
14 | "layout": {
15 | "left": [["DEBUG", "DEBUG"]],
16 | "right": [],
17 | "top": [],
18 | "bottom": []
19 | },
20 | // These are preset poll conditions that get prepended to the conditions of any items that they are applied to
21 | "poll_types": {
22 | "poll_type_name": [
23 | {
24 | // condition 1 etc.
25 | }
26 | ]
27 | },
28 | // Categories are the different visual groups of nodes in the node tree.
29 | // It doesn't matter what order they are defined in here, since they are laid out above
30 | "categories": {
31 | // The identifier of the category doesn't matter, just as long as it is unique
32 | "DEBUG": {
33 | "label": "Debug", // The name that is displayed at the top of the category
34 | // The color to draw at the side of the node.
35 | // This is defined by the name of that node category color in the Blender theme preferences.
36 | // The possible values, and their default colors are:
37 | // "converter": light blue
38 | // "color": yellow
39 | // "group": dark green
40 | // "layout": grey
41 | // "matte": rust
42 | // "distor": grey-blue This should be distort, but there is a typo in the blender code
43 | // "input": red
44 | // "output": dark red
45 | // "filter": deep purple
46 | // "vector": light purple
47 | // "texture": orange
48 | // "shader": green
49 | // "script": dark greeny blue
50 | // "geometry": light green
51 | // "attribute": dark blue
52 | "color": "converter",
53 | // A list of the nodes in this category
54 | "nodes": [
55 | // IMPORTANT: You can auto generate this from the selected nodes with the 'Copy nodes as json' operator
56 | {
57 | // The internal blender name of the node
58 | "identifier": "SN_TriggerNode",
59 | // The display name of the node. This is optional, only needs to be used if the auto generated one is wrong
60 | "label": "Trigger",
61 | // Optional, only needed if the color is different from the category color
62 | "color": "input",
63 | // Variants show up as a sub menu at the right of the node button, and allow for
64 | // having multple preset configurations for a node.
65 | // Each variant then has a list of setting names the corresponding values that they should be set to.
66 | // For a good reference on what's possible, have a look at the geometry nodes math nodes variants in the built in definition files.
67 | "variants": {
68 | // This defines a variant called "My variant", that has the setting "node_setting" set to the string "node_value"
69 | // This is equivalent to writing node.node_setting = "node_value" in python.
70 | "My variant": { "node_setting": "node_value" },
71 | // This sets the value of the first node input to 5
72 | "My other variant": { "inputs[0].default_value": 5 }
73 | },
74 | // Apply preset poll conditions
75 | "poll_type": "poll_type_name",
76 | // Poll conditions allow node items to only be displayed conditionally, depending on the context.
77 | // They also work on entire categories.
78 | "poll_conditions": [
79 | {
80 | // This is the python data path starting from "bpy.context."
81 | "context_path": "object.name",
82 | // The logical operator to apply to the value. One of:
83 | // "equals": check whether the returned value equals the provided value
84 | // "in": Check whether the returned value is in the provided value
85 | // "bool": cast the returned value to a boolean.
86 | "operand": "equals",
87 | // The value to check the returned value from the context_path against
88 | "value": "Cube"
89 | }
90 | ]
91 | },
92 | // Define a separator like this, the label is optional, is only show if subcategory labels are enabled in the preferences
93 | { "separator": true, "label": "My separator" },
94 | { "identifier": "SN_TimestampNode" }
95 | ]
96 | }
97 | },
98 | // This will add the imported files on top of the config specified in this file
99 | // There can be multiple given in the list to be added one on top of each other.
100 | // The name should just be the name of the definition file without the file extension.
101 | "imports": ["other_def_file"]
102 | }
103 |
--------------------------------------------------------------------------------
/node_pie/node_def_files/sockets/CompositorNodeTree_sockets_4_1.jsonc:
--------------------------------------------------------------------------------
1 | // This is a list of the socket types of all nodes
2 | // used for telling whether a node is valid during link drag.
3 | // It can be auto generated using the 'generate socket types file' operator
4 | {
5 | "bl_version": [
6 | 4,
7 | 1
8 | ],
9 | "nodes": {
10 | "CompositorNodeBokehImage": {
11 | "inputs": [],
12 | "outputs": [
13 | "NodeSocketColor"
14 | ]
15 | },
16 | "CompositorNodeImage": {
17 | "inputs": [],
18 | "outputs": [
19 | "NodeSocketFloat",
20 | "NodeSocketColor"
21 | ]
22 | },
23 | "CompositorNodeMask": {
24 | "inputs": [],
25 | "outputs": [
26 | "NodeSocketFloat"
27 | ]
28 | },
29 | "CompositorNodeMovieClip": {
30 | "inputs": [],
31 | "outputs": [
32 | "NodeSocketFloat",
33 | "NodeSocketColor"
34 | ]
35 | },
36 | "CompositorNodeRLayers": {
37 | "inputs": [],
38 | "outputs": [
39 | "NodeSocketFloat",
40 | "NodeSocketColor",
41 | "NodeSocketVector"
42 | ]
43 | },
44 | "CompositorNodeRGB": {
45 | "inputs": [],
46 | "outputs": [
47 | "NodeSocketColor"
48 | ]
49 | },
50 | "CompositorNodeSceneTime": {
51 | "inputs": [],
52 | "outputs": [
53 | "NodeSocketFloat"
54 | ]
55 | },
56 | "CompositorNodeTexture": {
57 | "inputs": [
58 | "NodeSocketVectorXYZ",
59 | "NodeSocketVectorTranslation"
60 | ],
61 | "outputs": [
62 | "NodeSocketFloat",
63 | "NodeSocketColor"
64 | ]
65 | },
66 | "CompositorNodeTime": {
67 | "inputs": [],
68 | "outputs": [
69 | "NodeSocketFloat"
70 | ]
71 | },
72 | "CompositorNodeTrackPos": {
73 | "inputs": [],
74 | "outputs": [
75 | "NodeSocketVectorVelocity",
76 | "NodeSocketFloat"
77 | ]
78 | },
79 | "CompositorNodeValue": {
80 | "inputs": [],
81 | "outputs": [
82 | "NodeSocketFloat"
83 | ]
84 | },
85 | "CompositorNodeComposite": {
86 | "inputs": [
87 | "NodeSocketFloat",
88 | "NodeSocketColor"
89 | ],
90 | "outputs": []
91 | },
92 | "CompositorNodeOutputFile": {
93 | "inputs": [
94 | "NodeSocketColor"
95 | ],
96 | "outputs": []
97 | },
98 | "CompositorNodeLevels": {
99 | "inputs": [
100 | "NodeSocketColor"
101 | ],
102 | "outputs": [
103 | "NodeSocketFloat"
104 | ]
105 | },
106 | "CompositorNodeViewer": {
107 | "inputs": [
108 | "NodeSocketFloat",
109 | "NodeSocketColor"
110 | ],
111 | "outputs": []
112 | },
113 | "CompositorNodeAlphaOver": {
114 | "inputs": [
115 | "NodeSocketFloatFactor",
116 | "NodeSocketColor"
117 | ],
118 | "outputs": [
119 | "NodeSocketColor"
120 | ]
121 | },
122 | "CompositorNodeBrightContrast": {
123 | "inputs": [
124 | "NodeSocketFloat",
125 | "NodeSocketColor"
126 | ],
127 | "outputs": [
128 | "NodeSocketColor"
129 | ]
130 | },
131 | "CompositorNodeColorBalance": {
132 | "inputs": [
133 | "NodeSocketFloatFactor",
134 | "NodeSocketColor"
135 | ],
136 | "outputs": [
137 | "NodeSocketColor"
138 | ]
139 | },
140 | "CompositorNodeColorCorrection": {
141 | "inputs": [
142 | "NodeSocketFloat",
143 | "NodeSocketColor"
144 | ],
145 | "outputs": [
146 | "NodeSocketColor"
147 | ]
148 | },
149 | "CompositorNodeExposure": {
150 | "inputs": [
151 | "NodeSocketFloat",
152 | "NodeSocketColor"
153 | ],
154 | "outputs": [
155 | "NodeSocketColor"
156 | ]
157 | },
158 | "CompositorNodeGamma": {
159 | "inputs": [
160 | "NodeSocketFloatUnsigned",
161 | "NodeSocketColor"
162 | ],
163 | "outputs": [
164 | "NodeSocketColor"
165 | ]
166 | },
167 | "CompositorNodeHueCorrect": {
168 | "inputs": [
169 | "NodeSocketFloatFactor",
170 | "NodeSocketColor"
171 | ],
172 | "outputs": [
173 | "NodeSocketColor"
174 | ]
175 | },
176 | "CompositorNodeHueSat": {
177 | "inputs": [
178 | "NodeSocketFloatFactor",
179 | "NodeSocketColor"
180 | ],
181 | "outputs": [
182 | "NodeSocketColor"
183 | ]
184 | },
185 | "CompositorNodeInvert": {
186 | "inputs": [
187 | "NodeSocketFloatFactor",
188 | "NodeSocketColor"
189 | ],
190 | "outputs": [
191 | "NodeSocketColor"
192 | ]
193 | },
194 | "CompositorNodeMixRGB": {
195 | "inputs": [
196 | "NodeSocketFloatFactor",
197 | "NodeSocketColor"
198 | ],
199 | "outputs": [
200 | "NodeSocketColor"
201 | ]
202 | },
203 | "CompositorNodePosterize": {
204 | "inputs": [
205 | "NodeSocketFloat",
206 | "NodeSocketColor"
207 | ],
208 | "outputs": [
209 | "NodeSocketColor"
210 | ]
211 | },
212 | "CompositorNodeCurveRGB": {
213 | "inputs": [
214 | "NodeSocketFloatFactor",
215 | "NodeSocketColor"
216 | ],
217 | "outputs": [
218 | "NodeSocketColor"
219 | ]
220 | },
221 | "CompositorNodeTonemap": {
222 | "inputs": [
223 | "NodeSocketColor"
224 | ],
225 | "outputs": [
226 | "NodeSocketColor"
227 | ]
228 | },
229 | "CompositorNodeZcombine": {
230 | "inputs": [
231 | "NodeSocketFloat",
232 | "NodeSocketColor"
233 | ],
234 | "outputs": [
235 | "NodeSocketFloat",
236 | "NodeSocketColor"
237 | ]
238 | },
239 | "CompositorNodePremulKey": {
240 | "inputs": [
241 | "NodeSocketColor"
242 | ],
243 | "outputs": [
244 | "NodeSocketColor"
245 | ]
246 | },
247 | "CompositorNodeConvertColorSpace": {
248 | "inputs": [
249 | "NodeSocketColor"
250 | ],
251 | "outputs": [
252 | "NodeSocketColor"
253 | ]
254 | },
255 | "CompositorNodeValToRGB": {
256 | "inputs": [
257 | "NodeSocketFloatFactor"
258 | ],
259 | "outputs": [
260 | "NodeSocketFloat",
261 | "NodeSocketColor"
262 | ]
263 | },
264 | "CompositorNodeCombineColor": {
265 | "inputs": [
266 | "NodeSocketFloatFactor"
267 | ],
268 | "outputs": [
269 | "NodeSocketColor"
270 | ]
271 | },
272 | "CompositorNodeCombineXYZ": {
273 | "inputs": [
274 | "NodeSocketFloat"
275 | ],
276 | "outputs": [
277 | "NodeSocketVector"
278 | ]
279 | },
280 | "CompositorNodeIDMask": {
281 | "inputs": [
282 | "NodeSocketFloat"
283 | ],
284 | "outputs": [
285 | "NodeSocketFloat"
286 | ]
287 | },
288 | "CompositorNodeMath": {
289 | "inputs": [
290 | "NodeSocketFloat"
291 | ],
292 | "outputs": [
293 | "NodeSocketFloat"
294 | ]
295 | },
296 | "CompositorNodeRGBToBW": {
297 | "inputs": [
298 | "NodeSocketColor"
299 | ],
300 | "outputs": [
301 | "NodeSocketFloat"
302 | ]
303 | },
304 | "CompositorNodeSeparateColor": {
305 | "inputs": [
306 | "NodeSocketColor"
307 | ],
308 | "outputs": [
309 | "NodeSocketFloat"
310 | ]
311 | },
312 | "CompositorNodeSeparateXYZ": {
313 | "inputs": [
314 | "NodeSocketVector"
315 | ],
316 | "outputs": [
317 | "NodeSocketFloat"
318 | ]
319 | },
320 | "CompositorNodeSetAlpha": {
321 | "inputs": [
322 | "NodeSocketFloat",
323 | "NodeSocketColor"
324 | ],
325 | "outputs": [
326 | "NodeSocketColor"
327 | ]
328 | },
329 | "CompositorNodeSwitch": {
330 | "inputs": [
331 | "NodeSocketColor"
332 | ],
333 | "outputs": [
334 | "NodeSocketColor"
335 | ]
336 | },
337 | "CompositorNodeSwitchView": {
338 | "inputs": [
339 | "NodeSocketColor"
340 | ],
341 | "outputs": [
342 | "NodeSocketColor"
343 | ]
344 | },
345 | "CompositorNodeAntiAliasing": {
346 | "inputs": [
347 | "NodeSocketColor"
348 | ],
349 | "outputs": [
350 | "NodeSocketColor"
351 | ]
352 | },
353 | "CompositorNodeBilateralblur": {
354 | "inputs": [
355 | "NodeSocketColor"
356 | ],
357 | "outputs": [
358 | "NodeSocketColor"
359 | ]
360 | },
361 | "CompositorNodeBlur": {
362 | "inputs": [
363 | "NodeSocketFloat",
364 | "NodeSocketColor"
365 | ],
366 | "outputs": [
367 | "NodeSocketColor"
368 | ]
369 | },
370 | "CompositorNodeBokehBlur": {
371 | "inputs": [
372 | "NodeSocketFloat",
373 | "NodeSocketColor"
374 | ],
375 | "outputs": [
376 | "NodeSocketColor"
377 | ]
378 | },
379 | "CompositorNodeDefocus": {
380 | "inputs": [
381 | "NodeSocketFloat",
382 | "NodeSocketColor"
383 | ],
384 | "outputs": [
385 | "NodeSocketColor"
386 | ]
387 | },
388 | "CompositorNodeDenoise": {
389 | "inputs": [
390 | "NodeSocketColor",
391 | "NodeSocketVector"
392 | ],
393 | "outputs": [
394 | "NodeSocketColor"
395 | ]
396 | },
397 | "CompositorNodeDespeckle": {
398 | "inputs": [
399 | "NodeSocketFloatFactor",
400 | "NodeSocketColor"
401 | ],
402 | "outputs": [
403 | "NodeSocketColor"
404 | ]
405 | },
406 | "CompositorNodeDilateErode": {
407 | "inputs": [
408 | "NodeSocketFloat"
409 | ],
410 | "outputs": [
411 | "NodeSocketFloat"
412 | ]
413 | },
414 | "CompositorNodeDBlur": {
415 | "inputs": [
416 | "NodeSocketColor"
417 | ],
418 | "outputs": [
419 | "NodeSocketColor"
420 | ]
421 | },
422 | "CompositorNodeFilter": {
423 | "inputs": [
424 | "NodeSocketFloatFactor",
425 | "NodeSocketColor"
426 | ],
427 | "outputs": [
428 | "NodeSocketColor"
429 | ]
430 | },
431 | "CompositorNodeGlare": {
432 | "inputs": [
433 | "NodeSocketColor"
434 | ],
435 | "outputs": [
436 | "NodeSocketColor"
437 | ]
438 | },
439 | "CompositorNodeInpaint": {
440 | "inputs": [
441 | "NodeSocketColor"
442 | ],
443 | "outputs": [
444 | "NodeSocketColor"
445 | ]
446 | },
447 | "CompositorNodeKuwahara": {
448 | "inputs": [
449 | "NodeSocketFloat",
450 | "NodeSocketColor"
451 | ],
452 | "outputs": [
453 | "NodeSocketColor"
454 | ]
455 | },
456 | "CompositorNodePixelate": {
457 | "inputs": [
458 | "NodeSocketColor"
459 | ],
460 | "outputs": [
461 | "NodeSocketColor"
462 | ]
463 | },
464 | "CompositorNodeSunBeams": {
465 | "inputs": [
466 | "NodeSocketColor"
467 | ],
468 | "outputs": [
469 | "NodeSocketColor"
470 | ]
471 | },
472 | "CompositorNodeVecBlur": {
473 | "inputs": [
474 | "NodeSocketVectorVelocity",
475 | "NodeSocketFloat",
476 | "NodeSocketColor"
477 | ],
478 | "outputs": [
479 | "NodeSocketColor"
480 | ]
481 | },
482 | "CompositorNodeMapRange": {
483 | "inputs": [
484 | "NodeSocketFloat"
485 | ],
486 | "outputs": [
487 | "NodeSocketFloat"
488 | ]
489 | },
490 | "CompositorNodeMapValue": {
491 | "inputs": [
492 | "NodeSocketFloat"
493 | ],
494 | "outputs": [
495 | "NodeSocketFloat"
496 | ]
497 | },
498 | "CompositorNodeNormal": {
499 | "inputs": [
500 | "NodeSocketVectorDirection"
501 | ],
502 | "outputs": [
503 | "NodeSocketFloat",
504 | "NodeSocketVectorDirection"
505 | ]
506 | },
507 | "CompositorNodeNormalize": {
508 | "inputs": [
509 | "NodeSocketFloat"
510 | ],
511 | "outputs": [
512 | "NodeSocketFloat"
513 | ]
514 | },
515 | "CompositorNodeCurveVec": {
516 | "inputs": [
517 | "NodeSocketVector"
518 | ],
519 | "outputs": [
520 | "NodeSocketVector"
521 | ]
522 | },
523 | "CompositorNodeBoxMask": {
524 | "inputs": [
525 | "NodeSocketFloat"
526 | ],
527 | "outputs": [
528 | "NodeSocketFloat"
529 | ]
530 | },
531 | "CompositorNodeChannelMatte": {
532 | "inputs": [
533 | "NodeSocketColor"
534 | ],
535 | "outputs": [
536 | "NodeSocketFloat",
537 | "NodeSocketColor"
538 | ]
539 | },
540 | "CompositorNodeChromaMatte": {
541 | "inputs": [
542 | "NodeSocketColor"
543 | ],
544 | "outputs": [
545 | "NodeSocketFloat",
546 | "NodeSocketColor"
547 | ]
548 | },
549 | "CompositorNodeColorMatte": {
550 | "inputs": [
551 | "NodeSocketColor"
552 | ],
553 | "outputs": [
554 | "NodeSocketFloat",
555 | "NodeSocketColor"
556 | ]
557 | },
558 | "CompositorNodeColorSpill": {
559 | "inputs": [
560 | "NodeSocketFloatFactor",
561 | "NodeSocketColor"
562 | ],
563 | "outputs": [
564 | "NodeSocketColor"
565 | ]
566 | },
567 | "CompositorNodeCryptomatteV2": {
568 | "inputs": [
569 | "NodeSocketColor"
570 | ],
571 | "outputs": [
572 | "NodeSocketFloat",
573 | "NodeSocketColor"
574 | ]
575 | },
576 | "CompositorNodeCryptomatte": {
577 | "inputs": [
578 | "NodeSocketColor"
579 | ],
580 | "outputs": [
581 | "NodeSocketFloat",
582 | "NodeSocketColor"
583 | ]
584 | },
585 | "CompositorNodeDiffMatte": {
586 | "inputs": [
587 | "NodeSocketColor"
588 | ],
589 | "outputs": [
590 | "NodeSocketFloat",
591 | "NodeSocketColor"
592 | ]
593 | },
594 | "CompositorNodeDistanceMatte": {
595 | "inputs": [
596 | "NodeSocketColor"
597 | ],
598 | "outputs": [
599 | "NodeSocketFloat",
600 | "NodeSocketColor"
601 | ]
602 | },
603 | "CompositorNodeDoubleEdgeMask": {
604 | "inputs": [
605 | "NodeSocketFloat"
606 | ],
607 | "outputs": [
608 | "NodeSocketFloat"
609 | ]
610 | },
611 | "CompositorNodeEllipseMask": {
612 | "inputs": [
613 | "NodeSocketFloat"
614 | ],
615 | "outputs": [
616 | "NodeSocketFloat"
617 | ]
618 | },
619 | "CompositorNodeKeying": {
620 | "inputs": [
621 | "NodeSocketFloat",
622 | "NodeSocketColor"
623 | ],
624 | "outputs": [
625 | "NodeSocketFloat",
626 | "NodeSocketColor"
627 | ]
628 | },
629 | "CompositorNodeKeyingScreen": {
630 | "inputs": [],
631 | "outputs": [
632 | "NodeSocketColor"
633 | ]
634 | },
635 | "CompositorNodeLumaMatte": {
636 | "inputs": [
637 | "NodeSocketColor"
638 | ],
639 | "outputs": [
640 | "NodeSocketFloat",
641 | "NodeSocketColor"
642 | ]
643 | },
644 | "CompositorNodeCornerPin": {
645 | "inputs": [
646 | "NodeSocketColor",
647 | "NodeSocketVector"
648 | ],
649 | "outputs": [
650 | "NodeSocketFloat",
651 | "NodeSocketColor"
652 | ]
653 | },
654 | "CompositorNodeCrop": {
655 | "inputs": [
656 | "NodeSocketColor"
657 | ],
658 | "outputs": [
659 | "NodeSocketColor"
660 | ]
661 | },
662 | "CompositorNodeDisplace": {
663 | "inputs": [
664 | "NodeSocketFloat",
665 | "NodeSocketColor",
666 | "NodeSocketVectorTranslation"
667 | ],
668 | "outputs": [
669 | "NodeSocketColor"
670 | ]
671 | },
672 | "CompositorNodeFlip": {
673 | "inputs": [
674 | "NodeSocketColor"
675 | ],
676 | "outputs": [
677 | "NodeSocketColor"
678 | ]
679 | },
680 | "CompositorNodeLensdist": {
681 | "inputs": [
682 | "NodeSocketFloat",
683 | "NodeSocketColor"
684 | ],
685 | "outputs": [
686 | "NodeSocketColor"
687 | ]
688 | },
689 | "CompositorNodeMapUV": {
690 | "inputs": [
691 | "NodeSocketColor",
692 | "NodeSocketVector"
693 | ],
694 | "outputs": [
695 | "NodeSocketColor"
696 | ]
697 | },
698 | "CompositorNodeMovieDistortion": {
699 | "inputs": [
700 | "NodeSocketColor"
701 | ],
702 | "outputs": [
703 | "NodeSocketColor"
704 | ]
705 | },
706 | "CompositorNodePlaneTrackDeform": {
707 | "inputs": [
708 | "NodeSocketColor"
709 | ],
710 | "outputs": [
711 | "NodeSocketFloat",
712 | "NodeSocketColor"
713 | ]
714 | },
715 | "CompositorNodeRotate": {
716 | "inputs": [
717 | "NodeSocketColor",
718 | "NodeSocketFloatAngle"
719 | ],
720 | "outputs": [
721 | "NodeSocketColor"
722 | ]
723 | },
724 | "CompositorNodeScale": {
725 | "inputs": [
726 | "NodeSocketFloat",
727 | "NodeSocketColor"
728 | ],
729 | "outputs": [
730 | "NodeSocketColor"
731 | ]
732 | },
733 | "CompositorNodeStabilize": {
734 | "inputs": [
735 | "NodeSocketColor"
736 | ],
737 | "outputs": [
738 | "NodeSocketColor"
739 | ]
740 | },
741 | "CompositorNodeTransform": {
742 | "inputs": [
743 | "NodeSocketFloat",
744 | "NodeSocketColor",
745 | "NodeSocketFloatAngle"
746 | ],
747 | "outputs": [
748 | "NodeSocketColor"
749 | ]
750 | },
751 | "CompositorNodeTranslate": {
752 | "inputs": [
753 | "NodeSocketFloat",
754 | "NodeSocketColor"
755 | ],
756 | "outputs": [
757 | "NodeSocketColor"
758 | ]
759 | },
760 | "NodeFrame": {
761 | "inputs": [],
762 | "outputs": []
763 | },
764 | "NodeReroute": {
765 | "inputs": [
766 | "NodeSocketColor"
767 | ],
768 | "outputs": [
769 | "NodeSocketColor"
770 | ]
771 | }
772 | }
773 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/sockets/CompositorNodeTree_sockets_4_3.jsonc:
--------------------------------------------------------------------------------
1 | // This is a list of the socket types of all nodes
2 | // used for telling whether a node is valid during link drag.
3 | // It contains all of the new and updated nodes in this blender version.
4 | // It can be auto generated using the 'generate socket types file' operator
5 | {
6 | "bl_version": [
7 | 4,
8 | 3
9 | ],
10 | "nodes": {
11 | "CompositorNodeSplit": {
12 | "inputs": [
13 | "NodeSocketColor"
14 | ],
15 | "outputs": [
16 | "NodeSocketColor"
17 | ]
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/sockets/GeometryNodeTree_sockets_4_2.jsonc:
--------------------------------------------------------------------------------
1 | // This is a list of the socket types of all nodes
2 | // used for telling whether a node is valid during link drag.
3 | // It contains all of the new and updated nodes in this blender version.
4 | // It can be auto generated using the 'generate socket types file' operator
5 | {
6 | "bl_version": [
7 | 4,
8 | 2
9 | ],
10 | "nodes": {
11 | "GeometryNodeCaptureAttribute": {
12 | "inputs": [
13 | "NodeSocketVirtual",
14 | "NodeSocketGeometry"
15 | ],
16 | "outputs": [
17 | "NodeSocketVirtual",
18 | "NodeSocketGeometry"
19 | ]
20 | },
21 | "GeometryNodeCurveToPoints": {
22 | "inputs": [
23 | "NodeSocketGeometry",
24 | "NodeSocketInt",
25 | "NodeSocketFloatDistance"
26 | ],
27 | "outputs": [
28 | "NodeSocketRotation",
29 | "NodeSocketGeometry",
30 | "NodeSocketVector"
31 | ]
32 | },
33 | "GeometryNodeSetCurveNormal": {
34 | "inputs": [
35 | "NodeSocketGeometry",
36 | "NodeSocketBool",
37 | "NodeSocketVectorXYZ"
38 | ],
39 | "outputs": [
40 | "NodeSocketGeometry"
41 | ]
42 | },
43 | "GeometryNodeTransform": {
44 | "inputs": [
45 | "NodeSocketVectorTranslation",
46 | "NodeSocketGeometry",
47 | "NodeSocketVectorXYZ",
48 | "NodeSocketRotation",
49 | "NodeSocketMatrix"
50 | ],
51 | "outputs": [
52 | "NodeSocketGeometry"
53 | ]
54 | },
55 | "GeometryNodeProximity": {
56 | "inputs": [
57 | "NodeSocketGeometry",
58 | "NodeSocketVector",
59 | "NodeSocketInt"
60 | ],
61 | "outputs": [
62 | "NodeSocketVector",
63 | "NodeSocketFloat",
64 | "NodeSocketBool"
65 | ]
66 | },
67 | "GeometryNodeToolMousePosition": {
68 | "inputs": [],
69 | "outputs": [
70 | "NodeSocketInt"
71 | ]
72 | },
73 | "GeometryNodeObjectInfo": {
74 | "inputs": [
75 | "NodeSocketObject",
76 | "NodeSocketBool"
77 | ],
78 | "outputs": [
79 | "NodeSocketRotation",
80 | "NodeSocketGeometry",
81 | "NodeSocketMatrix",
82 | "NodeSocketVector"
83 | ]
84 | },
85 | "GeometryNodeViewportTransform": {
86 | "inputs": [],
87 | "outputs": [
88 | "NodeSocketMatrix",
89 | "NodeSocketBool"
90 | ]
91 | },
92 | "GeometryNodeRealizeInstances": {
93 | "inputs": [
94 | "NodeSocketGeometry",
95 | "NodeSocketInt",
96 | "NodeSocketBool"
97 | ],
98 | "outputs": [
99 | "NodeSocketGeometry"
100 | ]
101 | },
102 | "GeometryNodeIndexSwitch": {
103 | "inputs": [
104 | "NodeSocketObject",
105 | "NodeSocketCollection",
106 | "NodeSocketVirtual",
107 | "NodeSocketImage",
108 | "NodeSocketFloat",
109 | "NodeSocketColor",
110 | "NodeSocketMaterial",
111 | "NodeSocketInt",
112 | "NodeSocketGeometry",
113 | "NodeSocketMenu",
114 | "NodeSocketVector",
115 | "NodeSocketRotation",
116 | "NodeSocketString",
117 | "NodeSocketMatrix",
118 | "NodeSocketBool"
119 | ],
120 | "outputs": [
121 | "NodeSocketObject",
122 | "NodeSocketCollection",
123 | "NodeSocketImage",
124 | "NodeSocketFloat",
125 | "NodeSocketColor",
126 | "NodeSocketMaterial",
127 | "NodeSocketGeometry",
128 | "NodeSocketMenu",
129 | "NodeSocketInt",
130 | "NodeSocketVector",
131 | "NodeSocketRotation",
132 | "NodeSocketString",
133 | "NodeSocketMatrix",
134 | "NodeSocketBool"
135 | ]
136 | },
137 | "GeometryNodeSwitch": {
138 | "inputs": [
139 | "NodeSocketObject",
140 | "NodeSocketCollection",
141 | "NodeSocketImage",
142 | "NodeSocketFloat",
143 | "NodeSocketColor",
144 | "NodeSocketMaterial",
145 | "NodeSocketGeometry",
146 | "NodeSocketMenu",
147 | "NodeSocketInt",
148 | "NodeSocketVector",
149 | "NodeSocketRotation",
150 | "NodeSocketString",
151 | "NodeSocketMatrix",
152 | "NodeSocketBool"
153 | ],
154 | "outputs": [
155 | "NodeSocketObject",
156 | "NodeSocketCollection",
157 | "NodeSocketImage",
158 | "NodeSocketFloat",
159 | "NodeSocketColor",
160 | "NodeSocketMaterial",
161 | "NodeSocketGeometry",
162 | "NodeSocketMenu",
163 | "NodeSocketInt",
164 | "NodeSocketVector",
165 | "NodeSocketRotation",
166 | "NodeSocketString",
167 | "NodeSocketMatrix",
168 | "NodeSocketBool"
169 | ]
170 | },
171 | "FunctionNodeAlignRotationToVector": {
172 | "inputs": [
173 | "NodeSocketRotation",
174 | "NodeSocketFloatFactor",
175 | "NodeSocketVectorXYZ"
176 | ],
177 | "outputs": [
178 | "NodeSocketRotation"
179 | ]
180 | },
181 | "FunctionNodeCombineTransform": {
182 | "inputs": [
183 | "NodeSocketVectorTranslation",
184 | "NodeSocketRotation",
185 | "NodeSocketVectorXYZ"
186 | ],
187 | "outputs": [
188 | "NodeSocketMatrix"
189 | ]
190 | },
191 | "FunctionNodeInvertMatrix": {
192 | "inputs": [
193 | "NodeSocketMatrix"
194 | ],
195 | "outputs": [
196 | "NodeSocketMatrix",
197 | "NodeSocketBool"
198 | ]
199 | },
200 | "FunctionNodeMatrixMultiply": {
201 | "inputs": [
202 | "NodeSocketMatrix"
203 | ],
204 | "outputs": [
205 | "NodeSocketMatrix"
206 | ]
207 | },
208 | "FunctionNodeProjectPoint": {
209 | "inputs": [
210 | "NodeSocketMatrix",
211 | "NodeSocketVectorXYZ"
212 | ],
213 | "outputs": [
214 | "NodeSocketVectorXYZ"
215 | ]
216 | },
217 | "FunctionNodeSeparateTransform": {
218 | "inputs": [
219 | "NodeSocketMatrix"
220 | ],
221 | "outputs": [
222 | "NodeSocketVectorTranslation",
223 | "NodeSocketRotation",
224 | "NodeSocketVectorXYZ"
225 | ]
226 | },
227 | "FunctionNodeTransformDirection": {
228 | "inputs": [
229 | "NodeSocketMatrix",
230 | "NodeSocketVectorXYZ"
231 | ],
232 | "outputs": [
233 | "NodeSocketVectorXYZ"
234 | ]
235 | },
236 | "FunctionNodeTransformPoint": {
237 | "inputs": [
238 | "NodeSocketMatrix",
239 | "NodeSocketVectorXYZ"
240 | ],
241 | "outputs": [
242 | "NodeSocketVectorXYZ"
243 | ]
244 | },
245 | "FunctionNodeTransposeMatrix": {
246 | "inputs": [
247 | "NodeSocketMatrix"
248 | ],
249 | "outputs": [
250 | "NodeSocketMatrix"
251 | ]
252 | }
253 | }
254 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/sockets/GeometryNodeTree_sockets_4_3.jsonc:
--------------------------------------------------------------------------------
1 | // This is a list of the socket types of all nodes
2 | // used for telling whether a node is valid during link drag.
3 | // It contains all of the new and updated nodes in this blender version.
4 | // It can be auto generated using the 'generate socket types file' operator
5 | {
6 | "bl_version": [
7 | 4,
8 | 3
9 | ],
10 | "nodes": {
11 | "ShaderNodeBlackbody": {
12 | "inputs": [
13 | "NodeSocketFloatColorTemperature"
14 | ],
15 | "outputs": [
16 | "NodeSocketColor"
17 | ]
18 | },
19 | "GeometryNodeCurvesToGreasePencil": {
20 | "inputs": [
21 | "NodeSocketGeometry",
22 | "NodeSocketBool"
23 | ],
24 | "outputs": [
25 | "NodeSocketGeometry"
26 | ]
27 | },
28 | "GeometryNodeGreasePencilToCurves": {
29 | "inputs": [
30 | "NodeSocketGeometry",
31 | "NodeSocketBool"
32 | ],
33 | "outputs": [
34 | "NodeSocketGeometry"
35 | ]
36 | },
37 | "GeometryNodeMergeLayers": {
38 | "inputs": [
39 | "NodeSocketGeometry",
40 | "NodeSocketInt",
41 | "NodeSocketBool"
42 | ],
43 | "outputs": [
44 | "NodeSocketGeometry"
45 | ]
46 | },
47 | "GeometryNodeBake": {
48 | "inputs": [
49 | "NodeSocketGeometry",
50 | "NodeSocketVirtual"
51 | ],
52 | "outputs": [
53 | "NodeSocketGeometry",
54 | "NodeSocketVirtual"
55 | ]
56 | },
57 | "GeometryNodeSetGeometryName": {
58 | "inputs": [
59 | "NodeSocketGeometry",
60 | "NodeSocketString"
61 | ],
62 | "outputs": [
63 | "NodeSocketGeometry"
64 | ]
65 | },
66 | "FunctionNodeInputRotation": {
67 | "inputs": [],
68 | "outputs": [
69 | "NodeSocketRotation"
70 | ]
71 | },
72 | "GeometryNodeWarning": {
73 | "inputs": [
74 | "NodeSocketBool",
75 | "NodeSocketString"
76 | ],
77 | "outputs": [
78 | "NodeSocketBool"
79 | ]
80 | },
81 | "GeometryNodeInputNamedLayerSelection": {
82 | "inputs": [
83 | "NodeSocketString"
84 | ],
85 | "outputs": [
86 | "NodeSocketBool"
87 | ]
88 | },
89 | "GeometryNodeToolActiveElement": {
90 | "inputs": [],
91 | "outputs": [
92 | "NodeSocketInt",
93 | "NodeSocketBool"
94 | ]
95 | },
96 | "GeometryNodeToolSelection": {
97 | "inputs": [],
98 | "outputs": [
99 | "NodeSocketFloat",
100 | "NodeSocketBool"
101 | ]
102 | },
103 | "GeometryNodeSetInstanceTransform": {
104 | "inputs": [
105 | "NodeSocketGeometry",
106 | "NodeSocketBool",
107 | "NodeSocketMatrix"
108 | ],
109 | "outputs": [
110 | "NodeSocketGeometry"
111 | ]
112 | },
113 | "GeometryNodeInstanceTransform": {
114 | "inputs": [],
115 | "outputs": [
116 | "NodeSocketMatrix"
117 | ]
118 | },
119 | "GeometryNodePointsToCurves": {
120 | "inputs": [
121 | "NodeSocketGeometry",
122 | "NodeSocketInt",
123 | "NodeSocketFloat"
124 | ],
125 | "outputs": [
126 | "NodeSocketGeometry"
127 | ]
128 | },
129 | "ShaderNodeTexGabor": {
130 | "inputs": [
131 | "NodeSocketVectorDirection",
132 | "NodeSocketFloatFactor",
133 | "NodeSocketFloat",
134 | "NodeSocketFloatAngle",
135 | "NodeSocketVector"
136 | ],
137 | "outputs": [
138 | "NodeSocketFloat"
139 | ]
140 | },
141 | "FunctionNodeHashValue": {
142 | "inputs": [
143 | "NodeSocketMatrix",
144 | "NodeSocketString",
145 | "NodeSocketRotation",
146 | "NodeSocketColor",
147 | "NodeSocketFloat",
148 | "NodeSocketVector",
149 | "NodeSocketInt"
150 | ],
151 | "outputs": [
152 | "NodeSocketInt"
153 | ]
154 | },
155 | "FunctionNodeIntegerMath": {
156 | "inputs": [
157 | "NodeSocketInt"
158 | ],
159 | "outputs": [
160 | "NodeSocketInt"
161 | ]
162 | },
163 | "GeometryNodeMenuSwitch": {
164 | "inputs": [
165 | "NodeSocketGeometry",
166 | "NodeSocketVirtual",
167 | "NodeSocketMenu",
168 | "NodeSocketMatrix",
169 | "NodeSocketString",
170 | "NodeSocketRotation",
171 | "NodeSocketBool",
172 | "NodeSocketColor",
173 | "NodeSocketMaterial",
174 | "NodeSocketObject",
175 | "NodeSocketFloat",
176 | "NodeSocketVector",
177 | "NodeSocketImage",
178 | "NodeSocketInt",
179 | "NodeSocketCollection"
180 | ],
181 | "outputs": [
182 | "NodeSocketGeometry",
183 | "NodeSocketMatrix",
184 | "NodeSocketString",
185 | "NodeSocketRotation",
186 | "NodeSocketBool",
187 | "NodeSocketColor",
188 | "NodeSocketMaterial",
189 | "NodeSocketObject",
190 | "NodeSocketFloat",
191 | "NodeSocketVector",
192 | "NodeSocketImage",
193 | "NodeSocketInt",
194 | "NodeSocketCollection"
195 | ]
196 | },
197 | "FunctionNodeAxesToRotation": {
198 | "inputs": [
199 | "NodeSocketVector"
200 | ],
201 | "outputs": [
202 | "NodeSocketRotation"
203 | ]
204 | },
205 | "FunctionNodeCombineMatrix": {
206 | "inputs": [
207 | "NodeSocketFloat"
208 | ],
209 | "outputs": [
210 | "NodeSocketMatrix"
211 | ]
212 | },
213 | "FunctionNodeMatrixDeterminant": {
214 | "inputs": [
215 | "NodeSocketMatrix"
216 | ],
217 | "outputs": [
218 | "NodeSocketFloat"
219 | ]
220 | },
221 | "FunctionNodeSeparateMatrix": {
222 | "inputs": [
223 | "NodeSocketMatrix"
224 | ],
225 | "outputs": [
226 | "NodeSocketFloat"
227 | ]
228 | },
229 | "GeometryNodeGizmoDial": {
230 | "inputs": [
231 | "NodeSocketFloat",
232 | "NodeSocketVectorTranslation",
233 | "NodeSocketBool",
234 | "NodeSocketVectorXYZ"
235 | ],
236 | "outputs": [
237 | "NodeSocketGeometry"
238 | ]
239 | },
240 | "GeometryNodeGizmoLinear": {
241 | "inputs": [
242 | "NodeSocketFloat",
243 | "NodeSocketVectorTranslation",
244 | "NodeSocketVectorXYZ"
245 | ],
246 | "outputs": [
247 | "NodeSocketGeometry"
248 | ]
249 | },
250 | "GeometryNodeGizmoTransform": {
251 | "inputs": [
252 | "NodeSocketVectorTranslation",
253 | "NodeSocketMatrix",
254 | "NodeSocketRotation"
255 | ],
256 | "outputs": [
257 | "NodeSocketGeometry"
258 | ]
259 | }
260 | }
261 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/sockets/ShaderNodeTree_sockets_4_1.jsonc:
--------------------------------------------------------------------------------
1 | // This is a list of the socket types of all nodes
2 | // used for telling whether a node is valid during link drag.
3 | // It can be auto generated using the 'generate socket types file' operator
4 | {
5 | "bl_version": [
6 | 4,
7 | 1
8 | ],
9 | "nodes": {
10 | "ShaderNodeAmbientOcclusion": {
11 | "inputs": [
12 | "NodeSocketFloat",
13 | "NodeSocketColor",
14 | "NodeSocketVector"
15 | ],
16 | "outputs": [
17 | "NodeSocketFloat",
18 | "NodeSocketColor"
19 | ]
20 | },
21 | "ShaderNodeAttribute": {
22 | "inputs": [],
23 | "outputs": [
24 | "NodeSocketFloat",
25 | "NodeSocketColor",
26 | "NodeSocketVector"
27 | ]
28 | },
29 | "ShaderNodeCameraData": {
30 | "inputs": [],
31 | "outputs": [
32 | "NodeSocketFloat",
33 | "NodeSocketVector"
34 | ]
35 | },
36 | "ShaderNodeVertexColor": {
37 | "inputs": [],
38 | "outputs": [
39 | "NodeSocketFloat",
40 | "NodeSocketColor"
41 | ]
42 | },
43 | "ShaderNodeHairInfo": {
44 | "inputs": [],
45 | "outputs": [
46 | "NodeSocketFloat",
47 | "NodeSocketVector"
48 | ]
49 | },
50 | "ShaderNodeFresnel": {
51 | "inputs": [
52 | "NodeSocketFloat",
53 | "NodeSocketVector"
54 | ],
55 | "outputs": [
56 | "NodeSocketFloat"
57 | ]
58 | },
59 | "ShaderNodeNewGeometry": {
60 | "inputs": [],
61 | "outputs": [
62 | "NodeSocketFloat",
63 | "NodeSocketVector"
64 | ]
65 | },
66 | "ShaderNodeLayerWeight": {
67 | "inputs": [
68 | "NodeSocketFloat",
69 | "NodeSocketVector"
70 | ],
71 | "outputs": [
72 | "NodeSocketFloat"
73 | ]
74 | },
75 | "ShaderNodeLightPath": {
76 | "inputs": [],
77 | "outputs": [
78 | "NodeSocketFloat"
79 | ]
80 | },
81 | "ShaderNodeObjectInfo": {
82 | "inputs": [],
83 | "outputs": [
84 | "NodeSocketFloat",
85 | "NodeSocketColor",
86 | "NodeSocketVector"
87 | ]
88 | },
89 | "ShaderNodePointInfo": {
90 | "inputs": [],
91 | "outputs": [
92 | "NodeSocketFloat",
93 | "NodeSocketVector"
94 | ]
95 | },
96 | "ShaderNodeRGB": {
97 | "inputs": [],
98 | "outputs": [
99 | "NodeSocketColor"
100 | ]
101 | },
102 | "ShaderNodeTangent": {
103 | "inputs": [],
104 | "outputs": [
105 | "NodeSocketVector"
106 | ]
107 | },
108 | "ShaderNodeTexCoord": {
109 | "inputs": [],
110 | "outputs": [
111 | "NodeSocketVector"
112 | ]
113 | },
114 | "ShaderNodeUVMap": {
115 | "inputs": [],
116 | "outputs": [
117 | "NodeSocketVector"
118 | ]
119 | },
120 | "ShaderNodeValue": {
121 | "inputs": [],
122 | "outputs": [
123 | "NodeSocketFloat"
124 | ]
125 | },
126 | "ShaderNodeVolumeInfo": {
127 | "inputs": [],
128 | "outputs": [
129 | "NodeSocketFloat",
130 | "NodeSocketColor"
131 | ]
132 | },
133 | "ShaderNodeWireframe": {
134 | "inputs": [
135 | "NodeSocketFloat"
136 | ],
137 | "outputs": [
138 | "NodeSocketFloat"
139 | ]
140 | },
141 | "ShaderNodeBevel": {
142 | "inputs": [
143 | "NodeSocketFloat",
144 | "NodeSocketVector"
145 | ],
146 | "outputs": [
147 | "NodeSocketVector"
148 | ]
149 | },
150 | "ShaderNodeParticleInfo": {
151 | "inputs": [],
152 | "outputs": [
153 | "NodeSocketFloat",
154 | "NodeSocketVector"
155 | ]
156 | },
157 | "ShaderNodeOutputAOV": {
158 | "inputs": [
159 | "NodeSocketFloat",
160 | "NodeSocketColor"
161 | ],
162 | "outputs": []
163 | },
164 | "ShaderNodeOutputLight": {
165 | "inputs": [
166 | "NodeSocketShader"
167 | ],
168 | "outputs": []
169 | },
170 | "ShaderNodeOutputMaterial": {
171 | "inputs": [
172 | "NodeSocketShader",
173 | "NodeSocketFloat",
174 | "NodeSocketVector"
175 | ],
176 | "outputs": []
177 | },
178 | "ShaderNodeAddShader": {
179 | "inputs": [
180 | "NodeSocketShader"
181 | ],
182 | "outputs": [
183 | "NodeSocketShader"
184 | ]
185 | },
186 | "ShaderNodeBsdfDiffuse": {
187 | "inputs": [
188 | "NodeSocketFloatFactor",
189 | "NodeSocketFloat",
190 | "NodeSocketColor",
191 | "NodeSocketVector"
192 | ],
193 | "outputs": [
194 | "NodeSocketShader"
195 | ]
196 | },
197 | "ShaderNodeEmission": {
198 | "inputs": [
199 | "NodeSocketFloat",
200 | "NodeSocketColor"
201 | ],
202 | "outputs": [
203 | "NodeSocketShader"
204 | ]
205 | },
206 | "ShaderNodeBsdfGlass": {
207 | "inputs": [
208 | "NodeSocketFloatFactor",
209 | "NodeSocketFloat",
210 | "NodeSocketColor",
211 | "NodeSocketVector"
212 | ],
213 | "outputs": [
214 | "NodeSocketShader"
215 | ]
216 | },
217 | "ShaderNodeBsdfAnisotropic": {
218 | "inputs": [
219 | "NodeSocketFloatFactor",
220 | "NodeSocketFloat",
221 | "NodeSocketColor",
222 | "NodeSocketVector"
223 | ],
224 | "outputs": [
225 | "NodeSocketShader"
226 | ]
227 | },
228 | "ShaderNodeHoldout": {
229 | "inputs": [
230 | "NodeSocketFloat"
231 | ],
232 | "outputs": [
233 | "NodeSocketShader"
234 | ]
235 | },
236 | "ShaderNodeMixShader": {
237 | "inputs": [
238 | "NodeSocketFloatFactor",
239 | "NodeSocketShader"
240 | ],
241 | "outputs": [
242 | "NodeSocketShader"
243 | ]
244 | },
245 | "ShaderNodeBsdfPrincipled": {
246 | "inputs": [
247 | "NodeSocketFloatFactor",
248 | "NodeSocketVector",
249 | "NodeSocketFloat",
250 | "NodeSocketColor",
251 | "NodeSocketFloatDistance"
252 | ],
253 | "outputs": [
254 | "NodeSocketShader"
255 | ]
256 | },
257 | "ShaderNodeVolumePrincipled": {
258 | "inputs": [
259 | "NodeSocketString",
260 | "NodeSocketFloatFactor",
261 | "NodeSocketFloat",
262 | "NodeSocketColor"
263 | ],
264 | "outputs": [
265 | "NodeSocketShader"
266 | ]
267 | },
268 | "ShaderNodeBsdfRefraction": {
269 | "inputs": [
270 | "NodeSocketFloatFactor",
271 | "NodeSocketFloat",
272 | "NodeSocketColor",
273 | "NodeSocketVector"
274 | ],
275 | "outputs": [
276 | "NodeSocketShader"
277 | ]
278 | },
279 | "ShaderNodeSubsurfaceScattering": {
280 | "inputs": [
281 | "NodeSocketFloatFactor",
282 | "NodeSocketFloat",
283 | "NodeSocketColor",
284 | "NodeSocketVector"
285 | ],
286 | "outputs": [
287 | "NodeSocketShader"
288 | ]
289 | },
290 | "ShaderNodeBsdfTranslucent": {
291 | "inputs": [
292 | "NodeSocketFloat",
293 | "NodeSocketColor",
294 | "NodeSocketVector"
295 | ],
296 | "outputs": [
297 | "NodeSocketShader"
298 | ]
299 | },
300 | "ShaderNodeBsdfTransparent": {
301 | "inputs": [
302 | "NodeSocketFloat",
303 | "NodeSocketColor"
304 | ],
305 | "outputs": [
306 | "NodeSocketShader"
307 | ]
308 | },
309 | "ShaderNodeVolumeAbsorption": {
310 | "inputs": [
311 | "NodeSocketFloat",
312 | "NodeSocketColor"
313 | ],
314 | "outputs": [
315 | "NodeSocketShader"
316 | ]
317 | },
318 | "ShaderNodeVolumeScatter": {
319 | "inputs": [
320 | "NodeSocketFloatFactor",
321 | "NodeSocketFloat",
322 | "NodeSocketColor"
323 | ],
324 | "outputs": [
325 | "NodeSocketShader"
326 | ]
327 | },
328 | "ShaderNodeBsdfToon": {
329 | "inputs": [
330 | "NodeSocketFloatFactor",
331 | "NodeSocketFloat",
332 | "NodeSocketColor",
333 | "NodeSocketVector"
334 | ],
335 | "outputs": [
336 | "NodeSocketShader"
337 | ]
338 | },
339 | "ShaderNodeBsdfHair": {
340 | "inputs": [
341 | "NodeSocketFloatFactor",
342 | "NodeSocketVector",
343 | "NodeSocketFloat",
344 | "NodeSocketColor",
345 | "NodeSocketFloatAngle"
346 | ],
347 | "outputs": [
348 | "NodeSocketShader"
349 | ]
350 | },
351 | "ShaderNodeBsdfHairPrincipled": {
352 | "inputs": [
353 | "NodeSocketFloatFactor",
354 | "NodeSocketVector",
355 | "NodeSocketFloat",
356 | "NodeSocketColor",
357 | "NodeSocketFloatAngle"
358 | ],
359 | "outputs": [
360 | "NodeSocketShader"
361 | ]
362 | },
363 | "ShaderNodeEeveeSpecular": {
364 | "inputs": [
365 | "NodeSocketFloatFactor",
366 | "NodeSocketFloat",
367 | "NodeSocketColor",
368 | "NodeSocketVector"
369 | ],
370 | "outputs": [
371 | "NodeSocketShader"
372 | ]
373 | },
374 | "ShaderNodeTexBrick": {
375 | "inputs": [
376 | "NodeSocketFloat",
377 | "NodeSocketColor",
378 | "NodeSocketVector"
379 | ],
380 | "outputs": [
381 | "NodeSocketFloat",
382 | "NodeSocketColor"
383 | ]
384 | },
385 | "ShaderNodeTexChecker": {
386 | "inputs": [
387 | "NodeSocketFloat",
388 | "NodeSocketColor",
389 | "NodeSocketVector"
390 | ],
391 | "outputs": [
392 | "NodeSocketFloat",
393 | "NodeSocketColor"
394 | ]
395 | },
396 | "ShaderNodeTexEnvironment": {
397 | "inputs": [
398 | "NodeSocketVector"
399 | ],
400 | "outputs": [
401 | "NodeSocketColor"
402 | ]
403 | },
404 | "ShaderNodeTexGradient": {
405 | "inputs": [
406 | "NodeSocketVector"
407 | ],
408 | "outputs": [
409 | "NodeSocketFloat",
410 | "NodeSocketColor"
411 | ]
412 | },
413 | "ShaderNodeTexImage": {
414 | "inputs": [
415 | "NodeSocketVector"
416 | ],
417 | "outputs": [
418 | "NodeSocketFloat",
419 | "NodeSocketColor"
420 | ]
421 | },
422 | "ShaderNodeTexMagic": {
423 | "inputs": [
424 | "NodeSocketFloat",
425 | "NodeSocketVector"
426 | ],
427 | "outputs": [
428 | "NodeSocketFloat",
429 | "NodeSocketColor"
430 | ]
431 | },
432 | "ShaderNodeTexNoise": {
433 | "inputs": [
434 | "NodeSocketFloatFactor",
435 | "NodeSocketFloat",
436 | "NodeSocketVector"
437 | ],
438 | "outputs": [
439 | "NodeSocketFloat",
440 | "NodeSocketColor"
441 | ]
442 | },
443 | "ShaderNodeTexSky": {
444 | "inputs": [
445 | "NodeSocketVector"
446 | ],
447 | "outputs": [
448 | "NodeSocketColor"
449 | ]
450 | },
451 | "ShaderNodeTexVoronoi": {
452 | "inputs": [
453 | "NodeSocketFloatFactor",
454 | "NodeSocketFloat",
455 | "NodeSocketVector"
456 | ],
457 | "outputs": [
458 | "NodeSocketFloat",
459 | "NodeSocketColor",
460 | "NodeSocketVector"
461 | ]
462 | },
463 | "ShaderNodeTexWave": {
464 | "inputs": [
465 | "NodeSocketFloatFactor",
466 | "NodeSocketFloat",
467 | "NodeSocketVector"
468 | ],
469 | "outputs": [
470 | "NodeSocketFloat",
471 | "NodeSocketColor"
472 | ]
473 | },
474 | "ShaderNodeTexWhiteNoise": {
475 | "inputs": [
476 | "NodeSocketFloat",
477 | "NodeSocketVector"
478 | ],
479 | "outputs": [
480 | "NodeSocketFloat",
481 | "NodeSocketColor"
482 | ]
483 | },
484 | "ShaderNodeTexIES": {
485 | "inputs": [
486 | "NodeSocketFloat",
487 | "NodeSocketVector"
488 | ],
489 | "outputs": [
490 | "NodeSocketFloat"
491 | ]
492 | },
493 | "ShaderNodeTexPointDensity": {
494 | "inputs": [
495 | "NodeSocketVector"
496 | ],
497 | "outputs": [
498 | "NodeSocketFloat",
499 | "NodeSocketColor"
500 | ]
501 | },
502 | "ShaderNodeBrightContrast": {
503 | "inputs": [
504 | "NodeSocketFloat",
505 | "NodeSocketColor"
506 | ],
507 | "outputs": [
508 | "NodeSocketColor"
509 | ]
510 | },
511 | "ShaderNodeGamma": {
512 | "inputs": [
513 | "NodeSocketFloatUnsigned",
514 | "NodeSocketColor"
515 | ],
516 | "outputs": [
517 | "NodeSocketColor"
518 | ]
519 | },
520 | "ShaderNodeHueSaturation": {
521 | "inputs": [
522 | "NodeSocketFloatFactor",
523 | "NodeSocketFloat",
524 | "NodeSocketColor"
525 | ],
526 | "outputs": [
527 | "NodeSocketColor"
528 | ]
529 | },
530 | "ShaderNodeInvert": {
531 | "inputs": [
532 | "NodeSocketFloatFactor",
533 | "NodeSocketColor"
534 | ],
535 | "outputs": [
536 | "NodeSocketColor"
537 | ]
538 | },
539 | "ShaderNodeMix": {
540 | "inputs": [
541 | "NodeSocketFloatFactor",
542 | "NodeSocketRotation",
543 | "NodeSocketVector",
544 | "NodeSocketFloat",
545 | "NodeSocketColor"
546 | ],
547 | "outputs": [
548 | "NodeSocketRotation",
549 | "NodeSocketFloat",
550 | "NodeSocketColor",
551 | "NodeSocketVector"
552 | ]
553 | },
554 | "ShaderNodeRGBCurve": {
555 | "inputs": [
556 | "NodeSocketFloatFactor",
557 | "NodeSocketColor"
558 | ],
559 | "outputs": [
560 | "NodeSocketColor"
561 | ]
562 | },
563 | "ShaderNodeLightFalloff": {
564 | "inputs": [
565 | "NodeSocketFloat"
566 | ],
567 | "outputs": [
568 | "NodeSocketFloat"
569 | ]
570 | },
571 | "ShaderNodeBump": {
572 | "inputs": [
573 | "NodeSocketFloatFactor",
574 | "NodeSocketFloat",
575 | "NodeSocketVector"
576 | ],
577 | "outputs": [
578 | "NodeSocketVector"
579 | ]
580 | },
581 | "ShaderNodeDisplacement": {
582 | "inputs": [
583 | "NodeSocketFloat",
584 | "NodeSocketVector"
585 | ],
586 | "outputs": [
587 | "NodeSocketVector"
588 | ]
589 | },
590 | "ShaderNodeMapping": {
591 | "inputs": [
592 | "NodeSocketVectorXYZ",
593 | "NodeSocketVectorEuler",
594 | "NodeSocketVectorTranslation",
595 | "NodeSocketVector"
596 | ],
597 | "outputs": [
598 | "NodeSocketVector"
599 | ]
600 | },
601 | "ShaderNodeNormal": {
602 | "inputs": [
603 | "NodeSocketVectorDirection"
604 | ],
605 | "outputs": [
606 | "NodeSocketFloat",
607 | "NodeSocketVectorDirection"
608 | ]
609 | },
610 | "ShaderNodeNormalMap": {
611 | "inputs": [
612 | "NodeSocketFloat",
613 | "NodeSocketColor"
614 | ],
615 | "outputs": [
616 | "NodeSocketVector"
617 | ]
618 | },
619 | "ShaderNodeVectorCurve": {
620 | "inputs": [
621 | "NodeSocketFloatFactor",
622 | "NodeSocketVector"
623 | ],
624 | "outputs": [
625 | "NodeSocketVector"
626 | ]
627 | },
628 | "ShaderNodeVectorDisplacement": {
629 | "inputs": [
630 | "NodeSocketFloat",
631 | "NodeSocketColor"
632 | ],
633 | "outputs": [
634 | "NodeSocketVector"
635 | ]
636 | },
637 | "ShaderNodeVectorRotate": {
638 | "inputs": [
639 | "NodeSocketVectorEuler",
640 | "NodeSocketFloatAngle",
641 | "NodeSocketVector"
642 | ],
643 | "outputs": [
644 | "NodeSocketVector"
645 | ]
646 | },
647 | "ShaderNodeVectorTransform": {
648 | "inputs": [
649 | "NodeSocketVector"
650 | ],
651 | "outputs": [
652 | "NodeSocketVector"
653 | ]
654 | },
655 | "ShaderNodeVectorMath": {
656 | "inputs": [
657 | "NodeSocketFloat",
658 | "NodeSocketVector"
659 | ],
660 | "outputs": [
661 | "NodeSocketFloat",
662 | "NodeSocketVector"
663 | ]
664 | },
665 | "ShaderNodeBlackbody": {
666 | "inputs": [
667 | "NodeSocketFloat"
668 | ],
669 | "outputs": [
670 | "NodeSocketColor"
671 | ]
672 | },
673 | "ShaderNodeClamp": {
674 | "inputs": [
675 | "NodeSocketFloat"
676 | ],
677 | "outputs": [
678 | "NodeSocketFloat"
679 | ]
680 | },
681 | "ShaderNodeValToRGB": {
682 | "inputs": [
683 | "NodeSocketFloatFactor"
684 | ],
685 | "outputs": [
686 | "NodeSocketFloat",
687 | "NodeSocketColor"
688 | ]
689 | },
690 | "ShaderNodeCombineColor": {
691 | "inputs": [
692 | "NodeSocketFloatFactor"
693 | ],
694 | "outputs": [
695 | "NodeSocketColor"
696 | ]
697 | },
698 | "ShaderNodeCombineXYZ": {
699 | "inputs": [
700 | "NodeSocketFloat"
701 | ],
702 | "outputs": [
703 | "NodeSocketVector"
704 | ]
705 | },
706 | "ShaderNodeFloatCurve": {
707 | "inputs": [
708 | "NodeSocketFloatFactor",
709 | "NodeSocketFloat"
710 | ],
711 | "outputs": [
712 | "NodeSocketFloat"
713 | ]
714 | },
715 | "ShaderNodeMapRange": {
716 | "inputs": [
717 | "NodeSocketFloat",
718 | "NodeSocketVector"
719 | ],
720 | "outputs": [
721 | "NodeSocketFloat",
722 | "NodeSocketVector"
723 | ]
724 | },
725 | "ShaderNodeMath": {
726 | "inputs": [
727 | "NodeSocketFloat"
728 | ],
729 | "outputs": [
730 | "NodeSocketFloat"
731 | ]
732 | },
733 | "ShaderNodeRGBToBW": {
734 | "inputs": [
735 | "NodeSocketColor"
736 | ],
737 | "outputs": [
738 | "NodeSocketFloat"
739 | ]
740 | },
741 | "ShaderNodeSeparateColor": {
742 | "inputs": [
743 | "NodeSocketColor"
744 | ],
745 | "outputs": [
746 | "NodeSocketFloat"
747 | ]
748 | },
749 | "ShaderNodeSeparateXYZ": {
750 | "inputs": [
751 | "NodeSocketVector"
752 | ],
753 | "outputs": [
754 | "NodeSocketFloat"
755 | ]
756 | },
757 | "ShaderNodeWavelength": {
758 | "inputs": [
759 | "NodeSocketFloat"
760 | ],
761 | "outputs": [
762 | "NodeSocketColor"
763 | ]
764 | },
765 | "ShaderNodeShaderToRGB": {
766 | "inputs": [
767 | "NodeSocketShader"
768 | ],
769 | "outputs": [
770 | "NodeSocketFloat",
771 | "NodeSocketColor"
772 | ]
773 | },
774 | "ShaderNodeScript": {
775 | "inputs": [],
776 | "outputs": []
777 | },
778 | "NodeFrame": {
779 | "inputs": [],
780 | "outputs": []
781 | },
782 | "NodeReroute": {
783 | "inputs": [
784 | "NodeSocketColor"
785 | ],
786 | "outputs": [
787 | "NodeSocketColor"
788 | ]
789 | }
790 | }
791 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/sockets/ShaderNodeTree_sockets_4_2.jsonc:
--------------------------------------------------------------------------------
1 | // This is a list of the socket types of all nodes
2 | // used for telling whether a node is valid during link drag.
3 | // It contains all of the new and updated nodes in this blender version.
4 | // It can be auto generated using the 'generate socket types file' operator
5 | {
6 | "bl_version": [
7 | 4,
8 | 2
9 | ],
10 | "nodes": {
11 | "ShaderNodeBsdfPrincipled": {
12 | "inputs": [
13 | "NodeSocketFloatWavelength",
14 | "NodeSocketFloatDistance",
15 | "NodeSocketFloatFactor",
16 | "NodeSocketVector",
17 | "NodeSocketColor",
18 | "NodeSocketFloat"
19 | ],
20 | "outputs": [
21 | "NodeSocketShader"
22 | ]
23 | },
24 | "ShaderNodeWavelength": {
25 | "inputs": [
26 | "NodeSocketFloatWavelength"
27 | ],
28 | "outputs": [
29 | "NodeSocketColor"
30 | ]
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/sockets/ShaderNodeTree_sockets_4_3.jsonc:
--------------------------------------------------------------------------------
1 | // This is a list of the socket types of all nodes
2 | // used for telling whether a node is valid during link drag.
3 | // It contains all of the new and updated nodes in this blender version.
4 | // It can be auto generated using the 'generate socket types file' operator
5 | {
6 | "bl_version": [
7 | 4,
8 | 3
9 | ],
10 | "nodes": {
11 | "ShaderNodeBackground": {
12 | "inputs": [
13 | "NodeSocketFloat",
14 | "NodeSocketColor"
15 | ],
16 | "outputs": [
17 | "NodeSocketShader"
18 | ]
19 | },
20 | "ShaderNodeUVAlongStroke": {
21 | "inputs": [],
22 | "outputs": [
23 | "NodeSocketVector"
24 | ]
25 | },
26 | "ShaderNodeBsdfMetallic": {
27 | "inputs": [
28 | "NodeSocketFloatFactor",
29 | "NodeSocketFloat",
30 | "NodeSocketVector",
31 | "NodeSocketColor"
32 | ],
33 | "outputs": [
34 | "NodeSocketShader"
35 | ]
36 | },
37 | "ShaderNodeVolumePrincipled": {
38 | "inputs": [
39 | "NodeSocketString",
40 | "NodeSocketColor",
41 | "NodeSocketFloatFactor",
42 | "NodeSocketFloat",
43 | "NodeSocketFloatColorTemperature"
44 | ],
45 | "outputs": [
46 | "NodeSocketShader"
47 | ]
48 | },
49 | "ShaderNodeBsdfSheen": {
50 | "inputs": [
51 | "NodeSocketFloatFactor",
52 | "NodeSocketFloat",
53 | "NodeSocketVector",
54 | "NodeSocketColor"
55 | ],
56 | "outputs": [
57 | "NodeSocketShader"
58 | ]
59 | },
60 | "ShaderNodeBsdfRayPortal": {
61 | "inputs": [
62 | "NodeSocketFloat",
63 | "NodeSocketVector",
64 | "NodeSocketColor"
65 | ],
66 | "outputs": [
67 | "NodeSocketShader"
68 | ]
69 | },
70 | "ShaderNodeTexGabor": {
71 | "inputs": [
72 | "NodeSocketVectorDirection",
73 | "NodeSocketFloatFactor",
74 | "NodeSocketFloat",
75 | "NodeSocketFloatAngle",
76 | "NodeSocketVector"
77 | ],
78 | "outputs": [
79 | "NodeSocketFloat"
80 | ]
81 | },
82 | "ShaderNodeGamma": {
83 | "inputs": [
84 | "NodeSocketFloat",
85 | "NodeSocketColor"
86 | ],
87 | "outputs": [
88 | "NodeSocketColor"
89 | ]
90 | },
91 | "ShaderNodeBlackbody": {
92 | "inputs": [
93 | "NodeSocketFloatColorTemperature"
94 | ],
95 | "outputs": [
96 | "NodeSocketColor"
97 | ]
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/node_pie/node_def_files/user/ScriptingNodesTree.jsonc:
--------------------------------------------------------------------------------
1 | // For more info about how to use this file, use the "Open Example File" operator in the right click menu of the node editor
2 | // You'll need Node Pie developer extras to be enabled in the preferences to see it.
3 |
4 | // The config for the Serpens node editor, big thanks to @ADRs on Blender Artists for making it :)
5 |
6 | {
7 | "layout": {
8 | "left": [
9 | ["INPUT"],
10 | ["INPUT_II", "SNIPPETS", "LAYOUT"],
11 | ["INTERFACE", "DEBUG"]
12 | ],
13 | "right": [
14 | ["PROGRAM", "PROPERTIES"],
15 | ["PROGRAM_II", "VARIABLES"],
16 | ["UTILITIES"]
17 | ],
18 | "top": [["EVENTS"]],
19 | "bottom": [["PYTHON"]]
20 | },
21 | "categories": {
22 | "INPUT": {
23 | "label": "Input",
24 | "color": "input",
25 | "nodes": [
26 | { "identifier": "SN_AddonInfoNode" },
27 | { "identifier": "SN_AssetNode" },
28 | { "identifier": "SN_GetEditSelectNode" },
29 | { "identifier": "SN_IconNode" },
30 | { "identifier": "SN_InModeNode" },
31 | { "identifier": "SN_IsExportNode" },
32 | { "identifier": "SN_IsObjectType" },
33 | { "identifier": "SN_NamedIconNode" },
34 | { "identifier": "SN_NodeIdnameNode" },
35 | { "identifier": "SN_NodeIsIdname" },
36 | { "identifier": "SN_RandomColorNode" },
37 | { "identifier": "SN_RandomNumberNode" },
38 | { "identifier": "SN_SceneContextNode" },
39 | { "identifier": "SN_TimeNode" },
40 | { "separator": true, "label": "Data" },
41 | { "identifier": "SN_BooleanNode" },
42 | { "identifier": "SN_BooleanVectorNode" },
43 | { "identifier": "SN_ColorNode" },
44 | { "identifier": "SN_FloatNode" },
45 | { "identifier": "SN_FloatVectorNode" },
46 | { "identifier": "SN_IntegerNode" },
47 | { "identifier": "SN_IntegerVectorNode" },
48 | { "identifier": "SN_ListNode" },
49 | { "identifier": "SN_NoneNode" },
50 | { "identifier": "SN_StringNode" },
51 | { "separator": true, "label": "Geometry" },
52 | { "identifier": "SN_BMeshEdgeDataNode" },
53 | { "identifier": "SN_BMeshFaceDataNode" },
54 | { "identifier": "SN_FacesToVertexLocationsNode" },
55 | { "identifier": "SN_BMeshDataNode" },
56 | { "identifier": "SN_BMeshVertexDataNode" },
57 | { "identifier": "SN_CreateLineLocationsNode" },
58 | { "identifier": "SN_CreateQuadLocationsNode" },
59 | { "identifier": "SN_CreateTriangleLocationsNode" },
60 | { "identifier": "SN_NgonToTriangleLocationsNode" },
61 | { "identifier": "SN_ObjectToBMeshNode" },
62 | { "identifier": "SN_TriangulateBmeshNode" }
63 | ]
64 | },
65 | "INPUT_II": {
66 | "label": "Areas",
67 | "color": "input",
68 | "nodes": [
69 | // { "separator": true, "label": "Areas" },
70 | { "identifier": "SN_2DViewZoomNode" },
71 | { "identifier": "SN_AreaByTypeNode" },
72 | { "identifier": "SN_AreaLocationsNode" },
73 | { "separator": true, "label": "Blend Data" },
74 | { "identifier": "SN_ActionsBlendDataNode" },
75 | { "identifier": "SN_ArmaturesBlendDataNode" },
76 | { "identifier": "SN_BlenderDataNode" },
77 | { "identifier": "SN_BrushesBlendDataNode" },
78 | { "identifier": "SN_CamerasBlendDataNode" },
79 | { "identifier": "SN_CollectionsBlendDataNode" },
80 | { "identifier": "SN_CurvesBlendDataNode" },
81 | { "identifier": "SN_FontsBlendDataNode" },
82 | { "identifier": "SN_GreasePencilsBlendDataNode" },
83 | { "identifier": "SN_ImagesBlendDataNode" },
84 | { "identifier": "SN_LatticesBlendDataNode" },
85 | { "identifier": "SN_LightsBlendDataNode" },
86 | { "identifier": "SN_MaterialsBlendDataNode" },
87 | { "identifier": "SN_MeshBlendDataNode" },
88 | { "identifier": "SN_MetaballsBlendDataNode" },
89 | { "identifier": "SN_NodeGroupsBlendDataNode" },
90 | { "identifier": "SN_ObjectBlendDataNode" },
91 | { "identifier": "SN_ScenesBlendDataNode" },
92 | { "identifier": "SN_ScreensBlendDataNode" },
93 | { "identifier": "SN_ShapeKeysBlendDataNode" },
94 | { "identifier": "SN_TextsBlendDataNode" },
95 | { "identifier": "SN_TexturesBlendDataNode" },
96 | { "identifier": "SN_VolumesBlendDataNode" },
97 | { "identifier": "SN_WindowManagersBlendDataNode" },
98 | { "identifier": "SN_WorkspacesBlendDataNode" },
99 | { "identifier": "SN_WorldsBlendDataNode" }
100 | ]
101 | },
102 | "INTERFACE": {
103 | "label": "Interface",
104 | "color": "vector",
105 | "nodes": [
106 | { "identifier": "SN_ButtonNodeNew" },
107 | { "identifier": "SN_DisplayCollectionListNodeNew" },
108 | { "identifier": "SN_DisplayEnumItemNode" },
109 | { "identifier": "SN_DisplayIconNodeNew" },
110 | { "identifier": "SN_DisplayPreviewNodeNew" },
111 | { "identifier": "SN_DisplayPropertyNodeNew" },
112 | { "identifier": "SN_DisplaySearchNodeNew" },
113 | { "identifier": "SN_DisplaySerpensShortcutNodeNew" },
114 | { "identifier": "SN_EnumMapInterfaceNode" },
115 | { "identifier": "SN_IconGalleryNodeNew" },
116 | { "identifier": "SN_LabelNodeNew" },
117 | { "identifier": "SN_SeparatorNodeNew" },
118 | { "separator": true, "label": "Layout" },
119 | { "identifier": "SN_AddToMenuNodeNew" },
120 | { "identifier": "SN_AddToPanelNodeNew" },
121 | { "identifier": "SN_InterfaceFunctionNode" },
122 | { "identifier": "SN_RunInterfaceFunctionNodeNew" },
123 | { "identifier": "SN_MenuNode" },
124 | { "identifier": "SN_PanelNode" },
125 | { "identifier": "SN_PieMenuNode" },
126 | { "identifier": "SN_PreferencesNode" },
127 | { "separator": true, "label": "SubLayout" },
128 | { "identifier": "SN_LayoutBoxNodeNew" },
129 | { "identifier": "SN_LayoutColumnNodeNew" },
130 | { "identifier": "SN_CopyMenuNodeNew" },
131 | { "identifier": "SN_CopyPanelNodeNew" },
132 | { "identifier": "SN_LayoutGridNode" },
133 | { "identifier": "SN_IfElseInterfaceNodeNew" },
134 | { "identifier": "SN_ForInterfaceNodeNew" },
135 | { "identifier": "SN_RepeatInterfaceNodeNew" },
136 | { "identifier": "SN_PopoverNodeNew" },
137 | { "identifier": "SN_LayoutRowNodeNew" },
138 | { "identifier": "SN_LayoutSplitNodeNew" },
139 | { "identifier": "SN_SubmenuNodeNew" }
140 | ]
141 | },
142 | "PROGRAM": {
143 | "label": "Program",
144 | "color": "geometry",
145 | "nodes": [
146 | { "identifier": "SN_FunctionNode" },
147 | { "identifier": "SN_FunctionReturnNode" },
148 | { "identifier": "SN_RunFunctionNode" },
149 | { "identifier": "SN_OperatorNode" },
150 | { "identifier": "SN_RunOperatorNode" },
151 | { "separator": true, "label": "Drawing" },
152 | { "identifier": "SN_DrawCircleNode" },
153 | { "identifier": "SN_DrawLineNode" },
154 | { "identifier": "SN_DrawPointNode" },
155 | { "identifier": "SN_DrawQuadNode" },
156 | { "identifier": "SN_DrawModalTextNode" },
157 | { "identifier": "SN_DrawTriangleNode" },
158 | { "identifier": "SN_EndDrawingNode" },
159 | { "identifier": "SN_StartDrawingNode" },
160 | { "separator": true, "label": "Files" },
161 | { "identifier": "SN_JoinPathNode" },
162 | { "identifier": "SN_ListBlendContentNode" },
163 | { "identifier": "SN_ListDirectoryFilesNode" },
164 | { "identifier": "SN_AbsolutePathNode" },
165 | { "identifier": "SN_PathInfoNode" }
166 | ]
167 | },
168 | "PROGRAM_II": {
169 | "label": "Modal",
170 | "color": "geometry",
171 | "nodes": [
172 | //{ "separator": true, "label": "Modal" },
173 | { "identifier": "SN_ModalEventNode" },
174 | { "identifier": "SN_ModalOperatorNode" },
175 | { "identifier": "SN_ModalShortcutPressedNode" },
176 | { "identifier": "SN_ModalViewportMovedNode" },
177 | { "identifier": "SN_ReturnModalNode" },
178 | { "identifier": "SN_SetModalCursorNode" },
179 | { "identifier": "SN_TextSizeNode" },
180 | { "separator": true, "label": "Operations" },
181 | { "identifier": "SN_BreakNode" },
182 | { "identifier": "SN_EnumMapNode" },
183 | { "identifier": "SN_FindShortcutOperator" },
184 | { "identifier": "SN_IfElseExecuteNode" },
185 | { "identifier": "SN_ForExecuteNode" },
186 | { "identifier": "SN_RepeatExecuteNode" },
187 | { "identifier": "SN_OpenMenuNode" },
188 | { "identifier": "SN_OpenPanelNode" },
189 | { "identifier": "SN_OpenPieMenuNode" },
190 | { "identifier": "SN_OverrideContextNode" },
191 | { "identifier": "SN_RefreshViewNode" },
192 | { "identifier": "SN_ReportNode" },
193 | { "identifier": "SN_SetEditSelectNode" },
194 | { "identifier": "SN_SetHeaderTextNode" },
195 | { "identifier": "SN_SetStatusTextNode" }
196 | ]
197 | },
198 | "PROGRAM_TIMER": {
199 | "label": "Timer",
200 | "color": "geometry",
201 | "nodes": [
202 | { "identifier": "SN_RunInIntervalsNode" },
203 | { "identifier": "SN_RunMultipleTimesNode" },
204 | { "identifier": "SN_RunWithDelayNode" }
205 | ]
206 | },
207 | "PROPERTIES": {
208 | "label": "Properties",
209 | "color": "converter",
210 | "nodes": [
211 | { "identifier": "SN_BlenderPropertyNode" },
212 | { "identifier": "SN_OnPropertyUpdateNode" },
213 | { "identifier": "SN_PropertyExistsNode" },
214 | { "identifier": "SN_RunPropertyFunctionNode" },
215 | { "identifier": "SN_SerpensPropertyNode" },
216 | { "identifier": "SN_SetPropertyNode" },
217 | { "separator": true, "label": "Collection" },
218 | { "identifier": "SN_AddCollectionItemNode" },
219 | { "identifier": "SN_CollectionLengthNode" },
220 | { "identifier": "SN_IndexCollectionPropertyNode" },
221 | { "identifier": "SN_IsIndexInCollectionPropertyNode" },
222 | { "identifier": "SN_MoveCollectionItemNode" },
223 | { "identifier": "SN_RemoveCollectionItemNode" },
224 | { "separator": true, "label": "Custom" },
225 | { "identifier": "SN_GetCustomPropertyNode" },
226 | { "identifier": "SN_HasCustomPropertyNode" },
227 | { "identifier": "SN_SetCustomPropertyNode" },
228 | { "separator": true, "label": "Enum" },
229 | { "identifier": "SN_GenerateEnumItemsNode" },
230 | { "identifier": "SN_MakeEnumItemNode" }
231 | ]
232 | },
233 | "PYTHON": {
234 | "label": "Python",
235 | "color": "texture",
236 | "nodes": [
237 | { "identifier": "SN_GetDataScriptlineNode" },
238 | { "identifier": "SN_GetPropertyScriptlineNode" },
239 | { "identifier": "SN_InterfaceScriptNode" },
240 | { "identifier": "SN_InterfaceScriptlineNode" },
241 | { "identifier": "SN_RunScriptNode" },
242 | { "identifier": "SN_ScriptNode" },
243 | { "identifier": "SN_ScriptlineNode" },
244 | { "separator": true, "label": "Attributes" },
245 | { "identifier": "SN_GetAttributeNode" },
246 | { "identifier": "SN_HasAttributeNode" },
247 | { "identifier": "SN_SetAttributeNode" }
248 | ]
249 | },
250 | "UTILITIES": {
251 | "label": "Utilities",
252 | "color": "group",
253 | "nodes": [
254 | { "identifier": "SN_CompareNode" },
255 | { "identifier": "SN_SwitchDataNode" },
256 | { "identifier": "SN_SwitchIconNode" },
257 | { "separator": true, "label": "Boolean" },
258 | { "identifier": "SN_BooleanMathNode" },
259 | { "identifier": "SN_InvertBooleanNode" },
260 | { "separator": true, "label": "Converter" },
261 | { "identifier": "SN_3DLocationTo2DNode" },
262 | { "identifier": "SN_CombineVectorNode" },
263 | { "identifier": "SN_RadiansNode" },
264 | { "identifier": "SN_DataToIconNode" },
265 | { "identifier": "SN_DefineDataType" },
266 | { "identifier": "SN_EnumSetToListNode" },
267 | { "identifier": "SN_RegionToViewNode" },
268 | { "identifier": "SN_SplitVectorNode" },
269 | { "identifier": "SN_ViewToRegionNode" },
270 | { "separator": true, "label": "Files" },
271 | { "identifier": "SN_JoinPathNode" },
272 | { "identifier": "SN_ListBlendContentNode" },
273 | { "identifier": "SN_ListDirectoryFilesNode" },
274 | { "identifier": "SN_AbsolutePathNode" },
275 | { "identifier": "SN_PathInfoNode" },
276 | { "separator": true, "label": "Math" },
277 | { "identifier": "SN_ClampNode" },
278 | { "identifier": "SN_MathNode" },
279 | { "identifier": "SN_RoundNode" },
280 | { "identifier": "SN_VectorMathNode" },
281 | { "separator": true, "label": "Text" },
282 | { "identifier": "SN_CombineStringsNode" },
283 | { "identifier": "SN_DecodeStringNode" },
284 | { "identifier": "SN_EncodeStringNode" },
285 | { "identifier": "SN_JoinStringsNode" },
286 | { "identifier": "SN_MapStringsNode" },
287 | { "identifier": "SN_PadStringNode" },
288 | { "identifier": "SN_ReplaceStringNode" },
289 | { "identifier": "SN_SliceStringNode" },
290 | { "identifier": "SN_SplitStringNode" },
291 | { "identifier": "SN_StringLengthNode" },
292 | { "identifier": "SN_StripStringNode" },
293 | { "identifier": "SN_IsInStringNode" }
294 | ]
295 | },
296 | "VARIABLES": {
297 | "label": "Variables",
298 | "color": "attribute",
299 | "nodes": [
300 | { "identifier": "SN_ChangeVariableByNode" },
301 | { "identifier": "SN_GetVariableNode" },
302 | { "identifier": "SN_ResetVariableNode" },
303 | { "identifier": "SN_SetVariableNode" },
304 | { "identifier": "SN_ToggleVariableNode" },
305 | { "separator": true, "label": "List" },
306 | { "identifier": "SN_AddToListNode" },
307 | { "identifier": "SN_IsInListNode" },
308 | { "identifier": "SN_IndexListNode" },
309 | { "identifier": "SN_IndexOfElementNode" },
310 | { "identifier": "SN_ListLengthNode" },
311 | { "identifier": "SN_RemoveFromListNode" },
312 | { "identifier": "SN_SortListNode" }
313 | ]
314 | },
315 | "EVENTS": {
316 | "label": "Events",
317 | "color": "color",
318 | "nodes": [
319 | { "identifier": "SN_BeforeExitNode" },
320 | { "identifier": "SN_DepsgraphUpdateNode" },
321 | { "identifier": "SN_ChangeFrameNode" },
322 | { "identifier": "SN_OnKeypressNode" },
323 | { "identifier": "SN_OnLoadNode" },
324 | { "identifier": "SN_RedoEventNode" },
325 | { "identifier": "SN_AfterRenderNode" },
326 | { "identifier": "SN_BeforeRenderNode" },
327 | { "identifier": "SN_OnSaveNode" },
328 | { "identifier": "SN_UndoEventNode" }
329 | ]
330 | },
331 | "DEBUG": {
332 | "label": "Debug",
333 | "color": "output",
334 | "nodes": [
335 | { "identifier": "SN_PrintNode" },
336 | { "identifier": "SN_SleepNode" },
337 | { "identifier": "SN_TimestampNode" },
338 | { "identifier": "SN_TriggerNode" }
339 | ]
340 | },
341 | "SNIPPETS": {
342 | "label": "Snippets",
343 | "color": "script",
344 | "nodes": [{ "identifier": "SN_SnippetNode" }]
345 | },
346 | "LAYOUT": {
347 | "label": "Layout",
348 | "color": "layout",
349 | "nodes": [
350 | { "identifier": "NodeFrame" },
351 | { "identifier": "SN_PortalNode" },
352 | { "identifier": "NodeReroute" }
353 | ]
354 | }
355 | }
356 | }
357 |
--------------------------------------------------------------------------------
/node_pie/npie_constants.py:
--------------------------------------------------------------------------------
1 | import platform
2 | from pathlib import Path
3 |
4 | import bpy
5 |
6 | IS_4_0 = bpy.app.version >= (4, 0, 0)
7 | IS_MACOS = platform.system() == "Darwin"
8 |
9 | POPULARITY_FILE = Path(__file__).parent / "nodes.json"
10 | if not POPULARITY_FILE.exists():
11 | with open(POPULARITY_FILE, "w") as f:
12 | pass
13 | POPULARITY_FILE_VERSION = (0, 0, 1)
14 |
15 | NODE_DEF_EXAMPLE_PREFIX = "node_def_"
16 | NODE_DEF_DIR = Path(__file__).parent / "node_def_files"
17 | NODE_DEF_BUILTIN = NODE_DEF_DIR / "builtin"
18 | NODE_DEF_USER = NODE_DEF_DIR / "user"
19 | NODE_DEF_BASE_FILE = NODE_DEF_DIR / "node_def_base.jsonc"
20 | NODE_DEF_EXAMPLE_FILE = NODE_DEF_DIR / "node_def_example.jsonc"
21 | NODE_DEF_SOCKETS = NODE_DEF_DIR / "sockets"
22 |
23 | SHADERS_DIR = Path(__file__).parent / "shaders"
24 |
--------------------------------------------------------------------------------
/node_pie/npie_drawing.py:
--------------------------------------------------------------------------------
1 | import gpu
2 | from gpu.types import GPUBatch, GPUShader, GPUShaderCreateInfo, GPUStageInterfaceInfo
3 | from gpu_extras.batch import batch_for_shader
4 | from mathutils import Vector as V
5 |
6 | from .npie_constants import IS_4_0, IS_MACOS, SHADERS_DIR
7 |
8 | # Create the the antialiased line shader
9 | # This is not trivial...
10 | # And it doesn't work on Apple Silicone because of course it doesn't
11 |
12 | if IS_MACOS:
13 | name = "UNIFORM_COLOR" if IS_4_0 else "2D_UNIFORM_COLOR"
14 | line_shader: GPUShader = gpu.shader.from_builtin(name)
15 |
16 | def draw_line(from_pos: V, to_pos: V, color: V, width=1):
17 | """Draw a rubbish normal line"""
18 |
19 | batch: GPUBatch = batch_for_shader(line_shader, "LINES", {"pos": [from_pos, to_pos]})
20 | line_shader.bind()
21 | gpu.state.blend_set("ALPHA")
22 | gpu.state.line_width_set(width * 3)
23 | line_shader.uniform_float("color", color)
24 | batch.draw(line_shader)
25 |
26 | else:
27 | vert_code = (SHADERS_DIR / "2D_vert.glsl").read_text()
28 | frag_code = (SHADERS_DIR / "2D_line_antialiased_frag.glsl").read_text()
29 |
30 | # Create a gpu shader from scratch
31 | line_shader_info = GPUShaderCreateInfo()
32 | line_shader_info.define("is_compile", "true")
33 | line_shader_info.push_constant("MAT4", "ModelViewProjectionMatrix")
34 | line_shader_info.push_constant("VEC4", "color")
35 | line_shader_info.vertex_in(0, "VEC2", "pos")
36 | line_shader_info.vertex_in(1, "VEC2", "uvs")
37 |
38 | line_shader_interface = GPUStageInterfaceInfo("line_shader")
39 | line_shader_interface.smooth("VEC2", "frag_uvs")
40 | line_shader_info.vertex_out(line_shader_interface)
41 |
42 | line_shader_info.fragment_out(0, "VEC4", "fragColor")
43 |
44 | line_shader_info.vertex_source(vert_code)
45 | line_shader_info.fragment_source(frag_code)
46 |
47 | line_shader = gpu.shader.create_from_info(line_shader_info)
48 |
49 | def draw_line(from_pos: V, to_pos: V, color: V, width=1):
50 | """Draw a beautiful antialiased line"""
51 |
52 | # Calculate the tangent
53 | normal = V(to_pos - from_pos)
54 | normal.normalize()
55 | tangent = V((normal.y, -normal.x))
56 | tangent *= width * 1.9
57 |
58 | # The four corners are the ends shifted along the tangent of the line
59 | coords = [from_pos + tangent, from_pos - tangent, to_pos + tangent, to_pos - tangent]
60 | indices = [(0, 1, 2), (1, 2, 3)]
61 | uvs = [(0, 0), (0, 1), (1, 0), (1, 1)]
62 |
63 | batch: GPUBatch = batch_for_shader(line_shader, "TRIS", {"pos": coords, "uvs": uvs}, indices=indices)
64 | line_shader.bind()
65 |
66 | gpu.state.blend_set("ALPHA")
67 | line_shader.uniform_float("color", color)
68 | batch.draw(line_shader)
69 |
--------------------------------------------------------------------------------
/node_pie/npie_helpers.py:
--------------------------------------------------------------------------------
1 | import json
2 | import re
3 | from typing import TYPE_CHECKING
4 |
5 | from bpy.types import AddonPreferences, Context, Node, NodeTree
6 | from mathutils import Vector as V
7 |
8 | from .. import __package__ as base_package
9 | from .npie_constants import NODE_DEF_DIR, NODE_DEF_EXAMPLE_PREFIX
10 |
11 | if TYPE_CHECKING:
12 | from .npie_prefs import NodePiePrefs
13 | else:
14 | NodePiePrefs = AddonPreferences
15 |
16 |
17 | def get_node_location(node: Node) -> V:
18 | """Get the actual location of a node, taking parent frame nodes into account"""
19 | location = node.location.copy()
20 | while node.parent:
21 | node = node.parent
22 | location += node.location
23 | return location
24 |
25 |
26 | def get_node_tree(context: Context) -> NodeTree:
27 | """Get the node tree currently being edited"""
28 | return context.space_data.edit_tree
29 |
30 |
31 | def get_prefs(context) -> NodePiePrefs:
32 | """Return the addon preferences"""
33 | return context.preferences.addons[base_package].preferences
34 |
35 |
36 | def lerp(fac, a, b) -> float:
37 | """Linear interpolation (mix) between two values"""
38 | return (fac * b) + ((1 - fac) * a)
39 |
40 |
41 | def inv_lerp(fac, a, b) -> float:
42 | """Inverse Linar Interpolation, get the fraction between a and b on which fac resides."""
43 | return (fac - a) / (b - a)
44 |
45 |
46 | def vec_divide(a, b) -> V:
47 | """Elementwise divide for two vectors"""
48 | return V(e1 / e2 if e2 != 0 else 0 for e1, e2 in zip(a, b))
49 |
50 |
51 | def vec_min(a, b) -> V:
52 | """Elementwise minimum for two vectors"""
53 | return V(min(e) for e in zip(a, b))
54 |
55 |
56 | def vec_max(a, b) -> V:
57 | """Elementwise maximum for two vectors"""
58 | return V(max(e) for e in zip(a, b))
59 |
60 |
61 | def map_range(val, from_min=0, from_max=1, to_min=0, to_max=2):
62 | """Map a value from one input range to another. Works in the same way as the map range node in blender.
63 | succinct formula from: https://stackoverflow.com/a/45389903"""
64 | return (val - from_min) / (from_max - from_min) * (to_max - to_min) + to_min
65 |
66 |
67 | class JSONWithCommentsDecoder(json.JSONDecoder):
68 |
69 | match_trailing_commas: re.Pattern = re.compile(r",(?=\s*?[\}\]])", re.MULTILINE)
70 |
71 | def __init__(self, **kw):
72 | super().__init__(**kw)
73 |
74 | def decode(self, s: str):
75 | # Remove comments
76 | s = "\n".join(l if not l.lstrip().startswith("//") else "" for l in s.split("\n"))
77 | # Remove trailing commas
78 | s = self.match_trailing_commas.sub("", s)
79 | return super().decode(s)
80 |
81 |
82 | def get_all_def_files():
83 | files = []
84 | for file in NODE_DEF_DIR.rglob("*"):
85 | if file.parent.name == "sockets":
86 | continue
87 | if file.is_file() and file.suffix == ".jsonc" and not file.name.startswith(NODE_DEF_EXAMPLE_PREFIX):
88 | files.append(file)
89 | return files
90 |
91 |
92 | class Rectangle:
93 | """Helper class to represent a rectangle"""
94 |
95 | __slots__ = ["min", "max"]
96 |
97 | def __init__(self, min_co=(0, 0), max_co=(0, 0)):
98 | min_co = V(min_co)
99 | max_co = V(max_co)
100 |
101 | self.min = min_co
102 | self.max = max_co
103 |
104 | # alternate getter syntax
105 | minx = property(fget=lambda self: self.min.x)
106 | miny = property(fget=lambda self: self.min.y)
107 | maxx = property(fget=lambda self: self.max.x)
108 | maxy = property(fget=lambda self: self.max.y)
109 |
110 | @property
111 | def coords(self):
112 | """Return coordinates for drawing"""
113 | coords = [
114 | (self.minx, self.miny),
115 | (self.maxx, self.miny),
116 | (self.maxx, self.maxy),
117 | (self.minx, self.maxy),
118 | ]
119 | return coords
120 |
121 | @property
122 | def size(self):
123 | return self.max - self.min
124 |
125 | # FIXME: This can just be changed to using vec_mean of the min and max
126 | @property
127 | def center(self):
128 | return self.min + vec_divide(self.max - self.min, V((2, 2)))
129 |
130 | # return the actual min/max values. Needed because the class does not check
131 | # if the min and max values given are actually min and max at init.
132 | # I could fix it, but a bunch of stuff is built on it already, and I can't really be bothered
133 | @property
134 | def true_min(self):
135 | return vec_min(self.min, self.max)
136 |
137 | @property
138 | def true_max(self):
139 | return vec_max(self.min, self.max)
140 |
141 | def __str__(self):
142 | return f"Rectangle(V({self.minx}, {self.miny}), V({self.maxx}, {self.maxy}))"
143 |
144 | def __repr__(self):
145 | return self.__str__()
146 |
147 | def __mul__(self, value):
148 | if not isinstance(value, V):
149 | value = V((value, value))
150 | return Rectangle(self.min * value, self.max * value)
151 |
152 | def __add__(self, value):
153 | if not isinstance(value, V):
154 | value = V((value, value))
155 | return Rectangle(self.min + value, self.max + value)
156 |
157 | def isinside(self, point) -> bool:
158 | """Check if a point is inside this rectangle"""
159 | point = point
160 | min = self.true_min
161 | max = self.true_max
162 | return min.x <= point[0] <= max.x and min.y <= point[1] <= max.y
163 |
164 | def as_lines(self, individual=False):
165 | """Return a list of lines that make up this rectangle"""
166 | lines = []
167 | add = lines.append if individual else lines.extend
168 | coords = self.coords
169 | for i, coord in enumerate(coords):
170 | add((coord, coords[i - 1]))
171 | return lines
172 |
173 | def crop(self, rectangle):
174 | """Crop this rectangle to the inside of another one"""
175 | self.min = vec_max(self.min, rectangle.min)
176 | self.max = vec_min(self.max, rectangle.max)
177 | # prevent min/max overspilling on other side
178 | self.min = vec_min(self.min, rectangle.max)
179 | self.max = vec_max(self.max, rectangle.min)
180 |
--------------------------------------------------------------------------------
/node_pie/npie_keymap.py:
--------------------------------------------------------------------------------
1 | import bpy
2 | from bpy.types import Context, KeyMap
3 |
4 | from .npie_btypes import BOperator
5 | from .operators.op_call_link_drag import NPIE_OT_call_link_drag
6 | from .operators.op_insert_node_pie import NPIE_OT_insert_node_pie
7 |
8 | addon_keymaps: list[tuple[bpy.types.KeyMap, bpy.types.KeyMapItem]] = []
9 |
10 |
11 | def get_operator_keymap_items(keymap: KeyMap, operator_idname: str) -> list[KeyMap]:
12 | return [kmi for kmi in keymap.keymap_items if kmi.idname == operator_idname]
13 |
14 |
15 | def register():
16 | addon_keymaps.clear()
17 |
18 | wm = bpy.context.window_manager
19 | kc = wm.keyconfigs.addon
20 | if kc:
21 | km = kc.keymaps.new(name="View2D")
22 |
23 | kmi = km.keymap_items.new(
24 | NPIE_OT_call_link_drag.bl_idname,
25 | type="LEFTMOUSE",
26 | value="PRESS",
27 | ctrl=True,
28 | )
29 | addon_keymaps.append((km, kmi))
30 | kmi = km.keymap_items.new(
31 | NPIE_OT_call_link_drag.bl_idname,
32 | type="A",
33 | value="PRESS",
34 | ctrl=True,
35 | )
36 | addon_keymaps.append((km, kmi))
37 | kmi = km.keymap_items.new(
38 | NPIE_OT_insert_node_pie.bl_idname,
39 | type="LEFTMOUSE",
40 | value="CLICK_DRAG",
41 | ctrl=True,
42 | alt=True,
43 | )
44 | addon_keymaps.append((km, kmi))
45 |
46 |
47 | def unregister():
48 | for km, kmi in addon_keymaps:
49 | km.keymap_items.remove(kmi)
50 | addon_keymaps.clear()
51 |
52 |
53 | def get_keymap() -> KeyMap:
54 | return bpy.context.window_manager.keyconfigs.user.keymaps["View2D"]
55 |
56 |
57 | def draw_keymap_item(kmi: bpy.types.KeyMapItem, layout: bpy.types.UILayout):
58 | """Draw a keymap item in a prettier way than the default"""
59 | row = layout.row(align=True)
60 | map_type = kmi.map_type
61 | row.prop(kmi, "map_type", text="")
62 | if map_type == "KEYBOARD":
63 | row.prop(kmi, "type", text="", full_event=True)
64 | elif map_type == "MOUSE":
65 | row.prop(kmi, "type", text="", full_event=True)
66 | elif map_type == "NDOF":
67 | row.prop(kmi, "type", text="", full_event=True)
68 | elif map_type == "TWEAK":
69 | subrow = row.row()
70 | subrow.prop(kmi, "type", text="")
71 | subrow.prop(kmi, "value", text="")
72 | elif map_type == "TIMER":
73 | row.prop(kmi, "type", text="")
74 | else:
75 | row.label()
76 |
77 | box = layout
78 |
79 | if map_type not in {"TEXTINPUT", "TIMER"}:
80 | sub = box.column()
81 | subrow = sub.row(align=True)
82 |
83 | if map_type == "KEYBOARD":
84 | subrow.prop(kmi, "type", text="", event=True)
85 | subrow.prop(kmi, "value", text="")
86 | subrow_repeat = subrow.row(align=True)
87 | subrow_repeat.active = kmi.value in {"ANY", "PRESS"}
88 | subrow_repeat.prop(kmi, "repeat", text="", icon="FILE_REFRESH")
89 | elif map_type in {"MOUSE", "NDOF"}:
90 | subrow.prop(kmi, "type", text="")
91 | subrow.prop(kmi, "value", text="")
92 |
93 | subrow = sub.row(align=True)
94 | subrow.scale_x = 0.75
95 | subrow.prop(kmi, "any", toggle=True)
96 | subrow.prop(kmi, "shift_ui", toggle=True)
97 | subrow.prop(kmi, "ctrl_ui", toggle=True)
98 | subrow.prop(kmi, "alt_ui", toggle=True)
99 | subrow.prop(kmi, "oskey_ui", text="Cmd", toggle=True)
100 | subrow.prop(kmi, "key_modifier", text="", event=True)
101 |
102 |
103 | @BOperator("node_pie")
104 | class NPIE_OT_edit_keymap_item(BOperator.type):
105 |
106 | index: bpy.props.IntProperty()
107 |
108 | operator: bpy.props.StringProperty()
109 |
110 | def invoke(self, context: bpy.types.Context, event: bpy.types.Event):
111 | return self.call_popup(width=400)
112 |
113 | def draw(self, context: Context):
114 | km = get_keymap()
115 | kmi = get_operator_keymap_items(km, self.operator)[self.index]
116 | layout = self.layout
117 | layout.scale_y = 1.2
118 | row = layout.row(align=True)
119 | row.label(text="Edit keybind:")
120 | draw_keymap_item(kmi, layout)
121 |
122 |
123 | @BOperator("node_pie")
124 | class NPIE_OT_remove_keymap_item(BOperator.type):
125 |
126 | index: bpy.props.IntProperty()
127 |
128 | operator: bpy.props.StringProperty()
129 |
130 | def execute(self, context):
131 | km = get_keymap()
132 | kmi = get_operator_keymap_items(km, self.operator)[self.index]
133 | km.keymap_items.remove(kmi)
134 |
135 |
136 | @BOperator("node_pie")
137 | class NPIE_OT_new_keymap_item(BOperator.type):
138 | """Add a new keymap item for calling the node pie menu"""
139 |
140 | operator: bpy.props.StringProperty()
141 |
142 | type: bpy.props.StringProperty(default="LEFTMOUSE")
143 |
144 | value: bpy.props.StringProperty(default="PRESS")
145 |
146 | ctrl: bpy.props.BoolProperty()
147 | shift: bpy.props.BoolProperty()
148 | alt: bpy.props.BoolProperty()
149 |
150 | def execute(self, context):
151 | km = get_keymap()
152 | km.keymap_items.new(
153 | self.operator,
154 | self.type,
155 | self.value,
156 | ctrl=self.ctrl,
157 | shift=self.shift,
158 | alt=self.alt,
159 | )
160 | return {"FINISHED"}
161 |
--------------------------------------------------------------------------------
/node_pie/npie_menus.py:
--------------------------------------------------------------------------------
1 | import bpy
2 | from bpy.types import UILayout
3 |
4 | from .npie_btypes import BMenu
5 | from .npie_helpers import get_all_def_files, get_prefs
6 | from .operators.op_check_missing_nodes import NPIE_OT_check_missing_nodes
7 | from .operators.op_generate_socket_types_file import NPIE_OT_generate_socket_types_file
8 |
9 |
10 | @BMenu("Node Pie Utilities")
11 | class NPIE_MT_node_pie_utilities(BMenu.type):
12 |
13 | def draw(self, context):
14 | layout: UILayout = self.layout
15 | op = layout.operator("node_pie.open_definition_file", text="Open example definition file")
16 | op.example = True
17 |
18 | for file in get_all_def_files():
19 | if file.name == f"{context.space_data.tree_type}.jsonc":
20 | word = "Open"
21 | break
22 | else:
23 | word = "Create"
24 | op = layout.operator("node_pie.open_definition_file", text=f"{word} definition file for this node tree type")
25 | op.example = False
26 | layout.operator("node_pie.copy_nodes_as_json")
27 | NPIE_OT_generate_socket_types_file.draw_button(layout)
28 | NPIE_OT_check_missing_nodes.draw_button(layout)
29 |
30 |
31 | def context_menu_draw(self, context):
32 | prefs = get_prefs(context)
33 | if not prefs.npie_dev_extras:
34 | return
35 | layout: UILayout = self.layout
36 | layout.separator()
37 | layout.menu(NPIE_MT_node_pie_utilities.bl_idname, icon="NODE")
38 |
39 |
40 | def register():
41 | bpy.types.NODE_MT_context_menu.append(context_menu_draw)
42 |
43 |
44 | def unregister():
45 | bpy.types.NODE_MT_context_menu.remove(context_menu_draw)
46 |
--------------------------------------------------------------------------------
/node_pie/npie_node_def_file.py:
--------------------------------------------------------------------------------
1 | import json
2 | from dataclasses import dataclass, field
3 | from inspect import isclass
4 | from pathlib import Path
5 | from typing import Any
6 |
7 | import bpy
8 | from bpy.types import Context
9 |
10 | from .npie_constants import NODE_DEF_BUILTIN, NODE_DEF_USER
11 | from .npie_helpers import JSONWithCommentsDecoder, get_all_def_files
12 |
13 |
14 | class PollCondition:
15 | """Represents a condition that can be evaluated to determine whether to show a node or not."""
16 |
17 | def __init__(self, context_path: str, operand: str, value: Any = None):
18 | supported_operands = {"bool", "equals", "in", "not_equals"}
19 | if operand not in supported_operands:
20 | raise ValueError(f"Operand for item {self} '{operand}' is not in {supported_operands}")
21 |
22 | self.context_path = context_path
23 | self.operand = operand
24 | self.value = value
25 |
26 | def evaluate(self, context: Context):
27 | attr_path = f"context.{self.context_path}"
28 | result = eval(attr_path)
29 | if self.operand == "bool" and bool(result):
30 | return True
31 | elif self.operand == "equals" and self.value == result:
32 | return True
33 | elif self.operand == "not_equals" and self.value != result:
34 | return True
35 | elif self.operand == "in" and self.value in result:
36 | return True
37 | return False
38 |
39 |
40 | @dataclass
41 | class NodeItem:
42 | """An imitator of the built in blender NodeItem class, that implements the necessary settings"""
43 |
44 | label: str
45 | idname: str
46 | settings: dict = field(default_factory=dict)
47 | variants: dict = field(default_factory=dict)
48 | poll_conditions: list[PollCondition] = field(default_factory=list)
49 | color: str = ""
50 | description: str = ""
51 | category = None
52 |
53 | def poll(self, context: Context):
54 | if not self.poll_conditions:
55 | return True
56 | for condition in self.poll_conditions:
57 | if condition.evaluate(context):
58 | return True
59 | return False
60 |
61 |
62 | @dataclass
63 | class NodeCategory:
64 | """An imitator of the built in blender NodeCategory class, that implements the necessary settings"""
65 |
66 | label: str
67 | nodes: list[NodeItem]
68 | color: str
69 | icon: str = ""
70 | children: list = None
71 | idname: str = ""
72 | poll_conditions: list[PollCondition] = field(default_factory=list)
73 |
74 | poll = NodeItem.poll
75 | # def poll(self, context: Context):
76 | # if not self.poll_conditions:
77 | # return True
78 | # for condition in self.poll_conditions:
79 | # if condition.evaluate(context):
80 | # return True
81 | # return False
82 |
83 | def items(self, context) -> list[NodeItem]:
84 | return self.nodes
85 |
86 |
87 | @dataclass
88 | class Separator:
89 | label: str = ""
90 | poll_conditions: list[PollCondition] = field(default_factory=list)
91 |
92 | poll = NodeItem.poll
93 |
94 |
95 | @dataclass
96 | class NodeOperator:
97 | idname: str
98 | label: str = ""
99 | settings: dict = field(default_factory=dict)
100 | color: str = ""
101 |
102 |
103 | def create_defaults(data: dict):
104 | # Add default values in case they are missing from the file
105 | default_layout = {"top": [[]], "bottom": [[]], "left": [[]], "right": [[]]}
106 | data["layout"] = data.get("layout", default_layout)
107 | default_layout.update(data["layout"])
108 | data["layout"] = default_layout
109 | data["poll_types"] = data.get("poll_types", {})
110 | data["categories"] = data.get("categories", {})
111 | return data
112 |
113 |
114 | def merge_configs(base: dict, additions: dict, removals: dict = {}):
115 | # Add default values in case they are missing from the file
116 | base = create_defaults(base)
117 | additions = create_defaults(additions)
118 | removals = create_defaults(removals)
119 |
120 | # REMOVALS
121 | # Process layout removals
122 | orig_layout = base["layout"]
123 | remove_layout = removals["layout"]
124 | for area_name, rem_area in remove_layout.items():
125 | for orig_column, rem_column in zip(orig_layout[area_name], rem_area):
126 | for cat_id in rem_column:
127 | orig_column.remove(cat_id)
128 |
129 | # Process category removals
130 | orig_categories = base["categories"]
131 | remove_categories = removals["categories"]
132 | for rem_cat_name, rem_cat in remove_categories.items():
133 | if nodes := rem_cat.get("nodes", []):
134 | orig_nodes = orig_categories[rem_cat_name]["nodes"]
135 | for rem_node in nodes:
136 | for i, orig_node in enumerate(orig_nodes.copy()):
137 | if rem_node.get("separator") or orig_node.get("separator"):
138 | continue
139 | if rem_node["identifier"] == orig_node["identifier"]:
140 | orig_nodes.remove(orig_node)
141 | break
142 | pass
143 | else:
144 | del orig_categories[rem_cat_name]
145 |
146 | # Process full node removals
147 | remove_nodes = removals.get("nodes", [])
148 | for rem_node in remove_nodes:
149 | for orig_category in orig_categories.values():
150 | for orig_node in orig_category["nodes"].copy():
151 | if rem_node == orig_node.get("identifier"):
152 | orig_category["nodes"].remove(orig_node)
153 | break
154 |
155 | # ADDITIONS
156 | # Merge layout
157 | for orig_area_name, orig_columns in base["layout"].items():
158 | new_columns = additions["layout"].get(orig_area_name)
159 | if not new_columns:
160 | continue
161 | for i, new_column in enumerate(new_columns):
162 | new_column = new_column.copy()
163 | for new_row in new_column:
164 | orig_columns = base["layout"][orig_area_name]
165 | if i > len(orig_columns) - 1:
166 | orig_columns.append([new_row])
167 | else:
168 | orig_columns[i].append(new_row)
169 |
170 | # Merge poll types
171 | poll_types: dict = base["poll_types"]
172 | poll_types.update(additions["poll_types"])
173 |
174 | # Merge in the new nodes
175 | for orig_cat_name, orig_cat in base["categories"].items():
176 | new_cat = additions["categories"].get(orig_cat_name)
177 | if new_cat:
178 | # Insert the node after the specified one.
179 | idx = -1
180 | for new_node in new_cat["nodes"]:
181 | if name := new_node.get("after_node"):
182 | if name == "top":
183 | idx = 0
184 | elif name == "bottom":
185 | idx = -1
186 | else:
187 | names = [n.get("identifier") for n in orig_cat["nodes"]]
188 | idx = names.index(name) + 1
189 | elif name := new_node.get("before_node"):
190 | names = [n.get("identifier") for n in orig_cat["nodes"]]
191 | idx = names.index(name)
192 |
193 | if idx == -1:
194 | orig_cat["nodes"].append(new_node)
195 | else:
196 | orig_cat["nodes"].insert(idx, new_node)
197 |
198 | # Add new categories
199 | new_cats = additions["categories"].keys() - base["categories"].keys()
200 | for new_cat in new_cats:
201 | base["categories"][new_cat] = additions["categories"][new_cat]
202 | return base
203 |
204 |
205 | modified_times = {}
206 |
207 |
208 | def load_custom_nodes_info(tree_identifier: str, context) -> tuple[dict[str, NodeCategory], dict]:
209 | categories = {}
210 | layout = {}
211 |
212 | all_files = get_all_def_files()
213 |
214 | # Different render engines can use different nodes in the default shader editor, account for that.
215 | if tree_identifier == "ShaderNodeTree":
216 | for file in all_files:
217 | with open(file, "r") as f:
218 | data = json.load(f, cls=JSONWithCommentsDecoder)
219 | if data.get("render_engine") == context.scene.render.engine:
220 | tree_identifier = file.name
221 | break
222 | else:
223 | # Auto generate if not blender render engine
224 | if context.scene.render.engine not in {
225 | "BLENDER_EEVEE",
226 | "BLENDER_EEVEE_NEXT",
227 | "CYCLES",
228 | "BLENDER_WORKBENCH",
229 | }:
230 | return {}, {}
231 |
232 | def get_def_files(dir: Path) -> Path:
233 | files = []
234 | for file in dir.rglob("*"):
235 | if file.is_file() and file.suffix == ".jsonc" and file.name.startswith(f"{tree_identifier}"):
236 | files.append(file)
237 | return files
238 |
239 | # Get files
240 | files = get_def_files(NODE_DEF_USER)
241 | names = {f.name for f in files}
242 | files += [f for f in get_def_files(NODE_DEF_BUILTIN) if f.name not in names]
243 |
244 | if not files:
245 | return {}, {}
246 |
247 | # Sort the files from first version to latest version so that they are applied in the correct order
248 | def sort(f):
249 | with open(f, "r") as file:
250 | fdata = json.load(file, cls=JSONWithCommentsDecoder)
251 | return fdata.get("blender_version", [0, 0, 0])
252 |
253 | files.sort(key=sort)
254 |
255 | with open(files[0], "r") as f:
256 | data = json.load(f, cls=JSONWithCommentsDecoder)
257 |
258 | # Merge in imports
259 | if imports := data.get("imports"):
260 | for import_name in imports:
261 | for file in all_files:
262 | if file.stem == import_name:
263 | with open(file, "r") as f:
264 | new_data = json.load(f, cls=JSONWithCommentsDecoder)
265 | merge_configs(data, new_data)
266 | break
267 | else:
268 | raise ValueError(f"file {import_name}.jsonc not found")
269 |
270 | # Merge in nodes from newer versions
271 | for file in files:
272 | with open(file, "r") as f:
273 | new_data = json.load(f, cls=JSONWithCommentsDecoder)
274 |
275 | if tuple(new_data.get("blender_version", [0, 0, 0])) > bpy.app.version:
276 | continue
277 |
278 | merge_configs(data, new_data.get("additions", {}), new_data.get("removals", {}))
279 |
280 | layout = data["layout"]
281 | poll_types = data.get("poll_types", {})
282 |
283 | # Get all node definition classes so that the labels can be auto generated
284 | bl_node_types = {n.bl_idname: n for n in bpy.types.Node.__subclasses__() if hasattr(n, "bl_idname")}
285 | types = {getattr(bpy.types, t) for t in dir(bpy.types)}
286 | for t in types:
287 | if isclass(t) and issubclass(t, bpy.types.Node):
288 | bl_node_types[t.bl_rna.identifier] = t
289 |
290 | not_found = []
291 |
292 | for cat_idname, cat in data["categories"].items():
293 | items = []
294 | for node in cat["nodes"]:
295 |
296 | # Create poll conditions
297 | poll_conditions = []
298 | if poll_type := node.get("poll_type"):
299 | conditions = node.get("poll_conditions", [])
300 | node["poll_conditions"] = poll_types[poll_type] + conditions
301 | for condition in node.get("poll_conditions", []):
302 | poll_conditions.append(PollCondition(**condition))
303 |
304 | if node.get("separator"):
305 | items.append(Separator(label=node.get("label", ""), poll_conditions=poll_conditions))
306 | continue
307 | if node.get("operator"):
308 | items.append(
309 | NodeOperator(
310 | node["operator"],
311 | label=node.get("label", ""),
312 | settings=node.get("settings", {}),
313 | )
314 | )
315 | continue
316 | idname = node["identifier"]
317 |
318 | # Get an auto generated label, if one is not provided
319 | bl_node = bl_node_types.get(idname)
320 | label = node.get("label")
321 | if not label:
322 | if bl_node:
323 | label = bl_node.bl_rna.name if bl_node.bl_rna.name != "Node" else bl_node.bl_label
324 |
325 | if not label and not bl_node:
326 | not_found.append(idname)
327 | continue
328 | description = bl_node.bl_rna.description if bl_node else ""
329 | item = NodeItem(label, idname, color=node.get("color", ""), description=description)
330 | item.settings = node.get("settings", {})
331 | item.variants: dict[str, dict] = node.get("variants", {})
332 | item.poll_conditions = poll_conditions
333 |
334 | for name, variant in item.variants.items():
335 | if name != "separator":
336 | all_settings = item.settings.copy()
337 | all_settings.update(variant)
338 | item.variants[name] = all_settings
339 | items.append(item)
340 |
341 | if not cat.get("label"):
342 | raise ValueError(f"No label found for category '{cat_idname}'")
343 | category = NodeCategory(
344 | cat["label"],
345 | items,
346 | color=cat.get("color", ""),
347 | idname=cat_idname,
348 | icon=cat.get("icon", ""),
349 | )
350 | for condition in cat.get("poll_conditions", {}):
351 | category.poll_conditions.append(PollCondition(**condition))
352 | categories[cat_idname] = category
353 | for nodeitem in category.nodes:
354 | nodeitem.category = category
355 |
356 | if not_found:
357 | raise ValueError(f"No label found for node(s) '{not_found}'")
358 | return categories, layout
359 |
--------------------------------------------------------------------------------
/node_pie/npie_node_info.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | import bpy
4 |
5 | from .npie_constants import NODE_DEF_SOCKETS
6 | from .npie_helpers import JSONWithCommentsDecoder
7 | from .npie_node_def_file import NodeItem
8 |
9 | # Convert from node socket types to node enum names
10 | # Switch and compare nodes have special cases that need to be dealt with individually
11 | COMPARE_TYPES = {
12 | "Float": "FLOAT",
13 | "Int": "INT",
14 | "Vector": "VECTOR",
15 | "String": "STRING",
16 | "Color": "RGBA",
17 | }
18 |
19 | SWITCH_TYPES = {
20 | "Bool": "BOOLEAN",
21 | "Object": "OBJECT",
22 | "Collection": "COLLECTION",
23 | "Image": "IMAGE",
24 | "Geometry": "GEOMETRY",
25 | "Rotation": "ROTATION",
26 | "Menu": "MENU",
27 | "Material": "MATERIAL",
28 | }
29 | SWITCH_TYPES.update(COMPARE_TYPES)
30 | if bpy.app.version >= (4, 2):
31 | SWITCH_TYPES["Matrix"] = "MATRIX"
32 |
33 |
34 | def add_socket_names(names_dict):
35 | return {"NodeSocket" + k: v for k, v in names_dict.items()}
36 |
37 |
38 | # All other nodes then have a list of enum types associated with each socket type
39 | ALL_TYPES = SWITCH_TYPES.copy()
40 | ALL_TYPES.update({"Shader": "SHADER"})
41 | ALL_TYPES.update({"Texture": "TEXTURE"})
42 | ALL_TYPES = {k: [v] for k, v in ALL_TYPES.items()}
43 | ALL_TYPES["Vector"].append("FLOAT_VECTOR")
44 | ALL_TYPES["Color"].append("FLOAT_COLOR")
45 |
46 | SWITCH_TYPES = add_socket_names(SWITCH_TYPES)
47 | COMPARE_TYPES = add_socket_names(COMPARE_TYPES)
48 | ALL_TYPES = add_socket_names(ALL_TYPES)
49 |
50 | EXCLUSIVE_SOCKETS = {"Material", "Object", "Collection", "Geometry", "Shader", "String", "Image", "Texture"}
51 | EXCLUSIVE_SOCKETS = {"NodeSocket" + s for s in EXCLUSIVE_SOCKETS}
52 |
53 |
54 | def is_socket_to_node_valid(from_socket_type: str, from_socket_is_output: bool, to_node: NodeItem, socket_data: dict):
55 | """Check if the given socket type has any valid connections to the given node."""
56 | in_out = "inputs" if from_socket_is_output else "outputs"
57 | valid_types = (
58 | {from_socket_type} if from_socket_type in EXCLUSIVE_SOCKETS else set(ALL_TYPES.keys()) - EXCLUSIVE_SOCKETS
59 | )
60 | node_socket_data = socket_data.get(to_node.idname)
61 | if not node_socket_data:
62 | print(f"Socket data not defined for node {to_node.idname}")
63 | return True
64 | if any(t in socket_data[to_node.idname][in_out] for t in valid_types):
65 | return True
66 | return False
67 |
68 |
69 | def get_node_socket_info(tree_type: str, max_bl_version=bpy.app.version):
70 | """Return a dictionary of nodes and their socket types"""
71 | sockets_files = NODE_DEF_SOCKETS.rglob(f"**/{tree_type}*.jsonc")
72 | sockets_files_data = [json.loads(f.read_text(), cls=JSONWithCommentsDecoder) for f in sockets_files]
73 | sockets_files_data.sort(key=lambda data: data["bl_version"])
74 |
75 | all_socket_data = {}
76 | for data in sockets_files_data:
77 | if tuple(data["bl_version"]) <= tuple(max_bl_version):
78 | all_socket_data.update(data["nodes"])
79 |
80 | return all_socket_data
81 |
--------------------------------------------------------------------------------
/node_pie/npie_prefs.py:
--------------------------------------------------------------------------------
1 | import bpy
2 | from bpy.props import BoolProperty, FloatProperty
3 | from bpy.types import KeyMap, KeyMapItem, UILayout
4 | from .npie_btypes import BRegister
5 |
6 | from .. import __package__ as base_package
7 | from .npie_helpers import get_prefs
8 | from .npie_keymap import get_keymap, get_operator_keymap_items
9 | from .npie_ui import draw_inline_prop, draw_section
10 | from .operators.op_call_link_drag import (
11 | NPIE_OT_call_link_drag,
12 | register_debug_handler,
13 | unregister_debug_handler,
14 | )
15 | from .operators.op_insert_node_pie import NPIE_OT_insert_node_pie
16 | from .operators.op_show_info import InfoSnippets
17 |
18 |
19 | @BRegister()
20 | class NodePiePrefs(bpy.types.AddonPreferences):
21 | """Node pie"""
22 |
23 | bl_idname: str = base_package
24 |
25 | layout: UILayout
26 | node_pie_enabled: BoolProperty(name="Enable node pie", default=True)
27 |
28 | npie_variable_sizes: BoolProperty(
29 | name="Use variable size",
30 | default=True,
31 | description="Whether to increase the size of node buttons that are used most often",
32 | )
33 |
34 | npie_normal_size: FloatProperty(
35 | name="Normal size",
36 | default=1,
37 | description="The default size of the nodes buttons",
38 | )
39 |
40 | npie_max_size: FloatProperty(
41 | name="Max size",
42 | default=1.7,
43 | description="The size of the most popular nodes",
44 | )
45 |
46 | npie_show_node_groups: BoolProperty(
47 | name="Show node groups",
48 | default=True,
49 | description="Whether to show node groups in the pie menu or not",
50 | )
51 |
52 | npie_expand_node_groups: BoolProperty(
53 | name="Expand node groups",
54 | default=False,
55 | description="Whether to draw the node groups as a sub menu or as individual buttons",
56 | )
57 |
58 | npie_color_size: FloatProperty(
59 | name="Color bar size",
60 | default=0.02,
61 | description="Having this value too low can cause the colors to disapear.",
62 | subtype="FACTOR",
63 | min=0,
64 | max=1,
65 | )
66 |
67 | npie_freeze_popularity: BoolProperty(
68 | name="Freeze popularity",
69 | default=False,
70 | description="Prevent new changes the popularity of nodes.",
71 | )
72 |
73 | npie_separator_headings: BoolProperty(
74 | name="Subcategory labels",
75 | default=True,
76 | description="Draw the headings of subcategories or just a gap",
77 | )
78 |
79 | npie_show_variants: BoolProperty(
80 | name="Show variants",
81 | default=True,
82 | description="Draw the variants menus for nodes that support them",
83 | )
84 |
85 | npie_show_icons: BoolProperty(
86 | name="Show icons",
87 | default=True,
88 | description="Draw icons for categories",
89 | )
90 |
91 | npie_dev_extras: BoolProperty(
92 | name="Show dev extras",
93 | default=False,
94 | description="Show some operators in the right click menu to make creating custom definition files easier.",
95 | )
96 |
97 | npie_use_link_dragging: BoolProperty(
98 | name="Enable link dragging",
99 | default=True,
100 | description="Allow automatically connecting the new node to a socket if it is hovered",
101 | )
102 |
103 | npie_link_drag_disable_invalid: BoolProperty(
104 | name="Disable invalid nodes",
105 | default=True,
106 | description="Grey out nodes that are no able to be connected when using link drag or link insert",
107 | )
108 |
109 | def draw_debug_update(self, context):
110 | if self.npie_draw_debug_lines:
111 | register_debug_handler()
112 | else:
113 | unregister_debug_handler()
114 |
115 | npie_draw_debug_lines: BoolProperty(
116 | name="Draw debug boxes",
117 | default=False,
118 | description="Draw some debug lines to show the bounding box for clicking a node socket for drag linking.",
119 | update=draw_debug_update,
120 | )
121 |
122 | npie_socket_separation: FloatProperty(
123 | name="Socket separation",
124 | default=22,
125 | description="The vertical distance between node sockets.\
126 | This should only be changed if you use a high or low UI scale,\
127 | and drag linking doesn't work as a result.".replace(
128 | " ", ""
129 | ),
130 | subtype="PIXEL",
131 | )
132 |
133 | # custom_node_def_directory: StringProperty(
134 | # name="Custom node definition file directory.",
135 | # description="A folder for user created definition files. This is used so that"
136 | # )
137 |
138 | def draw(self, context):
139 | layout = self.layout
140 | # layout = draw_enabled_button(layout, self, "node_pie_enabled")
141 | prefs = get_prefs(context)
142 | layout = layout.grid_flow(row_major=True, even_columns=True)
143 | # layout.label(text="hahah")
144 | fac = 0.515
145 |
146 | col = draw_section(layout, "General")
147 | col.scale_y = 0.9
148 | draw_inline_prop(col, prefs, "npie_show_icons", factor=fac)
149 | draw_inline_prop(col, prefs, "npie_show_variants", factor=fac)
150 | draw_inline_prop(col, prefs, "npie_separator_headings", factor=fac)
151 | draw_inline_prop(col, prefs, "npie_expand_node_groups", factor=fac)
152 | draw_inline_prop(col, prefs, "npie_dev_extras", factor=fac)
153 | draw_inline_prop(col, prefs, "npie_color_size", factor=fac)
154 |
155 | col = draw_section(layout, "Node Size")
156 | draw_inline_prop(col, prefs, "npie_variable_sizes", factor=fac)
157 | draw_inline_prop(col, prefs, "npie_normal_size", factor=fac)
158 | draw_inline_prop(col, prefs, "npie_max_size", factor=fac)
159 | row = col.row(align=True)
160 | row.scale_y = 1.5
161 | row.prop(
162 | prefs,
163 | "npie_freeze_popularity",
164 | text="Unfreeze popularity" if prefs.npie_freeze_popularity else "Freeze popularity",
165 | icon="FREEZE",
166 | toggle=True,
167 | )
168 | row.operator("node_pie.reset_popularity", icon="FILE_REFRESH")
169 |
170 | col = draw_section(layout, "On Link Drag")
171 | draw_inline_prop(col, prefs, "npie_use_link_dragging", factor=fac)
172 | if prefs.npie_use_link_dragging:
173 | draw_inline_prop(col, prefs, "npie_link_drag_disable_invalid", factor=fac)
174 | draw_inline_prop(col, prefs, "npie_draw_debug_lines", factor=fac)
175 | if prefs.npie_draw_debug_lines:
176 | draw_inline_prop(col, prefs, "npie_socket_separation", factor=fac)
177 | InfoSnippets.link_drag.draw(col)
178 |
179 | def draw_op_kmis(keymap: KeyMap, operator: str, text: str, properties: dict = {}, default_new: dict = {}):
180 | row = col.row(align=True)
181 | row.scale_y = 0.8
182 | row.label(text=text)
183 |
184 | row = row.row(align=True)
185 | row.alignment = "RIGHT"
186 | op = row.operator("node_pie.new_keymap_item", text="", icon="ADD", emboss=False)
187 | op.operator = operator
188 | for name, arg in default_new.items():
189 | setattr(op, name, arg)
190 |
191 | kmis = get_operator_keymap_items(keymap, operator)
192 | for i, kmi in enumerate(kmis):
193 | kmi: KeyMapItem
194 |
195 | # Check that properties are the same
196 | matches = True
197 | for key, value in properties.items():
198 | if getattr(kmi.properties, key) != value:
199 | matches = False
200 | break
201 | if not matches:
202 | continue
203 |
204 | row = col.row(align=True)
205 | row.active = kmi.active
206 | sub = row.row(align=True)
207 | sub.prop(kmi, "active", text="")
208 | sub = row.row(align=True)
209 | sub.scale_x = 0.5
210 | sub.prop(kmi, "type", full_event=True, text="")
211 | sub = row.row(align=True)
212 | sub.enabled = True
213 |
214 | # TODO get this working, currently doesn't save with the keymap
215 | # if hasattr(kmi.properties, "pass_through"):
216 | # sub.prop(kmi.properties, "pass_through", text="", icon="IPO_EASE_IN_OUT", invert_checkbox=True)
217 |
218 | op = sub.operator("node_pie.edit_keymap_item", text="", icon="GREASEPENCIL")
219 | op.index = i
220 | op.operator = operator
221 | # if kmi.is_user_modified:
222 | # op = sub.operator("preferences.keyitem_restore", text="", icon="BACK")
223 | # op.item_id = kmi.id
224 | op = sub.operator("node_pie.remove_keymap_item", text="", icon="X")
225 | op.index = i
226 | op.operator = operator
227 |
228 | col = draw_section(layout, "Keymap")
229 | km = get_keymap()
230 | draw_op_kmis(km, NPIE_OT_call_link_drag.bl_idname, "Pie menu:")
231 | col.separator()
232 | draw_op_kmis(km, NPIE_OT_insert_node_pie.bl_idname, "Link insert:", default_new={"value": "CLICK_DRAG"})
233 |
--------------------------------------------------------------------------------
/node_pie/operators/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strike-digital/node_pie/48322b48c80347382c4b8292cb8eaf04610dcfdc/node_pie/operators/__init__.py
--------------------------------------------------------------------------------
/node_pie/operators/op_add_node.py:
--------------------------------------------------------------------------------
1 | import json
2 | from typing import OrderedDict
3 |
4 | import bpy
5 | from bpy.types import Node, NodeSocket, NodeTree
6 |
7 | from ..npie_btypes import BOperator
8 | from ..npie_constants import POPULARITY_FILE, POPULARITY_FILE_VERSION
9 | from ..npie_node_info import ALL_TYPES, COMPARE_TYPES, EXCLUSIVE_SOCKETS, SWITCH_TYPES
10 | from ..npie_ui import NPIE_MT_node_pie, get_popularity_id
11 |
12 |
13 | def set_node_settings(socket: NodeSocket, node: Node, ui: bool = True):
14 | """Set a nodes settings so that the correct type of socket is visible.
15 | If ui is false don't make any changes that might be better for the user."""
16 | # Make sure that the node has the correct data type
17 | # exclude bool if ui so that bool inputs are put into switch rather than true or false socket
18 | if node.bl_idname == "GeometryNodeSwitch" and not (
19 | socket.bl_idname.startswith("NodeSocketBool") and ui and socket.is_output
20 | ):
21 | try:
22 | name = next(s for s in SWITCH_TYPES if socket.bl_idname.startswith(s))
23 | except StopIteration:
24 | return
25 | node.input_type = SWITCH_TYPES[name]
26 |
27 | elif node.bl_idname == "FunctionNodeCompare" and socket.is_output:
28 | try:
29 | name = next(s for s in COMPARE_TYPES if socket.bl_idname.startswith(s))
30 | except StopIteration:
31 | return
32 | node.data_type = COMPARE_TYPES[name]
33 |
34 | elif hasattr(node, "data_type"):
35 | name = next(s for s in ALL_TYPES if socket.bl_idname.startswith(s))
36 | for data_type in ALL_TYPES[name]:
37 | try:
38 | node.data_type = data_type
39 | except TypeError:
40 | pass
41 |
42 |
43 | def get_socket(from_socket, to_sockets):
44 | """
45 | Try to find the best match of from_socket in to_sockets based on socket types.
46 | Is agnostic to whether sockets are inputs or outputs.
47 | """
48 | if not to_sockets:
49 | return
50 |
51 | socket = None
52 | for s in to_sockets:
53 | if s.bl_idname != from_socket.bl_idname:
54 | if s.bl_idname in EXCLUSIVE_SOCKETS or from_socket.bl_idname in EXCLUSIVE_SOCKETS:
55 | continue
56 | socket = s
57 | break
58 | if not socket:
59 | socket = to_sockets[0]
60 | return socket
61 |
62 |
63 | def handle_node_linking(socket: NodeSocket, node: Node):
64 | """Make the optimal link between a node and a socket, taking into account socket types"""
65 |
66 | if socket.is_output:
67 | inputs = [s for s in node.inputs if s.enabled and not s.hide]
68 | to_socket = get_socket(socket, inputs)
69 | from_socket = socket
70 | else:
71 | outputs = [s for s in node.outputs if s.enabled and not s.hide]
72 | from_socket = get_socket(socket, outputs)
73 | to_socket = socket
74 |
75 | if from_socket and to_socket:
76 | node.id_data.links.new(from_socket, to_socket)
77 |
78 |
79 | @BOperator("node_pie", idname="add_node", undo=True)
80 | class NPIE_OT_add_node(BOperator.type):
81 | """Add a node to the node tree, and increase its popularity by 1"""
82 |
83 | type: bpy.props.StringProperty(name="Type", description="Node type to add", default="FunctionNodeInputVector")
84 | group_name: bpy.props.StringProperty(name="Group name", description="The name of the node group to add")
85 | use_transform: bpy.props.BoolProperty(default=True)
86 |
87 | settings: bpy.props.StringProperty(
88 | name="Settings",
89 | description="Settings to be applied on the newly created node",
90 | default="{}",
91 | options={"SKIP_SAVE"},
92 | )
93 |
94 | def execute(self, context):
95 | try:
96 | bpy.ops.node.add_node(
97 | "INVOKE_DEFAULT",
98 | False,
99 | type=self.type,
100 | use_transform=self.use_transform,
101 | )
102 | except RuntimeError as e:
103 | self.report({"ERROR"}, str(e))
104 | return {"CANCELLED"}
105 |
106 | node_tree: NodeTree = context.area.spaces.active.path[-1].node_tree
107 | node = node_tree.nodes.active
108 | if self.group_name:
109 | node.node_tree = bpy.data.node_groups[self.group_name]
110 |
111 | # If being added by dragging from a socket
112 | if socket := NPIE_MT_node_pie.from_socket:
113 | set_node_settings(socket, node)
114 |
115 | # Set the settings for the node
116 | settings = eval(self.settings)
117 | for name, value in settings.items():
118 | name = "node." + name
119 | attr = ".".join(name.split(".")[:-1])
120 | name = name.split(".")[-1]
121 | setattr(eval(attr), name, value)
122 |
123 | # If being added by dragging from a socket
124 | if socket := NPIE_MT_node_pie.from_socket:
125 | handle_node_linking(socket, node)
126 |
127 | # If being added by dragging from a socket
128 | if sockets := NPIE_MT_node_pie.to_sockets:
129 | for socket in sockets:
130 | handle_node_linking(socket, node)
131 |
132 | with open(POPULARITY_FILE, "r") as f:
133 | if text := f.read():
134 | try:
135 | data = json.loads(text)
136 | except json.decoder.JSONDecodeError:
137 | data = {}
138 | else:
139 | data = {}
140 |
141 | version = data.get("version", POPULARITY_FILE_VERSION)
142 |
143 | if version[0] > POPULARITY_FILE_VERSION[0]:
144 | self.report({"ERROR"}, "Saved nodes file is from a newer version of the addon")
145 | return {"CANCELLED"}
146 |
147 | trees = data.get("node_trees", {})
148 | nodes = OrderedDict(trees.get(node_tree.bl_rna.identifier, {}))
149 | key = get_popularity_id(self.type, self.settings)
150 | node = nodes.get(key, {})
151 | count = node.get("count", 0)
152 | count += 1
153 |
154 | data["version"] = POPULARITY_FILE_VERSION
155 | node["count"] = count
156 | nodes[key] = node
157 | # Sort the nodes in descending order
158 | nodes = OrderedDict(sorted(nodes.items(), key=lambda item: item[1].get("count", 0), reverse=True))
159 | trees[node_tree.bl_rna.identifier] = nodes
160 | data["node_trees"] = trees
161 |
162 | with open(POPULARITY_FILE, "w") as f:
163 | json.dump(data, f, indent=4)
164 |
165 | return {"PASS_THROUGH"}
166 |
--------------------------------------------------------------------------------
/node_pie/operators/op_call_link_drag.py:
--------------------------------------------------------------------------------
1 | import bpy
2 | import gpu
3 | from bpy.props import BoolProperty, IntProperty
4 | from bpy.types import Area, Context, Event, Node, NodeSocket
5 | from gpu_extras.batch import batch_for_shader
6 | from gpu_extras.presets import draw_circle_2d
7 | from mathutils import Vector as V
8 |
9 | from ..npie_btypes import BOperator
10 | from ..npie_constants import IS_4_0
11 | from ..npie_drawing import draw_line
12 | from ..npie_helpers import Rectangle, get_node_location, get_prefs
13 | from ..npie_ui import NPIE_MT_node_pie
14 |
15 | location = None
16 | hitbox_size = 10 # The radius in which to register a socket click
17 |
18 |
19 | def view_to_region(area: Area, coords: V) -> V:
20 | """Convert 2d editor to screen space coordinates"""
21 | coords = area.regions[3].view2d.view_to_region(coords[0], coords[1], clip=False)
22 | return V(coords)
23 |
24 |
25 | def region_to_view(area: Area, coords: V) -> V:
26 | """Convert screen space to 2d editor coordinates"""
27 | coords = area.regions[3].view2d.region_to_view(coords[0], coords[1])
28 | return V(coords)
29 |
30 |
31 | def dpifac():
32 | prefs = bpy.context.preferences.system
33 | return prefs.dpi / 72
34 | return prefs.dpi * prefs.pixel_size / 72
35 |
36 |
37 | def get_socket_bboxes(node: Node) -> tuple[dict[NodeSocket, V], dict[NodeSocket, Rectangle]]:
38 | """Get the bounding boxes of all inputs and outputs for the given node.
39 | There is no built in way to do this so it's mostly arbitrary numbers that look about right.
40 | This doesn't account for nodes with panels, as they are not currently accessible with the api"""
41 | if not node:
42 | return
43 |
44 | not_vectors = {"Subsurface Radius"} # Who decided to make this one socket different smh
45 |
46 | location = get_node_location(node)
47 | positions = {}
48 | bboxes = {}
49 |
50 | # inputs
51 | inputs = [i for i in node.inputs if not i.hide and i.enabled]
52 | bottom = V((location.x, location.y - node.dimensions.y / dpifac()))
53 | min_offset = V((18, 11)) * dpifac()
54 | max_offset_x = node.width * dpifac()
55 |
56 | if node.type == "REROUTE":
57 | pos = location * dpifac()
58 | positions[node.outputs[0]] = pos
59 | size = V((20, 20))
60 | bboxes[node.outputs[0]] = Rectangle(pos - size, pos + size)
61 | return positions, bboxes
62 |
63 | for i, input in enumerate(list(inputs)[::-1]):
64 | pos = bottom.copy()
65 | min_offset_y = 0
66 | if i == 0:
67 | pos.y -= 5
68 | if input.type in {"VECTOR", "ROTATION"} and not input.hide_value and not input.is_linked and input.name not in not_vectors:
69 | pos.y += 82
70 | min_offset_y = 65
71 | else:
72 | pos.y += get_prefs(bpy.context).npie_socket_separation
73 | bottom = pos
74 | positions[input] = pos * dpifac()
75 | pos = pos * dpifac()
76 |
77 | min_co = pos - V((min_offset.x, min_offset.y + min_offset_y))
78 | max_co = pos + V((max_offset_x, min_offset.y))
79 | bboxes[input] = Rectangle(min_co, max_co)
80 |
81 | # Outputs
82 | top = V((location.x + node.width, location.y))
83 | outputs = [o for o in node.outputs if not o.hide and o.enabled]
84 |
85 | for i, output in enumerate(list(outputs)[::]):
86 | pos = top.copy()
87 | if i == 0:
88 | pos.y -= 35
89 | else:
90 | pos.y -= 22
91 | top = pos
92 | positions[output] = pos * dpifac()
93 | pos = pos * dpifac()
94 |
95 | min_co = pos - V((max_offset_x, min_offset.y))
96 | max_co = pos + V((min_offset.x, min_offset.y))
97 | bboxes[output] = Rectangle(min_co, max_co)
98 |
99 | return positions, bboxes
100 |
101 |
102 | if IS_4_0:
103 | shader: gpu.types.GPUShader = gpu.shader.from_builtin("UNIFORM_COLOR")
104 | else:
105 | shader: gpu.types.GPUShader = gpu.shader.from_builtin("2D_UNIFORM_COLOR")
106 |
107 |
108 | def draw_debug_lines():
109 | """Draw a circle around the sockets of the active node, and also the last location that the node pie was activated"""
110 | node = bpy.context.active_node
111 | if node:
112 | positions, bboxes = get_socket_bboxes(node)
113 | for socket, bbox in bboxes.items():
114 | batch = batch_for_shader(shader, "LINES", {"pos": bbox.as_lines()})
115 | shader.bind()
116 | line_colour = (1, 0, 1, 0.9)
117 | shader.uniform_float("color", line_colour)
118 | batch.draw(shader)
119 | for socket, pos in positions.items():
120 | draw_circle_2d(pos, (1, 0, 1, 1), 5 * dpifac())
121 | if location:
122 | draw_circle_2d(location, (0, 1, 1, 1), 5 * dpifac())
123 |
124 |
125 | handlers = []
126 |
127 |
128 | def register_debug_handler():
129 | if get_prefs(bpy.context).npie_draw_debug_lines:
130 | global handlers
131 | handlers.append(bpy.types.SpaceNodeEditor.draw_handler_add(draw_debug_lines, (), "WINDOW", "POST_VIEW"))
132 |
133 |
134 | def unregister_debug_handler():
135 | unregister()
136 |
137 |
138 | def register():
139 | bpy.app.timers.register(register_debug_handler)
140 |
141 |
142 | def unregister():
143 | global handlers
144 | for handler in handlers:
145 | bpy.types.SpaceNodeEditor.draw_handler_remove(handler, "WINDOW")
146 | handlers.clear()
147 |
148 |
149 | @BOperator("node_pie")
150 | class NPIE_OT_call_link_drag(BOperator.type):
151 | """Call the node pie menu"""
152 |
153 | name: IntProperty()
154 |
155 | pass_through: BoolProperty(
156 | name="Enable link drag",
157 | description="Whether to check for link drag, or just pass through to the default pie menu",
158 | default=False,
159 | )
160 |
161 | @classmethod
162 | def poll(cls, context):
163 | if not context.space_data or context.area.type != "NODE_EDITOR" or not context.space_data.edit_tree:
164 | return False
165 | return True
166 |
167 | def invoke(self, context: Context, event: Event):
168 | self.handler = None
169 | self.socket = None
170 | self.from_pos = V((0, 0))
171 | self.released = False
172 |
173 | mouse_pos = region_to_view(context.area, self.mouse_region)
174 | global location
175 | location = mouse_pos
176 | # Look for a socket near to the mouse position
177 | for node in context.space_data.edit_tree.nodes:
178 | if node.hide:
179 | continue
180 | positions, bboxes = get_socket_bboxes(node)
181 | for socket, bbox in bboxes.items():
182 | if bbox.isinside(mouse_pos) and socket.bl_idname != "NodeSocketVirtual":
183 | self.socket = socket
184 | self.from_pos = view_to_region(context.area, positions[socket])
185 | break
186 |
187 | # if socket clicked
188 | if not self.pass_through and self.socket and get_prefs(context).npie_use_link_dragging:
189 | context.area.tag_redraw()
190 | self.handler = bpy.types.SpaceNodeEditor.draw_handler_add(
191 | self.draw_handler,
192 | tuple([context]),
193 | "WINDOW",
194 | "POST_PIXEL",
195 | )
196 | handlers.append(self.handler)
197 | return self.start_modal()
198 | else:
199 | NPIE_MT_node_pie.from_socket = self.socket
200 | NPIE_MT_node_pie.to_sockets = []
201 | bpy.ops.node_pie.call_node_pie("INVOKE_DEFAULT")
202 | return self.FINISHED
203 |
204 | def finish(self):
205 | bpy.types.SpaceNodeEditor.draw_handler_remove(self.handler, "WINDOW")
206 | global handlers
207 | handlers.remove(self.handler)
208 | return self.FINISHED
209 |
210 | def modal(self, context: Context, event: Event):
211 | context.area.tag_redraw()
212 |
213 | if event.type in {"RIGHTMOUSE", "ESC"} and event.value != "RELEASE":
214 | return self.finish()
215 |
216 | elif event.value == "RELEASE" and event.type not in {"CTRL", "ALT", "OSKEY", "SHIFT"}:
217 | NPIE_MT_node_pie.from_socket = self.socket
218 | NPIE_MT_node_pie.to_sockets = []
219 | bpy.ops.node_pie.call_node_pie("INVOKE_DEFAULT", reset_args=False)
220 | return self.finish()
221 |
222 | return self.RUNNING_MODAL
223 |
224 | def draw_handler(self, context: Context):
225 | to_pos = self.mouse_region
226 | color = self.socket.draw_color(context, self.socket.node)
227 | draw_line(self.from_pos, to_pos, color)
228 |
--------------------------------------------------------------------------------
/node_pie/operators/op_call_node_pie.py:
--------------------------------------------------------------------------------
1 | import bpy
2 |
3 | from ..npie_btypes import BOperator
4 | from ..npie_node_def_file import NodeItem, load_custom_nodes_info
5 | from ..npie_ui import NPIE_MT_node_pie, get_variants_menu, unregister_variants_menus
6 |
7 |
8 | @BOperator("node_pie")
9 | class NPIE_OT_call_node_pie(BOperator.type):
10 | """Call the node pie menu"""
11 |
12 | reset_args: bpy.props.BoolProperty(default=True)
13 |
14 | @classmethod
15 | def poll(cls, context):
16 | if not context.space_data or context.area.type != "NODE_EDITOR":
17 | return False
18 | return True
19 |
20 | def execute(self, context):
21 | unregister_variants_menus()
22 |
23 | # The variants menus can't be registered in a draw function, so add them here beforehand
24 | categories, cat_layout = load_custom_nodes_info(context.area.spaces.active.tree_type, context)
25 | has_node_file = categories != {}
26 | if has_node_file:
27 | for cat_name, category in categories.items():
28 | for node in category.nodes:
29 | if isinstance(node, NodeItem) and node.variants:
30 | get_variants_menu(node)
31 |
32 | if self.reset_args:
33 | NPIE_MT_node_pie.from_socket = None
34 | NPIE_MT_node_pie.to_socket = []
35 |
36 | bpy.ops.wm.call_menu_pie("INVOKE_DEFAULT", name=NPIE_MT_node_pie.__name__)
37 |
--------------------------------------------------------------------------------
/node_pie/operators/op_check_missing_nodes.py:
--------------------------------------------------------------------------------
1 | from inspect import isclass
2 |
3 | import bpy
4 | from bpy.types import NodeTree
5 |
6 | from ..npie_btypes import BOperator
7 | from ..npie_node_def_file import Separator, load_custom_nodes_info
8 | from .op_call_link_drag import region_to_view
9 |
10 | EXCLUDED_NODES = {
11 | "GeometryNodeRepeatInput",
12 | "GeometryNodeForeachGeometryElementInput",
13 | "GeometryNodeForeachGeometryElementOutput",
14 | "GeometryNodeSimulationOutput",
15 | "GeometryNodeSimulationInput",
16 | "GeometryNodeRepeatOutput",
17 | "GeometryNodeViewer",
18 | "GeometryNodeGroup",
19 | "NodeGroupInput",
20 | "NodeGroupOutput",
21 | "ShaderNodeOutputLineStyle",
22 | "ShaderNodeOutputWorld",
23 | "ShaderNodeGroup",
24 | "CompositorNodeGroup",
25 | }
26 |
27 |
28 | @BOperator(label="Check for missing nodes")
29 | class NPIE_OT_check_missing_nodes(BOperator.type):
30 | """Check for any nodes that aren't included in the node pie for this node tree type, and add them to the tree."""
31 |
32 | def execute(self, context):
33 | node_tree: NodeTree = context.space_data.edit_tree
34 |
35 | # Get a list of nodes already in the pie menu
36 | categories, cat_layout = load_custom_nodes_info(context.area.spaces.active.tree_type, context)
37 | nodes: set[str] = set()
38 | for cat in categories.values():
39 | for node in cat.nodes:
40 | if isinstance(node, Separator):
41 | continue
42 | nodes.add(node.idname)
43 |
44 | # Get a list of node types for this node tree
45 | bpy_nodes: set[str] = set()
46 | for bpy_type in dir(bpy.types):
47 | bpy_type = getattr(bpy.types, bpy_type)
48 | if not isclass(bpy_type) or not issubclass(bpy_type, bpy.types.Node):
49 | continue
50 | if bpy_type.bl_rna.identifier in EXCLUDED_NODES:
51 | continue
52 | try:
53 | node = node_tree.nodes.new(bpy_type.bl_rna.identifier)
54 | except RuntimeError:
55 | continue
56 | node_tree.nodes.remove(node)
57 | bpy_nodes.add(bpy_type.bl_rna.identifier)
58 |
59 | # Add missing nodes
60 | print("Missing nodes:")
61 | unused_nodes = bpy_nodes - nodes
62 | position = region_to_view(context.area, self.mouse_region)
63 | for node_type in unused_nodes:
64 | node = node_tree.nodes.new(node_type)
65 | node.location = position
66 | position.x += node.width + 20
67 | print(node_type)
--------------------------------------------------------------------------------
/node_pie/operators/op_copy_nodes_as_json.py:
--------------------------------------------------------------------------------
1 | from ..npie_btypes import BOperator
2 |
3 |
4 | @BOperator("node_pie")
5 | class NPIE_OT_copy_nodes_as_json(BOperator.type):
6 | """Copy the selected nodes in the correct format to be pasted into the node pie definition file."""
7 |
8 | @classmethod
9 | def poll(cls, context):
10 | if not context.space_data or context.area.type != "NODE_EDITOR":
11 | return False
12 | if not context.selected_nodes:
13 | return False
14 | return True
15 |
16 | def execute(self, context):
17 | items = []
18 | for node in context.selected_nodes:
19 | data_item = {"identifier": node.bl_idname}
20 | items.append(str(data_item).replace("'", '"'))
21 | # items.append(json.dumps(data_item, indent=2))
22 | items = ",\n".join(items)
23 | context.window_manager.clipboard = items
24 | print()
25 | print("Nodes to copy:")
26 | print(items)
27 | print()
28 | num = len(context.selected_nodes)
29 | self.report({"INFO"}, message=f"Copied {num} node{'' if num == 1 else 's'}")
30 | return {"FINISHED"}
--------------------------------------------------------------------------------
/node_pie/operators/op_generate_socket_types_file.py:
--------------------------------------------------------------------------------
1 | import json
2 | import webbrowser
3 | from dataclasses import dataclass
4 | from pathlib import Path
5 |
6 | import bpy
7 | from bpy.types import Context, NodeTree
8 |
9 | from ..npie_btypes import BOperator
10 | from ..npie_constants import NODE_DEF_SOCKETS
11 | from ..npie_node_def_file import NodeItem, load_custom_nodes_info
12 | from ..npie_node_info import ALL_TYPES, get_node_socket_info
13 | from . import op_add_node
14 | from .op_call_link_drag import NPIE_OT_call_link_drag
15 |
16 |
17 | @dataclass
18 | class DummySocket:
19 | bl_idname: str
20 | is_output: bool
21 |
22 |
23 | def generate_node_socket_info(context: Context, tree_type: str, directory: Path):
24 | """Generate a socket info file for the given node tree.
25 | This contains the type of each socket for each node,
26 | and is used to tell if a node should be greyed out during link dragging."""
27 | categories, layout = load_custom_nodes_info(context.area.spaces.active.tree_type, context)
28 | all_nodes: list[NodeItem] = []
29 | for cat in categories.values():
30 | for node_type in cat.nodes:
31 | if isinstance(node_type, NodeItem):
32 | all_nodes.append(node_type)
33 |
34 | data = {}
35 | data["bl_version"] = bpy.app.version[:2]
36 | all_node_data = {}
37 | node_tree: NodeTree = context.space_data.edit_tree
38 | for node_type in all_nodes:
39 | node = node_tree.nodes.new(node_type.idname)
40 | node_data = {}
41 | inputs = set()
42 | outputs = set()
43 | for socket_type in ALL_TYPES:
44 | op_add_node.set_node_settings(DummySocket(socket_type, True), node, ui=False)
45 | for socket in node.inputs:
46 | inputs.add(socket.bl_idname)
47 | for socket in node.outputs:
48 | outputs.add(socket.bl_idname)
49 |
50 | node_data["inputs"] = list(inputs)
51 | node_data["outputs"] = list(outputs)
52 | all_node_data[node_type.idname] = node_data
53 | node_tree.nodes.remove(node)
54 |
55 | # Remove nodes that have already been defined in a previous socket file
56 | version = list(bpy.app.version)
57 | version[1] -= 1
58 | prev_data = get_node_socket_info(tree_type, max_bl_version=version)
59 | if prev_data:
60 | for name, sockets in prev_data.items():
61 | if name not in all_node_data.keys():
62 | continue
63 | node_data = all_node_data[name]
64 | if set(sockets["inputs"]) != set(node_data["inputs"]):
65 | continue
66 | if set(sockets["outputs"]) != set(node_data["outputs"]):
67 | continue
68 |
69 | del all_node_data[name]
70 |
71 | if not all_node_data:
72 | return None
73 |
74 | data["nodes"] = all_node_data
75 | data_str = json.dumps(data, indent=2)
76 | data_str = (
77 | "// This is a list of the socket types of all nodes"
78 | + "\n// used for telling whether a node is valid during link drag."
79 | + "\n// It contains all of the new and updated nodes in this blender version."
80 | + "\n// It can be auto generated using the 'generate socket types file' operator\n"
81 | + data_str
82 | )
83 |
84 | version_str = "_".join(str(i) for i in bpy.app.version[:2])
85 | path = directory / f"{tree_type}_sockets_{version_str}.jsonc"
86 | with open(path, "w") as f:
87 | f.write(data_str)
88 | return path
89 |
90 |
91 | @BOperator("node_pie")
92 | class NPIE_OT_generate_socket_types_file(BOperator.type):
93 | """Generate a socket types file for this node tree type.
94 | This is used to disable node items that are not compatible during link dragging or inserting."""
95 |
96 | poll = NPIE_OT_call_link_drag.poll
97 |
98 | def invoke(self, context, event):
99 | node_tree: NodeTree = context.space_data.edit_tree
100 | self.tree_type = node_tree.bl_rna.identifier
101 | self.to_path = generate_node_socket_info(context, self.tree_type, NODE_DEF_SOCKETS)
102 | if not self.to_path:
103 | self.report({"INFO"}, "No new socket information in this blender version")
104 | return self.FINISHED
105 | return self.call_popup_confirm(width=500)
106 |
107 | def draw(self, context):
108 | layout = self.layout
109 | layout.label(text=f"Successfully generated socket types for node tree {self.tree_type}")
110 | layout.label(text="Open file?")
111 |
112 | def execute(self, context):
113 | # Open the newly generated file in the default text editor.
114 | webbrowser.open(self.to_path)
115 |
--------------------------------------------------------------------------------
/node_pie/operators/op_insert_node_pie.py:
--------------------------------------------------------------------------------
1 | import bpy
2 | from bpy.types import Context, Event, NodeTree
3 | from ..npie_ui import NPIE_MT_node_pie
4 | from ..npie_btypes import BOperator
5 |
6 |
7 | @BOperator("node_pie", undo=True)
8 | class NPIE_OT_insert_node_pie(BOperator.type):
9 | """Allow user to draw over a node link, call the node pie and insert the chosen node into the link"""
10 |
11 | @classmethod
12 | def poll(cls, context):
13 | if not context.space_data or context.area.type != "NODE_EDITOR":
14 | return False
15 | return True
16 |
17 | def invoke(self, context, event):
18 | self.quit = False
19 | self.start_names = {n.name for n in context.space_data.edit_tree.nodes}
20 |
21 | # Call the reroute operator as a quick way to get which links have been dragged over
22 | bpy.ops.node.add_reroute("INVOKE_DEFAULT")
23 | self.cursor.set_icon(self.cursor.PICK_AREA)
24 | return self.start_modal()
25 |
26 | def modal(self, context: Context, event: Event):
27 | node_tree: NodeTree = context.space_data.edit_tree
28 | self.cursor.set_icon(self.cursor.DOT)
29 |
30 | if event.type in {"RIGHTMOUSE", "ESC"} or self.quit:
31 | nodes = node_tree.nodes
32 | end_names = {n.name for n in nodes}
33 | new_nodes = list(end_names - self.start_names)
34 | if new_nodes:
35 | # Remove the newly created reroutes
36 | for new_node in new_nodes[::-1]:
37 | new_node = nodes[new_node]
38 | from_socket = new_node.inputs[0].links[0].from_socket
39 | to_sockets = [l.to_socket for l in new_node.outputs[0].links]
40 |
41 | for s in to_sockets:
42 | node_tree.links.new(from_socket, s)
43 | nodes.remove(new_node)
44 |
45 | # Call the node pie
46 | NPIE_MT_node_pie.from_socket = from_socket
47 | NPIE_MT_node_pie.to_sockets = to_sockets
48 | bpy.ops.node_pie.call_node_pie("INVOKE_DEFAULT", reset_args=False)
49 |
50 | self.cursor.reset_icon()
51 | return self.FINISHED
52 |
53 | elif event.type == "LEFTMOUSE" and event.value == "RELEASE":
54 | self.quit = True
55 | return self.PASS_THROUGH
56 |
57 | return self.PASS_THROUGH
--------------------------------------------------------------------------------
/node_pie/operators/op_open_definition_file.py:
--------------------------------------------------------------------------------
1 | from bpy.props import BoolProperty
2 | from ..npie_btypes import BOperator
3 | from ..npie_constants import NODE_DEF_BASE_FILE, NODE_DEF_DIR, NODE_DEF_EXAMPLE_FILE
4 | from ..npie_helpers import get_all_def_files
5 |
6 | import shutil
7 | import webbrowser
8 |
9 |
10 | @BOperator("node_pie")
11 | class NPIE_OT_open_definition_file(BOperator.type):
12 | """Open the node pie definition file for this node tree"""
13 |
14 | example: BoolProperty()
15 |
16 | @classmethod
17 | def poll(cls, context):
18 | if not context.space_data or context.area.type != "NODE_EDITOR":
19 | return False
20 | return True
21 |
22 | def execute(self, context):
23 | if self.example:
24 | file = NODE_DEF_EXAMPLE_FILE
25 | else:
26 | files = get_all_def_files()
27 | for file in files:
28 | if file.name == f"{context.space_data.tree_type}.jsonc":
29 | break
30 | else:
31 | file = NODE_DEF_DIR / "user" / f"{context.space_data.tree_type}.jsonc"
32 | if not file.exists():
33 | shutil.copyfile(NODE_DEF_BASE_FILE, file)
34 |
35 | webbrowser.open(file)
36 | return {"FINISHED"}
--------------------------------------------------------------------------------
/node_pie/operators/op_reset_popularity.py:
--------------------------------------------------------------------------------
1 | from bpy.types import UILayout
2 | from ..npie_btypes import BOperator
3 | from ..npie_constants import POPULARITY_FILE
4 |
5 |
6 | @BOperator("node_pie")
7 | class NPIE_OT_reset_popularity(BOperator.type):
8 | """Reset the popularity of all nodes back to zero"""
9 |
10 | def invoke(self, context, event):
11 | return context.window_manager.invoke_props_dialog(self)
12 |
13 | def draw(self, context):
14 | layout: UILayout = self.layout
15 | box = layout.box()
16 | box.alert = True
17 | col = box.column(align=True)
18 | col.scale_y = .8
19 | row = col.row(align=True)
20 | row.alignment = "CENTER"
21 | row.label(text="Warning!")
22 | row = col.row(align=True)
23 | row.alignment = "CENTER"
24 | row.label(text="This will reset the popularity of all nodes back to zero.")
25 | row = col.row(align=True)
26 | row.alignment = "CENTER"
27 | row.label(text="This cannot be undone.")
28 | row = col.row(align=True)
29 | row.alignment = "CENTER"
30 | row.label(text="Continue anyway?")
31 |
32 | def execute(self, context):
33 | with open(POPULARITY_FILE, "w"):
34 | pass
35 | self.report({"INFO"}, "Node popularity successfully reset")
36 | return {"FINISHED"}
--------------------------------------------------------------------------------
/node_pie/operators/op_show_info.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 |
3 | from bpy.props import IntProperty, BoolProperty, StringProperty
4 | from bpy.types import Context, UILayout
5 | from ..npie_btypes import BOperator
6 | import blf
7 |
8 |
9 | def wrap_text(
10 | context: Context,
11 | text: str,
12 | layout: UILayout,
13 | centered: bool = False,
14 | width=0,
15 | splitter=None,
16 | ) -> list[str]:
17 | """Take a string and draw it over multiple lines so that it is never concatenated."""
18 | return_text = []
19 | row_text = ''
20 |
21 | width = width or context.region.width
22 | system = context.preferences.system
23 | ui_scale = system.ui_scale
24 | width = (4 / (5 * ui_scale)) * width
25 |
26 | # dpi = 72 if system.ui_scale >= 1 else system.dpi
27 | blf.size(0, 11)
28 |
29 | for word in text.split(splitter):
30 | if word == "":
31 | return_text.append(row_text)
32 | row_text = ""
33 | continue
34 | word = f' {word}'
35 | line_len, _ = blf.dimensions(0, row_text + word)
36 |
37 | if line_len <= (width - 16):
38 | row_text += word
39 | else:
40 | return_text.append(row_text)
41 | row_text = word
42 |
43 | if row_text:
44 | return_text.append(row_text)
45 |
46 | for text in return_text:
47 | row = layout.row()
48 | if centered:
49 | row.alignment = "CENTER"
50 | row.label(text=text)
51 |
52 | return return_text
53 |
54 |
55 | @BOperator("npie")
56 | class NPIE_OT_show_info(BOperator.type):
57 |
58 | title: StringProperty()
59 |
60 | message: StringProperty()
61 |
62 | icon: StringProperty()
63 |
64 | show_content: BoolProperty()
65 |
66 | width: IntProperty(default=300)
67 |
68 | def invoke(self, context, event):
69 | return self.call_popup(self.width)
70 |
71 | def draw(self, context):
72 | layout = self.layout
73 | column = layout.column(align=True)
74 | box = column.box().row(align=True)
75 | box.alignment = "CENTER"
76 | offset = "" if self.icon else " "
77 | box.label(text=self.title + offset, icon=self.icon)
78 |
79 | box = column.box().column(align=True)
80 | message = self.message.replace(" ", "").replace("\n", " ")
81 | wrap_text(context, message, box, width=self.width * 1.25, splitter=" ")
82 | # wrap_text(None, context, message, box, width=self.width * 1.25)
83 |
84 |
85 | @dataclass
86 | class InfoSnippet():
87 |
88 | title: str
89 | message: str
90 | icon: str = "NONE"
91 |
92 | def draw(self, layout: UILayout, icon_override=""):
93 | op = layout.operator(NPIE_OT_show_info.bl_idname, text="", icon=icon_override or "INFO")
94 | op.title = self.title
95 | op.message = self.message
96 | op.icon = self.icon
97 |
98 |
99 | class InfoSnippets():
100 |
101 | link_drag = InfoSnippet(
102 | "Link drag",
103 | """\
104 | If you press the pie menu keyboard shortcut over a node socket, the node you select will be automatically \
105 | connected to that socket.\n
106 |
107 | NOTE: The node socket hitboxes can be innacurate at high or low UI scales, so if you use a UI scale that is \
108 | either especially high or especially low, you may want to turn on "Draw debug lines", and then adjust the \
109 | "Socket separation" parameter until they line up again.
110 | The problem will be most obvious with large nodes like the Principled BSDF or the Raycast node.
111 | """,
112 | icon="NODE",
113 | )
114 |
--------------------------------------------------------------------------------
/node_pie/shaders/2D_line_antialiased_frag.glsl:
--------------------------------------------------------------------------------
1 | #ifndef is_compile
2 | uniform vec4 color;
3 |
4 | in vec2 frag_uvs;
5 | out vec4 fragColor;
6 | #endif
7 |
8 | void main() {
9 |
10 | vec4 main_color = color;
11 | // Gradient going from 0 at edges to 1 in middle
12 | main_color.a *= 1 - abs(frag_uvs.y - .5f) * 2;
13 | // Antialias
14 | main_color.a = main_color.a / fwidth(main_color.a);
15 |
16 | // color = vec4(color.a, color.a, color.a, 1);
17 | fragColor = main_color;
18 | // fragColor = blender_srgb_to_framebuffer_space(color);
19 | }
--------------------------------------------------------------------------------
/node_pie/shaders/2D_vert.glsl:
--------------------------------------------------------------------------------
1 | // Use a preprocessor to remove the types at compile time.
2 | // I'm just using them for type hinting currently
3 |
4 | # ifndef is_compile
5 | uniform mat4 ModelViewProjectionMatrix;
6 |
7 | in vec2 pos;
8 | in vec2 uvs;
9 | out vec2 frag_uvs;
10 | # endif
11 |
12 | void main() {
13 | frag_uvs = uvs;
14 | gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0f, 1.0f);
15 | }
--------------------------------------------------------------------------------