blender/release/scripts/animation_bake_constraints.py
Campbell Barton 484ac5ea1a [#14405] New python Script - Bake Constraints
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)
2008-06-22 22:46:02 +00:00

801 lines
28 KiB
Python

#!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)"
__script__ = "Bake Constraints"
__version__ = "0.6"
__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
see also wiki Scripts/Manual/ Sandbox/Animation/Bake_Constraints (tbd)
Usage:<br>
- 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>
-- Set RT:1 in the Anim panel to create a test armature <br>
<br>
- Run the script <br>
- The clone copy of the object is created and it has an IPO curve assigned to it.
- The clone shadows the object by an offset locrot (see usrDelta)
- That Ipo has Location and Rotation curves that make the shadow mimic the movement of the selected object,
but without using constraints. Bones move identically in relation to the armature as the reference object
Version History:
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
License, Copyright, and Attribution:
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)
much lifted/learned from blender.org/documentation/245PytonDoc and wiki
some modules based on c3D_Import.py, PoseLib16.py and IPO/Armature code examples e.g. camera jitter
Pseudocode (planned versions):
Initialize
If at least one object is selected
For each selected object,
create a shadow object
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,
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
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
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
Pattern Notes (let me know if I've violated any):
Bergin Starting,Designing, Programming, Coding
Bergin 23 Indent for Structure - I don't like only 2, but the editor is set up that way
Bergin 26 Be Spacey Not Tabby - personal frustraion here. workaround is to Format->convert to whitespace
Bergin 27 Consistent Capitalization - except Blender, because I love it.
Bergin 28 Name Your Constants - not for those I plan on making variable
Python 01 Return Everything - I made this one up, all functions and methods end in return
even though it is decoration in Python, it helps Python throw an indentation error for typing mistakes
Wickes 01 Decorate Your Code - for visual appeal and to ease maintenance, include separators like #########
to visually distinquish and separate functions, making it quicker to scan through code for methods
Wickes 02 Whitespace helps readability - include blanks around = # and lines (after def, after return) to make it stand out and pretty
"""
########################################
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
MODE=Blender.Get('rt') #execution mode: 0=run normal, x=self-test (test error trapping etc)
DEBUG=100 #how much detail on internal processing for big brother to see
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
usrParent = False # True=keep parent (if exists), False = breakaway (usually with Real)
usrFreeze = 2 #2=yes, 0=no. Freezes shadow object in place at current frame as origin
#TODO - i wonder if usrFreeze means we should set Delta to the the difference between the original object and parent?
# 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
usrBAKEobjIPO = False # bake the object Ipo? it is useless for MoCap, as we only want the Action, and the Object does not move
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
#ipoObjectNamePrefix= ""
#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)
R2D = 18/3.1415 # radian to grad
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)
########################################
def sortBones(xbones): #returns a sorted list of bones that should be added,sorted based on parent dependency
print ('My suggestion would be:')
# 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
# else skip it, it's prego
# 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
if BLENDER_VERSION > 245: debug(0,'WARNING: Programmer check for Bone updates in dupliArmature')
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
def scrub(): # scrubs to startframe
staFrame,endFrame,curFrame = getRenderInfo()
# eye-candy, go from current to start, fwd or back
if not BATCH:
print "Positioning to start..."
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)
########################################
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
print arm_channels.keys()
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
########################################
def getOrCreateCurve(ipo, curvename):
"""
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
########################################
def eraseCurve(ipo,numCurves):
debug(80,'Erasing %i curves for %' % (numCurves,ipo.GetName()))
for i in range(numCurves):
nbBezPoints = ipo.getNBezPoints(i)
for j in range(nbBezPoints):
ipo.delBezPoint(i)
return
########################################
def resetIPO(ipo):
ipoName=ipoObjectNamePrefix + obName
debug(40,'Resetting ipo curve named %s' %ipoName)
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
########################################
def parse(string,delim):
index = string.find(delim) # -1 if not found, else pointer to delim
if index+1: return string[:index]
return string
########################################
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))
return
return name
########################################
def createIpo(ob): #create an Ipo and curves and link them to this object
#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
########################################
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
########################################
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
########################################
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
removeConstraintsOb(myob) #my object is a free man
deLinkOb('Ipo',myob) #kids, it's not nice to share. you've been lied to
if usrBAKEobjIPO:
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
# staframe,endframe,curframe = getRenderInfo()
# frame = staframe
# Blender.Set(CURFRAME,frame) # computes the constrained location of the 'real' objects
# frame +=1
# Blender.Set(CURFRAME,frame) # computes the constrained location of the 'real' objects
# frame -=1
# Blender.Set(CURFRAME,frame) # computes the constrained location of the 'real' objects
# if not BATCH: Blender.Redraw()
#
return myob
########################################
def bake(ob): #bakes an object of any type
debug(30,'Baking %s object %s' % (ob.getType(), ob))
myob = bakeObject(ob) #creates and bakes the object motion
if ob.getType() == ARMATURE:
# error('Object baked. Continue with bones?')
bakeBones(ob,myob) #go into the bones and copy from -> to in frame range
#future idea: bakeMesh (net result of Shapekeys, Softbody, Cloth, Fluidsim,...)
return
########################################
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
pbone.quat[:] = 1.000,0.000,0.000,0.0000
pbone.insertKey(arm_ob, frame, Object.Pose.ROT)
# 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
Blender.Set('curframe',curframe) # reset back to where we started
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]
obs = Blender.Object.GetSelected() #scn.objects.selected
debug(20,'Baking %i objects' % len(obs))
if len(obs) >= 1: # user might have multiple objects selected
for ob in obs:
bake(ob)
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
debug(60,'%s Script finished in %.2f seconds' % (__script__,sys.time()-t) )
Window.WaitCursor(0)
return
########################################
# This lets you can import the script without running it
if __name__ == '__main__':
debug(0, "------------------------------------")
debug(0, '%s %s Script begins with mode=%i debug=%i batch=%s version=%i' % (__script__,__version__,MODE,DEBUG,BATCH,BLENDER_VERSION))
benchmark()