TransWikia.com

How to obtain a set of separate objects from an Array Modifier (or other method) and keep their rotation and scale?

Blender Asked on November 25, 2021

I have been looking for methods to create a set of objects from an Array Modifier. Normally one creates an Array, sets an Array Offset and manipulates it so that the transformation propagates to all the copies and most of the times the goal is to have a geometry as you don’t care about the sub-parts own transforms but I do this time since my goal is to export these (empty in my case) transforms to a game engine.
To expand a bit, I already know that I can Apply the modifier and then separate the meshes By Loose Parts but it’s not what I want and the reason for that is that with that method, such loose parts will inherit all the same rotation and scale from the original object; in fact the matrix of the single copy of the array is lost when applying the array.

My question is slightly different from this one:
How can I use an array modifier to create individually manipulatable objects?

in that I need the individual rotations and scales to be kept.

What I need ideally is a workflow to go from an Array Modifier (but potentially parting ways from the Array Modifier) to create a big set (big enough to discourage manual duplication) of empty objects that keep their individual position, rotation and scale.

How can I achieve that?

One Answer

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

Blender Array Modifier (used as preview)

Result after running the script
All clones of the object are transformed in a way that matches the Array preview.

The result after running the script

Answered by SteakOverflow on November 25, 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