Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import bpy
- import re
- import random
- import mathutils
- modifier_name = "GROW"
- modifier_type = "BUILD"
- leaves_object_name = "IvyLeaf"
- frame_per_face = 1 / 4 # how to allocate a total build animation length according to the number of faces on a branch
- build_interval = 15 # how many frames to wait until starting to build 2nd branch
- wait_between_branches = 4 # how many frames to wait between branches
- build_start_frame = 1 # Build animation start frame for the first ivy branch
- leaf_growth_delay_factor = 20 # Maximum number of frames to delay the growth of leaves after branch build ends
- leaf_growth_duration = { "min" : 12, "max" : 24 }
- def find_nearest_face_on_mesh( mesh_obj, coordinates):
- """ function name: find_nearest_face_on_mesh
- parameters: mesh_obj - The object whose faces we want to access
- coordinates - the coordinates of the current leaf
- description: Iterates over all the faces in a mesh and find the one which is closest
- to the coordinates provided.
- return value: The smallest distance between any of the mesh's faces and the coordinates
- """
- mesh_obj.data.update( calc_tessface=True )
- distances = []
- for face in mesh_obj.data.tessfaces:
- pt1 = face.center * mesh_obj.matrix_world # convert to global coordinates
- pt2 = coordinates
- distance = abs(pt1.x - pt2.x) + abs(pt1.y - pt2.y) + abs(pt1.z - pt2.z)
- distances.append(distance)
- distances.sort()
- return distances[0] # return the first value which is the smallest after sorting
- def find_nearest_branch( ivy_objects, glob_co ):
- """ function name: find_nearest_branch
- parameters: ivy_objects [List] - Array of ivy branches
- global_co [Vector] - the coordinates of the object we want to
- match with the nearest branch
- description: Finds the nearest branch to the coordinates it gets as a parameter
- Iterates over branches and returns the closest one
- return value: The closest ivy branch (mesh object)
- """
- # First we'll calculate the first ivy branch's distance from the current leaf.
- # The we'll browse all branches and find if any other branch is closer to our leaf.
- closest_object = ivy_objects[0] # First branch is the initial closest object
- branch_obj = bpy.data.objects[closest_object["name"]]
- minimum_distance = find_nearest_face_on_mesh( branch_obj, glob_co)
- for branch in ivy_objects:
- branch_obj = bpy.data.objects[branch["name"]]
- distance = find_nearest_face_on_mesh( branch_obj, glob_co)
- if distance < minimum_distance:
- minimum_distance = distance
- closest_object = branch
- print( "nearest branch found: ", closest_object )
- return closest_object
- def create_shapekey( leaves, leaf_idx, start, duration ):
- """ function name: create_shapekey
- parameters: leaves [Mesh obj] - the leaves mesh object
- leaf_idx [Face Index] - Index of the leaf to be transformed and shape keyed
- start [Int] - The start frame for the closest branch's build modifier
- duration [Int] - The duration of the closest branch's build modifier
- description: Creates a shape key for the current leaf (face on leaves mesh object),
- which will enable to animate the progressive growth of this leaf on its branch
- """
- bpy.ops.object.shape_key_add(from_mix=False) # Create a shapekey for this leaf
- latest_shape_key_index = len(bpy.context.object.data.shape_keys.key_blocks) - 1
- current_shape_key = bpy.context.object.data.shape_keys.key_blocks[latest_shape_key_index]
- bpy.context.object.active_shape_key_index = latest_shape_key_index # Activate current shape key
- bpy.ops.object.mode_set(mode = 'EDIT') # edit mode to deselect all
- bpy.ops.mesh.select_mode( # Go to vertex selection mode
- use_extend=False,
- use_expand=False,
- type='VERT')
- bpy.ops.mesh.select_all(action='DESELECT') # Deselect all verts
- bpy.ops.object.mode_set(mode = 'OBJECT') # Go back to object mode
- bpy.context.object.data.update(calc_tessface=True) # Calculate the face data again
- # select face vertices
- idxmin = 0
- length = len(bpy.context.object.data.tessfaces[leaf_idx].vertices)
- for i in range(idxmin, length):
- vert_index = bpy.context.object.data.tessfaces[leaf_idx].vertices[i]
- bpy.context.object.data.vertices[vert_index].select = True
- bpy.ops.object.mode_set(mode = 'EDIT') # edit mode to edit face
- bpy.ops.mesh.select_mode( # Go to face selection mode
- use_extend=False,
- use_expand=False,
- type='FACE')
- bpy.ops.transform.resize(value=(0,0,0)) # resize face to 0 (invisible)
- bpy.ops.object.mode_set(mode = 'OBJECT') # return to object mode
- # Create the first keyframe where the value of the shapekey is 1 to make the leaf invisible at the beginning
- bpy.ops.anim.change_frame(frame = build_start_frame) # Select frame for keyframe
- current_shape_key.value = 1 # Set shapekey value
- current_shape_key.keyframe_insert('value') # Insert keyframe for shapekey val
- # Create the keyframe where the growth begins (leaf still invisible)
- start_frame = start + duration + random.randint(0, leaf_growth_delay_factor)
- bpy.ops.anim.change_frame(frame = start_frame) # Select frame for keyframe
- current_shape_key.value = 1 # Set shapekey value
- current_shape_key.keyframe_insert('value') # Insert keyframe for shapekey val
- # Create the keyframe where the growth ends (leaf at full size)
- end_frame = start_frame + random.randint(leaf_growth_duration["min"], leaf_growth_duration["max"])
- bpy.ops.anim.change_frame(frame = end_frame) # Select frame for keyframe
- current_shape_key.value = 0 # Set shapekey value
- current_shape_key.keyframe_insert('value') # Insert keyframe for shapekey val
- def animate_leaves( leaves_object_name, ivy_objects ):
- """ function name: animate_leaves
- parameters: leaves_object_name [string]
- ivy_objects [List of Mesh Objects]
- description: Master function for the leaves' animations. Iterates over all leaves,
- calculates what branch is the closest to each, and uses that information
- to create and animate shapekeys where this leaf grows gradually
- """
- leaves = bpy.data.objects[leaves_object_name]
- leaves.select = True # Select leaves object
- bpy.context.scene.objects.active = leaves # Make leaves the active object in the scene
- bpy.ops.object.shape_key_add(from_mix=False) # Create the first, base shapekey
- leaves.data.update(calc_tessface=True) # Calculate the face data
- leaf_indices = []
- for leaf in leaves.data.tessfaces:
- leaf_indices.append(leaf.index)
- worldmatrix = leaves.matrix_world
- for index in leaf_indices:
- print( "Iterating over face index: ", index)
- leaves.data.update(calc_tessface=True) # Calculate the face data
- leaf_center_pos_glob = leaves.data.tessfaces[index].center * worldmatrix # Get the global coordinates of this leaf's center
- branch = find_nearest_branch(ivy_objects, leaf_center_pos_glob) # Find closest branch to this leaf
- branch_obj = bpy.data.objects[branch["name"]]
- start = branch_obj.modifiers[modifier_name].frame_start # Get start frame for this branch's build modifier
- duration = branch_obj.modifiers[modifier_name].frame_duration # And the modifier's duration
- create_shapekey( leaves, index, start, duration) # Create a shapekey for this leaf
- def prepare_ivy_object():
- """ function name: prepare_ivy_object
- description: converts the active ivy object (which must be selected) form a curve into a mesh,
- places the 3D cursor at the first point of the ivy curve
- sorts all faces according to the distance from the 3D cursor (placed on the first point)
- adds a standard build modifier to this object
- and eventually splits according to loose parts
- """
- ivy_obj_name = bpy.context.object.name # Get selected object's name
- # Find the first point of the ivy curve, and place the 3D curser there
- first_curve_point = bpy.context.object.data.splines[0].points[0].co # get vector coordinates of the first point vector on curve
- # Set the 3D cursor position to the first curve point
- # I'm creating a new vector since the point natively comes with 4 arguments instead of just xyz,
- # and this produces an error when trying to set the cursor there
- bpy.context.scene.cursor_location = mathutils.Vector( [ first_curve_point.x, first_curve_point.y, first_curve_point.z ] )
- bpy.ops.object.convert(target='MESH') # Convert object to mesh
- bpy.ops.object.editmode_toggle() # Enter edit mode
- bpy.ops.mesh.sort_elements(type='CURSOR_DISTANCE', elements={'FACE'}) # Sort faces by 3D cursor distance
- bpy.context.object.modifiers.new( 'GROW', 'BUILD' ) # Add a Build modifier
- bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') # Goto face selection mode
- bpy.ops.mesh.select_all() # Select all mesh elements on selected object
- bpy.ops.mesh.separate(type='LOOSE') # Separate to different objects according to loose parts
- bpy.ops.object.editmode_toggle() # Leave edit mode
- return ivy_obj_name
- def find_ivy_branches( base_name ):
- """ function name: find_ivy_branches
- description: browses all objects and filters out those who have the base_name in their names
- return value: an array (ivy_objects) which contains a list of dictionaries with the name and facecount of each ivy object
- """
- ivy_objects = []
- for current_obj in bpy.data.objects: # browse all objects and filter out ivy branches
- if base_name in current_obj.name: # if the object name contains the base_name
- current_obj.data.update(calc_tessface=True) # calculate face data so that...
- face_count = len(current_obj.data.tessfaces) # we can obtain the total number of faces
- ivy_objects.append( { # add ivy object to list:
- "name" : current_obj.name, # include name
- "facecount" : face_count } ) # and face count
- return ivy_objects
- def find_biggest_branch(ivy_objects, frame_per_face):
- biggest_obj = ""
- most_faces = 0
- # Find the biggest object (highest face count)
- for obj in ivy_objects:
- if obj["facecount"] > most_faces: # if this object's facecount is larger than the previous highscore,
- most_faces = obj["facecount"] # then make this facecount one the new max
- biggest_obj = obj["name"] # and update the biggest object's name
- # set base build animation length according to the biggest object's size
- base_build_length = int( most_faces * frame_per_face )
- return biggest_obj, most_faces, base_build_length
- def set_build_timing(ivy_objects, build_start_frame, build_interval, wait_between_branches, most_faces, base_build_length):
- count = 0
- # set animation length and start frames to all objects in list
- for obj in ivy_objects:
- name = obj["name"]
- current_object = bpy.data.objects[name]
- # set the start frame of each object's build anim. by the order of names (which corresponds to order of creation)
- if count != 0: # Set build start for all the branches after the first one:
- bpy.data.objects[name].modifiers[modifier_name].frame_start = int( build_start_frame + build_interval + count * wait_between_branches )
- else: # Set when the first branch starts to build
- bpy.data.objects[name].modifiers[modifier_name].frame_start = int( build_start_frame )
- # Set build length in proportion to face count
- ratio = obj["facecount"] / most_faces
- bpy.data.objects[name].modifiers[modifier_name].frame_duration = int( ratio * base_build_length )
- count += 1
- # Actual executed code starts here
- object_name = prepare_ivy_object()
- ivy_objects = find_ivy_branches( object_name )
- ( biggest_obj, most_faces, base_build_length ) = find_biggest_branch(ivy_objects, frame_per_face)
- set_build_timing(ivy_objects, build_start_frame, build_interval, wait_between_branches, most_faces, base_build_length)
- animate_leaves( leaves_object_name, ivy_objects )
- # to execute this script:
- # filename = "H:\Tutorials\Blender\Python\animate_ivy_and_leaves.py"
- # exec(compile(open(filename).read(), filename, 'exec'))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement