Array Modifier Grid¶
This is a practical common scenario where we need to create objects in a Grid, particularly in motion graphic projects.
In this Tutorial, We’ll see how we can write scripts and add functionality step by step. I will try to make each addition only around 5 lines.
Note
This is first tutorial so it contains somewhat detailed instruction, later tutorials aren’t as details to avoid repetition.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import bpy
# create cube
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD',
location=(0, 0, 0), scale=(1, 1, 1))
# create array
bpy.ops.object.modifier_add(type='ARRAY')
# create count = 5
bpy.context.object.modifiers["Array"].count = 5
# create step = 2
bpy.context.object.modifiers["Array"].relative_offset_displace[0] = 2
# apply modifier
bpy.ops.object.modifier_apply(modifier="Array")
# enter editmode
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.separate(type='LOOSE')
# exit editmode
bpy.ops.object.editmode_toggle()
# set pivot
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
|
- There are 4 sub-steps:
create cube.
add array modifier
change array modifier’s properties.
apply and reset origin(centering pivot)
- There are several things to note in above code, lets see.
sub-step 4 will be same however many copies we create.
sub-step 3,
relative_offset_displace
andcount
should befloatField
andintField
respectively.sub-step 2, more modifiers can be added for all 3 axis
sub-step 1 can be an option, where we create any any mesh type. (eg. Sphere,Cylinder)
- Things to note:
highlighted lines are added.
one more modifier is added.
added modifiers
relative_offset_displace
is changed so that it goes in y axis.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import bpy
# create cube
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD',
location=(0, 0, 0), scale=(1, 1, 1))
# add array modifier
bpy.ops.object.modifier_add(type='ARRAY')
bpy.ops.object.modifier_add(type='ARRAY')
# change count = 5
bpy.context.object.modifiers["Array"].count = 5
# it's named Array.001 by default
bpy.context.object.modifiers["Array.001"].count = 5
# change step = 2
bpy.context.object.modifiers["Array"].relative_offset_displace[0] = 2
bpy.context.object.modifiers["Array.001"].relative_offset_displace[0] = 0
bpy.context.object.modifiers["Array.001"].relative_offset_displace[1] = 2
# apply modifier
bpy.ops.object.modifier_apply(modifier="Array")
bpy.ops.object.modifier_apply(modifier="Array.001")
# enter editmode
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.separate(type='LOOSE')
# exit editmode
bpy.ops.object.editmode_toggle()
# set pivot
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
|
- Things to note:
highlighted lines are added.
all three modifiers are added.
added modifiers
relative_offset_displace
is changed so that it goes in z axis.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | import bpy
# create cube
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD',
location=(0, 0, 0), scale=(1, 1, 1))
# add array modifier
bpy.ops.object.modifier_add(type='ARRAY')
bpy.ops.object.modifier_add(type='ARRAY')
bpy.ops.object.modifier_add(type='ARRAY')
# change count = 5
bpy.context.object.modifiers["Array"].count = 5
# it's named Array.001 by default
bpy.context.object.modifiers["Array.001"].count = 5
bpy.context.object.modifiers["Array.002"].count = 5
# change step = 2
bpy.context.object.modifiers["Array"].relative_offset_displace[0] = 2
bpy.context.object.modifiers["Array.001"].relative_offset_displace[0] = 0
bpy.context.object.modifiers["Array.001"].relative_offset_displace[1] = 2
bpy.context.object.modifiers["Array.002"].relative_offset_displace[0] = 0
bpy.context.object.modifiers["Array.002"].relative_offset_displace[1] = 0
bpy.context.object.modifiers["Array.002"].relative_offset_displace[2] = 2
# apply modifier
bpy.ops.object.modifier_apply(modifier="Array")
bpy.ops.object.modifier_apply(modifier="Array.001")
bpy.ops.object.modifier_apply(modifier="Array.002")
# enter editmode
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.separate(type='LOOSE')
# exit editmode
bpy.ops.object.editmode_toggle()
# set pivot
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
|
Now it’s time to add the boss feature, adding UI.
Check out boss documentation documentation, especially Button class
- Things to note:
at line 4,
create_grid
function is defined to create object and add modifiers.at line 8, reference to selected object is saved in
sel_obj
, which is return at last.at line 33,
apply_mods
contains applying part, has been moved to another functionat line 54,
caller.op.quit()
quits the operator.at line 68,
UICreator.deleteAllUi(op)
deletes all previously created UI.at line 70, grid is created and returned object is saved in
sel_obj
VectorIntField and Button are created and
sel_obj
is passed asparam
value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import bpy
from boss.ui_creator import UICreator, Boss_OT_base_ui,RectData,FieldValue
def create_grid():
# create cube
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD',
location=(0, 0, 0), scale=(1, 1, 1))
sel_obj = bpy.context.object
# add array modifier
bpy.ops.object.modifier_add(type='ARRAY')
bpy.ops.object.modifier_add(type='ARRAY')
bpy.ops.object.modifier_add(type='ARRAY')
# change count = 5
bpy.context.object.modifiers["Array"].count = 3
# it's named Array.001 by default
bpy.context.object.modifiers["Array.001"].count = 3
bpy.context.object.modifiers["Array.002"].count = 1
# change step = 2
bpy.context.object.modifiers["Array"].relative_offset_displace[0] = 2
bpy.context.object.modifiers["Array.001"].relative_offset_displace[0] = 0
bpy.context.object.modifiers["Array.001"].relative_offset_displace[1] = 2
bpy.context.object.modifiers["Array.002"].relative_offset_displace[0] = 0
bpy.context.object.modifiers["Array.002"].relative_offset_displace[1] = 0
bpy.context.object.modifiers["Array.002"].relative_offset_displace[2] = 2
return sel_obj
def apply_mods(caller):
sel_obj = caller.param
bpy.context.view_layer.objects.active = sel_obj
# apply modifier
bpy.ops.object.modifier_apply(modifier="Array")
bpy.ops.object.modifier_apply(modifier="Array.001")
bpy.ops.object.modifier_apply(modifier="Array.002")
# enter editmode
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.separate(type='LOOSE')
# exit editmode
bpy.ops.object.editmode_toggle()
# set pivot
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
caller.op.quit()
def onCountChanged(caller):
sel_obj = caller.param
x,y,z = caller.value
sel_obj.modifiers["Array"].count = x
sel_obj.modifiers["Array.001"].count = y
sel_obj.modifiers["Array.002"].count = z
def ui_elements(op: Boss_OT_base_ui):
UICreator.deleteAllUi(op)
sel_obj = create_grid()
fv = FieldValue(
value=(3, 3, 1),
min=(0, 0, 0),
max=(10, 10, 10),
changeBy=(1, 1, 1)
)
vif_cnt = UICreator.vectorIntField(op, RectData(100, UICreator.rr(op).height - 150, 200, 50),
value=fv,onValueChange= onCountChanged, param=sel_obj)
UICreator.button(op, vif_cnt.rectData.getBottom(5), text='Apply', buttonData=apply_mods, param=sel_obj)
|
Note
FieldValue
class is mainly used when setting minimum and maximum limits for field value. It’s
parameters are self-explanatory. value
is default value, min
is minimum value, max
is
maximum value and changeBy
is mouse-roll update value. Be careful to pass three values for
Vector(Int/Float)Fields
. Same class is also used for (Float/Int)Field
as FieldValue(3,0,10,1)
If you want To make an addon.