Skip to content

Instantly share code, notes, and snippets.

@rstecca
Last active December 30, 2020 16:40
Show Gist options
  • Save rstecca/7fe38e3846749c0118e6bd20cb83c39c to your computer and use it in GitHub Desktop.
Save rstecca/7fe38e3846749c0118e6bd20cb83c39c to your computer and use it in GitHub Desktop.
Blender Python script to process Array Modifiers to create transformed Object clones rather than just geometry
import bpy
from mathutils import Vector, Matrix, Quaternion, Euler
from random import uniform
# Uses Array modifier data to generate OBJECTS rather than just geometry
# Find this Gist at https://gist.github.com/rstecca/7fe38e3846749c0118e6bd20cb83c39c
# github.com/rstecca
# Only works with Array Modifiers with ObjectOffset
# Tested in Blender 2.83.1
# HOW TO USE
# - Select an object that has at least 1 Array Modifier
# - Make sure all Array Modifiers on that object have an Object Offset set and active
# (those that aren't set or active will be skipped)
# - Optionally disable the Render/Realtime Display of each Array modifiers so you won't see the Blender's result
# - Launch this script with Alt+P
# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Removes all modifiers from an object
## Needed to cleanup the clones
def RemoveModifiers(_obj):
for mod in _obj.modifiers:
_obj.modifiers.remove(mod)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Recursively applies the affine transformation
# copying _obj and applying the transformation described by _matrix to its copies, relatively to the currently processed object matrix
# writes all cloned objects to _clonelist
## _OT is the original object transformation matrix
def CopyAffine(_context, _obj, _matrix, _count, _clonelist):
copy = _obj.copy()
RemoveModifiers(copy)
_clonelist.append(copy)
copy.matrix_world = _obj.matrix_world @ _matrix #@ _obj.matrix_world @ _matrix.inverted() # _matrix @ _obj.matrix_world #
_context.collection.objects.link(copy)
c = _count - 1
if (c > 1):
CopyAffine(_context, copy, _matrix, c, _clonelist)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Gets all array modifiers from _obj
def GetArrayModifiers(_obj):
arrModifiers = []
allModifiers = _obj.modifiers
for mod in allModifiers:
if(mod.name.startswith("Array")):
arrModifiers.append(mod)
return arrModifiers
# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Process all Array Modifiers
def ApplyArrayModifiers(_context, _targetObject):
arrModifiers = GetArrayModifiers(_targetObject)
if(len(arrModifiers) == 0):
print("No Array modifiers found in " + _targetObject.name)
else:
objs = []
objs.append(_targetObject)
arrModifiers.reverse() # we have to go backwards, from the last modifiers to the first
for aMod in arrModifiers:
count = aMod.count
clones = []
# Here we need to counter the target object's transformation. Can this be seen as a change of base?
D = _targetObject.matrix_world.inverted()
if(aMod.use_object_offset):
D = D @ aMod.offset_object.matrix_world
for obj in objs:
CopyAffine(_context, obj, D, count, clones)
objs = objs + clones # concatenate clones to all objects for the next iteration
######################################
print(" - - - - - - - - - - - - - - ")
context = bpy.context
A = context.view_layer.objects.active
ApplyArrayModifiers(context, A)
@rstecca
Copy link
Author

rstecca commented Jul 23, 2020

In revisions < 3 there was a bug: I was using stupid brute force to create the matrix that's passed to the recursive method (in that TransformDifference method that's now gone). This caused the algorithm to fail when the target object transform (TRS) was not ((0,0,0),(0,0,0),(1,1,1)).
This is now solved much more stylishly with line D = _targetObject.matrix_world.inverted() @ offsetObj.matrix_world where we counter the target object's transformation. In other words we take the offset matrix and we change its basis to the target object's (but I might be forcing the definition of change of basis here?)
From revision 3 the target object can be transformed arbitrarily and the coherence with Blender's Array Modifier preview is restored.

@rstecca
Copy link
Author

rstecca commented Dec 30, 2020

In rev. 7 there's an attempt to make constant and relative offset work nicely in all cases (when the root object is not (0,0,0), combined with other offsets, when there's more than one Array Modifier, etc...).

@rstecca
Copy link
Author

rstecca commented Dec 30, 2020

Rev. 8: rolled back to a version that works well but for Object Offset only

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment