Nodes: Use plain menus for geometry nodes add menu

At the cost of slightly more boilerplate code, we can avoid the `NodeItem`
and `NodeCategory` abstractions used to build the node add menu.
This makes the menus more flexible and more obvious, which will
make them easier to extend with assets.

The identifiers for the new menu types are inconsistent with regular
class naming for backwards compatibility with the old "category"
menu naming.

Also adds an item for the "Self Object" node missed in dd5131bd700c.

Differential Revision: https://developer.blender.org/D15973
This commit is contained in:
Hans Goudey 2022-09-26 12:36:13 -05:00
parent 8a6dc0fac7
commit 837144b457
5 changed files with 466 additions and 307 deletions

@ -9,6 +9,8 @@ if "bpy" in locals():
del reload
_modules = [
"node_add_menu",
"node_add_menu_geometry",
"properties_animviz",
"properties_constraint",
"properties_data_armature",

@ -0,0 +1,63 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu
from bpy.app.translations import (
pgettext_iface as iface_,
contexts as i18n_contexts,
)
def add_node_type(layout, node_type, *, label=None):
"""Add a node type to a menu."""
bl_rna = bpy.types.Node.bl_rna_get_subclass(node_type)
if not label:
label = bl_rna.name if bl_rna else iface_("Unknown")
translation_context = bl_rna.translation_context if bl_rna else i18n_contexts.default
props = layout.operator("node.add_node", text=label, text_ctxt=translation_context)
props.type = node_type
props.use_transform = True
return props
def draw_node_group_add_menu(context, layout):
"""Add items to the layout used for interacting with node groups."""
space_node = context.space_data
node_tree = space_node.edit_tree
all_node_groups = context.blend_data.node_groups
layout.operator("node.group_make")
layout.operator("node.group_ungroup")
if node_tree in all_node_groups.values():
layout.separator()
add_node_type(layout, "NodeGroupInput")
add_node_type(layout, "NodeGroupOutput")
if node_tree:
from nodeitems_builtins import node_tree_group_type
def contains_group(nodetree, group):
if nodetree == group:
return True
for node in nodetree.nodes:
if node.bl_idname in node_tree_group_type.values() and node.node_tree is not None:
if contains_group(node.node_tree, group):
return True
return False
groups = [group for group in context.blend_data.node_groups if group.bl_idname == node_tree.bl_idname and
not contains_group(group, node_tree) and
not group.name.startswith('.')]
if groups:
layout.separator()
for group in groups:
props = add_node_type(layout, node_tree_group_type[group.bl_idname], label=group.name)
ops = props.settings.add()
ops.name = "node_tree"
ops.value = "bpy.data.node_groups[%r]" % group.name
classes = (
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

@ -0,0 +1,395 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu
from bl_ui import node_add_menu
from bpy.app.translations import pgettext_iface as iface_
class NODE_MT_geometry_node_GEO_ATTRIBUTE(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_ATTRIBUTE"
bl_label = "Attribute"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeCaptureAttribute")
node_add_menu.add_node_type(layout, "GeometryNodeAttributeDomainSize")
node_add_menu.add_node_type(layout, "GeometryNodeAttributeStatistic")
node_add_menu.add_node_type(layout, "GeometryNodeAttributeTransfer")
node_add_menu.add_node_type(layout, "GeometryNodeRemoveAttribute")
node_add_menu.add_node_type(layout, "GeometryNodeStoreNamedAttribute")
class NODE_MT_geometry_node_GEO_COLOR(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_COLOR"
bl_label = "Color"
def draw(self, _context):
layout = self.layout
props = node_add_menu.add_node_type(layout, "ShaderNodeMix", label=iface_("Mix Color"))
ops = props.settings.add()
ops.name = "data_type"
ops.value = "'RGBA'"
node_add_menu.add_node_type(layout, "ShaderNodeRGBCurve")
node_add_menu.add_node_type(layout, "ShaderNodeValToRGB")
node_add_menu.add_node_type(layout, "FunctionNodeSeparateColor")
node_add_menu.add_node_type(layout, "FunctionNodeCombineColor")
class NODE_MT_geometry_node_GEO_CURVE(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_CURVE"
bl_label = "Curve"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeCurveLength")
node_add_menu.add_node_type(layout, "GeometryNodeCurveToMesh")
node_add_menu.add_node_type(layout, "GeometryNodeCurveToPoints")
node_add_menu.add_node_type(layout, "GeometryNodeDeformCurvesOnSurface")
node_add_menu.add_node_type(layout, "GeometryNodeFillCurve")
node_add_menu.add_node_type(layout, "GeometryNodeFilletCurve")
node_add_menu.add_node_type(layout, "GeometryNodeResampleCurve")
node_add_menu.add_node_type(layout, "GeometryNodeReverseCurve")
node_add_menu.add_node_type(layout, "GeometryNodeSampleCurve")
node_add_menu.add_node_type(layout, "GeometryNodeSubdivideCurve")
node_add_menu.add_node_type(layout, "GeometryNodeTrimCurve")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeInputControlPointNeighbors")
node_add_menu.add_node_type(layout, "GeometryNodeInputCurveHandlePositions")
node_add_menu.add_node_type(layout, "GeometryNodeInputTangent")
node_add_menu.add_node_type(layout, "GeometryNodeInputCurveTilt")
node_add_menu.add_node_type(layout, "GeometryNodeCurveEndpointSelection")
node_add_menu.add_node_type(layout, "GeometryNodeCurveHandleTypeSelection")
node_add_menu.add_node_type(layout, "GeometryNodeInputSplineCyclic")
node_add_menu.add_node_type(layout, "GeometryNodeSplineLength")
node_add_menu.add_node_type(layout, "GeometryNodeSplineParameter")
node_add_menu.add_node_type(layout, "GeometryNodeInputSplineResolution")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeSetCurveRadius")
node_add_menu.add_node_type(layout, "GeometryNodeSetCurveTilt")
node_add_menu.add_node_type(layout, "GeometryNodeSetCurveHandlePositions")
node_add_menu.add_node_type(layout, "GeometryNodeCurveSetHandles")
node_add_menu.add_node_type(layout, "GeometryNodeSetSplineCyclic")
node_add_menu.add_node_type(layout, "GeometryNodeSetSplineResolution")
node_add_menu.add_node_type(layout, "GeometryNodeCurveSplineType")
class NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE"
bl_label = "Curve Primitives"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveLine")
node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveCircle")
node_add_menu.add_node_type(layout, "GeometryNodeCurveStar")
node_add_menu.add_node_type(layout, "GeometryNodeCurveSpiral")
node_add_menu.add_node_type(layout, "GeometryNodeCurveArc")
node_add_menu.add_node_type(layout, "GeometryNodeCurveQuadraticBezier")
node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveQuadrilateral")
node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveBezierSegment")
class NODE_MT_geometry_node_GEO_GEOMETRY(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_GEOMETRY"
bl_label = "Geometry"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeBoundBox")
node_add_menu.add_node_type(layout, "GeometryNodeConvexHull")
node_add_menu.add_node_type(layout, "GeometryNodeDeleteGeometry")
node_add_menu.add_node_type(layout, "GeometryNodeDuplicateElements")
node_add_menu.add_node_type(layout, "GeometryNodeProximity")
node_add_menu.add_node_type(layout, "GeometryNodeGeometryToInstance")
node_add_menu.add_node_type(layout, "GeometryNodeJoinGeometry")
node_add_menu.add_node_type(layout, "GeometryNodeMergeByDistance")
node_add_menu.add_node_type(layout, "GeometryNodeRaycast")
node_add_menu.add_node_type(layout, "GeometryNodeSampleIndex")
node_add_menu.add_node_type(layout, "GeometryNodeSampleNearest")
node_add_menu.add_node_type(layout, "GeometryNodeSeparateComponents")
node_add_menu.add_node_type(layout, "GeometryNodeSeparateGeometry")
node_add_menu.add_node_type(layout, "GeometryNodeTransform")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeSetID")
node_add_menu.add_node_type(layout, "GeometryNodeSetPosition")
class NODE_MT_geometry_node_GEO_INPUT(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_INPUT"
bl_label = "Input"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeInputBool")
node_add_menu.add_node_type(layout, "GeometryNodeCollectionInfo")
node_add_menu.add_node_type(layout, "FunctionNodeInputColor")
node_add_menu.add_node_type(layout, "FunctionNodeInputInt")
node_add_menu.add_node_type(layout, "GeometryNodeIsViewport")
node_add_menu.add_node_type(layout, "GeometryNodeInputMaterial")
node_add_menu.add_node_type(layout, "GeometryNodeObjectInfo")
node_add_menu.add_node_type(layout, "GeometryNodeSelfObject")
node_add_menu.add_node_type(layout, "FunctionNodeInputString")
node_add_menu.add_node_type(layout, "ShaderNodeValue")
node_add_menu.add_node_type(layout, "FunctionNodeInputVector")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeInputID")
node_add_menu.add_node_type(layout, "GeometryNodeInputIndex")
node_add_menu.add_node_type(layout, "GeometryNodeInputNamedAttribute")
node_add_menu.add_node_type(layout, "GeometryNodeInputNormal")
node_add_menu.add_node_type(layout, "GeometryNodeInputPosition")
node_add_menu.add_node_type(layout, "GeometryNodeInputRadius")
node_add_menu.add_node_type(layout, "GeometryNodeInputSceneTime")
class NODE_MT_geometry_node_GEO_INSTANCE(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_INSTANCE"
bl_label = "Instances"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeInstanceOnPoints")
node_add_menu.add_node_type(layout, "GeometryNodeInstancesToPoints")
node_add_menu.add_node_type(layout, "GeometryNodeRealizeInstances")
node_add_menu.add_node_type(layout, "GeometryNodeRotateInstances")
node_add_menu.add_node_type(layout, "GeometryNodeScaleInstances")
node_add_menu.add_node_type(layout, "GeometryNodeTranslateInstances")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceRotation")
node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceScale")
class NODE_MT_geometry_node_GEO_MATERIAL(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_MATERIAL"
bl_label = "Material"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeReplaceMaterial")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeInputMaterialIndex")
node_add_menu.add_node_type(layout, "GeometryNodeMaterialSelection")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeSetMaterial")
node_add_menu.add_node_type(layout, "GeometryNodeSetMaterialIndex")
class NODE_MT_geometry_node_GEO_MESH(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_MESH"
bl_label = "Mesh"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeDualMesh")
node_add_menu.add_node_type(layout, "GeometryNodeEdgePathsToCurves")
node_add_menu.add_node_type(layout, "GeometryNodeEdgePathsToSelection")
node_add_menu.add_node_type(layout, "GeometryNodeExtrudeMesh")
node_add_menu.add_node_type(layout, "GeometryNodeFlipFaces")
node_add_menu.add_node_type(layout, "GeometryNodeMeshBoolean")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToCurve")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToPoints")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToVolume")
node_add_menu.add_node_type(layout, "GeometryNodeSampleNearestSurface")
node_add_menu.add_node_type(layout, "GeometryNodeScaleElements")
node_add_menu.add_node_type(layout, "GeometryNodeSplitEdges")
node_add_menu.add_node_type(layout, "GeometryNodeSubdivideMesh")
node_add_menu.add_node_type(layout, "GeometryNodeSubdivisionSurface")
node_add_menu.add_node_type(layout, "GeometryNodeTriangulate")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshEdgeAngle")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshEdgeNeighbors")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshEdgeVertices")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceArea")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceNeighbors")
node_add_menu.add_node_type(layout, "GeometryNodeMeshFaceSetBoundaries")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceIsPlanar")
node_add_menu.add_node_type(layout, "GeometryNodeInputShadeSmooth")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshIsland")
node_add_menu.add_node_type(layout, "GeometryNodeInputShortestEdgePaths")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshVertexNeighbors")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeSetShadeSmooth")
class NODE_MT_category_PRIMITIVES_MESH(Menu):
bl_idname = "NODE_MT_category_PRIMITIVES_MESH"
bl_label = "Mesh Primitives"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeMeshCircle")
node_add_menu.add_node_type(layout, "GeometryNodeMeshCone")
node_add_menu.add_node_type(layout, "GeometryNodeMeshCube")
node_add_menu.add_node_type(layout, "GeometryNodeMeshCylinder")
node_add_menu.add_node_type(layout, "GeometryNodeMeshGrid")
node_add_menu.add_node_type(layout, "GeometryNodeMeshIcoSphere")
node_add_menu.add_node_type(layout, "GeometryNodeMeshLine")
class NODE_MT_category_GEO_OUTPUT(Menu):
bl_idname = "NODE_MT_category_GEO_OUTPUT"
bl_label = "Output"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeViewer")
class NODE_MT_category_GEO_POINT(Menu):
bl_idname = "NODE_MT_category_GEO_POINT"
bl_label = "Point"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeDistributePointsInVolume")
node_add_menu.add_node_type(layout, "GeometryNodeDistributePointsOnFaces")
node_add_menu.add_node_type(layout, "GeometryNodePoints")
node_add_menu.add_node_type(layout, "GeometryNodePointsToVertices")
node_add_menu.add_node_type(layout, "GeometryNodePointsToVolume")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeSetPointRadius")
class NODE_MT_category_GEO_TEXT(Menu):
bl_idname = "NODE_MT_category_GEO_TEXT"
bl_label = "Text"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeSliceString")
node_add_menu.add_node_type(layout, "GeometryNodeStringJoin")
node_add_menu.add_node_type(layout, "FunctionNodeStringLength")
node_add_menu.add_node_type(layout, "FunctionNodeReplaceString")
node_add_menu.add_node_type(layout, "FunctionNodeValueToString")
node_add_menu.add_node_type(layout, "GeometryNodeStringToCurves")
layout.separator()
node_add_menu.add_node_type(layout, "FunctionNodeInputSpecialCharacters")
class NODE_MT_category_GEO_TEXTURE(Menu):
bl_idname = "NODE_MT_category_GEO_TEXTURE"
bl_label = "Texture"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeTexBrick")
node_add_menu.add_node_type(layout, "ShaderNodeTexChecker")
node_add_menu.add_node_type(layout, "ShaderNodeTexGradient")
node_add_menu.add_node_type(layout, "GeometryNodeImageTexture")
node_add_menu.add_node_type(layout, "ShaderNodeTexMagic")
node_add_menu.add_node_type(layout, "ShaderNodeTexMusgrave")
node_add_menu.add_node_type(layout, "ShaderNodeTexNoise")
node_add_menu.add_node_type(layout, "ShaderNodeTexVoronoi")
node_add_menu.add_node_type(layout, "ShaderNodeTexWave")
node_add_menu.add_node_type(layout, "ShaderNodeTexWhiteNoise")
class NODE_MT_category_GEO_UTILITIES(Menu):
bl_idname = "NODE_MT_category_GEO_UTILITIES"
bl_label = "Utilities"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeAccumulateField")
node_add_menu.add_node_type(layout, "GeometryNodeFieldAtIndex")
node_add_menu.add_node_type(layout, "GeometryNodeFieldOnDomain")
node_add_menu.add_node_type(layout, "ShaderNodeMapRange")
node_add_menu.add_node_type(layout, "ShaderNodeFloatCurve")
node_add_menu.add_node_type(layout, "ShaderNodeClamp")
node_add_menu.add_node_type(layout, "ShaderNodeMath")
node_add_menu.add_node_type(layout, "FunctionNodeBooleanMath")
node_add_menu.add_node_type(layout, "FunctionNodeRotateEuler")
node_add_menu.add_node_type(layout, "FunctionNodeCompare")
node_add_menu.add_node_type(layout, "ShaderNodeMix")
node_add_menu.add_node_type(layout, "FunctionNodeFloatToInt")
node_add_menu.add_node_type(layout, "GeometryNodeSwitch")
node_add_menu.add_node_type(layout, "FunctionNodeRandomValue")
node_add_menu.add_node_type(layout, "FunctionNodeAlignEulerToVector")
class NODE_MT_category_GEO_UV(Menu):
bl_idname = "NODE_MT_category_GEO_UV"
bl_label = "UV"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeUVPackIslands")
node_add_menu.add_node_type(layout, "GeometryNodeUVUnwrap")
class NODE_MT_category_GEO_VECTOR(Menu):
bl_idname = "NODE_MT_category_GEO_VECTOR"
bl_label = "Vector"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeCombineXYZ")
node_add_menu.add_node_type(layout, "ShaderNodeSeparateXYZ")
node_add_menu.add_node_type(layout, "ShaderNodeVectorCurve")
node_add_menu.add_node_type(layout, "ShaderNodeVectorMath")
node_add_menu.add_node_type(layout, "ShaderNodeVectorRotate")
class NODE_MT_category_GEO_VOLUME(Menu):
bl_idname = "NODE_MT_category_GEO_VOLUME"
bl_label = "Volume"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeVolumeCube")
node_add_menu.add_node_type(layout, "GeometryNodeVolumeToMesh")
class NODE_MT_category_GEO_GROUP(Menu):
bl_idname = "NODE_MT_category_GEO_GROUP"
bl_label = "Group"
def draw(self, context):
layout = self.layout
node_add_menu.draw_node_group_add_menu(context, layout)
class NODE_MT_category_GEO_LAYOUT(Menu):
bl_idname = "NODE_MT_category_GEO_LAYOUT"
bl_label = "Layout"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "NodeFrame")
node_add_menu.add_node_type(layout, "NodeReroute")
class NODE_MT_geometry_node_add_all(Menu):
bl_idname = "NODE_MT_geometry_node_add_all"
bl_label = ""
def draw(self, _context):
layout = self.layout
layout.menu("NODE_MT_geometry_node_GEO_ATTRIBUTE")
layout.menu("NODE_MT_geometry_node_GEO_COLOR")
layout.menu("NODE_MT_geometry_node_GEO_CURVE")
layout.menu("NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE")
layout.menu("NODE_MT_geometry_node_GEO_GEOMETRY")
layout.menu("NODE_MT_geometry_node_GEO_INPUT")
layout.menu("NODE_MT_geometry_node_GEO_INSTANCE")
layout.menu("NODE_MT_geometry_node_GEO_MATERIAL")
layout.menu("NODE_MT_geometry_node_GEO_MESH")
layout.menu("NODE_MT_category_PRIMITIVES_MESH")
layout.menu("NODE_MT_category_GEO_OUTPUT")
layout.menu("NODE_MT_category_GEO_POINT")
layout.menu("NODE_MT_category_GEO_TEXT")
layout.menu("NODE_MT_category_GEO_TEXTURE")
layout.menu("NODE_MT_category_GEO_UTILITIES")
layout.menu("NODE_MT_category_GEO_UV")
layout.menu("NODE_MT_category_GEO_VECTOR")
layout.menu("NODE_MT_category_GEO_VOLUME")
layout.menu("NODE_MT_category_GEO_GROUP")
layout.menu("NODE_MT_category_GEO_LAYOUT")
classes = (
NODE_MT_geometry_node_add_all,
NODE_MT_geometry_node_GEO_ATTRIBUTE,
NODE_MT_geometry_node_GEO_COLOR,
NODE_MT_geometry_node_GEO_CURVE,
NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE,
NODE_MT_geometry_node_GEO_GEOMETRY,
NODE_MT_geometry_node_GEO_INPUT,
NODE_MT_geometry_node_GEO_INSTANCE,
NODE_MT_geometry_node_GEO_MATERIAL,
NODE_MT_geometry_node_GEO_MESH,
NODE_MT_category_PRIMITIVES_MESH,
NODE_MT_category_GEO_OUTPUT,
NODE_MT_category_GEO_POINT,
NODE_MT_category_GEO_TEXT,
NODE_MT_category_GEO_TEXTURE,
NODE_MT_category_GEO_UTILITIES,
NODE_MT_category_GEO_UV,
NODE_MT_category_GEO_VECTOR,
NODE_MT_category_GEO_VOLUME,
NODE_MT_category_GEO_GROUP,
NODE_MT_category_GEO_LAYOUT,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

@ -219,10 +219,14 @@ class NODE_MT_add(bpy.types.Menu):
import nodeitems_utils
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
if nodeitems_utils.has_node_categories(context):
snode = context.space_data
if snode.tree_type == 'GeometryNodeTree':
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
layout.separator()
layout.menu_contents("NODE_MT_geometry_node_add_all")
elif nodeitems_utils.has_node_categories(context):
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
props.use_transform = True

@ -39,14 +39,6 @@ class TextureNodeCategory(SortedNodeCategory):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'TextureNodeTree')
class GeometryNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'GeometryNodeTree')
# menu entry for node group tools
def group_tools_draw(_self, layout, _context):
layout.operator("node.group_make")
@ -63,198 +55,6 @@ node_tree_group_type = {
}
# Custom Menu for Geometry Node Curves.
def curve_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeCurveLength")
yield NodeItem("GeometryNodeCurveToMesh")
yield NodeItem("GeometryNodeCurveToPoints")
yield NodeItem("GeometryNodeDeformCurvesOnSurface")
yield NodeItem("GeometryNodeFillCurve")
yield NodeItem("GeometryNodeFilletCurve")
yield NodeItem("GeometryNodeResampleCurve")
yield NodeItem("GeometryNodeReverseCurve")
yield NodeItem("GeometryNodeSampleCurve")
yield NodeItem("GeometryNodeSubdivideCurve")
yield NodeItem("GeometryNodeTrimCurve")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputControlPointNeighbors")
yield NodeItem("GeometryNodeInputCurveHandlePositions")
yield NodeItem("GeometryNodeInputTangent")
yield NodeItem("GeometryNodeInputCurveTilt")
yield NodeItem("GeometryNodeCurveEndpointSelection")
yield NodeItem("GeometryNodeCurveHandleTypeSelection")
yield NodeItem("GeometryNodeInputSplineCyclic")
yield NodeItem("GeometryNodeSplineLength")
yield NodeItem("GeometryNodeSplineParameter")
yield NodeItem("GeometryNodeInputSplineResolution")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeSetCurveRadius")
yield NodeItem("GeometryNodeSetCurveTilt")
yield NodeItem("GeometryNodeSetCurveHandlePositions")
yield NodeItem("GeometryNodeCurveSetHandles")
yield NodeItem("GeometryNodeSetSplineCyclic")
yield NodeItem("GeometryNodeSetSplineResolution")
yield NodeItem("GeometryNodeCurveSplineType")
# Custom Menu for Geometry Node Mesh.
def mesh_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeDualMesh")
yield NodeItem("GeometryNodeEdgePathsToCurves")
yield NodeItem("GeometryNodeEdgePathsToSelection")
yield NodeItem("GeometryNodeExtrudeMesh")
yield NodeItem("GeometryNodeFlipFaces")
yield NodeItem("GeometryNodeMeshBoolean")
yield NodeItem("GeometryNodeMeshToCurve")
yield NodeItem("GeometryNodeMeshToPoints")
yield NodeItem("GeometryNodeMeshToVolume")
yield NodeItem("GeometryNodeSampleNearestSurface")
yield NodeItem("GeometryNodeScaleElements")
yield NodeItem("GeometryNodeSplitEdges")
yield NodeItem("GeometryNodeSubdivideMesh")
yield NodeItem("GeometryNodeSubdivisionSurface")
yield NodeItem("GeometryNodeTriangulate")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputMeshEdgeAngle")
yield NodeItem("GeometryNodeInputMeshEdgeNeighbors")
yield NodeItem("GeometryNodeInputMeshEdgeVertices")
yield NodeItem("GeometryNodeInputMeshFaceArea")
yield NodeItem("GeometryNodeInputMeshFaceNeighbors")
yield NodeItem("GeometryNodeMeshFaceSetBoundaries")
yield NodeItem("GeometryNodeInputMeshFaceIsPlanar")
yield NodeItem("GeometryNodeInputShadeSmooth")
yield NodeItem("GeometryNodeInputMeshIsland")
yield NodeItem("GeometryNodeInputShortestEdgePaths")
yield NodeItem("GeometryNodeInputMeshVertexNeighbors")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeSetShadeSmooth")
# Custom Menu for Geometry Nodes "Geometry" category.
def geometry_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeBoundBox")
yield NodeItem("GeometryNodeConvexHull")
yield NodeItem("GeometryNodeDeleteGeometry")
yield NodeItem("GeometryNodeDuplicateElements")
yield NodeItem("GeometryNodeProximity")
yield NodeItem("GeometryNodeGeometryToInstance")
yield NodeItem("GeometryNodeJoinGeometry")
yield NodeItem("GeometryNodeMergeByDistance")
yield NodeItem("GeometryNodeRaycast")
yield NodeItem("GeometryNodeSampleIndex")
yield NodeItem("GeometryNodeSampleNearest")
yield NodeItem("GeometryNodeSeparateComponents")
yield NodeItem("GeometryNodeSeparateGeometry")
yield NodeItem("GeometryNodeTransform")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeSetID")
yield NodeItem("GeometryNodeSetPosition")
# Custom Menu for UV Nodes.
def uv_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeUVPackIslands")
yield NodeItem("GeometryNodeUVUnwrap")
# Custom Menu for Geometry Node Input Nodes.
def geometry_input_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("FunctionNodeInputBool")
yield NodeItem("GeometryNodeCollectionInfo")
yield NodeItem("FunctionNodeInputColor")
yield NodeItem("FunctionNodeInputInt")
yield NodeItem("GeometryNodeIsViewport")
yield NodeItem("GeometryNodeInputMaterial")
yield NodeItem("GeometryNodeObjectInfo")
yield NodeItem("FunctionNodeInputString")
yield NodeItem("ShaderNodeValue")
yield NodeItem("FunctionNodeInputVector")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputID")
yield NodeItem("GeometryNodeInputIndex")
yield NodeItem("GeometryNodeInputNamedAttribute")
yield NodeItem("GeometryNodeInputNormal")
yield NodeItem("GeometryNodeInputPosition")
yield NodeItem("GeometryNodeInputRadius")
yield NodeItem("GeometryNodeInputSceneTime")
# Custom Menu for Geometry Node Instance Nodes.
def geometry_instance_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeInstanceOnPoints")
yield NodeItem("GeometryNodeInstancesToPoints")
yield NodeItem("GeometryNodeRealizeInstances")
yield NodeItem("GeometryNodeRotateInstances")
yield NodeItem("GeometryNodeScaleInstances")
yield NodeItem("GeometryNodeTranslateInstances")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputInstanceRotation")
yield NodeItem("GeometryNodeInputInstanceScale")
# Custom Menu for Material Nodes.
def geometry_material_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeReplaceMaterial")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputMaterialIndex")
yield NodeItem("GeometryNodeMaterialSelection")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeSetMaterial")
yield NodeItem("GeometryNodeSetMaterialIndex")
# Custom Menu for Geometry Node Points.
def point_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeDistributePointsInVolume")
yield NodeItem("GeometryNodeDistributePointsOnFaces")
yield NodeItem("GeometryNodePoints")
yield NodeItem("GeometryNodePointsToVertices")
yield NodeItem("GeometryNodePointsToVolume")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeSetPointRadius")
# Generic node group items generator for shader, compositor, geometry and texture node groups.
def node_group_items(context):
if context is None:
@ -648,122 +448,17 @@ texture_node_categories = [
]),
]
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeCaptureAttribute"),
NodeItem("GeometryNodeAttributeDomainSize"),
NodeItem("GeometryNodeAttributeStatistic"),
NodeItem("GeometryNodeRemoveAttribute"),
NodeItem("GeometryNodeStoreNamedAttribute"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeMix", label="Mix Color", settings={"data_type": "'RGBA'"}),
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("FunctionNodeSeparateColor"),
NodeItem("FunctionNodeCombineColor"),
]),
GeometryNodeCategory("GEO_CURVE", "Curve", items=curve_node_items),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
NodeItem("GeometryNodeCurvePrimitiveLine"),
NodeItem("GeometryNodeCurvePrimitiveCircle"),
NodeItem("GeometryNodeCurveStar"),
NodeItem("GeometryNodeCurveSpiral"),
NodeItem("GeometryNodeCurveArc"),
NodeItem("GeometryNodeCurveQuadraticBezier"),
NodeItem("GeometryNodeCurvePrimitiveQuadrilateral"),
NodeItem("GeometryNodeCurvePrimitiveBezierSegment"),
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=geometry_node_items),
GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items),
GeometryNodeCategory("GEO_INSTANCE", "Instances", items=geometry_instance_node_items),
GeometryNodeCategory("GEO_MATERIAL", "Material", items=geometry_material_node_items),
GeometryNodeCategory("GEO_MESH", "Mesh", items=mesh_node_items),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
NodeItem("GeometryNodeMeshCircle"),
NodeItem("GeometryNodeMeshCone"),
NodeItem("GeometryNodeMeshCube"),
NodeItem("GeometryNodeMeshCylinder"),
NodeItem("GeometryNodeMeshGrid"),
NodeItem("GeometryNodeMeshIcoSphere"),
NodeItem("GeometryNodeMeshLine"),
NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_OUTPUT", "Output", items=[
NodeItem("GeometryNodeViewer"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=point_node_items),
GeometryNodeCategory("GEO_TEXT", "Text", items=[
NodeItem("FunctionNodeStringLength"),
NodeItem("FunctionNodeSliceString"),
NodeItem("FunctionNodeValueToString"),
NodeItem("GeometryNodeStringJoin"),
NodeItem("FunctionNodeInputSpecialCharacters"),
NodeItem("GeometryNodeStringToCurves"),
NodeItem("FunctionNodeReplaceString"),
]),
GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexBrick"),
NodeItem("ShaderNodeTexChecker"),
NodeItem("ShaderNodeTexGradient"),
NodeItem("ShaderNodeTexMagic"),
NodeItem("ShaderNodeTexMusgrave"),
NodeItem("ShaderNodeTexNoise"),
NodeItem("ShaderNodeTexVoronoi"),
NodeItem("ShaderNodeTexWave"),
NodeItem("ShaderNodeTexWhiteNoise"),
NodeItem("GeometryNodeImageTexture"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("GeometryNodeAccumulateField"),
NodeItem("GeometryNodeFieldAtIndex"),
NodeItem("GeometryNodeFieldOnDomain"),
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeFloatCurve"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeRotateEuler"),
NodeItem("FunctionNodeCompare"),
NodeItem("ShaderNodeMix"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
NodeItem("FunctionNodeRandomValue"),
NodeItem("FunctionNodeAlignEulerToVector"),
]),
GeometryNodeCategory("GEO_UV", "UV", items=uv_node_items),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeVectorRotate"),
]),
GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
NodeItem("GeometryNodeVolumeCube"),
NodeItem("GeometryNodeVolumeToMesh"),
]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
]),
]
def register():
nodeitems_utils.register_node_categories('SHADER', shader_node_categories)
nodeitems_utils.register_node_categories('COMPOSITING', compositor_node_categories)
nodeitems_utils.register_node_categories('TEXTURE', texture_node_categories)
nodeitems_utils.register_node_categories('GEOMETRY', geometry_node_categories)
def unregister():
nodeitems_utils.unregister_node_categories('SHADER')
nodeitems_utils.unregister_node_categories('COMPOSITING')
nodeitems_utils.unregister_node_categories('TEXTURE')
nodeitems_utils.unregister_node_categories('GEOMETRY')
if __name__ == "__main__":