2
$\begingroup$

I'd like to add similar Geometry Node Groups to some curves. I try to use a function. A simplified version of the code is this:

import bpy
def purge_orphans():

    if bpy.app.version >= (3, 0, 0):
        # run this only for Blender versions 3.0 and higher
        bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
    else:
        # run this only for Blender versions lower than 3.0
        # call purge_orphans() recursively until there are no more orphan data blocks to purge
        result = bpy.ops.outliner.orphans_purge()
        if result.pop() != "CANCELLED":
            purge_orphans()

def crea_nodes(ProfileRadius):
    
    bpy.ops.node.new_geometry_nodes_modifier()
    node_tree = bpy.data.node_groups["Geometry Nodes"]
    
    out_node = node_tree.nodes["Group Output"]
    in_node = node_tree.nodes["Group Input"]
    
    circle_node = node_tree.nodes.new(type="GeometryNodeCurvePrimitiveCircle")
    circle_node.inputs["Radius"].default_value =ProfileRadius
    
    to_Mesh_node = node_tree.nodes.new(type="GeometryNodeCurveToMesh")
    
    node_tree.links.new(in_node.outputs['Geometry'], to_Mesh_node.inputs['Curve'])
    node_tree.links.new(to_Mesh_node.outputs['Mesh'], out_node.inputs['Geometry'])
    node_tree.links.new(circle_node.outputs['Curve'], to_Mesh_node.inputs['Profile Curve'])
    
if __name__ == "__main__":
    
    purge_orphans()
    bpy.ops.curve.primitive_bezier_circle_add(radius=1, location=(0, 0, 0), scale=(1, 1, 1))
    crea_nodes(0.2)
    
    bpy.ops.curve.primitive_bezier_circle_add(radius=1, location=(0, 2, 0), scale=(1, 1, 1))
    crea_nodes(0.1)

The problem

This is what I get:

The two function calls appear to affect the first curve:

While on the second curve I get this:

Please, what is the correct code?

$\endgroup$
3
  • $\begingroup$ Nowhere in your code you say "where" you want the nodes to go. Probably going to the active object. So, either attach the nodes to some object or make the second circle active before creating the second tree. $\endgroup$ Commented Jan 27 at 0:07
  • $\begingroup$ You have a hard coded named geometry node selected node_tree = bpy.data.node_groups["Geometry Nodes"] each call of crea_nodes() modifies the same geometry node. $\endgroup$ Commented Jan 27 at 0:39
  • $\begingroup$ You want to either create a node group once (perhaps outside of the script, so the script runs when it is already created) and add it (the same, node group) to each object of interest, so they share one node group, or you want to create a new node group for each object. In former case, you need to give each node group a separate name, as names have to be unique. In latter case you need to separate the code creating the node group, and the code assigning the node group to a modifier - the former you run once, the latter for each object. $\endgroup$ Commented Jan 27 at 9:19

2 Answers 2

6
$\begingroup$

node_tree = bpy.data.node_groups["Geometry Nodes"] will affect the same Geometry Nodes tree every time you call the function. If you look closely in your first object's Geometry Nodes Tree you should see duplicated nodes.

You can access the last created node tree with node_tree = bpy.data.node_groups[-1] instead of this line.

However this process will create a new, identical geometry nodes tree where only a value on a node changes. You can use the Group Input to setup a custom value per modifier. Something like that :

Note the script will only work for Blender > 4.3 when the nodes API changed a bit to accommodate panels.

import bpy

def purge_orphans():
    bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)


def crea_nodes(ProfileRadius):
    node_tree = bpy.data.node_groups.get("Geometry Nodes")
    if node_tree:
        mod = bpy.context.active_object.modifiers.new(type="NODES", name="Geometry Nodes")
        mod.node_group = node_tree
    else:
        bpy.ops.node.new_geometry_nodes_modifier()
        node_tree = bpy.data.node_groups[-1]
    
        out_node = node_tree.nodes["Group Output"]
        in_node = node_tree.nodes["Group Input"]
        
        circle_node = node_tree.nodes.new(type="GeometryNodeCurvePrimitiveCircle")
        circle_node.inputs["Radius"].default_value =ProfileRadius
        
        to_Mesh_node = node_tree.nodes.new(type="GeometryNodeCurveToMesh")
        
        node_tree.links.new(in_node.outputs['Geometry'], to_Mesh_node.inputs['Curve'])
        node_tree.links.new(to_Mesh_node.outputs['Mesh'], out_node.inputs['Geometry'])
        node_tree.links.new(circle_node.outputs['Curve'], to_Mesh_node.inputs['Profile Curve'])
        
        node_tree.interface.new_socket(in_out="INPUT", name="Radius", socket_type="NodeSocketFloat")
        node_tree.links.new(in_node.outputs[1], circle_node.inputs["Radius"])
        
        mod = next(m for m in bpy.context.active_object.modifiers if m.node_group == node_tree)
    
    radius_input = next(i for i in node_tree.interface.items_tree if i.name == "Radius")
    mod[radius_input.identifier] = ProfileRadius
    
    
