# --------- # Tested in Blender 2.75a and 2.76 # Makes a 'replicator grid' -- multiple copies of a given object, parented in a tranformation-control chain. # Provides a single empty that controls the relative size, displacement and rotation of each successive object in relation to # its parent. # # Usage: # Change the number of rows and columns below. # Select target object to be replicated. # Run the script. # If you did this with an object named 'cube', for example, # You will end up with a 'cube_replicator' empty, below which all of the duplicated objects # have been parented. # And you will have a new cube_replicator_control empty, which can be moved, scaled and rotated # to control the overall replicator grid. # Bret Battey / BatHatMedia.com September 2015 # --------- #import bpy from bpy.props import * # specify number of rows and columns here! rows = 5 columns = 5 # The following function is adapted from Nick Keeline "Cloud Generator" # addNewObject in object_cloud_gen.py def duplicateObject(scene, name, copyobj): # Create new mesh mesh = bpy.data.meshes.new(name) # Create a new object. ob_new = bpy.data.objects.new(name, mesh) tempme = copyobj.data ob_new.data = tempme.copy() ob_new.scale = copyobj.scale ob_new.location = copyobj.location # Link new object to the given scene and select it. scene.objects.link(ob_new) ob_new.select = True return ob_new # Build the parenting chain of replications tied to empties and control # constraints. If we built a parented chain of objects, we wouldn't be able to # transform each object independently. So instead we build a parented chain of # empties, then parent each duplicated object to one of the empties. def gridOfReps(rows, cols, source_obj, ctrl, scene): for row in range(rows): for col in range(cols): #Create a locator empty bpy.ops.object.add(type="EMPTY", location=(0,0,0)) rep_loc = bpy.context.object # The first empty becomes the root of the whole beast, and the # source object gets parented to it if((row == 0) and (col == 0)): rep_loc.name = source_obj.name+"_replicator" parent_obj = rep_loc prev_row_locator = rep_loc source_parent = source_obj.parent # If the source object has a parent, locate and parent # the new replicator base empty to that. if((source_parent) and (source_parent.type == 'EMPTY')): rep_loc.location = source_parent.location rep_loc.parent = source_parent # Otherwise just position the base empty to the source obj, # zero the source obj location, and parent to the base else: rep_loc.location = source_obj.location source_obj.location = (0,0,0) source_obj.parent = rep_loc else: rep_loc.name = "rep_loc_r"+str(row)+"c_"+str(col) # name can't #be specified via the .add method rep_loc.parent = parent_obj # parent to the previous locator # First locator in each row other than 0 gets special treatment if((row > 0) and (col == 0)): # First location constraint locks to the prev. row locator constraint = rep_loc.constraints.new("COPY_LOCATION") constraint.target = prev_row_locator constraint.use_x = True constraint.use_y = True constraint.use_z = True constraint.owner_space = "WORLD" constraint.target_space = "WORLD" constraint.influence = 1.0 # Second location constraint implements the z offset # from the controller constraint = rep_loc.constraints.new("COPY_LOCATION") constraint.target = ctrl constraint.use_x = False constraint.use_y = False constraint.use_z = True constraint.owner_space = "LOCAL" constraint.target_space = "WORLD" constraint.influence = 1.0 constraint.use_offset = True # use the controller's # location as an offset rather than an absolute location # And ready this locator to be basis of next row position prev_row_locator = rep_loc else: # For other items in a row, offset location x on basis of # controller constraint = rep_loc.constraints.new("COPY_LOCATION") constraint.target = ctrl constraint.use_x = True # for offsetting a row, only x is # drawn from the controller constraint.use_y = False constraint.use_z = False constraint.owner_space = "LOCAL" constraint.target_space = "WORLD" constraint.influence = 1.0 constraint.use_offset = True # use the controller's #location as an offset rather than an absolute location # For all locators except the root, apply these influences from #the controller: # Copy_Rotation Constraint constraint = rep_loc.constraints.new("COPY_ROTATION") constraint.target = ctrl constraint.owner_space = "LOCAL" constraint.target_space = "WORLD" constraint.influence = 1.0 # Copy_Scale Constraint. This will also cascade down the chain # due to the combined effect with the parenting constraint = rep_loc.constraints.new("COPY_SCALE") constraint.target = ctrl constraint.owner_space = "LOCAL" constraint.target_space = "WORLD" constraint.influence = 1.0 # In all cases (except root), duplicate source object and parent # to the replicator empty. # This strategy allows later transformations of the visible #object without influencing the children new_obj = duplicateObject(scene, "rep", source_obj) new_obj.parent = rep_loc # Advance parent assignment parent_obj = rep_loc return parent_obj # get the source object source_obj = bpy.context.object source_loc = source_obj.location source_scene = bpy.context.scene # create and name the controller empty bpy.ops.object.add(type="EMPTY", location=(0,0,0)) ctrl = bpy.context.object ctrl.name = source_obj.name+"_replicator_control" # name can't be specified #via the .add method #replicate! last_locator = gridOfReps(rows, columns, source_obj, ctrl, source_scene)
Bret Battey blogging sundry ideas, favorable events, works in progress, and miscellaneous solutions in digital music and video-music research and creation. I see this as a subsidiary of my web site, BatHatMedia.com.
Tuesday, February 16, 2016
"Replicator Grid" Blender script
This Blender script creates a 2D grid of replicated objects with a descending line of parenting control, running left to right, then down to the next row. The following video demonstrates:
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment