Updates from Roger Wickes

[#16494] Animation Bake Constraints update
links cloned children to cloned parents, useful for ik target baking. whitespace, capitalization.

[#15032] C3D Import script cleanup/IK
Changed code to put IK constraints on a user-defined layer, separate from Markers. cleaned up module naming convention.
removed questionable sloc. add revision history. forced the TrackTo constraint to use a valid marker, and not make up
one on its own.
This commit is contained in:
Campbell Barton 2008-07-05 11:38:16 +00:00
parent 2fc5281c83
commit e6e2087e90
2 changed files with 133 additions and 135 deletions

@ -9,8 +9,8 @@ Fillename: 'Bake_Constraint.py'
""" """
__author__ = "Roger Wickes (rogerwickes(at)yahoo.com)" __author__ = "Roger Wickes (rogerwickes(at)yahoo.com)"
__script__ = "Bake Constraints" __script__ = "Animation Bake Constraints"
__version__ = "0.6" __version__ = "0.7"
__url__ = ["Communicate problems and errors, http://www.blenderartists.com/forum/private.php?do=newpm to PapaSmurf"] __url__ = ["Communicate problems and errors, http://www.blenderartists.com/forum/private.php?do=newpm to PapaSmurf"]
__email__= ["Roger Wickes, rogerwickes@yahoo.com", "scripts"] __email__= ["Roger Wickes, rogerwickes@yahoo.com", "scripts"]
__bpydoc__ = """\ __bpydoc__ = """\
@ -28,58 +28,62 @@ Developed for use with MoCap data, where a bone is constrained to point at an em
moving through space and time. This records the actual locrot of the armature 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 so that the motion can be edited, reoriented, scaled, and used as NLA Actions
see also wiki Scripts/Manual/ Sandbox/Animation/Bake_Constraints (tbd) see also wiki Scripts/Manual/ Tutorial/Motion Capture <br>
Usage:<br> Usage: <br>
- Select the reference Object(s) you want to bake <br> - Select the reference Object(s) you want to bake <br>
- Set the frame range to bake in the Anim Panel <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 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> -- Set RT:1 to create a test armature <br>
-- Set RT: up to 100 for more debug messages and status updates <br>
<br> <br>
- Run the script <br> - Run the script <br>
- The clone copy of the object is created and it has an IPO curve assigned to it. - 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) - The clone shadows the object by an offset locrot (see usrDelta) <br>
- That Ipo has Location and Rotation curves that make the shadow mimic the movement of the selected object, - That Object has Ipo Location and Rotation curves that make the clone mimic the movement <br>
but without using constraints. Bones move identically in relation to the armature as the reference object 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: Version History:
0.1: bakes Loc Rot for a constrained object 0.1: bakes Loc Rot for a constrained object
0.2: bakes Loc and Rot for the bones within Armature object 0.2: bakes Loc and Rot for the bones within Armature object
0.3: UI for setting options 0.3: UI for setting options
0.3.1 add manual to script library 0.3.1 add manual to script library
0.4: bake multiple objects 0.4: bake multiple objects
0.5: root bone worldspace rotation 0.5: root bone worldspace rotation
0.6: re-integration with BPyArmature 0.6: re-integration with BPyArmature
0.7: bakes parents and leaves clones selected
License, Copyright, and Attribution: License, Copyright, and Attribution:
by Roger WICKES May 2008, released under Blender Artistic Licence to Public Domain by Roger WICKES May 2008, released under Blender Artistic Licence to Public Domain
feel free to add to any Blender Python Scripts Bundle. feel free to add to any Blender Python Scripts Bundle.
Thanks to Jean-Baptiste PERIN, IdeasMan42 (Campbell Barton?), Basil_Fawlty/Cage_drei (Andrew Cruse) Thanks to Jean-Baptiste PERIN, IdeasMan42 (Campbell Barton), Basil_Fawlty/Cage_drei (Andrew Cruse)
much lifted/learned from blender.org/documentation/245PytonDoc and wiki 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 some modules based on c3D_Import.py, PoseLib16.py and IPO/Armature code examples e.g. camera jitter
Pseudocode (planned versions): Pseudocode:
Initialize Initialize
If at least one object is selected If at least one object is selected
For each selected object, For each selected object,
create a shadow object create a cloned object
remove any constraints on the clone remove any constraints on the clone
create or reset an ipo curve named like the object create or reset an ipo curve named like the object
for each frame for each frame
set the clone's locrot key based on the reference object set the clone's locrot key based on the reference object
if it's an armature, if it's an armature,
create an action (which is an Ipo for each bone) create an action (which is an Ipo for each bone)
for each frame of the animation for each frame of the animation
for each bone in the armature for each bone in the armature
set the key set the key
Else you're a smurf Else you're a smurf
Test Conditions and Regressions: Test Conditions and Regressions:
1. (v0.1) Non-armatures (the cube), with ipo curve and constraints at the object level 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 2. armatures, with ipo curve and constraints at the object level
3. armatures, with bones that have ipo curves and constraints 3. armatures, with bones that have ipo curves and constraints
4. objects without parents, children with unselected parents, select children first.
Naming conventions: Naming conventions:
arm = a specific objec type armature arm = a specific objec type armature
bone = bones that make up the skeleton of an armature bone = bones that make up the skeleton of an armature
@ -89,19 +93,6 @@ Naming conventions:
pbone = pose bone, a posed bone in an object pbone = pose bone, a posed bone in an object
tst = testing, self-test routines tst = testing, self-test routines
usr = user-entered or designated stuff 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
""" """
######################################## ########################################
@ -131,8 +122,8 @@ POSE_XFORM= [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT]
# set senstitivity for displaying debug/console messages. 0=none, 100=max # set senstitivity for displaying debug/console messages. 0=none, 100=max
# then call debug(num,string) to conditionally display status/info in console window # 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) MODE=Blender.Get('rt') #execution mode: 0=run normal, 1=make test armature
DEBUG=100 #how much detail on internal processing for big brother to see 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? 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, #there are two coordinate systems, the real, or absolute 3D space,
@ -143,13 +134,11 @@ COORD_REAL = 1
# User Settings - Change these options manually or via GUI (future TODO) # User Settings - Change these options manually or via GUI (future TODO)
usrCoord = COORD_REAL # what the user wants usrCoord = COORD_REAL # what the user wants
usrParent = False # True=keep parent (if exists), False = breakaway (usually with Real) 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 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 # 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 usrDelta = [10,10,0,0,0,0] #order specific - Loc xyz Rot xyz
usrACTION = True # Offset baked Action frames to start at frame 1 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 CURFRAME = 'curframe' #keyword to use when getting the frame number that the scene is presently on
ARMATURE = 'Armature' #en anglais ARMATURE = 'Armature' #en anglais
@ -159,7 +148,7 @@ BONE_SPACES = ['ARMATURESPACE','BONESPACE']
#Ipo curves created are prefixed with a name, like Ipo_ or Bake_ followed by the object/bone name #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 #bakedArmName = "b." #used for both the armature class and object instance
#ipoObjectNamePrefix= "" usrObjectNamePrefix= ""
#ipoBoneNamePrefix = "" #ipoBoneNamePrefix = ""
# for example, if on entry an armature named Man was selected, and the object prefix was "a." # 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 # on exit an armature and an IPO curve named a.Man exists for the object as a whole
@ -177,7 +166,6 @@ scn = Blender.Scene.GetCurrent()
#================= #=================
######################################## ########################################
def debug(num,msg): #use log4j or just console here. def debug(num,msg): #use log4j or just console here.
if DEBUG >= num: if DEBUG >= num:
if BATCH == False: if BATCH == False:
print 'debug: '[:num/10+7]+msg print 'debug: '[:num/10+7]+msg
@ -201,9 +189,26 @@ def getRenderInfo():
debug(90,'Scene is on frame %i and frame range is %i to %i' % (curframe,staframe,endframe)) debug(90,'Scene is on frame %i and frame range is %i to %i' % (curframe,staframe,endframe))
return (staframe,endframe,curframe) 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 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, # while there are bones to add,
# look thru the list of bones we need to add # look thru the list of bones we need to add
# if we have not already added this bone # if we have not already added this bone
@ -215,7 +220,7 @@ def sortBones(xbones): #returns a sorted list of bones that should be added,sort
# else #we need to keep cycling and catch its parent # else #we need to keep cycling and catch its parent
# else it is a root bone # else it is a root bone
# add it # add it
# else skip it, it's prego # else skip it, it's already in there
# endfor # endfor
# endwhile # endwhile
xboneNames=[] xboneNames=[]
@ -240,7 +245,6 @@ def sortBones(xbones): #returns a sorted list of bones that should be added,sort
######################################## ########################################
def dupliArmature(ob): #makes a copy in current scn of the armature used by ob and its bones def dupliArmature(ob): #makes a copy in current scn of the armature used by ob and its bones
ob_mat = ob.matrixWorld ob_mat = ob.matrixWorld
ob_data = ob.getData() ob_data = ob.getData()
debug(49,'Reference object uses %s' % ob_data) debug(49,'Reference object uses %s' % ob_data)
@ -261,8 +265,6 @@ def dupliArmature(ob): #makes a copy in current scn of the armature used by ob a
#when creating a child, we cannot link to a parent if it does not yet exist in our armature #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 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) eboneNames = sortBones(xbones)
i=0 i=0
@ -306,13 +308,13 @@ def dupliArmature(ob): #makes a copy in current scn of the armature used by ob a
print myob.matrix print myob.matrix
return myob return myob
########################################
def scrub(): # scrubs to startframe def scrub(): # scrubs to startframe
staFrame,endFrame,curFrame = getRenderInfo() staFrame,endFrame,curFrame = getRenderInfo()
# eye-candy, go from current to start, fwd or back # eye-candy, go from current to start, fwd or back
if not BATCH: if not BATCH:
print "Positioning to start..." debug(100, "Positioning to start...")
frameinc=(staFrame-curFrame)/10 frameinc=(staFrame-curFrame)/10
if abs(frameinc) >= 1: if abs(frameinc) >= 1:
for i in range(10): for i in range(10):
@ -320,6 +322,7 @@ def scrub(): # scrubs to startframe
Blender.Set(CURFRAME,curFrame) # computes the constrained location of the 'real' objects Blender.Set(CURFRAME,curFrame) # computes the constrained location of the 'real' objects
Blender.Redraw() Blender.Redraw()
Blender.Set(CURFRAME, staFrame) Blender.Set(CURFRAME, staFrame)
return
######################################## ########################################
def bakeBones(ref_ob,arm_ob): #copy pose from ref_ob to arm_ob def bakeBones(ref_ob,arm_ob): #copy pose from ref_ob to arm_ob
@ -335,7 +338,6 @@ def bakeBones(ref_ob,arm_ob): #copy pose from ref_ob to arm_ob
arm_channels = act.getAllChannelIpos() arm_channels = act.getAllChannelIpos()
pose= arm_ob.getPose() pose= arm_ob.getPose()
pbones= pose.bones.values() #we want the bones themselves, not the dictionary lookup pbones= pose.bones.values() #we want the bones themselves, not the dictionary lookup
print arm_channels.keys()
for pbone in pbones: for pbone in pbones:
debug (100,'Channel listing for %s: %s' % (pbone.name,arm_channels[pbone.name] )) debug (100,'Channel listing for %s: %s' % (pbone.name,arm_channels[pbone.name] ))
ipo=arm_channels[pbone.name] ipo=arm_channels[pbone.name]
@ -344,8 +346,7 @@ def bakeBones(ref_ob,arm_ob): #copy pose from ref_ob to arm_ob
return return
######################################## ########################################
def getOrCreateCurve(ipo, curvename): def getOrCreateCurve(ipo, curvename):
""" """
Retrieve or create a Blender Ipo Curve named C{curvename} in the C{ipo} Ipo 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, Either an ipo curve named C{curvename} exists before the call then this curve is returned,
@ -362,18 +363,17 @@ def getOrCreateCurve(ipo, curvename):
return mycurve return mycurve
######################################## ########################################
def eraseCurve(ipo,numCurves): def eraseCurve(ipo,numCurves):
debug(80,'Erasing %i curves for %' % (numCurves,ipo.GetName())) debug(90,'Erasing %i curves for %' % (numCurves,ipo.GetName()))
for i in range(numCurves): for i in range(numCurves):
nbBezPoints = ipo.getNBezPoints(i) nbBezPoints= ipo.getNBezPoints(i)
for j in range(nbBezPoints): for j in range(nbBezPoints):
ipo.delBezPoint(i) ipo.delBezPoint(i)
return return
######################################## ########################################
def resetIPO(ipo): def resetIPO(ipo):
ipoName=ipoObjectNamePrefix + obName debug(60,'Resetting ipo curve named %s' %ipo.name)
debug(40,'Resetting ipo curve named %s' %ipoName)
numCurves = ipo.getNcurves() #like LocX, LocY, etc numCurves = ipo.getNcurves() #like LocX, LocY, etc
if numCurves > 0: if numCurves > 0:
eraseCurve(ipo, numCurves) #erase data if one exists eraseCurve(ipo, numCurves) #erase data if one exists
@ -399,11 +399,11 @@ def resetIPOs(ob): #resets all IPO curvess assocated with an object and its bone
return return
######################################## ########################################
def parse(string,delim): def parse(string,delim):
index = string.find(delim) # -1 if not found, else pointer to delim index = string.find(delim) # -1 if not found, else pointer to delim
if index+1: return string[:index] if index+1: return string[:index]
return string return string
######################################## ########################################
def newIpo(ipoName): #add a new Ipo object to the Blender scene def newIpo(ipoName): #add a new Ipo object to the Blender scene
ipo=Blender.Ipo.New('Object',ipoName) ipo=Blender.Ipo.New('Object',ipoName)
@ -438,19 +438,16 @@ def makeUpaName(type,name): #i know this exists in Blender somewhere...
name=ipoName name=ipoName
else: else:
debug (0,'FATAL ERROR: I dont know how to make up a new %s name based on %s' % (type,ob)) debug (0,'FATAL ERROR: I dont know how to make up a new %s name based on %s' % (type,ob))
return return None
return name return name
######################################## ########################################
def createIpo(ob): #create an Ipo and curves and link them to this object def createIpo(ob): #create an Ipo and curves and link them to this object
#first, we have to create a unique name #first, we have to create a unique name
#try first with just the name of the object to keep things simple. #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 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) debug(20,'Ipo and LocRot curves called %s' % ipoName)
ipo=newIpo(ipoName) ipo=newIpo(ipoName)
ob.setIpo(ipo) #link them ob.setIpo(ipo) #link them
return ipo return ipo
@ -465,7 +462,7 @@ def getLocLocal(ob):
ob.RotZ*R2D ob.RotZ*R2D
] ]
return key return key
######################################## ########################################
def getLocReal(ob): def getLocReal(ob):
obMatrix = ob.matrixWorld #Thank you IdeasMan42 obMatrix = ob.matrixWorld #Thank you IdeasMan42
@ -505,7 +502,7 @@ def getCurves(ipo):
ipo[Ipo.OB_ROTZ] ipo[Ipo.OB_ROTZ]
] ]
return ipos return ipos
######################################## ########################################
def addPoint(time,keyLocRot,ipos): def addPoint(time,keyLocRot,ipos):
if BLENDER_VERSION < 245: if BLENDER_VERSION < 245:
@ -579,7 +576,7 @@ def removeConstraints(ob):
debug(90,'removed %s => %s' % (ob.name, const)) debug(90,'removed %s => %s' % (ob.name, const))
ob.constraints.remove(const) ob.constraints.remove(const)
return return
######################################## ########################################
def removeConstraintsOb(ob): # from object or armature def removeConstraintsOb(ob): # from object or armature
debug(40,'Removing constraints from '+ob.getName()) debug(40,'Removing constraints from '+ob.getName())
@ -605,37 +602,27 @@ def bakeObject(ob): #bakes the core object locrot and assigns the Ipo to a Clone
if ob != None: if ob != None:
# Clone the object - duplicate it, clean the clone, and create an ipo curve for the clone # Clone the object - duplicate it, clean the clone, and create an ipo curve for the clone
myob = duplicateLinked(ob) #clone it myob = duplicateLinked(ob) #clone it
myob.name= usrObjectNamePrefix + ob.getName()
removeConstraintsOb(myob) #my object is a free man removeConstraintsOb(myob) #my object is a free man
deLinkOb('Ipo',myob) #kids, it's not nice to share. you've been lied to deLinkOb('Ipo',myob) #kids, it's not nice to share. you've been lied to
if usrBAKEobjIPO: if ob.getType() != ARMATURE: # baking armatures is based on bones, not object
myipo = createIpo(myob) #create own IPO and curves for the clone 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 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 return myob
######################################## ########################################
def bake(ob): #bakes an object of any type def bake(ob,par): #bakes an object of any type, linking it to parent
debug(0,'Baking %s object %s' % (ob.getType(), ob))
debug(30,'Baking %s object %s' % (ob.getType(), ob)) clone = bakeObject(ob) #creates and bakes the object motion
if par!= None:
myob = bakeObject(ob) #creates and bakes the object motion par.makeParent([clone])
debug(20,"assigned object to parent %s" % par)
if ob.getType() == ARMATURE: if ob.getType() == ARMATURE:
# error('Object baked. Continue with bones?') ## error('Object baked. Continue with bones?')
bakeBones(ob,myob) #go into the bones and copy from -> to in frame range bakeBones(ob,clone) #go into the bones and copy from -> to in frame range
#future idea: bakeMesh (net result of Shapekeys, Softbody, Cloth, Fluidsim,...) #future idea: bakeMesh (net result of Shapekeys, Softbody, Cloth, Fluidsim,...)
return clone
return
######################################## ########################################
def tstCreateArm(): #create a test armature in scene def tstCreateArm(): #create a test armature in scene
# rip-off from http://www.blender.org/documentation/245PythonDoc/Pose-module.html - thank you! # rip-off from http://www.blender.org/documentation/245PythonDoc/Pose-module.html - thank you!
@ -690,8 +677,8 @@ def tstCreateArm(): #create a test armature in scene
frame = 1 frame = 1
for pbone in pbones: # set bones to no rotation for pbone in pbones: # set bones to no rotation
pbone.quat[:] = 1.000,0.000,0.000,0.0000 pbone.quat[:] = 1.000,0.000,0.000,0.0000
pbone.insertKey(arm_ob, frame, Object.Pose.ROT) pbone.insertKey(arm_ob, frame, Object.Pose.ROT)
# Set a different rotation at frame 25 # Set a different rotation at frame 25
pbones[0].quat[:] = 1.000,0.1000,0.2000,0.20000 pbones[0].quat[:] = 1.000,0.1000,0.2000,0.20000
@ -747,7 +734,7 @@ def tstMoveOb(ob): # makes a simple LocRot animation of object in the scene
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])) 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 frame += frameDelta
Blender.Set('curframe',curframe) # reset back to where we started Blender.Set(CURFRAME,curframe) # reset back to where we started
return return
#================= #=================
# Program Template # Program Template
@ -755,25 +742,31 @@ def tstMoveOb(ob): # makes a simple LocRot animation of object in the scene
######################################## ########################################
def main(): def main():
# return code set via rt button in Blender Buttons Scene Context Anim panel # return code set via rt button in Blender Buttons Scene Context Anim panel
if MODE == 1: #create test armature #1 if MODE == 1: #create test armature #1
ob = tstCreateArm() # make test arm and select it ob = tstCreateArm() # make test arm and select it
tstMoveOb(ob) tstMoveOb(ob)
scn.objects.selected = [ob] scn.objects.selected = [ob]
obs = Blender.Object.GetSelected() #scn.objects.selected obs= Blender.Object.GetSelected() #scn.objects.selected
debug(20,'Baking %i objects' % len(obs)) obs= sortObjects(obs)
debug(0,'Baking %i objects' % len(obs))
if len(obs) >= 1: # user might have multiple objects selected if len(obs) >= 1: # user might have multiple objects selected
i= 0
clones=[] # my clone army
for ob in obs: for ob in obs:
bake(ob) 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: else:
error('Please select at least one object') error('Please select at least one object')
return return
######################################## ########################################
def benchmark(): # This lets you benchmark (time) the script's running duration def benchmark(): # This lets you benchmark (time) the script's running duration
Window.WaitCursor(1) Window.WaitCursor(1)
t = sys.time() t = sys.time()
debug(60,'%s began at %.0f' %(__script__,sys.time())) debug(60,'%s began at %.0f' %(__script__,sys.time()))
@ -787,7 +780,7 @@ def benchmark(): # This lets you benchmark (time) the script's running duration
if in_editmode: Window.EditMode(1) if in_editmode: Window.EditMode(1)
# Timing the script is a good way to be aware on any speed hits when scripting # 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) ) debug(0,'%s Script finished in %.2f seconds' % (__script__,sys.time()-t) )
Window.WaitCursor(0) Window.WaitCursor(0)
return return
@ -795,6 +788,5 @@ def benchmark(): # This lets you benchmark (time) the script's running duration
# This lets you can import the script without running it # This lets you can import the script without running it
if __name__ == '__main__': if __name__ == '__main__':
debug(0, "------------------------------------") debug(0, "------------------------------------")
debug(0, '%s %s Script begins with mode=%i debug=%i batch=%s version=%i' % (__script__,__version__,MODE,DEBUG,BATCH,BLENDER_VERSION)) debug(0, "%s %s Script begins with mode=%i debug=%i batch=%s" % (__script__,__version__,MODE,DEBUG,BATCH))
benchmark() benchmark()

