Page MenuHome

PoseLib16.py

PoseLib16.py

#!BPY
""" Registration info for Blender menus:
Name: '_Blender Pose Handler'
Blender: 242
Group: 'Animation'
Tooltip: 'Specialized saving and loading of Blender Library-style poses'
"""
__author__ = ["Basil_Fawlty/Cage_drei (Andrew Cruse)"]
__url__ = ("http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/Animation/PoseLib16","http://www.kuroyumes-developmentzone.com/~Cage/PoseLib16.zip")
__version__ = "1.0 (Sept 22,2006)"
__email__ = "Cagedrei@aol.com"
__bpydoc__ = """\
This script provides several tools for pose and armature handling. It uses Blender Library type poses
and provides animation support using batch import and export of those poses. It also provides various
tools for pose tweaking and development and can import and export Poser poses.
This script processes bone quaternion rotations four different ways to produce four varieties of poses.
1) Blender Library type. This method seems to embed bone roll data in the pose, making it difficult
to use the pose with armatures other than the source armature.
Save:
quat without processing
Load:
quat without processing
Note: No conversion processing is required for default poses.
2) Restmatrix type (exported in bvh data, also from PoserPython). Essentially lacks embedded bone roll
data in the pose, so the pose can be loaded onto armatures with different bone roll setups.
Save:
quat = (rest_matrix_invert * quat_as_matrix * rest_matrix).toQuat()
Load:
quat = (rest_matrix * quat_as_matrix * rest_matrix_invert).toQuat()
Note: Load restmatrix type poses using the 'Restmatrix pose ('No Roll')' option.
3) Posematrix type. This type uses the poseMatrix to export IK and constraints. This type also embeds
bone roll data.
Save:
quat = (posematrix * parent_posematrix).toQuat()
Load:
quat = (quat_as_matrix * (rest_matrix * parent_rest_matrix_invert).invert()).toQuat()
Note: Load posematrix type poses using the 'Posematrix pose ('IK')' option.
4) Combination posematrix and restmatrix type. Combined correctly, types 2) and 3) can render a pose which
both contains IK and constraint data, but lacks embedded bone roll. This is not simply a matter of encoding
the data both ways on export and import. That works - in the order 3)-2) for export and 2)-3) for import - but
the bone roll information remains embedded. To export the benefits of both, they are processed as they would be
if type 3) were saved, then loaded, then saved again as type 2).
Save:
quat = ((posematrix * parent_posematrix) * (rest_matrix * parent_restmatrix_invert).invert()).toQuat()
This is a combination of the save and load for type three. Then it is passed to the save process for type two.
quat = (rest_matrix_invert * quat_as_matrix * rest_matrix).toQuat()
Load:
quat = (rest_matrix * quat_as_matrix * rest_matrix_invert).toQuat()
Note: Load combination type poses using the 'Restmatrix pose ('No Roll')' option.
That's kind of a shorthand version of the handling in this script. Type 2 poses are based on the work of Campbell Barton.
Type three poses are based on scripts by der_ton and others.
- Save fxn, load fxn, and pose formatting are adapted from Blender Library<br>
- PoseMatrix pose handling adapted from der_ton's md5 I/O scripts<br>
- "No roll"/restmatrix pose handling adapted from Campbell Barton's bvh_import.py for 2.42<br>
- Radians conversion borrowed from xsi export, by "Elira"<br>
- GUI colorbox from discombobulator.py<br>
This script is designed to work with my poser2blender.py for PoserPython
Some elements of this script are still in development:<br>
- Bone layer handling will be added once it is added to the BPy API.<br>
- align_chains and boneroll_correction both need tweaking.<br>
- Poses exported with save_poser are occasionally incorrect. Blender's quaternion to euler conversions don't
seem to return the correct results for this purpose, and no wholly correct conversion method has yet been found.
"""
# --------------------------------------------------------------------------
# Blender Pose Handler V1.0
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
import Blender
from Blender import *
from Blender.Mathutils import *
from Blender.Registry import *
from Blender.Draw import *
from Blender.BGL import *
import math, os, re
##-------Save Functions----------------------------------------------------
"""
object = Blender.Scene.GetCurrent().getActiveObject()
if not object or object.getType() != 'Mesh':
"""
def goSave (save_name):
global EmpPose, PoserSave
#print save_name
if save_name != "":
poseText = ""
if EmpPose == 1: poseText = Export_Empties() #Export a pose from bvh import empties
elif EmpPose == 0:
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
pose = ob.getPose()
if PoserSave == 0: #save a Blender Library type pose
for bone in pose.bones.values():
savePart = save_part(bone) #check screening with partial groups and bones list
if savePart == 1:
quat = bone.quat
quat = save_convert(ob, pose, bone, quat, 0) #posematrix and restmatrix type conversions
poseText += "%s," %(bone.name)
if No_Trans == 1: poseText += "0.0,0.0,0.0," #Don't save translations
elif No_Trans == 0: poseText += "%f,%f,%f," %(bone.loc[0], bone.loc[1], bone.loc[2]) #Save trans
poseText += "%f,%f,%f,%f," %(quat[0], quat[1], quat[2], quat[3])
if No_Scale == 1: poseText += "1.0,1.0,1.0\n" #Don't save scaling
elif No_Scale == 0: poseText += "%f,%f,%f\n" %(bone.size[0], bone.size[1], bone.size[2]) #Save scaling
elif PoserSave == 1: poseText = save_poser(ob, pose)#save a Poser pose
if poseText != "": #Don't proceed if there's nothing to write
if PoserSave == 0:
if os.path.splitext(save_name)[1] == "": save_name += ".blend"
if os.path.splitext(save_name)[1] != ".blend" and os.path.splitext(save_name) != ".bPose":
save_name = sys.makename(save_name, ext = ".blend", strip = 0)
elif PoserSave == 1:
compress = PupMenu("Use file compression? %t|Yes %x1|No %x2")
if compress == 2: save_name = os.path.splitext(save_name)[0] + ".pz2"
else: save_name = os.path.splitext(save_name)[0] + ".p2z"
warning = 0
if sys.exists(save_name) == 1:
warning = PupMenu("Overwrite file %s? %%t|Yes %%x1|No %%x2" %(sys.basename(save_name))) #Don't overwrite
if sys.exists(save_name) != 1 or warning != 2:
if compress == 2:
text = open(save_name,'w')
elif compress == 1: #save compressed Poser pose
import gzip
text = gzip.GzipFile(save_name,'wb')
text.write(poseText)
text.close()
Blender.Redraw()
#------check for save part screening (partial groups and bones list)--------------
def save_part(bone):
global partVar, sideVar
savePart = 0
if bonesSave == 1 and bone.name in boxBones: savePart = 1 #Save only bones listbox selections
if bonesSave == 2 and bone.name not in boxBones: savePart = 1 #Exclude bones listbox selections from save
if partVar == "" and sideVar == "" and bonesSave == 0: savePart = 1 #Partial pose saving
if partVar != "":
if sideVar != "": #Symmetrical save
partVar2 = partVar.split("@%#%@")
for group in range(0,len(partVar2)):
if bone.name in groupsDict:
if groupsDict[bone.name][:-1] == partVar2[group]:
savePart = 0
if (sideVar == "right" or sideVar == "both") and (pref == 1 and bone.name[:lenR] == right) or\
(suff == 1 and bone.name[-lenR:] == right):
savePart = 1
if (sideVar == "left" or sideVar == "both") and (pref == 1 and bone.name[:lenL] == left) or\
(suff == 1 and bone.name[-lenL:] == left):
savePart = 1
partVar2 = partVar.split("@%#%@") #This allows multiple group handling
for group in range(0,len(partVar2)):
if bone.name in groupsDict:
if groupsDict[bone.name][:-1] == partVar2[group]: savePart = 1
return savePart
#--------run pose type conversions (restmatrix/posematrix)--------------------------------
def save_convert(ob, pose, bone, quat):
global IKPose, NoRollPose
if IKPose == 1: quat = IK_save(bone, ob, pose) #Save pose from bone.poseMatrix()
if IKPose == 1 and NoRollPose == 1: #This enables posematrix poses to have benefits of
bone1 = pose.bones[bone.name]#restmatrix poses (bone roll becomes irrelevant)
quat = IK_load(bone1, quat, ob, pose)
if NoRollPose == 1: quat = roll_correction(bone, quat) #Save pose relative to armature rest matrix
return quat
#-------General save options---------------------------
def export_options():
global EmpPose, IKPose, NoRollPose, OptPose, saveBatch, LibSave, LibPath, LibTog, LibVar, partVar, sideVar,\
NoRollTog, IKTog
expo = PupMenu("Save pose options: %t|Save pose from .bvh empties - Load with 'No Roll' option. %x1\
|Save pose with IK/constraints (poseMatrix) - Load with 'IK' option. %x2\
|Save pose without embedded bone roll (restmatrix) - Load with 'No Roll' %x3\
|Save combination IK and 'no roll' - Load with 'No Roll' option %x4\
|Save animation as batch of pose files %x5\
|Save poses to current load list folder (load menu browser) %x6\
|Deactivate all options %x7|Cancel (Keep current options)%x8")
if expo == 1: #save pose from empties
EmpPose = 1 - EmpPose
IKPose = 0
NoRollPose = 0
elif expo == 2: #save posematrix
IKPose = 1 - IKPose
NoRollPose = 0
EmpPose = 0
elif expo == 3: #save restmatrix
NoRollPose = 1 - NoRollPose
IKPose = 0
EmpPose = 0
elif expo == 4: #save combination posematrix and restmatrix
NoRollPose = IKPose = 1 - NoRollPose
EmpPose = 0
elif expo == 5: #batch export for animation
saveBatch = 1 - saveBatch
elif expo == 6: #save to menu browse folder
if LibPath != "":
LibSave = 1 - LibSave
if LibPath == "":
if LibTog == 0: sub = PupMenu("List load browsing not enabled %t|Enable list browsing %x1|Cancel %x2")
if LibTog == 1: sub = PupMenu("List load folder not specified %t|Select list load folder %x1|Cancel %x2")
if sub == 1:
LibVar = LibTog = LibSave = OptPose = 1
Window.FileSelector(list_browser, 'Select pose folder:')
if LibSave == EmpPose == IKPose == NoRollPose == saveBatch == 0: OptPose = 0
elif expo == 7: EmpPose = IKPose = NoRollPose = saveBatch = OptPose = 0 #deactivate all
if EmpPose == IKPose == NoRollPose == saveBatch == LibSave == 0: #cancel
OptPose = 0
NoRollTog = NoRollPose ##set the toggle display
IKTog = IKPose
#-----save posematrix (IK) conversion----------------------------
#Based (primarily) on der_ton's blender2md5export.py
def IK_save(bone, armature, pose): #Saves a poseMatrix pose - enabling saving of IK and constraints
MATRIX_IDENTITY_4x4 = Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
AData = armature.getData()
rest_bone = AData.bones[bone.name]
pose_bone_mat = Matrix(bone.poseMatrix)
if rest_bone.parent:
parentposemat = Matrix(pose.bones[rest_bone.parent.name].poseMatrix)
else: parentposemat = MATRIX_IDENTITY_4x4
parentposemat.invert()
finalMatrix = pose_bone_mat * parentposemat
return finalMatrix.toQuat()
#-------save restmatrix (no roll) conversion-------------------------------
#Based on Campbell Barton's bvh_import.py for Blender 2.42
def roll_correction(bone, quat): #This is the inverse of the Poser Convert function, adapted for save
#rx,ry,rz = quat.toEuler().unique() #Saves a pose which will load on another armature, without bone roll problems.
bone_rotation_matrix = quat.toMatrix()#go directly to matrix, skipping euler conversion....
arm = Object.GetSelected()[0]
AData = arm.getData()
bone1 = AData.bones[bone.name]
bone_rest_matrix = bone1.matrix['ARMATURESPACE'].rotationPart()
bone_rest_matrix_inv= Matrix(bone_rest_matrix)
bone_rest_matrix_inv.invert()
#bone_rotation_matrix= Euler(rx,ry,rz).toMatrix()
finalMatrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix #Switch the order of multiplication
return finalMatrix.toQuat()
#--------export empties---------------------------------
def Export_Empties(): #Export a pose from .bvh import empties
poseText = ""
if EmpPose == 1:
objs = Blender.Scene.GetCurrent().getChildren()
for ob in objs:
if ob.getType() == "Empty":
if ob.name[0] == "_": ob.name = ob.name[1:len(ob.name)]
if ob.name[-4:] != "_end" and ob.name[0] != "_" and ob.name[-4:] != ".001": #omit junk empties
poseText += ob.name + ","
poseText += "0.0,0.0,0.0," #translation is ignored
oQuat = Euler(r2d(ob.rot[0]),r2d(ob.rot[1]),r2d(ob.rot[2])).unique().toQuat()#radians to degrees
poseText += "%f,%f,%f,%f," %(oQuat[0], oQuat[1], oQuat[2], oQuat[3])
poseText += "1.0,1.0,1.0\n" #Ignore scaling, too.
return poseText
# ---------------------------------------------------------------------------
# r2d - radians to degrees (from .xsi export, by "Elira")
# ---------------------------------------------------------------------------
def r2d(r):
return round(r*180.0/math.pi,4) #Approx. 57.295828 before rounding
#----save partial groups menu-------------------------------------
def partSave(): #Combination toggle/popup for partial save specification.
global partVar, sideVar, TrimPose, maxVal, minVal, bonesSave, boxBones
groupcount = [] #Dict for list of parts visible to user
groupsides = [] #Dict for internal version of parts (includes symmetry flag)
puplist = "" #The menu list
partvar = ""
sideVar = ""
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
if groupsDict == {}: groups() #Find body part groups for partial save
if flipDict == {}: flip()
for group in groupsDict.values(): #Fill the menu list from the groupsDict
if groupsides.count(group) < 1:
groupsides.append(group)
groupcount.append(group[:-1]) #Make the cleaned-up version visible
groupcount.sort()
groupsides.sort()
noGroups = "No groups found. Try changing selection parameters."
if groupcount == []: groupcount.append(noGroups)
for i in range(0, len(groupcount)):
puplist += "%s %%x%i|" %(groupcount[i], i + 1)
if i == len(groupcount)-1:
puplist += "Alter automatic group selection parameters %%x%i|" %(i + 2)
puplist += "Save only bones listbox selections in pose %%x%i|" %(i + 3)
puplist += "Exclude bones listbox selections from saved pose %%x%i|" %(i + 4)
puplist += "Deactivate all groups %%x%i|" %(i + 5)
puplist += "Cancel (Keep current options) %%x%i" %(i + 6)
partList = PupMenu("Select partial pose group or bones list screening: %t|" + puplist)
for part in range(1, len(groupcount) + 1):
if partList == part and groupcount[0] != noGroups:
partVar += groupcount[part - 1]
partVar += "@%#%@" #Pick an unlikely sequence of symbols to split multiple entries for multiple group selections
if groupsides[part - 1][-1:] == "!": #Check notation to see if it's centered
sideVar = symmetrical_save() #Symmetry handling
if sideVar == "": partVar = "" #Cancelled during symmetry selection
#break
if partList == len(groupcount) + 1: #Change automatic group selection parameters
maxVal = PupIntInput("Max group size:", totalBones/2, 1, totalBones-1)
if maxVal == 0 or maxVal == None: maxVal = 1#Avoid division by zero errors
maxVal = int(totalBones/maxVal) #Convert target size to divisor value
if maxVal == 0 or maxVal == None: maxVal = 1
minVal = PupIntInput("Min group size:", 3, 0, totalBones-1)
if minVal == None: minVal = 2
partVar = sideVar = ""
groups()
if partVar == "" and sideVar == "": TrimPose = 0
elif partList == len(groupcount) + 2 or partList == len(groupcount) + 3:
if partVar != "":
ask = PupMenu("Options %t|Combine listbox selection with partial save group(s) %x1\
|Deactivate partial save group(s) %x2|Cancel %x3")
if ask == 2:
partVar = ""
sideVar = ""
if ask == 3: return
if partList == len(groupcount) + 2: #bones list save inclusive
if boxBones == []: error = PupMenu("No current bone listbox selection. %t|Please make a bone listbox selection.")
else:
if bonesSave == 1: bonesSave = 0
elif bonesSave == 0 or bonesSave == 2: bonesSave = 1 #1 is inclusive
elif partList == len(groupcount) + 3: #bones list save exclusive
if boxBones == []: error = PupMenu("No current bone listbox selection. %t|Please make a bone listbox selection.")
else:
if bonesSave == 2: bonesSave = 0
elif bonesSave == 0 or bonesSave == 1: bonesSave = 2 #2 is exclusive
elif partList == len(groupcount) + 4 or\
(partList == 1 and groupcount[0] == noGroups): #Deactivate all
TrimPose = 0
partVar = ""
sideVar = ""
bonesSave = 0
if partVar == sideVar == "" and bonesSave == 0: TrimPose = 0 #Cancel
else:
TrimPose = 0 #No armature selection
error = PupMenu("Error.%t|Please select an Armature")
def symmetrical_save(): #Select the side if a group is symmetrized
sideList = PupMenu("Select side(s) to save in pose: %t|Right %x1|Left %x2|Both %x3|Cancel %x4")
if sideList == 1: sideVar = "right"
elif sideList == 2: sideVar = "left"
elif sideList == 3: sideVar = "both"
else: sideVar = ""
return sideVar
##-------Load Functions (and "real time" application)-------------------------------------------------
def load_pose(file_name):
global bodyscale, PoserConvert, flipbone, realTime, right, left, lenR, lenL
ext = os.path.splitext(file_name)[1]
if ext == ".pz2" or ext == ".p2z" or ext == ".hd2" or ext == ".hdz": #Poser pose loading
load_poser_pose(file_name)
return
if file_name != "" or realTime == 1:
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
if groupsDict == {}: groups() #partial groups and root part definition
pose = ob.getPose()
if realTime == 0: #realTime is a toggle variable which enables us to multitask this loop.
txt = open(file_name,"r") #With realTime == 0, we load a pose from a textfile.
lines = txt.readlines() #With realTime == 1, we apply selected changes (from gui buttons) to
if realTime == 1: #the current armature pose, without loading a file.
lines = pose.bones.values()
for line in lines:
if realTime == 0:
data = line.rstrip("\n").split(",")
bone = pose.bones[data[0]]
if realTime == 1: #collect the bone's data in the form passed by a saved pose
bone = line
loc = bone.loc
quat = bone.quat
size = bone.size
data = (bone.name, loc[0], loc[1], loc[2], quat[0], quat[1], quat[2], quat[3], size[0], size[1], size[2])
if bone:
if flipDict == {}: flip() #Opposite side bones
flipbone = ""
screening = 0 #Flag variable to exclude a bone from handling in this loop
if ExcludePart == 1: screening = Part_Load(bone, 0) #Exclude selected partial save group when loading
if IncludePart == 1: screening = Part_Load(bone, 1) #Use only selected partial save group when loading
if bonesLoad == 1 and bone.name not in boxBones: screening = 1 #Load only bones listbox selections
if bonesLoad == 2 and bone.name in boxBones: screening = 1 #Exclude bones listbox selections when loading
if (ScreenPose == 1 or DupPose == 1) and screening == 0:
screening = Screen_Pose(bone.name, pose) #Screen out right or left side when loading
#------HipPose and bodyscale are still formatted for cr2-derived armatures
#------The HipPose functions should still find the root in an armature, however.
#root and rootchild are inherited from the groups() fxn.
if bone.name == "BODY" and root.name == bone.name and bodyscale == 0:#On/off for BODY (root)
screening = 1 #Poser-specific
#print bone.name, bone.loc, root.name, screening, rootchild
noRot = 0
noTrans = 0
if HipPose == 1: #On/off for root OR for hip (child of root) if root is BODY (as w/ Poser cr2)
if (bone.name == "hip" and rootchild != None and bone.name == rootchild.name) or \
(bone.name == root.name and bone.name != "BODY"):
#print "root: ", root, "rootchild: ", rootchild, HipPose, hipR, hipT
if hipR == 1: noRot = 1
if hipT == 1: noTrans = 1 #Need to clean this up. Five variables?
#---------------------------------------
if screening == 0:
if noTrans == 1: loc = bone.loc #loc = Vector(0,0,0)
if noTrans == 0: #We start applying the values (from Blender Library).
loc = Vector(data[1],data[2],data[3])
if noRot == 1: quat = bone.quat #quat = Quaternion(1,0,0,0)
if noRot == 0:
quat = Quaternion(data[4],data[5],data[6],data[7])
if bodyscale == 1 and addscale == 1 and bone.name == root.name:
quat = MixPose(quat, bone, 0, 0) #Additive rotation for BODY - cr2 armature-specific
#------------------pose type conversions
if PoserConvert == 1:
quat = Poser_Conversion(bone, quat, noRot) #Convert Poser export/"empty" pose/restmatrix "no roll" pose
if PoseMat == 1:
quat = IK_load(bone, quat, ob, pose) #Convert poseMatrix "IK" pose
#------------------------------combinatory modifiers
if noRot == 0:
if partPose != 1.0:
quat = Partial_Pose(quat, 0, 0) #Apply pose partially, from slider value
loc = Partial_Pose(0, loc, 1) #translation
if MixPose == 1:
quat = Mix_Pose(quat, bone, 0, 0) #Mix pose with current armature rotation
loc = Mix_Pose(0, bone, loc, 1) #translation
bone.loc = loc
bone.quat = quat
size = Vector(data[8],data[9],data[10]) #Finish applying base values (Blender Library)
bone.size = size
#--------Symmetry stuff----------------------------------------
if DupPose == 1: #Duplicate sides (Symmetry Right/Left button)
if bone.name in flipDict:
flipbone = flipDict[bone.name]
flipbone = pose.bones[flipbone]
loc = Flip_Pose2(0, loc, 2)
flipbone.loc = loc
quat = Flip_Pose2(quat, 0, 1)
flipbone.quat = quat
else: #Zero the torso chain or other un-mirrored parts
#This checks the flipDict. Would it be better to check for centered parts, as w/ groups?
if torsoZero == 0:
bone.loc = Flip_Zero(0, loc, 2)
bone.quat = Flip_Zero(quat, 0, 1)
if MirrorPose == 1 or MirrorBone == 1: #Mirror pose (Flip Pose button)
if bone.name not in flipDict or MirrorBone == 1: #1 - parts not mirrored
quat = Flip_Pose2(bone.quat, 0, 1)
loc = Flip_Pose2(0, bone.loc, 2)
bone.quat = quat
bone.loc = loc
#-------End symmetry stuff. End main loop-----------------------------------------
else: #If body part in pose doesn't match any bones, just overlook it
pass
#-----------Last part of flip (outside loop)------------------------------------------------
#Symmetrical parts are mirrored in a separate loop. All data for both sides needs to
#be available from the outset, which isn't true when reading a pose - so the function
#is run outside the main loop.
if MirrorPose == 1 and MirrorBone == 0: #These are mutually exclusive, so this is redundant
for bone in pose.bones.values():
if bone.name in flipDict: #2 - mirrored parts
screening = 0 #repeat the partial groups screening from the main loop....
if ExcludePart == 1: screening = Part_Load(bone, 0)
if IncludePart == 1: screening = Part_Load(bone, 1)
if bonesLoad == 1 and bone.name not in boxBones: screening = 1
if bonesLoad == 2 and bone.name in boxBones: screening = 1
if screening == 0:
flipbone = pose.bones[flipDict[bone.name]]
quat = Flip_Pose2(flipbone.quat, 0, 1)
loc = Flip_Pose2(0, flipbone.loc, 2)
flipquat = Flip_Pose2(bone.quat, 0, 1)
fliploc = Flip_Pose2(0, bone.loc, 2)
bone.quat = quat
bone.loc = loc
flipbone.quat = flipquat
flipbone.loc = fliploc
del flipDict[bone.name]
del flipDict[flipbone.name]
#-----------------------------------------------------------------------------------------------
if askKey != 2:
if askKey == 0: #Ipo curve handling replaces Blender Library keyframing, which was slow.
c = PupMenu("Insert pose keys for this frame?%t|Yes%x1|No%x2")
if askKey == 1: c = 1
if c == 1:
for bone in pose.bones.values():
add_curves(ob, pose, bone.name, bone.quat, bone.loc, bone.size, Blender.Get('curframe'))
if realTime == 0: txt.close()
if realTime == 1: realTime = 1 - realTime
if askKey != 2: #openBatch sets askKey to 2. This doesn't need to run w/ batch load (not sure why...?).
pose.update()
ob.makeDisplayList()
if openBatch == 1: #Ipo curve keyframing
for bone in pose.bones.values():
add_curves(ob, pose, bone.name, bone.quat, bone.loc, bone.size, Blender.Get('curframe'))
elif ob.getType() != "Armature":
error = PupMenu("Error.%t|Please select an Armature")
Blender.Redraw()
flipBone = ""
flipDict.clear()
#---------------------------------------------------------
def Mix_Pose(quat, bone, loc, switch):
#Load pose additive to current pose
if switch == 0: #rotation
x1,y1,z1 = quat.toEuler().unique()
xa,ya,za = bone.quat.toEuler().unique()
qEu = Euler(x1+xa,y1+ya,z1+za).unique()
return qEu.toQuat()
if switch == 1: #translation
x1,y1,z1 = loc
xa,ya,za = bone.loc
loc = Vector(x1+xa,y1+ya,z1+za)
return loc
#---------------------------------------------------------
def Partial_Pose(quat, loc, switch):
#Apply pose by slider percentage
if switch == 0: #rotation
xp,yp,zp = quat.toEuler().unique()
quat = Euler(xp * partPose,yp * partPose,zp * partPose).unique()
return quat.toQuat()
if switch == 1: #translation
xp,yp,zp = loc
loc = Vector(xp * partPose,yp * partPose,zp * partPose)
return loc
#-----"Screen" sides (exclusion)---------------------------------
#Screen one side, based on symmetry notation, with check to be sure no bone is screened
#which lacks a symmetrical counterpart.
def Screen_Pose(bone_name, pose):
screening = 0
if bone_name in flipDict.keys():
flipbone = flipDict[bone_name]
if screenL == 1:
if (pref == 1 and flipbone[:lenL] == left) or (suff == 1 and flipbone[-lenL:] == left) or\
(pref == 1 and flipbone[:len(left_alt)] == left_alt):
screening = 1
elif screenR == 1:
if (pref == 1 and flipbone[:lenR] == right) or (suff == 1 and flipbone[-lenR:] == right) or\
(pref == 1 and flipbone[:len(right_alt)] == right_alt):
screening = 1
return screening
#-----Flip pose (mirror)--------------------------------------
#This handles both the "Flip" and "Symmetry" buttons
#With DupPose - not MirrorPose - this inherits its symmetry screening from a ScreenPose call
def Flip_Pose2(quat, loc, switch):
if switch == 1:
quat = Quaternion(quat[0], quat[1], -quat[2], -quat[3]) #Use quaternion flipping
#xf,yf,zf = quat.toEuler().unique() #paste_posebuf in poseobject.c simply flips euler signs....
#quat = Euler(xf,-yf,-zf).unique().toQuat()
return quat
if switch == 2:
loc = Vector(-loc[0], loc[1], loc[2])
return loc
#----zeroes un-mirrored bones------------------------
def Flip_Zero(quat, loc, switch):
if switch == 1:
quat = Quaternion(quat[0],quat[1],0.0,0.0) #Quaternion version
#quat = Quaternion(1,0,0,0) #Zero all rotational axes
#xq,yq,zq = quat.toEuler().unique() #Euler version
#quat = Euler(xq,0,0).toQuat() #Just zero y and z
return quat
if switch == 2:
loc = Vector(0,loc[1],loc[2]) #Just zero the x
#loc = Vector(0,0,0) #Zero all translation
return loc
#-----Poser conversion (restmatrix/no roll) import--------------------------------
def Poser_Conversion(bone, quat, noRot):
#Based on Campbell Barton's bvh_import.py (for 2.42)
if noRot == 0:
#rx,ry,rz = quat.toEuler().unique() #Work with eulers mainly to reduce changes to Cambo source
bone_rotation_matrix = quat.toMatrix() #go straight to matrix
arm = Object.GetSelected()[0]
AData = arm.getData()
bone1 = AData.bones[bone.name]
bone_rest_matrix = bone1.matrix['ARMATURESPACE'].rotationPart()
bone_rest_matrix_inv= Matrix(bone_rest_matrix)
bone_rest_matrix_inv.invert()
#bone_rotation_matrix= Euler(rx,ry,rz).toMatrix()
finalMatrix = bone_rest_matrix * bone_rotation_matrix * bone_rest_matrix_inv
return finalMatrix.toQuat()
#----PoseMatrix/constraints pose import----------------------------------------
# Based (primarily) on der_ton's blender2md5import.py
def IK_load(bone, quat, armature, pose):
MATRIX_IDENTITY_4x4 = Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
AData = armature.getData()
bone1 = AData.bones[bone.name]
pose_bone_mat = quat.toMatrix().resize4x4()
bone_rest_matrix = bone1.matrix['ARMATURESPACE']
if bone1.parent:
parent_restMat = bone1.parent.matrix['ARMATURESPACE']
else: parent_restMat = MATRIX_IDENTITY_4x4
parent_restMat_inv = Matrix(parent_restMat)#make a copy so the actual matrix isn't inverted inadvertantly
parent_restMat_inv.invert()
localMat = pose_bone_mat * (bone_rest_matrix * parent_restMat_inv).invert()
return localMat.toQuat()
#-------Partial pose group loading------------------------------------------------
def Part_Load(bone, switch): #Same basic code is seen in the goSave function
#This is used by load_pose and by bonebox_menu
global partVar, sideVar
screening = 0
if partVar == "" and sideVar == "": return screening #This is redundant
if partVar != "":
if sideVar != "": #With symmetry selection
partVar2 = partVar.split("@%#%@")
for group in range(0,len(partVar2)):
if bone.name in groupsDict:
if groupsDict[bone.name][:-1] == partVar2[group]:
screening = 0
if (sideVar == "right" or sideVar == "both") and (pref == 1 and bone.name[:lenR] == right) or\
(suff == 1 and bone.name[-lenR:] == right):
screening = 1
if (sideVar == "left" or sideVar == "both") and (pref == 1 and bone.name[:lenL] == left) or\
(suff == 1 and bone.name[-lenL:] == left):
screening = 1
partVar2 = partVar.split("@%#%@")
for group in range(0,len(partVar2)):
if bone.name in groupsDict:
if groupsDict[bone.name][:-1] == partVar2[group]: screening = 1
if switch == 0: return screening #Exclude
if switch == 1: return 1 - screening #Include
#--------Loading options menu------------------------------------------------
def Load_Menu():
global bodyscale, addscale, HipPose, hipR, hipT, LibVar, openBatch, LibTog, \
TrimPose, LibPath, loadlist, poseList, askKey, torsoZero, poseLimit
menu = PupMenu("Apply pose options: %t|Toggle menu browser and file browser %x1|Exclude root rotation %x2\
|Exclude root translation %x3|Exclude both root rotation and translation %x4\
|Don't zero un-mirrored parts with 'Symmetry' button %x5\
|Poser CR2 'BODY' handling %x6|Import animation as batch of pose files %x7\
|Configure keyframing %x8|Configure menu browsing %x9|Deactivate all options %x10\
|Cancel (Keep current options) %x11")
if menu == 1: #toggle menu browser
LibVar = 1 - LibVar #1 = menu, 0 = file selector
if ListTabTog == 0:
LibPath = "" #Only clear the path if the listbox browser is closed
loadlist = "Pose list browser %t|Browse for a new folder."
poseList = []
elif menu == 2: #exclude root rot
HipPose = hipR = 1 - hipR
hipT = 0
elif menu == 3: #exclude root trans
HipPose = hipT = 1 - hipT
hipR = 0
elif menu == 4: #exclude both rot and trans for root
HipPose = hipR = hipT = 1
elif menu == 5: torsoZero = 1 - torsoZero #Zero torso chain w/ mirror
elif menu == 6: #Poser CR2-derived armature special handling
submenu = PupMenu("CR2 'BODY' options: %t|Use rotation %x1|Use additive rotation %x2| Cancel %x3")
if submenu == 1:
bodyscale = 1 - bodyscale
addscale = 0
elif submenu == 2:
bodyscale = addscale = 1 - addscale
elif submenu == 3:
bodyscale = addscale = LibTog = 0
elif menu == 7: #Animation load
openBatch = 1 - openBatch
elif menu == 8: #configure keyframing
submenu = PupMenu("Keyframing options: %t|Ask every time %x1|Add keyframe without asking %x2\
|Don't add keyframes %x3|Remove all keyframes %x4|Cancel %x5")
if submenu == 1: askKey = 0
elif submenu == 2: askKey = 1
elif submenu == 3: askKey = 2
elif submenu == 4: remove_keys()
elif menu == 9: #Configure menu browser columns
poseLimit = PupIntInput("Max list items:", 54, 1, 100)
elif menu == 10: #Deactivate
HipPose = hipR = hipT = bodyscale = addscale = openBatch\
= LibTog = askKey = LibVar = 0
#if menu == 11 or menu == 0 or menu == -1 or menu == 11: #Cancel
if LibVar == HipPose == hipR == hipT == bodyscale == addscale \
== openBatch == askKey == torsoZero == 0:
LibTog = 0
#-----Load menu for partial group handling--------------------------------
def GroupLoad_Menu():
global IncludePart, ExcludePart, TrimPose, bonesLoad, boxBones, GroupTog
menu = PupMenu("Apply screening options: %t|Load only selected partial save group(s) %x1\
|Exclude partial save group(s) when loading %x2|Load only bone listbox selections %x3\
|Exclude bone listbox selections when loading %x4|Deactivate all groups %x5\
|Cancel (Keep current options) %x6")
if menu == 1: #partial inclusive
if partVar == sideVar == "":
error = PupMenu("Please select a partial save group.")
partSave()
if partVar == sideVar == "": GroupTog = 0
if partVar != "" or sideVar != "":
IncludePart = 1
ExcludePart = 0
TrimPose = 1
elif menu == 2: #partial exclusive
if partVar == sideVar == "":
error = PupMenu("Please select a partial save group.")
partSave()
if partVar == sideVar == "": GroupTog = 0
if partVar != "" or sideVar != "":
IncludePart = 0
ExcludePart = 1
TrimPose = 1
elif menu == 3: #bones list inclusive
if boxBones == []: error = PupMenu("No current bone listbox selection. %t|Please make a bone listbox selection.")
else:
if bonesLoad == 1: bonesLoad = 0
elif bonesLoad == 0 or bonesLoad == 2: bonesLoad = 1 #1 is inclusive
elif menu == 4: #bones list exclusive
if boxBones == []: error = PupMenu("No current bone listbox selection. %t|Please make a bone listbox selection.")
else:
if bonesLoad == 2: bonesLoad = 0
elif bonesLoad == 0 or bonesLoad == 1: bonesLoad = 2 #2 is exclusive
elif menu == 5: #Deactivate
IncludePart = ExcludePart = bonesLoad = GroupTog = 0
if IncludePart == ExcludePart == bonesLoad == 0: #Cancel
GroupTog = 0
##-----Zero pose fxn-------------------------------------------------------------
#Set pose to zero values (rest position values)
def Zero_Pose():
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
pose = ob.getPose()
for bone in pose.bones.values():
bone.loc = Vector(0.00,0.00,0.00)
bone.quat = Quaternion(1.00,0.00,0.00,0.00)
bone.size = Vector(1.00,1.00,1.00)
if askKey != 2:
if askKey == 0:
c = PupMenu("Insert pose keys for this frame?%t|Yes%x1|No%x2")
if askKey == 1: c = 1
if c == 1:
for bone in pose.bones.values():
add_curves(ob, pose, bone.name, bone.quat, bone.loc, bone.size, Blender.Get('curframe'))
pose.update()
ob.makeDisplayList()
Blender.Redraw()
##-----Symmetry notation handling----------------------------------------------------------
#Change right and left values to alter default suffix of prefix
#Swap suff and pref values to change default from prefix to suffix
def Symmetry_Notation():
global right, left
right = PupStrInput("Enter right (-x):", right, 25)
left = PupStrInput("Enter left (+x):", left, 25)
if right == None: right = "r"
if left == None: left = "l"
right = right.replace(".", "") #Strip the dots (".") because len comparisons don't include them
left = left.replace(".", "")
lenR = len(right)
lenL = len(left)
#-----Define the opposite side--------------------------------------
def flip():
global right, left, suff, pref, flipbone
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
AData = ob.getData()
if ob.getType() == "Armature":
AData = ob.getData()
flipnames = AData.bones.keys()
for name in flipnames:
flipbone = "None"
if suff == 1:
if name[-lenR:] == right: flipbone = name[:-lenR] + left
elif name[-lenL:] == left: flipbone = name[:-lenL] + right
if pref == 1:
if name[:lenR] == right: flipbone = left + name[lenR:]
elif name[:lenL] == left: flipbone = right + name[lenL:]
elif name[:len(right_alt)] == right_alt: flipbone = left_alt + name[len(right_alt):] #custom extras
elif name[:len(left_alt)] == left_alt: flipbone = right_alt + name[len(left_alt):]
if flipbone in flipnames: #Don't enter the name if the bone doesn't exist...
flipDict[name] = flipbone #(e.g. - "lowerfoot" might become "rowerfoot" and create a bad entry)
#------Automatically locate partial save groups----------------
def groups(): #Fill groups for use with partial pose saves.
global root, rootchild, maxVal, minVal, totalBones
if flipDict == {}: flip()
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
AData = ob.getData()
groupnames = AData.bones.values()
groupsDict.clear()
for group1 in groupnames:
if not group1.hasParent(): #We assume the root is the bone which has children, but no parent....
if group1.hasChildren(): root = group1
totalBones = len(group1.getAllChildren())
if len(root.children) == 1: rootchild = root.children[0]
break
for group1 in groupnames:
if group1.hasChildren():
if len(group1.children) > 1:
for i in group1.getAllChildren():
if len(group1.getAllChildren()) < totalBones/maxVal and len(group1.getAllChildren()) >= minVal:
#Screening parameters are configurable, in the partSave function.
if round(AData.bones[group1.name].head['ARMATURESPACE'][0],4) == 0.0000: add = "="
elif round(AData.bones[group1.name].head['ARMATURESPACE'][0],4) != 0.0000: add = "!"
#Determine whether the ultimate parent of group is centered on x.
#(Note: using the flipDict for this proved inadequate....)
if pref == 1 and group1.name[:lenR] == right and group1.name in flipDict:
group_name = group1.name[lenR:] + "_parts" + add
elif suff == 1 and group1.name[-lenR:] == right and group1.name in flipDict:
group_name = group1.name[:-lenR] + "_parts" + add
elif pref == 1 and group1.name[:lenL] == left and group1.name in flipDict:
group_name = group1.name[lenL:] + "_parts" + add
elif suff == 1 and group1.name[-lenL:] == left and group1.name in flipDict:
group_name = group1.name[:-lenL] + "_parts" + add
else: group_name = str(group1.name) + "_parts" + add
group_name = group_name.lower()
groupsDict[i.name] = group_name
##----------Armature and Bone roll functions---------------------------------------------------
#makeEditable() reportedly has a bug. Each implementation seems to require that the function be called
#in a different location, relative to a loop using the Armature.bones dict. Placing it within the loop
#seems to cause a memory error in which Blender system variable names (or memory locations?) are substituted
#for dict keys or values.
#-------Menu for bone functions-----------------------------------------
def Bone_Menu():
menu = PupMenu("Armature settings options: %t|Save armature settings to .txt %x1|Load armature settings from .txt %x2\
|Zero bone roll %x3|Restore bone roll (in-session only) %x4|Correct bone roll angles for all bones %x5\
|Correct bone roll for bones list bones %x6|Create armature from .txt or .cr2/.crz %x7|Cancel %x8")
if menu == 1: Window.FileSelector(boneSave, 'Save as .txt file.')
elif menu == 2: Window.FileSelector(boneLoad, 'Browse for .txt file.')
elif menu == 3: Bone_Roll_Z()
elif menu == 4: Bone_Roll_R()
elif menu == 5: boneroll_correct(0)
elif menu == 6: boneroll_correct(1)
elif menu == 7: Window.FileSelector(create_armature, 'Browse for .txt file.')
#--------Save armature settings to .txt file-----------------------------
def boneSave(save_name):
#Saves roll, head, tail, parent, IK target bone, IK chainlen
if save_name != "":
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
temptext = ""
AData = ob.getData()
pose = ob.getPose()
option = PupMenu("Bones to save? %t|Save all bones in armature %x1|Save only bones list selections %x2")
if option == 1: templist = AData.bones.values()
elif option == 2: templist = list(AData.bones[bone] for bone in boxBones)
if AData:
for bone1 in templist:
head = bone1.head['ARMATURESPACE']
tail = bone1.tail['ARMATURESPACE']
parent = bone1.parent
if parent == None: parent = "None"
else:
parent = parent.name
IK = "None"
chainlen = 0
bone = pose.bones[bone1.name]
for const in bone.constraints:
if const.type == Constraint.Type.IKSOLVER:
IK = const[Constraint.Settings.BONE]
chainlen = const[Constraint.Settings.CHAINLEN]
temptext += "%s,%f,"%(bone1.name, bone1.roll['ARMATURESPACE'])
temptext += "%f,%f,%f," %(head[0], head[1], head[2])
temptext += "%f,%f,%f," %(tail[0], tail[1], tail[2])
temptext += "%s,%s,%i\n" %(parent, IK, chainlen)
save_name = os.path.splitext(save_name)[0] + ".txt"
if sys.exists(save_name) == 1:
warning = PupMenu("Overwrite file %s? %%t|Yes %%x1|No %%x2" %(sys.basename(save_name))) #Don't overwrite
if sys.exists(save_name) != 1 or warning != 2:
rollsave = open(save_name,"w")
rollsave.write(temptext)
rollsave.close()
#--------Load bone roll settings from .txt file---------------------------
def boneLoad(file_name):
#Loads roll, head, tail, parent, IK target bone, IK chainlen; optionally adds missing bones
warning = PupMenu("Warning: This will alter your armature. %t|Load bone roll settings %x1\
|Load head and tail settings %x2|Load parenting %x3|Load IK %x4|Load IK with parenting %x5\
|Load all %x6|Cancel %x7")
if warning != 7:
if file_name != "":
temptext = ""
addlist = []
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
AData = ob.getData()
pose = ob.getPose()
if AData:
option = PupMenu("Bones to load: %t|Load all bones in armature %x1|Load only bones list selections %x2")
if option == 1: templist = AData.bones.keys()
elif option == 2: templist = boxBones
AData.makeEditable()
temptext = open(file_name,"r")
lines = temptext.readlines()
for line in lines:
data = line.rstrip("\n").split(",")
if data[0] not in AData.bones.keys(): #optionally add missing bones (all or none)
addbone = PupMenu("Bone not found: %s %%t|Add missing bone(s) to armature %%x1\
|Don't add missing bone(s) %%x2" %(data[0]))
if addbone == 1: #Add all missing bones in a subloop, to avoid parenting problems
scale = PupFloatInput("Scaling:", 1.0, 0.001, 1000.0,10,2)
if scale == None: scale = 1.0 #pup inputs can return None
for line in lines:
data2 = line.rstrip("\n").split(",")
if data2[0] not in AData.bones.keys():
head = (float(data2[2])*scale, float(data2[3])*scale, float(data2[4])*scale)
head = Vector(head)
tail = (float(data2[5])*scale, float(data2[6])*scale, float(data2[7])*scale)
tail = Vector(tail)
create_bone(AData, data2[0], head, tail, float(data2[1]))
addlist.append(data2[0])#pass parenting info for new bones to parenting step
templist.append(data2[0])
AData.update()#these update the armature data in case two armatures are being combined
AData.makeEditable()
pose = ob.getPose()
if data[0] in templist: ##set the requested values for bones
bone1 = AData.bones[data[0]]
if bone1.name == data[0]:
if (warning == 1 or warning == 6) and data[0] not in addlist: #roll
bone1.roll = float(data[1])
if (warning == 2 or warning == 6) and data[0] not in addlist: #head and tail
bone1.head = Vector(data[2], data[3], data[4])
bone1.tail = Vector(data[5], data[6], data[7])
if warning == 3 or warning == 5 or warning == 6 or data[0] in addlist: #parenting
if data[8] in AData.bones.keys():
bone1.parent = AData.bones[data[8]]
if warning == 4 or warning == 5 or warning == 6: #IK
if data[9] in AData.bones.keys():
bone = pose.bones[bone1.name]
bone.constraints.append(Constraint.Type.IKSOLVER)
const = bone.constraints[len(bone.constraints)-1] #last entry is our appended IKSOLVER
const[Constraint.Settings.TARGET] = ob #target object is the selected armature
const[Constraint.Settings.BONE] = data[9] #active bones list selection is target bone
const[Constraint.Settings.CHAINLEN] = int(data[10])
else: print "Bone not loaded:",data[0]
temptext.close()
AData.update()
pose.update()
ob.makeDisplayList()
Blender.Redraw()
print "Done loading armature settings."
#---------Create a bone----------------------------------------------------
def create_bone(armature, name, head, tail, roll):
bone = Armature.Editbone()
bone.roll = roll
bone.head = head
bone.tail = tail
armature.bones[name] = bone
print "Added bone %s to armature." %(name)
#---------Create an armature from saved .txt settings------------------------------------------
def create_armature(file_name):
if os.path.splitext(file_name)[1] == ".txt": switch = 1
elif os.path.splitext(file_name)[1] == ".cr2" or os.path.splitext(file_name)[1] == ".crz": switch = 2
scale = PupFloatInput("Scaling:", 1.0, 0.001, 1000.0,10,2)
if scale == None: scale = 1.0 #pup inputs can return None
armname = PupStrInput("Armature name:", "ARM.NEWARM", 25) #get a name for the new armature
if armname == None: armname = "ARM.NEWARM"
objs = Blender.Scene.GetCurrent().getChildren()
objs = list(obj.name for obj in objs)
if armname in objs: addname += "_" + str(len(objs)).rjust(4,"0")
ob = Object.New('Armature')
AData = Armature.Armature(armname)
ob.link(AData)
AData.makeEditable()
if switch == 1: #custom armature backup
temptext = open(file_name,"r")
lines = temptext.readlines()
elif switch == 2: #Poser cr2 import
lines = parse_poser_file(file_name)
for line in lines: #add the bones
data = line.rstrip("\n").split(",")
bone = Armature.Editbone()
bone.roll = float(data[1])
bone.head = Vector(float(data[2])*scale, float(data[3])*scale, float(data[4])*scale)
bone.tail = Vector(float(data[5])*scale, float(data[6])*scale, float(data[7])*scale)
AData.bones[data[0]] = bone
for line in lines: #assign parenting after all bones are present
data = line.rstrip("\n").split(",")
if data[0] in AData.bones.keys():
bone = AData.bones[data[0]]
if data[8] in AData.bones.keys():
bone.parent = AData.bones[data[8]]
AData.update()
scn= Blender.Scene.GetCurrent()
scn.link(ob)
scn.update()
pose = ob.getPose()
try:
for line in lines: #A third loop for IK (posebone-level) addition, after armature is updated and scene-linked
data = line.rstrip("\n").split(",")
if data[9] in pose.bones.keys():
bone1 = pose.bones[data[0]]
bone1.constraints.append(Constraint.Type.IKSOLVER)
const = bone1.constraints[len(bone1.constraints)-1] #last entry is our appended IKSOLVER
const[Constraint.Settings.TARGET] = ob #target object is the new armature
const[Constraint.Settings.BONE] = data[9] #target bone
const[Constraint.Settings.CHAINLEN] = int(data[10])
const.influence = 1.0
except: pass #IK scripting not available before 242
pose.update()
ob.select(1)
if switch == 1: temptext.close()
ob.makeDisplayList() #to refresh the bones display
#---------Restore bone roll settings (in-session only) -----------------------------------------
def Bone_Roll_R(): #Restore boneroll from original values in oldRolls dict
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
AData = ob.getData()
if AData:
AData.makeEditable()
bones = AData.bones.values()
for bone1 in bones:
if oldRolls.has_key(bone1.name):
bRoll2 = oldRolls[bone1.name]
bone1.roll = bRoll2
AData.update()
#--------------Zero bone roll settings----------------------------------------------------------
def Bone_Roll_Z(): #Zero bone roll angles; save to oldRolls dict w/ option to save .txt backup
if Object.GetSelected() != []:
c = PupMenu("Save old bone roll settings to .txt file? %t|Yes %x1|No %x2|Cancel %x3")
if c == 1:
Window.FileSelector(boneSave, 'File to save as?')
if c != 3:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
AData = ob.getData()
if AData:
if oldRolls == {}:
for bone1 in AData.bones.values(): #Don't replace backup values if they've already been set.
oldRolls[bone1.name] = int(bone1.roll['ARMATURESPACE'])
AData.makeEditable()
bones = AData.bones.values()
for bone1 in bones:
bone1.roll = 0.0
AData.update()
#-------"Correct" the roll angles to conform with Blender standards.------------------------------------
#Roll angles need to be zeroed before running this.
#This is probably a ridiculous kludge approach. Some z-pointing bones aren't quite correct....
#points bone z-axis in "correct" direction. Up for horizontal bones, forward for vertical.
def boneroll_correct(switch):
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
c = PupMenu("Save old bone roll settings to .txt file? %t|Yes %x1|No %x2|Cancel %x3")
if c == 1:
Window.FileSelector(boneSave, 'File to save as?')
if c != 3:
ob = Object.GetSelected()[0]
AData = ob.getData()
if AData:
if oldRolls == {}:
for bone1 in AData.bones.values(): #Don't replace backup values if they've already been set.
oldRolls[bone1.name] = int(bone1.roll['ARMATURESPACE'])
pose = ob.getPose()
rollDict = {}
y_dir = Vector(0,1,0) #Directions x,y,z as vectors
z_dir = Vector(0,0,1)
x_dir = Vector(1,0,0)
if switch == 0: templist = pose.bones.keys()
elif switch == 1: templist = boxBones
#if AData.drawAxes == False: AData.drawAxes = True
for bone in templist:
#y_dir2 = y_dir * pose.bones[bone].poseMatrix.rotationPart() # poseMatrix is worldspace
#z_dir2 = z_dir * pose.bones[bone].poseMatrix.rotationPart()
#x_dir2 = x_dir * pose.bones[bone].poseMatrix.rotationPart()
y_dir2 = y_dir * AData.bones[bone].matrix['ARMATURESPACE'].rotationPart()
z_dir2 = z_dir * AData.bones[bone].matrix['ARMATURESPACE'].rotationPart()
#x_dir2 = x_dir * AData.bones[bone].matrix['ARMATURESPACE'].rotationPart()
dx,dy,dz = y_dir2
dirtup = (abs(dx),abs(dy),abs(dz))
dirtup = list(dirtup)
dirmax = max(dirtup) #Find the dominant axis
axis = z_dir2 #The bone axis to be "pointed"
#Define "direx" as the direction in which to point "axis"
#Proof that this is a hack: I don't really know why the round(dirtup) stuff helps, but it seems to....
if dirtup.index(dirmax) == 0: #x-pointing
if round(dirtup[2],1) > 0.3: #if there is z-pointing angling
direx = x_dir
else:
direx = y_dir #point up
elif dirtup.index(dirmax) == 1: #y-pointing
if round(dirtup[2],1) > 0.3: #if there is z-pointing angling
print bone
if dy >= 0:
direx = y_dir-z_dir
elif dy < 0:
if dz >= 0: direx = y_dir+z_dir
else:
direx = y_dir-z_dir
direx = -direx
else:
direx = z_dir #point forward
elif dirtup.index(dirmax) == 2: #z-pointing
if round(dirtup[1],1) > 0.3: #if there is x-pointing angling
if dy >= 0:
direx = y_dir-z_dir
elif dy < 0:
direx = y_dir+z_dir
else:
direx = y_dir #point up
roll_ang = AngleBetweenVecs(direx, axis) #roll value is angle between direx and axis
if dx > 0: roll_ang = -roll_ang #if bone points in +x direction, flip the roll value
rollDict[bone] = roll_ang #can't query values after makeEditable, so pass between loops using dict
AData.makeEditable()
for bone in templist:
roll_ang = rollDict[bone]
AData.bones[bone].roll = roll_ang
AData.update()
##----------------Animation-----------------------------------------
#------------Keyframe handling--------------------------------------
def anim_menu():
menu = PupMenu("Frames options: %t|Select all keyframes %x1|De-select all %x2|Invert keyframe selection %x3\
|Delete all keyframes %x4|Delete selected keyframes for all bones %x5\
|Delete selected keyframes for bones list selections %x6\
|Delete selected keyframes for active bones list bone %x7|Change frame count %x8|Cancel %x9")
if menu == 1: select_keys()
elif menu == 2: deselect_keys()
elif menu == 3: invert_keys()
elif menu == 4: remove_keys()
elif menu == 5: remove_selected(2)
elif menu == 6: remove_selected(0)
elif menu == 7: remove_selected(1)
elif menu == 8: change_frames()
def change_frames(): #This changes the length of the frames list display. Python apparently can't set the endframe.
global animlen
animlen = PupIntInput("Frame count:", animlen, 1, animlen+1000)
def remove_keys(): #Delete all keyframes.
global boxFrames
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
if ob.getAction():
pose = ob.getPose()
act = ob.getAction() #get the current action
all = act.getAllChannelIpos() #all channel curves for action
if len(all) > 0: #if there are keyframes
for bone in pose.bones.keys(): #loop through all bones
if bone in all: #if bone is keyframed
act.removeChannel(bone) #remove all channels for bone
boxFrames = [] #clear the display list selections
else: oops = PupMenu("No keyframes to delete!")
ob.makeDisplayList() #to refresh the bones display
def select_keys():#select current keyframes
global boxFrames
try:
boxFrames = []
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
if ob.getAction():
act = ob.getAction()
c = act.getFrameNumbers() #Not available before 242
boxFrames = list(str(k) for k in c)
if c == []: oops = PupMenu("No keyframes to select!")
except: error = PupMenu("This feature is only available in versions later than 2.41")
def deselect_keys(): #deselect everything in anim listbox
global boxFrames
boxFrames = []
def invert_keys(): #invert keyframe selection in bones listbox
global boxFrames
try:
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
if ob.getAction():
act = ob.getAction()
a = set(int(i) for i in boxFrames)
b = set(act.getFrameNumbers())
c = list(b-a)
boxFrames = list(str(k) for k in c)
except: error = PupMenu("This feature is only available in versions later than 2.41")
def remove_selected(switch):#Delete selected keyframes
global boxFrames
try:
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature" and ob.getAction(): #if there is an armature selection
startTime = sys.time()#keep track of speed
templist = []
if switch == 0 and boxBones != []: templist = boxBones #use only bones list selections
if switch == 1 and lastBone != "": #use only active bones list selection
templist = []
templist.append(lastBone)
if switch == 2: #use all bones in armature
pose = ob.getPose()
templist = pose.bones.keys()
act = ob.getAction() #get the action
if len(act.getAllChannelIpos()) > 0: #if there are keyframes
a = set(templist)
b = set(act.getAllChannelIpos())
templist = list(a & b) #the list of bones which have Ipo curves
a = set(int(i) for i in boxFrames)
b = set(act.getFrameNumbers())
c = sorted(list(b - a)) #the list of keyframes which aren't selected
d = list(a & b) #the list of keyframes which are selected (for tracking)
if templist != []:
if c == []: #if all keyframed frames are selected
for bone in templist:
act.removeChannel(bone)#delete all channels for selected bones
if c!= []: #not all keyframed frames selected
framesDict = {}
for indx, frame in enumerate(c):
data = ""
Blender.Set('curframe', frame)
for bone in templist: #for selected bones
bone = pose.bones[bone] #collect the animation data
lx,ly,lz = bone.loc
qw,qx,qy,qz = bone.quat
sx,sy,sz = bone.size
data += "%s,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n" %(bone.name,lx,ly,lz,qw,qx,qy,qz,sx,sy,sz)
if indx == len(c) - 1: #if this is the last frame in the sequence
act.removeChannel(bone.name) #remove all the bone's ipo channels
framesDict[frame] = data #add the data to the dict
Blender.Set('curframe', c[0]) #restore first frame not to be deleted
for frame, data in framesDict.iteritems(): #use the dict to add frames back into animation
lines = data.split("\n")
for line1 in lines:
line = line1.split(",")
if line != ['']: #don't try to run last line, which is blank
bone = pose.bones[line[0]]
bone.loc = Vector(line[1], line[2], line[3]) #rebuild the data
bone.quat = Quaternion(line[4], line[5], line[6], line[7])
bone.size = Vector(line[8], line[9], line[10])
add_curves(ob, pose, bone.name, bone.quat, bone.loc, bone.size, frame) #restore the undeleted frames
framesDict.clear()
a = set(boxFrames)
b = set(str(k) for k in act.getFrameNumbers())
boxFrames = list(a & b) #refresh boxFrames as the set of keyframes which weren't deleted
ob.makeDisplayList() #to refresh the bones display
finTime = sys.time() - startTime
print "Done deleting %i keyframes from %i bones in %f seconds" %(len(d), len(templist), finTime)
else: oops = PupMenu("No keyframes to delete!")
except: error = PupMenu("This feature is only available in versions later than 2.41")
def add_curves(ob, pose, bone, quat, loc, size, time):
#This is kind of a compressed and simplified version of Cambo's Ipo keyframing from bvh_import.py for 242.
#This is used by open_batch and remove_selected...
#Also by load_pose, Zero_Pose, numerical_entry, and load_poser_pose for keyframe insertion
if not ob.getAction():
act = Armature.NLA.NewAction("Action")
act.setActive(ob)
else: act = ob.getAction()
act_ipo = act.getAllChannelIpos()
if bone in act_ipo:
ipo = act_ipo[bone]
ipo[Ipo.PO_LOCX].append((time, loc[0]))
ipo[Ipo.PO_LOCY].append((time, loc[1]))
ipo[Ipo.PO_LOCZ].append((time, loc[2]))
ipo[Ipo.PO_SCALEX].append((time, size[0]))
ipo[Ipo.PO_SCALEY].append((time, size[1]))
ipo[Ipo.PO_SCALEZ].append((time, size[2]))
ipo[Ipo.PO_QUATW].append((time, quat[0]))
ipo[Ipo.PO_QUATX].append((time, quat[1]))
ipo[Ipo.PO_QUATY].append((time, quat[2]))
ipo[Ipo.PO_QUATZ].append((time, quat[3]))
elif not bone in act_ipo: # a keyframe needs to be added to create the bone ipo if it doesn't already exist
bone1 = pose.bones[bone]
bone1.insertKey(ob,time,Object.Pose.LOC)
bone1.insertKey(ob,time,Object.Pose.ROT)
bone1.insertKey(ob,time,Object.Pose.SIZE)
#-----------Animation load and save (batch handling)------------------------------------------------
def frames_sort():
global boxFrames
if batchFrames == 1: #sort boxFrames numerically
templist = sorted(int(i) for i in boxFrames)
boxFrames = list(str(i) for i in templist)
#-------------------------------------------------------------------------
def open_batch(filename): #load a batch of poses, option to specify destination keys using frames listbox
#This cannot change the endframe of an animation, but it will increase the range of the frames listbox
global askKey, batchFrames, animlen
if len(boxFrames) < 1: batchFrames = 0
startTime = sys.time()
curframe = Blender.Get('curframe')
if batchLoad == 0: #default if not using pose listbox - load numbered batch, as saved
frames = PupIntInput("animation length:", 1, Blender.Get('staframe'), Blender.Get('endframe'))
if frames != None:
if batchFrames == 0: frame = curframe#Blender.Get('curframe') #load starts from current frame
elif batchFrames == 1: #frames listbox screening
frames_sort() #sort the selected list frames numerically
frame = int(boxFrames[0])
Blender.Set('curframe', frame)
path = sys.dirname(filename)
filename = sys.basename(filename)
file_ext = os.path.splitext(filename)[1]
filebase = os.path.splitext(filename)[0][:-4]
temp = askKey
askKey = 2
for filenum in range(1, frames+1):
fileload = sys.join(path,filebase + str(filenum).rjust(4,"0") + file_ext)
if sys.exists(fileload) == 1:
load_pose(fileload)
if batchFrames == 0:
Blender.Set('curframe', frame + filenum)
curframe = frame + filenum
elif batchFrames == 1: #frames listbox screening
if filenum < len(boxFrames):
Blender.Set('curframe', int(boxFrames[filenum]))
curframe = int(boxFrames[filenum])
else:
Blender.Set('curframe', int(boxFrames[len(boxFrames) - 1]) + (filenum - len(boxFrames))+1)
curframe = int(boxFrames[len(boxFrames) - 1]) + (filenum - len(boxFrames))+1
if curframe > animlen: animlen += curframe - animlen
askKey = temp #Reset keyframe screening
Blender.Set('curframe', frame)#Go back to start frame
#--------------------------------------------------------------------------------------
elif batchLoad == 1: #loading via pose listbox - load custom listbox selections
if len(boxPoses) > 0:
frames = len(boxPoses)
if batchFrames == 0: frame = curframe#Blender.Get('curframe') #load starts from current frame
elif batchFrames == 1: #frames listbox screening
frames_sort() #sort the selected list frames numerically
frame = int(boxFrames[0])
Blender.Set('curframe', frame)
temp = askKey
askKey = 2
for filenum in range(1, frames+1):
fileload = sys.join(LibPath, boxPoses[filenum - 1])
if sys.exists(fileload) == 1:
load_pose(fileload)
if batchFrames == 0:
Blender.Set('curframe', frame + filenum)
curframe = frame + filenum
elif batchFrames == 1: #frames listbox screening
if filenum < len(boxFrames):
Blender.Set('curframe', int(boxFrames[filenum]))
curframe = int(boxFrames[filenum])
else:
Blender.Set('curframe', int(boxFrames[len(boxFrames) - 1]) + (filenum - len(boxFrames))+1)
curframe = int(boxFrames[len(boxFrames) - 1]) + (filenum - len(boxFrames))+1
if curframe > animlen: animlen += curframe - animlen
askKey = temp #Reset keyframe screening
Blender.Set('curframe', frame)#Go back to start frame
finTime = sys.time() - startTime
if frames != None: print "Done loading %i poses in %f seconds" %(frames, finTime) #PupIntInput can return None.
#--------------------------------------------------------------------------------------------
def save_batch(filename): #Save animation as a batch of numbered poses, optional selection via frames list
if PoserSave == 0:
if batchFrames == 0:
frames = PupIntInput("animation length:", 1, Blender.Get('staframe'), Blender.Get('endframe'))
frame = Blender.Get('curframe') #default save starts from current frame
elif batchFrames == 1:
if len(boxFrames) > 0:
frames_sort()
frames = len(boxFrames)
frame = int(boxFrames[0])
Blender.Set('curframe', frame)
path = sys.dirname(filename)
filename = sys.basename(filename)
filebase = os.path.splitext(filename)[0]
if PoserSave == 0: #Poser pose export is all written to one file; don't run batch
for filenum in range(1, frames+1):
filesave = sys.join(path,filebase + "_" + str(filenum).rjust(4,"0")) ##file extension will be appended by goSave
goSave(filesave)
#print filesave, filenum, boxFrames[filenum-1]
if batchFrames == 0:
Blender.Set('curframe', frame+filenum)
elif batchFrames == 1:
if filenum < len(boxFrames):
Blender.Set('curframe', int(boxFrames[filenum]))
Blender.Set('curframe', frame)#Go back to start frame
elif PoserSave == 1:
filesave = sys.join(path,filebase)
goSave(filesave)
##--------------IK handling--------------------------------------------
def IK_menu():
menu = PupMenu("IK options: %t|Select all bones with IK %x1|De-select all %x2|Set IK influence for all IK chains %x3\
|Set IK influence for list selections %x4|Set IK influence for active selection %x5|Cancel %x6")
if menu == 1: select_IK()
elif menu == 2: deselect_IK()
elif menu == 3: IK_toggle(2)
elif menu == 4: IK_toggle(0)
elif menu == 5: IK_toggle(1)
def IK_toggle(switch):
try:
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
pose = ob.getPose()
togval = PupFloatInput("value:", 1.0, 0.0, 1.0,10,2) #get the setting
if togval == None: togval = 1.0
templist = []
if switch == 0 and boxBones != []: templist = boxBones #use only bones list selections
if switch == 1 and lastBone != "": #use only active bones list selection
templist = []
templist.append(lastBone)
if switch == 2: #use all bones in armature
templist = pose.bones.keys()
for bonename in templist:
bone = pose.bones[bonename]
for const in bone.constraints:
if const.type == Constraint.Type.IKSOLVER:
const.influence = togval
ob.makeDisplayList() #to refresh the bones display
except: error = PupMenu("This feature is only available in versions later than 2.41")
def deselect_IK(): #deselect everything in anim listbox
global boxBones
boxBones = []
def select_IK():
global boxBones, lastBone
try:
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
pose = ob.getPose()
for bone in pose.bones.values():
for const in bone.constraints:
if const.type == Constraint.Type.IKSOLVER:
boxBones.append(bone.name)
if len(boxBones) > 0: #Set active selection as first displayed item in list
boxBones.sort()
boxBones.reverse()
lastBone = boxBones[len(boxBones)-1]
except: error = PupMenu("This feature is only available in versions later than 2.41")
#-------Bones list toolset for IK custom construction-------------------------
#some tools to assist in building or removing IK solver constraints
def tools_menu():
menu = PupMenu("Bones toolset options: %t|De-select all %x1|Re-parent selected bones %x2\
|Change IK target bone for selections %x3|Add IK to selection(s) %x4\
|Remove IK from all selections %x5|Add a bone to current armature %x6\
|Add IK goal bone to armature %x7|Add IK bend target bone to armature %x8\
|Delete all selected bones %x9| %x18|Scale selected bones %x10\
|Translate selected bones %x11|Swap head and tail of selected bones %x12\
|Flip selected bones along an axis %x13|Connect selected bones with active selection %x14\
|Connect selected bones with their parents %x15|Straighten selected bones %x16\
|Straighten and align chains %x17|Cancel %x18")
if menu == 1: deselect_IK()
elif menu == 2: reparent()
elif menu == 3: retarget()
elif menu == 4: add_IK()
elif menu == 5: remove_IK()
elif menu == 6: add_bone(0)
elif menu == 7: add_bone(1)
elif menu == 8: add_bone(2)
elif menu == 9: delete_bones()
elif menu == 10: scale_bones()
elif menu == 11: move_bones()
elif menu == 12: swap_bones()
elif menu == 13: flip_bone_axis()
elif menu == 14: connect_bones(0)
elif menu == 15: connect_bones(1)
elif menu == 16: align_bones()
elif menu == 17: align_chains()
def reparent(): #make active (last) bones list selection new parent of all previous selections
global root, boxBones, lastBone, boxList
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
if lastBone != "":
if len(boxBones) > 1:
AData = ob.getData()
templist = boxBones #keep bone from being parented to one of its offspring
boxBones = []
for bone in templist:
if bone != lastBone and AData.bones[bone].hasChildren():
offspring = list(child.name for child in AData.bones[bone].getAllChildren())
if not lastBone in offspring: #check the family tree
print "Reparented %s to %s." %(bone, lastBone)
boxBones.append(bone)
else: print "Cannot reparent %s to %s: %s is a descendant of %s." %(bone, lastBone, lastBone, bone)
else: boxBones.append(bone)#bones without children and active bone are restored to list
AData.makeEditable()
boneparent = AData.bones[lastBone]
for selected in range(0,len(boxBones)-1):
bone = AData.bones[boxBones[selected]]
bone.parent = boneparent
AData.update()
ob.makeDisplayList() #to refresh the bones display
if boneSort == 1: boxList = sort_hierarchy(boxList) #re-sort for hierarchical display
def retarget(): #change target bone to active list bone for all inactive selections
try:
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
if lastBone != "":
if len(boxBones) > 1:
pose = ob.getPose()
for selected in range(0,len(boxBones)-1):
bone = pose.bones[boxBones[selected]]
for const in bone.constraints:
if const.type == Constraint.Type.IKSOLVER:
const[Constraint.Settings.BONE] = lastBone ##set the target bone
pose.update()
ob.makeDisplayList() #to refresh the bones display
except: error = PupMenu("This feature is only available in versions later than 2.41")
#This can crash Blender if no pose bone has been selected
def remove_IK(): #remove IK solver constraints from bones list selections
global boxBones
try:
if Object.GetSelected() != [] and boxBones != []:
ob = Object.GetSelected()[0]
pose = ob.getPose()
if ob.getType() == "Armature":
try: pose.bones[boxBones[0]].sel = True #try to select a bone (only available in 242 CVS)
except: print "Could not auto-select a pose bone. Method not available in this build of Blender."
AData = ob.getData() #Verify that a bone has been selected
selected = 0
for selbone in AData.bones.values():
if Armature.BONE_SELECTED in selbone.options:
selected = 1
break
if selected == 1: #crash-prevention: only proceed if there is a user-selected bone
for bone in boxBones:
bone = pose.bones[bone]
for const in bone.constraints:
if const.type == Constraint.Type.IKSOLVER:
bone.constraints.remove(const)
boxBones = [] #entries have been deleted, so clear the list.
pose.update()
ob.makeDisplayList() #to refresh the bones display
else: error = PupMenu("A bone must be selected in the 3D view before IK can be removed.")
except: error = PupMenu("This feature is only available in versions later than 2.41")
def add_IK(): #add IK solver constraints to bones list selections
try: #This has gotten kind of messy... needs some reorganization....
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
if len(boxBones) > 0:
pose = ob.getPose()
AData = ob.getData()
chaintype = 1
chain_len = 1
if len(boxBones) > 1: #Chain will be added in order selected w/ chaintype = 2
chaintype = PupMenu("Options: %t|Add IK to last inactive list bone only %x1\
|Add IK as chain to all inactive selections %x2|Cancel %x3")
if chaintype == 1:
chain_len = PupIntInput("Chainlen:", 1, 1, len(pose.bones))
option = 2
if chaintype == 1 or chaintype == 2:
if chaintype == 2:
inkyname = PupStrInput("Name for new bone:", "inkychain", 25) #get a name
if inkyname == None: inkyname = "inkychain"
if inkyname in AData.bones.keys(): inkyname += "_" + str(len(AData.bones.keys())).rjust(4,"0")
option = PupMenu("Include active selection in chain? %t|Yes %x1|No %x2")
root = sort_hierarchy(pose.bones.keys())[0]
rootlen = AData.bones[root].length
AData.makeEditable()
for i in range(len(boxBones)-1,-1,-1):
bone = boxBones[i]
bone1 = pose.bones[bone]
const = None
if (option == 2 and boxBones.index(bone) < len(boxBones)-1) or\
(option == 1 and boxBones.index(bone) < len(boxBones)):
bone1.constraints.append(Constraint.Type.IKSOLVER)
const = bone1.constraints[len(bone1.constraints)-1] #last entry is our appended IKSOLVER
const[Constraint.Settings.TARGET] = ob #target object is the selected armature
const[Constraint.Settings.CHAINLEN] = i+1#boxBones.index(bone) + 1 #add chainlen value
const.influence = 1.0
if chaintype == 1: #single bone
if const != None:
const[Constraint.Settings.BONE] = lastBone #active bones list selection is target bone
const[Constraint.Settings.CHAINLEN] = chain_len #user-specified chainlen value
elif chaintype == 2:
bone1 = AData.bones[bone]
h = ob.getData().bones[bone].head['ARMATURESPACE']
t = ob.getData().bones[bone].tail['ARMATURESPACE']
dirextup = (abs(t[0]-h[0]),abs(t[1]-h[1]),abs(t[2]-h[2]))
direx = max(dirextup)#find direction in which bone y axis points
if boxBones.index(bone) == len(boxBones)-1: #create goal bone and parent Poser goal actor to it
if option == 2:
_t = ob.getData().bones[boxBones[len(boxBones)-2]].tail['ARMATURESPACE']
_h = ob.getData().bones[boxBones[len(boxBones)-2]].head['ARMATURESPACE']
else:
_t = t
_h = h
dirextup2 = (abs(_t[0]-_h[0]),abs(_t[1]-_h[1]),abs(_t[2]-_h[2]))
direx2 = max(dirextup2)#find direction in which bone y axis points
if direx2 == dirextup2[0]: length = max(abs(abs(_t[0])-abs(_h[0]))/2,rootlen/3)
elif direx2 == dirextup2[1]: length = max(abs(abs(_t[1])-abs(_h[1]))/2,rootlen/3)
elif direx2 == dirextup2[2]: length = max(abs(abs(_t[2])-abs(_h[2]))/2,rootlen/3)
_taillen = _t[1]-length
if _t[2] < -0.003: _taillen = -(_taillen/option)#needs work
_tail = Vector(_t[0],_taillen,_t[2])
if direx == dirextup[1]: _tail = Vector(_t[0],_t[1],_taillen)
create_bone(AData, inkyname, Vector(_t[0],_t[1],_t[2]), _tail, 0.0)
AData.bones[inkyname].parent = AData.bones[root]
if option == 2: bone1.parent = AData.bones[inkyname]
if (option == 2 and boxBones.index(bone) == len(boxBones)-2) or\
(option == 1 and boxBones.index(bone) == len(boxBones)-1): #point last link in Blender chain at goal bone
const[Constraint.Settings.BONE] = inkyname
if (option == 2 and boxBones.index(bone) < len(boxBones)-2) or\
(option == 1 and boxBones.index(bone) < len(boxBones)-1): #create bend target bones
if direx == dirextup[0]:#bone points on x
bend = max(abs(abs(t[2])-(abs(t[0])-abs(h[0]))/2),rootlen/3)
bend = -bend
bend2 = t[1]
elif direx == dirextup[1]:#bone points on y
if t[2] < -0.003: bend = -max(abs(abs(t[2])-(abs(t[1])-abs(h[1]))/2),rootlen/3)
else: bend = max(abs(abs(t[2])+(abs(t[1])-abs(h[1]))/2),rootlen/3)
bend2 = t[1]
elif direx == dirextup[2]:#bone points on z
bend2 = max(abs(abs(t[1])+(abs(t[2])-abs(h[2]))/2),rootlen/3)
bend = t[2]
create_bone(AData, bone+"_bend", Vector(t[0],bend2,bend), Vector(t[0],t[1],bend*2), 0.0)
AData.bones[bone+"_bend"].parent = AData.bones[root]
const[Constraint.Settings.BONE] = bone + "_bend"
if chaintype == 2: AData.update()
pose.update()
ob.makeDisplayList() #to refresh the bones display
except: error = PupMenu("This feature is only available in versions later than 2.41")
def add_bone(switch):
global root
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
if lastBone != "":
if switch == 2: #are more options needed here? Up-down? Right-left?
side = PupMenu("Place bone in front of or behind active list bone? %t\
|Front (joint bends forward) %x1|Behind (joint bends backward) %x2")
offset = PupFloatInput("Offset:", 0.0, 0.0, 100.0,10,2)
if side == -1: side = 1
if offset == None: offset = 0.0
AData = ob.getData()
pose = ob.getPose()
addname = PupStrInput("Name for new bone:", "new_bone", 25) #get a name for the new bone
if addname == None: addname = "new_bone"
if addname in AData.bones.keys(): addname += "_" + str(len(AData.bones.keys())).rjust(4,"0")
for bone in AData.bones.values(): #find the root bone
if not bone.hasParent():
if bone.hasChildren(): root = bone
break
#most of the following specifies positioning relative to active bones list selection
y_dir = Vector(0,1,0) #Cambo method to find direction in which bone axis points
y_dir = y_dir * pose.bones[lastBone].poseMatrix.rotationPart() # poseMatrix is worldspace
#y_dir = y_dir * AData.bones[bone].matrix['ARMATURESPACE'].rotationPart()
dx,dy,dz = y_dir
dirtup = (abs(dx),abs(dy),abs(dz))
dirtup = list(dirtup)
dirmax = max(dirtup) #Find the dominant axis
bone1 = AData.bones[lastBone]
tail_x = bone1.tail['ARMATURESPACE'][0]
tail_y = bone1.tail['ARMATURESPACE'][1]
tail_z = bone1.tail['ARMATURESPACE'][2]
_len = max(bone1.length/2,root.length/5) #This is arbitrary - set length relative to selection or root
if switch < 2: _head = Vector(tail_x,tail_y,tail_z)
elif switch == 2:
if side == 2: offset = -offset
if dirtup.index(dirmax) < 2: tail_z += root.length * offset
elif dirtup.index(dirmax) == 2: tail_y += root.length * offset
if side == 1:
if dirtup.index(dirmax) < 2: _head = Vector(tail_x,tail_y,tail_z + (_len/2))
elif dirtup.index(dirmax) == 2: _head = Vector(tail_x,tail_y + (_len/2),tail_z)
_len = -_len
elif side == 2:
if dirtup.index(dirmax) < 2: _head = Vector(tail_x,tail_y,tail_z - (_len/2))
elif dirtup.index(dirmax) == 2: _head = Vector(tail_x,tail_y - (_len/2),tail_z)
if dirtup.index(dirmax) == 0: _tail = Vector(tail_x,tail_y,tail_z-_len)
elif dirtup.index(dirmax) == 1: _tail = Vector(tail_x,tail_y,tail_z-_len)
elif dirtup.index(dirmax) == 2: _tail = Vector(tail_x,tail_y-_len,tail_z)
#now add the bone (adapted from epydocs example)
AData.makeEditable()
bone = Armature.Editbone()
if switch == 0: bone.parent = AData.bones[lastBone] #add mere bone
elif switch > 0: bone.parent = AData.bones[root.name] #add IK handling bones
bone.roll = 0.0
bone.head = _head
bone.tail = _tail
AData.bones[addname] = bone
AData.update()
ob.makeDisplayList() #to refresh the bones display
def delete_bones(): #delete selected bones and re-parent their children
global boxBones, lastBone
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature": #if there is an armature selection
if len(boxBones) > 0:
warning = PupMenu("This will delete all selected bones. Proceed? %t|Yes %x1|No %x2")
if warning == 1:
remove_selected(0) #Clear any Ipo curves associated with the bones before deleting
AData2 = ob.getData()
parents = {} #stores a list of all parents of bone
children = {} #stores a list containing immediate children of bone
for bone in boxBones: #gather parent and child data before calling makeEditable
if AData2.bones[bone].hasParent():
parentlist = []
bone1 = bone
while AData2.bones[bone1].parent != None: #Fill the list with the parenting hierarchy
parentlist.append(AData2.bones[bone1].parent.name)
bone1 = AData2.bones[bone1].parent.name
parents[bone] = parentlist
else: parents[bone] = None
if AData2.bones[bone].hasChildren():
childlist = list(child.name for child in AData2.bones[bone].children)
children[bone] = childlist
else: children[bone] = "None"
AData = AData2 #It's silly, but sometimes makeEditable seems to need this....
AData.makeEditable()
for bone in boxBones:
if children[bone] != "None":
for child in children[bone]:
if not child in boxBones: #Don't try to reparent what will be deleted
if parents[bone] != None:
for bone1 in parents[bone]: #Move up the hierarchical chain, find first ancestor not to be deleted
if not bone1 in boxBones:
AData.bones[child].parent = AData.bones[bone1]
break
else: AData.bones[child].clearParent()#Set parent to None if root has been deleted
del AData.bones[bone]
AData.update()
ob.makeDisplayList() #to refresh the bones display
lastBone = "" #clear listbox info (can cause problems with number tab)
boxBones = []
def scale_bones(): #Re-scale selected bones by specified value
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
if boxBones != []:
scale = PupFloatInput("Scaling:", 1.0, 0.001, 1000.0,10,2)
if scale == None: scale = 1.0 #pup inputs can return None
AData = ob.getData()
AData.makeEditable()
for bone in boxBones:
bone = AData.bones[bone]
_head = bone.head
_tail = bone.tail
bone.head = Vector(_head[0]*scale, _head[1]*scale, _head[2]*scale)
bone.tail = Vector(_tail[0]*scale, _tail[1]*scale, _tail[2]*scale)
AData.update()
ob.makeDisplayList() #to refresh the bones display
else: error = PupMenu("No bones list selection.")
def move_bones(): #translates bones list selections as a group
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
if boxBones != []:
xval = Create(0.0)
yval = Create(0.0)
zval = Create(0.0)
block = []
block.append(("X translation:",xval,-100.000,100.000,"Warning: this will alter your armature!"))
block.append(("Y translation:",yval,-100.000,100.000,"Warning: this will alter your armature!"))
block.append(("Z translation:",zval,-100.000,100.000,"Warning: this will alter your armature!"))
moveVal = PupBlock("Translate selected bones", block)
if moveVal == 1:
AData = ob.getData()
AData.makeEditable()
for bone in boxBones:
bone = AData.bones[bone]
_head = bone.head #editbones are armaturespace
_tail = bone.tail
bone.head = Vector(_head[0]+xval.val, _head[1]+yval.val, _head[2]+zval.val)
bone.tail = Vector(_tail[0]+xval.val, _tail[1]+yval.val, _tail[2]+zval.val)
AData.update()
ob.makeDisplayList() #to refresh the bones display
else: error = PupMenu("No bones list selection.")
def swap_bones(): #flips bones list selections over themselves (swaps head and tail)
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
if boxBones != []:
AData = ob.getData()
AData.makeEditable()
for bone in boxBones:
bone = AData.bones[bone]
hx = bone.head[0]#using same assignment method as move_bones leads to zero-sized deletion errors
hy = bone.head[1]
hz = bone.head[2]
tx = bone.tail[0]
ty = bone.tail[1]
tz = bone.tail[2]
bone.head = Vector(tx,ty,tz)
bone.tail = Vector(hx,hy,hz)
AData.update()
ob.makeDisplayList() #to refresh the bones display
else: error = PupMenu("No bones list selection.")
def flip_bone_axis(): #flips bones list selections over selected axes, as a group
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
if boxBones != []:
xval = Create(0)
yval = Create(0)
zval = Create(0)
block = []
block.append(("Flip X axis",xval,"Warning: this will alter your armature!"))
block.append(("Flip Y axis",yval,"Warning: this will alter your armature!"))
block.append(("Flip Z axis",zval,"Warning: this will alter your armature!"))
moveVal = PupBlock("Axes to flip:", block)
if moveVal == 1:
AData = ob.getData()
AData.makeEditable()
for bone in boxBones:
bone = AData.bones[bone]
hx = bone.head[0]
hy = bone.head[1]
hz = bone.head[2]
tx = bone.tail[0]
ty = bone.tail[1]
tz = bone.tail[2]
if xval.val == 1:
hx = -hx
tx = -tx
if yval.val == 1:
hy = -hy
ty = -ty
if zval.val == 1:
hz = -hz
tz = -tz
bone.head = Vector(hx,hy,hz)
bone.tail = Vector(tx,ty,tz)
AData.update()
ob.makeDisplayList() #to refresh the bones display
else: error = PupMenu("No bones list selection.")
def connect_bones(switch): #connect bone head with parent tail or active bones list selection tail
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
if len(boxBones) > 1:
AData = ob.getData()
if switch == 0:
_tail = AData.bones[lastBone].tail['ARMATURESPACE']
elif switch == 1:
parents = []
for bone in boxBones:
if AData.bones[bone].hasParent():
par_tail = AData.bones[bone].parent.tail['ARMATURESPACE']
parents.append(par_tail)
else: parents.append(AData.bones[bone].head['ARMATURESPACE'])
AData.makeEditable()
for count, bone in enumerate(boxBones):
if bone != lastBone:
if switch == 0: AData.bones[bone].head = _tail
elif switch == 1:
AData.bones[bone].head = parents[count]
AData.update()
ob.makeDisplayList()
def align_bones(): #align bones list selections with the axis along which they predominantly point
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
if len(boxBones) != 0:
AData = ob.getData()
AData.makeEditable()
for bone in boxBones:
y_dir = Vector(0,1,0) #Cambo method to find direction in which bone axis points
bone = AData.bones[bone]
hx = bone.head[0]
hy = bone.head[1]
hz = bone.head[2]
tx = bone.tail[0]
ty = bone.tail[1]
tz = bone.tail[2]
y_dir = y_dir * bone.matrix.rotationPart()
xd,yd,zd = y_dir
y_dir = [abs(xd),abs(yd),abs(zd)]
dirmax = max(y_dir) #this may get funny with 45 degree angles....
if y_dir.index(dirmax) == 0: bone.tail = Vector(tx,hy,hz)
elif y_dir.index(dirmax) == 1: bone.tail = Vector(hx,ty,hz)
elif y_dir.index(dirmax) == 2: bone.tail = Vector(hx,hy,tz)
AData.update()
ob.makeDisplayList()
def align_chains(): #align bones with parents, straightening chains
global boxBones, boxList
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
if len(boxBones) != 0:
hval = Create(0)
tval = Create(0)
block = []
block.append(("head to parent tail",hval,"Warning: this will alter your armature!"))
block.append(("tail to parent tail",tval,"Warning: this will alter your armature!"))
moveVal = PupBlock("Align bones:", block)
if moveVal == 1:
if flipDict == {}: flip()
AData = ob.getData()
parents = []
boxBones = sort_hierarchy(boxBones)
for bone in boxBones:
if AData.bones[bone].hasChildren(): numchild = len(AData.bones[bone].children)
else: numchild = 0
if AData.bones[bone].hasParent():
par_tail = (AData.bones[bone].parent.name,AData.bones[bone].parent.tail['ARMATURESPACE'],
numchild)
else:
par_tail = ("None",AData.bones[bone].head['ARMATURESPACE'],numchild)
parents.append(par_tail)
AData.makeEditable()
for count, bone in enumerate(boxBones):
y_dir = Vector(0,1,0)
bone = AData.bones[bone]
_tail = parents[count][1]
hx = bone.head[0]
hy = bone.head[1]
hz = bone.head[2]
tpx = _tail[0]
tpy = _tail[1]
tpz = _tail[2]
y_dir = y_dir * bone.matrix.rotationPart()
xd,yd,zd = y_dir
y_dir = [abs(xd),abs(yd),abs(zd)]
dirmax = max(y_dir)
if y_dir.index(dirmax) == 0:
if parents[count][2] == 1:
if hval.val == 1: bone.head = Vector(hx,tpy,tpz)
if tval.val == 1: bone.tail = Vector(bone.tail[0],tpy,tpz)
elif y_dir.index(dirmax) == 1:
if (parents[count][0] in flipDict and bone.name in flipDict) or\
(parents[count][0] not in flipDict and bone.name not in flipDict):
if hval.val == 1: bone.head = Vector(tpx,hy,tpz)
if tval.val == 1: bone.tail = Vector(tpx,bone.tail[1],tpz)
elif y_dir.index(dirmax) == 2:
if parents[count][2] == 1:
if hval.val == 1: bone.head = Vector(tpx,tpy,hz)
if tval.val == 1: bone.tail = Vector(tpx,tpy,bone.tail[2])
AData.update()
ob.makeDisplayList()
boxList = sort_hierarchy(boxList)
#------Other bone functions------------------------------------------------------------------
def sort_hierarchy(bonelist): #produces a hierarchically sorted bone list
global listTree
if len(bonelist) != 0: #if the submitted list isn't blank
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
AData = ob.getData()
templist = []
levellist = []
listTree = []
level = 0
for bone in AData.bones.keys(): #a separate loop to force these to be first in list
if not AData.bones[bone].hasParent():
templist.append(bone)
levellist.append(level)
templist.sort()
for bone in templist:
if AData.bones[bone].hasChildren():
children = sorted(child.name for child in AData.bones[bone].children)
children.reverse()
for child in children:
level = levellist[templist.index(AData.bones[child].parent.name)]+1
if not child in templist:
if not AData.bones[child].parent.name in templist:
templist.append(child)
levellist.append(level)
else:
templist.insert(templist.index(AData.bones[child].parent.name)+1, child)
levellist.insert(templist.index(AData.bones[child].parent.name)+1, level)
templist2 = []
for bone in templist: #templist contains all bones. Now screen against submitted list
if bone in bonelist:
templist2.append(bone)
if AData.bones[bone].parent:
level = levellist[templist.index(bone)]
#listTree.append((AData.bones[bone].parent.name,level))
listTree.append(level)
#else: listTree.append(("None",0))
else: listTree.append(0)
#print zip(templist2,listTree)
return templist2
else: return [] #Need to return a blank list if one was submitted as an argument
def bone_search():
global boxBones, lastBone
name = PupStrInput("Name of bone:", "", 50) #get a name for the new bone
if name != None and name != "":
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
try:
pose = ob.getPose()
if name[-1:] == "*": prog = re.compile(name[:-1],re.I) #wildcards
else: prog = re.compile(name)#case sensitive
for bone in pose.bones.keys():
result = prog.search(bone)
if name == bone or result != None:
pose.bones[bone].sel = TRUE
if bone not in boxBones: boxBones.append(bone)
lastBone = bone
pose.update()
except:
pass #pose bone .sel is not available in current (Sept 06) formal release of 242, only CVS
def bone_grab():
if Object.GetSelected() != []: #if there is a current selection in 3d view
ob = Object.GetSelected()[0]
if ob.getType() == "Armature": #if there is an armature selection
try:
pose = ob.getPose()
if len(boxBones) != 0:
for bone in boxBones:
pose.bones[bone].sel = 1 - pose.bones[bone].sel #toggle selection
elif len(boxBones) == 0: #clear selections if box is empty
for bone in pose.bones.values():
bone.sel = FALSE
pose.update()
ob.makeDisplayList()
Blender.Redraw()
except:
pass #pose bone .sel is not available in current (Sept 06) formal release of 242, only CVS
##----------------Poser-specific functions----------------------------------
#-------Poser pose (.pz2 or .p2z) export functions-------------------------------------------------
def save_poser(ob, pose):
global NoRollPose, PoserConvert, curFrame
poseText = ""
temp = NoRollPose
NoRollPose = 1 #Poser poses are restMatrix type
frames = [] #the list of frames to export
runAnim = 0 #armature can have an action with no keyframes, so we need a control variable
curframe = Blender.Get('curframe')#local - used to advance frames for posematrix use
curFrame = Blender.Get('curframe')#global - used to reset start frame at end of run
#thighLen = ""
if ob.getAction():
act = ob.getAction()
if len(act.getAllChannelIpos()) > 0: #if there are keyframes
runAnim = 1
if saveBatch == 1 and runAnim == 1: #animation
if batchFrames == 0: #not using frames list screening
framelen = PupIntInput("animation length:", 1, Blender.Get('staframe'), Blender.Get('endframe'))
if framelen == None: framelen = 1
frames = list(i+curframe for i in range(framelen))
elif batchFrames == 1: #with frames list screening
if len(boxFrames) > 0:
frames_sort()
frames = list(int(i) for i in boxFrames)
elif saveBatch == 0 or runAnim == 0: #single frame; only current
frames.append(curframe)
poseText += "{\nversion\n\t{\n\tnumber\n\t}\n\n" #version header
if saveBatch == 1 and runAnim == 1:
poseText += "clearFigureKeys %i\n\n" %(frames[len(frames)-1])#clears the keyframes in Poser
bonelist = pose.bones.keys()
#bonelist.sort() #alphabetical sort
bonelist = sort_hierarchy(bonelist) #get the hierarchical setup
AData = ob.getData()
for bone in AData.bones.keys(): #Poser pose format omits root part - not necessary
if not AData.bones[bone].hasParent():
if AData.bones[bone].hasChildren():
bonelist.remove(bone)
#if AData.bones[bone].name == "rThigh": #only mess with thighLength if this is a Poser-derived armature
# thighLen = "thighLength %f\n\n" %(AData.bones[bone].length)
# poseText += thighLen
for bone in bonelist:
if JPdict == {} or len(JPdict.keys()) != len(AData.bones.keys()):
axis_direction(AData, 0) #get generic rotation orders for bones without defined joint orders
order = JPdict[bone] #define joint rotation order
rxlist = [] #we only need to worry about six parameters
rylist = [] #these are lists for those parameters
rzlist = []
txlist = []
tylist = []
tzlist = []
curvelist = [] #stores the Ipo curves
savePart = save_part(bone) #screening check
if savePart == 1:
if saveBatch == 1 and runAnim == 1: #act is defined when runAnim is set, above
if bone in act.getAllChannelIpos(): #if the bone is keyframed
ipo = act.getChannelIpo(bone) #gather together all the key data for the bone
icu_locx = ipo[Ipo.PO_LOCX] #1
icu_locy = ipo[Ipo.PO_LOCY] #2
icu_locz = ipo[Ipo.PO_LOCZ] #3
icu_quatw = ipo[Ipo.PO_QUATW] #25
icu_quatx = ipo[Ipo.PO_QUATX] #26
icu_quaty = ipo[Ipo.PO_QUATY] #27
icu_quatz = ipo[Ipo.PO_QUATZ] #28
curvelist = [icu_locx, icu_locy, icu_locz, icu_quatw, icu_quatx, icu_quaty, icu_quatz]
for framenum in frames: #loop through the frames to reorganize all the frame data
lx = 0
ly = 0
lz = 0
qw = 0
qx = 0
qy = 0
qz = 0
if saveBatch == 1 and runAnim == 1: #get the data for each frame if we're handling multiple frames
for count, icu in enumerate(curvelist):
if icu != None and len(icu.bezierPoints) > 0:#We might get errors is some parameters lack curves....
if count == 0: lx = icu[framenum]
elif count == 1: ly = icu[framenum]
elif count == 2: lz = icu[framenum]
elif count == 3: qw = icu[framenum]
elif count == 4: qx = icu[framenum]
elif count == 5: qy = icu[framenum]
elif count == 6: qz = icu[framenum]
quat = Quaternion(qw,qx,qy,qz) ##compile the quaternion rotations
elif saveBatch == 0 or runAnim == 0:
quat = pose.bones[bone].quat #if we're only doing current frame, get the current pose values
lx,ly,lz = pose.bones[bone].loc
if IKPose == 1: #posematrix needs to get data from current frame - this is very bloody slow!!!!
Blender.Set('curframe',framenum)
quat = save_convert(ob, pose, pose.bones[bone], quat) #Do the restmatrix/posematrix conversions
#qx,qy,qz = quat.toEuler()#.unique() #convert the quaternion to euler for Poser (unique doesn't help)
qx,qy,qz = quat_to_euler2(quat)#alternate conversion returns better results - but still not quite right....
rxlist.append(qx) #append the gathered animation data to the proper list for each parameter
rylist.append(qy)
rzlist.append(qz)
txlist.append(lx)
tylist.append(lz)#flip y and z... not really sure if this helps anything, or not....
tzlist.append(ly)
params1 = [] #a list to store Poser parameter names
params2 = [] #a list to store the corresponding list of animation data (created above)
for axis in order: #order is a list containing the joint rotation order
if axis == 0: #fill the parameters lists - this is where we specify joint rotation order
params1.append("rotateX xrot")
params2.append(rxlist)
elif axis == 1:
params1.append("rotateY yrot")
params2.append(rylist)
elif axis == 2:
params1.append("rotateZ zrot")
params2.append(rzlist)
params1.append("translateX xtran")
params1.append("translateY ytran")
params1.append("translateZ ztran")
params2.append(txlist)
params2.append(tylist)
params2.append(tzlist)
poseText += "actor %s:1\n\t{\n\tchannels\n\t\t{\n" %(bone) #actor line
for channel, param in zip(params1,params2): #correlate the two parameters lists
poseText += "\t\t%s\n\t\t\t{\n\t\t\tkeys\n\t\t\t\t{\n" %(channel) #channel line and keys
for value in param: #identify parameters with all zero keys - only export the first key for these
if round(value,6) <> 0.000000:
allZero = 0
break
else: allZero = 1
for key, value in enumerate(param):
if allZero == 0:
if saveBatch == 1 and runAnim == 1: frame = frames[key]-1#multi-frame saves to frame index
elif saveBatch == 0 or runAnim == 0: frame = key #single frame should save to zero
elif allZero == 1 and key == 0: frame = key
poseText += "\t\t\t\tk %i %f\n" %(frame, value) #add each keyframe, value pair - double spaces between!
#if allZero == 0:
# poseText += "\t\t\t\tsl 1\n\t\t\t\tspl\n\t\t\t\tsm\n" #spline interpolation listings
#elif allZero == 1: break
if allZero == 1: break
poseText += "\t\t\t\t}\n" #close all the brackets
poseText += "\t\t\t}\n"
poseText += "\t\t}\n\t}\n\n" #(That's a lotta brackets....)
poseText += "figure\n\t{\n\t}\n" #figure section at end of pose
poseText += "\nversion\n\t{\n\tnumber\n\t}\n\n" #IK section repeats header,clearfigure, and figure blocks
if saveBatch == 1 and runAnim == 1: poseText += "clearFigureKeys %i\n\n" %(frames[len(frames)-1])
#if thighLen != "": poseText += thighLen
poseText += "figure\n\t{\n\t}\n}"
NoRollPose = temp #reset restMatrix save value
if IKPose == 1: Blender.Set('curframe',curFrame)#restore the start frame if posematrix has had us walk the frames
return poseText
JPdict = {}
def axis_direction(AData, switch): #find generic joint rotation orders for Poser export
#Generic orders are based on a sampling of applied rotation orders in Poser figures. When building a Poser figure,
#'bend' apparently s/b the axis least likely to receive 90 degrees rotation - but that standard rarely seems to have
#been followed with existing figs. Here, bend is the common bend axis in most figures I examined.
for bone in AData.bones.keys():
if switch == 1 or bone not in JPdict:
bonemat = AData.bones[bone].matrix['ARMATURESPACE'].rotationPart()
direx = ["X","Y","Z"]
y_dir = [bonemat[1][0],bonemat[1][1],bonemat[1][2]]
y_dir2 = list(abs(i) for i in y_dir)
y_dir3 = max(y_dir2) #direction in which bone y axis points
direx[0] = y_dir2.index(y_dir3)
if direx[0] == 0: #x-pointing bone
direx[1] = 1 #assume most humanoid rigs will have y-bend for x-twist bones
elif direx[0] == 1: #y-pointing
direx[1] = 0 #assume most humanoid rigs will have x-bend for y-twist bones
elif direx[0] == 2: #z-pointing
direx[1] = 1 #assume most humanoid rigs will have y-bend for z-twist bones
for i in range(3): #fill direx[2] with whichever axis is left unaccounted for
if not i in direx:
direx[2] = i
break
JPdict[bone] = direx
#------quaternion conversion---------------------------------------------------------
def quat_to_euler2(q1):
#adapted from Maths - Conversion Quaternion to Euler - Martin Baker
#http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm
sqw = q1.w * q1.w
sqx = q1.x * q1.x
sqy = q1.y * q1.y
sqz = q1.z * q1.z
unit = sqx + sqy + sqz + sqw # if normalised is one, otherwise is correction factor
test = (q1.x * q1.y) + (q1.z * q1.w)
#test = (q1.x*q1.z) - (q1.y*q1.w)
if test > 0.499*unit:# and test < 0.501*unit: # singularity at north pole
#print "north", test, unit, 0.498*unit, 0.501*unit
bank = 2 * math.atan2(q1.x,q1.w)
attitude = math.pi/2
heading = 0
elif test < -0.499*unit:# and test > -0.501*unit: # singularity at south pole
#print "south", test, unit, -0.498*unit, -0.501*unit
bank = -2 * math.atan2(q1.x,q1.w)
attitude = -math.pi/2
heading = 0
else:
heading = math.atan2(2 * q1.y * q1.w-2 * q1.x * q1.z , sqx - sqy - sqz + sqw)
attitude = math.asin(2 * test/unit)
bank = math.atan2(2 * q1.x * q1.w-2 * q1.y * q1.z , -sqx + sqy - sqz + sqw)
return r2d(bank),r2d(heading),r2d(attitude)
#return r2d(heading),r2d(attitude),r2d(bank)
#-----joint order functions----------------------------------------------
def change_JP_order():
if len(boxBones) > 0:
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
if JPdict == {}:
axis_direction(ob.getData(), 1)
if JPdict != {}:
menu = PupMenu("New joint rotation order for bones list selections: %t|XYZ %x1|XZY %x2|YXZ %x3\
|YZX %x4|ZXY %x5|ZYX %x6|Cancel %x7")
if menu != 7:
if menu == 1: order = [0,1,2]
elif menu == 2: order = [0,2,1]
elif menu == 3: order = [1,0,2]
elif menu == 4: order = [1,2,0]
elif menu == 5: order = [2,0,1]
elif menu == 6: order = [2,1,0]
for bone in boxBones:
JPdict[bone] = order
else: error = PupMenu("You must make an armature selection to use this function.")
else: error = PupMenu("You must make one or more bones listbox selections to use this function.")
def export_JP_order(save_name): #
if save_name != "":
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
temptext = ""
pose = ob.getPose()
if JPdict == {}: axis_direction(ob.getData(), 1)
for bone in pose.bones.keys():
rotOrder = ""
order = JPdict[bone]
for i in order:
if i == 0: rotOrder += "X"
elif i == 1: rotOrder += "Y"
elif i == 2: rotOrder += "Z"
temptext += "%s,%s,%s,%s\n" %(bone,rotOrder[0],rotOrder[1],rotOrder[2])
save_name = os.path.splitext(save_name)[0] + ".txt"
if sys.exists(save_name) == 1:
warning = PupMenu("Overwrite file %s? %%t|Yes %%x1|No %%x2" %(sys.basename(save_name))) #Don't overwrite
if sys.exists(save_name) != 1 or warning != 2:
jpsave = open(save_name,"w")
jpsave.write(temptext)
jpsave.close()
def save_JP_order(save_name): #saves a rotation order text file straight from JP dict
if save_name != "":
if JPdict != {}:
temptext = ""
for key, value in JPdict.iteritems():
rotOrder = ""
for i in value:
if i == 0: rotOrder += "X"
elif i == 1: rotOrder += "Y"
elif i == 2: rotOrder += "Z"
temptext += "%s,%s,%s,%s\n" %(key,rotOrder[0],rotOrder[1],rotOrder[2])
save_name = os.path.splitext(save_name)[0] + ".txt"
if sys.exists(save_name) == 1:
warning = PupMenu("Overwrite file %s? %%t|Yes %%x1|No %%x2" %(sys.basename(save_name))) #Don't overwrite
if sys.exists(save_name) != 1 or warning != 2:
jpsave = open(save_name,"w")
jpsave.write(temptext)
jpsave.close()
def import_JP_order(file_name): #imports a rotation order text file saved by this script
if file_name != "":
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
pose = ob.getPose()
temptext = open(file_name,"r")
lines = temptext.readlines()
for line in lines:
data = line.rstrip("\n").split(",")
if data[0] in pose.bones.keys(): #Keep dict from being badly updated; would create errors later
rotOrder = []
for i in range(1,4):
if data[i] == "X": rotOrder.append(0)
elif data[i] == "Y": rotOrder.append(1)
elif data[i] == "Z": rotOrder.append(2)
JPdict[data[0]] = rotOrder
temptext.close()
def parse_JP_order(file_name): #parses rotation orders out of a Poser .pz2 or .cr2 file
if file_name != "":
ext = os.path.splitext(file_name)[1]
if ext == ".cr2" or ext == ".pz2" or ext == ".crz" or ext == ".p2z":
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
pose = ob.getPose()
if ext[3] == "2":
temptext = open(file_name,"r")
if ext[3] == "z":
try :
import gzip
temptext=gzip.GzipFile(file_name)
except:
error = Blender.Draw.PupMenu("Could not read .pzz file. Try decompressing and reading as .pz2")
return
lines = temptext.readlines()
name = ""
rotOrder = []
if len(lines) == 1: lines = lines[0].replace("\r","\n").split("\n")#if it's all been read as one line
for line in lines:
line = line.replace("\n","").replace("\t","").replace("\r","").replace(" "," ")
if line.find("actor") != -1:
name = line.split(":")[0].replace("actor ","")
if name != "":
if line.find("xrot") != -1: rotOrder.append(0)
if line.find("yrot") != -1: rotOrder.append(1)
if line.find("zrot") != -1: rotOrder.append(2)
if name != "" and len(rotOrder) == 3:
if name in pose.bones.keys():
JPdict[name] = rotOrder
name = ""
rotOrder = []
temptext.flush()
temptext.close()
if JPdict != {}:
menu = PupMenu("Save rotation data as text file? %t|Yes %x1|No %x2")
if menu == 1: Window.FileSelector(save_JP_order, 'Browse for save location.')
#---------Poser file reading--------------------------------------------------------
#This is called by create_armature(); this merely collects data - create armature makes the rig
def parse_poser_file(file_name): #Build an armature from a .cr2 file, optionally including IK and rotation order save
if file_name != "":
ext = os.path.splitext(file_name)[1]
if ext == ".cr2" or ext == ".crz":
IKask = PupMenu("Add IK to armature? %t|Yes %x1|No %x2|Yes, but omit 'Collar' parts %x3")
startTime = sys.time()#keep track of speed
if ext[3] == "2":
temptext = open(file_name,"r")
if ext[3] == "z":
try :
import gzip
temptext=gzip.GzipFile(file_name)
except:
error = Blender.Draw.PupMenu("Could not read .pzz file. Try decompressing and reading as .pz2")
return
lines = temptext.readlines()
flag = 0
data = []
rotOrder = []
IKlist = []
temp = ["name","h0","h1","h2","t0","t1","t2","None"]
if len(lines) == 1: lines = lines[0].replace("\r","\n").split("\n")#Macs save \r and .crz reading fails to split at \r
for indx,line in enumerate(lines):
line = line.replace("\n","").replace("\t","").replace("\r","").replace(" "," ")
if line.find("actor") != -1 and lines[indx+2].find("name") != -1: #get the bone name
name = line.split(":")[0].replace("actor ","").replace("\t","").replace("\n","").replace("\r","")
temp[0] = name
if line.find("parent") != -1: #get the parent name
if lines[indx+2].find("nonInkyParent") != -1:#if cr2 was saved with IK on
read = lines[indx+2].split(":")[0].replace("\n","").replace("\t","").replace("\r","").split(" ")
else: read = line.split(":")[0].split(" ")
if read[1] != "UNIVERSE": temp[7] = read[1]
else: root = name
if len(rotOrder) < 3: #get the joint rotation order for the actor
if line.find("xrot") != -1: rotOrder.append(0)
if line.find("yrot") != -1: rotOrder.append(1)
if line.find("zrot") != -1: rotOrder.append(2)
if line.find("endPoint") != -1: #get the endPoint data (tail)
read = line.split(" ")
for i in range(1,4): temp[i+3] = read[i]
if line.find("origin") != -1: #get the origin data (head)
read = line.split(" ")
for i in range(1,4): temp[i] = read[i]
flag = 1
#build the IK structure-------------------------------------------------------------
if IKask == 1 or IKask == 3:
if line.find("inkyChain") != -1:
inkyname = line.replace("inkyChain ","").strip()
IKlist = []
num = indx + 4
while lines[num].find("linkWeight") == -1: #fill a list with the IK chain data
if not (IKask == 3 and lines[num].find('Collar') != -1): #omit collars, which don't handle well w/ Blender IK
IKlist.append(lines[num].split(" ")[1].replace("\t","").replace("\n","").replace("\r","").split(":")[0])
num += 1
for chainlen, bone in enumerate(IKlist):
for items in data:
item = items.split(",")
if item[0] == bone:
item[10] = str(chainlen+1) #add chainlen value
if chainlen < len(IKlist)-2: #create bend target bones
dirextup = (abs(float(item[5])-float(item[2])),abs(float(item[6])-float(item[3])),
abs(float(item[7])-float(item[4])))
direx = max(dirextup)#find direction in which bone y axis points
if direx == dirextup[0]:#bone points on x
bend = max(abs(abs(float(item[7]))-(abs(float(item[5]))-abs(float(item[2])))/2),rootlen/3)
bend = -bend
bend2 = float(item[6])
elif direx == dirextup[1]:#bone points on y
bend = max(abs(abs(float(item[7]))+(abs(float(item[6]))-abs(float(item[3])))/2),rootlen/3)
bend2 = float(item[6])
elif direx == dirextup[2]:#bone points on z
bend2 = max(abs(abs(float(item[6]))+(abs(float(item[7]))-abs(float(item[4])))/2),rootlen/3)
bend = float(item[7])
dataline = "%s,0.0,%s,%f,%f,%s,%s,%f,%s,None,0\n" %(bone+"_bend",item[5],bend2,bend,item[5],
item[6],bend*2,root)
data.append(dataline)
item[9] = bone + "_bend"
elif chainlen == len(IKlist)-2: #point last link in Blender chain at goal bone
item[9] = inkyname
elif chainlen == len(IKlist)-1: #create goal bone and parent Poser goal actor to it
item[8] = inkyname
_tail = data[data.index(items)-1].split(",")
if direx == dirextup[0]: length = max(abs(abs(float(_tail[5]))-abs(float(_tail[2])))/2,rootlen/3)
elif direx == dirextup[1]: length = max(abs(abs(float(_tail[6]))-abs(float(_tail[3])))/2,rootlen/3)
elif direx == dirextup[2]: length = max(abs(abs(float(_tail[7]))-abs(float(_tail[4])))/2,rootlen/3)
_taillen = float(_tail[6])-length
dataline = "%s,0.0,%s,%s,%s,%s,%f,%s,%s,None,0\n" %(inkyname,_tail[5],_tail[6],_tail[7],_tail[5],
_taillen,_tail[7],root)
data.append(dataline)
dataline = ""
for param in item:
dataline += str(param)+","
dataline += "\n" #string will be split by commas, so \n after the comma is no problem
data[data.index(items)] = dataline
#end IK----------------------------------------------------
if flag == 1:
if name == root: #line up the root (BODY) with its child (hip - Poser will have only one rootchild)
rootlen = max(abs(float(temp[5])-float(temp[3]))/3,0.2)#0.2 is a value which is scaled for Poser sizes
if temp[7] == root:
dataline = "%s,0.0,%s,%s,%s,%s,%s,%f,None,None,0\n" %(root,temp[1],temp[2],temp[3],temp[1],
temp[2],-rootlen)
data[0] = dataline
dataline = "%s,0.0,%s,%s,%s,%s,%s,%s,%s,None,0\n" %(temp[0],temp[1],temp[2],temp[3],temp[4],
temp[5],temp[6],temp[7])
data.append(dataline)
JPdict[name] = rotOrder #enter rotation orders in our Poser export dict
flag = 0
name = ""
rotOrder = []
temptext.flush()
temptext.close()
finTime = sys.time() - startTime
print "Done reading armature from %s in %f seconds" %(sys.basename(file_name),finTime)
option = PupMenu("Save joint rotation orders for Poser pose export? %t|Yes %x1|No %x2")
if option == 1: Window.FileSelector(save_JP_order, 'Browse for save location.')
return data #passes armature data back to create_armature()
def load_poser_pose(file_name):
global PoserConvert, PoseMat, realTime, askKey, flipbone
if file_name != "":
ext = os.path.splitext(file_name)[1]
if ext == ".pz2" or ext == ".p2z" or ext == ".hd2" or ext == ".hdz": #support pose and hand files
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
startTime = sys.time()#keep track of speed
hand = 0
if ext == ".hd2" or ext == ".hdz":
hand = PupMenu("Load for which hand? %t|Right %x1|Left %x2")
tempval = PoserConvert
PoserConvert = 1
pose = ob.getPose()
if ext[3] == "2":
temptext = open(file_name,"r")
if ext[3] == "z":
try :
import gzip
temptext=gzip.GzipFile(file_name)
except:
error = Blender.Draw.PupMenu("Could not read .p2z file. Try decompressing and reading as .pz2")
return
lines = temptext.readlines()
name = ""
flag = 0
multi = 1
numActors = 0
if len(lines) == 1:
lines = lines[0].replace("\r","\n").split("\n")#Macs save \r and .p2z reading fails to split at \r
for indx,line in enumerate(lines):
line = line.replace("\n","").replace("\t","").replace("\r","").replace(" "," ")
if line.find("clearFigureKeys") != -1: multi = int(line.split(" ")[1])#number of frames, if animated
if line.find("actor") != -1:
name = line.split(":")[0].replace("actor ","") #bone name is Poser internal name minus figure number
xrot = []
yrot = []
zrot = []
xtran = []
ytran = []
ztran = []
if name != "":
if line.find("xrot") != -1: xrot, flag = read_keys(lines, line, indx, xrot, flag)
if line.find("yrot") != -1: yrot, flag = read_keys(lines, line, indx, yrot, flag)
if line.find("zrot") != -1: zrot, flag = read_keys(lines, line, indx, zrot, flag)
if line.find("xtran") != -1: xtran, flag = read_keys(lines, line, indx, xtran, flag)
if line.find("ytran") != -1: ytran, flag = read_keys(lines, line, indx, ytran, flag)
if line.find("ztran") != -1: ztran, flag = read_keys(lines, line, indx, ztran, flag)
if name != "" and flag == 6:
if name in pose.bones.keys():
numActors += 1
#pose screening (from load_pose())----------------
if flipDict == {}: flip()
flipbone = ""
screening = 0
noRot = 0
noTrans = 0
if ExcludePart == 1: screening = Part_Load(pose.bones[name], 0)
if IncludePart == 1: screening = Part_Load(pose.bones[name], 1)
if bonesLoad == 1 and name not in boxBones: screening = 1
if bonesLoad == 2 and name in boxBones: screening = 1
if (ScreenPose == 1 or DupPose == 1) and screening == 0: screening = Screen_Pose(name, pose)
#if (name == "BODY" or numActors == 1) and bodyscale == 0: screening = 1
if HipPose == 1:
if name == "hip" or numActors == 1:
if hipR == 1: noRot = 1
if hipT == 1: noTrans = 1
#if numActors <= 2: print name, numActors, noRot, noTrans, HipPose, bodyscale, screening
if numActors > 1: noTrans = 1 #exclude IK goal translation in poses
#end pose screening---------------------
if screening == 0:
size = Vector(1,1,1)
xr = 0.0
yr = 0.0
zr = 0.0
xt = 0.0
yt = 0.0
zt = 0.0
for i in range(0,multi): #loop through frames, sort out the keys
if noRot == 0:
if len(xrot) > i and xrot[i][0] == i: xr = xrot[i][1]
if len(yrot) > i and yrot[i][0] == i: yr = yrot[i][1]
if len(zrot) > i and zrot[i][0] == i: zr = zrot[i][1]
if noTrans == 0:
if len(xtran) > i and xtran[i][0] == i: xt = xtran[i][1]
if len(ytran) > i and ytran[i][0] == i: yt = ytran[i][1]
if len(ztran) > i and ztran[i][0] == i: zt = ztran[i][1]
quat = Euler(xr,yr,zr)
quat = quat.toQuat()
quat = Poser_Conversion(pose.bones[name], quat, 0) #convert Poser eulers to restmatrix type quat
if hand != 0: #selected hand for .hd2 hand poses
if (hand == 1 and (pref == 1 and name[:lenR] == left) or (suff == 1 and name[-lenR:] == left)) or\
(hand == 2 and (pref == 1 and name[:lenR] == right) or (suff == 1 and name[-lenR:] == right)):
name = flipDict[name]
quat = Flip_Pose2(quat, 0, 1)
if noTrans == 0: loc = Vector(xt,zt,yt) #change axis order - only for tran
else: loc = Vector(0,0,0)
#combinatory modifiers (from load_pose())---------------------
if noRot == 0:
if partPose != 1.0:
quat = Partial_Pose(quat, 0, 0)
loc = Partial_Pose(0, loc, 1)
if MixPose == 1:
quat = Mix_Pose(quat, pose.bones[name], 0, 0)
loc = Mix_Pose(0, pose.bones[name], loc, 1)
#end combo modifiers------------------------------------------
if batchFrames == 0:
time = i + curFrame# - 1 #load relative to current frame
elif batchFrames == 1:
if i < len(boxFrames):
time = int(boxFrames[i])# - 1
if i == 0 and curFrame != time: Blender.Set('curframe', time)#for add_curves() in frame 1
else:
time = int(boxFrames[len(boxFrames)-1]) + (i - (len(boxFrames)) + 1)
if i == 0:#multi == 0: #single frame or first keyframe
bone = pose.bones[name]
bone.loc = loc
bone.quat = quat
bone.size = size
if multi != 1:
add_curves(ob, pose, name, quat, loc, size, time) #multiple frames
flag = 0
name = ""
if DupPose == 1 or MirrorPose == 1 or MirrorBone == 1: #pose symmetry modifiers
tempval2 = PoseMat
tempval3 = askKey
askKey = 2
PoseMat = 0
PoserConvert = 0
realTime = 1
load_pose("")
PoseMat = tempval2
askKey = tempval3
if multi == 1 and askKey != 2: #keyframe single pose
if askKey == 0:
c = PupMenu("Insert pose keys for this frame?%t|Yes%x1|No%x2")
if askKey == 1: c = 1
if c == 1:
for bone in pose.bones.values():
add_curves(ob, pose, bone.name, bone.quat, bone.loc, bone.size, Blender.Get('curframe'))
PoserConvert = tempval
pose.update()
ob.makeDisplayList()
Blender.Redraw()
temptext.flush()
temptext.close()
Blender.Set('curframe', curFrame)#reset current frame
finTime = sys.time() - startTime
#xrot = yrot = zrot = xtran = ytran = ztran = xr = yr = zr = xt = yt = zt = 0.0
print "Done loading %i frames for %i bones in %f seconds" %(multi, numActors, finTime)
def read_keys(lines, line, indx, paramlist, flag): #reads keyframe ('k') lines in a Poser pose
num = indx + 4
while lines[num].find("}") == -1:
if lines[num].find("k ") != -1:
read = lines[num].replace("\n","").replace("\r","").replace("\t","").replace(" "," ").split(" ")
param = (int(read[1]), float(read[2]))
paramlist.append(param)
num += 1
flag += 1
return (paramlist, flag)
##---------------Numerical entry-----------------------------------------
#Results of rounding a float are not as controlled as one would like. The decimal module might be a better
#tool for handling controlled rounding, but it is not part of the standard BPy package....
def entry_values():
#Sets the dial values for the numerical tab
global _tran_x, _tran_y, _tran_z, _rot_x, _rot_y, _rot_z, _size_x, _size_y, _size_z, \
_boneroll_num, _head_x, _head_y, _head_z, _tail_x, tail_y, _tail_z, lastBone, lastLen, \
boxBones
if lastBone != "":
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
AData = ob.getData()
pose = ob.getPose()
bone1 = AData.bones[lastBone]
lastLen = bone1.length #Bone length is used to configure loc dial sensitivity
bone = pose.bones[lastBone]
if uniqueRot == 1: rot = bone.quat.toEuler().unique() #option to use unique to avoid gimbal lock
elif uniqueRot == 0: rot = bone.quat.toEuler()
_tran_x.val = round(bone.loc[0],6) #control value inputs with rounding
_tran_y.val = round(bone.loc[1],6)
_tran_z.val = round(bone.loc[2],6)
_rot_x.val = round(rot[0],6)
_rot_y.val = round(rot[1],6)
_rot_z.val = round(rot[2],6)
_size_x.val = round(bone.size[0],6)
_size_y.val = round(bone.size[1],6)
_size_z.val = round(bone.size[2],6)
_boneroll_num.val = round(bone1.roll['ARMATURESPACE'],6)
_head_x.val = round(bone1.head['ARMATURESPACE'][0],6)
_head_y.val = round(bone1.head['ARMATURESPACE'][1],6)
_head_z.val = round(bone1.head['ARMATURESPACE'][2],6)
_tail_x.val = round(bone1.tail['ARMATURESPACE'][0],6)
_tail_y.val = round(bone1.tail['ARMATURESPACE'][1],6)
_tail_z.val = round(bone1.tail['ARMATURESPACE'][2],6)
if BonesTabTog == 1:
boxBones = []
boxBones.append(lastBone) #Restrict bones list selections to one when in numerical mode
def numerical_entry():
global _tran_x, _tran_y, _tran_z, _rot_x, _rot_y, _rot_z, _size_x, _size_y, _size_z#, myKeys, keyType, boxBones
#Applies pose mode dial entries returned by the numerical tab
if lastBone != "":
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
pose = ob.getPose()
bone = pose.bones[lastBone]
if uniqueRot == 1: rot = bone.quat.toEuler().unique()
elif uniqueRot == 0: rot = bone.quat.toEuler()
if round(_tran_x.val,6) != round(bone.loc[0],6): #locX
loc = round(_tran_x.val,6), bone.loc[1], bone.loc[2]
bone.loc = Vector(loc)
elif round(_tran_y.val,6) != round(bone.loc[1],6): #locY
loc = bone.loc[0], round(_tran_y.val,6), bone.loc[2]
bone.loc = Vector(loc)
elif round(_tran_z.val,6) != round(bone.loc[2],6): #locZ
loc = bone.loc[0], bone.loc[1], round(_tran_z.val,6)
bone.loc = Vector(loc)
elif round(_rot_x.val,6) != round(rot[0],6): #rotX
quat = round(_rot_x.val,6), rot[1], rot[2]
if uniqueRot == 1: bone.quat = Euler(quat).unique().toQuat()
if uniqueRot == 0: bone.quat = Euler(quat).toQuat()
elif round(_rot_y.val,6) != round(rot[1],6): #rotY
quat = rot[0], round(_rot_y.val,6), rot[2]
if uniqueRot == 1: bone.quat = Euler(quat).unique().toQuat()
if uniqueRot == 0: bone.quat = Euler(quat).toQuat()
elif round(_rot_z.val,6) != round(rot[2],6): #rotZ
quat = rot[0], rot[1], round(_rot_z.val,6)
if uniqueRot == 1: bone.quat = Euler(quat).unique().toQuat()
if uniqueRot == 0: bone.quat = Euler(quat).toQuat()
elif round(_size_x.val,6) != round(bone.size[0],6): #scaleX
size = round(_size_x.val,6), bone.size[1], bone.size[2]
bone.size = Vector(size)
elif round(_size_y.val,6) != round(bone.size[1],6): #scaleY
size = bone.size[0], round(_size_y.val,6), bone.size[2]
bone.size = Vector(size)
elif round(_size_z.val,6) != round(bone.size[2],6): #scaleZ
size = bone.size[0], bone.size[1], round(_size_z.val,6)
bone.size = Vector(size)
if myKeys == 1: #Add keyframe
bonelist = [] #Note: 2 and 3 will be the same if bones list is open in dual mode.
if keyType == 1: bonelist = pose.bones.values()
elif keyType == 2: bonelist = list(pose.bones[bone] for bone in boxBones)
elif keyType == 3: bonelist.append(pose.bones[lastBone])
for bone in bonelist:
add_curves(ob, pose, bone.name, bone.quat, bone.loc, bone.size, Blender.Get('curframe'))
pose.update()
ob.makeDisplayList()
entry_values() #refresh the listings
Blender.Redraw()
def numerical_edit():
global _boneroll_num, _head_x, _head_y, _head_z, _tail_x, tail_y, _tail_z, lastBone
#Applies edit mode dial entries from numerical tab
if lastBone != "":
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
AData = ob.getData()
bone1 = AData.bones[lastBone]
roll1 = bone1.roll['ARMATURESPACE']
head1 = bone1.head['ARMATURESPACE']
tail1 = bone1.tail['ARMATURESPACE']
AData2 = AData #makeEditable seems to require this odd step sometimes....
AData2.makeEditable()
bone1 = AData2.bones[lastBone] #makeEditable() has a real #$%@*!! problem....
if round(_boneroll_num.val,6) != round(roll1,6): #roll
bone1.roll = round(_boneroll_num.val,6)
elif round(_head_x.val,6) != round(head1[0],6): #head x
head = round(_head_x.val,6), head1[1], head1[2]
bone1.head = Vector(head)
elif round(_head_y.val,6) != round(head1[1],6): #head y
head = head1[0], round(_head_y.val,6), head1[2]
bone1.head = Vector(head)
elif round(_head_z.val,6) != round(head1[2],6): #head z
head = head1[0], head1[1], round(_head_z.val,6)
bone1.head = Vector(head)
elif round(_tail_x.val,6) != round(tail1[0],6): #tail x
tail = round(_tail_x.val,6), tail1[1], tail1[2]
bone1.tail = Vector(tail)
elif round(_tail_y.val,6) != round(tail1[1],6): #tail y
tail = tail1[0], round(_tail_y.val,6), tail1[2]
bone1.tail = Vector(tail)
elif round(_tail_z.val,6) != round(tail1[2],6): #tail z
tail = tail1[0], tail1[1], round(_tail_z.val,6)
bone1.tail = Vector(tail)
AData.update()
ob.makeDisplayList()
entry_values() #refresh the listings
Blender.Redraw()
def zero_entries(): #Zero the buttons
global _tran_x, _tran_y, _tran_z, _rot_x, _rot_y, _rot_z, _size_x, _size_y, _size_z, \
_boneroll_num, _head_x, _head_y, _head_z, _tail_x, tail_y, _tail_z
_tran_x.val = 0.0000
_tran_y.val = 0.0000
_tran_z.val = 0.0000
_rot_x.val = 0.0000
_rot_y.val = 0.0000
_rot_z.val = 0.0000
_size_x.val = 0.0000
_size_y.val = 0.0000
_size_z.val = 0.0000
_head_x.val = 0.0000
_head_y.val = 0.0000
_head_z.val = 0.0000
_tail_x.val = 0.0000
_tail_y.val = 0.0000
_tail_z.val = 0.0000
_boneroll_num.val = 0.0
##---------------File browsing (Menu and listbox)------------------------------------------------
#----Fills the poseList for menu and listbox browsing, fills loadlist for menu-------
def list_browser(path):
global LibPath, loadlist, poseList, poseStart, posePages, poseLimit, foldTog, boxList
poseList = []
puplist = ""
if LibPath == "": # or ListTabTog == 1:
poseStart = 0
posePages = 0
if os.path.exists(sys.dirname(path)): path = sys.dirname(path)
else: path = sys.expandpath('//')
LibPath = path
for posefile in os.listdir(LibPath):
ext = os.path.splitext(posefile)[1]
if ext == ".blend" or ext == ".bPose" or\
ext == ".pz2" or ext == ".p2z" or ext == ".hd2" or ext == ".hdz":
poseList.append(sys.basename(posefile))
if poseList == []:
if ListTabTog == 0:
nothing = PupMenu("No files found %t|Please browse to select a different folder.")
LibPath = ""
if ListTabTog == 1:
poseList.append("No poses found.")
poseList.append("Select a new folder.")
if ListTabTog == 1: boxList = poseList #Fill the listbox to pass to gui
if poseList != []:
poseList.sort()
poseTotal = len(poseList) #Total number of poses in folder
poseSplit = int(math.ceil(poseTotal/poseLimit)) #Number of split listings
path = os.path.split(path)[1]
if poseTotal > poseLimit: path += "(%i)" %(posePages + 1)
puplist += "Poses in: %s %%t|Browse for a new folder %%x1|" %(path)
for i in range(poseStart, min(poseStart + poseLimit, poseTotal)):
puplist += "%s %%x%i|" %(sys.basename(poseList[i]), i + 2)
if i == poseStart + poseLimit - 1 or i == poseTotal -1:
if poseTotal > poseLimit:
if poseStart/poseLimit < poseSplit:
puplist += "Forward (%i more poses) %%x%i" %(poseTotal-(poseStart + poseLimit),poseTotal + 10)
if poseStart == 0:
puplist += "|List part %i of %i %%x%i" %(posePages + 1, poseSplit + 1, poseTotal + 11)
if poseStart > 0: puplist += "|Back (%i previous poses) %%x%i" %(poseStart, poseTotal + 11)
if posePages == poseSplit:
puplist += "List part %i of %i %%x%i" %(posePages + 1, poseSplit + 1, poseTotal + 10)
puplist += "|Back (%i previous poses) %%x%i" %(poseStart, poseTotal + 11)
loadlist = puplist
#-----Menu browser file loading--------------------
def list_select(selected):
global poseStart, LibPath, loadlist, posePages, poseLimit, boxList
poseTotal = len(poseList)
poseSplit = int(math.ceil(poseTotal/poseLimit))
pageTotal = min((posePages + 1) * poseLimit, poseTotal)
if selected == 1: #One is always 'browse for new folder'
loadlist = "Pose list browser %t|Browse for a new folder %x1"
LibPath = ""
Window.FileSelector(list_browser, 'Locate pose folder:')
if selected > poseTotal + 5: #Set arbitrarily high
if selected == poseTotal + 10 and posePages != poseSplit: #Total plus 10 is always forward
poseStart += poseLimit
posePages += 1
if selected == poseTotal + 11 and poseStart != 0: #Total plus 11 is always back
poseStart -= poseLimit
posePages -= 1
LibPath = sys.join(LibPath, sys.basename(Blender.Get('filename')))
list_browser(LibPath)
if selected > 1 and selected <= pageTotal + 1 and selected < poseTotal + 2:
filepath = sys.join(LibPath, poseList[selected - 2])
#load_pose(filepath)
if sys.exists(filepath) == 1:
ext = os.path.splitext(filepath)[1]
if ext == ".pz2" or ext == ".p2z" or ext == "hd2" or ext == ".hdz": load_poser_pose(filepath)
else:
if openBatch == 0: load_pose(filepath) #single pose
elif openBatch == 1: open_batch(filepath) #Animated batch support
print "Menu browse file:", filepath
else: error = PupMenu("Error %t|Selected path is not valid.")
#print selected
#-----Folder browsing with listbox-------------------------
def folder_browse(path):
global LibPath, poseList
poseList = []
if path == "": path = sys.expandpath('//') #Start in current .blend folder
if path.endswith(sys.sep) and path != os.path.splitdrive(path)[0] + sys.sep: path = path[:-1] #Strip trailing sep symbol
LibPath = path
for folder in os.listdir(LibPath):
if sys.exists(sys.join(LibPath, folder)) == 2:
poseList.append(sys.basename(folder))
if poseList != []: poseList.sort()
poseList.insert(0,"Up a level")
#print "folder", LibPath, poseList
#---------search for poses in folder (pose tab search button)----------------------
def pose_search():
global boxPoses, batchLoad, openBatch, LibTog, saveBatch, OptPose
name = PupStrInput("Name of pose:", "", 50) #get a name for the new bone
if name != None and name != "":
if LibPath != "" and poseList != []: #if a folder is selected
boxPoses = []
if name[-1:] == "*": prog = re.compile(name[:-1],re.I) #wildcards
else: prog = re.compile(name)#case sensitive
for pose in poseList:
result = prog.search(pose)
if name == pose or result != None:
boxPoses.append(pose)
if len(boxPoses) > 1:
batchLoad = 1
openBatch = 1 #batch loading toggle
LibTog = 1 #load options menu toggle
saveBatch = 1 #batch save toggle
OptPose = 1 #save options menu toggle
##---------Listbox handling-------------------------------------------------
#-----------------Draws contents of listbox-----------------------
def listbox(box_l, box_r, box_b, box_t, text_h, line_top, line_bot, multi, text_offset, armname):
global boxList, boxPoses, boxBones, in_box, lastBone, boxFrames, curFrame
if ListTabTog == 1: box_select = boxPoses #Multi-tasking with multiple lists
elif BonesTabTog == 1: box_select = boxBones
elif AnimTabTog == 1: box_select = boxFrames
glColor3f(0.0,0.0,0.0) #Text color = black
for line in range(line_top,min(line_bot,len(boxList))):
text_l = (box_t-text_h-(line*text_h))+line_top*text_h #Line position
glRasterPos2i(box_l+8, text_l)#Draw position
#---Draw listbox contents--------
if mouse_y > text_l and mouse_y < text_l+text_h and in_box == 1: #Select current line
if box_select.count(boxList[line]) == 0:
if multi == 0 and len(box_select) == 0:
box_select.append(boxList[line])
elif multi == 1:
box_select.append(boxList[line])
lastBone = boxList[line] #Assign active user selection (always most recent) in multi-select mode
elif multi == 2: #multiple select w/out active assignment
box_select.append(boxList[line])
elif box_select.count(boxList[line]) != 0: #Remove w/ click during mutliple use
if multi != 0:
box_select.remove(boxList[line])
if multi == 1:
if len(box_select) >= 1: lastBone = box_select[len(box_select)-1] #Assign last item in list as active selection
if len(box_select) == 0: lastBone = "" #Clear active selection if list is empty
if multi != 0: in_box = 0 #This needs to be reset to prevent looping and display errors w/ wheelmouse
elif mouse_y > text_l and mouse_y < text_l+text_h and in_box == 2: #right click
if AnimTabTog == 1:
Blender.Set("curframe",int(boxList[line])) #Make 3d window current frame equal clicked frame number
curFrame = int(boxList[line])
if NumTabTog == 1: entry_values() #refresh the numbers tab listings
in_box = 0
if box_select.count(boxList[line]) != 0: #selection boxes
glColor3f(1.00, 1.00, 0.00)#yellow
glRecti(box_l + 1, text_l-text_h/5, box_r - 1, text_l + 4*text_h/5)#draw selection box
if multi == 1: #Special handling for active selection with multiple (also a draw event in box_click)
if boxList[line] == lastBone:
glColor3f(1.00, 0.60, 0.60)#red for active selection
glRecti(box_l + 1, text_l-text_h/5, box_r - 1, text_l + 4*text_h/5)#draw selection box
glColor3f(0.0,0.0,0.0)#restore text color
lineText = boxList[line]
if BonesTabTog == 1 and boneSort == 1: #indentation with tree view
space = "." #character used for display spacing
level = listTree[line]#[1] #number of spaces (depth of bone in hierarchy tree)
#lineText = str(level) + " " + (space*level) + lineText
lineText = "%s%s" %((space*level),lineText)
if GetStringWidth(lineText) > (box_r-box_l)-10: #trim lines which are too long
for i in range(len(lineText),0,-1):
if GetStringWidth(lineText[:i]) < (box_r-box_l)-10:
Text(lineText[:i])
break
else: Text(lineText) #Draw unaltered line
#----Line numbers and headers------------
if numList == 1:
glRasterPos2i(box_l-15, text_l)#Draw line numbers
Text(str(line+1),"tiny")#Start from one
glColor3f(0.0,0.0,0.0) #Draw headers
glRasterPos2i(box_l, box_t+(text_offset/2))
if ListTabTog == 1:
if foldTog == 0: #Pose browse mode
Text("Poses in: %s (%i)" %(os.path.split(LibPath)[1],len(boxList)), "small")
if foldTog == 1: #Folder browse mode
path = LibPath
if path != os.path.splitdrive(path)[0] + sys.sep: path = os.path.split(path)[1]
Text("Folders in: %s (%i)" %(path, len(boxList)), "small")
boxPoses = box_select
elif BonesTabTog == 1:
if armname != "": #If there's an armature selection
Text("bones in %s: %i" %(armname,len(boxList)), "small")
boxBones = box_select
elif AnimTabTog == 1:
boxFrames = box_select
#-----------------Mouse click interaction with listbox-------------------
def box_click(double, right):
global box_l, box_r, box_t, box_b, mouse_y, in_box, boxPoses, box_select
in_box = 0
#Find out if we're in the box-------------------------
for win in range(0,len(Window.GetScreenInfo())): #Find out if we're inside the box
win1 = Window.GetScreenInfo()[win] #Blender screen arrangement
if win1['type'] == 14: #script window type is 14
l = win1['vertices'][0] + box_l
r = win1['vertices'][0] + box_r
t = win1['vertices'][1] + (box_b-8)
b = win1['vertices'][1] + (box_t+3)
#wx = win1['vertices'][0]
wy = win1['vertices'][1]
if Window.GetMouseCoords()[0] > l and Window.GetMouseCoords()[0] < r and \
Window.GetMouseCoords()[1] > t and Window.GetMouseCoords()[1] < b:
mouse_y = Window.GetMouseCoords()[1]-wy #We only need mouse y value to find the clicked line
if multi == 0: box_select = [] #Limit list of selected items to 1
if right == 0: in_box = 1 #Allows listbox fxn to respond
elif right == 1: in_box = 2 #right-click handling
#print "INSIDE BOX!", Window.GetMouseCoords(), Window.GetMouseCoords()[0]-wx, Window.GetMouseCoords()[1]-wy
#Process information returned by the box-------------------------------
if multi == 0: #single mode
if double == 0:
boxPoses = []
if double == 1:
box_run() #Run the activation function if double click
boxPoses = box_select
box_select = []
Draw()#Display results of interaction with box
if multi == 1:
if NumTabTog == 1: entry_values() #refresh numerical entry values
Draw() #Refresh to display active selection line correctly (multiple mode)
#---------Runs the click activation for listbox when multiple selections are disabled--------
def box_run():
global boxPoses, LibPath, in_box, box_select, boxList, _partial_slider, partPose
if in_box == 1 and len(boxPoses) == 1:
#-----------Poses
if foldTog == 0:
partPose = _partial_slider.val #pass the partial pose value to load_pose
filepath = sys.join(LibPath, poseList[poseList.index(boxPoses[0])])#With single selection, index 0 is always selected
if sys.exists(filepath) == 1:
if os.path.splitext(filepath)[1] == ".pz2" or os.path.splitext(filepath)[1] == ".p2z": load_poser_pose(filepath)
else:
if openBatch == 0: load_pose(filepath) #single pose
elif openBatch == 1: open_batch(filepath) #Animated batch support
print "listbox file:", filepath
else: error = PupMenu("Error %t|Selected path is not valid.")
boxPoses = []
if NumTabTog == 1: entry_values() #refresh numerical entry values
#----------Folders
if foldTog == 1 and boxPoses[0] in boxList:
if LibPath.endswith(sys.sep): LibPath = LibPath[:-1]
if boxList.index(boxPoses[0]) == 0: #if selection is "Up a level"
LibPath = LibPath.rsplit(sys.sep,1)[0]
if LibPath == os.path.splitdrive(LibPath)[0]: LibPath += sys.sep #Special handling for root (Windows-specific?)
if boxList.index(boxPoses[0]) > 0: LibPath = sys.join(LibPath, boxPoses[0])
if os.path.isdir(LibPath):
folder_browse(LibPath)
else: print "Clicked selection is not a directory", LibPath
boxList = poseList #fill the display list
in_box = 0 #re-set to prevent looping
Draw() #Folder browser changes list and display needs to be refreshed
#----------------Selection options for the bone listbox----------------
def bonebox_menu():
global boxBones, boxList, TrimPose, lastBone, boneSort
menu = PupMenu("Bone listbox selection options: %t|Select All %x1|Deselect All %x2|Invert current selection %x6\
|Select using partial save group %x3|Remove using partial save group %x4\
|Change Partial group selection %x5|Add immediate children of active selection %x7\
|Add all children of active selection %x8|Remove immediate children of active selection %x9\
|Remove all children of active selection %x10|Toggle sorting: alphabtically/hierarchically %x11\
|Cancel %x11")
if menu == 1: #Add all
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
pose = ob.getPose()
for bone in pose.bones.keys():
boxBones.append(bone)
if boneSort == 0: boxBones.sort()
elif boneSort == 1: boxBones = sort_hierarchy(boxBones)
boxBones.reverse() #To make new active selection the first sorted entry in new list
if len(boxBones) > 0: lastBone = boxBones[len(boxBones)-1] #Active is last item in list
elif menu == 2: #Remove all
boxBones = []
lastBone = "" #Clear the active user selection
elif menu == 3 or menu == 4: #Add or remove using partial save group
if partVar == "" and sideVar == "":
partSave() #Show the save groups selection pupmenu
if partVar != "": #Proceed if there is a selected group
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
pose = ob.getPose()
for bone in pose.bones.values():
screening = Part_Load(bone, 0)
if menu == 3: #add
if screening == 1: boxBones.append(bone.name)
elif menu == 4: #remove
if screening == 1:
if bone.name in boxBones:
boxBones.remove(bone.name)
if bone.name == lastBone and lastBone in boxBones:
boxBones.remove(lastBone) #This needs to be specified or lastBone is retained
lastBone = "" #Clear the active user selection if it is removed
if lastBone == "" and len(boxBones) > 0:
if boneSort == 0: boxBones.sort()
elif boneSort == 1: boxBones = sort_hierarchy(boxBones)
boxBones.reverse()
lastBone = boxBones[len(boxBones)-1]
if menu == 3: #sort the added listings
if boneSort == 0: boxBones.sort()
elif boneSort == 1: boxBones = sort_hierarchy(boxBones)
boxBones.reverse()
if lastBone in boxBones:
boxBones.remove(lastBone)
boxBones.append(lastBone) #Place active selection at end of list
else:
if len(boxBones) > 0: lastBone = boxBones[len(boxBones)-1] #Active is last item in list
elif menu == 5: #Change partial save group selections
partSave()
if partVar != "": TrimPose = 1 #Set partial group save toggle
elif menu == 6: #Invert selection
boxList = boxBones
boxBones = []
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob and ob.getType() == "Armature":
pose = ob.getPose()
for bone in pose.bones.keys():
if bone not in boxList: boxBones.append(bone)
if boneSort == 0: boxBones.sort()
elif boneSort == 1: boxBones = sort_hierarchy(boxBones)
#print boxBones
boxBones.reverse() #To make new active selection the first sorted entry in new list
if len(boxBones) > 0: lastBone = boxBones[len(boxBones)-1] #Active is last item in list
elif menu > 6 and menu < 11: #work with children
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature" and len(boxBones) > 0: #There must be a list selection
AData = ob.getData()
bone1 = AData.bones[lastBone] #Last bone selected
if bone1.hasChildren():
if menu % 2 != 0: offspring = bone1.children #7, 9 = odd
elif menu % 2 == 0: offspring = bone1.getAllChildren()#8, 10 = even
if menu < 9: #7 & 8 are additive
for child in offspring: boxBones.append(child.name)
if boneSort == 0: boxBones.sort()
elif boneSort == 1: boxBones = sort_hierarchy(boxBones)
boxBones.reverse()
if lastBone in boxBones:
boxBones.remove(bone1.name) #update to keep last user selection last in list
boxBones.append(bone1.name)
elif menu > 8: #9, 10 are subtractive
for child in offspring:
if child.name in boxBones:
boxBones.remove(child.name)
if child.name == lastBone: lastBone = "" #Clear the active user selection if it is removed
if boneSort == 0: boxBones.sort()
elif boneSort == 1: boxBones = sort_hierarchy(boxBones)
boxBones.reverse()
elif menu == 11: boneSort = 1 - boneSort
if boneSort == 0: boxList.sort() #boxList needs to be sorted after sort/reverse for lastBone display (above).
elif boneSort == 1: boxList = sort_hierarchy(boxList)
#boxList = boxBones
##---------------------GUI adapted from Blender Cal3D script----------------------
#----------------------Some additions adapted from discombobulator.py------------------
#pose application----------------------------------------------------
realTime = 0 #Toggle variable for applying pose changes to current without loading a file
#load pose type conversion------------------------------------------
PoserConvert = 0 #Poser conversion toggle
PoseMat = 0 #Toggle for import conversion of a pose saved using the PoseMatrix option for IK
#save pose type conversion-------------------------------------------
NoRollTog = 0 #Toggle button variable for restmatrix pose save
IKTog = 0 #Toggle button variable for posematrix save
PoserSave = 0 #Toggle button variable for Poser pose export
#pose modifiers------------------------------------------------------
MirrorPose = 0 #Toggle for option to flip sides of a pose R/L
ScreenPose = 0 #Screen pose toggle
screenR = 1 #Load only side R with screening
screenL = 0 #Load only side L with screening
DupPose = 0 #Symmetrical pose toggle variable
dupR = 1 #Symmetrical pose R to L
dupL = 0 #Symmetrical pose L to R
torsoZero = 0 #Option to zero centered parts with symmetrize button. 0 = zero them, 1 = don't.
partPose = 1.0 #Partial load pose slider value
MixPose = 0 #Combine new pose with current toggle variable
MirrorBone = 0 #Toggle for option to flip a bone's own values
#root handling-------------------------------------------------------
bodyscale = 0 #BODY screening toggle
HipPose = 0 #hip screening toggle
hipR = 0 #Screen hip rotation
hipT = 0 #Screen hip translation
addscale = 0 #Additive rotation loading for BODY - will probably remove this.
#partial pose loading------------------------------------------------
IncludePart = 0 #Toggle var to load only partial save group(s) in pose
ExcludePart = 0 #Toggle var to exclude partial save groups when loading a pose
#animation-----------------------------------------------------------
openBatch = 0 #For batch file import to animate from a series of static poses
saveBatch = 0 #For batch file save for animation support
askKey = 0 #Variable for keyframe options. 0 = ask, 1 = yes, 2 = no
batchLoad = 0 #Batch toggle for poses listbox. Allows multiple selections for loading of non-sequential frames
batchFrames = 0 #Batch toggle for frames listbox. Screen load/save using selected frame numbers
animlen = Blender.Get('endframe') - (Blender.Get('staframe')-1) #length of the current animation
curFrame = Blender.Get("curframe") #Blender.Get calls seem to cause memory jumps, so minimize the calls w/ a global
#save pose types------------------------------------------------------
EmpPose = 0 #Save pose from empties toggle
IKPose = 0 #Toggle to save a poseMatrix pose, including IK/constraints.
NoRollPose = 0 #Toggle to save a "PoserConvert" compatible pose, which excludes embedded bone roll data
#symmetry notation-----------------------------------------------------
suff = 0 #Symmetry notation using prefix
pref = 1 #Symmetry notation using suffix
right = "r" #"R" = right(-x) prefix/suffix
left = "l" #"L" = left(+x) prefix/suffix
flipbone = "" #The bone which flips
lenR = len(right) #length of right prefix/suffix
lenL = len(left) #length of left prefix/suffix
right_alt = "capeR" #Specify for non-standard part handling
left_alt = "capeL" #alt types currently only work with prefixes
flipDict = {} #Dict to store symmetrical bone pairs
#save modifiers----------------------------------------------------------
No_Trans = 0 #Toggle variable to exclude translation w/ pose save
No_Scale = 0 #Toggle variable to exclude scaling with pose save
#save and load option menus-----------------------------------------------
LibTog = 0 #Variable for pose load options toggle
OptPose = 0 #Toggle for export options on or off (toggle display)
#partial group handling---------------------------------------------------
TrimPose = 0 #Partial pose menu toggle for save
GroupTog = 0 #Partial pose menu toggle for load
partVar = "" #Stores partial group selection
sideVar = "" #Stores selected side with symmetrized groups
root = None #root part
rootchild = None #Single child of root if root has only one child (for special handling as w/ cr2 BODY and hip).
groupsDict = {} #Store the groups
maxVal = 2 #Value by which total number of bones will be divided when determining group selection maximum size
minVal = 3 #Minimum number of children and subchildren a bone can have for group selection minimum size
totalBones = 1 #Total count of bones in armature (minus the root itself)
#Bone roll----------------------------------------------------------------
oldRolls = {} #Dictionary to cache old values for in-session restoration
#menu and listbox file browser handling-------------------------------------
LibVar = 0 #Toggle var to select or deselect Pupmenu file loading. 1 is menu browsing, 0 is file selector browsing.
LibPath = "" #Path to pose folder
loadlist = "Pose list browser %t|Browse for a new folder %x1" #Variable to store menu for list browsing
LibSave = 0 #Option to save to selected list loading folder
poseList = [] #List of poses in target folder
poseStart = 0 #Starting number for each displayed sub-list
posePages = 0 #Number of sub-lists into which list of poses is split
poseLimit = 54 #Max number of poses per displayed list (user-configurable in load menu)
#page tabs-----------------------------------------------------------------
LoadTabTog = 1 #Load tab
SaveTabTog = 0 #Save tab
MiscTabTog = 0 #Miscellaneous options tab
ListTabTog = 0 #Listbox tab
BonesTabTog = 0 #Bone list tab
NumTabTog = 0 #Numerical entry tab
AnimTabTog = 0 #Animation frames listbox tab
dualList = 0 #Dual display toggle for page tabs
#listbox------------------------------------------------------------------
line_top = 0 #listbox top line
line_bot = 0 # listbox bottom line
box_h = 0 #Listbox height (calculated later; global to pass limits to mouse wheel event)
box_select = [] #Active list of selected listbox items
boxList = [] #Active listbox multitasking list. Stores poseList, bones list, or folders list
boxPoses = [] #Stored listbox pose selections to pass on or restore
boxBones = [] #Stored listbox bone selections to pass on or restore
boxFrames = [] #Stored listbox frames selection for animation i/o and keyframe use
mouse_y = 0 #pass the mouse y coords between click event and listbox gui
in_box = 0 #Screening for inside/outside of box. 0 = out, 1 = in/left click, 2 = in/right click
multi = 0 #multiple selection support flag; 0 = single select, 1 = multiple w/ active, 2 = multiple w/out active
numList = 1 #Toggle for listbox line numbers
wheelslider = 1 #Toggle listbox scrolling between slider and wheelmouse modes. Slider = 0, wheelmouse = 1
listScroll = 1 - wheelslider #Start value for pose/file list slider
listScrollB = 1 - wheelslider #Start value for bones list slider
listScrollA = 1 - wheelslider #Start value for animation list slider
foldTog = 0 #Folder browser toggle in pose listbox mode
#Bone listbox selection handling------------------------------------------
bonesLoad = 0 #Toggle option for load/apply screening: 1 = include, 2 = exclude
bonesSave = 0 #Toggle option for save screening: 1 = include, 2 = exclude
lastBone = "" #Last user-selected bone name (the active bones list selection)
boneSort = 1 #Type of sorting for bones list. 0 = alphabetical, 1 = hierarchical
listTree = [] #List for treeview
#Numerical entry--------------------------------------------------------
myKeys = 0 #Keyframe toggle button variable for number tab.
keyType = 0 #Keyframe handling for myKeys toggle. 0 = no keyframes, 1 = keyframe all bones, 2 = keyframe bones list active
lastLen = 0 #Length of lastBone (to configure translation sensitivity)
uniqueRot = 0 #Toggle to use euler.unique() with rot dials (may return very unusual results....)
#General preferences----------------------------------------------------
setprefs = 0 #flag to set preferences at next exit
#gui buttons-------------------------------------------------------------
_partial_slider = Create(1.0) #Slider handling from der_ton's .md5 exporter.
_load_menu = Create(1) #So we can pass the load menu .val parameter
_scroll_slider = Create(0.0) #Listbox slider
_dual_width = Create(240) #dual width number button - to alter button_width and display width
_tran_x = Create(0.0000) #These are numerical entry tab dials
_tran_y = Create(0.0000)
_tran_z = Create(0.0000)
_rot_x = Create(0.0000)
_rot_y = Create(0.0000)
_rot_z = Create(0.0000)
_size_x = Create(0.0000)
_size_y = Create(0.0000)
_size_z = Create(0.0000)
_head_x = Create(0.0000)
_head_y = Create(0.0000)
_head_z = Create(0.0000)
_tail_x = Create(0.0000)
_tail_y = Create(0.0000)
_tail_z = Create(0.0000)
_boneroll_num = Create(0.0)
#These are all buttons which don't use .val as their linked values
_quit_button = Create(0)
_loadtab_toggle = Create(0)
_savetab_toggle = Create(0)
_misctab_toggle = Create(0)
_numtab_toggle = Create(0)
_listtab_toggle = Create(0)
_bonetab_toggle = Create(0)
_animtab_toggle = Create(0)
_savelist_button = Create(0)
_folder_toggle = Create(0)
_folder_button = Create(0)
_menu_button = Create(0)
_load_button = Create(0)
_load_menu = Create(0)
_apply_button = Create(0)
_zero_button = Create(0)
_lib_toggle = Create(0)
_group_toggle = Create(0)
_poserL_toggle = Create(0)
_posematL_toggle = Create(0)
_flip_toggle = Create(0)
_flipbone_toggle = Create(0)
_screen_toggle = Create(0)
_screen_R = Create(0)
_screen_L = Create(0)
_dup_toggle = Create(0)
_dup_R = Create(0)
_dup_L = Create(0)
_mix_toggle = Create(0)
_save_button = Create(0)
_part_toggle = Create(0)
_noTrans_toggle = Create(0)
_noScale_toggle = Create(0)
_opt_toggle = Create(0)
_poserS_toggle = Create(0)
_posematS_toggle = Create(0)
_suffBone_button = Create(0)
_prefix_toggle = Create(0)
_suffix_toggle = Create(0)
_wheel_toggle = Create(0)
_number_toggle = Create(0)
_dual_toggle = Create(0)
_menuA_button = Create(0)
_batchload_toggle = Create(0)
_batchload_button = Create(0)
_batchframes_toggle = Create(0)
_IK_button = Create(0)
_tools_button = Create(0)
_reg_button = Create(0)
_poserSave_toggle = Create(0)
_boneSearch_button = Create(0)
_boneGrab_button = Create(0)
_poseSearch_button = Create(0)
#Button event constants---------------------------
EVENT_NOEVENT = 0
EVENT_QUIT = 1
EVENT_SAVE = 2
EVENT_LOAD = 3
EVENT_CONVERT = 4
EVENT_FLIP = 5
EVENT_SCREEN = 6
EVENT_DUP = 7
EVENT_SCR_R = 8
EVENT_SCR_L = 9
EVENT_DUP_R = 10
EVENT_DUP_L = 11
EVENT_PART = 12
EVENT_MIX = 13
EVENT_EMP = 14
EVENT_ZERO = 15
EVENT_SUFFPREF = 16
EVENT_PREF = 17
EVENT_SUFF = 18
EVENT_APPLY = 19
EVENT_NOTRANS = 20
EVENT_NOSCALE = 21
EVENT_POSEMAT = 22
EVENT_BONE = 23
EVENT_LIB = 24
EVENT_TAB_L = 25
EVENT_TAB_S = 26
EVENT_TAB_M = 27
EVENT_TAB_T = 28
EVENT_TAB_B = 29
EVENT_LISTSCROLL = 30
EVENT_WHEEL = 31
EVENT_NUMB = 32
EVENT_FOLDER = 33
EVENT_BONEMENU = 34
EVENT_GROUP = 35
EVENT_NOROLL = 36
EVENT_IK = 37
EVENT_DUAL = 38
EVENT_TAB_N = 39
EVENT_ENTRY = 40
EVENT_KEYF = 41
EVENT_EDIT = 42
EVENT_UNIQUE = 43
EVENT_LISTLOAD = 44
EVENT_FLIPBONE = 45
EVENT_TAB_A = 46
EVENT_ANIMMENU = 47
EVENT_BATCHTOG = 48
EVENT_BATCHLOAD = 49
EVENT_BATCHFRAMES = 50
EVENT_IKTOG = 51
EVENT_TOOLS = 52
EVENT_REG = 53
EVENT_POSERSAVE = 54
EVENT_BONESEARCH = 55
EVENT_BONEGRAB = 56
EVENT_POSESEARCH = 57
def colorbox(x,y,xright,bottom): #from discombobulator.py
glColor3f(0.75, 0.75, 0.75)
glRecti(x + 1, y + 1, xright - 1, bottom - 1)
def gui():
global _partial_slider, _load_menu, _scroll_slider, _dual_width, _tran_x, _tran_y, \
_tran_z, _rot_x, _rot_y, _rot_z, _size_x, _size_y, _size_z, _head_x, _head_y, \
_head_z, _tail_x, _tail_y, _tail_z, _boneroll_num, _quit_button, _loadtab_toggle, \
_savetab_toggle, _misctab_toggle, _numtab_toggle, _listtab_toggle, _bonetab_toggle, \
_savelist_button, _folder_toggle, _folder_button, _menu_button, _load_button, \
_load_menu, _apply_button, _zero_button, _lib_toggle, _group_toggle, _poserL_toggle, \
_posematL_toggle, _flip_toggle, _flipbone_toggle, _screen_toggle, _screen_R, _screen_L, \
_dup_toggle, _dup_R, _dup_L, _mix_toggle, _save_button, _part_toggle, _noTrans_toggle, \
_noScale_toggle, _opt_toggle, _poserS_toggle, _posematS_toggle, _suffBone_button, \
_prefix_toggle, _suffix_toggle, _wheel_toggle, _number_toggle, _dual_toggle, \
_animtab_toggle, _menuA_button, _batchload_toggle, _batchload_button, _batchframes_toggle, \
_IK_button, _tools_button, _reg_button, _poserSave_toggle, _boneSearch_button, _boneGrab_button,\
_poseSearch_button
global line_top, line_bot, box_l, box_r, box_t, box_b, box_h, mouse_y, multi, \
box_select, boxList, listScroll, numList, text_h, boxPoses, boxBones, wheelslider, \
listScrollB, foldTog, lastBone, button_width, myKeys, lastLen, uniqueRot, \
boxFrames, listScrollA, animlen, boneSort, curFrame
button_left = 20
#button_width = 240 #(default)
button_width = _dual_width.val
button_height = 15
text_offset = 5
button_space = button_height + button_height/4
#Various functions-------------------------------------------------------
colorbox(button_left - 8, button_height/4, button_width + 28, 2*button_space+text_offset)
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left + (button_width/2 - GetStringWidth("Blender Pose Handler V.1","small")/2),
button_space/2-text_offset/2)
Text("Blender Pose Handler V.1", "small")
_quit_button = Button("Quit", EVENT_QUIT, button_left, button_space, button_width, button_height, "Exit from script")
#Tab toggles------------------------------------------------------------
colorbox(button_left - 8, 23*button_space-text_offset, button_width + 28, 25*button_space+text_offset)
_loadtab_toggle = Toggle("Apply", EVENT_TAB_L, button_left, 24*button_space, button_width/4, button_height,
LoadTabTog, "Select to view pose options for loading or real-time application.")
_savetab_toggle = Toggle("Save", EVENT_TAB_S, button_left+button_width/4, 24*button_space, button_width/4,
button_height, SaveTabTog, "Select to view pose options for saving.")
_misctab_toggle = Toggle("Options", EVENT_TAB_M, button_left+(2*button_width/4), 24*button_space, button_width/4,
button_height, MiscTabTog,
"Select to view options for bone roll, symmetry notation handling, and IK.")
_numtab_toggle = Toggle("Number", EVENT_TAB_N, button_left+(3*button_width/4), 24*button_space, button_width/4,
button_height, NumTabTog, "Numerical entry posing for bones list selection.")
_listtab_toggle = Toggle("Poses", EVENT_TAB_T, button_left, 23*button_space, button_width/3, button_height,
ListTabTog, "Use listbox file browser.")
_bonetab_toggle = Toggle("Bones", EVENT_TAB_B, button_left+(button_width/3), 23*button_space, button_width/3,
button_height, BonesTabTog, "Work with list of bones in the selected armature.")
_animtab_toggle = Toggle("Frames", EVENT_TAB_A, button_left+(2*button_width/3), 23*button_space, button_width/3,
button_height, AnimTabTog, "Work with list of frames in current animation.")
#----Listboxes----------------------------------------------------------
if ListTabTog == 1 or BonesTabTog == 1 or AnimTabTog == 1:
#These are the GUI draw elements of the empty listbox. Listbox contents are in the listbox() fxn.
#Mouse interaction is in box_click().
#Change these to alter drawn position of listbox, slider, and listbox contents
num_offset = 0
if numList == 1: num_offset = 15 #GUI re-spacing w/ line numbers on/off
dual_offset = 0
if dualList == 1: dual_offset = button_left+button_width+8 #Dual tab display spacing
text_h = 12 #text height (10 plus spacing)
box_l = button_left+num_offset+dual_offset #box left
box_r = 4*(button_width/5)+num_offset+dual_offset #box right
box_b = 5 * button_height #box bottom
box_t = 26 * button_height #box top
box_h = ((box_t+3)-(box_b-8))/text_h #box height
multi = 0 #multiple selection toggle
armname = "" #Name of selected armature
colorbox(box_l-num_offset-8, 3*button_space-10, dual_offset+button_width+28, box_t+button_height)#background
but_w = button_width/4 #re-spacing w/ list number toggle
but_l = (3*but_w)+dual_offset
if numList == 1:
but_w = button_width/5
but_l = (4*but_w)+dual_offset
if ListTabTog == 1:#-----file---------------------------------------------
multi = 0
if batchLoad == 1: multi = 2 #multiple selection for animation import
armname = ""
#For the pose listbox, the boxList is filled in:
#--EVENT_FOLDER for _folder_toggle (on and off)
#--list_browser() for _folder_button (and for list refresh with _save_button)
#--box_run() for folder click.
#--get_from_ini() for startup if ListTabTog == 1 and preferred settings are used
_savelist_button = Button("Save", EVENT_SAVE, button_left+but_l, 8*button_height, but_w, button_height,
"Save pose or batch of poses to current folder")
_folder_toggle = Toggle("Folder", EVENT_FOLDER, button_left+but_l, 10*button_height, but_w, button_height, foldTog,
"Use listbox to locate a new folder.")
_folder_button = Button("Browse", EVENT_LISTLOAD, button_left+but_l, 12*button_height, but_w, button_height,
"Locate a new folder using the file browser")
_batchload_toggle = Toggle("Batch", EVENT_BATCHTOG, button_left+but_l, 18*button_height, but_w, button_height,
batchLoad, "Toggle multiple file selections, batch load, and batch save for animation.")
_poseSearch_button = Button("Search", EVENT_POSESEARCH, button_left+but_l, 14*button_height, but_w, button_height,
"Search by name for pose files in current folder.")
if batchLoad == 1:
_batchload_button = Button("load", EVENT_BATCHLOAD, button_left+but_l, 16*button_height, but_w, button_height,
"Load multiple selections as animated batch.")
else: _batchload_button = Create(0)
if wheelslider == 0:
_scroll_slider = Slider("list top: ", EVENT_LISTSCROLL, box_l, (3*button_space)-8, box_r-box_l, button_height,
listScroll, 1, max((len(boxList)-box_h)+1,0), 0, "Scroll the listbox with the slider bar.")
elif BonesTabTog == 1:#---bones-------------------------------------------------
multi = 1
#For the bones listbox, this is the only time the boxlist is filled
if armname == "": #Get armature data only if boxList is empty
if Object.GetSelected() != []:
ob = Object.GetSelected()[0]
if ob.getType() == "Armature":
armname = ob.name
pose = ob.getPose()
if len(boxList) != len(pose.bones.keys()) or (len(boxList) == 0 and armname != ""): #refill the list
boxList = pose.bones.keys()
if boneSort == 0: boxList.sort()
elif boneSort == 1: boxList = sort_hierarchy(boxList)
else:
boxList = []
armname = ""
if NumTabTog == 0:
_menu_button = Button("Select", EVENT_BONEMENU, button_left+but_l, 8*button_height, but_w, button_height,
"Selection options for bones listbox")
_IK_button = Button("IK", EVENT_IKTOG, button_left+but_l, 10*button_height, but_w, button_height,
"IK handling options for bones listbox")
_tools_button = Button("Tools", EVENT_TOOLS, button_left+but_l, 12*button_height, but_w, button_height,
"Edit mode tools to aid IK construction - warning: these will alter your armature.")
_boneSearch_button = Button("Search", EVENT_BONESEARCH, button_left+but_l, 14*button_height, but_w, button_height,
"Search by name for bones in bones list and 3d view.")
_boneGrab_button = Button("Grab", EVENT_BONEGRAB, button_left+but_l, 16*button_height, but_w, button_height,
"Grab selected bones list bones in the 3D view.")
if wheelslider == 0:
_scroll_slider = Slider("list top: ", EVENT_LISTSCROLL, box_l, (3*button_space)-8, box_r-box_l, button_height,
listScrollB, 1, max((len(boxList)-box_h)+1,0), 0, "Scroll the listbox with the slider bar.")
elif AnimTabTog == 1: #----frames--------------------------------------------------------
multi = 2
armname = ""
frame = curFrame#Blender.Get("curframe")
if frame > animlen: animlen += frame - animlen #extend the displayed list if user goes beyond the limit
#This is the only time boxList is filled for the frames list
if len(boxList) != animlen: #Fill the frames list
boxList = []
for f in range(Blender.Get('staframe'), animlen+1):
boxList.append(str(f))
_menuA_button = Button("Select", EVENT_ANIMMENU, button_left+but_l, 8*button_height, but_w, button_height,
"Selection options for frames listbox")
_batchframes_toggle = Toggle("Batch", EVENT_BATCHFRAMES, button_left+but_l, 18*button_height, but_w, button_height,
batchFrames, "Screen batch load/save using selected frames.")
if wheelslider == 0:
_scroll_slider = Slider("list top: ", EVENT_LISTSCROLL, box_l, (3*button_space)-8, box_r-box_l, button_height,
listScrollA, 1, max((len(boxList)-box_h)+1,0), 0, "Scroll the listbox with the slider bar.")
glColor3f(0.0,0.0,0.0)
glRasterPos2i(box_l-10, box_t+(text_offset/2))
Text("Frame count: %i Current frame: %i" %(animlen, frame), "small")
#---------wheelmouse vs. slider---------------------------------------------------------
if wheelslider == 1: #Label if slider is not in use
glColor3f(0.0,0.0,0.0)
glRasterPos2i(box_l, 3*button_space-text_offset)
Text("Use mouse wheel to scroll list")
elif wheelslider == 0:
if ListTabTog == 1:
line_top = int(listScroll)-1 #Set top line for box display (slider value minus one to convert to list indices)
if BonesTabTog == 1:
line_top = int(listScrollB)-1
if AnimTabTog == 1:
line_top = int(listScrollA)-1
#--------default drawn elements---------------------------------------------
glColor3f(1.0,1.0,1.0) #listbox color = white
box = glRecti(box_l, box_b-text_h, box_r, box_t) #Draw the textbox
line_bot = min(line_top + box_h, len(boxList)) #Define the bottom
if ListTabTog == 1 and LibPath == "": boxList = [] #If folder browse w/ file browser was cancelled.
if boxList != []:
listbox(box_l, box_r, box_b, box_t, text_h, line_top, line_bot, multi, text_offset, armname)
elif boxList == []:
glColor3f(0.0,0.0,0.0)
glRasterPos2i(box_l, box_t+(text_offset/2))
if ListTabTog == 1:
Text("No folder selected.")#Default header for pose list tab
if BonesTabTog == 1:
if armname == "": Text("No armature selected.")#Default header for bones list tab
#Frames list will always be filled... unless a zero length animation is possible?
#------Pages-------------------------------------------------------
if LoadTabTog == 1:#Load tab
#Load functions------------------------------------------------------
colorbox(button_left - 8, 19*button_space - 8, button_width + 28, 22*button_space)#options
colorbox(button_left - 8, 12*button_space - 8, button_width + 28, 18*button_space)#modifier
colorbox(button_left - 8, 8*button_space - 8, button_width + 28, 11*button_space)#conversion
colorbox(button_left - 8, 3*button_space - 8, button_width + 28, 7*button_space)#loading
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 21 * button_space + text_offset)
Text("Pose application options:")
glRasterPos2i(button_left, 17 * button_space + text_offset)
Text("Pose modifier settings:")
glRasterPos2i(button_left, 6 * button_space + text_offset)
Text("Loading and application:")
glRasterPos2i(button_left, 10 * button_space + text_offset)
Text("Pose type conversion:")
glRasterPos2i(button_left, 16 * button_space + text_offset)
Text("pose load settings:")
if LibVar == 0:
_load_button = Button("Load pose from file", EVENT_LOAD, button_left, 3*button_space, button_width, button_height,
"Browse for Pose to Load")
elif LibVar == 1: #use if "if" rather than "elif" causes a python24dll error crash bug in 242 (w/ dual ListTabTog)
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 4 * button_space + 3)
Text("Menu browser:")
_load_menu = Menu(loadlist, EVENT_LOAD, button_left+(button_width/3), 3*button_space, 2*button_width/3,
button_height, _load_menu.val, "Browse for Pose to Load")
_apply_button = Button("Apply modifiers to current pose", EVENT_APPLY, button_left, 4*button_space, button_width,
button_height, "Apply pose modifier or type conversion settings to current pose.")
_zero_button = Button("Zero current pose", EVENT_ZERO, button_left, 5*button_space, button_width, button_height,
"Restore all rotation, scaling, and translation settings to rest pose values")
_lib_toggle = Toggle("General loading options", EVENT_LIB, button_left, 20*button_space, button_width,
button_height, LibTog, "Set options for loading a pose.")
_group_toggle = Toggle("Partial group loading options", EVENT_GROUP, button_left, 19*button_space, button_width,
button_height, GroupTog, "Set screening options to exclude some bones when loading a pose.")
#Pose types
_poserL_toggle = Toggle("Restmatrix pose ('No Roll')", EVENT_CONVERT, button_left, 9*button_space, button_width,
button_height, PoserConvert, "Convert a restmatrix/no roll pose or a combo type pose.")
_posematL_toggle = Toggle("Posematrix pose ('IK')", EVENT_POSEMAT, button_left, 8*button_space, button_width,
button_height, PoseMat, "Convert a pose saved with IK/constraints (poseMatrix) option.")
#Symmetry handling
_flip_toggle = Toggle("Flip Pose", EVENT_FLIP, button_left, 12*button_space, button_width/2, button_height,
MirrorPose, "Mirror the pose (reverse left and right).")
_flipbone_toggle = Toggle("Flip Bones", EVENT_FLIPBONE, button_left+(button_width/2), 12*button_space, button_width/2,
button_height, MirrorBone, "Flip each bone over itself.")
_screen_toggle = Toggle("Screen Right/Left", EVENT_SCREEN, button_left, 13*button_space, (button_width/10)*8,
button_height, ScreenPose, "Apply only one side of pose.")
_screen_R = Toggle("R", EVENT_SCR_R, button_left+(button_width/10)*8, 13*button_space, button_width/10,
button_height, screenR, "Use only right side of pose.")
_screen_L = Toggle("L", EVENT_SCR_L, button_left+(button_width/10)*9, 13*button_space, button_width/10,
button_height, screenL, "Use only left side of pose.")
_dup_toggle = Toggle("Symmetry Right/Left", EVENT_DUP, button_left, 14*button_space, (button_width/10)*8,
button_height, DupPose, "Load one side of pose and mirror it to second side.")
_dup_R = Toggle("R", EVENT_DUP_R, button_left+(button_width/10)*8, 14*button_space, button_width/10,
button_height, dupR, "Copy right side to left side.")
_dup_L = Toggle("L", EVENT_DUP_L, button_left+(button_width/10)*9, 14*button_space, button_width/10,
button_height, dupL, "Copy left side to right side.")
#Combinatory options
if dualList == 0: sliderlabel = "Pose intensity:"
elif dualList == 1: sliderlabel = "Intensity:"
_partial_slider = Slider(sliderlabel, EVENT_NOEVENT, button_left, 15*button_space, button_width, button_height,
_partial_slider.val, 0.01, 2.0, 0, "Adjust the amount of effect for the selected pose.")
_mix_toggle = Toggle("Combine with current pose", EVENT_MIX, button_left, 16*button_space, button_width,
button_height, MixPose, "Add loaded pose data to current pose (mix).")
if SaveTabTog == 1:#Save tab
#Save functions-------------------------------------------------------
colorbox(button_left - 8, 19*button_space - 8, button_width + 28, 22*button_space)#options
colorbox(button_left - 8, 16*button_space - 8, button_width + 28, 18*button_space)#modifier
colorbox(button_left - 8, 11*button_space - 8, button_width + 28, 15*button_space)#conversion
colorbox(button_left - 8, 8*button_space - 8, button_width + 28, 10*button_space)#save
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 21 * button_space + text_offset)
Text("Pose save options:")
glRasterPos2i(button_left, 17 * button_space + text_offset)
Text("Save modifier settings:")
glRasterPos2i(button_left, 14 * button_space + text_offset)
Text("Save type conversion:")
glRasterPos2i(button_left, 9 * button_space + text_offset)
Text("Save:")
_save_button = Button("Save Pose", EVENT_SAVE, button_left, 8*button_space, button_width, button_height,
"Browse for Pose Save Location")
_part_toggle = Toggle("Partial group save options", EVENT_PART, button_left, 19*button_space, button_width,
button_height, TrimPose, "Set screening options to exclude some bones from saved pose.")
_noTrans_toggle = Toggle("No translation", EVENT_NOTRANS, button_left, 16*button_space, button_width/2,
button_height, No_Trans, "Select to exclude translation data from saved pose.")
_noScale_toggle = Toggle("No scaling", EVENT_NOSCALE, button_left+(button_width/2), 16*button_space, button_width/2,
button_height, No_Scale, "Select to exclude scaling data from saved pose.")
_opt_toggle = Toggle("General save options", EVENT_EMP, button_left, 20*button_space, button_width, button_height,
OptPose, "Set options for saving poses.")
#Save types
_poserS_toggle = Toggle("Restmatrix pose ('No Roll')", EVENT_NOROLL, button_left, 13*button_space, button_width,
button_height, NoRollTog, "Save a pose without 'embedded' bone roll.")
_posematS_toggle = Toggle("Posematrix pose ('IK')", EVENT_IK, button_left, 12*button_space, button_width,
button_height, IKTog, "Save IK and constraints data in the pose.")
_poserSave_toggle = Toggle("Export Poser pose (.pz2)", EVENT_POSERSAVE, button_left, 11*button_space, button_width,
button_height, PoserSave, "Save a Poser pose, instead of a Blender pose.")
if MiscTabTog == 1:#Options tab
#Symmetry notation functions------------------------------------------
colorbox(button_left - 8, 19*button_space - 8, button_width + 28, 22*button_space)
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 21 * button_space + text_offset)
Text("Symmetry Notation Handling:")
_suffBone_button = Button("Enter symmetry prefix or suffix", EVENT_SUFFPREF, button_left, 19*button_space,
(button_width), button_height, "Press to enter right/left prefix or suffix")
_prefix_toggle = Toggle("Use prefix", EVENT_PREF, button_left, 20*button_space, button_width/2, button_height,
pref, "select if right/left handling uses a prefix")
_suffix_toggle = Toggle("Use suffix", EVENT_SUFF, button_left+(button_width/2), 20*button_space, button_width/2,
button_height, suff, "select if right/left handling uses a suffix")
#Listbox settings options-----------------------------------------
colorbox(button_left - 8, 15*button_space - 8, button_width + 28, 18*button_space)
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 17 * button_space + text_offset)
Text("Listbox display options:")
_wheel_toggle = Toggle("Wheel mouse", EVENT_WHEEL, button_left, 16*button_space, button_width/2, button_height,
wheelslider, "Select to use wheel mouse to scroll listboxes")
_number_toggle = Toggle("Line numbers", EVENT_NUMB, button_left+(button_width/2), 16*button_space, button_width/2,
button_height, numList, "Select to use line numbers with listboxes")
_dual_toggle = Toggle("Dual display", EVENT_DUAL, button_left, 15*button_space, button_width/2, button_height,
dualList, "Select to use two tab pages side-by-side")
_dual_width = Number("Width", EVENT_NOEVENT, button_left+(button_width/2), 15*button_space, button_width/2,
button_height, _dual_width.val, 100, 240, "Change width of displayed page (default value is 240).")
#Armature backup functions--------------------------------------------------
colorbox(button_left - 8, 12*button_space - 8, button_width + 28, 14*button_space)
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 13 * button_space + text_offset)
Text("Armature options:")
_bone_button = Button("Armature text backup options", EVENT_BONE, button_left, 12*button_space, button_width,
button_height, "Save or load armature settings from a textfile backup.")
#Numerical entry options----------------------------------------------
colorbox(button_left - 8, 9*button_space - 8, button_width + 28, 11*button_space)
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 10 * button_space + text_offset)
Text("Numerical entry options:")
_unique_toggle = Toggle("Use Euler Unique", EVENT_UNIQUE, button_left, 9*button_space, button_width, button_height,
uniqueRot, "Select to use euler.unique() to avoid gimbal lock with rotation dials.")
#Registry backup options-----------------------------------------------
colorbox(button_left - 8, 6*button_space - 8, button_width + 28, 8*button_space)
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 7 * button_space + text_offset)
Text("General settings options:")
_reg_button = Button("Gui preference options", EVENT_REG, button_left, 6*button_space, button_width, button_height,
"Set or deactivate general preferences.")
if NumTabTog == 1: #Numerical entry tab
#Pose mode entry----------------------------------------------------
#Number buttons handle differently in 241 and 242. This is optimized for 242 handling
maxval = 1000.0000
minval = -1000.0000
colorbox(button_left - 8, 3*button_space - 8, button_width + 28, 22*button_space+text_offset)
glColor3f(0.67,0.67,0.67)
glRecti(button_left - 8, 21*button_space, button_width + 28, 23*button_space-text_offset)
glRecti(button_left - 8, 15*button_space, button_width + 28, 18*button_space)
glRecti(button_left - 8, 10*button_space-(button_height/4), button_width + 28, 12*button_space-(button_height/4))
glRecti(button_left - 8, 4*button_space-(button_height/4), button_width + 28, 7*button_space-(button_height/4))
glColor3f(0.0,0.0,0.0)
glRasterPos2i(button_left, 22 * button_space+text_offset)
if lastBone == "": Text("No active bones list selection","small")
if lastBone != "": Text("Active bones list bone: %s" %(lastBone),"small")
glRasterPos2i(button_left, 21 * button_space+text_offset)
Text("Pose mode:")
glRasterPos2i(button_left+(button_width/3)+8, 20 * button_space+text_offset)
Text("locX: %f" %(_tran_x.val))
glRasterPos2i(button_left+(button_width/3)+8, 19 * button_space+text_offset)
Text("LocY: %f" %(_tran_y.val))
glRasterPos2i(button_left+(button_width/3)+8, 18 * button_space+text_offset)
Text("LocZ: %f" %(_tran_z.val))
glRasterPos2i(button_left+(button_width/3)+8, 17 * button_space+text_offset)
Text("RotX: %f" %(_rot_x.val))
glRasterPos2i(button_left+(button_width/3)+8, 16 * button_space+text_offset)
Text("RotY: %f" %(_rot_y.val))
glRasterPos2i(button_left+(button_width/3)+8, 15 * button_space+text_offset)
Text("RotZ: %f" %(_rot_z.val))
glRasterPos2i(button_left+(button_width/3)+8, 14 * button_space+text_offset)
Text("ScaleX: %f" %(_size_x.val))
glRasterPos2i(button_left+(button_width/3)+8, 13 * button_space+text_offset)
Text("ScaleY: %f" %(_size_y.val))
glRasterPos2i(button_left+(button_width/3)+8, 12 * button_space+text_offset)
Text("ScaleZ: %f" %(_size_z.val))
_tran_x = Number("", EVENT_ENTRY, button_left, 20*button_space, button_width/3, button_height, _tran_x.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set x translation of active bone list selection")
_tran_y = Number("", EVENT_ENTRY, button_left, 19*button_space, button_width/3, button_height, _tran_y.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set y translation of active bone list selection")
_tran_z = Number("", EVENT_ENTRY, button_left, 18*button_space, button_width/3, button_height, _tran_z.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set z translation of active bone list selection")
_rot_x = Number("", EVENT_ENTRY, button_left, 17*button_space, button_width/3, button_height, _rot_x.val,
minval*10, maxval*10, "Set x rotation of active bone list selection")
_rot_y = Number("", EVENT_ENTRY, button_left, 16*button_space, button_width/3, button_height, _rot_y.val,
minval*10, maxval*10, "Set y rotation of active bone list selection")
_rot_z = Number("", EVENT_ENTRY, button_left, 15*button_space, button_width/3, button_height, _rot_z.val,
minval*10, maxval*10, "Set z rotation of active bone list selection")
_size_x = Number("", EVENT_ENTRY, button_left, 14*button_space, button_width/3, button_height, _size_x.val,
minval, maxval, "Set x scaling for active bone list selection")
_size_y = Number("", EVENT_ENTRY, button_left, 13*button_space, button_width/3, button_height, _size_y.val,
minval, maxval, "Set y scaling for active bone list selection")
_size_z = Number("", EVENT_ENTRY, button_left, 12*button_space, button_width/3, button_height, _size_z.val,
minval, maxval, "Set z scaling for active bone list selection")
_keyframe_toggle = Toggle("Add keyframes", EVENT_KEYF, button_left+(button_width/2), 21*button_space+text_offset,
button_width/2, button_height, myKeys, "Toggle on to add keyframing for each entry")
#Edit mode entry-----------------------------------------
glRasterPos2i(button_left, 3 * button_space)
Text("Caution: these will alter your armature.", "small")
glRasterPos2i(button_left, 11 * button_space + text_offset)
Text("Edit mode:")
glRasterPos2i(button_left+(button_width/3)+8, 11 * button_space + text_offset)
Text("Length: %f" %(lastLen))
glRasterPos2i(button_left+(button_width/3)+8, 10 * button_space+text_offset)
Text("Roll: %f" %(_boneroll_num.val))
glRasterPos2i(button_left+(button_width/3)+8, 9 * button_space+text_offset)
Text("RootX: %f" %(_head_x.val))
glRasterPos2i(button_left+(button_width/3)+8, 8 * button_space+text_offset)
Text("RootY: %f" %(_head_y.val))
glRasterPos2i(button_left+(button_width/3)+8, 7 * button_space+text_offset)
Text("RootZ: %f" %(_head_z.val))
glRasterPos2i(button_left+(button_width/3)+8, 6 * button_space+text_offset)
Text("TipX: %f" %(_tail_x.val))
glRasterPos2i(button_left+(button_width/3)+8, 5 * button_space+text_offset)
Text("TipY: %f" %(_tail_y.val))
glRasterPos2i(button_left+(button_width/3)+8, 4 * button_space+text_offset)
Text("TipZ: %f" %(_tail_z.val))
_boneroll_num = Number("", EVENT_EDIT, button_left, 10*button_space, button_width/3, button_height,
_boneroll_num.val, -360, 360, "Set bone roll for active bone list selection")
_head_x = Number("", EVENT_EDIT, button_left, 9*button_space, button_width/3, button_height, _head_x.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set head x translation for active bone list selection")
_head_y = Number("", EVENT_EDIT, button_left, 8*button_space, button_width/3, button_height, _head_y.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set head y translation for active bone list selection")
_head_z = Number("", EVENT_EDIT, button_left, 7*button_space, button_width/3, button_height, _head_z.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set head z translation for active bone list selection")
_tail_x = Number("", EVENT_EDIT, button_left, 6*button_space, button_width/3, button_height, _tail_x.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set tail x translation for active bone list selection")
_tail_y = Number("", EVENT_EDIT, button_left, 5*button_space, button_width/3, button_height, _tail_y.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set tail y translation for active bone list selection")
_tail_z = Number("", EVENT_EDIT, button_left, 4*button_space, button_width/3, button_height, _tail_z.val,
minval*(lastLen/2), maxval*(lastLen/2), "Set tail z translation for active bone list selection")
#if NumTabTog == 0: zero_entries() #Clear the numerical tab buttons if the tab is not in use.
clicktime = sys.time()
def event(evt, val):
global line_top, box_h, clicktime, box_select, boxPoses, curFrame
if AnimTabTog == 1:
curFrame = Blender.Get('curframe')
if not val:
if AnimTabTog == 1:
curFrame = Blender.Get('curframe')
if (evt == ESCKEY):
if setprefs == 1: update_ini()
Exit()
return
elif evt == LEFTMOUSE: #For the listbox
if ListTabTog == 1 or BonesTabTog == 1 or AnimTabTog == 1:
double = 0
if round(sys.time()-clicktime,1) < 0.5: double = 1 #Alter to change double-click sensitivity
clicktime = sys.time()
box_click(double, 0)
elif evt == RIGHTMOUSE: #Right-click changes current frame in animation tab
if AnimTabTog == 1: box_click(0, 1)
elif (evt == WHEELDOWNMOUSE or evt == DOWNARROWKEY) and len(boxList) > box_h: #Avoid list index errors
if (ListTabTog == 1 or BonesTabTog == 1 or AnimTabTog == 1) and wheelslider == 1:
if line_top < (len(boxList)-box_h): line_top +=3
if line_top > (len(boxList)-box_h): line_top = len(boxList)-box_h
Draw()
elif (evt == WHEELUPMOUSE or evt == UPARROWKEY) and len(boxList) > box_h:
if (ListTabTog == 1 or BonesTabTog == 1 or AnimTabTog == 1) and wheelslider == 1:
if line_top > 0: line_top -=3
if line_top < 0: line_top = 0
Draw()
def bevent(evt):
global _partial_slider, _load_menu, _scroll_slider, \
bodyscale, PoserConvert, MirrorPose, ScreenPose, screenR, screenL, DupPose, dupR, dupL, \
HipPose, hipR, hipT, addscale, partPose, MixPose, EmpPose, suff, pref, TrimPose, \
openBatch, saveBatch, realTime, No_Trans, No_Scale, IKPose, NoRollPose, OptPose, PoseMat, \
LibVar, LibPath, LibSave, LibTog, IncludePart, ExcludePart, askKey, LoadTabTog, \
SaveTabTog, MiscTabTog, ListTabTog, BonesTabTog, line_top, multi, boxList, listScroll, numList, \
boxPoses, wheelslider, listScrollB, foldTog, in_box, GroupTog, NoRollTog, IKTog, lastBone, \
dualList, NumTabTog, myKeys, uniqueRot, MirrorBone, loadlist, AnimTabTog, boxFrames, listScrollA, \
batchLoad, batchFrames, keyType, setprefs, PoserSave, curFrame
if AnimTabTog == 1:
curFrame = Blender.Get('curframe')
if evt == EVENT_LOAD: #load button and load pulldown menu
partPose = _partial_slider.val
if LibVar == 0:
if openBatch == 0: Window.FileSelector(load_pose, 'Pose to open?') #File browse loading
elif openBatch == 1: Window.FileSelector(open_batch, 'Pose to open?')#Animated batch support
elif LibVar == 1: #Menu browse loading
if LibPath == "":
Window.FileSelector(list_browser, 'Select pose folder:')
elif LibPath != "":
list_select(_load_menu.val)
elif evt == EVENT_LISTLOAD: #Listbox folder loading
partPose = _partial_slider.val
if ListTabTog == 1: #Listbox use
foldTog = 0
line_top = 0 #Re-set top line of listbox to keep things in-bounds
listScroll = 1 - wheelslider
Window.FileSelector(list_browser, 'Select pose folder:')
elif evt == EVENT_SAVE: #Save button for both save tab and listbox tab
if ListTabTog == 1:
temp = LibSave
LibSave = 1 #Listbox use
if LibSave == 0:
if saveBatch == 0: Window.FileSelector(goSave, 'File to save as?') #File browse saving
elif saveBatch == 1: Window.FileSelector(save_batch, 'File to save as?')#Animated batch support
if LibSave == 1: #Menu and listbox browse saving
savename = Create("Pose")
saveblock = [("Enter save name:", savename, 0, 30, "Enter name as which to save pose file.")]
saveVal = PupBlock("Save to list browse folder", saveblock)
if saveVal == 1 and LibPath != "":
savename = sys.join(LibPath,savename.val)
if saveBatch == 0: goSave(savename)
elif saveBatch == 1: save_batch(savename)
LibPath = sys.join(LibPath, sys.basename(Blender.Get('filename')))#pass a dummy filename to list_browser
list_browser(LibPath) #Refesh the menu to display new addition
if ListTabTog == 1: LibSave = temp
elif evt == EVENT_QUIT: #"Quit" button
if setprefs == 1: update_ini()
Exit()
return
elif evt == EVENT_CONVERT: #load tab restmatrix conversion toggle
PoserConvert = 1 - PoserConvert
if PoserConvert == 1:
PoseMat = 0
elif evt == EVENT_FLIP: #load tab "Flip Pose" toggle
MirrorPose = 1 - MirrorPose
if MirrorPose == 1:
MirrorBone = 0
elif evt == EVENT_SCREEN: #load tab screen pose toggle
ScreenPose = 1 - ScreenPose
if DupPose == 1:
DupPose = 0
elif evt == EVENT_DUP: #load tab symmetrize pose toggle
DupPose = 1 - DupPose
if ScreenPose == 1:
ScreenPose = 0
elif evt == EVENT_SCR_R: #load tab screen right toggle
screenL = screenR
screenR = 1 - screenR
dupR = screenR
dupL = screenL
elif evt == EVENT_SCR_L: #load tab screen left toggle
screenR = screenL
screenL = 1 - screenL
dupR = screenR
dupL = screenL
elif evt == EVENT_DUP_R: #load tab symmetrize right toggle
dupL = dupR
dupR = 1 - dupR
screenR = dupR
screenL = dupL
elif evt == EVENT_DUP_L: #load tab symmetrize left toggle
dupR = dupL
dupL = 1 - dupL
screenR = dupR
screenL = dupL
elif evt == EVENT_MIX: #load tab additive modifier toggle
MixPose = 1 - MixPose
elif evt == EVENT_EMP: #Toggle adjusted to allow multiple menu selections
if OptPose == 0: #save tab general options toggle
OptPose = 1 - OptPose
if OptPose == 1: export_options()
elif evt == EVENT_POSEMAT: #load tab posematrix conversion toggle
PoseMat = 1 - PoseMat
if PoseMat == 1:
PoserConvert = 0
elif evt == EVENT_ZERO: #load tab zero pose button
Zero_Pose()
elif evt == EVENT_SUFFPREF: #options tab symmetry button
Symmetry_Notation()
elif evt == EVENT_PREF: #options tab symmetry prefix toggle
pref = 1 - pref
suff = 1 - suff
elif evt == EVENT_SUFF: #options tab symmetry suffix toggle
suff = 1 - suff
pref = 1 - pref
elif evt == EVENT_PART: #Toggle adjusted to allow multiple menu selections
if TrimPose == 0: #save tab partial groups toggle
TrimPose = 1 - TrimPose
if TrimPose == 1: partSave()
if TrimPose == 0:
partVar = 0
sideVar = 0
elif evt == EVENT_APPLY: #load tab realtime application button
realTime = 1 - realTime
partPose = _partial_slider.val
load_pose('')
elif evt == EVENT_NOTRANS: #save tab no translation modifier toggle
No_Trans = 1 - No_Trans
elif evt == EVENT_NOSCALE: #save tab no scaling modifier toggle
No_Scale = 1 - No_Scale
elif evt == EVENT_BONE: #options tab boneroll button
Bone_Menu()
elif evt == EVENT_LIB: #Toggle adjusted to allow multiple menu selections
if LibTog == 0: #Load tab general options toggle
LibTog = 1 - LibTog
if LibTog == 1: Load_Menu()
elif evt == EVENT_TAB_L: #Load tab
if NumTabTog == 1: zero_entries() #Clear the numerical tab buttons if the tab is not in use.
LoadTabTog = 1
SaveTabTog = 0
MiscTabTog = 0
NumTabTog = 0
if dualList == 0:
if wheelslider == 1:
if ListTabTog == 1: listScroll = line_top #store the current listbox top line
elif BonesTabTog == 1: listScrollB = line_top
elif AnimTabTog == 1: listScrollA = line_top
ListTabTog = 0
BonesTabTog = 0
AnimTabTog = 0
boxList = [] #clear the display list
elif evt == EVENT_TAB_S: #Save tab
if NumTabTog == 1: zero_entries() #Clear the numerical tab buttons if the tab is not in use.
LoadTabTog = 0
SaveTabTog = 1
MiscTabTog = 0
NumTabTog = 0
if dualList == 0:
if wheelslider == 1:
if ListTabTog == 1: listScroll = line_top
elif BonesTabTog == 1: listScrollB = line_top
elif AnimTabTog == 1: listScrollA = line_top
ListTabTog = 0
BonesTabTog = 0
AnimTabTog = 0
boxList = []
elif evt == EVENT_TAB_M: #options (Miscelllaneous) Tab
if NumTabTog == 1: zero_entries() #Clear the numerical tab buttons if the tab is not in use.
LoadTabTog = 0
SaveTabTog = 0
MiscTabTog = 1
NumTabTog = 0
if dualList == 0:
if wheelslider == 1:
if ListTabTog == 1: listScroll = line_top
elif BonesTabTog == 1: listScrollB = line_top
elif AnimTabTog == 1: listScrollA = line_top
ListTabTog = 0
BonesTabTog = 0
AnimTabTog = 0
boxList = []
elif evt == EVENT_TAB_N: #Numerical entry tab
LoadTabTog = 0
SaveTabTog = 0
MiscTabTog = 0
NumTabTog = 1
if dualList == 0:
if wheelslider == 1:
if ListTabTog == 1: listScroll = line_top
elif BonesTabTog == 1: listScrollB = line_top
elif AnimTabTog == 1: listScrollA = line_top
ListTabTog = 0
BonesTabTog = 0
AnimTabTog = 0
boxList = []
lastBone = ""
if len(boxBones) > 0: lastBone = boxBones[len(boxBones)-1]
entry_values() #load the dial values
elif evt == EVENT_TAB_T: #lisT tab
if wheelslider == 1:
if BonesTabTog == 1: listScrollB = line_top
elif AnimTabTog == 1: listScrollA = line_top
line_top = listScroll
if BonesTabTog == 1 or AnimTabTog == 1: in_box = 0
boxList = poseList #Fill the pose list for display
ListTabTog = 1
BonesTabTog = 0
AnimTabTog = 0
if dualList == 0:
if NumTabTog == 1: zero_entries() #Clear the numerical tab buttons if the tab is not in use.
LoadTabTog = 0
SaveTabTog = 0
MiscTabTog = 0
NumTabTog = 0
elif evt == EVENT_TAB_B: #Bones tab
if wheelslider == 1:
if ListTabTog == 1: listScroll = line_top
elif AnimTabTog == 1: listScrollA = line_top
line_top = listScrollB
if ListTabTog == 1 or AnimTabTog == 1: in_box = 0 #Re-set to keep multi-task list from bleeding over
boxList = [] #clear the display list (re-filled in gui listbox draw event)
ListTabTog = 0
BonesTabTog = 1
AnimTabTog = 0
if dualList == 0:
if NumTabTog == 1: zero_entries() #Clear the numerical tab buttons if the tab is not in use.
LoadTabTog = 0
SaveTabTog = 0
MiscTabTog = 0
NumTabTog = 0
if NumTabTog == 1: entry_values() #Refresh both numbers tab and the boxBones display list
elif evt == EVENT_TAB_A: #Animation frames tab
if wheelslider == 1:
if ListTabTog == 1: listScroll = line_top
elif BonesTabTog == 1: line_top = listScrollB
line_top = listScrollA
if ListTabTog == 1 or BonesTabTog == 1: in_box = 0 #Re-set to keep multi-task list from bleeding over
boxList = [] #clear the display list (re-filled in gui listbox draw event)
ListTabTog = 0
BonesTabTog = 0
AnimTabTog = 1
if dualList == 0:
if NumTabTog == 1: zero_entries() #Clear the numerical tab buttons if the tab is not in use.
LoadTabTog = 0
SaveTabTog = 0
MiscTabTog = 0
NumTabTog = 0
curFrame = Blender.Get('curframe')
elif evt == EVENT_LISTSCROLL: #listbox slider handling
if ListTabTog == 1: #This handles three different listbox sliders
listScroll = int(_scroll_slider.val)
if listScroll == 0: listScroll += 1
elif BonesTabTog == 1:
listScrollB = int(_scroll_slider.val)
if listScrollB == 0: listScrollB += 1
elif AnimTabTog == 1:
listScrollA = int(_scroll_slider.val)
if listScrollA == 0: listScrollA += 1
in_box = 0 #Keep mouse event from mis-interpreting click release
elif evt == EVENT_WHEEL: #options tab wheelmouse toggle
wheelslider = 1 - wheelslider
if wheelslider == 1: #Convert between displayed line number values and list index values
listScroll -= 1
listScrollB -= 1
listScrollA -= 1
if wheelslider == 0:
listScroll += 1
listScrollB += 1
listScrollA += 1
elif evt == EVENT_NUMB: #options tab line numbers toggle
numList = 1 - numList
elif evt == EVENT_FOLDER: #listbox folder browse toggle
foldTog = 1 - foldTog
box_select = [] #clear the display list
if foldTog == 1:
line_top = 0 #Re-set top line of listbox to keep things in-bounds
listScroll = 1
folder_browse(LibPath)
if foldTog == 0:
LibPath = sys.join(LibPath, sys.basename(Blender.Get('filename')))#list_browser needs a dummy filename
list_browser(LibPath)
batchLoad = 0 #turn off batch mode
batchSave = 0
boxList = poseList #fill the list
elif evt == EVENT_BONEMENU: #Bones list options button
bonebox_menu()
elif evt == EVENT_GROUP: #Toggle adjusted to allow multiple menu selections
if GroupTog == 0: #Load tab partial groups toggle
GroupTog = 1 - GroupTog
if GroupTog == 1: GroupLoad_Menu()
elif evt == EVENT_NOROLL: #Save tab restmatrix conversion button
NoRollTog = 1 - NoRollTog
NoRollPose = NoRollTog
elif evt == EVENT_IK: #Save tab posematrix conversion button
IKTog = 1 - IKTog
IKPose = IKTog
elif evt == EVENT_DUAL: #Dual display option toggle (with width change)
dualList = 1 - dualList
if dualList == 1:
if _dual_width.val == 240: _dual_width.val = 180 #resets only if user hasn't altered defaults
ListTabTog = 1 #Turn on the pose listbox
boxList = poseList #Fill the pose list for display
if dualList == 0:
if _dual_width.val == 180: _dual_width.val = 240
if ListTabTog == 1: #Close lists
if wheelslider == 1: listScroll = line_top
ListTabTog = 0
elif BonesTabTog == 1:
if wheelslider == 1: listScrollB = line_top
BonesTabTog = 0
elif AnimTabTog == 1:
if wheelslider == 1: listScrollA = line_top
AnimTabTog = 0
in_box = 0 #prevent mouse-click errors with listbox re-positioning
elif evt == EVENT_ENTRY: #numerical tab pose mode entries
numerical_entry()
elif evt == EVENT_KEYF: #numerical tab keyframes toggle
myKeys = 1 - myKeys
if myKeys == 1:
options = PupMenu("Select keyframing option: %t|Add keyframes for all bones %x1\
|Add Keyframes only for bones list selections %x2|Add keyframe only for active bones list bone %x3\
|Cancel %x5")
if options == 1: keyType = 1
elif options == 2: keyType = 2
elif options == 3: keyType = 3
else:
if keyType == 0: myKeys = 0 #cancel selected or -1 returned
elif myKeys == 0: keyType = 0
elif evt == EVENT_EDIT: #numerical tab edit mode entries
numerical_edit()
elif evt == EVENT_UNIQUE: #euler unique option in options tab
uniqueRot = 1 - uniqueRot
elif evt == EVENT_FLIPBONE: #load tab "Flip Bones" button
MirrorBone = 1 - MirrorBone
if MirrorBone == 1:
MirrorPose = 0
elif evt == EVENT_ANIMMENU: #Select button on frames tab
anim_menu()
elif evt == EVENT_BATCHLOAD: #Load button on poses tab (batch mode)
if len(boxPoses) > 0:
partPose = _partial_slider.val
open_batch(boxPoses[0])
elif evt == EVENT_BATCHTOG: #Batch toggle on poses tab.
if foldTog == 0:
batchLoad = 1 - batchLoad
if batchLoad == 1:
openBatch = 1 #batch loading toggle
LibTog = 1 #load options menu toggle
saveBatch = 1 #batch save toggle
OptPose = 1 #save options menu toggle
elif batchLoad == 0: #reset load/save menu toggles, if appropriate
openBatch = 0
saveBatch = 0
if LibVar == HipPose == hipR == hipT == bodyscale == addscale \
== openBatch == askKey == torsoZero == 0:
LibTog = 0
if EmpPose == IKPose == NoRollPose == saveBatch == LibSave == 0:
OptPose = 0
boxPoses = []
elif evt == EVENT_BATCHFRAMES: #Batch toggle on frames tab
batchFrames = 1 - batchFrames
elif evt == EVENT_IKTOG: #IK button on frames tab
IK_menu()
elif evt == EVENT_TOOLS: #Tolls button on frames tab
tools_menu()
elif evt == EVENT_REG:
regmenu = PupMenu("Options: %t|Set GUI preferences with next script exit %x1\
|Remove preferred settings %x2|Cancel %x3")
if regmenu == 1:
setprefs = 1 - setprefs
if setprefs == 1: notice = PupMenu("Settings will be stored in registry when you exit the script.")
elif regmenu == 2:
saveloc = Blender.Get('homedir')
if sys.exists(sys.join(saveloc,"PoseHandlerSettings.txt")) == 1:
os.remove(sys.join(saveloc,"PoseHandlerSettings.txt"))
elif evt == EVENT_POSERSAVE:
global JPdict
PoserSave = 1 - PoserSave
if PoserSave == 0: JPdict.clear()
if PoserSave == 1:
menu = PupMenu("Poser export requires joint rotation order data %t|Parse joint order from a cr2/crz or pz2/p2z %x1\
|Load joint order from a saved text file %x2|Save current loaded joint orders to a text file %x3\
|Change joint order for bones list selections %x4|Use automatic (generic) joint rotation order %x5")
if menu == 1:
JPdict = {}
Window.FileSelector(parse_JP_order, 'Browse for .cr2 or .pz2.')
elif menu == 2:
JPdict = {}
Window.FileSelector(import_JP_order, 'Browse for .txt file.')
elif menu == 3:
Window.FileSelector(export_JP_order, 'Browse for save location.')
elif menu == 4:
change_JP_order()
elif evt == EVENT_BONESEARCH:
bone_search()
elif evt == EVENT_BONEGRAB:
bone_grab()
elif evt == EVENT_POSESEARCH:
if foldTog == 0: #disabled in folder browser mode
pose_search()
elif evt == EVENT_NOEVENT: #This prompts a gui re-draw
pass
else: return # no need to redraw if nothing changed
Draw()
def update_ini():
saveloc = Blender.Get('homedir')
temptext = ""
temptext += "LoadTabTog<:>%i\n" %(LoadTabTog)
temptext += "SaveTabTog<:>%i\n" %(SaveTabTog)
temptext += "MiscTabTog<:>%i\n" %(MiscTabTog)
temptext += "ListTabTog<:>%i\n" %(ListTabTog)
temptext += "BonesTabTog<:>%i\n" %(BonesTabTog)
temptext += "NumTabTog<:>%i\n" %(NumTabTog)
temptext += "AnimTabTog<:>%i\n" %(AnimTabTog)
temptext += "numList<:>%i\n" %(numList)
temptext += "wheelslider<:>%i\n" %(wheelslider)
temptext += "dualList<:>%i\n" %(dualList)
temptext += "uniqueRot<:>%i\n" %(uniqueRot)
temptext += "loadlist<:>%s\n" %(loadlist)
temptext += "_dual_width<:>%i\n" %(_dual_width.val)
temptext += "bodyscale<:>%i\n" %(bodyscale)
temptext += "addscale<:>%i\n" %(addscale)
temptext += "HipPose<:>%i\n" %(HipPose)
temptext += "hipR<:>%i\n" %(hipR)
temptext += "hipT<:>%i\n" %(hipT)
temptext += "LibVar<:>%i\n" %(LibVar)
temptext += "LibTog<:>%i\n" %(LibTog)
temptext += "LibPath<:>%s\n" %(LibPath)
temptext += "poseList<:>%s\n" %(poseList)
temptext += "torsoZero<:>%i\n" %(torsoZero)
temptext += "poseStart<:>%i\n" %(poseStart)
temptext += "posePages<:>%i\n" %(posePages)
temptext += "poseLimit<:>%i\n" %(poseLimit)
temptext += "suff<:>%i\n" %(suff)
temptext += "pref<:>%i\n" %(pref)
temptext += "right<:>%s\n" %(right)
temptext += "left<:>%s\n" %(left)
temptext += "LibPath<:>%s\n" %(LibPath)
temptext += "right_alt<:>%s\n" %(right_alt)
temptext += "left_alt<:>%s\n" %(left_alt)
temptext += "minVal<:>%i\n" %(minVal)
temptext += "maxVal<:>%i\n" %(maxVal)
temptext += "boneSort<:>%i" %(boneSort)
settings = open(sys.join(saveloc,"PoseHandlerSettings.txt"),"w")
settings.write(temptext)
settings.close()
def get_from_ini():
global LoadTabTog, SaveTabTog, MiscTabTog, ListTabTog, BonesTabTog, numList, wheelslider, dualList,\
NumTabTog, uniqueRot, loadlist, AnimTabTog, _dual_width, listScroll, listScrollA, listScrollB
global bodyscale, addscale, HipPose, hipR, hipT, LibVar, openBatch, LibTog, \
TrimPose, LibPath, poseList, torsoZero, boneSort
global poseStart, posePages, poseLimit, suff, pref, right, left , lenR, lenL, right_alt, \
left_alt, maxVal, minVal
saveloc = Blender.Get('homedir')
if sys.exists(sys.join(saveloc,"PoseHandlerSettings.txt")) == 1:
settings = open(sys.join(saveloc,"PoseHandlerSettings.txt"),"r")
lines = settings.readlines()
for line in lines:
data = line.rstrip("\n").split("<:>")
if data[0] == "LoadTabTog": LoadTabTog = int(data[1])
elif data[0] == "SaveTabTog": SaveTabTog = int(data[1])
elif data[0] == "MiscTabTog": MiscTabTog = int(data[1])
elif data[0] == "ListTabTog": ListTabTog = int(data[1])
elif data[0] == "BonesTabTog": BonesTabTog = int(data[1])
elif data[0] == "NumTabTog": NumTabTog = int(data[1])
elif data[0] == "AnimTabTog": AnimTabTog = int(data[1])
elif data[0] == "numList": numList = int(data[1])
elif data[0] == "wheelslider": wheelslider = int(data[1])
elif data[0] == "dualList": dualList = int(data[1])
elif data[0] == "uniqueRot": uniqueRot = int(data[1])
elif data[0] == "loadlist": loadlist = data[1]
elif data[0] == "_dual_width": _dual_width.val = int(data[1])
elif data[0] == "bodyscale": bodyscale = int(data[1])
elif data[0] == "addscale": addscale = int(data[1])
elif data[0] == "HipPose": HipPose = int(data[1])
elif data[0] == "hipR": hipR = int(data[1])
elif data[0] == "hipT": hipT = int(data[1])
elif data[0] == "LibVar": LibVar = int(data[1])
elif data[0] == "LibTog": LibTog = int(data[1])
elif data[0] == "LibPath": LibPath = data[1]
elif data[0] == "poseList": poseList = data[1]
elif data[0] == "torsoZero": torsoZero = int(data[1])
elif data[0] == "poseStart": poseStart = int(data[1])
elif data[0] == "posePages": posePages = int(data[1])
elif data[0] == "poseLimit": poseLimit = int(data[1])
elif data[0] == "suff": suff = int(data[1])
elif data[0] == "pref": pref = int(data[1])
elif data[0] == "right": right = data[1]
elif data[0] == "left": left = data[1]
elif data[0] == "right_alt": right_alt = data[1]
elif data[0] == "left_alt": left_alt = data[1]
elif data[0] == "maxVal": maxVal = int(data[1])
elif data[0] == "minVal": minVal = int(data[1])
elif data[0] == "boneSort": boneSort = int(data[1])
settings.close()
lenR = len(right)
lenL = len(left)
listScroll = 1 - wheelslider
listScrollB = 1 - wheelslider
listScrollA = 1 - wheelslider
if sys.exists(LibPath) != 2:
LibPath = ""
libVar = 0
loadlist = []
poseStart = 0
posePages = 0
else:
LibPath = sys.join(LibPath, sys.basename(Blender.Get('filename')))#list_browser needs a dummy filename
list_browser(LibPath)
if ListTabTog == 1: boxList = poseList #fill the list
else: boxList = [] #force a refresh for anim and bones tab
get_from_ini()
Register(gui, event, bevent)
if Blender.Get('version') < 242: #notification for versions prior to 242
notice = PupMenu("Some features will not function properly in versions before 2.42: %t\
|- Numerical entry dials on 'number' tab will respond incorrectly %x1\
|- Some keyframe options on 'frames' tab will not work %x2\
|- All IK-related functions on 'bones' tab will be disabled %x3\
|Some features will not work without a CVS build of 242: %x6\
|- The 'bones' tab 'grab' function will not work. %x4\
|- The 'bones' tab 'search' function will not select a bone in 3d view. %x5")

File Metadata

Mime Type
text/x-python
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ab/4c/c22e9c941d0d8ed7dcd994ae1b79

Event Timeline