Well since I couldn't find an exact solution for this anywhere, I came up with a Python script that processes all Array Modifiers of an object to create clones of such object (rather than just create geometry like Blender does) that correspond exactly to the distribution obtained with the Array Modifier.
This is particularly useful for me when I create something using Array Modifiers and want to export the whole geometry resulting from applying them but also the positions, rotations and scales of all parts. For example I use this in Unity to have one joined mesh to draw efficiently but using this script I can also keep the information about the parts' position, rotation and scale in Unity's Transforms and use this information for interactions.
import bpy
from mathutils import Vector, Matrix, Quaternion, Euler
from random import uniform
# Uses Array modifier data to generate OBJECTS rather than just geometry
# 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
offsetObj = aMod.offset_object
print(offsetObj.name)
if(offsetObj == None or not aMod.use_object_offset):
print("offsetObject not set or not active")
else:
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() @ offsetObj.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)
Blender Array Modifier
Result after running the script
All clones of the object are transformed in a way that matches the Array preview.
Do mind that this is not perfect but I hope it helps.