Merged 15170:15635 from trunk (no conflicts or even merges)
[blender.git] / release / scripts / bpymodules / BPyArmature.py
1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
5 #
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 # GNU General Public License for more details.
10 #
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
14 # Version History:
15 #   1.0 original release bakes an armature into a matrix
16 #   1.1 optional params (ACTION_BAKE, ACTION_BAKE_FIRST_FRAME, direct function to key and return the Action
17
18 import Blender
19 from Blender import sys
20 import bpy
21 def getBakedPoseData(ob_arm, start_frame, end_frame, ACTION_BAKE = False, ACTION_BAKE_FIRST_FRAME = True):
22         '''
23         If you are currently getting IPO's this function can be used to
24         ACTION_BAKE==False: return a list of frame aligned bone dictionary's
25         ACTION_BAKE==True: return an action with keys aligned to bone constrained movement
26         if ACTION_BAKE_FIRST_FRAME is not supplied or is true: keys begin at frame 1
27         
28         The data in these can be swaped in for the IPO loc and quat
29         
30         If you want to bake an action, this is not as hard and the ipo hack can be removed.
31         '''
32         
33         # --------------------------------- Dummy Action! Only for this functon
34         backup_action = ob_arm.action
35         backup_frame = Blender.Get('curframe')
36         
37         DUMMY_ACTION_NAME = '~DONT_USE~'
38         # Get the dummy action if it has no users
39         try:
40                 new_action = bpy.data.actions[DUMMY_ACTION_NAME]
41                 if new_action.users:
42                         new_action = None
43         except:
44                 new_action = None
45         
46         if not new_action:
47                 new_action = bpy.data.actions.new(DUMMY_ACTION_NAME)
48                 new_action.fakeUser = False
49         # ---------------------------------- Done
50         
51         Matrix = Blender.Mathutils.Matrix
52         Quaternion = Blender.Mathutils.Quaternion
53         Vector = Blender.Mathutils.Vector
54         POSE_XFORM= [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT]
55         
56         # Each dict a frame
57         bake_data = [{} for i in xrange(1+end_frame-start_frame)]
58         
59         pose=                   ob_arm.getPose()
60         armature_data=  ob_arm.getData();
61         pose_bones=             pose.bones
62         
63         # --------------------------------- Build a list of arma data for reuse
64         armature_bone_data = []
65         bones_index = {}
66         for bone_name, rest_bone in armature_data.bones.items():
67                 pose_bone = pose_bones[bone_name]
68                 rest_matrix = rest_bone.matrix['ARMATURESPACE']
69                 rest_matrix_inv = rest_matrix.copy().invert()
70                 armature_bone_data.append( [len(bones_index), -1, bone_name, rest_bone, rest_matrix, rest_matrix_inv, pose_bone, None ])
71                 bones_index[bone_name] = len(bones_index)
72         
73         # Set the parent ID's
74         for bone_name, pose_bone in pose_bones.items():
75                 parent = pose_bone.parent
76                 if parent:
77                         bone_index= bones_index[bone_name]
78                         parent_index= bones_index[parent.name]
79                         armature_bone_data[ bone_index ][1]= parent_index
80         # ---------------------------------- Done
81         
82         
83         
84         # --------------------------------- Main loop to collect IPO data
85         frame_index = 0
86         NvideoFrames= end_frame-start_frame
87         for current_frame in xrange(start_frame, end_frame+1):
88                 if   frame_index==0: start=sys.time()
89                 elif frame_index==15: print NvideoFrames*(sys.time()-start),"seconds estimated..." #slows as it grows *3
90                 elif frame_index >15:
91                         percom= frame_index*100/NvideoFrames
92                         print "Frame %i Overall %i percent complete\r" % (current_frame, percom),
93                 ob_arm.action = backup_action
94                 #pose.update() # not needed
95                 Blender.Set('curframe', current_frame)
96                 #Blender.Window.RedrawAll()
97                 #frame_data = bake_data[frame_index]
98                 ob_arm.action = new_action
99                 ###for i,pose_bone in enumerate(pose_bones):
100                 
101                 for index, parent_index, bone_name, rest_bone, rest_matrix, rest_matrix_inv, pose_bone, ipo in armature_bone_data:
102                         matrix= pose_bone.poseMatrix
103                         parent_bone= rest_bone.parent
104                         if parent_index != -1:
105                                 parent_pose_matrix =            armature_bone_data[parent_index][6].poseMatrix
106                                 parent_bone_matrix_inv =        armature_bone_data[parent_index][5]
107                                 matrix=                                         matrix * parent_pose_matrix.copy().invert()
108                                 rest_matrix=                            rest_matrix * parent_bone_matrix_inv
109                         
110                         matrix=matrix * rest_matrix.copy().invert()
111                         pose_bone.quat= matrix.toQuat()
112                         pose_bone.loc=  matrix.translationPart()
113                         if ACTION_BAKE==False:
114                                 pose_bone.insertKey(ob_arm, 1, POSE_XFORM) # always frame 1
115          
116                                 # THIS IS A BAD HACK! IT SUCKS BIGTIME BUT THE RESULT ARE NICE
117                                 # - use a temp action and bake into that, always at the same frame
118                                 #   so as not to make big IPO's, then collect the result from the IPOs
119                         
120                                 # Now get the data from the IPOs
121                                 if not ipo:     ipo = armature_bone_data[index][7] = new_action.getChannelIpo(bone_name)
122                         
123                                 loc = Vector()
124                                 quat  = Quaternion()
125                         
126                                 for curve in ipo:
127                                         val = curve.evaluate(1)
128                                         curve_name= curve.name
129                                         if   curve_name == 'LocX':  loc[0] = val
130                                         elif curve_name == 'LocY':  loc[1] = val
131                                         elif curve_name == 'LocZ':  loc[2] = val
132                                         elif curve_name == 'QuatW': quat[3]  = val
133                                         elif curve_name == 'QuatX': quat[0]  = val
134                                         elif curve_name == 'QuatY': quat[1]  = val
135                                         elif curve_name == 'QuatZ': quat[2]  = val
136                         
137                                 bake_data[frame_index][bone_name] = loc, quat
138                         else:
139                                 if ACTION_BAKE_FIRST_FRAME: pose_bone.insertKey(ob_arm, frame_index+1,  POSE_XFORM)
140                                 else:           pose_bone.insertKey(ob_arm, current_frame , POSE_XFORM)
141                 frame_index+=1
142         print "\nBaking Complete."
143         ob_arm.action = backup_action
144         if ACTION_BAKE==False:
145                 Blender.Set('curframe', backup_frame)
146                 return bake_data
147         elif ACTION_BAKE==True:
148                 return new_action
149         else: print "ERROR: Invalid ACTION_BAKE %i sent to BPyArmature" % ACTION_BAKE
150
151
152