Speeding Up Per-Frame Functions in MB: FBSystem().Scene.Evaluate()
KxL on Autodesk’s ‘The Area‘ forum has really shed some light on to why MotionBuilder was so slow when iterating through frames. It all boils down to the usage of FBSystem().Scene.Evaluate(). This evaluates the entire scene and is very, very slow. Motionbuilder does not allow for getting a parameter at time i, or do partial scene evaluation.
So this basically means that any time you need to get the position, rotation, or matrix of a model, you should go to the keyfame data directly. (unless it’s a constraint!) Here is an example function that prints position every frame. It does so by getting the information from the fcurves themselves, and *not* querying the nodes.
#prints position given a model and a frame def PrintPosition(pModel,pFrame): lFrameTime = FBTime(0,0,0,pFrame) lAnimationNode = pModel.Translation.GetAnimationNode() lPositionV = FBVector3d( lAnimationNode.Nodes[0].FCurve.Evaluate(lFrameTime), lAnimationNode.Nodes[1].FCurve.Evaluate(lFrameTime), lAnimationNode.Nodes[2].FCurve.Evaluate(lFrameTime)) print lPositionV del(lFrameTime, lAnimationNode, lPositionV) |
Here is another example, showing how to build a transformation matrix from animation data (nodes). This is required because the normal way of requesting the matrix of a model in MBuilder (model.GetMatrix(pMatrix)), requires an Evaluate(). The following requires importing the following libraries: [math].
def FBMatrixFromAnimationNode( pModel, pTime ): lResult = FBMatrix() lTranslationNode = pModel.Translation.GetAnimationNode() lRotationNode = pModel.Rotation.GetAnimationNode() lScaleNode = pModel.Scaling.GetAnimationNode() lRotationV = FBVector3d( lRotationNode.Nodes[0].FCurve.Evaluate(pTime) * 0.017453292519943295769236907684886, lRotationNode.Nodes[1].FCurve.Evaluate(pTime) * 0.017453292519943295769236907684886, lRotationNode.Nodes[2].FCurve.Evaluate(pTime) * 0.017453292519943295769236907684886) lScaleV = FBVector3d( lScaleNode.Nodes[0].FCurve.Evaluate(pTime), lScaleNode.Nodes[1].FCurve.Evaluate(pTime), lScaleNode.Nodes[2].FCurve.Evaluate(pTime)) sphi = math.sin(lRotationV[0]) cphi = math.cos(lRotationV[0]) stheta = math.sin(lRotationV[1]) ctheta = math.cos(lRotationV[1]) spsi = math.sin(lRotationV[2]) cpsi = math.cos(lRotationV[2]) lResult[0] = (cpsi*ctheta)*lScaleV[0] lResult[1] = (spsi*ctheta)*lScaleV[0] lResult[2] = (-stheta)*lScaleV[0] lResult[4] = (cpsi*stheta*sphi - spsi*cphi)*lScaleV[1] lResult[5] = (spsi*stheta*sphi + cpsi*cphi)*lScaleV[1] lResult[6] = (ctheta*sphi)*lScaleV[1] lResult[8] = (cpsi*stheta*cphi + spsi*sphi)*lScaleV[2] lResult[9] = (spsi*stheta*cphi - cpsi*sphi)*lScaleV[2] lResult[10] = (ctheta*cphi)*lScaleV[2] lResult[12] = lTranslationNode.Nodes[0].FCurve.Evaluate(pTime) lResult[13] = lTranslationNode.Nodes[1].FCurve.Evaluate(pTime) lResult[14] = lTranslationNode.Nodes[2].FCurve.Evaluate(pTime) return lResult |
Soon, I hope to build an example tool showing how to iterate through frames and do facial motioncapture stabilization. That was sort of how all this came up, I wrote the tool, but it was ~20 times slower than it should have been.