@ -8,7 +8,7 @@ Tooltip: 'Import a C3D Motion Capture file'
""" """
__script__ = "C3D Motion Capture file import" __script__ = "C3D Motion Capture file import"
__author__ = " Jean-Baptiste PERIN, Roger D. Wickes (rogerwickes@yahoo.com)" __author__ = " Jean-Baptiste PERIN, Roger D. Wickes (rogerwickes@yahoo.com)"
__version__ = "0.8" __version__ = "0.9"
__url__ = ["Communicate problems and errors, BlenderArtists.org, Python forum"] __url__ = ["Communicate problems and errors, BlenderArtists.org, Python forum"]
__email__= ["rogerwickes@yahoo.com", "c3d script"] __email__= ["rogerwickes@yahoo.com", "c3d script"]
__bpydoc__ = """\ __bpydoc__ = """\
@ -16,9 +16,9 @@ c3d_import.py v0.8
Script loading Graphics Lab Motion Capture file, Script loading Graphics Lab Motion Capture file,
Usage:<br> Usage:<br>
- Run the script <br> - Run the script <br>
- Choose the file to open<br> - Choose the file to open<br>
- Press Import C3D button<br> - Press Import C3D button<br>
Version History: Version History:
0.4: PERIN Released under Blender Artistic Licence 0.4: PERIN Released under Blender Artistic Licence
@ -26,6 +26,7 @@ Version History:
0.6: WICKES creates armature for each subject 0.6: WICKES creates armature for each subject
0.7: WICKES constrains armature to follow the empties (markers). Verified for shake hands s 0.7: WICKES constrains armature to follow the empties (markers). Verified for shake hands s
0.8: WICKES resolved DEC support issue 0.8: WICKES resolved DEC support issue
0.9: BARTON removed scene name change, whitespace edits. WICKES added IK layers
""" """
#---------------------------------------------- #----------------------------------------------
@ -69,12 +70,12 @@ XYZ_LIMIT= 10000 #max value for coordinates if in integer format
# selecting only layer 2 shows only the armature moving, 12 shows only the empties # selecting only layer 2 shows only the armature moving, 12 shows only the empties
LAYERS_ARMOB= [1,2] LAYERS_ARMOB= [1,2]
LAYERS_MARKER=[1,12] LAYERS_MARKER=[1,12]
LAYERS_IK=[1,11]
IK_PREFIX="ik_" # prefix in empty name: ik_prefix+subject prefix+bone name
CLEAN=True # Should program ignore markers at (0,0,0) and beyond the outer limits? CLEAN=True # Should program ignore markers at (0,0,0) and beyond the outer limits?
scn = Blender.Scene.GetCurrent() scn = Blender.Scene.GetCurrent()
# Why on earth would you rename a scene when importing data??? - Campbell
# scn.name="MoCap" #may want this enterable or derived based on motion being analyzed
#TODO: ultimately, a library of motions to append from means you need good naming of things
BCS=Blender.Constraint.Settings # shorthand dictionary - define with brace, reference with bracket BCS=Blender.Constraint.Settings # shorthand dictionary - define with brace, reference with bracket
trackto={"+x":BCS.TRACKX, "+y":BCS.TRACKY, "+z":BCS.TRACKZ, "-x":BCS.TRACKNEGX, "-y":BCS.TRACKNEGY, "-z":BCS.TRACKNEGZ} trackto={"+x":BCS.TRACKX, "+y":BCS.TRACKY, "+z":BCS.TRACKZ, "-x":BCS.TRACKNEGX, "-y":BCS.TRACKNEGY, "-z":BCS.TRACKNEGZ}
@ -169,22 +170,21 @@ def getEmpty(name):
# in : objname : le nom de l'empty recherche # in : objname : le nom de l'empty recherche
# out : myobj : l'empty cree ou retrouve # out : myobj : l'empty cree ou retrouve
######### #########
def GetOrCreateEmpty(objname): def getOrCreateEmpty(objname):
myobj= getEmpty(objname) myobj= getEmpty(objname)
if myobj==None: if myobj==None:
myobj = scn.objects.new("Empty",objname) myobj = scn.objects.new("Empty",objname)
myobj.layers= LAYERS_MARKER
debug(50,'Marker/Empty created %s' % myobj) debug(50,'Marker/Empty created %s' % myobj)
return myobj return myobj
def GetOrCreateCurve(ipo, curvename): def getOrCreateCurve(ipo, curvename):
""" """
Retrieve or create a Blender Ipo Curve named C{curvename} in the C{ipo} Ipo Retrieve or create a Blender Ipo Curve named C{curvename} in the C{ipo} Ipo
>>> import mylib >>> import mylib
>>> lIpo = GetOrCreateIPO("Une IPO") >>> lIpo = GetOrCreateIPO("Une IPO")
>>> laCurve = GetOrCreateCurve(lIpo, "RotX") >>> laCurve = getOrCreateCurve(lIpo, "RotX")
Either an ipo curve named C{curvename} exists before the call then this curve is returned, 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 Or such a curve doesn't exist before the call .. then it is created into the c{ipo} Ipo and returned
@ -338,7 +338,8 @@ def makeNodes(prefix, markerList, empties, marker_set): #make sure the file has
elif usrOption==1: #add these markers as static empties, and user will automate them later elif usrOption==1: #add these markers as static empties, and user will automate them later
#and the bones will be keyed to them, so it will all be good. #and the bones will be keyed to them, so it will all be good.
#file may have just mis-named the empty, or the location can be derived based on other markers #file may have just mis-named the empty, or the location can be derived based on other markers
em= GetOrCreateEmpty(err[2]) em= getOrCreateEmpty(err[2])
em.layers= LAYERS_MARKER
else: abort() #abend else: abort() #abend
if DEBUG==100: status("Nodes Updated") if DEBUG==100: status("Nodes Updated")
return nodes #nodes may be updated return nodes #nodes may be updated
@ -411,8 +412,9 @@ def makeConstIK(prefix,pbone,const):
#Blender 246 only supports one IK Solver per bone, but we might want many, #Blender 246 only supports one IK Solver per bone, but we might want many,
# so we need to create a reference empty named after the bone # so we need to create a reference empty named after the bone
# that floats between the markers, so the bone can point to it as a singularity # that floats between the markers, so the bone can point to it as a singularity
myob= GetOrCreateEmpty(prefix+pbone.name) myob= getOrCreateEmpty(IK_PREFIX+prefix+pbone.name)
# note that this empty gets all the IK constraints added on myob.layers= LAYERS_IK
# note that this empty gets all the IK constraints added on as location constraints
myconst= myob.constraints.append(Constraint.Type.COPYLOC) myconst= myob.constraints.append(Constraint.Type.COPYLOC)
myconst.name=const[0]+"-"+const[1] myconst.name=const[0]+"-"+const[1]
myconst[Constraint.Settings.TARGET]= Blender.Object.Get(const[1]) myconst[Constraint.Settings.TARGET]= Blender.Object.Get(const[1])
@ -438,15 +440,18 @@ def makeConstTT(pbone,const):
myconst= pbone.constraints.append(Constraint.Type.TRACKTO) myconst= pbone.constraints.append(Constraint.Type.TRACKTO)
myconst.name=const[0]+"-"+const[1] myconst.name=const[0]+"-"+const[1]
debug(70,"%s %s" % (myconst,const[3])) debug(70,"%s %s" % (myconst,const[3]))
myob= GetOrCreateEmpty(const[1]) myob= getEmpty(const[1])
myconst[BCS.TARGET]= myob if myob!= None:
myconst.influence = const[2] myconst[BCS.TARGET]= myob
#const[3] is the Track and the thrird char is the Up indicator myconst.influence = const[2]
myconst[BCS.TRACK]= trackto[const[3][0:2].lower()] #const[3] is the Track and the thrird char is the Up indicator
myconst[BCS.UP]=trackup[const[3][2].lower()]#up direction myconst[BCS.TRACK]= trackto[const[3][0:2].lower()]
myconst[BCS.OWNERSPACE]= BCS.SPACE_LOCAL myconst[BCS.UP]=trackup[const[3][2].lower()]#up direction
myconst[BCS.TARGETSPACE]= [BCS.SPACE_LOCAL] myconst[BCS.OWNERSPACE]= BCS.SPACE_LOCAL
if const[3][1]==const[3][2]: debug(0,"WARNING: Track To axis and up axis should not be the same. Constraint is INACTIVE") myconst[BCS.TARGETSPACE]= [BCS.SPACE_LOCAL]
if const[3][1]==const[3][2]: debug(0,"WARNING: Track To axis and up axis should not be the same. Constraint is INACTIVE")
else: #marker not found. could be missing from this file, or an error in node spec
error("TrackTo Constraint for %s |specifies unknown marker %s" % (pbone.name,const[1]))
return return
def makePoses(prefix,arm_ob,nodes): # pose this armature object based on node requirements def makePoses(prefix,arm_ob,nodes): # pose this armature object based on node requirements
@ -543,15 +548,16 @@ def makeCloud(Nmarkers,markerList,StartFrame,EndFrame,Markers):
for i in range(Nmarkers): for i in range(Nmarkers):
debug(100,"%i marker %s"%(i, markerList[i])) debug(100,"%i marker %s"%(i, markerList[i]))
emptyname = markerList[i] # rdw: to use meaningful names from Points parameter emptyname = markerList[i] # rdw: to use meaningful names from Points parameter
em= GetOrCreateEmpty(emptyname) #in this scene em= getOrCreateEmpty(emptyname) #in this scene
em.layers= LAYERS_MARKER
#make a list of the actual empty #make a list of the actual empty
empties.append(em) empties.append(em)
#assign it an ipo with the loc xyz curves #assign it an ipo with the loc xyz curves
lipo = Ipo.New("Object",em.name) lipo = Ipo.New("Object",em.name)
ipos.append(lipo) ipos.append(lipo)
curvesX.append(GetOrCreateCurve(ipos[i],'LocX')) curvesX.append(getOrCreateCurve(ipos[i],'LocX'))
curvesY.append(GetOrCreateCurve(ipos[i],'LocY')) curvesY.append(getOrCreateCurve(ipos[i],'LocY'))
curvesZ.append(GetOrCreateCurve(ipos[i],'LocZ')) curvesZ.append(getOrCreateCurve(ipos[i],'LocZ'))
empties[i].setIpo(ipos[i]) empties[i].setIpo(ipos[i])
debug(30,"Cloud of %i empties created." % len(empties)) debug(30,"Cloud of %i empties created." % len(empties))
NvideoFrames= EndFrame-StartFrame+1 NvideoFrames= EndFrame-StartFrame+1