I am trying to create a data driven animation face animation using a socket and Modal operator. In each loop of the Modal operator I receive data from a socket , use the data to create the keyframes of the animation and then play the animation. But i am unable to figure out how to stop the animation when the animation has reached the end keyframe.
Here is my code
SocketModalOperator.py
import bpy
import socket
import os
import sys
absolutePath = "/Users/pulkitjuneja/Documents/projects/EVA/scripts"
sys.path.append(absolutePath)
from animationController import main
class socketModal(bpy.types.Operator):
bl_idname = "object.modal_operator"
bl_label = "Lip synch Operator"
_timer = None
def __init__(self):
print("Listen Start")
os.chdir(absolutePath)
self.port = 1301
def __del__(self):
print("Listen End")
def execute(self, context):
print ("execute")
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.setblocking(0)
self.socket.bind(("127.0.0.1", self.port))
print('socket listening on port', self.port)
self._timer = context.window_manager.event_timer_add(1.0, context.window)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
print ("modal")
if event.type == "TIMER":
data = None
try:
data = self.socket.recv(1024 * 1024)
except socket.error:
print ("no data")
main(context, data) # call to animation controller
if event.type == 'BACK_SLASH':
self.socket.close()
return {'FINISHED'}
return {'PASS_THROUGH'}
bpy.utils.register_class(socketModal)
AnimationController.py
import bpy
import json
shapeKeyNames = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'X']
dataPathPrefix = "key_blocks[\""
dataPathSuffix = "\"].value"
def getShapeKeyAnimationInfo(mouthCue, fps):
frames = (float(mouthCue['end']) - float(mouthCue['start'])) * fps
frames = round(frames)
# print(frames)
phoneme = mouthCue['value']
for name in shapeKeyNames:
if phoneme in name:
return (frames, name)
def addFaceShapeKeyFrame(shapeKeyName, previousKeyName, startFrame, frameCount):
faceShapeKeyParent = bpy.data.meshes['face'].shape_keys
dataPath = dataPathPrefix + shapeKeyName + dataPathSuffix
faceShapeKeyParent.key_blocks[shapeKeyName].value = 0.0
faceShapeKeyParent.keyframe_insert(dataPath, frame=startFrame)
faceShapeKeyParent.key_blocks[shapeKeyName].value = 1.0
finalFrame = startFrame + frameCount
if previousKeyName != None and previousKeyName != shapeKeyName:
dataPathPrevious = dataPathPrefix + previousKeyName + dataPathSuffix
faceShapeKeyParent.key_blocks[previousKeyName].value = 0.0
faceShapeKeyParent.keyframe_insert(dataPathPrevious, frame=finalFrame)
faceShapeKeyParent.keyframe_insert(dataPath, frame=finalFrame)
def addJawShapeKeyFrame(shapeKeyName, previousKeyName, startFrame, frameCount):
jawShapeKeyParent = bpy.data.meshes['jaw'].shape_keys
jawKeyName = getJawKeyName(shapeKeyName)
dataPath = dataPathPrefix + jawKeyName + dataPathSuffix
previousJawKeyName = None
if previousKeyName:
previousJawKeyName = getJawKeyName(previousKeyName)
dataPathPrev = dataPathPrefix + previousJawKeyName + dataPathSuffix
finalFrame = startFrame + frameCount
print (jawKeyName , previousJawKeyName)
if previousJawKeyName != None and previousJawKeyName != jawKeyName:
jawShapeKeyParent.key_blocks[jawKeyName].value = 0.0
jawShapeKeyParent.keyframe_insert(dataPath, frame=startFrame)
jawShapeKeyParent.key_blocks[previousJawKeyName].value = 0.0
jawShapeKeyParent.keyframe_insert(dataPathPrev, frame=finalFrame)
jawShapeKeyParent.key_blocks[jawKeyName].value = 1.0
jawShapeKeyParent.keyframe_insert(dataPath, frame=finalFrame)
def getJawKeyName(shapeKeyName):
if shapeKeyName in ['D', 'H']:
return 'W'
if shapeKeyName in ['C', 'E', 'F', 'G']:
return 'M'
if shapeKeyName in ['X', 'A', 'B']:
return 'C'
def main(context, data):
scene = context.scene
scene.render.fps = 60
fps = scene.render.fps
clearAllAnimation()
phonemes = json.loads(data)
mouthCues = phonemes['mouthCues']
previousKey = None
framecounter = 1
for x in mouthCues:
animationData = getShapeKeyAnimationInfo(x, fps)
addFaceShapeKeyFrame(animationData[1], previousKey, framecounter, animationData[0])
addJawShapeKeyFrame(animationData[1], previousKey, framecounter, animationData[0])
previousKey = animationData[1]
framecounter += animationData[0]
scene.frame_set(1)
scene.frame_end = framecounter + 1
bpy.ops.screen.animation_play() # Here is where the animation starts playing
def clearAllAnimation():
faceShapeKeyParent = bpy.data.meshes['face'].shape_keys
faceShapeKeyParent.animation_data_clear()
if (__name__ == "__main__"):
main()
I can calculate the time required for the animation by multiplying the FPS with the total number of frames added but how do i stop the animation after that time without blocking blender UI
context.scene.frame_current
. Have you triedbpy.ops.screen.animation_cancel()
? $\endgroup$scene.frame_set(frame)
where you calculate time, and convert to frame in the operator.event_timer_add(1.0,...)
makes the timer click, i.e. send a timer event, every 1.0 seconds. $\endgroup$