Don’t panic you don’t need all that code… below is a py from the mocap madness addon i’ve been grinding away on. In the execute method of the operator MocapMadness_ComparePoses(bpy.types.Operator): it calls the posecompare routine as a thread. I had to delete the code here to be under max post length. Full code is in the link. I’m using a global xml object as a datalayer. In my case as posecompare runs it finds cycles and branches and appends them as an xml node. The draw method of the panel MMComparePosePanel(MocapMadness3DPanel, bpy.types.Panel) updates the cycle and branch count.
In principle tho it should work with custom properties as well, for instance a %completed, which “click over” when displayed in the draw method of the panel.
import bpy
from bpy.props import *
from xml.etree.ElementTree import Element
from math import radians
import MocapMadness.MMData as mocapmadness
from threading import Thread
########################################################################
'''
POSE MATCHING SCRIPT
v 0.0.0
An attempt to find best match poses in an action collection and mark
them in actions with markers
'''
########################################################################
def compare_fcurves(fcurves1,frame1,fcurves2,frame2,knockoff=100.0,MAX_EULER_ROT_DIFF=radians(10)):
diff = 0.0
'''
BIG SCOPE for IMPROVEMENT HERE.. need to look at locs and rots to also ditch big rots
make a class method.
'''
root = 'pose.bones["Hips"]'
j = 0
#fcurve1 = fcurves1[frame_index1]
while j < len(fcurves1)-1:
fcurve1 = fcurves1[j]
fcurve2 = fcurves2[j]
if fcurve1.data_path.find(root) < 0 :
#print("%d,%d"%(j,len(fcurves1)))
df = abs(fcurve1.evaluate(frame1) - fcurve2.evaluate(frame2))
#print(df)
if diff > knockoff:
return(1000.0)
if fcurve1.data_path.find("rotation") >= 0 and df > MAX_EULER_ROT_DIFF : #work out how to use for quaternions
#print("rotdiff")
return(1000.0)
diff += df
j += 1
#print(diff)
return(diff)
def posecompare(operator,tolerance,MIN_CYCLE_LENGTH=15,FRAMESTEP=2,MAX_EULER_ROT_DIFF=radians(5.0)): #Minimum Cycle length to set up.
*****************8 CODE DELETED TO MAKE ROOM FOR POST ***************************************
class MocapMadness3DPanel():
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
@classmethod
def poll(cls, context):
return (mocapmadness.data is not None)
class MMComparePosePanel(MocapMadness3DPanel, bpy.types.Panel):
bl_idname = "MMComparePoses"
bl_label = "Compare Poses"
@classmethod
def poll(cls,context):
return(mocapmadness.data != None)
def draw(self, context):
layout = self.layout
layout.label(text = "Cycles:%d"%len(mocapmadness.data.findall("Actions/Action/cycle")))
layout.label(text = "Branches:%d"%len(mocapmadness.data.findall("Actions/Action/branch")))
layout.label("Small Class")
layout.operator("mocapmadness.posecompare")
layout.operator("mocapmadness.posecompare",text="Clean").clean=True
class MMPlayPanel(MocapMadness3DPanel, bpy.types.Panel):
bl_idname = "VIEW3D_PT_test_2"
bl_label = "Panel Two"
@classmethod
def poll(self,context):
try:
ad = context.object.animation_data or None
if ad:
return True
except:
return False
return False
#Going to be used as a play mocap panel
def draw(self, context):
layout = self.layout
layout.menu("mocapmadness.actionmenu")
self.layout.label("MocapMadness Player")
if context.object.animation_data:
action = context.object.animation_data.action or None
if action:
layout.prop(action,"name")
#self.layout.operator("mocapmadness.compareposes")
class MocapMadness_ComparePoses(bpy.types.Operator):
if mocapmadness.settings:
print("GOT SETTINGS")
bl_idname = "mocapmadness.posecompare"
bl_label = "Compare Poses"
bl_option = "REGISTER"
mocapmadness_name = bl_idname
print("COMPARE POSE OPERATOR CLASS DEF")
for cmd in mocapmadness.SetOperatorProperties("mocapmadness.posecompare"):
exec(cmd)
clean = BoolProperty("Clean",default=False)
#self.threshold = 0.001
def clean_compare(self,context):
#remove all the previous branches
Actions = mocapmadness.data.findall("Actions/Action")
print("cleaning %d cycles"%len(Actions))
for Action in Actions:
children = Action.findall("*") #// all childnodes
for child in children:
Action.remove(child)
return True
def invoke(self, context, event):
mocapmadness.SetOperatorSettings(self)
wm = context.window_manager
#return wm.invoke_props_dialog(self)
if not self.clean:
return wm.invoke_props_dialog(self)
else:
print("clean")
#return wm.invoke_popup(self)
return self.execute(context)
def draw(self,context):
if self.clean:
return
layout = self.layout
layout.prop(self,"tolerance")
layout.prop(self,"max_euler_rot_Diff")
layout.prop(self,"max_location_Diff")
layout.prop(self,"framestep")
layout.prop(self,"min_cycle_length")
layout.prop(self,"Threaded",icon="ERROR")
#print("DRAEWWWW")
def execute(self, context):
#posecompare(tolerance,MIN_CYCLE_LENGTH=15,FRAMESTEP=2)
self.clean_compare(context)
if self.clean:
return{'FINISHED'}
if self.Threaded:
t = Thread(target=posecompare,args=(self,self.tolerance, \
self.min_cycle_length,self.framestep,radians(self.max_euler_rot_Diff)))
t.start()
else:
posecompare(operator=self,tolerance=self.tolerance,FRAMESTEP=self.framestep, \
MIN_CYCLE_LENGTH=self.min_cycle_length,MAX_EULER_ROT_DIFF=radians(self.max_euler_rot_Diff))
mocapmadness.PreserveSettings(self)
#bpy.ops.graph.clean(threshold=self.threshold)
return{'FINISHED'}
print("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD")
def register():
bpy.utils.register_class(MMComparePosePanel)
bpy.utils.register_class(MMPlayPanel)
bpy.utils.register_class(MocapMadness_ComparePoses)
def unregister():
bpy.utils.unregister_class(MMComparePosePanel)
bpy.utils.unregister_class(MMPlayPanel)
bpy.utils.unregister_class(MocapMadness_ComparePoses)