Advertisement
Guest User

Ivy Growth Animator Script

a guest
Jan 26th, 2013
551
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.55 KB | None | 0 0
  1. import bpy
  2. import re
  3. import random
  4. import mathutils
  5.  
  6. modifier_name      = "GROW"
  7. modifier_type      = "BUILD"
  8. leaves_object_name = "IvyLeaf"
  9.  
  10. frame_per_face           = 1 / 4   # how to allocate a total build animation length according to the number of faces on a branch
  11. build_interval           = 15      # how many frames to wait until starting to build 2nd branch
  12. wait_between_branches    = 4       # how many frames to wait between branches
  13. build_start_frame        = 1       # Build animation start frame for the first ivy branch
  14. leaf_growth_delay_factor = 20      # Maximum number of frames to delay the growth of leaves after branch build ends
  15. leaf_growth_duration     = { "min" : 12, "max" : 24 }
  16.  
  17. def find_nearest_face_on_mesh( mesh_obj, coordinates):
  18.     """ function name:  find_nearest_face_on_mesh
  19.        parameters:     mesh_obj    - The object whose faces we want to access
  20.                        coordinates - the coordinates of the current leaf
  21.        description:    Iterates over all the faces in a mesh and find the one which is closest
  22.                        to the coordinates provided.
  23.        return value:   The smallest distance between any of the mesh's faces and the coordinates
  24.    """
  25.  
  26.     mesh_obj.data.update( calc_tessface=True )
  27.     distances = []
  28.     for face in mesh_obj.data.tessfaces:
  29.         pt1 = face.center * mesh_obj.matrix_world # convert to global coordinates
  30.         pt2 = coordinates
  31.         distance = abs(pt1.x - pt2.x) + abs(pt1.y - pt2.y) + abs(pt1.z - pt2.z)
  32.         distances.append(distance)
  33.        
  34.     distances.sort()
  35.    
  36.     return distances[0]  # return the first value which is the smallest after sorting
  37.    
  38. def find_nearest_branch( ivy_objects, glob_co ):
  39.     """ function name:  find_nearest_branch
  40.        parameters:     ivy_objects [List] - Array of ivy branches
  41.                        global_co [Vector] - the coordinates of the object we want to
  42.                                             match with the nearest branch
  43.        description:    Finds the nearest branch to the coordinates it gets as a parameter
  44.                        Iterates over branches and returns the closest one
  45.        return value:   The closest ivy branch (mesh object)
  46.    """
  47.     # First we'll calculate the first ivy branch's distance from the current leaf.
  48.     # The we'll browse all branches and find if any other branch is closer to our leaf.
  49.  
  50.     closest_object   = ivy_objects[0]   # First branch is the initial closest object
  51.     branch_obj       = bpy.data.objects[closest_object["name"]]
  52.     minimum_distance = find_nearest_face_on_mesh( branch_obj, glob_co)
  53.  
  54.     for branch in ivy_objects:
  55.         branch_obj = bpy.data.objects[branch["name"]]
  56.         distance = find_nearest_face_on_mesh( branch_obj, glob_co)
  57.        
  58.         if distance < minimum_distance:
  59.             minimum_distance = distance
  60.             closest_object   = branch
  61.    
  62.     print( "nearest branch found: ", closest_object )
  63.    
  64.     return closest_object
  65.  
  66. def create_shapekey( leaves, leaf_idx, start, duration ):
  67.     """ function name:  create_shapekey
  68.        parameters:     leaves   [Mesh obj]   - the leaves mesh object
  69.                        leaf_idx [Face Index] - Index of the leaf to be transformed and shape keyed
  70.                        start    [Int]        - The start frame for the closest branch's build modifier
  71.                        duration [Int]        - The duration of the closest branch's build modifier
  72.        description:    Creates a shape key for the current leaf (face on leaves mesh object),
  73.                        which will enable to animate the progressive growth of this leaf on its branch
  74.    """    
  75.    
  76.     bpy.ops.object.shape_key_add(from_mix=False) # Create a shapekey for this leaf
  77.     latest_shape_key_index = len(bpy.context.object.data.shape_keys.key_blocks) - 1
  78.     current_shape_key      = bpy.context.object.data.shape_keys.key_blocks[latest_shape_key_index]
  79.     bpy.context.object.active_shape_key_index = latest_shape_key_index  # Activate current shape key
  80.    
  81.     bpy.ops.object.mode_set(mode = 'EDIT')              # edit mode to deselect all
  82.     bpy.ops.mesh.select_mode(                           # Go to vertex selection mode
  83.         use_extend=False,
  84.         use_expand=False,
  85.         type='VERT')
  86.     bpy.ops.mesh.select_all(action='DESELECT')          # Deselect all verts
  87.     bpy.ops.object.mode_set(mode = 'OBJECT')            # Go back to object mode
  88.     bpy.context.object.data.update(calc_tessface=True)  # Calculate the face data again
  89.    
  90.     # select face vertices
  91.     idxmin = 0
  92.     length = len(bpy.context.object.data.tessfaces[leaf_idx].vertices)
  93.     for i in range(idxmin, length):
  94.         vert_index = bpy.context.object.data.tessfaces[leaf_idx].vertices[i]
  95.         bpy.context.object.data.vertices[vert_index].select = True
  96.  
  97.     bpy.ops.object.mode_set(mode = 'EDIT')    # edit mode to edit face
  98.     bpy.ops.mesh.select_mode(                 # Go to face selection mode
  99.         use_extend=False,
  100.         use_expand=False,
  101.         type='FACE')
  102.     bpy.ops.transform.resize(value=(0,0,0))   # resize face to 0 (invisible)
  103.     bpy.ops.object.mode_set(mode = 'OBJECT')  # return to object mode
  104.  
  105.     # Create the first keyframe where the value of the shapekey is 1 to make the leaf invisible at the beginning
  106.     bpy.ops.anim.change_frame(frame = build_start_frame) # Select frame for keyframe
  107.     current_shape_key.value = 1                          # Set shapekey value
  108.     current_shape_key.keyframe_insert('value')           # Insert keyframe for shapekey val
  109.    
  110.     # Create the keyframe where the growth begins (leaf still invisible)
  111.     start_frame = start + duration + random.randint(0, leaf_growth_delay_factor)
  112.     bpy.ops.anim.change_frame(frame = start_frame) # Select frame for keyframe
  113.     current_shape_key.value = 1                    # Set shapekey value
  114.     current_shape_key.keyframe_insert('value')     # Insert keyframe for shapekey val    
  115.    
  116.     # Create the keyframe where the growth ends (leaf at full size)
  117.     end_frame = start_frame + random.randint(leaf_growth_duration["min"], leaf_growth_duration["max"])
  118.     bpy.ops.anim.change_frame(frame = end_frame)   # Select frame for keyframe
  119.     current_shape_key.value = 0                    # Set shapekey value
  120.     current_shape_key.keyframe_insert('value')     # Insert keyframe for shapekey val    
  121.  
  122. def animate_leaves( leaves_object_name, ivy_objects ):
  123.     """ function name:  animate_leaves
  124.        parameters:     leaves_object_name [string]
  125.                        ivy_objects        [List of Mesh Objects]
  126.        description:    Master function for the leaves' animations. Iterates over all leaves,
  127.                        calculates what branch is the closest to each, and uses that information
  128.                        to create and animate shapekeys where this leaf grows gradually
  129.    """
  130.     leaves = bpy.data.objects[leaves_object_name]
  131.     leaves.select = True                       # Select leaves object
  132.     bpy.context.scene.objects.active = leaves  # Make leaves the active object in the scene
  133.    
  134.     bpy.ops.object.shape_key_add(from_mix=False) # Create the first, base shapekey
  135.     leaves.data.update(calc_tessface=True)       # Calculate the face data
  136.    
  137.     leaf_indices = []
  138.     for leaf in leaves.data.tessfaces:
  139.         leaf_indices.append(leaf.index)
  140.  
  141.     worldmatrix = leaves.matrix_world
  142.  
  143.     for index in leaf_indices:
  144.         print( "Iterating over face index: ", index)
  145.         leaves.data.update(calc_tessface=True)                                   # Calculate the face data
  146.         leaf_center_pos_glob = leaves.data.tessfaces[index].center * worldmatrix # Get the global coordinates of this leaf's center
  147.         branch     = find_nearest_branch(ivy_objects, leaf_center_pos_glob)      # Find closest branch to this leaf
  148.         branch_obj = bpy.data.objects[branch["name"]]
  149.         start      = branch_obj.modifiers[modifier_name].frame_start             # Get start frame for this branch's build modifier
  150.         duration   = branch_obj.modifiers[modifier_name].frame_duration          # And the modifier's duration
  151.         create_shapekey( leaves, index, start, duration)                         # Create a shapekey for this leaf
  152.    
  153. def prepare_ivy_object():
  154.     """ function name:  prepare_ivy_object
  155.        description:    converts the active ivy object (which must be selected) form a curve into a mesh,
  156.                        places the 3D cursor at the first point of the ivy curve
  157.                        sorts all faces according to the distance from the 3D cursor (placed on the first point)
  158.                        adds a standard build modifier to this object
  159.                        and eventually splits according to loose parts
  160.    """
  161.  
  162.     ivy_obj_name = bpy.context.object.name  # Get selected object's name
  163.    
  164.     # Find the first point of the ivy curve, and place the 3D curser there
  165.     first_curve_point = bpy.context.object.data.splines[0].points[0].co  # get vector coordinates of the first point vector on curve
  166.    
  167.     # Set the 3D cursor position to the first curve point
  168.     # I'm creating a new vector since the point natively comes with 4 arguments instead of just xyz,
  169.     # and this produces an error when trying to set the cursor there
  170.     bpy.context.scene.cursor_location = mathutils.Vector( [ first_curve_point.x, first_curve_point.y, first_curve_point.z ] )
  171.    
  172.     bpy.ops.object.convert(target='MESH')                                       # Convert object to mesh
  173.     bpy.ops.object.editmode_toggle()                                            # Enter edit mode
  174.     bpy.ops.mesh.sort_elements(type='CURSOR_DISTANCE', elements={'FACE'})       # Sort faces by 3D cursor distance
  175.     bpy.context.object.modifiers.new( 'GROW', 'BUILD' )                         # Add a Build modifier
  176.     bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')   # Goto face selection mode
  177.     bpy.ops.mesh.select_all()                                                   # Select all mesh elements on selected object
  178.     bpy.ops.mesh.separate(type='LOOSE')                                         # Separate to different objects according to loose parts
  179.     bpy.ops.object.editmode_toggle()                                            # Leave edit mode
  180.    
  181.     return ivy_obj_name
  182.  
  183.  
  184. def find_ivy_branches( base_name ):
  185.     """ function name:  find_ivy_branches
  186.        description:    browses all objects and filters out those who have the base_name in their names
  187.        return value:   an array (ivy_objects) which contains a list of dictionaries with the name and facecount of each ivy object
  188.    """
  189.    
  190.     ivy_objects = []
  191.     for current_obj in bpy.data.objects:                 # browse all objects and filter out ivy branches
  192.         if base_name in current_obj.name:                # if the object name contains the base_name
  193.             current_obj.data.update(calc_tessface=True)  # calculate face data so that...
  194.             face_count = len(current_obj.data.tessfaces) # we can obtain the total number of faces
  195.             ivy_objects.append( {                        # add ivy object to list:
  196.                 "name" : current_obj.name,               #   include name
  197.                 "facecount" : face_count } )             #   and face count
  198.  
  199.     return ivy_objects
  200.  
  201. def find_biggest_branch(ivy_objects, frame_per_face):
  202.     biggest_obj = ""
  203.     most_faces  = 0
  204.  
  205.     # Find the biggest object (highest face count)
  206.     for obj in ivy_objects:
  207.         if obj["facecount"] > most_faces:   # if this object's facecount is larger than the previous highscore,
  208.             most_faces  = obj["facecount"]  # then make this facecount one the new max
  209.             biggest_obj = obj["name"]       # and update the biggest object's name
  210.  
  211.     # set base build animation length according to the biggest object's size
  212.     base_build_length = int( most_faces * frame_per_face )
  213.    
  214.     return biggest_obj, most_faces, base_build_length
  215.  
  216. def set_build_timing(ivy_objects, build_start_frame, build_interval, wait_between_branches, most_faces, base_build_length):    
  217.     count = 0
  218.  
  219.     # set animation length and start frames to all objects in list
  220.     for obj in ivy_objects:
  221.         name = obj["name"]
  222.         current_object = bpy.data.objects[name]
  223.  
  224.         # set the start frame of each object's build anim. by the order of names (which corresponds to order of creation)
  225.         if count != 0: # Set build start for all the branches after the first one:
  226.             bpy.data.objects[name].modifiers[modifier_name].frame_start = int( build_start_frame + build_interval + count * wait_between_branches )
  227.         else:   # Set when the first branch starts to build
  228.             bpy.data.objects[name].modifiers[modifier_name].frame_start = int( build_start_frame )
  229.        
  230.         # Set build length in proportion to face count
  231.         ratio = obj["facecount"] / most_faces
  232.         bpy.data.objects[name].modifiers[modifier_name].frame_duration = int( ratio * base_build_length )
  233.  
  234.         count += 1
  235.  
  236. # Actual executed code starts here
  237.  
  238. object_name = prepare_ivy_object()
  239. ivy_objects = find_ivy_branches( object_name )
  240. ( biggest_obj, most_faces, base_build_length ) = find_biggest_branch(ivy_objects, frame_per_face)
  241. set_build_timing(ivy_objects, build_start_frame, build_interval, wait_between_branches, most_faces, base_build_length)
  242. animate_leaves( leaves_object_name, ivy_objects )
  243.  
  244.  
  245.  
  246.  
  247. # to execute this script:
  248. # filename = "H:\Tutorials\Blender\Python\animate_ivy_and_leaves.py"
  249. # exec(compile(open(filename).read(), filename, 'exec'))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement