I'm trying to make a script that assigns a custom Geometry Node Tree to a bezier curve. The node tree contains 3 custom node groups. I have created the groups with code but I can't assign them to the Geometry Node Modifier/Root Tree. I basically want the script to create a bezier curve with the Geometry Nodes already assigned, how can I do that? Thanks!!
-
$\begingroup$ Hello ! So you want your script to 1. Create a bezier curve object 2. Assign an existing GN tree to the bezier curve object ? $\endgroup$– GorgiousJan 10, 2022 at 13:32
-
$\begingroup$ Yes! My script thud far creates the curve with a GN modifier, creates the custom node groups that will be inside the GN tree, but I can't create the actual node tree that is assigned to the curve.. $\endgroup$– Alex ZampetasJan 10, 2022 at 13:36
1 Answer
You need to access the node tree through the geometry nodes modifier. Here's the start of a script that would allow you to do that:
import bpy
from mathutils import Vector
bpy.ops.curve.primitive_bezier_curve_add()
bpy.ops.object.modifier_add(type='NODES')
curve = bpy.context.active_object
def new_GeometryNodes_group():
''' Create a new empty node group that can be used
in a GeometryNodes modifier.
'''
node_group = bpy.data.node_groups.new('GeometryNodes', 'GeometryNodeTree')
inNode = node_group.nodes.new('NodeGroupInput')
inNode.outputs.new('NodeSocketGeometry', 'Geometry')
outNode = node_group.nodes.new('NodeGroupOutput')
outNode.inputs.new('NodeSocketGeometry', 'Geometry')
node_group.links.new(inNode.outputs['Geometry'], outNode.inputs['Geometry'])
inNode.location = Vector((-1.5*inNode.width, 0))
outNode.location = Vector((1.5*outNode.width, 0))
return node_group
# In 3.2 Adding the modifier no longer automatically creates a node group.
# This test could be done with versioning, but this approach is more general
# in case a later version of Blender goes back to including a node group.
if curve.modifiers[-1].node_group:
node_group = curve.modifiers[-1].node_group
else:
node_group = new_GeometryNodes_group()
curve.modifiers[-1].node_group = node_group
nodes = node_group.nodes
Update: As per an edit by Gorgious, Blender 3.2 no longer creates a node tree when you add the modifier. I've updated that edit to handle both older versions of Blender and Blender 3.2
You still have to hook the Geometry output of the Group Input node to your first node and the Geometry output of your last node to the Geometry Input node of the Group Output node. Here's an example with a single node:
group_in = nodes.get('Group Input')
group_out = nodes.get('Group Output')
new_node = nodes.new('GeometryNodeMeshToPoints')
node_group.links.new(group_in.outputs['Geometry'], new_node.inputs['Mesh'])
node_group.links.new(new_node.outputs['Points'], group_out.inputs['Geometry'])
For Blender 3.6
import bpy
from mathutils import Vector
bpy.ops.curve.primitive_bezier_curve_add()
bpy.ops.object.modifier_add(type='NODES')
curve = bpy.context.active_object
def new_GeometryNodes_group():
''' Create a new empty node group that can be used
in a GeometryNodes modifier.
'''
node_group = bpy.data.node_groups.new('GeometryNodes', 'GeometryNodeTree')
inNode = node_group.nodes.new('NodeGroupInput')
node_group.outputs.new('NodeSocketGeometry', 'Geometry')
outNode = node_group.nodes.new('NodeGroupOutput')
node_group.inputs.new('NodeSocketGeometry', 'Geometry')
node_group.links.new(inNode.outputs['Geometry'], outNode.inputs['Geometry'])
inNode.location = Vector((-1.5*inNode.width, 0))
outNode.location = Vector((1.5*outNode.width, 0))
return node_group
# In 3.2 Adding the modifier no longer automatically creates a node group.
# This test could be done with versioning, but this approach is more general
# in case a later version of Blender goes back to including a node group.
if curve.modifiers[-1].node_group:
node_group = curve.modifiers[-1].node_group
else:
node_group = new_GeometryNodes_group()
curve.modifiers[-1].node_group = node_group
nodes = node_group.nodes
A list of node sockets can be added with reference to this document.
https://docs.blender.org/api/current/bpy.types.NodeSocketStandard.html
-
-
$\begingroup$ @gorgious thanks for the edit. I modified it so that it doesn't create an extra node tree in versions of blender prior to 3.2 As a bonus I threw in a function to create the node group. $\endgroup$ Jun 23, 2022 at 17:07
-
$\begingroup$ Hello ! This setup throws on V3.4 a
RuntimeError: Error: Cannot add socket to built-in node
on this lineinNode.outputs.new("NodeSocketGeometry", "Geometry")
would you happen to know a solution ? I'm resorting to creating a temporary join geometry node to link the input and output, then deleting it but I don't like it :) $\endgroup$– GorgiousFeb 25 at 23:07 -
$\begingroup$ In 3.5 using
node_group.outputs.new('NodeSocketGeometry', 'Geometry')
instead ofinNode.outputs.new('NodeSocketGeometry', 'Geometry')
appears to fix that issue. Same case forinNode.inputs
. $\endgroup$– akrestixMay 10 at 15:05 -
$\begingroup$ In case someone reached here when trying to create a new socket in Group Input/Output, this answer was helpful for me since I was creating the new socket in the Group Input node and called
new
directly from that node, while I should have been calling it from thenode_group.inputs
, e.g.bpy.context.active_object.modifiers['GeometryNodes'].node_group.inputs.new('NodeSocketMaterial', 'Material')
$\endgroup$– ofekpJul 27 at 14:09