210 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # ##### BEGIN GPL LICENSE BLOCK #####
 | |
| #
 | |
| #  This program is free software; you can redistribute it and/or
 | |
| #  modify it under the terms of the GNU General Public License
 | |
| #  as published by the Free Software Foundation; either version 2
 | |
| #  of the License, or (at your option) any later version.
 | |
| #
 | |
| #  This program is distributed in the hope that it will be useful,
 | |
| #  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| #  GNU General Public License for more details.
 | |
| #
 | |
| #  You should have received a copy of the GNU General Public License
 | |
| #  along with this program; if not, write to the Free Software Foundation,
 | |
| #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | |
| #
 | |
| # ##### END GPL LICENSE BLOCK #####
 | |
| 
 | |
| # <pep8-80 compliant>
 | |
| 
 | |
| # This file defines a set of methods that are useful for various
 | |
| # Relative Keying Set (RKS) related operations, such as: callbacks
 | |
| # for polling, iterator callbacks, and also generate callbacks.
 | |
| # All of these can be used in conjunction with the others.
 | |
| 
 | |
| __all__ = (
 | |
|     "path_add_property",
 | |
|     "RKS_POLL_selected_objects",
 | |
|     "RKS_POLL_selected_bones",
 | |
|     "RKS_POLL_selected_items",
 | |
|     "RKS_ITER_selected_item",
 | |
|     "RKS_GEN_available",
 | |
|     "RKS_GEN_location",
 | |
|     "RKS_GEN_rotation",
 | |
|     "RKS_GEN_scaling",
 | |
|     )
 | |
| 
 | |
| import bpy
 | |
| 
 | |
| ###########################
 | |
| # General Utilities
 | |
| 
 | |
| 
 | |
| # Append the specified property name on the the existing path
 | |
| def path_add_property(path, prop):
 | |
|     if path:
 | |
|         return path + "." + prop
 | |
|     else:
 | |
|         return prop
 | |
| 
 | |
| ###########################
 | |
| # Poll Callbacks
 | |
| 
 | |
| 
 | |
| # selected objects (active object must be in object mode)
 | |
| def RKS_POLL_selected_objects(ksi, context):
 | |
|     ob = context.active_object
 | |
|     if ob:
 | |
|         return ob.mode == 'OBJECT'
 | |
|     else:
 | |
|         return bool(context.selected_objects)
 | |
| 
 | |
| 
 | |
| # selected bones
 | |
| def RKS_POLL_selected_bones(ksi, context):
 | |
|     # we must be in Pose Mode, and there must be some bones selected
 | |
|     ob = context.active_object
 | |
|     if ob and ob.mode == 'POSE':
 | |
|         if context.active_pose_bone or context.selected_pose_bones:
 | |
|             return True
 | |
| 
 | |
|     # nothing selected
 | |
|     return False
 | |
| 
 | |
| 
 | |
| # selected bones or objects
 | |
| def RKS_POLL_selected_items(ksi, context):
 | |
|     return (RKS_POLL_selected_bones(ksi, context) or
 | |
|             RKS_POLL_selected_objects(ksi, context))
 | |
| 
 | |
| ###########################
 | |
| # Iterator Callbacks
 | |
| 
 | |
| 
 | |
| # all selected objects or pose bones, depending on which we've got
 | |
| def RKS_ITER_selected_item(ksi, context, ks):
 | |
|     ob = context.active_object
 | |
|     if ob and ob.mode == 'POSE':
 | |
|         for bone in context.selected_pose_bones:
 | |
|             ksi.generate(context, ks, bone)
 | |
|     else:
 | |
|         for ob in context.selected_objects:
 | |
|             ksi.generate(context, ks, ob)
 | |
| 
 | |
| 
 | |
| # all select objects only
 | |
| def RKS_ITER_selected_objects(ksi, context, ks):
 | |
|     for ob in context.selected_objects:
 | |
|         ksi.generate(context, ks, ob)
 | |
| 
 | |
| ###########################
 | |
| # Generate Callbacks
 | |
| 
 | |
| 
 | |
| # 'Available' F-Curves
 | |
