AddOn: Duplicate Multiple Linked

Here’s a little AddOn you might find useful, it will help you quickly create multiple linked duplicates of the selected object. The AddOn adds a new menu item to the Object menu, “Duplicate Multiple Linked”. When you run the command, options in the Toolshelf allow you to interactively set them: number of copies, XYZ positional offset of the copies, and a checkbox to decide if you want the copies, including the original, parented under an empty.

The AddOn is coded to be used with one object selected, but it does also work on multiple objects - in the latter case, it helps to turn off the “Parent under Empty” checkbox, and you will need to select the copies with Shift-Alt-L every time you want to repeat the command. In the example render below, a grid of 11,554 objects was created in 4 steps that would have taken mere seconds were it not for the slowdown due to the large number (it took about 3 minutes on my 3.2Ghz box).

I’m releasing it as public domain, so feel free to appropriate it and if you feel generous, post an improved version. There are plenty of improvement that could be made. My favourite would the ability to input the name of a curve object, and have the duplication done along the curve, ie avoid DupliFrames. Another cool thing would be to set a translation factor for the whole chain of duplicates, to have them all progressively rotate or scale as they cascade into the distance. Alas, this simple version is all I have time for.

Enjoy! :slight_smile:


bl_info = {
    "name": "Duplicate Multiple Linked",
    "description": "Copies the selected object into multiple linked duplicates, optionally parented under an empty.",
    "author": "This script is public domain",
    "version": (0,1),
    "blender": (2, 5, 7),
    "api": 36138,
    "location": "View3D > Object",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}

import bpy

