forked from bartvdbraak/blender
484ac5ea1a
AGAIN PLEAST USE TABS, lost quite some time with mixed tab/space adjustments alone. Other then that, patch is very useful ;) ---Text from patch submission --- Using a slightly revised BPy_Armature, this script takes any non-armature object type and creates an Action that keys the object location (by default, for every frame). If it is an Armature, it goes into each bone and keys the locrot of the bone. You can now edit the armature, but the motions still rotate the bones. This enables the next step, re-targeting, which changes bone lengths to fit a mesh. High-level, we are working toward: 1. import mocap (bvh or c3d) 2. bake to make an action library (using this script) 3. re-target and use the actions to drive/deform any character mesh (theeth)
153 lines
5.7 KiB
Python
153 lines
5.7 KiB
Python
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
# Version History:
|
|
# 1.0 original release bakes an armature into a matrix
|
|
# 1.1 optional params (ACTION_BAKE, ACTION_BAKE_FIRST_FRAME, direct function to key and return the Action
|
|
|
|
import Blender
|
|
from Blender import sys
|
|
import bpy
|
|
def getBakedPoseData(ob_arm, start_frame, end_frame, ACTION_BAKE = False, ACTION_BAKE_FIRST_FRAME = True):
|
|
'''
|
|
If you are currently getting IPO's this function can be used to
|
|
ACTION_BAKE==False: return a list of frame aligned bone dictionary's
|
|
ACTION_BAKE==True: return an action with keys aligned to bone constrained movement
|
|
if ACTION_BAKE_FIRST_FRAME is not supplied or is true: keys begin at frame 1
|
|
|
|
The data in these can be swaped in for the IPO loc and quat
|
|
|
|
If you want to bake an action, this is not as hard and the ipo hack can be removed.
|
|
'''
|
|
|
|
# --------------------------------- Dummy Action! Only for this functon
|
|
backup_action = ob_arm.action
|
|
backup_frame = Blender.Get('curframe')
|
|
|
|
DUMMY_ACTION_NAME = '~DONT_USE~'
|
|
# Get the dummy action if it has no users
|
|
try:
|
|
new_action = bpy.data.actions[DUMMY_ACTION_NAME]
|
|
if new_action.users:
|
|
new_action = None
|
|
except:
|
|
new_action = None
|
|
|
|
if not new_action:
|
|
new_action = bpy.data.actions.new(DUMMY_ACTION_NAME)
|
|
new_action.fakeUser = False
|
|
# ---------------------------------- Done
|
|
|
|
Matrix = Blender.Mathutils.Matrix
|
|
Quaternion = Blender.Mathutils.Quaternion
|
|
Vector = Blender.Mathutils.Vector
|
|
POSE_XFORM= [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT]
|
|
|
|
# Each dict a frame
|
|
bake_data = [{} for i in xrange(1+end_frame-start_frame)]
|
|
|
|
pose= ob_arm.getPose()
|
|
armature_data= ob_arm.getData();
|
|
pose_bones= pose.bones
|
|
|
|
# --------------------------------- Build a list of arma data for reuse
|
|
armature_bone_data = []
|
|
bones_index = {}
|
|
for bone_name, rest_bone in armature_data.bones.items():
|
|
pose_bone = pose_bones[bone_name]
|
|
rest_matrix = rest_bone.matrix['ARMATURESPACE']
|
|
rest_matrix_inv = rest_matrix.copy().invert()
|
|
armature_bone_data.append( [len(bones_index), -1, bone_name, rest_bone, rest_matrix, rest_matrix_inv, pose_bone, None ])
|
|
bones_index[bone_name] = len(bones_index)
|
|
|
|
# Set the parent ID's
|
|
for bone_name, pose_bone in pose_bones.items():
|
|
parent = pose_bone.parent
|
|
if parent:
|
|
bone_index= bones_index[bone_name]
|
|
parent_index= bones_index[parent.name]
|
|
armature_bone_data[ bone_index ][1]= parent_index
|
|
# ---------------------------------- Done
|
|
|
|
|
|
|
|
# --------------------------------- Main loop to collect IPO data
|
|
frame_index = 0
|
|
NvideoFrames= end_frame-start_frame
|
|
for current_frame in xrange(start_frame, end_frame+1):
|
|
if frame_index==0: start=sys.time()
|
|
elif frame_index==15: print NvideoFrames*(sys.time()-start),"seconds estimated..." #slows as it grows *3
|
|
elif frame_index >15:
|
|
percom= frame_index*100/NvideoFrames
|
|
print "Frame %i Overall %i percent complete\r" % (current_frame, percom),
|
|
ob_arm.action = backup_action
|
|
#pose.update() # not needed
|
|
Blender.Set('curframe', current_frame)
|
|
#Blender.Window.RedrawAll()
|
|
#frame_data = bake_data[frame_index]
|
|
ob_arm.action = new_action
|
|
###for i,pose_bone in enumerate(pose_bones):
|
|
|
|
for index, parent_index, bone_name, rest_bone, rest_matrix, rest_matrix_inv, pose_bone, ipo in armature_bone_data:
|
|
matrix= pose_bone.poseMatrix
|
|
parent_bone= rest_bone.parent
|
|
if parent_index != -1:
|
|
parent_pose_matrix = armature_bone_data[parent_index][6].poseMatrix
|
|
parent_bone_matrix_inv = armature_bone_data[parent_index][5]
|
|
matrix= matrix * parent_pose_matrix.copy().invert()
|
|
rest_matrix= rest_matrix * parent_bone_matrix_inv
|
|
|
|
matrix=matrix * rest_matrix.copy().invert()
|
|
pose_bone.quat= matrix.toQuat()
|
|
pose_bone.loc= matrix.translationPart()
|
|
if ACTION_BAKE==False:
|
|
pose_bone.insertKey(ob_arm, 1, POSE_XFORM) # always frame 1
|
|
|
|
# THIS IS A BAD HACK! IT SUCKS BIGTIME BUT THE RESULT ARE NICE
|
|
# - use a temp action and bake into that, always at the same frame
|
|
# so as not to make big IPO's, then collect the result from the IPOs
|
|
|
|
# Now get the data from the IPOs
|
|
if not ipo: ipo = armature_bone_data[index][7] = new_action.getChannelIpo(bone_name)
|
|
|
|
loc = Vector()
|
|
quat = Quaternion()
|
|
|
|
for curve in ipo:
|
|
val = curve.evaluate(1)
|
|
curve_name= curve.name
|
|
if curve_name == 'LocX': loc[0] = val
|
|
elif curve_name == 'LocY': loc[1] = val
|
|
elif curve_name == 'LocZ': loc[2] = val
|
|
elif curve_name == 'QuatW': quat[3] = val
|
|
elif curve_name == 'QuatX': quat[0] = val
|
|
elif curve_name == 'QuatY': quat[1] = val
|
|
elif curve_name == 'QuatZ': quat[2] = val
|
|
|
|
bake_data[frame_index][bone_name] = loc, quat
|
|
else:
|
|
if ACTION_BAKE_FIRST_FRAME: pose_bone.insertKey(ob_arm, frame_index+1, POSE_XFORM)
|
|
else: pose_bone.insertKey(ob_arm, current_frame , POSE_XFORM)
|
|
frame_index+=1
|
|
print "\nBaking Complete."
|
|
ob_arm.action = backup_action
|
|
if ACTION_BAKE==False:
|
|
Blender.Set('curframe', backup_frame)
|
|
return bake_data
|
|
elif ACTION_BAKE==True:
|
|
return new_action
|
|
else: print "ERROR: Invalid ACTION_BAKE %i sent to BPyArmature" % ACTION_BAKE
|
|
|
|
|
|
|