*update. Found another method. still open maya with mdg time is faster. But this is not half bad.
T.L.D.R
the “om_plug_as_double_mdg_set_time” function is the fastest I could find.
be careful with currentTime eval=False and cmds.keyframe. Scroll down for the script.
Explanation:
Soooo I had to export a bunch of data (10k+ maya files) for some dataset.
Which forced me to find a fast way to export attrbutes per frame.
(And it also had to work in mayapy)
And I dint want to have to dive into a c++ plugin or anything.
So I compared a bunch of different ways that i know to getAttr and updating the maya time.
Problems:
1. Dont use cmds.currentTime(frameNr, eval=False) !!
This results in the attribute dg not being evaluated. sooooo its useless.
2. cmds.keyframe(attr, query=True, eval=True)
This only evaluates the active anim layer’s keyframe data.
And changing the active anim layer in mayapy is a nightmare soooo… yeah don’t use it. Also it only works if there are keyframes set. So a channel with a constraint will not return any value.
So for testing i made a simple sphere and 2 additive anim layers with some animation in them.
Timings with a sphere:
Shortest duration for each function tested 100 times:
1 2 3 4 5 6 7 8 9 |
cmds_get_attr_cmds_set_time 4.07231167047 cmds_get_attr_no_eval 0.142337732946 = ERROR RESULT IS NOT CORRECT!!! cmds_get_attr_arg_time 0.188162308316 cmds_get_attr_om_set_time 0.367623159295 mel_get_attr 0.211408009643 pymel_get_attr_om_set_time 0.466954109253 pymel_get_attr_arg_time 0.274113534882 cmds_eval_keyframe_om_set_time 0.0870191721787 = WARNING RESULT IS NOT CORRECT WHEN USING ANIM LAYERS or no keyframes exist!!! om_plug_as_double_om_set_time 0.159895729674 = FASTEST RESULT! |
Timing with a heavier scene with skinning and constraints.
1 2 3 4 5 6 7 8 9 |
cmds_get_attr_cmds_set_time 8.21378763774 cmds_get_attr_no_eval 0.0179609034819 = ERROR RESULT IS NOT CORRECT!!! cmds_get_attr_arg_time 0.484571124028 cmds_get_attr_om_set_time 6.1720235551 mel_get_attr 0.494029596401 pymel_get_attr_om_set_time 6.14228203626 pymel_get_attr_arg_time 0.499620215035 cmds_eval_keyframe_om_set_time 0.00708293210846 = WARNING RESULT IS NOT CORRECT WHEN USING ANIM LAYERS or no keyframes exist!!! om_plug_as_double_om_set_time 0.468321232931 = FASTEST RESULT! |
Timing becomes more clear this way.
Code should mostly speak for itself.
If you have any questions just ask.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
from maya.api import OpenMayaAnim from maya.api import OpenMaya from maya import mel import inspect import timeit import pymel.core as pm ## Get the current UI Unit uiUnit = OpenMaya.MTime.uiUnit() ##### ## Util functions ##### def get_dep_node(n): return OpenMaya.MGlobal.getSelectionListByName(n).getDependNode(0) ################################ ## Get attr methods ################################ def cmds_get_attr_cmds_set_time(obj, attr): ''' The normal method of getting an attribute. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' fullName = "%s.%s" % (obj, attr) values = [] for frameNr in xrange(20): cmds.currentTime(frameNr) value = cmds.getAttr(fullName) values.append(value) return values def cmds_get_attr_no_eval(obj, attr): ''' ERROR DOESNT EVAL THE ATTR SO NO DATA WILL CHANGE The normal method of getting an attribute but without updating the time Doesnt work correctly as the attribute does not update. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' fullName = "%s.%s" % (obj, attr) values = [] for frameNr in xrange(20): cmds.currentTime(frameNr, update=False) value = cmds.getAttr(fullName) values.append(value) return values def cmds_get_attr_arg_time(obj, attr): ''' The normal method of getting an attribute but without updating the time but using the time argument of the get attr. Definitly the fastest method if you dont want to use open maya. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' fullName = "%s.%s" % (obj, attr) values = [] for frameNr in xrange(20): value = cmds.getAttr(fullName, time = frameNr) values.append(value) return values def cmds_get_attr_om_set_time(obj, attr): ''' Normal get attr but using open maya to change the time. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' fullName = "%s.%s" % (obj, attr) values = [] for frameNr in xrange(20): OpenMayaAnim.MAnimControl.setCurrentTime(OpenMaya.MTime(frameNr, uiUnit)) value = cmds.getAttr(fullName) values.append(value) return values def mel_get_attr(obj, attr): ''' Using mel get attr. using open maya to change the time. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' ## Slight speed optimisation, this way we dont have to construct the string every time. fullName = "getAttr -time %s "+obj+"."+attr values = [] for frameNr in xrange(20): #OpenMayaAnim.MAnimControl.setCurrentTime(OpenMaya.MTime(frameNr, uiUnit)) value = mel.eval(fullName%frameNr) values.append(value) return values def pymel_get_attr_om_set_time(obj, attr): ''' Using pymel get with openmaya set time. pymel basically uses the openmaya api so should be faster. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' ## Slight speed optimisation, this way we dont have to construct the string every time. pmAttr = pm.PyNode("%s.%s" % (obj, attr)) values = [] for frameNr in xrange(20): OpenMayaAnim.MAnimControl.setCurrentTime(OpenMaya.MTime(frameNr, uiUnit)) value = pmAttr.get() values.append(value) return values def pymel_get_attr_arg_time(obj, attr): ''' Using pymel get with passing time as argument pymel basically uses the openmaya api so should be faster. Definitly faster as setting time with maya api :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' ## Slight speed optimisation, this way we dont have to construct the string every time. pmAttr = pm.PyNode("%s.%s" % (obj, attr)) values = [] for frameNr in xrange(20): value = pmAttr.get(time=frameNr) values.append(value) return values def cmds_eval_keyframe_om_set_time(obj, attr): ''' WARNING DOESNT EVAL CORRECTLY WITH MULTIPLE ANIM LAYERS!! Use the cmds.keyframe function to evaluate the keyframe. Its really fast! but .... doesnt eval correctly with anim layers. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' fullName = "%s.%s" % (obj, attr) values = [] for frameNr in xrange(20): value = cmds.keyframe(fullName, query=True, eval=True, t=(frameNr,)) or 0 values.append(value) return values def om_plug_as_double_mdg_set_time(obj, attr): ''' Getting the plug from openMaya. Then create a time mdgContext and evaluate it. This is the fastest with layer compatability... however i suspect there to be issues when doing complex simulations. :param obj: object string name i.e. "pSphere1" :param attr: the attribute to check i.e. "tx" :return: a list of values per frame. ''' values = [] obj = get_dep_node(obj) objMfn = OpenMaya.MFnDependencyNode(obj) ## Get the plug of the node. (networkedplug = False, as it no longer profides a speed improvement) plug = objMfn.findPlug(attr, False) for frameNr in xrange(20): ## The creaction of the MDG context takes the longest! mdg = OpenMaya.MDGContext(OpenMaya.MTime(frameNr, uiUnit)) value = plug.asDouble(mdg) values.append(value ) return values ## Put all functions in a nice list fList = [cmds_get_attr_cmds_set_time, cmds_get_attr_no_eval, cmds_get_attr_arg_time, cmds_get_attr_om_set_time, mel_get_attr, pymel_get_attr_om_set_time, pymel_get_attr_arg_time, cmds_eval_keyframe_om_set_time, om_plug_as_double_mdg_set_time] ## Prepare some constants obj = "pSphere1" attr = "tx" ## Start testing all results resultList = [] print "Duration for each function 100 times: " for f in fList: ## Using the timeit module to check how long the functions last duration = timeit.timeit(f.__name__ + "('%s','%s')" % (obj, attr), setup="from __main__ import %s" % f.__name__, number=10) ## Run it once more to get the actual results result = f(obj, attr) ## Store the results in a list resultList.append(result) ## And report the total duration. print "\t", f.__name__, duration print "Accuracy comparison: " for index, results in enumerate( resultList[1:]): print "Function Name: ",fList[index+1].__name__ for rIndex, result in enumerate( results ): print "\t", result - resultList[0][rIndex] |