Files
2025-09-29 00:52:08 +02:00

182 lines
7.1 KiB
Python
Executable File

""" This module provides access to per-frame animation data from FaceFX Studio.
Owner: Jamie Redmond
Copyright (c) 2002-2011 OC3 Entertainment, Inc.
"""
from FxStudio import FaceFXError, getBoneFrame, getFaceGraphFrame, getSkeletonFrame, getSkeletonRefFrame, issueCommand
from FxFaceGraph import FaceGraph
from FxMath import Vector, Quaternion
class NodeFrameData(object):
""" Holds information about the state of a face graph node for the current animation frame in FaceFX Studio. """
def __init__(self, nodeFrameDataTupleFromStudio):
""" Initializes the node frame data from a tuple sent back from FaceFX Studio. """
self.name = nodeFrameDataTupleFromStudio[0]
self.value = nodeFrameDataTupleFromStudio[1]
def __str__(self):
""" Returns the string repsentation of the node frame data. """
return '{0}: {1}'.format(self.name, self.value)
class FaceGraphFrame(object):
""" Holds information about the state of all face graph nodes for the current animation frame in FaceFX Studio. """
def __init__(self):
""" Initializes the face graph frame from a tuple sent back from FaceFX Studio. """
self.nodes = [NodeFrameData(n) for n in getFaceGraphFrame()]
def __str__(self):
""" Returns the string representation of the face graph frame. """
r = ""
nodeIndex = 0
for node in self.nodes:
r += "[" + str(nodeIndex) + "]: " + str(node) + "\n"
nodeIndex += 1
return r
class BoneFrameData(object):
""" Holds information about the state of a FaceFX-controlled bone for the current animation frame in FaceFX Studio.
The bone is given in local (parent) space.
weight is the currently calculated weight that FaceFX is using for the bone.
"""
def __init__(self, boneFrameDataTupleFromStudio):
""" Initializes the bone frame data from a tuple sent back from FaceFX Studio. """
self.name = boneFrameDataTupleFromStudio[0]
self.position = Vector(boneFrameDataTupleFromStudio[1])
self.rotation = Quaternion(boneFrameDataTupleFromStudio[2])
self.scale = Vector(boneFrameDataTupleFromStudio[3])
self.weight = boneFrameDataTupleFromStudio[4]
def __str__(self):
""" Returns the string repsentation of the bone frame data. """
return '{0}:\n position: {1}\n rotation: {2}\n scale: {3}\n weight: {4}'.format(self.name, self.position, self.rotation, self.scale, self.weight)
class BoneFrame(object):
""" Holds information about the state of all FaceFX-controlled bones for the current animation frame in FaceFX Studio. """
def __init__(self):
""" Initializes the bone frame from a tuple sent back from FaceFX Studio. """
self.bones = [BoneFrameData(b) for b in getBoneFrame()]
def __str__(self):
""" Returns the string representation of the bone frame. """
r = ""
boneIndex = 0
for bone in self.bones:
r += "[" + str(boneIndex) + "]: " + str(bone) + "\n"
boneIndex += 1
return r
class AnimationFrame(object):
""" Holds information about the state of all face graph nodes and all FaceFX-controlled bones for the current animation frame in FaceFX Studio. """
def __init__(self):
""" Initializes the animation frame with a face graph frame and a bone frame sent back from FaceFX Studio. """
self.faceGraphFrame = FaceGraphFrame()
self.boneFrame = BoneFrame()
def __str__(self):
""" Returns the string representation of the animation frame. """
return "==============[ Face Graph Frame ]==============\n" + str(self.faceGraphFrame) + "==============[ Bone Frame ]==============\n" + str(self.boneFrame)
class SkeletonFrameData(object):
""" Holds information about the state of the entire skeleton for the current animation frame in FaceFX Studio.
The bone is given in local (parent) space.
"""
def __init__(self, skeletonFrameDataTupleFromStudio):
""" Initializes the skeleton frame data from a tuple sent back from FaceFX Studio. """
self.name = skeletonFrameDataTupleFromStudio[0]
self.position = Vector(skeletonFrameDataTupleFromStudio[1])
self.rotation = Quaternion(skeletonFrameDataTupleFromStudio[2])
self.scale = Vector(skeletonFrameDataTupleFromStudio[3])
self.parentName = str(skeletonFrameDataTupleFromStudio[4])
self.parent = None
def __str__(self):
""" Returns the string representation of the skeleton frame data. """
return '{0}:\n position: {1}\n rotation: {2}\n scale: {3}\n parent: {4}'.format(self.name, self.position, self.rotation, self.scale, self.parentName)
class SkeletonFrame(object):
""" Holds information about the state of the entire skeleton for the current animation frame in FaceFX Studio. """
def __init__(self, forceBindPose=False):
""" Initializes the skeleton frame from a tuple sent back from FaceFX Studio. """
if forceBindPose == True:
self.bones = [SkeletonFrameData(b) for b in getSkeletonRefFrame()]
else:
self.bones = [SkeletonFrameData(b) for b in getSkeletonFrame()]
# Link up all of the bones in the skeleton.
for bone in self.bones:
if bone.parentName != "None":
parent = [p for p in self.bones if p.name == bone.parentName]
if len(parent) == 1:
bone.parent = parent[0]
else:
raise RuntimeError("Skeleton hierarchy error in FrameData.SkeletonFrame!")
def __str__(self):
""" Returns the string representation of the skeleton frame. """
r = ""
boneIndex = 0
for bone in self.bones:
r += "[" + str(boneIndex) + "]: " + str(bone) + "\n"
boneIndex += 1
return r
class SkeletonRefFrame(SkeletonFrame):
""" Holds information about the state of the entire skeleton in its bind pose in FaceFX Studio. """
def __init__(self):
""" Initializes the skeleton reference frame from a tuple sent back from FaceFX Studio. """
SkeletonFrame.__init__(self, True)
def CreateBonePoseFromCurrentFrame(bonePoseName):
""" Creates a bone pose node with the specified name using the FaceFX bones as they are in the current frame of animation in FaceFX Studio. """
# Check that there isn't already a node in the face graph with the same name as the one we're trying to create.
fg = FaceGraph()
if fg.findNode(bonePoseName) is not None:
raise FaceFXError('FrameData.CreateBonePoseFromCurrentFrame: cannot create bone pose named {0} because a node with that name already exists in the face graph!'.format(bonePoseName))
# Start the creation process.
issueCommand('batch')
issueCommand('graph -addnode -name "{0}" -nodetype "FxBonePoseNode"'.format(bonePoseName))
# Grab the FaceFX bones for the current frame.
bf = BoneFrame()
# Add them to the bone pose node.
for b in bf.bones:
issueCommand('bonepose -name "{0}" -addbone "{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}|{9}|{10}|{11}"'.format(bonePoseName, b.name, b.position.x, b.position.y, b.position.z, b.rotation.w, b.rotation.x, b.rotation.y, b.rotation.z, b.scale.x, b.scale.y, b.scale.z))
# Finish the creation process. Don't pass any flags to execBatch to avoid having signals fired that could contain batch commands themselves.
issueCommand('execBatch -addednodes')