2008-06-22 22:46:02 +00:00
#!BPY
"""
Name : ' Bake Constraints '
Blender : 246
Group : ' Animation '
Tooltip : ' Bake a Constrained object/rig to IPOs '
Fillename : ' Bake_Constraint.py '
"""
__author__ = " Roger Wickes (rogerwickes(at)yahoo.com) "
2008-07-05 11:38:16 +00:00
__script__ = " Animation Bake Constraints "
__version__ = " 0.7 "
2008-06-22 22:46:02 +00:00
__url__ = [ " Communicate problems and errors, http://www.blenderartists.com/forum/private.php?do=newpm to PapaSmurf " ]
__email__ = [ " Roger Wickes, rogerwickes@yahoo.com " , " scripts " ]
__bpydoc__ = """ \
bake_constraints
This script bakes the real - world LocRot of an object ( the net effect of any constraints -
( Copy , Limit , Track , Follow , - that affect Location , Rotation )
( usually one constrained to match another ' s location and/or Tracked to another)
and creates a clone with a set of Ipo Curves named Ipo < objname >
These curves control a non - constrained object and thus make it mimic the constrained object
Actions can be then be edited without the need for the drivers / constraining objects
Developed for use with MoCap data , where a bone is constrained to point at an empty
moving through space and time . This records the actual locrot of the armature
so that the motion can be edited , reoriented , scaled , and used as NLA Actions
2008-07-05 11:38:16 +00:00
see also wiki Scripts / Manual / Tutorial / Motion Capture < br >
2008-06-22 22:46:02 +00:00
2008-07-05 11:38:16 +00:00
Usage : < br >
2008-06-22 22:46:02 +00:00
- Select the reference Object ( s ) you want to bake < br >
- Set the frame range to bake in the Anim Panel < br >
- Set the test code ( if you want a self - test ) in the RT field in the Anim Panel < br >
2008-07-05 11:38:16 +00:00
- - Set RT : 1 to create a test armature < br >
- - Set RT : up to 100 for more debug messages and status updates < br >
2008-06-22 22:46:02 +00:00
< br >
2008-07-05 11:38:16 +00:00
- Run the script < br >
- The clone copy of the object is created and it has an IPO curve assigned to it . < br >
- The clone shadows the object by an offset locrot ( see usrDelta ) < br >
- That Object has Ipo Location and Rotation curves that make the clone mimic the movement < br >
of the selected object , but without using constraints . < br >
- If the object was an Armature , the clone ' s bones move identically in relation to the <br>
original armature , and an Action is created that drives the bone movements . < br >
2008-06-22 22:46:02 +00:00
Version History :
2008-07-05 11:38:16 +00:00
0.1 : bakes Loc Rot for a constrained object
0.2 : bakes Loc and Rot for the bones within Armature object
0.3 : UI for setting options
0.3 .1 add manual to script library
0.4 : bake multiple objects
0.5 : root bone worldspace rotation
0.6 : re - integration with BPyArmature
0.7 : bakes parents and leaves clones selected
2008-06-22 22:46:02 +00:00
License , Copyright , and Attribution :
2008-07-05 11:38:16 +00:00
by Roger WICKES May 2008 , released under Blender Artistic Licence to Public Domain
feel free to add to any Blender Python Scripts Bundle .
Thanks to Jean - Baptiste PERIN , IdeasMan42 ( Campbell Barton ) , Basil_Fawlty / Cage_drei ( Andrew Cruse )
2008-06-22 22:46:02 +00:00
much lifted / learned from blender . org / documentation / 245 PytonDoc and wiki
some modules based on c3D_Import . py , PoseLib16 . py and IPO / Armature code examples e . g . camera jitter
2008-07-05 11:38:16 +00:00
Pseudocode :
2008-06-22 22:46:02 +00:00
Initialize
If at least one object is selected
For each selected object ,
2008-07-05 11:38:16 +00:00
create a cloned object
2008-06-22 22:46:02 +00:00
remove any constraints on the clone
create or reset an ipo curve named like the object
for each frame
set the clone ' s locrot key based on the reference object
if it ' s an armature,
2008-07-05 11:38:16 +00:00
create an action ( which is an Ipo for each bone )
for each frame of the animation
for each bone in the armature
set the key
2008-06-22 22:46:02 +00:00
Else you ' re a smurf
Test Conditions and Regressions :
1. ( v0 .1 ) Non - armatures ( the cube ) , with ipo curve and constraints at the object level
2. armatures , with ipo curve and constraints at the object level
3. armatures , with bones that have ipo curves and constraints
2008-07-05 11:38:16 +00:00
4. objects without parents , children with unselected parents , select children first .
2008-06-22 22:46:02 +00:00
Naming conventions :
arm = a specific objec type armature
bone = bones that make up the skeleton of an armature
ob = object , an instance of an object type
ebone = edit bone , a bone in edit mode
pbone = pose bone , a posed bone in an object
tst = testing , self - test routines
usr = user - entered or designated stuff
"""
########################################
import Blender
from Blender import *
from Blender . Mathutils import *
import struct
import string
import bpy
import BPyMessages
import BPyArmature
# reload(BPyArmature)
from BPyArmature import getBakedPoseData
Vector = Blender . Mathutils . Vector
Euler = Blender . Mathutils . Euler
Matrix = Blender . Mathutils . Matrix #invert() function at least
RotationMatrix = Blender . Mathutils . RotationMatrix
TranslationMatrix = Blender . Mathutils . TranslationMatrix
Quaternion = Blender . Mathutils . Quaternion
Vector = Blender . Mathutils . Vector
POSE_XFORM = [ Blender . Object . Pose . LOC , Blender . Object . Pose . ROT ]
#=================
# Global Variables
#=================
# set senstitivity for displaying debug/console messages. 0=none, 100=max
# then call debug(num,string) to conditionally display status/info in console window
2008-07-05 11:38:16 +00:00
MODE = Blender . Get ( ' rt ' ) #execution mode: 0=run normal, 1=make test armature
DEBUG = Blender . Get ( ' rt ' ) #how much detail on internal processing for user to see. range 0-100
2008-06-22 22:46:02 +00:00
BATCH = False #called from command line? is someone there? Would you like some cake?
#there are two coordinate systems, the real, or absolute 3D space,
# and the local relative to a parent.
COORDINATE_SYSTEMS = [ ' local ' , ' real ' ]
COORD_LOCAL = 0
COORD_REAL = 1
# User Settings - Change these options manually or via GUI (future TODO)
usrCoord = COORD_REAL # what the user wants
2008-07-05 11:38:16 +00:00
usrParent = False # True=clone keeps original parent, False = clone's parent is the clone of the original parent (if cloned)
2008-06-22 22:46:02 +00:00
usrFreeze = 2 #2=yes, 0=no. Freezes shadow object in place at current frame as origin
# delta is amount to offset/change from the reference object. future set in a ui, so technically not a constant
usrDelta = [ 10 , 10 , 0 , 0 , 0 , 0 ] #order specific - Loc xyz Rot xyz
usrACTION = True # Offset baked Action frames to start at frame 1
CURFRAME = ' curframe ' #keyword to use when getting the frame number that the scene is presently on
ARMATURE = ' Armature ' #en anglais
BONE_SPACES = [ ' ARMATURESPACE ' , ' BONESPACE ' ]
# 'ARMATURESPACE' - this matrix of the bone in relation to the armature
# 'BONESPACE' - the matrix of the bone in relation to itself
#Ipo curves created are prefixed with a name, like Ipo_ or Bake_ followed by the object/bone name
#bakedArmName = "b." #used for both the armature class and object instance
2008-07-05 11:38:16 +00:00
usrObjectNamePrefix = " "
2008-06-22 22:46:02 +00:00
#ipoBoneNamePrefix = ""
# for example, if on entry an armature named Man was selected, and the object prefix was "a."
# on exit an armature and an IPO curve named a.Man exists for the object as a whole
# if that armature had bones (spine, neck, arm) and the bone prefix was "a."
# the bones and IPO curves will be (a.spine, a.neck, a.arm)
2009-01-26 08:34:40 +00:00
R2D = 18 / 3.141592653589793 # radian to grad
2008-06-22 22:46:02 +00:00
BLENDER_VERSION = Blender . Get ( ' version ' )
# Gets the current scene, there can be many scenes in 1 blend file.
scn = Blender . Scene . GetCurrent ( )
#=================
# Methods
#=================
########################################
def debug ( num , msg ) : #use log4j or just console here.
if DEBUG > = num :
if BATCH == False :
print ' debug: ' [ : num / 10 + 7 ] + msg
#TODO: else write out to file (runs faster if it doesnt have to display details)
return
########################################
def error ( str ) :
debug ( 0 , ' ERROR: ' + str )
if BATCH == False :
Draw . PupMenu ( ' ERROR % t| ' + str )
return
########################################
def getRenderInfo ( ) :
context = scn . getRenderingContext ( )
staframe = context . startFrame ( )
endframe = context . endFrame ( )
if endframe < staframe : endframe = staframe
curframe = Blender . Get ( CURFRAME )
debug ( 90 , ' Scene is on frame %i and frame range is %i to %i ' % ( curframe , staframe , endframe ) )
return ( staframe , endframe , curframe )
2008-07-05 11:38:16 +00:00
########################################
def sortObjects ( obs ) : #returns a list of objects sorted based on parent dependency
obClones = [ ]
while len ( obClones ) < len ( obs ) :
for ob in obs :
if not ob in obClones :
par = ob . getParent ( )
#if no parent, or the parent is not scheduled to be cloned
if par == None :
obClones . append ( ob ) # add the independent
elif par not in obs : # parent will not be cloned
obClones . append ( ob ) # add the child
elif par in obClones : # is it on the list?
obClones . append ( ob ) # add the child
# parent may be a child, so it will be caught next time thru
debug ( 100 , ' clone object order: \n %s ' % obClones )
return obClones # ordered list of (ob, par) tuples
2008-06-22 22:46:02 +00:00
########################################
def sortBones ( xbones ) : #returns a sorted list of bones that should be added,sorted based on parent dependency
# while there are bones to add,
# look thru the list of bones we need to add
# if we have not already added this bone
# if it does not have a parent
# add it
# else, it has a parent
# if we already added it's parent
# add it now.
# else #we need to keep cycling and catch its parent
# else it is a root bone
# add it
2008-07-05 11:38:16 +00:00
# else skip it, it's already in there
2008-06-22 22:46:02 +00:00
# endfor
# endwhile
xboneNames = [ ]
for xbone in xbones : xboneNames . append ( xbone . name )
debug ( 80 , ' reference bone order: \n %s ' % xboneNames )
eboneNames = [ ]
while len ( eboneNames ) < len ( xboneNames ) :
for xbone in xbones :
if not xbone . name in eboneNames :
if not xbone . parent :
eboneNames . append ( xbone . name )
else :
if xbone . parent . name in eboneNames :
eboneNames . append ( xbone . name )
#else skip it
#endif
#else prego
#endfor
#endwhile
debug ( 80 , ' clone bone order: \n %s ' % eboneNames )
return eboneNames
########################################
def dupliArmature ( ob ) : #makes a copy in current scn of the armature used by ob and its bones
ob_mat = ob . matrixWorld
ob_data = ob . getData ( )
debug ( 49 , ' Reference object uses %s ' % ob_data )
arm_ob = Armature . Get ( ob_data . name ) #the armature used by the passed object
arm = Blender . Armature . New ( )
debug ( 20 , ' Cloning Armature %s to create %s ' % ( arm_ob . name , arm . name ) )
arm . drawType = Armature . STICK #set the draw type
arm . makeEditable ( ) #enter editmode
# for each bone in the object's armature,
xbones = ob . data . bones . values ( )
usrSpace = 0 #0=armature, 1=local
space = [ BONE_SPACES [ usrSpace ] ] [ 0 ]
#we have to make a list of bones, then figure out our parents, then add to the arm
#when creating a child, we cannot link to a parent if it does not yet exist in our armature
ebones = [ ] #list of the bones I want to create for my arm
eboneNames = sortBones ( xbones )
i = 0
# error('bones sorted. continue?')
for abone in eboneNames : #set all editable attributes to fully define the bone.
for bone in xbones :
if bone . name == abone : break # get the reference bone
ebone = Armature . Editbone ( ) #throw me a bone, bone-man!
ebones . append ( ebone ) #you're on my list, buddy
ebone . name = bone . name
ebone . headRadius = bone . headRadius
ebone . tailRadius = bone . tailRadius
ebone . weight = bone . weight
ebone . options = bone . options
ebone . head = bone . head [ space ] #dictionary lookups
ebone . tail = bone . tail [ space ]
ebone . matrix = bone . matrix [ space ]
ebone . roll = bone . roll [ space ]
debug ( 30 , ' Generating new %s as child of %s ' % ( bone , bone . parent ) )
if bone . hasParent ( ) :
# parent=bone.parent.name
# debug(100,'looking for %s' % parent)
# for parbone in xbones: if parbone.name == parent: break # get the parent bone
# ebone.parent = arm.bones[ebones[j].name]
ebone . parent = arm . bones [ bone . parent . name ]
# else:
# ebone.parent = None
debug ( 30 , ' Generating new editbone %s as child of %s ' % ( ebone , ebone . parent ) )
arm . bones [ ebone . name ] = ebone # i would have expected an append or add function, but this works
debug ( 100 , ' arm.bones: \n %s ' % arm . bones )
debug ( 20 , ' Cloned %i bones now in armature %s ' % ( len ( arm . bones ) , arm . name ) )
myob = scn . objects . new ( arm ) #interestingly, object must be created before
arm . update ( ) #armature can be saved
debug ( 40 , ' dupArm finished %s instanced as object %s ' % ( arm . name , myob . getName ( ) ) )
print ob . matrix
print myob . matrix
return myob
2008-07-05 11:38:16 +00:00
########################################
2008-06-22 22:46:02 +00:00
def scrub ( ) : # scrubs to startframe
staFrame , endFrame , curFrame = getRenderInfo ( )
# eye-candy, go from current to start, fwd or back
if not BATCH :
2008-07-05 11:38:16 +00:00
debug ( 100 , " Positioning to start... " )
2008-06-22 22:46:02 +00:00
frameinc = ( staFrame - curFrame ) / 10
if abs ( frameinc ) > = 1 :
for i in range ( 10 ) :
curFrame + = frameinc
Blender . Set ( CURFRAME , curFrame ) # computes the constrained location of the 'real' objects
Blender . Redraw ( )
Blender . Set ( CURFRAME , staFrame )
2008-07-05 11:38:16 +00:00
return
2008-06-22 22:46:02 +00:00
########################################
def bakeBones ( ref_ob , arm_ob ) : #copy pose from ref_ob to arm_ob
scrub ( )
staFrame , endFrame , curFrame = getRenderInfo ( )
act = getBakedPoseData ( ref_ob , staFrame , endFrame , ACTION_BAKE = True , ACTION_BAKE_FIRST_FRAME = usrACTION ) # bake the pose positions of the reference ob to the armature ob
arm_ob . action = act
scrub ( )
# user comprehension feature - change action name and channel ipo names to match the names of the bone they drive
debug ( 80 , ' Renaming each action ipo to match the bone they pose ' )
act . name = arm_ob . name
arm_channels = act . getAllChannelIpos ( )
pose = arm_ob . getPose ( )
pbones = pose . bones . values ( ) #we want the bones themselves, not the dictionary lookup
for pbone in pbones :
debug ( 100 , ' Channel listing for %s : %s ' % ( pbone . name , arm_channels [ pbone . name ] ) )
ipo = arm_channels [ pbone . name ]
ipo . name = pbone . name # since bone names are unique within an armature, the pose names can be the same since they are within an Action
return
########################################
2008-07-05 11:38:16 +00:00
def getOrCreateCurve ( ipo , curvename ) :
2008-06-22 22:46:02 +00:00
"""
Retrieve or create a Blender Ipo Curve named C { curvename } in the C { ipo } Ipo
Either an ipo curve named C { curvename } exists before the call then this curve is returned ,
Or such a curve doesn ' t exist before the call .. then it is created into the c {ipo} Ipo and returned
"""
try :
mycurve = ipo . getCurve ( curvename )
if mycurve != None :
pass
else :
mycurve = ipo . addCurve ( curvename )
except :
mycurve = ipo . addCurve ( curvename )
return mycurve
########################################
2008-07-05 11:38:16 +00:00
def eraseCurve ( ipo , numCurves ) :
debug ( 90 , ' Erasing %i curves for % ' % ( numCurves , ipo . GetName ( ) ) )
2008-06-22 22:46:02 +00:00
for i in range ( numCurves ) :
2008-07-05 11:38:16 +00:00
nbBezPoints = ipo . getNBezPoints ( i )
2008-06-22 22:46:02 +00:00
for j in range ( nbBezPoints ) :
ipo . delBezPoint ( i )
return
########################################
def resetIPO ( ipo ) :
2008-07-05 11:38:16 +00:00
debug ( 60 , ' Resetting ipo curve named %s ' % ipo . name )
2008-06-22 22:46:02 +00:00
numCurves = ipo . getNcurves ( ) #like LocX, LocY, etc
if numCurves > 0 :
eraseCurve ( ipo , numCurves ) #erase data if one exists
return
########################################
def resetIPOs ( ob ) : #resets all IPO curvess assocated with an object and its bones
debug ( 30 , ' Resetting any ipo curves linked to %s ' % ob . getName ( ) )
ipo = ob . getIpo ( ) #may be None
ipoName = ipo . getName ( ) #name of the IPO that guides/controls this object
debug ( 70 , ' Object IPO is %s ' % ipoName )
try :
ipo = Ipo . Get ( ipoName )
except :
ipo = Ipo . New ( ' Object ' , ipoName )
resetIPO ( ipo )
if ob . getType ( ) == ARMATURE :
arm_data = ob . getData ( )
bones = arm_data . bones . values ( )
for bone in bones :
#for each bone: get the name and check for a Pose IPO
debug ( 10 , ' Processing ' + bone . name )
return
########################################
2008-07-05 11:38:16 +00:00
def parse ( string , delim ) :
2008-06-22 22:46:02 +00:00
index = string . find ( delim ) # -1 if not found, else pointer to delim
if index + 1 : return string [ : index ]
return string
2008-07-05 11:38:16 +00:00
2008-06-22 22:46:02 +00:00
########################################
def newIpo ( ipoName ) : #add a new Ipo object to the Blender scene
ipo = Blender . Ipo . New ( ' Object ' , ipoName )
ipo . addCurve ( ' LocX ' )
ipo . addCurve ( ' LocY ' )
ipo . addCurve ( ' LocZ ' )
ipo . addCurve ( ' RotX ' )
ipo . addCurve ( ' RotY ' )
ipo . addCurve ( ' RotZ ' )
return ipo
########################################
def makeUpaName ( type , name ) : #i know this exists in Blender somewhere...
debug ( 90 , ' Making up a new %s name using %s as a basis. ' % ( type , name ) )
name = ( parse ( name , ' . ' ) )
if type == ' Ipo ' :
ipoName = name # maybe we get lucky today
ext = 0
extlen = 3 # 3 digit extensions, like hello.002
success = False
while not ( success ) :
try :
debug ( 100 , ' Trying %s ' % ipoName )
ipo = Ipo . Get ( ipoName )
#that one exists if we get here. add on extension and keep trying
ext + = 1
if ext > = 10 * * extlen : extlen + = 1 # go to more digits if 999 not found
ipoName = ' %s . %s ' % ( name , str ( ext ) . zfill ( extlen ) )
except : # could not find it
success = True
name = ipoName
else :
debug ( 0 , ' FATAL ERROR: I dont know how to make up a new %s name based on %s ' % ( type , ob ) )
2008-07-05 11:38:16 +00:00
return None
2008-06-22 22:46:02 +00:00
return name
########################################
2008-07-05 11:38:16 +00:00
def createIpo ( ob ) : #create an Ipo and curves and link them to this object
2008-06-22 22:46:02 +00:00
#first, we have to create a unique name
#try first with just the name of the object to keep things simple.
ipoName = makeUpaName ( ' Ipo ' , ob . getName ( ) ) # make up a name for a new Ipo based on the object name
debug ( 20 , ' Ipo and LocRot curves called %s ' % ipoName )
ipo = newIpo ( ipoName )
ob . setIpo ( ipo ) #link them
return ipo
########################################
def getLocLocal ( ob ) :
key = [
ob . LocX ,
ob . LocY ,
ob . LocZ ,
ob . RotX * R2D , #get the curves in this order
ob . RotY * R2D ,
ob . RotZ * R2D
]
return key
2008-07-05 11:38:16 +00:00
2008-06-22 22:46:02 +00:00
########################################
def getLocReal ( ob ) :
obMatrix = ob . matrixWorld #Thank you IdeasMan42
loc = obMatrix . translationPart ( )
rot = obMatrix . toEuler ( )
key = [
loc . x ,
loc . y ,
loc . z ,
rot . x / 10 ,
rot . y / 10 ,
rot . z / 10
]
return key
########################################
def getLocRot ( ob , space ) :
if space in xrange ( len ( COORDINATE_SYSTEMS ) ) :
if space == COORD_LOCAL :
key = getLocLocal ( ob )
return key
elif space == COORD_REAL :
key = getLocReal ( ob )
return key
else : #hey, programmers make mistakes too.
debug ( 0 , ' Fatal Error: getLoc called with %i ' % space )
return
########################################
def getCurves ( ipo ) :
ipos = [
ipo [ Ipo . OB_LOCX ] ,
ipo [ Ipo . OB_LOCY ] ,
ipo [ Ipo . OB_LOCZ ] ,
ipo [ Ipo . OB_ROTX ] , #get the curves in this order
ipo [ Ipo . OB_ROTY ] ,
ipo [ Ipo . OB_ROTZ ]
]
return ipos
2008-07-05 11:38:16 +00:00
2008-06-22 22:46:02 +00:00
########################################
def addPoint ( time , keyLocRot , ipos ) :
if BLENDER_VERSION < 245 :
debug ( 0 , ' WARNING: addPoint uses BezTriple ' )
for i in range ( len ( ipos ) ) :
point = BezTriple . New ( ) #this was new with Blender 2.45 API
point . pt = ( time , keyLocRot [ i ] )
point . handleTypes = [ 1 , 1 ]
ipos [ i ] . append ( point )
return ipos
########################################
def bakeFrames ( ob , myipo ) : #bakes an object in a scene, returning the IPO containing the curves
myipoName = myipo . getName ( )
debug ( 20 , ' Baking frames for scene %s object %s to ipo %s ' % ( scn . getName ( ) , ob . getName ( ) , myipoName ) )
ipos = getCurves ( myipo )
#TODO: Gui setup idea: myOffset
# reset action to start at frame 1 or at location
myOffset = 0 #=1-staframe
#loop through frames in the animation. Often, there is rollup and the mocap starts late
staframe , endframe , curframe = getRenderInfo ( )
for frame in range ( staframe , endframe + 1 ) :
debug ( 80 , ' Baking Frame %i ' % frame )
#tell Blender to advace to frame
Blender . Set ( CURFRAME , frame ) # computes the constrained location of the 'real' objects
if not BATCH : Blender . Redraw ( ) # no secrets, let user see what we are doing
#using the constrained Loc Rot of the object, set the location of the unconstrained clone. Yea! Clones are FreeMen
key = getLocRot ( ob , usrCoord ) #a key is a set of specifed exact channel values (LocRotScale) for a certain frame
key = [ a + b for a , b in zip ( key , usrDelta ) ] #offset to the new location
myframe = frame + myOffset
Blender . Set ( CURFRAME , myframe )
time = Blender . Get ( ' curtime ' ) #for BezTriple
ipos = addPoint ( time , key , ipos ) #add this data at this time to the ipos
debug ( 100 , ' %s %i %.3f %.2f %.2f %.2f %.2f %.2f %.2f ' % ( myipoName , myframe , time , key [ 0 ] , key [ 1 ] , key [ 2 ] , key [ 3 ] , key [ 4 ] , key [ 5 ] ) )
# eye-candy - smoothly rewind the animation, showing now how the clone match moves
if endframe - staframe < 400 and not BATCH :
for frame in range ( endframe , staframe , - 1 ) : #rewind
Blender . Set ( CURFRAME , frame ) # computes the constrained location of the 'real' objects
Blender . Redraw ( )
Blender . Set ( CURFRAME , staframe )
Blender . Redraw ( )
return ipos
########################################
def duplicateLinked ( ob ) :
obType = ob . type
debug ( 10 , ' Duplicating %s Object named %s ' % ( obType , ob . getName ( ) ) )
scn . objects . selected = [ ob ]
## rdw: simplified by just duplicating armature. kept code as reference for creating armatures
## disadvantage is that you cant have clone as stick and original as octahedron
## since they share the same Armature. User can click Make Single User button.
## if obType == ARMATURE: #build a copy from scratch
## myob= dupliArmature(ob)
## else:
Blender . Object . Duplicate ( ) # Duplicate linked, including pose constraints.
myobs = Object . GetSelected ( ) #duplicate is top on the list
myob = myobs [ 0 ]
if usrParent == False :
myob . clrParent ( usrFreeze )
debug ( 20 , ' =myob= was created as %s ' % myob . getName ( ) )
return myob
########################################
def removeConstraints ( ob ) :
for const in ob . constraints :
debug ( 90 , ' removed %s => %s ' % ( ob . name , const ) )
ob . constraints . remove ( const )
return
2008-07-05 11:38:16 +00:00
2008-06-22 22:46:02 +00:00
########################################
def removeConstraintsOb ( ob ) : # from object or armature
debug ( 40 , ' Removing constraints from ' + ob . getName ( ) )
if BLENDER_VERSION > 241 : #constraints module not available before 242
removeConstraints ( ob )
if ob . getType ( ) == ARMATURE :
pose = ob . getPose ( )
for pbone in pose . bones . values ( ) :
#bone = pose.bones[bonename]
removeConstraints ( pbone )
#should also check if it is a deflector?
return
########################################
def deLinkOb ( type , ob ) : #remove linkages
if type == ' Ipo ' :
success = ob . clearIpo ( ) #true=there was one
if success : debug ( 80 , ' deLinked Ipo curve to %s ' % ob . getName ( ) )
return
########################################
def bakeObject ( ob ) : #bakes the core object locrot and assigns the Ipo to a Clone
if ob != None :
# Clone the object - duplicate it, clean the clone, and create an ipo curve for the clone
myob = duplicateLinked ( ob ) #clone it
2008-09-19 20:22:54 +00:00
myob . setName ( usrObjectNamePrefix + ob . getName ( ) )
2008-06-22 22:46:02 +00:00
removeConstraintsOb ( myob ) #my object is a free man
deLinkOb ( ' Ipo ' , myob ) #kids, it's not nice to share. you've been lied to
2008-07-05 11:38:16 +00:00
if ob . getType ( ) != ARMATURE : # baking armatures is based on bones, not object
2008-06-22 22:46:02 +00:00
myipo = createIpo ( myob ) #create own IPO and curves for the clone object
ipos = bakeFrames ( ob , myipo ) #bake the locrot for this obj for the scene frames
return myob
2008-07-05 11:38:16 +00:00
########################################
def bake ( ob , par ) : #bakes an object of any type, linking it to parent
debug ( 0 , ' Baking %s object %s ' % ( ob . getType ( ) , ob ) )
clone = bakeObject ( ob ) #creates and bakes the object motion
if par != None :
par . makeParent ( [ clone ] )
debug ( 20 , " assigned object to parent %s " % par )
2008-06-22 22:46:02 +00:00
if ob . getType ( ) == ARMATURE :
2008-07-05 11:38:16 +00:00
## error('Object baked. Continue with bones?')
bakeBones ( ob , clone ) #go into the bones and copy from -> to in frame range
2008-06-22 22:46:02 +00:00
#future idea: bakeMesh (net result of Shapekeys, Softbody, Cloth, Fluidsim,...)
2008-07-05 11:38:16 +00:00
return clone
2008-06-22 22:46:02 +00:00
########################################
def tstCreateArm ( ) : #create a test armature in scene
# rip-off from http://www.blender.org/documentation/245PythonDoc/Pose-module.html - thank you!
debug ( 0 , ' Making Test Armature ' )
# New Armature
arm_data = Armature . New ( ' myArmature ' )
print arm_data
arm_ob = scn . objects . new ( arm_data )
arm_data . makeEditable ( )
# Add 4 bones
ebones = [ Armature . Editbone ( ) , Armature . Editbone ( ) , Armature . Editbone ( ) , Armature . Editbone ( ) ]
# Name the editbones
ebones [ 0 ] . name = ' Bone.001 '
ebones [ 1 ] . name = ' Bone.002 '
ebones [ 2 ] . name = ' Bone.003 '
ebones [ 3 ] . name = ' Bone.004 '
# Assign the editbones to the armature
for eb in ebones :
arm_data . bones [ eb . name ] = eb
# Set the locations of the bones
ebones [ 0 ] . head = Mathutils . Vector ( 0 , 0 , 0 )
ebones [ 0 ] . tail = Mathutils . Vector ( 0 , 0 , 1 ) #tip
ebones [ 1 ] . head = Mathutils . Vector ( 0 , 0 , 1 )
ebones [ 1 ] . tail = Mathutils . Vector ( 0 , 0 , 2 )
ebones [ 2 ] . head = Mathutils . Vector ( 0 , 0 , 2 )
ebones [ 2 ] . tail = Mathutils . Vector ( 0 , 0 , 3 )
ebones [ 3 ] . head = Mathutils . Vector ( 0 , 0 , 3 )
ebones [ 3 ] . tail = Mathutils . Vector ( 0 , 0 , 4 )
ebones [ 1 ] . parent = ebones [ 0 ]
ebones [ 2 ] . parent = ebones [ 1 ]
ebones [ 3 ] . parent = ebones [ 2 ]
arm_data . update ( )
# Done with editing the armature
# Assign the pose animation
arm_pose = arm_ob . getPose ( )
act = arm_ob . getAction ( )
if not act : # Add a pose action if we dont have one
act = Armature . NLA . NewAction ( )
act . setActive ( arm_ob )
xbones = arm_ob . data . bones . values ( )
pbones = arm_pose . bones . values ( )
frame = 1
for pbone in pbones : # set bones to no rotation
2008-07-05 11:38:16 +00:00
pbone . quat [ : ] = 1.000 , 0.000 , 0.000 , 0.0000
pbone . insertKey ( arm_ob , frame , Object . Pose . ROT )
2008-06-22 22:46:02 +00:00
# Set a different rotation at frame 25
pbones [ 0 ] . quat [ : ] = 1.000 , 0.1000 , 0.2000 , 0.20000
pbones [ 1 ] . quat [ : ] = 1.000 , 0.6000 , 0.5000 , 0.40000
pbones [ 2 ] . quat [ : ] = 1.000 , 0.1000 , 0.3000 , 0.40000
pbones [ 3 ] . quat [ : ] = 1.000 , - 0.2000 , - 0.3000 , 0.30000
frame = 25
for i in xrange ( 4 ) :
pbones [ i ] . insertKey ( arm_ob , frame , Object . Pose . ROT )
pbones [ 0 ] . quat [ : ] = 1.000 , 0.000 , 0.000 , 0.0000
pbones [ 1 ] . quat [ : ] = 1.000 , 0.000 , 0.000 , 0.0000
pbones [ 2 ] . quat [ : ] = 1.000 , 0.000 , 0.000 , 0.0000
pbones [ 3 ] . quat [ : ] = 1.000 , 0.000 , 0.000 , 0.0000
frame = 50
for pbone in pbones : # set bones to no rotation
pbone . quat [ : ] = 1.000 , 0.000 , 0.000 , 0.0000
pbone . insertKey ( arm_ob , frame , Object . Pose . ROT )
return arm_ob
########################################
def tstMoveOb ( ob ) : # makes a simple LocRot animation of object in the scene
anim = [
#Loc Rot/10
#
( 0 , 0 , 0 , 0 , 0 , 0 ) , #frame 1 origin
( 1 , 0 , 0 , 0 , 0 , 0 ) , #frame 2
( 1 , 1 , 0 , 0 , 0 , 0 ) ,
( 1 , 1 , 1 , 0 , 0 , 0 ) ,
( 1 , 1 , 1 , 4.5 , 0 , 0 ) ,
( 1 , 1 , 1 , 4.5 , 4.5 , 0 ) ,
( 1 , 1 , 1 , 4.5 , 4.5 , 4.5 )
]
space = COORD_LOCAL
ipo = createIpo ( ob ) #create an Ipo and curves for this object
ipos = getCurves ( ipo )
# span this motion over the currently set anim range
# to set points, i need time but do not know how it is computed, so will have to advance the animation
staframe , endframe , curframe = getRenderInfo ( )
frame = staframe #x position of new ipo datapoint. set to staframe if you want a match
frameDelta = ( endframe - staframe ) / ( len ( anim ) ) #accomplish the animation in frame range
for key in anim : #effectively does a getLocRot()
#tell Blender to advace to frame
Blender . Set ( ' curframe ' , frame ) # computes the constrained location of the 'real' objects
time = Blender . Get ( ' curtime ' )
ipos = addPoint ( time , key , ipos ) #add this data at this time to the ipos
debug ( 100 , ' %s %i %.3f %.2f %.2f %.2f %.2f %.2f %.2f ' % ( ipo . name , frame , time , key [ 0 ] , key [ 1 ] , key [ 2 ] , key [ 3 ] , key [ 4 ] , key [ 5 ] ) )
frame + = frameDelta
2008-07-05 11:38:16 +00:00
Blender . Set ( CURFRAME , curframe ) # reset back to where we started
2008-06-22 22:46:02 +00:00
return
#=================
# Program Template
#=================
########################################
def main ( ) :
# return code set via rt button in Blender Buttons Scene Context Anim panel
if MODE == 1 : #create test armature #1
ob = tstCreateArm ( ) # make test arm and select it
tstMoveOb ( ob )
scn . objects . selected = [ ob ]
2008-07-05 11:38:16 +00:00
obs = Blender . Object . GetSelected ( ) #scn.objects.selected
obs = sortObjects ( obs )
debug ( 0 , ' Baking %i objects ' % len ( obs ) )
2008-06-22 22:46:02 +00:00
if len ( obs ) > = 1 : # user might have multiple objects selected
2008-07-05 11:38:16 +00:00
i = 0
clones = [ ] # my clone army
2008-06-22 22:46:02 +00:00
for ob in obs :
2008-07-05 11:38:16 +00:00
par = ob . getParent ( )
if not usrParent :
if par in obs :
par = clones [ obs . index ( par ) ]
clones . append ( bake ( ob , par ) )
scn . objects . selected = clones
2008-06-22 22:46:02 +00:00
else :
error ( ' Please select at least one object ' )
return
########################################
def benchmark ( ) : # This lets you benchmark (time) the script's running duration
Window . WaitCursor ( 1 )
t = sys . time ( )
debug ( 60 , ' %s began at %.0f ' % ( __script__ , sys . time ( ) ) )
# Run the function on the active scene
in_editmode = Window . EditMode ( )
if in_editmode : Window . EditMode ( 0 )
main ( )
if in_editmode : Window . EditMode ( 1 )
# Timing the script is a good way to be aware on any speed hits when scripting
2008-07-05 11:38:16 +00:00
debug ( 0 , ' %s Script finished in %.2f seconds ' % ( __script__ , sys . time ( ) - t ) )
2008-06-22 22:46:02 +00:00
Window . WaitCursor ( 0 )
return
########################################
# This lets you can import the script without running it
if __name__ == ' __main__ ' :
debug ( 0 , " ------------------------------------ " )
2008-07-05 11:38:16 +00:00
debug ( 0 , " %s %s Script begins with mode= %i debug= %i batch= %s " % ( __script__ , __version__ , MODE , DEBUG , BATCH ) )
2008-06-22 22:46:02 +00:00
benchmark ( )