if __name__ == "__main__":
    purge_orphans()
    bpy.ops.curve.primitive_bezier_circle_add(radius=1, location=(0, 0, 0), scale=(1, 1, 1))
    crea_nodes(0.2)
    
    bpy.ops.curve.primitive_bezier_circle_add(radius=1, location=(0, 2, 0), scale=(1, 1, 1))
    crea_nodes(0.1)

Relevant docs

$\endgroup$
0
1
$\begingroup$

Combining the valuable information provided in Gorgious's answer and the answers to this question I came up with this script:

import bpy

def purge_orphans():
    if bpy.app.version >= (3, 0, 0):
        # run this only for Blender versions 3.0 and higher
        bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
    else:
        # run this only for Blender versions lower than 3.0
        # call purge_orphans() recursively until there are no more orphan data blocks to purge
        result = bpy.ops.outliner.orphans_purge()
        if result.pop() != "CANCELLED":
            purge_orphans()
            
def create_node_group():
    # create a new Geometry Node Tree
    node_tree=bpy.data.node_groups.new('GeoNodes','GeometryNodeTree')
    
    # create and link the nodes
    in_node=node_tree.nodes.new(type="NodeGroupInput")
    out_node=node_tree.nodes.new(type="NodeGroupOutput")
    
    # add a 'Geometry' socket to the input and output nodes (they are not added at the above creation)
    node_tree.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
    node_tree.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
    
    circle_node = node_tree.nodes.new(type="GeometryNodeCurvePrimitiveCircle")
    
    to_Mesh_node = node_tree.nodes.new(type="GeometryNodeCurveToMesh")
    
    node_tree.links.new(in_node.outputs['Geometry'], to_Mesh_node.inputs['Curve'])
    node_tree.links.new(to_Mesh_node.outputs['Mesh'], out_node.inputs['Geometry'])
    node_tree.links.new(circle_node.outputs['Curve'], to_Mesh_node.inputs['Profile Curve'])
    
    # creates a new input parameter that controls the radius of the profile
    node_tree.interface.new_socket(in_out="INPUT", name="Radius", socket_type="NodeSocketFloat")
    node_tree.links.new(in_node.outputs['Radius'], circle_node.inputs["Radius"])
        
    return node_tree


def assign_node_tree(to_obj=bpy.context.active_object, node_tree=None, ProfileRadius=0.5):
    # add to_object a Geometry Node modifier with node_tree as node_group
    mod = to_obj.modifiers.new(type="NODES", name="Geo Nodes")
    mod.node_group = node_tree
    # set input parameter of the modifier
    radius_input=node_tree.interface.items_tree['Radius']
    mod[radius_input.identifier] = ProfileRadius
    
 
if __name__ == "__main__":
    purge_orphans()
    
    node_tree=create_node_group()
    
    bpy.ops.curve.primitive_bezier_circle_add(radius=1, location=(0, 0, 0), scale=(1, 1, 1))
    act_obj=bpy.context.active_object
    assign_node_tree(to_obj=act_obj, node_tree=node_tree, ProfileRadius=0.1)
    
    bpy.ops.curve.primitive_bezier_circle_add(radius=1.5, location=(0, 3, 0), scale=(1, 1, 1))
    act_obj=bpy.context.active_object
    assign_node_tree(to_obj=act_obj, node_tree=node_tree, ProfileRadius=0.15)
$\endgroup$
1
  • $\begingroup$ Nice, thanks for sharing. Do note using node_tree.interface.new_socket API means the script will only work with Blender version >= 4.0 so testing for Blender versions < 3.0 doesn't really make sense since running it on older version than 4.0 will throw an error. Cheers $\endgroup$ Commented Jan 28 at 8:22

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.