| def RKS_GEN_available(ksi, context, ks, data):
 | |
|     # try to get the animation data associated with the closest
 | |
|     # ID-block to the data (neither of which may exist/be easy to find)
 | |
|     id_block = data.id_data
 | |
|     adt = getattr(id_block, "animation_data", None)
 | |
| 
 | |
|     # there must also be an active action...
 | |
|     if adt is None or adt.action is None:
 | |
|         return
 | |
| 
 | |
|     # if we haven't got an ID-block as 'data', try to restrict
 | |
|     # paths added to only those which branch off from here
 | |
|     # i.e. for bones
 | |
|     if id_block != data:
 | |
|         basePath = data.path_from_id()
 | |
|     else:
 | |
|         basePath = None  # this is not needed...
 | |
| 
 | |
|     # for each F-Curve, include a path to key it
 | |
|     # NOTE: we don't need to set the group settings here
 | |
|     for fcu in adt.action.fcurves:
 | |
|         if basePath:
 | |
|             if basePath in fcu.data_path:
 | |
|                 ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
 | |
|         else:
 | |
|             ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
 | |
| 
 | |
| # ------
 | |
| 
 | |
| 
 | |
| # get ID block and based ID path for transform generators
 | |
| # private function
 | |
| def get_transform_generators_base_info(data):
 | |
|     # ID-block for the data
 | |
|     id_block = data.id_data
 | |
| 
 | |
|     # get base path and grouping method/name
 | |
|     if isinstance(data, bpy.types.ID):
 | |
|         # no path in this case
 | |
|         path = ""
 | |
| 
 | |
|         # data on ID-blocks directly should get grouped by the KeyingSet
 | |
|         grouping = None
 | |
|     else:
 | |
|         # get the path to the ID-block
 | |
|         path = data.path_from_id()
 | |
| 
 | |
|         # try to use the name of the data element to group the F-Curve
 | |
|         # else fallback on the KeyingSet name
 | |
|         grouping = getattr(data, "name", None)
 | |
| 
 | |
|     # return the ID-block and the path
 | |
|     return id_block, path, grouping
 | |
| 
 | |
| 
 | |
| # Location
 | |
| def RKS_GEN_location(ksi, context, ks, data):
 | |
|     # get id-block and path info
 | |
|     id_block, base_path, grouping = get_transform_generators_base_info(data)
 | |
| 
 | |
|     # add the property name to the base path
 | |
|     path = path_add_property(base_path, "location")
 | |
| 
 | |
|     # add Keying Set entry for this...
 | |
|     if grouping:
 | |
|         ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
 | |
|     else:
 | |
|         ks.paths.add(id_block, path)
 | |
| 
 | |
| 
 | |
| # Rotation
 | |
| def RKS_GEN_rotation(ksi, context, ks, data):
 | |
|     # get id-block and path info
 | |
|     id_block, base_path, grouping = get_transform_generators_base_info(data)
 | |
| 
 | |
|     # add the property name to the base path
 | |
|     #   rotation mode affects the property used
 | |
|     if data.rotation_mode == 'QUATERNION':
 | |
|         path = path_add_property(base_path, "rotation_quaternion")
 | |
|     elif data.rotation_mode == 'AXIS_ANGLE':
 | |
|         path = path_add_property(base_path, "rotation_axis_angle")
 | |
|     else:
 | |
|         path = path_add_property(base_path, "rotation_euler")
 | |
| 
 | |
|     # add Keying Set entry for this...
 | |
|     if grouping:
 | |
|         ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
 | |
|     else:
 | |
|         ks.paths.add(id_block, path)
 | |
| 
 | |
| 
 | |
| # Scaling
 | |
| def RKS_GEN_scaling(ksi, context, ks, data):
 | |
|     # get id-block and path info
 | |
|     id_block, base_path, grouping = get_transform_generators_base_info(data)
 | |
| 
 | |
|     # add the property name to the base path
 | |
|     path = path_add_property(base_path, "scale")
 | |
| 
 | |
|     # add Keying Set entry for this...
 | |
|     if grouping:
 | |
|         ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
 | |
|     else:
 | |
|         ks.paths.add(id_block, path)
 |