1
$\begingroup$

I am trying to generate roads from curves that i have placed in the scene. I have three curves and a road mesh that I apply a curve and array modifier to through python. I also set the values of the modifiers through python.

In python I loop through all objects in the scene, and for each curve I duplicate the main road mesh and apply modifiers with the new curve as the target.

My problem is that for each road it duplicates the road by an amount of 2 * the previously duplicated amount. So for the first curve it makes one road, for the second curve 2 roads, and 4 roads for the 3rd curve so on so forth.

I am fairly new to blender programming, so I am certain i'm missing something logical, but I can't figure out what.

Before operator execute

After operator execute

Finally here's the code for the operator:

def execute(self, context):
    objects = bpy.context.scene.objects
    road = bpy.data.objects["Road"]

    for obj in objects:
        if obj.type == "CURVE":
            road = bpy.data.objects["Road"]
            bpy.data.objects['Road'].select_set(True)

            newroad = road.copy()
            bpy.context.collection.objects.link(newroad)

            bpy.ops.object.modifier_add(type='ARRAY')
            bpy.ops.object.modifier_add(type='CURVE')

            arrmod = bpy.context.object.modifiers["Array"]
            curvemod = bpy.context.object.modifiers["Curve"]

            arrmod.fit_type = 'FIT_CURVE'
            arrmod.curve = bpy.data.objects[obj.name]
            curvemod.object = bpy.data.objects[obj.name]

    return{'FINISHED'}

Thanks!

$\endgroup$

1 Answer 1

1
$\begingroup$

You don't need to select the object before copying it, so bpy.data.objects['Road'].select_set(True) is redundant.

Instead of that you need to make the newly created object active before calling any bpy.ops to make the op function operate on the new object. But I guess, you can avoid call to ops altogether and simply call newroad.modifiers.new to create the modifier.

So that the modified code will look like this (this is not tested)...

def execute(self, context):
    objects = bpy.context.scene.objects
    road = bpy.data.objects["Road"]

    for obj in objects:
        if obj.type == "CURVE":
            # road = bpy.data.objects["Road"] # Already set above so not needed
            # bpy.data.objects['Road'].select_set(True) # Not needed

            newroad = road.copy()
            newroad.data = road.data.copy() # You might also want to copy the data block
            bpy.context.collection.objects.link(newroad)

            # bpy.ops.object.modifier_add(type='ARRAY')
            # bpy.ops.object.modifier_add(type='CURVE')

            # arrmod = bpy.context.object.modifiers["Array"]
            # curvemod = bpy.context.object.modifiers["Curve"]

            arrayModName = 'array'
            arrmod = newroad.modifiers.new(arrayModName, 'ARRAY')

            curveModName = 'curve'
            curvemod = newroad.modifiers.new(curveModName, 'CURVE')

            arrmod.fit_type = 'FIT_CURVE'

            # arrmod.curve = bpy.data.objects[obj.name]
            # curvemod.object = bpy.data.objects[obj.name]

            # obj is the same as bpy.data.objects[obj.name]
            arrmod.curve = obj 
            curvemod.object = obj

    return{'FINISHED'}
$\endgroup$
2
  • $\begingroup$ That worked! I'm still not entirely sure what wen't wrong logically, but I can see how this method works better. Thanks. $\endgroup$ Commented Sep 19, 2019 at 11:20
  • $\begingroup$ Glad that it worked. bpy.ops operate on the active object and the newly created object does not become active automatically. So I guess, the modifiers were getting created on whatever object was active at the time of executing the block. $\endgroup$ Commented Sep 19, 2019 at 11:38

You must log in to answer this question.

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