TransWikia.com

python -- apply/run script for every array instance individually

Blender Asked on November 5, 2021

I have the following blender scene setup:
enter image description here
(notice the python script, will be mentioned soon in question)

As you can see, there are many boards being duplicated to form a wall.

The thing is: in real life, the middle support board is usually different for each board, like this:

enter image description here

So basically, I want to randomly set the Z position within the range of the top beam and bottom beam (of the boards in my scene) so that the middle support-beam is randomly positioned, reapplied for each array instance. So I went ahead and made a python script that randomly sets the beam’ss Z position from the top beam to bottom beam, and currently whenever I hit “Run Script”, it re-randomly sets the position of the beam, exactly how I wanted.

The problem:
As you can see, all of the array instances are the same, but I want it to randomly move the beam (a.k.a., somehow re-apply the script) for every array instance, but I have absolutely no idea how to do this.

Anyone?

3 Answers

Distribute copies.

Further to @moonboots answer can copy and distribute the object. Each copy shares the one mesh.

import bpy
from mathutils import Vector
from random import uniform


def array(context, count, rel_offset=Vector(), rnd_var_range=(0, 0), rnd_var_dir=Vector()):
    scene = context.scene
    for ob in context.selected_objects:
        mw = ob.matrix_world
        R = mw.to_quaternion().to_matrix()
        offset =  R * rel_offset.dot(ob.dimensions) * rel_offset.normalized()
        copies = count - 1
       
        for i in range(copies):
            copy = ob.copy()
            t = copy.matrix_world.translation
            t  += (i + 1) * offset            
            copy.delta_location = uniform(*rnd_var_range) * rnd_var_dir
            copy.select = True
            scene.objects.link(copy)

# test call

context = bpy.context

array(context, 
    count = 5,
    rel_offset = Vector((1.1 , 0, 0)),
    rnd_var_dir = Vector((0, 1, 0)),
    rnd_var_range = (-1, 1))
    
array(context, 
    count = 5,
    rel_offset = Vector((0, 0, 1.1)),
    rnd_var_dir = Vector((0, 1, 0)),
    rnd_var_range = (-1, 1))

enter image description here

Result of running script with Suzanne selected. The variation of both arrays in test call are in local Y direction.

The same script updated to Blender 2.8 API

import bpy
from mathutils import Vector
from random import uniform


def array(context, count, rel_offset=Vector(), rnd_var_range=(0, 0), rnd_var_dir=Vector()):
    scene = context.scene
    for ob in context.selected_objects:
        mw = ob.matrix_world
        R = mw.to_quaternion().to_matrix()
        offset =  R @ (rel_offset.dot(ob.dimensions) * rel_offset.normalized())
        copies = count - 1

        for i in range(copies):
            copy = ob.copy()
            t = copy.matrix_world.translation
            t  += (i + 1) * offset            
            copy.delta_location = uniform(*rnd_var_range) * rnd_var_dir
            context.collection.objects.link(copy)
            copy.select_set(state=True)
            context.view_layer.objects.active = copy
            
           
            

# test call

context = bpy.context

array(context, 
    count = 5,
    rel_offset = Vector((1.1 , 0, 0)),
    rnd_var_dir = Vector((0, 1, 0)),
    rnd_var_range = (-1, 1))

array(context, 
    count = 5,
    rel_offset = Vector((0, 0, 1.1)),
    rnd_var_dir = Vector((0, 1, 0)),
    rnd_var_range = (-1, 1))

Main changes are:

  • @ operator is now used for matrix/vector component-wise multiplication
  • selection of an object in context is now done with copy.select_set(state=True)
  • The link must now be done via collections and not view_layer

Answered by batFINGER on November 5, 2021

set the object name to something meaningful like "bar" or whatever. apply the modifier ass it is with no Z variation. then press [TAB] to enter edit mode, press [A] to select all, press [P] and select separate by loose parts. press [tab] back to object mode. and run:

import bpy, random as r
objs = bpy.data.objects
for i in objs:    
    if "bar" in i.name:    #"bar" name of the array obj "Cube" to you n
        i.select=True       
        bpy.context.object.location[2] =1+r.randint(-20,20)/10 #"here your random funct"

...i think this should do the thing.

Answered by Javier on November 5, 2021

I am not sure if you can change the data for individual instance of the array modifier, since, I guess, the instances share the same data block.

One option is to apply the modifier, so as to combine the data of all objects into the parent object. This will complicate the calculations, though. The other option would be creating separate object for each instance. The most obvious way to do this is to copy the mesh data mesh = bpy.data.objects[x].data.copy() and use it to create a new objectobj = bpy.data.objects.new('new_obj', mesh) followed by bpy.context.scene.objects.link(obj). The objects location can be changed after it's linked into the scene.

Answered by Blender Dadaist on November 5, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP