7
$\begingroup$

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!!

$\endgroup$
2
  • $\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$
    – Gorgious
    Commented Jan 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$ Commented Jan 10, 2022 at 13:36

3 Answers 3

12
$\begingroup$

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

$\endgroup$
5
  • $\begingroup$ Thanks! That was it! $\endgroup$ Commented Jan 10, 2022 at 23:57
  • $\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$ Commented 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 line inNode.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$
    – Gorgious
    Commented Feb 25, 2023 at 23:07
  • $\begingroup$ In 3.5 using node_group.outputs.new('NodeSocketGeometry', 'Geometry') instead of inNode.outputs.new('NodeSocketGeometry', 'Geometry') appears to fix that issue. Same case for inNode.inputs. $\endgroup$
    – akrestix
    Commented May 10, 2023 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 the node_group.inputs, e.g. bpy.context.active_object.modifiers['GeometryNodes'].node_group.inputs.new('NodeSocketMaterial', 'Material') $\endgroup$
    – ofekp
    Commented Jul 27, 2023 at 14:09
2
$\begingroup$

Blender 4.0

inputs and outputs has been replaced by this:

node_group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")

in_out= can be "INPUT" or "OUTPUT"

Changelog: https://docs.blender.org/api/4.0/change_log.html#bpy-types-nodetree

Full script:

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')
    # NEW IN BLENDER > 4.0 -> creates a Group Input Socket
    node_group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
    outNode = node_group.nodes.new('NodeGroupOutput')
    # NEW IN BLENDER > 4.0 -> creates a Group Ouput Socket
    node_group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
    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
$\endgroup$
1
  • $\begingroup$ thanks, that helped a lot! $\endgroup$
    – Kolibril
    Commented Jun 3 at 15:40
2
$\begingroup$

In Blender 4.1, the following script will produce the following screenshot:

import bpy
from mathutils import Vector

bpy.ops.object.select_all(action='DESELECT')
bpy.ops.curve.primitive_bezier_curve_add(radius=2)
bezier_curve = bpy.context.active_object
bezier_curve.name = 'MyBezierCurve'

geo_nodes = bezier_curve.modifiers.new(name='GeometryNodes', type='NODES')
node_group = bpy.data.node_groups.new('GeometryNodes', 'GeometryNodeTree')
geo_nodes.node_group = node_group

inNode = node_group.nodes.new('NodeGroupInput')
outNode = node_group.nodes.new('NodeGroupOutput')
transformNode = node_group.nodes.new('GeometryNodeTransform')

node_group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
node_group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")

node_group.links.new(inNode.outputs['Geometry'], transformNode.inputs['Geometry'])
node_group.links.new(transformNode.outputs['Geometry'], outNode.inputs['Geometry'])

inNode.location = Vector((-3 * inNode.width, 0))
transformNode.location = Vector((0, 0))
outNode.location = Vector((3 * outNode.width, 0))

transformNode.inputs['Translation'].default_value = (0, 2, 2)

bpy.context.view_layer.update()

enter image description here

$\endgroup$
2
  • 1
    $\begingroup$ You don't have to use Vector just like you don't use it on line 27 (second last line). Welcome to BSE! $\endgroup$ Commented Jun 3 at 16:34
  • 1
    $\begingroup$ Good point and thanks for the welcome! :) $\endgroup$
    – Kolibril
    Commented Jun 4 at 8:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .