First Commit
This commit is contained in:
156
operators/mmd.py
Normal file
156
operators/mmd.py
Normal file
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import bpy
|
||||
from utilities.constants import MMD_VISEME_COUNT, MMD_SEP_1, MMD_SEP_2
|
||||
from utilities.functions import MMD_JP_MAPPING
|
||||
from utilities.helpers import _ps, guess_shape, _restore_shapekey_values
|
||||
|
||||
|
||||
class VRCVIS_OT_MMDAutoDetect(bpy.types.Operator):
|
||||
bl_idname = 'vrc_viseme.mmd_auto_detect'
|
||||
bl_label = 'Auto-rileva (EN → JP)'
|
||||
bl_description = (
|
||||
'Cerca automaticamente i nomi inglesi/VRChat comuni e precompila '
|
||||
'i campi di destinazione MMD giapponese'
|
||||
)
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
mesh_name = scene.vrcvis_mesh
|
||||
if not mesh_name or mesh_name not in bpy.data.objects:
|
||||
self.report({'ERROR'}, 'Nessuna mesh selezionata')
|
||||
return {'CANCELLED'}
|
||||
obj = bpy.data.objects[mesh_name]
|
||||
found = 0
|
||||
for _jp, suffix, _en, candidates in MMD_JP_MAPPING:
|
||||
result = guess_shape(obj, candidates)
|
||||
if result:
|
||||
setattr(scene, f'vrcvis_mmd_{suffix}', result)
|
||||
found += 1
|
||||
if found:
|
||||
self.report({'INFO'}, f'Rilevate {found} shape key')
|
||||
else:
|
||||
self.report({'WARNING'}, 'Nessun nome riconosciuto trovato')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VRCVIS_OT_MMDConvert(bpy.types.Operator):
|
||||
bl_idname = 'vrc_viseme.mmd_convert'
|
||||
bl_label = 'Converti in MMD'
|
||||
bl_description = (
|
||||
'Duplica le shape key selezionate con i nomi MMD giapponesi, '
|
||||
'aggiunge i separatori e posiziona tutto in fondo alla lista'
|
||||
)
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
mesh_name = scene.vrcvis_mesh
|
||||
if not mesh_name or mesh_name not in bpy.data.objects:
|
||||
self.report({'ERROR'}, 'Seleziona una mesh valida')
|
||||
return {'CANCELLED'}
|
||||
obj = bpy.data.objects[mesh_name]
|
||||
if not obj.data.shape_keys:
|
||||
self.report({'ERROR'}, 'La mesh non ha shape key')
|
||||
return {'CANCELLED'}
|
||||
|
||||
if _ps.active:
|
||||
_restore_shapekey_values(obj)
|
||||
_ps.active = False
|
||||
scene.vrcvis_preview_active = False
|
||||
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
obj.show_only_shape_key = False
|
||||
|
||||
keys = obj.data.shape_keys.key_blocks
|
||||
overwrite = scene.vrcvis_overwrite
|
||||
created = []
|
||||
skipped = []
|
||||
|
||||
def _add_empty(name):
|
||||
if name in keys:
|
||||
if overwrite:
|
||||
obj.active_shape_key_index = keys.find(name)
|
||||
bpy.ops.object.shape_key_remove()
|
||||
else:
|
||||
skipped.append(name)
|
||||
return
|
||||
bpy.ops.object.shape_key_clear()
|
||||
obj.shape_key_add(name=name, from_mix=True)
|
||||
created.append(name)
|
||||
|
||||
def _add_copy(src_name, dst_name):
|
||||
if not src_name or src_name not in keys:
|
||||
return
|
||||
if dst_name in keys:
|
||||
if overwrite:
|
||||
obj.active_shape_key_index = keys.find(dst_name)
|
||||
bpy.ops.object.shape_key_remove()
|
||||
else:
|
||||
skipped.append(dst_name)
|
||||
return
|
||||
bpy.ops.object.shape_key_clear()
|
||||
keys[src_name].slider_max = max(keys[src_name].slider_max, 1.0)
|
||||
keys[src_name].value = 1.0
|
||||
obj.shape_key_add(name=dst_name, from_mix=True)
|
||||
bpy.ops.object.shape_key_clear()
|
||||
created.append(dst_name)
|
||||
|
||||
_add_empty(MMD_SEP_1)
|
||||
for jp_name, suffix, _en, _ in MMD_JP_MAPPING[:MMD_VISEME_COUNT]:
|
||||
src = getattr(scene, f'vrcvis_mmd_{suffix}')
|
||||
_add_copy(src, jp_name)
|
||||
|
||||
_add_empty(MMD_SEP_2)
|
||||
for jp_name, suffix, _en, _ in MMD_JP_MAPPING[MMD_VISEME_COUNT:]:
|
||||
src = getattr(scene, f'vrcvis_mmd_{suffix}')
|
||||
_add_copy(src, jp_name)
|
||||
|
||||
obj.active_shape_key_index = 0
|
||||
|
||||
msg_parts = []
|
||||
if created:
|
||||
msg_parts.append(f'Create: {len(created)}')
|
||||
if skipped:
|
||||
msg_parts.append(f'Saltate: {len(skipped)}')
|
||||
self.report({'INFO'}, ' | '.join(msg_parts) if msg_parts else 'Nessuna shape key creata')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VRCVIS_OT_MMDRemove(bpy.types.Operator):
|
||||
bl_idname = 'vrc_viseme.mmd_remove'
|
||||
bl_label = 'Rimuovi MMD'
|
||||
bl_description = (
|
||||
'Rimuove le shape key MMD giapponesi e i separatori '
|
||||
'(---MMD Visemes--- e ^MMD Visemes / Other v)'
|
||||
)
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
mesh_name = scene.vrcvis_mesh
|
||||
if not mesh_name or mesh_name not in bpy.data.objects:
|
||||
self.report({'ERROR'}, 'Nessuna mesh selezionata')
|
||||
return {'CANCELLED'}
|
||||
obj = bpy.data.objects[mesh_name]
|
||||
if not obj.data.shape_keys:
|
||||
self.report({'INFO'}, 'Nessuna shape key presente')
|
||||
return {'FINISHED'}
|
||||
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
mmd_names = {jp for jp, _, _en, _ in MMD_JP_MAPPING} | {MMD_SEP_1, MMD_SEP_2}
|
||||
to_remove = [k.name for k in obj.data.shape_keys.key_blocks if k.name in mmd_names]
|
||||
count = 0
|
||||
for name in to_remove:
|
||||
keys = obj.data.shape_keys.key_blocks
|
||||
if name in keys:
|
||||
obj.active_shape_key_index = keys.find(name)
|
||||
bpy.ops.object.shape_key_remove()
|
||||
count += 1
|
||||
self.report({'INFO'}, f'Rimosse {count} shape key MMD')
|
||||
return {'FINISHED'}
|
||||
Reference in New Issue
Block a user