Last active
December 30, 2020 16:40
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
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...).
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
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.