Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions op_blender_rhubarb.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from queue import Queue, Empty
import json
import os
from mathutils import Matrix



class RhubarbLipsyncOperator(bpy.types.Operator):
"""Run Rhubarb lipsync"""
Expand Down Expand Up @@ -58,27 +61,26 @@ def modal(self, context, event):
fps = context.scene.render.fps
lib = context.object.pose_library
last_frame = 0
prev_pose = 0
prev_pose = context.object.pose_library.mouth_shapes["mouth_x"]

for cue in results['mouthCues']:
frame_num = round(cue['start'] * fps) + lib.mouth_shapes.start_frame


# add hold key if time since last key is large
if frame_num - last_frame > self.hold_frame_threshold:
print("hold frame: {0}".format(frame_num- self.hold_frame_threshold))
bpy.ops.poselib.apply_pose(pose_index=prev_pose)
self.set_keyframes(context, frame_num - self.hold_frame_threshold)
self.apply_pose(context, frame_num - self.hold_frame_threshold, bpy.data.actions[prev_pose])

print("start: {0} frame: {1} value: {2}".format(cue['start'], frame_num , cue['value']))

mouth_shape = 'mouth_' + cue['value'].lower()
if mouth_shape in context.object.pose_library.mouth_shapes:
pose_index = context.object.pose_library.mouth_shapes[mouth_shape]
else:
pose_index = 0

bpy.ops.poselib.apply_pose(pose_index=pose_index)
self.set_keyframes(context, frame_num)
pose_index = context.object.pose_library.mouth_shapes["mouth_x"]

self.apply_pose(context, frame_num - self.hold_frame_threshold, bpy.data.actions[pose_index])


prev_pose = pose_index
Expand All @@ -99,15 +101,17 @@ def modal(self, context, event):
print(template.format(type(ex).__name__, ex.args))
wm.progress_end()
return {'CANCELLED'}

def set_keyframes(self, context, frame):
for bone in context.selected_pose_bones:
bone.keyframe_insert(data_path='location', frame=frame)
if bone.rotation_mode == 'QUATERNION':
bone.keyframe_insert(data_path='rotation_quaternion', frame=frame)
else:
bone.keyframe_insert(data_path='rotation_euler', frame=frame)
bone.keyframe_insert(data_path='scale', frame=frame)

def apply_pose(self,context, frame, pose):
bpy.context.scene.frame_set(frame)

print(pose)

context.object.pose.apply_pose_from_action(action=pose,evaluation_time=frame)

for i in pose.fcurves:
i.evaluate(frame)
context.object.pose.bones[i.data_path.split("\"")[1]].keyframe_insert(data_path=i.data_path.split("]")[1].replace(".",""), frame=frame)

def invoke(self, context, event):
preferences = context.preferences
Expand Down Expand Up @@ -149,6 +153,7 @@ def finished(self, context):
def cancel(self, context):
wm = context.window_manager
wm.event_timer_remove(self._timer)




Expand Down
100 changes: 61 additions & 39 deletions pnl_blender_rhubarb.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,79 @@ def poll(cls, context):
def draw(self, context):
layout = self.layout

if context.object.pose_library:
prop = context.object.pose_library.mouth_shapes

col = layout.column()
col.prop(prop, 'mouth_a', text="Mouth A (MBP)")
col.prop(prop, 'mouth_b', text="Mouth B (EE/etc)")
col.prop(prop, 'mouth_c', text="Mouth C (E)")
col.prop(prop, 'mouth_d', text="Mouth D (AI)")
col.prop(prop, 'mouth_e', text="Mouth E (O)")
col.prop(prop, 'mouth_f', text="Mouth F (WQ)")
col.prop(prop, 'mouth_g', text="Mouth G (FV)")
col.prop(prop, 'mouth_h', text="Mouth H (L)")
col.prop(prop, 'mouth_x', text="Mouth X (rest)")

row = layout.row(align=True)
row.prop(prop, 'sound_file', text='Sound file')

row = layout.row(align=True)
row.prop(prop, 'dialog_file', text='Dialog file')
prop = context.object.mouth_shapes

row = layout.row()
row.prop(prop, 'start_frame', text='Start frame')
col = layout.column()
col.prop(prop, 'mouth_a', text="Mouth A (MBP)")
col.prop(prop, 'mouth_b', text="Mouth B (EE/etc)")
col.prop(prop, 'mouth_c', text="Mouth C (E)")
col.prop(prop, 'mouth_d', text="Mouth D (AI)")
col.prop(prop, 'mouth_e', text="Mouth E (O)")
col.prop(prop, 'mouth_f', text="Mouth F (WQ)")
col.prop(prop, 'mouth_g', text="Mouth G (FV)")
col.prop(prop, 'mouth_h', text="Mouth H (L)")
col.prop(prop, 'mouth_x', text="Mouth X (rest)")

row = layout.row()
row = layout.row(align=True)
row.prop(prop, 'sound_file', text='Sound file')

if not (context.preferences.addons[__package__].preferences.executable_path):
row.label(text="Please set rhubarb executable location in addon preferences")
row = layout.row()
row = layout.row(align=True)
row.prop(prop, 'dialog_file', text='Dialog file')

row.operator(operator = "object.rhubarb_lipsync")
row = layout.row()
row.prop(prop, 'start_frame', text='Start frame')

else:
row = layout.row(align=True)
row.label(text="Rhubarb Lipsync requires a pose library")
row = layout.row()

if not (context.preferences.addons[__package__].preferences.executable_path):
row.label(text="Please set rhubarb executable location in addon preferences")
row = layout.row()

pose_markers = []
row.operator(operator = "object.rhubarb_lipsync")

def pose_markers_items(self, context):
"""Dynamic list of items for Object.pose_libs_for_char."""

lib = bpy.context.object.pose_library
#https://blender.stackexchange.com/a/78592
enum_items_store = []

if not context or not context.object:
return []
def enum_items(self, context):

pose_markers = [(marker, marker, 'Poses', '', idx) for idx, marker in enumerate(lib.pose_markers.keys())]
return pose_markers
items = []
for action in bpy.data.actions:
not_an_action = False
if (not(action.asset_data is None)):
for i in action.fcurves:
if (not (i.data_path.split("\"")[1] in context.object.pose.bones)):
not_an_action = True
print("hello!")
break

else:
continue
if not_an_action:
continue
# NEW CODE
# Scan the list of IDs to see if we already have one for this mesh
maxid = -1
id = -1
found = False
for idrec in enum_items_store:
id = idrec[0]
if id > maxid:
maxid = id
if idrec[1] == action.name:
found = True
break

if not found:
enum_items_store.append((maxid+1, action.name))

# AMENDED CODE - include the ID
items.append( (action.name, action.name, "", id) )

return items

poses = bpy.props.EnumProperty(
items=pose_markers_items,
items=enum_items,
name='Poses',
description='Poses',
)
Expand All @@ -93,7 +115,7 @@ def register():
bpy.utils.register_class(MouthShapesProperty)
bpy.utils.register_class(RhubarbLipsyncPanel)

bpy.types.Action.mouth_shapes = bpy.props.PointerProperty(type=MouthShapesProperty)
bpy.types.Object.mouth_shapes = bpy.props.PointerProperty(type=MouthShapesProperty)


def unregister():
Expand Down