6
$\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
    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$ Jan 10, 2022 at 13:36

1 Answer 1

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$ 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$ 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
    Feb 25 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
    May 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 the node_group.inputs, e.g. bpy.context.active_object.modifiers['GeometryNodes'].node_group.inputs.new('NodeSocketMaterial', 'Material') $\endgroup$
    – ofekp
    Jul 27 at 14:09

You must log in to answer this question.

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