def duplicateObject(context, numCopies, transVec, doParent):
	activeObj = context.active_object
	dupCopies = []
	dupCopies.append(activeObj)
	while numCopies > 0:
		bpy.ops.object.duplicate_move_linked(OBJECT_OT_duplicate={"linked":True, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":transVec, "release_confirm":False})
		dupCopies.append(context.active_object)
		numCopies -= 1
	if doParent:
		groupName = activeObj.name + "_Copies"
		bpy.ops.object.add(type='EMPTY')
		groupEmpty = context.active_object
		groupEmpty.name = groupName
		for i in dupCopies:
			bpy.ops.object.select_name(name=i.name, extend=True)
		bpy.ops.object.select_name(name=groupEmpty.name, extend=True)
		bpy.ops.object.parent_set(type="OBJECT")
	else:
		for i in dupCopies:
			bpy.ops.object.select_name(name=i.name, extend=True)

class multiDuplicate(bpy.types.Operator):
	bl_idname = "object.multi_duplicate"
	bl_label = "Duplicate Multiple Linked"
	bl_options = {"REGISTER", "UNDO"}
	
	my_int = bpy.props.IntProperty(name="Number of Copies:", default=1, description="Number of copies", min=1, max=200, subtype="NONE")
	my_floatvec = bpy.props.FloatVectorProperty(name="XYZ Offset:", default=(0.0,0.0,0.0), min=-1000, max=1000, description="XYZ Offset")
	my_bool = bpy.props.BoolProperty(name="Parent under Empty", default=1)
 
	@classmethod
	def poll(cls, context):
		ob = context.object
		if ob == None:
			return False
		elif ob.select:
			return True
		return False

	def execute(self, context):
		duplicateObject(context, self.my_int, self.my_floatvec, self.my_bool)
		return {'FINISHED'}
	
	def invoke(self, context, event):
		return self.execute(context)

def menu_draw(self, context):
	self.layout.operator_context = 'INVOKE_REGION_WIN'
	self.layout.operator(multiDuplicate.bl_idname, "Duplicate Multiple Linked")
 
def register():
	bpy.utils.register_module(__name__)
	bpy.types.VIEW3D_MT_object.append(menu_draw)
 
def unregister():
	bpy.types.VIEW3D_MT_object.remove(menu_draw)
	bpy.utils.unregister_module(__name__)
 
if __name__ == '__main__':
	register()

Attachments


This no longer works because ‘bpy.ops.object.select_name’ has been removed from trunk.

Here is an updated version.

I changed the version number to 0.2 and the Blender version to 2.63.

Save it as ‘duplicate_objects.py’ and drop it into your ‘addons’ folder.

Enjoy.

bl_info = {
    "name": "Duplicate Multiple Linked",
    "description": "Copies the selected object into multiple linked duplicates, optionally parented under an empty.",
    "author": "This script is public domain",
    "version": (0,2),
    "blender": (2, 6, 3),
    "api": 46461,
    "location": "View3D > Object",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}

import bpy

def duplicateObject(context, numCopies, transVec, doParent):
	activeObj = context.active_object
	dupCopies = []
	dupCopies.append(activeObj)
	while numCopies > 0:
		bpy.ops.object.duplicate_move_linked(OBJECT_OT_duplicate={"linked":True, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":transVec, "release_confirm":False})
		dupCopies.append(context.active_object)
		numCopies -= 1
	if doParent:
		groupName = activeObj.name + "_Copies"
		bpy.ops.object.add(type='EMPTY')
		groupEmpty = context.active_object
		groupEmpty.name = groupName
		for i in dupCopies:
			bpy.data.objects[i.name].select = True

			bpy.data.objects[groupEmpty.name].select = True
		bpy.ops.object.parent_set(type="OBJECT")
	else:
		for i in dupCopies:
			bpy.ops.object.select_name(name=i.name, extend=True)

class multiDuplicate(bpy.types.Operator):
	bl_idname = "object.multi_duplicate"
	bl_label = "Duplicate Multiple Linked"
	bl_options = {"REGISTER", "UNDO"}
	
	my_int = bpy.props.IntProperty(name="Number of Copies:", default=1, description="Number of copies", min=1, max=5000, subtype="NONE")
	my_floatvec = bpy.props.FloatVectorProperty(name="XYZ Offset:", default=(0.0,0.0,0.0), min=-1000, max=1000, description="XYZ Offset")
	my_bool = bpy.props.BoolProperty(name="Parent under Empty", default=1)
 
	@classmethod
	def poll(cls, context):
		ob = context.object
		if ob == None:
			return False
		elif ob.select:
			return True
		return False

	def execute(self, context):
		duplicateObject(context, self.my_int, self.my_floatvec, self.my_bool)
		return {'FINISHED'}
	
	def invoke(self, context, event):
		return self.execute(context)

def menu_draw(self, context):
	self.layout.operator_context = 'INVOKE_REGION_WIN'
	self.layout.operator(multiDuplicate.bl_idname, "Duplicate Multiple Linked")
 
def register():
	bpy.utils.register_module(__name__)
	bpy.types.VIEW3D_MT_object.append(menu_draw)
 
def unregister():
	bpy.types.VIEW3D_MT_object.remove(menu_draw)
	bpy.utils.unregister_module(__name__)
 
if __name__ == '__main__':
	register()

I just came across this, but am not a Blender artist by any means, or a code person, but I’m doing some work in Blender and want to instance things with some manual control and this script seems like what I need. But how do I take this bunch of code and save it as an addon?

thank you very much. works like a charm. must add to default addons for blender. :slight_smile:

updated
bpy.ops.object.select_name(name=i.name, extend=True)
to
bpy.ops.object.select_pattern(pattern=i.name, extend=True)
works now in 2.79


bl_info = {
    "name": "Duplicate Multiple Linked",
    "description": "Copies the selected object into multiple linked duplicates, optionally parented under an empty.",
    "author": "This script is public domain",
    "version": (0,3),
    "blender": (2, 6, 3),
    "api": 46461,
    "location": "View3D > Object",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}

import bpy

def duplicateObject(context, numCopies, transVec, doParent):
    activeObj = context.active_object
    dupCopies = []
    dupCopies.append(activeObj)
    while numCopies > 0:
        bpy.ops.object.duplicate_move_linked(OBJECT_OT_duplicate={"linked":True, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":transVec, "release_confirm":False})
        dupCopies.append(context.active_object)
        numCopies -= 1
    if doParent:
        groupName = activeObj.name + "_Copies"
        bpy.ops.object.add(type='EMPTY')
        groupEmpty = context.active_object
        groupEmpty.name = groupName
        for i in dupCopies:
            bpy.data.objects[i.name].select = True

            bpy.data.objects[groupEmpty.name].select = True
        bpy.ops.object.parent_set(type="OBJECT")
    else:
        for i in dupCopies:
            bpy.ops.object.select_pattern(pattern=i.name, extend=True)

class multiDuplicate(bpy.types.Operator):
    bl_idname = "object.multi_duplicate"
    bl_label = "Duplicate Multiple Linked"
    bl_options = {"REGISTER", "UNDO"}
    
    my_int = bpy.props.IntProperty(name="Number of Copies:", default=1, description="Number of copies", min=1, max=5000, subtype="NONE")
    my_floatvec = bpy.props.FloatVectorProperty(name="XYZ Offset:", default=(0.0,0.0,0.0), min=-1000, max=1000, description="XYZ Offset")
    my_bool = bpy.props.BoolProperty(name="Parent under Empty", default=1)
 
    @classmethod
    def poll(cls, context):
        ob = context.object
        if ob == None:
            return False
        elif ob.select:
            return True
        return False

    def execute(self, context):
        duplicateObject(context, self.my_int, self.my_floatvec, self.my_bool)
        return {'FINISHED'}
    
    def invoke(self, context, event):
        return self.execute(context)

def menu_draw(self, context):
    self.layout.operator_context = 'INVOKE_REGION_WIN'
    self.layout.operator(multiDuplicate.bl_idname, "Duplicate Multiple Linked")
 
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_object.append(menu_draw)
 
def unregister():
    bpy.types.VIEW3D_MT_object.remove(menu_draw)
    bpy.utils.unregister_module(__name__)
 
if __name__ == '__main__':
    register()

Thanks for update