forked from bartvdbraak/blender
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:
parent
2fc5281c83
commit
e6e2087e90
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user