diff options
author | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2008-11-13 00:16:53 +0300 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2008-11-13 00:16:53 +0300 |
commit | bdfe7d89e2f1292644577972c716931b4ce3c6c3 (patch) | |
tree | d00eb50b749cb001e2b08272c91791e66740b05d /release/scripts/animation_bake_constraints.py | |
parent | 78a1c27c4a6abe0ed31ca93ad21910f3df04da56 (diff) | |
parent | 7e4db234cee71ead34ee81a12e27da4bd548eb4b (diff) |
Merge of trunk into blender 2.5:
svn merge https://svn.blender.org/svnroot/bf-blender/trunk/blender -r12987:17416
Issues:
* GHOST/X11 had conflicting changes. Some code was added in 2.5, which was
later added in trunk also, but reverted partially, specifically revision
16683. I have left out this reversion in the 2.5 branch since I think it is
needed there.
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=16683
* Scons had various conflicting changes, I decided to go with trunk version
for everything except priorities and some library renaming.
* In creator.c, there were various fixes and fixes for fixes related to the -w
-W and -p options. In 2.5 -w and -W is not coded yet, and -p is done
differently. Since this is changed so much, and I don't think those fixes
would be needed in 2.5, I've left them out.
* Also in creator.c: there was code for a python bugfix where the screen was not
initialized when running with -P. The code that initializes the screen there
I had to disable, that can't work in 2.5 anymore but left it commented as a
reminder.
Further I had to disable some new function calls. using src/ and python/, as
was done already in this branch, disabled function calls:
* bpath.c: error reporting
* BME_conversions.c: editmesh conversion functions.
* SHD_dynamic: disabled almost completely, there is no python/.
* KX_PythonInit.cpp and Ketsji/ build files: Mathutils is not there, disabled.
* text.c: clipboard copy call.
* object.c: OB_SUPPORT_MATERIAL.
* DerivedMesh.c and subsurf_ccg, stipple_quarttone.
Still to be done:
* Go over files and functions that were moved to a different location but could
still use changes that were done in trunk.
Diffstat (limited to 'release/scripts/animation_bake_constraints.py')
-rw-r--r-- | release/scripts/animation_bake_constraints.py | 792 |
1 files changed, 792 insertions, 0 deletions
diff --git a/release/scripts/animation_bake_constraints.py b/release/scripts/animation_bake_constraints.py new file mode 100644 index 00000000000..58e9e2b1d02 --- /dev/null +++ b/release/scripts/animation_bake_constraints.py @@ -0,0 +1,792 @@ +#!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__ = "Animation Bake Constraints" +__version__ = "0.7" +__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/ Tutorial/Motion Capture <br> + +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 to create a test armature <br> + -- Set RT: up to 100 for more debug messages and status updates <br> +<br> + - 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> + +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 + 0.7: bakes parents and leaves clones selected + +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: + Initialize + If at least one object is selected + For each selected object, + create a cloned 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 + 4. objects without parents, children with unselected parents, select children first. + +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 +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 +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=clone keeps original parent, False = clone's parent is the clone of the original parent (if cloned) +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 +usrObjectNamePrefix= "" +#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 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 + +######################################## +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 +# else skip it, it's already in there +# 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 +######################################## +def scrub(): # scrubs to startframe + staFrame,endFrame,curFrame = getRenderInfo() + + # eye-candy, go from current to start, fwd or back + if not BATCH: + debug(100, "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) + return + +######################################## +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 + +######################################## +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(90,'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): + debug(60,'Resetting ipo curve named %s' %ipo.name) + 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 None + 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 + myob.setName(usrObjectNamePrefix + ob.getName()) + removeConstraintsOb(myob) #my object is a free man + deLinkOb('Ipo',myob) #kids, it's not nice to share. you've been lied to + if ob.getType() != ARMATURE: # baking armatures is based on bones, not object + 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 + +######################################## +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) + if ob.getType() == ARMATURE: +## error('Object baked. Continue with bones?') + bakeBones(ob,clone) #go into the bones and copy from -> to in frame range + #future idea: bakeMesh (net result of Shapekeys, Softbody, Cloth, Fluidsim,...) + return clone + +######################################## +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 + obs= sortObjects(obs) + debug(0,'Baking %i objects' % len(obs)) + + if len(obs) >= 1: # user might have multiple objects selected + i= 0 + clones=[] # my clone army + for ob in obs: + par= ob.getParent() + if not usrParent: + if par in obs: + par= clones[obs.index(par)] + clones.append(bake(ob,par)) + scn.objects.selected = clones + 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(0,'%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" % (__script__,__version__,MODE,DEBUG,BATCH)) + benchmark() |