Newer
Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
"author": "Yousef Harfoush - bat3a)",
"version": (0, 5, 2),
"location": "3D window > Tool Shelf",
"description": "Plots Moho (Papagayo, Jlipsync, Yolo) file "
"to frames and adds automatic blinking",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php?title=Extensions:2.6/Py/"
"Scripts/Import-Export/Lipsync Importer",
"tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
import bpy
import re
from bpy.props import (
EnumProperty,
IntProperty,
FloatProperty,
StringProperty,
PointerProperty,
)
lastPhoneme = "nothing"
CoDEmanX
committed
if scn.regMenuTypes.enumBlinkTypes == '0':
modifier = 0
elif scn.regMenuTypes.enumBlinkTypes == '1':
modifier = scn.regMenuTypes.blinkMod
CoDEmanX
committed
# creating keys with blinkNm count
for y in range(scn.regMenuTypes.blinkNm):
frame = y * scn.regMenuTypes.blinkSp + int(random() * modifier)
createShapekey('blink', frame)
# -- code contributed by dalai felinto adds armature support modified by me --
"AI": ('location', 0),
"E": ('location', 1),
"FV": ('location', 2),
"L": ('rotation_euler', 0),
"MBP": ('rotation_euler', 1),
"O": ('rotation_euler', 2),
"U": ('scale', 0),
"WQ": ('scale', 1),
"etc": ('scale', 2),
"rest": ('ik_stretch', -1)
def lipsyncerBone():
# reading imported file & creating keys
scene = bpy.context.scene
bone = bpy.context.active_pose_bone
CoDEmanX
committed
CoDEmanX
committed
f = open(scene.regMenuTypes.fpath) # importing file
f.readline() # reading the 1st line that we don"t need
CoDEmanX
committed
for line in f:
# removing new lines
lsta = re.split("\n+", line)
# building a list of frames & shapes indexes
lst = re.split(":? ", lsta[0]) # making a list of a frame & number
CoDEmanX
committed
for key, attribute in bone_keys.items():
if lst[1] == key:
createBoneKeys(key, bone, attribute, frame)
def resetBoneScale(bone):
# set the attributes used by papagayo to 0.0
for attribute, index in bone_keys.values():
# bone.location[0] = 0.0
exec("bone.%s[%d] = %f" % (attribute, index, 0.0))
else:
exec("bone.%s = %f" % (attribute, 0.0))
def addBoneKey(bone, data_path, index=-1, value=None, frame=0, group=""):
# set a value and keyframe for the bone
# it assumes the 'bone' variable was defined before
# and it's the current selected bone
frame = bpy.context.scene.frame_current
if value is not None:
if index != -1:
# bone.location[0] = 0.0
exec("bone.%s[%d] = %f" % (data_path, index, value))
else:
exec("bone.%s = %f" % (data_path, value))
# bone.keyframe_insert("location", 0, 10.0, "Lipsync")
exec('bone.keyframe_insert("%s", %d, %f, "%s")' % (data_path, index, frame, group))
# creating keys with offset and eases for a phonem @ the Skframe
def createBoneKeys(phoneme, bone, attribute, frame):
global lastPhoneme
CoDEmanX
committed
CoDEmanX
committed
offst = scene.regMenuTypes.offset # offset value
skVlu = scene.regMenuTypes.skscale # shape key value
CoDEmanX
committed
# in case of Papagayo format
if scene.regMenuTypes.enumFileTypes == '0':
frmIn = scene.regMenuTypes.easeIn # ease in value
frmOut = scene.regMenuTypes.easeOut # ease out value
hldIn = scene.regMenuTypes.holdGap # holding time value
CoDEmanX
committed
# in case of Jlipsync format or Yolo
elif scene.regMenuTypes.enumFileTypes == '1':
frmIn = 1
frmOut = 1
hldIn = 0
# inserting the In key only when phonem change or when blinking
if lastPhoneme != phoneme or eval(scene.regMenuTypes.enumModeTypes) == 1:
addBoneKey(bone, attribute[0], attribute[1], 0.0, offst + frame - frmIn, "Lipsync")
addBoneKey(bone, attribute[0], attribute[1], skVlu, offst + frame, "Lipsync")
addBoneKey(bone, attribute[0], attribute[1], skVlu, offst + frame + hldIn, "Lipsync")
addBoneKey(bone, attribute[0], attribute[1], 0.0, offst + frame + hldIn + frmOut, "Lipsync")
lastPhoneme = phoneme
CoDEmanX
committed
# -------------------------------------------------------------------------------
# reading imported file & creating keys
def lipsyncer():
CoDEmanX
committed
CoDEmanX
committed
f = open(bpy.path.abspath(scn.regMenuTypes.fpath)) # importing file
f.readline() # reading the 1st line that we don"t need
CoDEmanX
committed
# removing new lines
lsta = re.split("\n+", line)
# building a list of frames & shapes indexes
lst = re.split(":? ", lsta[0]) # making a list of a frame & number
CoDEmanX
committed
for key in obj.data.shape_keys.key_blocks:
if lst[1] == key.name:
createShapekey(key.name, frame)
# creating keys with offset and eases for a phonem @ the frame
def createShapekey(phoneme, frame):
CoDEmanX
committed
CoDEmanX
committed
scn = bpy.context.scene
obj = bpy.context.object
objSK = obj.data.shape_keys
CoDEmanX
committed
offst = scn.regMenuTypes.offset # offset value
skVlu = scn.regMenuTypes.skscale # shape key value
CoDEmanX
committed
# in case of Papagayo format
if scn.regMenuTypes.enumFileTypes == '0':
frmIn = scn.regMenuTypes.easeIn # ease in value
frmOut = scn.regMenuTypes.easeOut # ease out value
hldIn = scn.regMenuTypes.holdGap # holding time value
CoDEmanX
committed
# in case of Jlipsync format or Yolo
elif scn.regMenuTypes.enumFileTypes == '1':
frmIn = 1
frmOut = 1
hldIn = 0
# inserting the In key only when phonem change or when blinking
if lastPhoneme != phoneme or eval(scn.regMenuTypes.enumModeTypes) == 1:
objSK.key_blocks[phoneme].value = 0.0
objSK.key_blocks[phoneme].keyframe_insert(
"value", -1, offst + frame - frmIn, "Lipsync"
)
objSK.key_blocks[phoneme].value = skVlu
objSK.key_blocks[phoneme].keyframe_insert(
"value", -1, offst + frame, "Lipsync"
)
objSK.key_blocks[phoneme].value = skVlu
objSK.key_blocks[phoneme].keyframe_insert(
"value", -1, offst + frame + hldIn, "Lipsync"
)
objSK.key_blocks[phoneme].value = 0.0
objSK.key_blocks[phoneme].keyframe_insert(
"value", -1, offst + frame + hldIn + frmOut, "Lipsync"
)
class btn_lipsyncer(bpy.types.Operator):
bl_label = 'Start Processing'
bl_description = 'Plots the voice file keys to timeline'
def execute(self, context):
obj = context.active_object
# testing if object is valid
if obj is not None:
if obj.type == "MESH":
if obj.data.shape_keys is not None:
if scn.regMenuTypes.fpath != '':
lipsyncer()
else:
print("select a Moho file")
else:
print("No shape keys")
elif obj.type == "ARMATURE":
if 1: # XXX add prop test
if scn.regMenuTypes.fpath != '':
lipsyncerBone()
else:
print("select a Moho file")
else:
print("Create Pose properties")
else:
print("Object is not a mesh ot bone")
else:
print("Select object")
return {'FINISHED'}
class btn_blinker(bpy.types.Operator):
bl_idname = 'blink.go'
bl_label = 'Start Processing'
bl_description = 'Add blinks at random or specifice frames'
CoDEmanX
committed
# testing if object is valid
if obj is not None:
if obj.type == "MESH":
if obj.data.shape_keys is not None:
for key in obj.data.shape_keys.key_blocks:
if key.name == 'blink':
# return
else:
print("No shape keys")
else:
print("Object is not a mesh ot bone")
else:
print("Select object")
# defining custom enumeratos
class menuTypes(bpy.types.PropertyGroup):
enumFileTypes: EnumProperty(
items=(('0', 'Papagayo', ''),
('1', 'Jlipsync Or Yolo', '')
# ('2', 'Retarget', '')
),
name='Choose FileType',
default='0'
)
enumBlinkTypes: EnumProperty(
items=(('0', 'Specific', ''),
('1', 'Random', '')),
name='Choose BlinkType',
default='0'
)
enumModeTypes: EnumProperty(
items=(('0', 'Lipsyncer', ''),
('1', 'Blinker', '')),
name='Choose Mode',
default='0'
)
name="Import File ",
description="Select your voice file",
subtype="FILE_PATH"
)
description="Smoothing shape key values",
min=0.1, max=1.0,
default=0.8
)
description="Offset your frames",
default=0
)
description="Smoothing In curve",
min=1, default=3
)
description="Smoothing Out curve",
min=1,
default=3
)
description="Holding for slow keys",
min=0,
default=0
)
description="Space between blinks",
min=1,
default=100
)
description="Number of blinks",
min=1,
default=10
)
description="Randomzing keyframe placment",
min=1,
default=10
)
CoDEmanX
committed
# drawing the user interface
class LipSyncBoneUI(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_label = "Phonemes"
def draw(self, context):
layout = self.layout
col = layout.column()
bone = bpy.context.active_pose_bone
CoDEmanX
committed
# showing the current object type
if bone: # and if scn.regMenuTypes.enumModeTypes == '0':
col.prop(bone, "location", index=0, text="AI")
col.prop(bone, "location", index=1, text="E")
col.prop(bone, "location", index=2, text="FV")
if bpy.context.scene.unit_settings.system_rotation == 'RADIANS':
col.prop(bone, "rotation_euler", index=0, text="L")
col.prop(bone, "rotation_euler", index=1, text="MBP")
col.prop(bone, "rotation_euler", index=2, text="O")
else:
row = col.row()
row.prop(bone, "rotation_euler", index=0, text="L")
row.label(text=str("%4.2f" % (bone.rotation_euler.x)))
row = col.row()
row.prop(bone, "rotation_euler", index=1, text="MBP")
row.label(text=str("%4.2f" % (bone.rotation_euler.y)))
row = col.row()
row.prop(bone, "rotation_euler", index=2, text="O")
row.label(text=str("%4.2f" % (bone.rotation_euler.z)))
col.prop(bone, "scale", index=0, text="U")
col.prop(bone, "scale", index=1, text="WQ")
col.prop(bone, "scale", index=2, text="etc")
else:
layout.label(text="No good bone is selected")
CoDEmanX
committed
# drawing the user interface
class LipSyncUI(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "TOOL_PROPS"
CoDEmanX
committed
def draw(self, context):
obj = bpy.context.active_object
CoDEmanX
committed
layout = self.layout
# showing the current object type
if obj is not None:
if obj.type == "MESH":
split = col.split(align=True)
split.label(text="The active object is: ", icon="OBJECT_DATA")
split.label(text=obj.name, icon="EDITMODE_HLT")
elif obj.type == "ARMATURE": # bone needs to be selected
if obj.mode == "POSE": # mode needs to be pose
split = col.split(align=True)
split.label(text="The active object is: ", icon="ARMATURE_DATA")
split.label(text=obj.name, icon="EDITMODE_HLT")
else:
col.label(text="You need to select Pose mode!", icon="OBJECT_DATA")
else:
col.label(text="The active object is not a Mesh or Armature!", icon="OBJECT_DATA")
layout.label(text="No object is selected", icon="OBJECT_DATA")
CoDEmanX
committed
col.row().prop(scn.regMenuTypes, 'enumModeTypes')
CoDEmanX
committed
# the lipsyncer panel
if scn.regMenuTypes.enumModeTypes == '0':
# choose the file format
col.row().prop(scn.regMenuTypes, 'enumFileTypes', text=' ', expand=True)
CoDEmanX
committed
if scn.regMenuTypes.enumFileTypes == '0':
col.prop(scn.regMenuTypes, "fpath")
split.label(text="Key Value :")
split.prop(scn.regMenuTypes, "skscale")
split.label(text="Frame Offset :")
split.prop(scn.regMenuTypes, "offset")
split.prop(scn.regMenuTypes, "easeIn", "Ease In")
split.prop(scn.regMenuTypes, "holdGap", "Hold Gap")
split.prop(scn.regMenuTypes, "easeOut", "Ease Out")
CoDEmanX
committed
col.operator('lipsync.go', text='Plot Keys to the Timeline')
elif scn.regMenuTypes.enumFileTypes == '1':
col.prop(scn.regMenuTypes, "fpath")
split.label(text="Key Value :")
split.prop(scn.regMenuTypes, "skscale")
split.label(text="Frame Offset :")
split.prop(scn.regMenuTypes, "offset")
CoDEmanX
committed
col.operator('lipsync.go', text='Plot Keys to the Timeline')
CoDEmanX
committed
elif scn.regMenuTypes.enumModeTypes == '1':
# choose blink type
col.row().prop(scn.regMenuTypes, 'enumBlinkTypes', text=' ', expand=True)
CoDEmanX
committed
if scn.regMenuTypes.enumBlinkTypes == '0':
split.label(text="Key Value :")
split.prop(scn.regMenuTypes, "skscale")
split.label(text="Frame Offset :")
split.prop(scn.regMenuTypes, "offset")
split.prop(scn.regMenuTypes, "easeIn", "Ease In")
split.prop(scn.regMenuTypes, "holdGap", "Hold Gap")
split.prop(scn.regMenuTypes, "easeOut", "Ease Out")
col.prop(scn.regMenuTypes, "blinkSp", "Spacing")
col.prop(scn.regMenuTypes, "blinkNm", "Times")
col.operator('blink.go', text='Add Keys to the Timeline')
CoDEmanX
committed
elif scn.regMenuTypes.enumBlinkTypes == '1':
split.label(text="Key Value :")
split.prop(scn.regMenuTypes, "skscale")
split.label(text="Frame Start :")
split.prop(scn.regMenuTypes, "offset")
split.prop(scn.regMenuTypes, "easeIn", "Ease In")
split.prop(scn.regMenuTypes, "holdGap", "Hold Gap")
split.prop(scn.regMenuTypes, "easeOut", "Ease Out")
split.prop(scn.regMenuTypes, "blinkSp", "Spacing")
split.prop(scn.regMenuTypes, "blinkMod", "Random Modifier")
col.prop(scn.regMenuTypes, "blinkNm", "Times")
col.operator('blink.go', text='Add Keys to the Timeline')
CoDEmanX
committed
# registering the script
def register():
bpy.types.Scene.regMenuTypes = PointerProperty(type=menuTypes)
def unregister():
del bpy.types.Scene.regMenuTypes
if __name__ == "__main__":