Fix for access of undefined ground object in UI code giving pointless
[blender-addons-contrib.git] / io_import_lipSync_Importer.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 bl_info = {
20     "name": "LipSync Importer & Blinker",
21     "author": "Yousef Harfoush - bat3a ;)",
22     "version": (0, 5, 1),
23     "blender": (2, 70, 0),
24     "location": "3D window > Tool Shelf",
25     "description": "Plots Moho (Papagayo, Jlipsync, Yolo) file "
26         "to frames and adds automatic blinking",
27     "warning": "",
28     "wiki_url": "http://wiki.blender.org/index.php?title=Extensions:2.6/Py/"
29         "Scripts/Import-Export/Lipsync Importer",
30     "tracker_url": "https://developer.blender.org/T24080",
31     "category": "Import-Export"}
32
33
34 import bpy, re
35 from random import random
36 from bpy.props import *
37 from bpy.props import IntProperty, FloatProperty, StringProperty
38
39 global lastPhoneme
40 lastPhoneme="nothing"
41
42 # add blinking
43 def blinker():
44
45     scn = bpy.context.scene
46     obj = bpy.context.object
47
48     if scn.regMenuTypes.enumBlinkTypes == '0':
49         modifier = 0
50     elif scn.regMenuTypes.enumBlinkTypes == '1':
51         modifier = scn.blinkMod
52
53     #creating keys with blinkNm count
54     for y in range(scn.blinkNm):
55         frame = y * scn.blinkSp + int(random()*modifier)
56         createShapekey('blink', frame)
57
58 # -----------code contributed by dalai felinto adds armature support modified by me-------------------
59
60 bone_keys = {
61 "AI":   ('location', 0),
62 "E":    ('location', 1),
63 "FV":   ('location', 2),
64 "L":    ('rotation_euler', 0),
65 "MBP":  ('rotation_euler', 1),
66 "O":    ('rotation_euler', 2),
67 "U":    ('scale', 0),
68 "WQ":   ('scale', 1),
69 "etc":  ('scale', 2),
70 "rest": ('ik_stretch', -1)
71 }
72
73 def lipsyncerBone():
74     # reading imported file & creating keys
75     object = bpy.context.object
76     scene = bpy.context.scene
77     bone = bpy.context.active_pose_bone
78
79     resetBoneScale(bone)
80
81     f=open(scene.fpath) # importing file
82     f.readline() # reading the 1st line that we don"t need
83
84     for line in f:
85         # removing new lines
86         lsta = re.split("\n+", line)
87
88         # building a list of frames & shapes indexes
89         lst = re.split(":? ", lsta[0])# making a list of a frame & number
90         frame = int(lst[0])
91
92         for key,attribute in bone_keys.items():
93             if lst[1] == key:
94                 createBoneKeys(key, bone, attribute, frame)
95
96 def resetBoneScale(bone):
97     # set the attributes used by papagayo to 0.0
98     for attribute,index in bone_keys.values():
99         if index != -1:
100             #bone.location[0] = 0.0
101             exec("bone.%s[%d] = %f" % (attribute, index, 0.0))
102         else:
103             exec("bone.%s = %f" % (attribute, 0.0))
104
105 def addBoneKey(bone, data_path, index=-1, value=None, frame=0, group=""):
106     # set a value and keyframe for the bone
107     # it assumes the 'bone' variable was defined before
108     # and it's the current selected bone
109     frame=bpy.context.scene.frame_current
110     if value != None:
111         if index != -1:
112             # bone.location[0] = 0.0
113             exec("bone.%s[%d] = %f" % (data_path, index, value))
114         else:
115             exec("bone.%s = %f" % (data_path, value))
116
117     # bone.keyframe_insert("location", 0, 10.0, "Lipsync")
118     exec('bone.keyframe_insert("%s", %d, %f, "%s")' % (data_path, index, frame, group))
119
120 # creating keys with offset and eases for a phonem @ the Skframe
121 def createBoneKeys(phoneme, bone, attribute, frame):
122     global lastPhoneme
123
124     scene = bpy.context.scene
125     object = bpy.context.object
126
127     offst = scene.offset     # offset value
128     skVlu = scene.skscale    # shape key value
129
130     #in case of Papagayo format
131     if scene.regMenuTypes.enumFileTypes == '0' :
132         frmIn = scene.easeIn     # ease in value
133         frmOut = scene.easeOut   # ease out value
134         hldIn = scene.holdGap    # holding time value
135
136     #in case of Jlipsync format or Yolo
137     elif scene.regMenuTypes.enumFileTypes == '1' :
138         frmIn = 1
139         frmOut = 1
140         hldIn = 0
141
142     # inserting the In key only when phonem change or when blinking
143     if lastPhoneme!=phoneme or eval(scene.regMenuTypes.enumModeTypes) == 1:
144         addBoneKey(bone, attribute[0], attribute[1], 0.0, offst+frame-frmIn, "Lipsync")
145
146     addBoneKey(bone, attribute[0], attribute[1], skVlu, offst+frame, "Lipsync")
147     addBoneKey(bone, attribute[0], attribute[1], skVlu, offst+frame+hldIn, "Lipsync")
148     addBoneKey(bone, attribute[0], attribute[1], 0.0, offst+frame+hldIn+frmOut, "Lipsync")
149
150     lastPhoneme=phoneme
151
152 # -------------------------------------------------------------------------------
153
154 # reading imported file & creating keys
155 def lipsyncer():
156
157     obj = bpy.context.object
158     scn = bpy.context.scene
159
160     f=open(scn.fpath) # importing file
161     f.readline() # reading the 1st line that we don"t need
162
163     for line in f:
164
165         # removing new lines
166         lsta = re.split("\n+", line)
167
168         # building a list of frames & shapes indexes
169         lst = re.split(":? ", lsta[0])# making a list of a frame & number
170         frame = int(lst[0])
171
172         for key in obj.data.shape_keys.key_blocks:
173             if lst[1] == key.name:
174                 createShapekey(key.name, frame)
175
176 # creating keys with offset and eases for a phonem @ the frame
177 def createShapekey(phoneme, frame):
178
179     global lastPhoneme
180
181     scn = bpy.context.scene
182     obj = bpy.context.object
183     objSK = obj.data.shape_keys
184
185     offst = scn.offset     # offset value
186     skVlu = scn.skscale    # shape key value
187
188     #in case of Papagayo format
189     if scn.regMenuTypes.enumFileTypes == '0' :
190         frmIn = scn.easeIn     # ease in value
191         frmOut = scn.easeOut   # ease out value
192         hldIn = scn.holdGap    # holding time value
193
194     #in case of Jlipsync format or Yolo
195     elif scn.regMenuTypes.enumFileTypes == '1' :
196         frmIn = 1
197         frmOut = 1
198         hldIn = 0
199
200     # inserting the In key only when phonem change or when blinking
201     if lastPhoneme!=phoneme or eval(scn.regMenuTypes.enumModeTypes) == 1:
202         objSK.key_blocks[phoneme].value=0.0
203         objSK.key_blocks[phoneme].keyframe_insert("value",
204             -1, offst+frame-frmIn, "Lipsync")
205
206     objSK.key_blocks[phoneme].value=skVlu
207     objSK.key_blocks[phoneme].keyframe_insert("value",
208         -1, offst+frame, "Lipsync")
209
210     objSK.key_blocks[phoneme].value=skVlu
211     objSK.key_blocks[phoneme].keyframe_insert("value",
212         -1, offst+frame+hldIn, "Lipsync")
213
214     objSK.key_blocks[phoneme].value=0.0
215     objSK.key_blocks[phoneme].keyframe_insert("value",
216     -1, offst+frame+hldIn+frmOut, "Lipsync")
217
218     lastPhoneme = phoneme
219
220 # lipsyncer operation start
221 class btn_lipsyncer(bpy.types.Operator):
222     bl_idname = 'lipsync.go'
223     bl_label = 'Start Processing'
224     bl_description = 'Plots the voice file keys to timeline'
225
226     def execute(self, context):
227
228         scn = context.scene
229         obj = context.active_object
230
231         # testing if object is valid
232         if obj!=None:
233             if obj.type=="MESH":
234                 if obj.data.shape_keys!=None:
235                     if scn.fpath!='': lipsyncer()
236                     else: print ("select a Moho file")
237                 else: print("No shape keys")
238
239             elif obj.type=="ARMATURE":
240                 if 1:#XXX add prop test
241                     if scn.fpath!='': lipsyncerBone()
242                     else: print ("select a Moho file")
243                 else: print("Create Pose properties")
244
245             else: print ("Object is not a mesh ot bone")
246         else: print ("Select object")
247         return {'FINISHED'}
248
249 # blinker operation start
250 class btn_blinker(bpy.types.Operator):
251     bl_idname = 'blink.go'
252     bl_label = 'Start Processing'
253     bl_description = 'Add blinks at random or specifice frames'
254
255     def execute(self, context):
256
257         scn = context.scene
258         obj = context.object
259
260          # testing if object is valid
261         if obj!=None:
262             if obj.type=="MESH":
263                 if obj.data.shape_keys!=None:
264                     for key in obj.data.shape_keys.key_blocks:
265                         if key.name=='blink':
266                             blinker()
267                             #return
268                 else: print("No shape keys")
269             else: print ("Object is not a mesh ot bone")
270         else: print ("Select object")
271         return {'FINISHED'}
272
273
274 #defining custom enumeratos
275 class menuTypes(bpy.types.PropertyGroup):
276
277     enumFileTypes = EnumProperty(items =(('0', 'Papagayo', ''),
278                                          ('1', 'Jlipsync Or Yolo', '')
279                                        #,('2', 'Retarget', '')
280                                          ),
281                                  name = 'Choose FileType',
282                                  default = '0')
283
284     enumBlinkTypes = EnumProperty(items =(('0', 'Specific', ''),
285                                           ('1', 'Random','')),
286                                   name = 'Choose BlinkType',
287                                   default = '0')
288
289     enumModeTypes = EnumProperty(items =(('0', 'Lipsyncer',''),
290                                          ('1', 'Blinker','')),
291                                  name = 'Choose Mode',
292                                  default = '0')
293
294 # drawing the user interface
295 class LipSyncBoneUI(bpy.types.Panel):
296     bl_space_type = "VIEW_3D"
297     bl_region_type = "UI"
298     bl_label = "Phonemes"
299     bl_category = 'Animation'
300     
301     def draw(self, context):
302         layout = self.layout
303         col = layout.column()
304
305         bone = bpy.context.active_pose_bone
306
307         #showing the current object type
308         if bone: #and if scn.regMenuTypes.enumModeTypes == '0':
309             col.prop(bone, "location", index=0, text="AI")
310             col.prop(bone, "location", index=1, text="E")
311             col.prop(bone, "location", index=2, text="FV")
312             if bpy.context.scene.unit_settings.system_rotation == 'RADIANS':
313                 col.prop(bone, "rotation_euler", index=0, text="L")
314                 col.prop(bone, "rotation_euler", index=1, text="MBP")
315                 col.prop(bone, "rotation_euler", index=2, text="O")
316             else:
317                 row=col.row()
318                 row.prop(bone, "rotation_euler", index=0, text="L")
319                 row.label(text=str("%4.2f" % (bone.rotation_euler.x)))
320                 row=col.row()
321                 row.prop(bone, "rotation_euler", index=1, text="MBP")
322                 row.label(text=str("%4.2f" % (bone.rotation_euler.y)))
323                 row=col.row()
324                 row.prop(bone, "rotation_euler", index=2, text="O")
325                 row.label(text=str("%4.2f" % (bone.rotation_euler.z)))
326             col.prop(bone, "scale", index=0, text="U")
327             col.prop(bone, "scale", index=1, text="WQ")
328             col.prop(bone, "scale", index=2, text="etc")
329         else:
330             layout.label(text="No good bone is selected")
331
332 # drawing the user interface
333 class LipSyncUI(bpy.types.Panel):
334     bl_space_type = "VIEW_3D"
335     bl_region_type = "TOOL_PROPS"
336     bl_label = "LipSync Importer & Blinker"
337
338     newType= bpy.types.Scene
339
340     newType.fpath = StringProperty(name="Import File ", description="Select your voice file", subtype="FILE_PATH")
341     newType.skscale = FloatProperty(description="Smoothing shape key values", min=0.1, max=1.0, default=0.8)
342     newType.offset = IntProperty(description="Offset your frames", default=0)
343
344     newType.easeIn = IntProperty(description="Smoothing In curve", min=1, default=3)
345     newType.easeOut = IntProperty(description="Smoothing Out curve", min=1, default=3)
346     newType.holdGap = IntProperty(description="Holding for slow keys", min=0, default=0)
347
348     newType.blinkSp = IntProperty(description="Space between blinks", min=1, default=100)
349     newType.blinkNm = IntProperty(description="Number of blinks", min=1, default=10)
350
351     newType.blinkMod = IntProperty(description="Randomzing keyframe placment", min=1, default=10)
352
353     def draw(self, context):
354
355         obj = bpy.context.active_object
356         scn = bpy.context.scene
357
358         layout = self.layout
359         col = layout.column()
360
361         # showing the current object type
362         if obj != None:
363             if obj.type == "MESH":
364                 split = col.split(align=True)
365                 split.label(text="The active object is: ", icon="OBJECT_DATA")
366                 split.label(obj.name, icon="EDITMODE_HLT")
367             elif obj.type == "ARMATURE": # bone needs to be selected
368                 if obj.mode == "POSE": # mode needs to be pose
369                     split = col.split(align=True)
370                     split.label(text="The active object is: ", icon="ARMATURE_DATA")
371                     split.label(obj.name, icon="EDITMODE_HLT")
372                 else:
373                     col.label(text="You need to select Pose mode!", icon="OBJECT_DATA")
374             else:
375                 col.label(text="The active object is not a Mesh or Armature!", icon="OBJECT_DATA")
376         else:
377             layout.label(text="No object is selected", icon="OBJECT_DATA")
378
379         col.row().prop(scn.regMenuTypes, 'enumModeTypes')
380         col.separator()
381
382         # the lipsyncer panel
383         if scn.regMenuTypes.enumModeTypes == '0':
384             # choose the file format
385             col.row().prop(scn.regMenuTypes, 'enumFileTypes', text = ' ', expand = True)
386
387             # Papagayo panel
388             if scn.regMenuTypes.enumFileTypes == '0':
389                 col.prop(context.scene, "fpath")
390                 split = col.split(align=True)
391                 split.label("Key Value :")
392                 split.prop(context.scene, "skscale")
393                 split = col.split(align=True)
394                 split.label("Frame Offset :")
395                 split.prop(context.scene, "offset")
396                 split = col.split(align=True)
397                 split.prop(context.scene, "easeIn", "Ease In")
398                 split.prop(context.scene, "holdGap", "Hold Gap")
399                 split.prop(context.scene, "easeOut", "Ease Out")
400
401                 col.operator('lipsync.go', text='Plot Keys to the Timeline')
402
403             # Jlipsync & Yolo panel
404             elif scn.regMenuTypes.enumFileTypes == '1':
405                 col.prop(context.scene, "fpath")
406                 split = col.split(align=True)
407                 split.label("Key Value :")
408                 split.prop(context.scene, "skscale")
409                 split = col.split(align=True)
410                 split.label("Frame Offset :")
411                 split.prop(context.scene, "offset")
412
413                 col.operator('lipsync.go', text='Plot Keys to the Timeline')
414
415         # the blinker panel
416         elif scn.regMenuTypes.enumModeTypes == '1':
417             # choose blink type
418             col.row().prop(scn.regMenuTypes, 'enumBlinkTypes', text = ' ', expand = True)
419
420             # specific panel
421             if scn.regMenuTypes.enumBlinkTypes == '0':
422                 split = col.split(align=True)
423                 split.label("Key Value :")
424                 split.prop(context.scene, "skscale")
425                 split = col.split(align=True)
426                 split.label("Frame Offset :")
427                 split.prop(context.scene, "offset")
428                 split = col.split(align=True)
429                 split.prop(context.scene, "easeIn", "Ease In")
430                 split.prop(context.scene, "holdGap", "Hold Gap")
431                 split.prop(context.scene, "easeOut", "Ease Out")
432                 col.prop(context.scene, "blinkSp", "Spacing")
433                 col.prop(context.scene, "blinkNm", "Times")
434                 col.operator('blink.go', text='Add Keys to the Timeline')
435
436             # Random panel
437             elif scn.regMenuTypes.enumBlinkTypes == '1':
438                 split = col.split(align=True)
439                 split.label("Key Value :")
440                 split.prop(context.scene, "skscale")
441                 split = col.split(align=True)
442                 split.label("Frame Start :")
443                 split.prop(context.scene, "offset")
444                 split = col.split(align=True)
445                 split.prop(context.scene, "easeIn", "Ease In")
446                 split.prop(context.scene, "holdGap", "Hold Gap")
447                 split.prop(context.scene, "easeOut", "Ease Out")
448                 split = col.split(align=True)
449                 split.prop(context.scene, "blinkSp", "Spacing")
450                 split.prop(context.scene, "blinkMod", "Random Modifier")
451                 col.prop(context.scene, "blinkNm", "Times")
452                 col.operator('blink.go', text='Add Keys to the Timeline')
453
454
455 # clearing vars
456 def clear_properties():
457
458     # can happen on reload
459     if bpy.context.scene is None:
460         return
461
462     props = ["fpath", "skscale", "offset", "easeIn", "easeOut", "holdGap", "blinkSp", "blinkNm", "blinkMod"]
463     for p in props:
464         if p in bpy.types.Scene.bl_rna.properties:
465             exec("del bpy.types.Scene."+p)
466         if p in bpy.context.scene:
467             del bpy.context.scene[p]
468
469 # registering the script
470 def register():
471     bpy.utils.register_module(__name__)
472     bpy.types.Scene.regMenuTypes = PointerProperty(type = menuTypes)
473
474 def unregister():
475     bpy.utils.unregister_module(__name__)
476     del bpy.types.Scene.regMenuTypes
477
478     clear_properties()
479
480 if __name__ == "__main__":
481     register()