Files
test2/scripts/startup/bl_ui/node_add_menu_geometry.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1159 lines
47 KiB
Python
Raw Permalink Normal View History

# SPDX-FileCopyrightText: 2022-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bl_ui import node_add_menu
from bpy.app.translations import (
contexts as i18n_contexts,
)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_attribute_base(node_add_menu.NodeMenu):
bl_label = "Attribute"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeAttributeStatistic")
self.node_operator(layout, "GeometryNodeAttributeDomainSize")
layout.separator()
self.node_operator(layout, "GeometryNodeBlurAttribute")
self.node_operator(layout, "GeometryNodeCaptureAttribute")
self.node_operator(layout, "GeometryNodeRemoveAttribute")
self.node_operator(layout, "GeometryNodeStoreNamedAttribute", search_weight=1.0)
self.draw_assets_for_catalog(layout, self.bl_label)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_color_base(node_add_menu.NodeMenu):
bl_label = "Color"
def draw(self, context):
layout = self.layout
self.node_operator(layout, "ShaderNodeBlackbody")
self.node_operator(layout, "ShaderNodeGamma")
self.node_operator(layout, "ShaderNodeValToRGB")
self.node_operator(layout, "ShaderNodeRGBCurve")
self.color_mix_node(context, layout)
layout.separator()
self.node_operator(layout, "FunctionNodeCombineColor")
self.node_operator(layout, "FunctionNodeSeparateColor")
self.draw_assets_for_catalog(layout, self.bl_label)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_curve_base(node_add_menu.NodeMenu):
bl_label = "Curve"
def draw(self, _context):
layout = self.layout
self.draw_menu(layout, path="Curve/Read")
self.draw_menu(layout, path="Curve/Sample")
self.draw_menu(layout, path="Curve/Write")
layout.separator()
self.draw_menu(layout, path="Curve/Operations")
self.draw_menu(layout, path="Curve/Primitives")
self.draw_menu(layout, path="Curve/Topology")
self.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_gn_curve_read_base(node_add_menu.NodeMenu):
bl_label = "Read"
menu_path = "Curve/Read"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeInputCurveHandlePositions")
self.node_operator(layout, "GeometryNodeCurveLength")
self.node_operator(layout, "GeometryNodeInputTangent")
self.node_operator(layout, "GeometryNodeInputCurveTilt")
self.node_operator(layout, "GeometryNodeCurveEndpointSelection")
self.node_operator(layout, "GeometryNodeCurveHandleTypeSelection")
self.node_operator(layout, "GeometryNodeInputSplineCyclic")
self.node_operator(layout, "GeometryNodeSplineLength")
self.node_operator(layout, "GeometryNodeSplineParameter")
self.node_operator(layout, "GeometryNodeInputSplineResolution")
self.draw_assets_for_catalog(layout, self.menu_path)
2023-01-16 14:39:10 +01:00
class NODE_MT_gn_curve_sample_base(node_add_menu.NodeMenu):
bl_label = "Sample"
menu_path = "Curve/Sample"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeSampleCurve")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_curve_write_base(node_add_menu.NodeMenu):
bl_label = "Write"
menu_path = "Curve/Write"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeSetCurveNormal")
self.node_operator(layout, "GeometryNodeSetCurveRadius")
self.node_operator(layout, "GeometryNodeSetCurveTilt")
self.node_operator(layout, "GeometryNodeSetCurveHandlePositions")
self.node_operator(layout, "GeometryNodeCurveSetHandles")
self.node_operator(layout, "GeometryNodeSetSplineCyclic")
self.node_operator(layout, "GeometryNodeSetSplineResolution")
self.node_operator(layout, "GeometryNodeCurveSplineType")
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_curve_operations_base(node_add_menu.NodeMenu):
bl_label = "Operations"
menu_path = "Curve/Operations"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeCurveToMesh")
self.node_operator(layout, "GeometryNodeCurveToPoints")
self.node_operator(layout, "GeometryNodeCurvesToGreasePencil")
self.node_operator(layout, "GeometryNodeDeformCurvesOnSurface")
self.node_operator(layout, "GeometryNodeFillCurve")
self.node_operator(layout, "GeometryNodeFilletCurve")
self.node_operator(layout, "GeometryNodeInterpolateCurves")
self.node_operator(layout, "GeometryNodeResampleCurve")
self.node_operator(layout, "GeometryNodeReverseCurve")
self.node_operator(layout, "GeometryNodeSubdivideCurve")
self.node_operator(layout, "GeometryNodeTrimCurve")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_curve_primitives_base(node_add_menu.NodeMenu):
bl_label = "Primitives"
menu_path = "Curve/Primitives"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeCurveArc")
self.node_operator(layout, "GeometryNodeCurvePrimitiveBezierSegment")
self.node_operator(layout, "GeometryNodeCurvePrimitiveCircle")
self.node_operator(layout, "GeometryNodeCurvePrimitiveLine")
self.node_operator(layout, "GeometryNodeCurveSpiral")
self.node_operator(layout, "GeometryNodeCurveQuadraticBezier")
self.node_operator(layout, "GeometryNodeCurvePrimitiveQuadrilateral")
self.node_operator(layout, "GeometryNodeCurveStar")
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_curve_topology_base(node_add_menu.NodeMenu):
bl_label = "Topology"
menu_path = "Curve/Topology"
Geometry Nodes: Curve and mesh topology access nodes This patch contains an initial set of nodes to access basic mesh topology information, as explored in T100020. The nodes allow six direct topology mappings for meshes: - **Corner -> Face** The face a corner is in, the index in the face - **Vertex -> Edge** Choose an edge attached to the vertex - **Vertex -> Corner** Choose a corner attached to the vertex - **Corner -> Edge** The next and previous edge at each face corner - **Corner -> Vertex** The vertex associated with a corner - **Corner -> Corner** Offset a corner index within a face And two new topology mappings for curves: - **Curve -> Points** Choose a point within a curve - **Point -> Curve** The curve a point is in, the index in the curve The idea is that some of the 16 possible mesh mappings are more important, and that this is a useful set of nodes to start exploring this area. For mappings with an arbitrary number of connections, we must sort them and use an index to choose a single element, because geometry nodes does not support list fields. Note that the sort index has repeating behavior as it goes over the "Total" number of connections, and negative sort indices choose from the end. Currently which of the "start" elements is used is determined by the field context, so the "Field at Index" and "Interpolate Domain" nodes will be quite important. Also, currently the "Sort Index" inputs are clamped to the number of connections. One important feature that isn't implemented here is using the winding order for the output elements. This can be a separate mode for some of these nodes. It will be optional because of the performance impact. There are several todos for separate commits after this: - Rename "Control Point Neighbors" to be consistent with this naming - Version away the "Vertex Neighbors" node which is fully redundant now - Implement a special case for when no weights are used for performance - De-duplicating some of the sorting logic between the nodes - Improve performance and memory use of topology mappings - Look into caching some of the mappings on meshes Differential Revision: https://developer.blender.org/D16029
2022-09-28 14:38:27 -05:00
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeCurveOfPoint")
self.node_operator(layout, "GeometryNodeOffsetPointInCurve")
self.node_operator(layout, "GeometryNodePointsOfCurve")
self.draw_assets_for_catalog(layout, self.menu_path)
Geometry Nodes: Curve and mesh topology access nodes This patch contains an initial set of nodes to access basic mesh topology information, as explored in T100020. The nodes allow six direct topology mappings for meshes: - **Corner -> Face** The face a corner is in, the index in the face - **Vertex -> Edge** Choose an edge attached to the vertex - **Vertex -> Corner** Choose a corner attached to the vertex - **Corner -> Edge** The next and previous edge at each face corner - **Corner -> Vertex** The vertex associated with a corner - **Corner -> Corner** Offset a corner index within a face And two new topology mappings for curves: - **Curve -> Points** Choose a point within a curve - **Point -> Curve** The curve a point is in, the index in the curve The idea is that some of the 16 possible mesh mappings are more important, and that this is a useful set of nodes to start exploring this area. For mappings with an arbitrary number of connections, we must sort them and use an index to choose a single element, because geometry nodes does not support list fields. Note that the sort index has repeating behavior as it goes over the "Total" number of connections, and negative sort indices choose from the end. Currently which of the "start" elements is used is determined by the field context, so the "Field at Index" and "Interpolate Domain" nodes will be quite important. Also, currently the "Sort Index" inputs are clamped to the number of connections. One important feature that isn't implemented here is using the winding order for the output elements. This can be a separate mode for some of these nodes. It will be optional because of the performance impact. There are several todos for separate commits after this: - Rename "Control Point Neighbors" to be consistent with this naming - Version away the "Vertex Neighbors" node which is fully redundant now - Implement a special case for when no weights are used for performance - De-duplicating some of the sorting logic between the nodes - Improve performance and memory use of topology mappings - Look into caching some of the mappings on meshes Differential Revision: https://developer.blender.org/D16029
2022-09-28 14:38:27 -05:00
class NODE_MT_gn_grease_pencil_read_base(node_add_menu.NodeMenu):
bl_label = "Read"
menu_path = "Grease Pencil/Read"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeInputNamedLayerSelection")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_grease_pencil_write_base(node_add_menu.NodeMenu):
bl_label = "Write"
menu_path = "Grease Pencil/Write"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeSetGreasePencilColor")
self.node_operator(layout, "GeometryNodeSetGreasePencilDepth")
self.node_operator(layout, "GeometryNodeSetGreasePencilSoftness")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_grease_pencil_operations_base(node_add_menu.NodeMenu):
bl_label = "Operations"
menu_path = "Grease Pencil/Operations"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeGreasePencilToCurves")
self.node_operator(layout, "GeometryNodeMergeLayers")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_grease_pencil_base(node_add_menu.NodeMenu):
bl_label = "Grease Pencil"
def draw(self, _context):
layout = self.layout
self.draw_menu(layout, path="Grease Pencil/Read")
self.draw_menu(layout, path="Grease Pencil/Write")
layout.separator()
self.draw_menu(layout, path="Grease Pencil/Operations")
self.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_gn_geometry_base(node_add_menu.NodeMenu):
bl_label = "Geometry"
def draw(self, _context):
layout = self.layout
self.draw_menu(layout, path="Geometry/Read")
self.draw_menu(layout, path="Geometry/Sample")
self.draw_menu(layout, path="Geometry/Write")
layout.separator()
self.draw_menu(layout, path="Geometry/Material")
self.draw_menu(layout, path="Geometry/Operations")
layout.separator()
self.node_operator(layout, "GeometryNodeGeometryToInstance")
self.node_operator(layout, "GeometryNodeJoinGeometry", search_weight=1.0)
self.draw_assets_for_catalog(layout, self.bl_label)
2023-01-16 14:39:10 +01:00
class NODE_MT_gn_geometry_read_base(node_add_menu.NodeMenu):
bl_label = "Read"
menu_path = "Geometry/Read"
def draw(self, context):
layout = self.layout
self.node_operator(layout, "GeometryNodeInputID")
self.node_operator(layout, "GeometryNodeInputIndex")
self.node_operator(layout, "GeometryNodeInputNamedAttribute", search_weight=1.0)
self.node_operator(layout, "GeometryNodeInputNormal")
self.node_operator(layout, "GeometryNodeInputPosition", search_weight=1.0)
self.node_operator(layout, "GeometryNodeInputRadius")
if context.space_data.node_tree_sub_type == 'TOOL':
self.node_operator(layout, "GeometryNodeToolSelection")
self.node_operator(layout, "GeometryNodeToolActiveElement")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_geometry_write_base(node_add_menu.NodeMenu):
bl_label = "Write"
menu_path = "Geometry/Write"
def draw(self, context):
layout = self.layout
self.node_operator(layout, "GeometryNodeSetGeometryName")
self.node_operator(layout, "GeometryNodeSetID")
self.node_operator(layout, "GeometryNodeSetPosition", search_weight=1.0)
if context.space_data.node_tree_sub_type == 'TOOL':
self.node_operator(layout, "GeometryNodeToolSetSelection")
self.draw_assets_for_catalog(layout, self.menu_path)
2023-01-16 14:39:10 +01:00
class NODE_MT_gn_geometry_operations_base(node_add_menu.NodeMenu):
bl_label = "Operations"
menu_path = "Geometry/Operations"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeBake")
self.node_operator(layout, "GeometryNodeBoundBox")
self.node_operator(layout, "GeometryNodeConvexHull")
self.node_operator(layout, "GeometryNodeDeleteGeometry")
self.node_operator(layout, "GeometryNodeDuplicateElements")
self.node_operator(layout, "GeometryNodeMergeByDistance")
self.node_operator(layout, "GeometryNodeSortElements")
self.node_operator(layout, "GeometryNodeTransform", search_weight=1.0)
layout.separator()
self.node_operator(layout, "GeometryNodeSeparateComponents")
self.node_operator(layout, "GeometryNodeSeparateGeometry")
self.node_operator(layout, "GeometryNodeSplitToInstances")
self.draw_assets_for_catalog(layout, self.menu_path)
2023-01-16 14:39:10 +01:00
class NODE_MT_gn_geometry_sample_base(node_add_menu.NodeMenu):
bl_label = "Sample"
menu_path = "Geometry/Sample"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeProximity")
self.node_operator(layout, "GeometryNodeIndexOfNearest")
self.node_operator(layout, "GeometryNodeRaycast")
self.node_operator(layout, "GeometryNodeSampleIndex")
self.node_operator(layout, "GeometryNodeSampleNearest")
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_input_base(node_add_menu.NodeMenu):
bl_label = "Input"
def draw(self, context):
layout = self.layout
self.draw_menu(layout, path="Input/Constant")
if context.space_data.node_tree_sub_type != 'TOOL':
self.draw_menu(layout, path="Input/Gizmo")
self.draw_menu(layout, path="Input/Group")
self.draw_menu(layout, path="Input/Import")
self.draw_menu(layout, path="Input/Scene")
self.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_gn_input_constant_base(node_add_menu.NodeMenu):
bl_label = "Constant"
bl_translation_context = i18n_contexts.id_nodetree
menu_path = "Input/Constant"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "FunctionNodeInputBool")
self.node_operator(layout, "GeometryNodeInputCollection")
self.node_operator(layout, "FunctionNodeInputColor")
self.node_operator(layout, "GeometryNodeInputImage")
self.node_operator(layout, "FunctionNodeInputInt")
self.node_operator(layout, "GeometryNodeInputMaterial")
self.node_operator(layout, "GeometryNodeInputObject")
self.node_operator(layout, "FunctionNodeInputRotation")
self.node_operator(layout, "FunctionNodeInputString")
self.node_operator(layout, "ShaderNodeValue")
self.node_operator(layout, "FunctionNodeInputVector")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_input_group_base(node_add_menu.NodeMenu):
bl_label = "Group"
menu_path = "Input/Group"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "NodeGroupInput")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_input_scene_base(node_add_menu.NodeMenu):
bl_label = "Scene"
menu_path = "Input/Scene"
def draw(self, context):
layout = self.layout
if context.space_data.node_tree_sub_type == 'TOOL':
self.node_operator(layout, "GeometryNodeTool3DCursor")
self.node_operator(layout, "GeometryNodeInputActiveCamera")
self.node_operator_with_outputs(
context,
layout,
"GeometryNodeCameraInfo",
[
"Projection Matrix",
"Focal Length",
"Sensor",
"Shift",
"Clip Start",
"Clip End",
"Focus Distance",
"Is Orthographic",
"Orthographic Scale",
],
)
self.node_operator(layout, "GeometryNodeCollectionInfo")
self.node_operator(layout, "GeometryNodeImageInfo")
self.node_operator(layout, "GeometryNodeIsViewport")
if context.space_data.node_tree_sub_type == 'TOOL':
self.node_operator_with_outputs(
context, layout, "GeometryNodeToolMousePosition",
["Mouse X", "Mouse Y", "Region Width", "Region Height"],
)
self.node_operator(layout, "GeometryNodeObjectInfo")
self.node_operator_with_outputs(context, layout, "GeometryNodeInputSceneTime", ["Frame", "Seconds"])
self.node_operator(layout, "GeometryNodeSelfObject")
if context.space_data.node_tree_sub_type == 'TOOL':
self.node_operator_with_outputs(
context, layout, "GeometryNodeViewportTransform",
["Projection", "View", "Is Orthographic"],
)
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_input_gizmo_base(node_add_menu.NodeMenu):
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
bl_label = "Gizmo"
menu_path = "Input/Gizmo"
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
def draw(self, context):
del context
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
layout = self.layout
self.node_operator(layout, "GeometryNodeGizmoDial")
self.node_operator(layout, "GeometryNodeGizmoLinear")
self.node_operator(layout, "GeometryNodeGizmoTransform")
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
self.draw_assets_for_catalog(layout, self.menu_path)
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
class NODE_MT_gn_instance_base(node_add_menu.NodeMenu):
bl_label = "Instances"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeInstanceOnPoints", search_weight=2.0)
self.node_operator(layout, "GeometryNodeInstancesToPoints")
layout.separator()
self.node_operator(layout, "GeometryNodeRealizeInstances", search_weight=1.0)
self.node_operator(layout, "GeometryNodeRotateInstances")
self.node_operator(layout, "GeometryNodeScaleInstances")
self.node_operator(layout, "GeometryNodeTranslateInstances")
self.node_operator(layout, "GeometryNodeSetInstanceTransform")
layout.separator()
self.node_operator(layout, "GeometryNodeInputInstanceBounds")
self.node_operator(layout, "GeometryNodeInstanceTransform")
self.node_operator(layout, "GeometryNodeInputInstanceRotation")
self.node_operator(layout, "GeometryNodeInputInstanceScale")
self.draw_assets_for_catalog(layout, self.bl_label)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_material_base(node_add_menu.NodeMenu):
bl_label = "Material"
menu_path = "Geometry/Material"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeReplaceMaterial")
layout.separator()
self.node_operator(layout, "GeometryNodeInputMaterialIndex")
self.node_operator(layout, "GeometryNodeMaterialSelection")
layout.separator()
self.node_operator(layout, "GeometryNodeSetMaterial", search_weight=1.0)
self.node_operator(layout, "GeometryNodeSetMaterialIndex")
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_mesh_base(node_add_menu.NodeMenu):
bl_label = "Mesh"
def draw(self, _context):
layout = self.layout
self.draw_menu(layout, path="Mesh/Read")
self.draw_menu(layout, path="Mesh/Sample")
self.draw_menu(layout, path="Mesh/Write")
layout.separator()
self.draw_menu(layout, path="Mesh/Operations")
self.draw_menu(layout, path="Mesh/Primitives")
self.draw_menu(layout, path="Mesh/Topology")
self.draw_menu(layout, path="Mesh/UV")
self.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_gn_mesh_read_base(node_add_menu.NodeMenu):
bl_label = "Read"
menu_path = "Mesh/Read"
def draw(self, context):
layout = self.layout
self.node_operator(layout, "GeometryNodeInputMeshEdgeAngle")
self.node_operator(layout, "GeometryNodeInputMeshEdgeNeighbors")
self.node_operator(layout, "GeometryNodeInputMeshEdgeVertices")
self.node_operator(layout, "GeometryNodeEdgesToFaceGroups")
self.node_operator(layout, "GeometryNodeInputMeshFaceArea")
self.node_operator(layout, "GeometryNodeMeshFaceSetBoundaries")
self.node_operator(layout, "GeometryNodeInputMeshFaceNeighbors")
if context.space_data.node_tree_sub_type == 'TOOL':
self.node_operator(layout, "GeometryNodeToolFaceSet")
self.node_operator(layout, "GeometryNodeInputMeshFaceIsPlanar")
self.node_operator(layout, "GeometryNodeInputShadeSmooth")
self.node_operator(layout, "GeometryNodeInputEdgeSmooth")
self.node_operator(layout, "GeometryNodeInputMeshIsland")
self.node_operator(layout, "GeometryNodeInputShortestEdgePaths")
self.node_operator(layout, "GeometryNodeInputMeshVertexNeighbors")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_mesh_sample_base(node_add_menu.NodeMenu):
bl_label = "Sample"
menu_path = "Mesh/Sample"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeSampleNearestSurface")
self.node_operator(layout, "GeometryNodeSampleUVSurface")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_mesh_write_base(node_add_menu.NodeMenu):
bl_label = "Write"
menu_path = "Mesh/Write"
def draw(self, context):
layout = self.layout
if context.space_data.node_tree_sub_type == 'TOOL':
self.node_operator(layout, "GeometryNodeToolSetFaceSet")
self.node_operator(layout, "GeometryNodeSetMeshNormal")
self.node_operator(layout, "GeometryNodeSetShadeSmooth")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_mesh_operations_base(node_add_menu.NodeMenu):
bl_label = "Operations"
menu_path = "Mesh/Operations"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "GeometryNodeDualMesh")
self.node_operator(layout, "GeometryNodeEdgePathsToCurves")
self.node_operator(layout, "GeometryNodeEdgePathsToSelection")
self.node_operator(layout, "GeometryNodeExtrudeMesh")
self.node_operator(layout, "GeometryNodeFlipFaces")
self.node_operator(layout, "GeometryNodeMeshBoolean")
self.node_operator(layout, "GeometryNodeMeshToCurve")
self.node_operator(layout, "GeometryNodeMeshToDensityGrid")
self.node_operator(layout, "GeometryNodeMeshToPoints")
self.node_operator(layout, "GeometryNodeMeshToSDFGrid")
self.node_operator(layout, "GeometryNodeMeshToVolume")
self.node_operator(layout, "GeometryNodeScaleElements")
self.node_operator(layout, "GeometryNodeSplitEdges")
self.node_operator(layout, "GeometryNodeSubdivideMesh")
self.node_operator(layout, "GeometryNodeSubdivisionSurface")
self.node_operator(layout, "GeometryNodeTriangulate")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_mesh_primitives_base(node_add_menu.NodeMenu):
bl_label = "Primitives"
menu_path = "Mesh/Primitives"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeMeshCone")
self.node_operator(layout, "GeometryNodeMeshCube")
self.node_operator(layout, "GeometryNodeMeshCylinder")
self.node_operator(layout, "GeometryNodeMeshGrid")
self.node_operator(layout, "GeometryNodeMeshIcoSphere")
self.node_operator(layout, "GeometryNodeMeshCircle")
self.node_operator(layout, "GeometryNodeMeshLine")
self.node_operator(layout, "GeometryNodeMeshUVSphere")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_input_import_base(node_add_menu.NodeMenu):
bl_label = "Import"
menu_path = "Input/Import"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeImportCSV", label="CSV (.csv)")
self.node_operator(layout, "GeometryNodeImportOBJ", label="Wavefront (.obj)")
self.node_operator(layout, "GeometryNodeImportPLY", label="Stanford PLY (.ply)")
self.node_operator(layout, "GeometryNodeImportSTL", label="STL (.stl)")
self.node_operator(layout, "GeometryNodeImportText", label="Text (.txt)")
self.node_operator(layout, "GeometryNodeImportVDB", label="OpenVDB (.vdb)")
2022-09-27 16:36:27 +10:00
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_mesh_topology_base(node_add_menu.NodeMenu):
bl_label = "Topology"
menu_path = "Mesh/Topology"
Geometry Nodes: Curve and mesh topology access nodes This patch contains an initial set of nodes to access basic mesh topology information, as explored in T100020. The nodes allow six direct topology mappings for meshes: - **Corner -> Face** The face a corner is in, the index in the face - **Vertex -> Edge** Choose an edge attached to the vertex - **Vertex -> Corner** Choose a corner attached to the vertex - **Corner -> Edge** The next and previous edge at each face corner - **Corner -> Vertex** The vertex associated with a corner - **Corner -> Corner** Offset a corner index within a face And two new topology mappings for curves: - **Curve -> Points** Choose a point within a curve - **Point -> Curve** The curve a point is in, the index in the curve The idea is that some of the 16 possible mesh mappings are more important, and that this is a useful set of nodes to start exploring this area. For mappings with an arbitrary number of connections, we must sort them and use an index to choose a single element, because geometry nodes does not support list fields. Note that the sort index has repeating behavior as it goes over the "Total" number of connections, and negative sort indices choose from the end. Currently which of the "start" elements is used is determined by the field context, so the "Field at Index" and "Interpolate Domain" nodes will be quite important. Also, currently the "Sort Index" inputs are clamped to the number of connections. One important feature that isn't implemented here is using the winding order for the output elements. This can be a separate mode for some of these nodes. It will be optional because of the performance impact. There are several todos for separate commits after this: - Rename "Control Point Neighbors" to be consistent with this naming - Version away the "Vertex Neighbors" node which is fully redundant now - Implement a special case for when no weights are used for performance - De-duplicating some of the sorting logic between the nodes - Improve performance and memory use of topology mappings - Look into caching some of the mappings on meshes Differential Revision: https://developer.blender.org/D16029
2022-09-28 14:38:27 -05:00
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeCornersOfEdge")
self.node_operator(layout, "GeometryNodeCornersOfFace")
self.node_operator(layout, "GeometryNodeCornersOfVertex")
self.node_operator(layout, "GeometryNodeEdgesOfCorner")
self.node_operator(layout, "GeometryNodeEdgesOfVertex")
self.node_operator(layout, "GeometryNodeFaceOfCorner")
self.node_operator(layout, "GeometryNodeOffsetCornerInFace")
self.node_operator(layout, "GeometryNodeVertexOfCorner")
self.draw_assets_for_catalog(layout, self.menu_path)
Geometry Nodes: Curve and mesh topology access nodes This patch contains an initial set of nodes to access basic mesh topology information, as explored in T100020. The nodes allow six direct topology mappings for meshes: - **Corner -> Face** The face a corner is in, the index in the face - **Vertex -> Edge** Choose an edge attached to the vertex - **Vertex -> Corner** Choose a corner attached to the vertex - **Corner -> Edge** The next and previous edge at each face corner - **Corner -> Vertex** The vertex associated with a corner - **Corner -> Corner** Offset a corner index within a face And two new topology mappings for curves: - **Curve -> Points** Choose a point within a curve - **Point -> Curve** The curve a point is in, the index in the curve The idea is that some of the 16 possible mesh mappings are more important, and that this is a useful set of nodes to start exploring this area. For mappings with an arbitrary number of connections, we must sort them and use an index to choose a single element, because geometry nodes does not support list fields. Note that the sort index has repeating behavior as it goes over the "Total" number of connections, and negative sort indices choose from the end. Currently which of the "start" elements is used is determined by the field context, so the "Field at Index" and "Interpolate Domain" nodes will be quite important. Also, currently the "Sort Index" inputs are clamped to the number of connections. One important feature that isn't implemented here is using the winding order for the output elements. This can be a separate mode for some of these nodes. It will be optional because of the performance impact. There are several todos for separate commits after this: - Rename "Control Point Neighbors" to be consistent with this naming - Version away the "Vertex Neighbors" node which is fully redundant now - Implement a special case for when no weights are used for performance - De-duplicating some of the sorting logic between the nodes - Improve performance and memory use of topology mappings - Look into caching some of the mappings on meshes Differential Revision: https://developer.blender.org/D16029
2022-09-28 14:38:27 -05:00
class NODE_MT_gn_output_base(node_add_menu.NodeMenu):
bl_label = "Output"
def draw(self, context):
layout = self.layout
self.node_operator(layout, "NodeEnableOutput")
self.node_operator(layout, "NodeGroupOutput")
self.node_operator(layout, "GeometryNodeViewer")
self.node_operator_with_searchable_enum(context, layout, "GeometryNodeWarning", "warning_type")
self.draw_assets_for_catalog(layout, self.bl_label)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_point_base(node_add_menu.NodeMenu):
bl_label = "Point"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "GeometryNodeDistributePointsInVolume")
self.node_operator(layout, "GeometryNodeDistributePointsInGrid")
self.node_operator(layout, "GeometryNodeDistributePointsOnFaces")
layout.separator()
self.node_operator(layout, "GeometryNodePoints")
self.node_operator(layout, "GeometryNodePointsToCurves")
self.node_operator(layout, "GeometryNodePointsToSDFGrid")
self.node_operator(layout, "GeometryNodePointsToVertices")
self.node_operator(layout, "GeometryNodePointsToVolume")
layout.separator()
self.node_operator(layout, "GeometryNodeSetPointRadius")
self.draw_assets_for_catalog(layout, self.bl_label)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_simulation_base(node_add_menu.NodeMenu):
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
bl_label = "Simulation"
def draw(self, _context):
layout = self.layout
self.simulation_zone(layout, label="Simulation")
self.draw_assets_for_catalog(layout, self.bl_label)
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
class NODE_MT_gn_utilities_text_base(node_add_menu.NodeMenu):
bl_label = "Text"
menu_path = "Utilities/Text"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "FunctionNodeFormatString")
self.node_operator(layout, "GeometryNodeStringJoin")
self.node_operator(layout, "FunctionNodeMatchString")
self.node_operator(layout, "FunctionNodeReplaceString")
self.node_operator(layout, "FunctionNodeSliceString")
layout.separator()
self.node_operator(layout, "FunctionNodeFindInString")
self.node_operator(layout, "FunctionNodeStringLength")
self.node_operator(layout, "GeometryNodeStringToCurves")
self.node_operator(layout, "FunctionNodeStringToValue")
self.node_operator(layout, "FunctionNodeValueToString")
layout.separator()
self.node_operator(layout, "FunctionNodeInputSpecialCharacters")
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_texture_base(node_add_menu.NodeMenu):
bl_label = "Texture"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "ShaderNodeTexBrick")
self.node_operator(layout, "ShaderNodeTexChecker")
self.node_operator(layout, "ShaderNodeTexGabor")
self.node_operator(layout, "ShaderNodeTexGradient")
self.node_operator(layout, "GeometryNodeImageTexture")
self.node_operator(layout, "ShaderNodeTexMagic")
self.node_operator(layout, "ShaderNodeTexNoise")
self.node_operator(layout, "ShaderNodeTexVoronoi")
self.node_operator(layout, "ShaderNodeTexWave")
self.node_operator(layout, "ShaderNodeTexWhiteNoise")
self.draw_assets_for_catalog(layout, self.bl_label)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_utilities_base(node_add_menu.NodeMenu):
bl_label = "Utilities"
def draw(self, context):
layout = self.layout
self.draw_menu(layout, path="Utilities/Math")
self.draw_menu(layout, path="Utilities/Text")
self.draw_menu(layout, path="Utilities/Vector")
layout.separator()
self.draw_menu(layout, path="Utilities/Bundle")
self.draw_menu(layout, path="Utilities/Closure")
self.draw_menu(layout, path="Utilities/Field")
if context.preferences.experimental.use_geometry_nodes_lists:
self.draw_menu(layout, path="Utilities/List")
self.draw_menu(layout, path="Utilities/Matrix")
self.draw_menu(layout, path="Utilities/Rotation")
layout.separator()
self.for_each_element_zone(layout, label="For Each Element")
self.node_operator(layout, "GeometryNodeIndexSwitch")
self.node_operator(layout, "GeometryNodeMenuSwitch")
self.node_operator(layout, "FunctionNodeRandomValue")
self.repeat_zone(layout, label="Repeat")
self.node_operator(layout, "GeometryNodeSwitch")
layout.separator()
self.draw_assets_for_catalog(layout, self.bl_label)
layout.separator()
self.draw_menu(layout, path="Utilities/Deprecated")
class NODE_MT_gn_utilities_deprecated_base(node_add_menu.NodeMenu):
bl_label = "Deprecated"
menu_path = "Utilities/Deprecated"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "FunctionNodeAlignEulerToVector")
self.node_operator(layout, "FunctionNodeRotateEuler")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_utilities_field_base(node_add_menu.NodeMenu):
bl_label = "Field"
menu_path = "Utilities/Field"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeAccumulateField")
self.node_operator(layout, "GeometryNodeFieldAtIndex")
self.node_operator(layout, "GeometryNodeFieldOnDomain")
self.node_operator(layout, "GeometryNodeFieldAverage")
self.node_operator(layout, "GeometryNodeFieldMinAndMax")
self.node_operator(layout, "GeometryNodeFieldVariance")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_utilities_rotation_base(node_add_menu.NodeMenu):
bl_label = "Rotation"
menu_path = "Utilities/Rotation"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "FunctionNodeAlignRotationToVector")
self.node_operator(layout, "FunctionNodeAxesToRotation")
self.node_operator(layout, "FunctionNodeAxisAngleToRotation")
self.node_operator(layout, "FunctionNodeEulerToRotation")
self.node_operator(layout, "FunctionNodeInvertRotation")
props = self.node_operator(layout, "ShaderNodeMix", label="Mix Rotation")
ops = props.settings.add()
ops.name = "data_type"
ops.value = "'ROTATION'"
self.node_operator(layout, "FunctionNodeRotateRotation")
self.node_operator(layout, "FunctionNodeRotateVector")
self.node_operator(layout, "FunctionNodeRotationToAxisAngle")
self.node_operator(layout, "FunctionNodeRotationToEuler")
self.node_operator(layout, "FunctionNodeRotationToQuaternion")
self.node_operator(layout, "FunctionNodeQuaternionToRotation")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_utilities_matrix_base(node_add_menu.NodeMenu):
bl_label = "Matrix"
menu_path = "Utilities/Matrix"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "FunctionNodeCombineMatrix")
self.node_operator(layout, "FunctionNodeCombineTransform")
self.node_operator(layout, "FunctionNodeMatrixDeterminant", label="Determinant")
self.node_operator(layout, "FunctionNodeInvertMatrix")
self.node_operator(layout, "FunctionNodeMatrixMultiply")
self.node_operator(layout, "FunctionNodeProjectPoint")
self.node_operator(layout, "FunctionNodeSeparateMatrix")
self.node_operator(layout, "FunctionNodeSeparateTransform")
self.node_operator(layout, "FunctionNodeTransformDirection")
self.node_operator(layout, "FunctionNodeTransformPoint")
self.node_operator(layout, "FunctionNodeTransposeMatrix")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_category_utilities_bundle_base(node_add_menu.NodeMenu):
bl_label = "Bundle"
menu_path = "Utilities/Bundle"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "NodeCombineBundle")
self.node_operator(layout, "NodeSeparateBundle")
self.node_operator(layout, "NodeJoinBundle")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_category_utilities_closure_base(node_add_menu.NodeMenu):
bl_label = "Closure"
menu_path = "Utilities/Closure"
def draw(self, context):
del context
layout = self.layout
self.closure_zone(layout, label="Closure")
self.node_operator(layout, "NodeEvaluateClosure")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_utilities_list_base(node_add_menu.NodeMenu):
bl_label = "List"
menu_path = "Utilities/List"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeList")
self.node_operator(layout, "GeometryNodeListGetItem")
self.node_operator(layout, "GeometryNodeListLength")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_utilities_math_base(node_add_menu.NodeMenu):
bl_label = "Math"
menu_path = "Utilities/Math"
def draw(self, context):
layout = self.layout
self.node_operator_with_searchable_enum(
context, layout, "FunctionNodeBitMath", "operation", search_weight=-1.0,
)
self.node_operator_with_searchable_enum(context, layout, "FunctionNodeBooleanMath", "operation")
self.node_operator_with_searchable_enum(context, layout, "FunctionNodeIntegerMath", "operation")
self.node_operator(layout, "ShaderNodeClamp")
self.node_operator(layout, "FunctionNodeCompare")
self.node_operator(layout, "ShaderNodeFloatCurve")
self.node_operator(layout, "FunctionNodeFloatToInt")
self.node_operator(layout, "FunctionNodeHashValue")
self.node_operator(layout, "ShaderNodeMapRange")
self.node_operator_with_searchable_enum(context, layout, "ShaderNodeMath", "operation")
self.node_operator(layout, "ShaderNodeMix")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_mesh_uv_base(node_add_menu.NodeMenu):
bl_label = "UV"
menu_path = "Mesh/UV"
def draw(self, _context):
layout = self.layout
self.node_operator(layout, "GeometryNodeUVPackIslands")
self.node_operator(layout, "GeometryNodeUVTangent")
self.node_operator(layout, "GeometryNodeUVUnwrap")
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_utilities_vector_base(node_add_menu.NodeMenu):
bl_label = "Vector"
menu_path = "Utilities/Vector"
def draw(self, context):
layout = self.layout
self.node_operator(layout, "ShaderNodeCombineXYZ")
props = self.node_operator(layout, "ShaderNodeMapRange")
ops = props.settings.add()
ops.name = "data_type"
ops.value = "'FLOAT_VECTOR'"
props = self.node_operator(layout, "ShaderNodeMix", label="Mix Vector")
ops = props.settings.add()
ops.name = "data_type"
ops.value = "'VECTOR'"
self.node_operator(layout, "ShaderNodeSeparateXYZ")
layout.separator()
self.node_operator(layout, "ShaderNodeRadialTiling")
self.node_operator(layout, "ShaderNodeVectorCurve")
self.node_operator_with_searchable_enum(context, layout, "ShaderNodeVectorMath", "operation")
self.node_operator(layout, "ShaderNodeVectorRotate")
self.draw_assets_for_catalog(layout, self.menu_path)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_volume_base(node_add_menu.NodeMenu):
bl_label = "Volume"
bl_translation_context = i18n_contexts.id_id
def draw(self, context):
layout = self.layout
self.draw_menu(layout, path="Volume/Read")
self.draw_menu(layout, path="Volume/Sample")
self.draw_menu(layout, path="Volume/Write")
layout.separator()
self.draw_menu(layout, path="Volume/Operations")
self.draw_menu(layout, path="Volume/Primitives")
self.draw_assets_for_catalog(layout, self.bl_label)
2022-09-27 16:36:27 +10:00
class NODE_MT_gn_volume_read_base(node_add_menu.NodeMenu):
bl_label = "Read"
menu_path = "Volume/Read"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "GeometryNodeGetNamedGrid")
self.node_operator(layout, "GeometryNodeGridInfo")
self.node_operator(layout, "GeometryNodeInputVoxelIndex")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_volume_write_base(node_add_menu.NodeMenu):
bl_label = "Write"
menu_path = "Volume/Write"
def draw(self, context):
layout = self.layout
self.node_operator(layout, "GeometryNodeStoreNamedGrid")
self.node_operator(layout, "GeometryNodeSetGridBackground")
self.node_operator(layout, "GeometryNodeSetGridTransform")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_volume_sample_base(node_add_menu.NodeMenu):
bl_label = "Sample"
menu_path = "Volume/Sample"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "GeometryNodeSampleGrid")
self.node_operator(layout, "GeometryNodeSampleGridIndex")
self.node_operator(layout, "GeometryNodeGridAdvect")
self.node_operator(layout, "GeometryNodeGridCurl")
self.node_operator(layout, "GeometryNodeGridDivergence")
self.node_operator(layout, "GeometryNodeGridGradient")
self.node_operator(layout, "GeometryNodeGridLaplacian")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_volume_operations_base(node_add_menu.NodeMenu):
bl_label = "Operations"
menu_path = "Volume/Operations"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "GeometryNodeVolumeToMesh")
self.node_operator(layout, "GeometryNodeGridToMesh")
self.node_operator(layout, "GeometryNodeSDFGridBoolean")
self.node_operator(layout, "GeometryNodeSDFGridFillet")
self.node_operator(layout, "GeometryNodeSDFGridLaplacian")
self.node_operator(layout, "GeometryNodeSDFGridMean")
self.node_operator(layout, "GeometryNodeSDFGridMeanCurvature")
self.node_operator(layout, "GeometryNodeSDFGridMedian")
self.node_operator(layout, "GeometryNodeSDFGridOffset")
self.node_operator(layout, "GeometryNodeFieldToGrid")
self.node_operator(layout, "GeometryNodeGridPrune")
self.node_operator(layout, "GeometryNodeGridVoxelize")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_volume_primitives_base(node_add_menu.NodeMenu):
bl_label = "Primitives"
menu_path = "Volume/Primitives"
def draw(self, context):
del context
layout = self.layout
self.node_operator(layout, "GeometryNodeVolumeCube")
self.draw_assets_for_catalog(layout, self.menu_path)
class NODE_MT_gn_all_base(node_add_menu.NodeMenu):
bl_label = ""
menu_path = "Root"
# NOTE: Menus are looked up via their label, this is so that both the Add
# & Swap menus can share the same layout while each using their
# corresponding menus
def draw(self, context):
del context
layout = self.layout
self.draw_menu(layout, "Input")
self.draw_menu(layout, "Output")
layout.separator()
self.draw_menu(layout, "Attribute")
self.draw_menu(layout, "Geometry")
layout.separator()
self.draw_menu(layout, "Curve")
self.draw_menu(layout, "Grease Pencil")
self.draw_menu(layout, "Instances")
self.draw_menu(layout, "Mesh")
self.draw_menu(layout, "Point")
self.draw_menu(layout, "Volume")
layout.separator()
self.draw_menu(layout, "Simulation")
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
layout.separator()
self.draw_menu(layout, "Color")
self.draw_menu(layout, "Texture")
self.draw_menu(layout, "Utilities")
layout.separator()
self.draw_root_assets(layout)
layout.separator()
self.draw_menu(layout, "Group")
self.draw_menu(layout, "Layout")
add_menus = {
# menu bl_idname: baseclass
"NODE_MT_geometry_node_GEO_ATTRIBUTE": NODE_MT_gn_attribute_base,
"NODE_MT_geometry_node_GEO_INPUT": NODE_MT_gn_input_base,
"NODE_MT_geometry_node_GEO_INPUT_CONSTANT": NODE_MT_gn_input_constant_base,
"NODE_MT_geometry_node_GEO_INPUT_GIZMO": NODE_MT_gn_input_gizmo_base,
"NODE_MT_geometry_node_GEO_INPUT_GROUP": NODE_MT_gn_input_group_base,
"NODE_MT_category_import": NODE_MT_gn_input_import_base,
"NODE_MT_geometry_node_GEO_INPUT_SCENE": NODE_MT_gn_input_scene_base,
"NODE_MT_category_GEO_OUTPUT": NODE_MT_gn_output_base,
"NODE_MT_geometry_node_GEO_CURVE": NODE_MT_gn_curve_base,
"NODE_MT_geometry_node_GEO_CURVE_READ": NODE_MT_gn_curve_read_base,
"NODE_MT_geometry_node_GEO_CURVE_SAMPLE": NODE_MT_gn_curve_sample_base,
"NODE_MT_geometry_node_GEO_CURVE_WRITE": NODE_MT_gn_curve_write_base,
"NODE_MT_geometry_node_GEO_CURVE_OPERATIONS": NODE_MT_gn_curve_operations_base,
"NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE": NODE_MT_gn_curve_primitives_base,
"NODE_MT_geometry_node_curve_topology": NODE_MT_gn_curve_topology_base,
"NODE_MT_geometry_node_grease_pencil": NODE_MT_gn_grease_pencil_base,
"NODE_MT_geometry_node_grease_pencil_read": NODE_MT_gn_grease_pencil_read_base,
"NODE_MT_geometry_node_grease_pencil_write": NODE_MT_gn_grease_pencil_write_base,
"NODE_MT_geometry_node_grease_pencil_operations": NODE_MT_gn_grease_pencil_operations_base,
"NODE_MT_geometry_node_GEO_GEOMETRY": NODE_MT_gn_geometry_base,
"NODE_MT_geometry_node_GEO_GEOMETRY_READ": NODE_MT_gn_geometry_read_base,
"NODE_MT_geometry_node_GEO_GEOMETRY_WRITE": NODE_MT_gn_geometry_write_base,
"NODE_MT_geometry_node_GEO_GEOMETRY_OPERATIONS": NODE_MT_gn_geometry_operations_base,
"NODE_MT_geometry_node_GEO_GEOMETRY_SAMPLE": NODE_MT_gn_geometry_sample_base,
"NODE_MT_geometry_node_GEO_INSTANCE": NODE_MT_gn_instance_base,
"NODE_MT_geometry_node_GEO_MESH": NODE_MT_gn_mesh_base,
"NODE_MT_geometry_node_GEO_MESH_READ": NODE_MT_gn_mesh_read_base,
"NODE_MT_geometry_node_GEO_MESH_SAMPLE": NODE_MT_gn_mesh_sample_base,
"NODE_MT_geometry_node_GEO_MESH_WRITE": NODE_MT_gn_mesh_write_base,
"NODE_MT_geometry_node_GEO_MESH_OPERATIONS": NODE_MT_gn_mesh_operations_base,
"NODE_MT_category_PRIMITIVES_MESH": NODE_MT_gn_mesh_uv_base,
"NODE_MT_geometry_node_mesh_topology": NODE_MT_gn_mesh_topology_base,
"NODE_MT_category_GEO_UV": NODE_MT_gn_mesh_primitives_base,
"NODE_MT_category_GEO_POINT": NODE_MT_gn_point_base,
"NODE_MT_category_simulation": NODE_MT_gn_simulation_base,
"NODE_MT_category_GEO_VOLUME": NODE_MT_gn_volume_base,
"NODE_MT_geometry_node_GEO_VOLUME_READ": NODE_MT_gn_volume_read_base,
"NODE_MT_geometry_node_volume_sample": NODE_MT_gn_volume_sample_base,
"NODE_MT_geometry_node_GEO_VOLUME_WRITE": NODE_MT_gn_volume_write_base,
"NODE_MT_geometry_node_GEO_VOLUME_OPERATIONS": NODE_MT_gn_volume_operations_base,
"NODE_MT_geometry_node_GEO_VOLUME_PRIMITIVES": NODE_MT_gn_volume_primitives_base,
"NODE_MT_geometry_node_GEO_COLOR": NODE_MT_gn_color_base,
"NODE_MT_geometry_node_GEO_MATERIAL": NODE_MT_gn_material_base,
"NODE_MT_category_GEO_TEXTURE": NODE_MT_gn_texture_base,
"NODE_MT_category_GEO_UTILITIES": NODE_MT_gn_utilities_base,
"NODE_MT_category_GEO_TEXT": NODE_MT_gn_utilities_text_base,
"NODE_MT_category_GEO_VECTOR": NODE_MT_gn_utilities_vector_base,
"NODE_MT_category_utilities_bundle": NODE_MT_category_utilities_bundle_base,
"NODE_MT_category_utilities_closure": NODE_MT_category_utilities_closure_base,
"NODE_MT_category_GEO_UTILITIES_FIELD": NODE_MT_gn_utilities_field_base,
"NODE_MT_category_GEO_UTILITIES_MATH": NODE_MT_gn_utilities_math_base,
"NODE_MT_category_GEO_UTILITIES_ROTATION": NODE_MT_gn_utilities_rotation_base,
"NODE_MT_category_utilities_list": NODE_MT_gn_utilities_list_base,
"NODE_MT_category_utilities_matrix": NODE_MT_gn_utilities_matrix_base,
"NODE_MT_category_GEO_UTILITIES_DEPRECATED": NODE_MT_gn_utilities_deprecated_base,
"NODE_MT_geometry_node_add_all": NODE_MT_gn_all_base,
}
add_menus = node_add_menu.generate_menus(
add_menus,
template=node_add_menu.AddNodeMenu,
base_dict=node_add_menu.add_base_pathing_dict
)
swap_menus = {
# menu bl_idname: baseclass
"NODE_MT_gn_attribute_swap": NODE_MT_gn_attribute_base,
"NODE_MT_gn_input_swap": NODE_MT_gn_input_base,
"NODE_MT_gn_input_constant_swap": NODE_MT_gn_input_constant_base,
"NODE_MT_gn_input_gizmo_swap": NODE_MT_gn_input_gizmo_base,
"NODE_MT_gn_input_group_swap": NODE_MT_gn_input_group_base,
"NODE_MT_gn_input_import_swap": NODE_MT_gn_input_import_base,
"NODE_MT_gn_input_scene_swap": NODE_MT_gn_input_scene_base,
"NODE_MT_gn_output_swap": NODE_MT_gn_output_base,
"NODE_MT_gn_curve_swap": NODE_MT_gn_curve_base,
"NODE_MT_gn_curve_read_swap": NODE_MT_gn_curve_read_base,
"NODE_MT_gn_curve_sample_swap": NODE_MT_gn_curve_sample_base,
"NODE_MT_gn_curve_write_swap": NODE_MT_gn_curve_write_base,
"NODE_MT_gn_curve_operations_swap": NODE_MT_gn_curve_operations_base,
"NODE_MT_gn_curve_primitives_swap": NODE_MT_gn_curve_primitives_base,
"NODE_MT_gn_curve_topology_swap": NODE_MT_gn_curve_topology_base,
"NODE_MT_gn_grease_pencil_swap": NODE_MT_gn_grease_pencil_base,
"NODE_MT_gn_grease_pencil_read_swap": NODE_MT_gn_grease_pencil_read_base,
"NODE_MT_gn_grease_pencil_write_swap": NODE_MT_gn_grease_pencil_write_base,
"NODE_MT_gn_grease_pencil_operations_swap": NODE_MT_gn_grease_pencil_operations_base,
"NODE_MT_gn_geometry_swap": NODE_MT_gn_geometry_base,
"NODE_MT_gn_geometry_read_swap": NODE_MT_gn_geometry_read_base,
"NODE_MT_gn_geometry_write_swap": NODE_MT_gn_geometry_write_base,
"NODE_MT_gn_geometry_operations_swap": NODE_MT_gn_geometry_operations_base,
"NODE_MT_gn_geometry_sample_swap": NODE_MT_gn_geometry_sample_base,
"NODE_MT_gn_instance_swap": NODE_MT_gn_instance_base,
"NODE_MT_gn_mesh_swap": NODE_MT_gn_mesh_base,
"NODE_MT_gn_mesh_read_swap": NODE_MT_gn_mesh_read_base,
"NODE_MT_gn_mesh_sample_swap": NODE_MT_gn_mesh_sample_base,
"NODE_MT_gn_mesh_write_swap": NODE_MT_gn_mesh_write_base,
"NODE_MT_gn_mesh_operations_swap": NODE_MT_gn_mesh_operations_base,
"NODE_MT_gn_mesh_uv_swap": NODE_MT_gn_mesh_uv_base,
"NODE_MT_gn_mesh_topology_swap": NODE_MT_gn_mesh_topology_base,
"NODE_MT_gn_mesh_primitives_swap": NODE_MT_gn_mesh_primitives_base,
"NODE_MT_gn_point_swap": NODE_MT_gn_point_base,
"NODE_MT_gn_simulation_swap": NODE_MT_gn_simulation_base,
"NODE_MT_gn_volume_swap": NODE_MT_gn_volume_base,
"NODE_MT_gn_volume_read_swap": NODE_MT_gn_volume_read_base,
"NODE_MT_gn_volume_sample_swap": NODE_MT_gn_volume_sample_base,
"NODE_MT_gn_volume_write_swap": NODE_MT_gn_volume_write_base,
"NODE_MT_gn_volume_operations_swap": NODE_MT_gn_volume_operations_base,
"NODE_MT_gn_volume_primitives_swap": NODE_MT_gn_volume_primitives_base,
"NODE_MT_gn_color_swap": NODE_MT_gn_color_base,
"NODE_MT_gn_material_swap": NODE_MT_gn_material_base,
"NODE_MT_gn_texture_swap": NODE_MT_gn_texture_base,
"NODE_MT_gn_utilities_swap": NODE_MT_gn_utilities_base,
"NODE_MT_gn_utilities_text_swap": NODE_MT_gn_utilities_text_base,
"NODE_MT_gn_utilities_vector_swap": NODE_MT_gn_utilities_vector_base,
"NODE_MT_gn_utilities_bundle_swap": NODE_MT_category_utilities_bundle_base,
"NODE_MT_gn_utilities_closure_swap": NODE_MT_category_utilities_closure_base,
"NODE_MT_gn_utilities_field_swap": NODE_MT_gn_utilities_field_base,
"NODE_MT_gn_utilities_math_swap": NODE_MT_gn_utilities_math_base,
"NODE_MT_gn_utilities_rotation_swap": NODE_MT_gn_utilities_rotation_base,
"NODE_MT_gn_utilities_list_swap": NODE_MT_gn_utilities_list_base,
"NODE_MT_gn_utilities_matrix_swap": NODE_MT_gn_utilities_matrix_base,
"NODE_MT_gn_utilities_deprecated_swap": NODE_MT_gn_utilities_deprecated_base,
"NODE_MT_geometry_node_swap_all": NODE_MT_gn_all_base,
}
swap_menus = node_add_menu.generate_menus(
swap_menus,
template=node_add_menu.SwapNodeMenu,
base_dict=node_add_menu.swap_base_pathing_dict
)
classes = (
*add_menus,
*swap_menus,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)