space_node.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # <pep8 compliant>
  19. import bpy
  20. import nodeitems_utils
  21. from bpy.types import Header, Menu, Panel
  22. from bpy.app.translations import pgettext_iface as iface_
  23. from bpy.app.translations import contexts as i18n_contexts
  24. from bl_ui.utils import PresetPanel
  25. from bl_ui.properties_grease_pencil_common import (
  26. AnnotationDataPanel,
  27. GreasePencilToolsPanel,
  28. )
  29. from bl_ui.space_toolsystem_common import (
  30. ToolActivePanelHelper,
  31. )
  32. from bl_ui.properties_material import (
  33. EEVEE_MATERIAL_PT_settings,
  34. MATERIAL_PT_viewport
  35. )
  36. from bl_ui.properties_world import (
  37. WORLD_PT_viewport_display
  38. )
  39. from bl_ui.properties_data_light import (
  40. DATA_PT_light,
  41. DATA_PT_EEVEE_light,
  42. )
  43. class NODE_HT_header(Header):
  44. bl_space_type = 'NODE_EDITOR'
  45. def draw(self, context):
  46. layout = self.layout
  47. scene = context.scene
  48. snode = context.space_data
  49. snode_id = snode.id
  50. id_from = snode.id_from
  51. tool_settings = context.tool_settings
  52. is_compositor = snode.tree_type == 'CompositorNodeTree'
  53. layout.template_header()
  54. # Now expanded via the 'ui_type'
  55. # layout.prop(snode, "tree_type", text="")
  56. if snode.tree_type == 'ShaderNodeTree':
  57. layout.prop(snode, "shader_type", text="")
  58. ob = context.object
  59. if snode.shader_type == 'OBJECT' and ob:
  60. ob_type = ob.type
  61. NODE_MT_editor_menus.draw_collapsible(context, layout)
  62. # No shader nodes for Eevee lights
  63. if snode_id and not (context.engine == 'BLENDER_EEVEE' and ob_type == 'LIGHT'):
  64. row = layout.row()
  65. row.prop(snode_id, "use_nodes")
  66. layout.separator_spacer()
  67. types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'GPENCIL'}
  68. # disable material slot buttons when pinned, cannot find correct slot within id_from (#36589)
  69. # disable also when the selected object does not support materials
  70. has_material_slots = not snode.pin and ob_type in types_that_support_material
  71. if ob_type != 'LIGHT':
  72. row = layout.row()
  73. row.enabled = has_material_slots
  74. row.ui_units_x = 4
  75. row.popover(panel="NODE_PT_material_slots")
  76. row = layout.row()
  77. row.enabled = has_material_slots
  78. # Show material.new when no active ID/slot exists
  79. if not id_from and ob_type in types_that_support_material:
  80. row.template_ID(ob, "active_material", new="material.new")
  81. # Material ID, but not for Lights
  82. if id_from and ob_type != 'LIGHT':
  83. row.template_ID(id_from, "active_material", new="material.new")
  84. if snode.shader_type == 'WORLD':
  85. NODE_MT_editor_menus.draw_collapsible(context, layout)
  86. if snode_id:
  87. row = layout.row()
  88. row.prop(snode_id, "use_nodes")
  89. layout.separator_spacer()
  90. row = layout.row()
  91. row.enabled = not snode.pin
  92. row.template_ID(scene, "world", new="world.new")
  93. if snode.shader_type == 'LINESTYLE':
  94. view_layer = context.view_layer
  95. lineset = view_layer.freestyle_settings.linesets.active
  96. if lineset is not None:
  97. NODE_MT_editor_menus.draw_collapsible(context, layout)
  98. if snode_id:
  99. row = layout.row()
  100. row.prop(snode_id, "use_nodes")
  101. layout.separator_spacer()
  102. row = layout.row()
  103. row.enabled = not snode.pin
  104. row.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
  105. elif snode.tree_type == 'TextureNodeTree':
  106. layout.prop(snode, "texture_type", text="")
  107. NODE_MT_editor_menus.draw_collapsible(context, layout)
  108. if snode_id:
  109. layout.prop(snode_id, "use_nodes")
  110. layout.separator_spacer()
  111. if id_from:
  112. if snode.texture_type == 'BRUSH':
  113. layout.template_ID(id_from, "texture", new="texture.new")
  114. else:
  115. layout.template_ID(id_from, "active_texture", new="texture.new")
  116. elif snode.tree_type == 'CompositorNodeTree':
  117. NODE_MT_editor_menus.draw_collapsible(context, layout)
  118. if snode_id:
  119. layout.prop(snode_id, "use_nodes")
  120. else:
  121. # Custom node tree is edited as independent ID block
  122. NODE_MT_editor_menus.draw_collapsible(context, layout)
  123. layout.separator_spacer()
  124. layout.template_ID(snode, "node_tree", new="node.new_node_tree")
  125. # Put pin next to ID block
  126. if not is_compositor:
  127. layout.prop(snode, "pin", text="", emboss=False)
  128. layout.separator_spacer()
  129. # Put pin on the right for Compositing
  130. if is_compositor:
  131. layout.prop(snode, "pin", text="", emboss=False)
  132. layout.operator("node.tree_path_parent", text="", icon='FILE_PARENT')
  133. # Backdrop
  134. if is_compositor:
  135. row=layout.row(align=True)
  136. row.prop(snode, "show_backdrop", toggle=True)
  137. sub=row.row(align=True)
  138. sub.active = snode.show_backdrop
  139. sub.prop(snode, "backdrop_channels", icon_only=True, text="", expand=True)
  140. # Snap
  141. row = layout.row(align=True)
  142. row.prop(tool_settings, "use_snap", text="")
  143. row.prop(tool_settings, "snap_node_element", icon_only=True)
  144. if tool_settings.snap_node_element != 'GRID':
  145. row.prop(tool_settings, "snap_target", text="")
  146. class NODE_MT_editor_menus(Menu):
  147. bl_idname = "NODE_MT_editor_menus"
  148. bl_label = ""
  149. def draw(self, _context):
  150. layout = self.layout
  151. layout.menu("NODE_MT_view")
  152. layout.menu("NODE_MT_select")
  153. layout.menu("NODE_MT_add")
  154. layout.menu("NODE_MT_node")
  155. class NODE_MT_add(bpy.types.Menu):
  156. bl_space_type = 'NODE_EDITOR'
  157. bl_label = "Add"
  158. bl_translation_context = i18n_contexts.operator_default
  159. def draw(self, context):
  160. layout = self.layout
  161. layout.operator_context = 'INVOKE_DEFAULT'
  162. props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
  163. props.use_transform = True
  164. layout.separator()
  165. # actual node submenus are defined by draw functions from node categories
  166. nodeitems_utils.draw_node_categories_menu(self, context)
  167. class NODE_MT_view(Menu):
  168. bl_label = "View"
  169. def draw(self, context):
  170. layout = self.layout
  171. snode = context.space_data
  172. layout.prop(snode, "show_region_toolbar")
  173. layout.prop(snode, "show_region_ui")
  174. layout.separator()
  175. # Auto-offset nodes (called "insert_offset" in code)
  176. layout.prop(snode, "use_insert_offset")
  177. layout.separator()
  178. layout.operator("view2d.zoom_in")
  179. layout.operator("view2d.zoom_out")
  180. layout.separator()
  181. layout.operator("node.view_selected")
  182. layout.operator("node.view_all")
  183. if context.space_data.show_backdrop:
  184. layout.separator()
  185. layout.operator("node.backimage_move", text="Backdrop Move")
  186. layout.operator("node.backimage_zoom", text="Backdrop Zoom In").factor = 1.2
  187. layout.operator("node.backimage_zoom", text="Backdrop Zoom Out").factor = 1.0 / 1.2
  188. layout.operator("node.backimage_fit", text="Fit Backdrop to Available Space")
  189. layout.separator()
  190. layout.menu("INFO_MT_area")
  191. class NODE_MT_select(Menu):
  192. bl_label = "Select"
  193. def draw(self, _context):
  194. layout = self.layout
  195. layout.operator("node.select_box").tweak = False
  196. layout.operator("node.select_circle")
  197. layout.separator()
  198. layout.operator("node.select_all").action = 'TOGGLE'
  199. layout.operator("node.select_all", text="Inverse").action = 'INVERT'
  200. layout.operator("node.select_linked_from")
  201. layout.operator("node.select_linked_to")
  202. layout.separator()
  203. layout.operator("node.select_grouped").extend = False
  204. layout.operator("node.select_same_type_step", text="Activate Same Type Previous").prev = True
  205. layout.operator("node.select_same_type_step", text="Activate Same Type Next").prev = False
  206. layout.separator()
  207. layout.operator("node.find_node")
  208. class NODE_MT_node(Menu):
  209. bl_label = "Node"
  210. def draw(self, _context):
  211. layout = self.layout
  212. layout.operator("transform.translate")
  213. layout.operator("transform.rotate")
  214. layout.operator("transform.resize")
  215. layout.separator()
  216. layout.operator("node.clipboard_copy", text="Copy")
  217. layout.operator("node.clipboard_paste", text="Paste")
  218. layout.operator("node.duplicate_move")
  219. layout.operator("node.delete")
  220. layout.operator("node.delete_reconnect")
  221. layout.separator()
  222. layout.operator("node.join", text="Join in New Frame")
  223. layout.operator("node.detach", text="Remove from Frame")
  224. layout.separator()
  225. layout.operator("node.link_make").replace = False
  226. layout.operator("node.link_make", text="Make and Replace Links").replace = True
  227. layout.operator("node.links_cut")
  228. layout.operator("node.links_detach")
  229. layout.separator()
  230. layout.operator("node.group_edit").exit = False
  231. layout.operator("node.group_ungroup")
  232. layout.operator("node.group_make")
  233. layout.operator("node.group_insert")
  234. layout.separator()
  235. layout.operator("node.hide_toggle")
  236. layout.operator("node.mute_toggle")
  237. layout.operator("node.preview_toggle")
  238. layout.operator("node.hide_socket_toggle")
  239. layout.operator("node.options_toggle")
  240. layout.operator("node.collapse_hide_unused_toggle")
  241. layout.separator()
  242. layout.operator("node.read_viewlayers")
  243. class NODE_PT_active_tool(ToolActivePanelHelper, Panel):
  244. bl_space_type = 'NODE_EDITOR'
  245. bl_region_type = 'UI'
  246. bl_category = "Tool"
  247. class NODE_PT_material_slots(Panel):
  248. bl_space_type = 'NODE_EDITOR'
  249. bl_region_type = 'HEADER'
  250. bl_label = "Slot"
  251. bl_ui_units_x = 12
  252. def draw_header(self, context):
  253. ob = context.object
  254. self.bl_label = (
  255. "Slot " + str(ob.active_material_index + 1) if ob.material_slots else
  256. "Slot"
  257. )
  258. # Duplicate part of 'EEVEE_MATERIAL_PT_context_material'.
  259. def draw(self, context):
  260. layout = self.layout
  261. row = layout.row()
  262. col = row.column()
  263. ob = context.object
  264. col.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index")
  265. col = row.column(align=True)
  266. col.operator("object.material_slot_add", icon='ADD', text="")
  267. col.operator("object.material_slot_remove", icon='REMOVE', text="")
  268. col.separator()
  269. col.menu("MATERIAL_MT_context_menu", icon='DOWNARROW_HLT', text="")
  270. if len(ob.material_slots) > 1:
  271. col.separator()
  272. col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
  273. col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
  274. class NODE_PT_node_color_presets(PresetPanel, Panel):
  275. """Predefined node color"""
  276. bl_label = "Color Presets"
  277. preset_subdir = "node_color"
  278. preset_operator = "script.execute_preset"
  279. preset_add_operator = "node.node_color_preset_add"
  280. class NODE_MT_node_color_context_menu(Menu):
  281. bl_label = "Node Color Specials"
  282. def draw(self, _context):
  283. layout = self.layout
  284. layout.operator("node.node_copy_color", icon='COPY_ID')
  285. class NODE_MT_context_menu(Menu):
  286. bl_label = "Node Context Menu"
  287. def draw(self, context):
  288. layout = self.layout
  289. selected_nodes_len = len(context.selected_nodes)
  290. # If nothing is selected
  291. # (disabled for now until it can be made more useful).
  292. '''
  293. if selected_nodes_len == 0:
  294. layout.operator_context = 'INVOKE_DEFAULT'
  295. layout.menu("NODE_MT_add")
  296. layout.operator("node.clipboard_paste", text="Paste")
  297. return
  298. '''
  299. # If something is selected
  300. layout.operator_context = 'INVOKE_DEFAULT'
  301. layout.operator("node.duplicate_move")
  302. layout.operator("node.delete")
  303. layout.operator("node.clipboard_copy", text="Copy")
  304. layout.operator("node.clipboard_paste", text="Paste")
  305. layout.operator_context = 'EXEC_DEFAULT'
  306. layout.operator("node.delete_reconnect")
  307. if selected_nodes_len > 1:
  308. layout.separator()
  309. layout.operator("node.link_make").replace = False
  310. layout.operator("node.link_make", text="Make and Replace Links").replace = True
  311. layout.operator("node.links_detach")
  312. layout.separator()
  313. layout.operator("node.group_make", text="Group")
  314. layout.operator("node.group_ungroup", text="Ungroup")
  315. layout.operator("node.group_edit").exit = False
  316. layout.separator()
  317. layout.operator("node.hide_toggle")
  318. layout.operator("node.mute_toggle")
  319. layout.operator("node.preview_toggle")
  320. layout.operator("node.hide_socket_toggle")
  321. layout.operator("node.options_toggle")
  322. layout.operator("node.collapse_hide_unused_toggle")
  323. class NODE_PT_active_node_generic(Panel):
  324. bl_space_type = 'NODE_EDITOR'
  325. bl_region_type = 'UI'
  326. bl_category = "Item"
  327. bl_label = "Node"
  328. @classmethod
  329. def poll(cls, context):
  330. return context.active_node is not None
  331. def draw(self, context):
  332. layout = self.layout
  333. node = context.active_node
  334. layout.prop(node, "name", icon='NODE')
  335. layout.prop(node, "label", icon='NODE')
  336. class NODE_PT_active_node_color(Panel):
  337. bl_space_type = 'NODE_EDITOR'
  338. bl_region_type = 'UI'
  339. bl_category = "Item"
  340. bl_label = "Color"
  341. bl_options = {'DEFAULT_CLOSED'}
  342. bl_parent_id = 'NODE_PT_active_node_generic'
  343. @classmethod
  344. def poll(cls, context):
  345. return context.active_node is not None
  346. def draw_header(self, context):
  347. node = context.active_node
  348. self.layout.prop(node, "use_custom_color", text="")
  349. def draw_header_preset(self, _context):
  350. NODE_PT_node_color_presets.draw_panel_header(self.layout)
  351. def draw(self, context):
  352. layout = self.layout
  353. node = context.active_node
  354. layout.enabled = node.use_custom_color
  355. row = layout.row()
  356. row.prop(node, "color", text="")
  357. row.menu("NODE_MT_node_color_context_menu", text="", icon='DOWNARROW_HLT')
  358. class NODE_PT_active_node_properties(Panel):
  359. bl_space_type = 'NODE_EDITOR'
  360. bl_region_type = 'UI'
  361. bl_category = "Item"
  362. bl_label = "Properties"
  363. bl_options = {'DEFAULT_CLOSED'}
  364. @classmethod
  365. def poll(cls, context):
  366. return context.active_node is not None
  367. def draw(self, context):
  368. layout = self.layout
  369. node = context.active_node
  370. # set "node" context pointer for the panel layout
  371. layout.context_pointer_set("node", node)
  372. if hasattr(node, "draw_buttons_ext"):
  373. node.draw_buttons_ext(context, layout)
  374. elif hasattr(node, "draw_buttons"):
  375. node.draw_buttons(context, layout)
  376. # XXX this could be filtered further to exclude socket types
  377. # which don't have meaningful input values (e.g. cycles shader)
  378. value_inputs = [socket for socket in node.inputs if socket.enabled and not socket.is_linked]
  379. if value_inputs:
  380. layout.separator()
  381. layout.label(text="Inputs:")
  382. for socket in value_inputs:
  383. row = layout.row()
  384. socket.draw(context, row, node, iface_(socket.name, socket.bl_rna.translation_context))
  385. class NODE_PT_texture_mapping(Panel):
  386. bl_space_type = 'NODE_EDITOR'
  387. bl_region_type = 'UI'
  388. bl_category = "Item"
  389. bl_label = "Texture Mapping"
  390. bl_options = {'DEFAULT_CLOSED'}
  391. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  392. @classmethod
  393. def poll(cls, context):
  394. node = context.active_node
  395. return node and hasattr(node, "texture_mapping") and (context.engine in cls.COMPAT_ENGINES)
  396. def draw(self, context):
  397. layout = self.layout
  398. layout.use_property_split = True
  399. layout.use_property_decorate = False # No animation.
  400. node = context.active_node
  401. mapping = node.texture_mapping
  402. layout.prop(mapping, "vector_type")
  403. layout.separator()
  404. col = layout.column(align=True)
  405. col.prop(mapping, "mapping_x", text="Projection X")
  406. col.prop(mapping, "mapping_y", text="Y")
  407. col.prop(mapping, "mapping_z", text="Z")
  408. layout.separator()
  409. layout.prop(mapping, "translation")
  410. layout.prop(mapping, "rotation")
  411. layout.prop(mapping, "scale")
  412. # Node Backdrop options
  413. class NODE_PT_backdrop(Panel):
  414. bl_space_type = 'NODE_EDITOR'
  415. bl_region_type = 'UI'
  416. bl_category = "View"
  417. bl_label = "Backdrop"
  418. @classmethod
  419. def poll(cls, context):
  420. snode = context.space_data
  421. return snode.tree_type == 'CompositorNodeTree'
  422. def draw_header(self, context):
  423. snode = context.space_data
  424. self.layout.prop(snode, "show_backdrop", text="")
  425. def draw(self, context):
  426. layout = self.layout
  427. layout.use_property_split = True
  428. layout.use_property_decorate = False
  429. snode = context.space_data
  430. layout.active = snode.show_backdrop
  431. col = layout.column()
  432. col.prop(snode, "backdrop_channels", text="Channels")
  433. col.prop(snode, "backdrop_zoom", text="Zoom")
  434. col.prop(snode, "backdrop_offset", text="Offset")
  435. col.separator()
  436. col.operator("node.backimage_move", text="Move")
  437. col.operator("node.backimage_fit", text="Fit")
  438. class NODE_PT_quality(bpy.types.Panel):
  439. bl_space_type = 'NODE_EDITOR'
  440. bl_region_type = 'UI'
  441. bl_category = "Options"
  442. bl_label = "Performance"
  443. @classmethod
  444. def poll(cls, context):
  445. snode = context.space_data
  446. return snode.tree_type == 'CompositorNodeTree' and snode.node_tree is not None
  447. def draw(self, context):
  448. layout = self.layout
  449. layout.use_property_split = True
  450. layout.use_property_decorate = False
  451. snode = context.space_data
  452. tree = snode.node_tree
  453. col = layout.column()
  454. col.prop(tree, "render_quality", text="Render")
  455. col.prop(tree, "edit_quality", text="Edit")
  456. col.prop(tree, "chunk_size")
  457. col = layout.column()
  458. col.prop(tree, "use_opencl")
  459. col.prop(tree, "use_groupnode_buffer")
  460. col.prop(tree, "use_two_pass")
  461. col.prop(tree, "use_viewer_border")
  462. col.separator()
  463. col.prop(snode, "use_auto_render")
  464. class NODE_UL_interface_sockets(bpy.types.UIList):
  465. def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
  466. socket = item
  467. color = socket.draw_color(context)
  468. if self.layout_type in {'DEFAULT', 'COMPACT'}:
  469. row = layout.row(align=True)
  470. # inputs get icon on the left
  471. if not socket.is_output:
  472. row.template_node_socket(color=color)
  473. row.prop(socket, "name", text="", emboss=False, icon_value=icon)
  474. # outputs get icon on the right
  475. if socket.is_output:
  476. row.template_node_socket(color=color)
  477. elif self.layout_type == 'GRID':
  478. layout.alignment = 'CENTER'
  479. layout.template_node_socket(color=color)
  480. # Grease Pencil properties
  481. class NODE_PT_grease_pencil(AnnotationDataPanel, Panel):
  482. bl_space_type = 'NODE_EDITOR'
  483. bl_region_type = 'UI'
  484. bl_category = "View"
  485. bl_options = {'DEFAULT_CLOSED'}
  486. # NOTE: this is just a wrapper around the generic GP Panel
  487. @classmethod
  488. def poll(cls, context):
  489. snode = context.space_data
  490. return snode is not None and snode.node_tree is not None
  491. class NODE_PT_grease_pencil_tools(GreasePencilToolsPanel, Panel):
  492. bl_space_type = 'NODE_EDITOR'
  493. bl_region_type = 'UI'
  494. bl_category = "View"
  495. bl_options = {'DEFAULT_CLOSED'}
  496. # NOTE: this is just a wrapper around the generic GP tools panel
  497. # It contains access to some essential tools usually found only in
  498. # toolbar, but which may not necessarily be open
  499. def node_draw_tree_view(_layout, _context):
  500. pass
  501. # Adapt properties editor panel to display in node editor. We have to
  502. # copy the class rather than inherit due to the way bpy registration works.
  503. def node_panel(cls):
  504. node_cls = type('NODE_' + cls.__name__, cls.__bases__, dict(cls.__dict__))
  505. node_cls.bl_space_type = 'NODE_EDITOR'
  506. node_cls.bl_region_type = 'UI'
  507. node_cls.bl_category = "Options"
  508. if hasattr(node_cls, 'bl_parent_id'):
  509. node_cls.bl_parent_id = 'NODE_' + node_cls.bl_parent_id
  510. return node_cls
  511. classes = (
  512. NODE_HT_header,
  513. NODE_MT_editor_menus,
  514. NODE_MT_add,
  515. NODE_MT_view,
  516. NODE_MT_select,
  517. NODE_MT_node,
  518. NODE_MT_node_color_context_menu,
  519. NODE_MT_context_menu,
  520. NODE_PT_material_slots,
  521. NODE_PT_node_color_presets,
  522. NODE_PT_active_node_generic,
  523. NODE_PT_active_node_color,
  524. NODE_PT_active_node_properties,
  525. NODE_PT_texture_mapping,
  526. NODE_PT_active_tool,
  527. NODE_PT_backdrop,
  528. NODE_PT_quality,
  529. NODE_PT_grease_pencil,
  530. NODE_PT_grease_pencil_tools,
  531. NODE_UL_interface_sockets,
  532. node_panel(EEVEE_MATERIAL_PT_settings),
  533. node_panel(MATERIAL_PT_viewport),
  534. node_panel(WORLD_PT_viewport_display),
  535. node_panel(DATA_PT_light),
  536. node_panel(DATA_PT_EEVEE_light),
  537. )
  538. if __name__ == "__main__": # only for live edit.
  539. from bpy.utils import register_class
  540. for cls in classes:
  541. register_class(cls)