diff --git a/__init__.py b/__init__.py index 3a08f583..b2401c08 100644 --- a/__init__.py +++ b/__init__.py @@ -37,23 +37,24 @@ from bpy.app.handlers import persistent from bpy_extras.io_utils import ExportHelper, ImportHelper -from . import humanoid +from . import addon_updater_ops from . import algorithms from . import animationengine -from . import proxyengine +from . import creation_tools_ops from . import expressionengine +from . import expressionscreator +from . import facerig from . import file_ops -from . import object_ops from . import hairengine -from . import numpy_ops +from . import humanoid +from . import humanoid_rotations +from . import morphcreator from . import node_ops +from . import numpy_ops +from . import object_ops +from . import proxyengine from . import utils -from . import humanoid_rotations from . import preferences -from . import addon_updater_ops -from . import facerig -from . import morphcreator -from . import creation_tools_ops logger = logging.getLogger(__name__) @@ -77,6 +78,7 @@ mblab_retarget = animationengine.RetargetEngine() mblab_shapekeys = expressionengine.ExpressionEngineShapeK() mblab_proxy = proxyengine.ProxyEngine() +mbcrea_expressionscreator = expressionscreator.ExpressionsCreator() gui_status = "NEW_SESSION" gui_err_msg = "" @@ -219,6 +221,16 @@ def realtime_update(self, context): # time1 = time.time() scn = bpy.context.scene mblab_humanoid.update_character(category_name=scn.morphingCategory, mode="update_realtime") + #Teto + # Dirty, but I didn't want to touch the code too much. + # I tried things, but I am pretty sure that they would + # bring inconsistencies when changing model without + # quitting Blender. + # So we always update expressions category, because same + # prop are used in "facial expression creator". + if scn.morphingCategory != "Expressions": + mblab_humanoid.update_character(category_name="Expressions", mode="update_realtime") + #End Teto mblab_humanoid.sync_gui_according_measures() # print("realtime_update: {0}".format(time.time()-time1)) @@ -330,7 +342,9 @@ def init_morphing_props(humanoid_instance): bpy.types.Object, prop, bpy.props.FloatProperty( - name=prop, + #Teto + name=prop.split("_")[1], + #End Teto min=-5.0, max=5.0, soft_min=0.0, @@ -339,7 +353,6 @@ def init_morphing_props(humanoid_instance): default=0.5, update=realtime_update)) - def init_measures_props(humanoid_instance): for measure_name, measure_val in humanoid_instance.morph_engine.measures.items(): setattr( @@ -350,9 +363,10 @@ def init_measures_props(humanoid_instance): default=measure_val)) humanoid_instance.sync_gui_according_measures() - +#Teto def init_categories_props(humanoid_instance): categories_enum = [] + # All categories for "Body Measures" for category in mblab_humanoid.get_categories(): categories_enum.append( (category.name, category.name, category.name)) @@ -361,7 +375,16 @@ def init_categories_props(humanoid_instance): items=categories_enum, update=modifiers_update, name="Morphing categories") - + + # Sub-categories for "Facial expressions" + mbcrea_expressionscreator.set_expressions_modifiers(mblab_humanoid) + sub_categories_enum = mbcrea_expressionscreator.get_expressions_sub_categories() + + bpy.types.Scene.expressionsSubCategory = bpy.props.EnumProperty( + items=sub_categories_enum, + update=modifiers_update, + name="Expressions sub-categories") +#End Teto def init_restposes_props(humanoid_instance): if humanoid_instance.exists_rest_poses_database(): @@ -665,7 +688,7 @@ def hair_style_list(self, context): #Teto def mbcrea_enum_expressions_items_update(self, context): - return expressionscreator.get_expressions_items() + return mbcrea_expressionscreator.get_expressions_items() bpy.types.Scene.mbcrea_enum_expressions_items = bpy.props.EnumProperty( @@ -2406,6 +2429,10 @@ class StartSession(bpy.types.Operator): def execute(self, context): start_lab_session() + #Teto + morphcreator.init_morph_names_database() + mbcrea_expressionscreator.reset_expressions_items() + #End Teto return {'FINISHED'} @@ -2471,7 +2498,6 @@ def draw(self, context): if scn.mblab_use_cycles or scn.mblab_use_eevee: box_new_opt.prop(scn, 'mblab_use_lamps', icon='LIGHT_DATA') box_new_opt.operator('mbast.init_character', icon='ARMATURE_DATA') - morphcreator.init_morph_names_database() if gui_status != "ACTIVE_SESSION": self.layout.label(text=" ") @@ -2511,9 +2537,9 @@ def draw(self, context): if hasattr(obj, expr_name) and scn.mblab_expression_filter in expr_name: box_exp.prop(obj, expr_name) else: - expressionscreator.set_expressions_items(sorted_expressions) + mbcrea_expressionscreator.set_expressions_items(sorted_expressions) box_exp.prop(scn, 'mbcrea_enum_expressions_items') - result = expressionscreator.get_expressions_item(scn.mbcrea_enum_expressions_items) + result = mbcrea_expressionscreator.get_expressions_item(scn.mbcrea_enum_expressions_items) box_exp.prop(obj, result) #End Teto box_exp.operator("mbast.reset_expression", icon="RECOVER_LAST") @@ -2737,8 +2763,10 @@ def draw(self, context): col.prop(scn, "morphingCategory") for prop in mblab_humanoid.get_properties_in_category(scn.morphingCategory): - if hasattr(obj, prop): + #Teto + if hasattr(obj, prop) and not prop.startswith("Expressions_ID"): col.prop(obj, prop) + #End Teto if mblab_humanoid.exists_measure_database() and scn.mblab_show_measures: col = split.column() @@ -2975,13 +3003,13 @@ def draw(self, context): box_morphexpression = box_adaptation_tools.box() if is_objet == "FOUND": box_morphexpression.operator('mbast.button_store_base_vertices', icon="SPHERE") #Store all vertices of the actual body. - box_morphexpression.label(text="Expression wording - Name", icon='SORT_ASC') + box_morphexpression.label(text="Expr. wording - Name", icon='SORT_ASC') box_morphexpression.prop(scn, "mbcrea_standard_base_expr") - final_name = "Expressions_" + expressionscreator.get_standard_base_expr(scn.mbcrea_standard_base_expr) - if scn.mbcrea_standard_base_expr == 'NE': + final_name = "Expressions_" + mbcrea_expressionscreator.get_standard_base_expr(scn.mbcrea_standard_base_expr) + if scn.mbcrea_standard_base_expr == 'OT': box_morphexpression.prop(scn, "mbcrea_body_part_expr") - final_name = "Expressions_" + expressionscreator.get_body_parts_expr(scn.mbcrea_body_part_expr) - if scn.mbcrea_body_part_expr == 'NE': + final_name = "Expressions_" + mbcrea_expressionscreator.get_body_parts_expr(scn.mbcrea_body_part_expr) + if scn.mbcrea_body_part_expr == 'OT': box_morphexpression.prop(scn, "mbcrea_new_base_expr_name") final_name = "Expressions_" + scn.mbcrea_new_base_expr_name.lower() box_morphexpression.prop(scn, "mbcrea_expr_name") @@ -2989,22 +3017,22 @@ def draw(self, context): box_morphexpression.prop(scn, "mbcrea_min_max_expr") if scn.mbcrea_min_max_expr == 'MI': box_morphexpression.label(text="Reminder, min only not allowed.", icon='INFO') - final_name += "_" + expressionscreator.get_min_max_expr(scn.mbcrea_min_max_expr) - if final_name in expressionscreator.get_standard_expressions_list(): + final_name += "_" + mbcrea_expressionscreator.get_min_max_expr(scn.mbcrea_min_max_expr) + if final_name in mbcrea_expressionscreator.get_standard_expressions_list(): box_morphexpression.label(text="!WARNING! may overwrite standard expression!", icon='ERROR') - expressionscreator.set_expression_name(final_name) + mbcrea_expressionscreator.set_expression_name(final_name) box_morphexpression.label(text="Complete name : " + final_name, icon='INFO') #------------------------------ - box_morphexpression.label(text="Expression wording - File", icon='SORT_ASC') + box_morphexpression.label(text="Expr. wording - File", icon='SORT_ASC') box_morphexpression.prop(scn, "mbcrea_expr_pseudo") box_morphexpression.prop(scn, 'mbcrea_incremental_saves_expr') box_morphexpression.prop(scn, 'mbcrea_standard_ID_expr') - expressionscreator.set_expression_ID(scn.mbcrea_standard_ID_expr) + mbcrea_expressionscreator.set_expression_ID(scn.mbcrea_standard_ID_expr) if scn.mbcrea_standard_ID_expr == 'OT': box_morphexpression.prop(scn, "mbcrea_other_ID_expr") - expressionscreator.set_expression_ID(scn.mbcrea_other_ID_expr) + mbcrea_expressionscreator.set_expression_ID(scn.mbcrea_other_ID_expr) else: - expressionscreator.set_expression_ID(str(scn.mbcrea_standard_ID_expr).capitalize()) + mbcrea_expressionscreator.set_expression_ID(str(scn.mbcrea_standard_ID_expr).capitalize()) box_morphexpression.operator('mbast.button_store_work_in_progress', icon="MONKEY") #Store all vertices of the modified expression in a wip. box_morphexpression.operator('mbcrea.button_save_final_base_expression', icon="FREEZE") #Save the final expression. box_morphexpression.label(text="Tools", icon='SORT_ASC') @@ -3020,11 +3048,47 @@ def draw(self, context): else: box_adaptation_tools.operator('mbcrea.button_combinexpression_off', icon=icon_collapse) box_combinexpression = box_adaptation_tools.box() - box_combinexpression.label(text="#TODO Combine expressions") - box_combinexpression.label(text="to have plain expressions...") - + if is_objet == "FOUND": + obj = algorithms.get_active_body() #to be sure... + mblab_humanoid.bodydata_realtime_activated = True + #------------------------- + box_combinexpression.operator("mbcrea.reset_expressionscategory", icon="RECOVER_LAST") + box_combinexpression.label(text="Base expressions", icon='SORT_ASC') + #--------- Expression filter --------- + box_combinexpression.prop(scn, 'mbcrea_base_expression_filter') + sorted_expressions = sorted(mblab_humanoid.get_properties_in_category("Expressions")) + if len(str(scn.mbcrea_base_expression_filter)) > 0: + for expr_name in sorted_expressions: + if hasattr(obj, expr_name) and scn.mbcrea_base_expression_filter in expr_name and not expr_name.startswith("Expressions_ID"): + box_combinexpression.prop(obj, expr_name) + #-------- Expression enumProp -------- + else: + box_combinexpression.prop(scn, 'expressionsSubCategory') + props = sorted(mbcrea_expressionscreator.get_items_in_sub(scn.expressionsSubCategory), reverse = True) + for prop in props: + if hasattr(obj, prop): + box_combinexpression.prop(obj, prop) + #-------- New expression name -------- + box_combinexpression.label(text="Expr. wording - Name", icon='SORT_ASC') + box_combinexpression.prop(scn, 'mbcrea_comb_expression_filter') + comb_name = str(scn.mbcrea_comb_expression_filter).lower() + comb_name = algorithms.split_name(comb_name, splitting_char=mbcrea_expressionscreator.forbidden_char_list) + box_combinexpression.label(text="File name : " + comb_name, icon='INFO') + check_root = mblab_humanoid.get_root_model_name() + if mbcrea_expressionscreator.is_comb_expression_exists(check_root, comb_name): + box_combinexpression.label(text="File already exists !", icon='ERROR') + #-------- New expression file -------- + box_combinexpression.label(text="Expr. wording - File", icon='SORT_ASC') + if len(comb_name) < 1: + box_combinexpression.label(text="Choose a name !", icon='ERROR') + else: + box_combinexpression.label(text="Save in : " + mblab_humanoid.get_root_model_name(), icon='INFO') + box_combinexpression.operator('mbcrea.button_save_final_comb_expression', icon="FREEZE") #Save the final expression. + else: + box_combinexpression.label(text="!NO COMPATIBLE MODEL!", icon='ERROR') + box_combinexpression.enabled = False + #Create/edit tools... - if gui_active_panel_first != "compat_tools": box_tools.operator('mbcrea.button_compat_tools_on', icon=icon_expand) else: @@ -3123,6 +3187,7 @@ def draw(self, context): box_compat_tools.operator('mbcrea.button_management_tools_off', icon=icon_collapse) box_management_tools = box_compat_tools.box() box_management_tools.label(text="#TODO files management tools...") + box_tools.separator(factor=0.5) """ bpy.types.Scene.mblab_incremental_saves = bpy.props.BoolProperty( @@ -3168,12 +3233,12 @@ def draw(self, context): subtype='FILE_NAME') bpy.types.Scene.mbcrea_standard_base_expr = bpy.props.EnumProperty( - items=expressionscreator.get_standard_base_expr(), + items=mbcrea_expressionscreator.get_standard_base_expr(), name="", default="CK") bpy.types.Scene.mbcrea_body_part_expr = bpy.props.EnumProperty( - items=expressionscreator.get_body_parts_expr(), + items=mbcrea_expressionscreator.get_body_parts_expr(), name="Body part", default="MO") @@ -3192,7 +3257,7 @@ def draw(self, context): subtype='FILE_NAME') bpy.types.Scene.mbcrea_min_max_expr = bpy.props.EnumProperty( - items=expressionscreator.get_min_max_expr(), + items=mbcrea_expressionscreator.get_min_max_expr(), name="min/max:", default="MA") @@ -3208,7 +3273,7 @@ def draw(self, context): description="Does an incremental save each time\n the final save button is pressed.\nFrom 001 to 999\nCaution : returns to 001 between sessions") bpy.types.Scene.mbcrea_standard_ID_expr = bpy.props.EnumProperty( - items=expressionscreator.get_expression_ID_list(), + items=mbcrea_expressionscreator.get_expression_ID_list(), name="Model ID", default="HU") @@ -3219,6 +3284,21 @@ def draw(self, context): maxlen=1024, subtype='FILE_NAME') +bpy.types.Scene.mbcrea_base_expression_filter = bpy.props.StringProperty( + name="Filter", + description="Filter the base expressions available.\nCase sensitive !", + default="", + maxlen=1024, + subtype='FILE_NAME') + +bpy.types.Scene.mbcrea_comb_expression_filter = bpy.props.StringProperty( + name="Name", + description="Name the new face expression", + default="", + maxlen=1024, + subtype='FILE_NAME') + + class FinalizeExpression(bpy.types.Operator): """ Working like FinalizeMorph @@ -3226,7 +3306,7 @@ class FinalizeExpression(bpy.types.Operator): bl_label = 'Finalize the base expression' bl_idname = 'mbcrea.button_save_final_base_expression' filename_ext = ".json" - bl_description = 'Finalize the expression, ask for min and max files, create or open the expression file, replace or append new expression' + bl_description = 'Finalize the expression,\nask for min and max files,\ncreate or open the expression file,\nreplace or append new expression' bl_context = 'objectmode' bl_options = {'REGISTER', 'INTERNAL'} @@ -3254,17 +3334,17 @@ def execute(self, context): if len(scn.mbcrea_expr_pseudo) > 0: file_name += "_" + scn.mbcrea_expr_pseudo if scn.mbcrea_incremental_saves_expr: - file_name += "_" + expressionscreator.get_next_number() + file_name += "_" + mbcrea_expressionscreator.get_next_number() #-------Expression name---------- - expression_name = expressionscreator.get_expression_name() + expression_name = mbcrea_expressionscreator.get_expression_name() #-------Expression path---------- file_path_name = os.path.join(file_ops.get_data_path(), "expressions_morphs", file_name + ".json") file = file_ops.load_json_data(file_path_name, "Try to load an expression file") if file == None: file = {} #---Creating new expression------- - file[expressionscreator.get_expression_name()] = indexed_vertices - file[expressionscreator.get_expression_ID()] = [] + file[mbcrea_expressionscreator.get_expression_name()] = indexed_vertices + file[mbcrea_expressionscreator.get_expression_ID()] = [] file_ops.save_json_data(file_path_name, file) #---------------------------- return {'FINISHED'} @@ -3275,6 +3355,35 @@ def draw(self, context): self.layout.label(text=message) bpy.context.window_manager.popup_menu(draw, title = title, icon = icon) +class FinalizeCombExpression(bpy.types.Operator): + """ + Working like Save character + """ + bl_label = 'Finalize the face expression' + bl_idname = 'mbcrea.button_save_final_comb_expression' + filename_ext = ".json" + bl_description = 'Finalize the face expression,\ncreate or open the face expression file,\nreplace or create new face expression' + bl_context = 'objectmode' + bl_options = {'REGISTER', 'INTERNAL'} + + def execute(self, context): + scn = bpy.context.scene + mbcrea_expressionscreator.set_lab_version(bl_info["version"]) + #-------File name---------- + comb_name = str(scn.mbcrea_comb_expression_filter).lower() + comb_name = algorithms.split_name(comb_name, splitting_char=mbcrea_expressionscreator.forbidden_char_list) + #--expression path + name-- + path = os.path.join(file_ops.get_data_path(), "expressions_comb", mblab_humanoid.get_root_model_name() + "_expressions", comb_name+".json") + #--------Saving file------- + mbcrea_expressionscreator.save_face_expression(path) + return {'FINISHED'} + + def ShowMessageBox(self, message = "", title = "Message Box", icon = 'INFO'): + + def draw(self, context): + self.layout.label(text=message) + bpy.context.window_manager.popup_menu(draw, title = title, icon = icon) + class ButtonCompatToolsDir(bpy.types.Operator): #just for quick tests bl_label = 'Create project directories' @@ -3326,17 +3435,11 @@ class ButtonForTest(bpy.types.Operator): bl_options = {'REGISTER', 'INTERNAL'} def execute(self, context): - global mblab_shapekeys - test = mblab_shapekeys.get_loaded_expression_database() - print(test[0]) - print(test[1]) - print(test[2]) - print(test[3]) - print("------------------------------") + global mblab_humanoid return {'FINISHED'} class ButtonAdaptationToolsON(bpy.types.Operator): - bl_label = 'Adaptation tools' + bl_label = 'Model edition' bl_idname = 'mbcrea.button_adaptation_tools_on' bl_description = 'All tools to change / adapt from an existing model' bl_context = 'objectmode' @@ -3349,7 +3452,7 @@ def execute(self, context): return {'FINISHED'} class ButtonAdaptationToolsOFF(bpy.types.Operator): - bl_label = 'Adaptation tools' + bl_label = 'Model edition' bl_idname = 'mbcrea.button_adaptation_tools_off' bl_description = 'All tools to change / adapt from an existing model' bl_context = 'objectmode' @@ -3362,7 +3465,7 @@ def execute(self, context): return {'FINISHED'} class ButtonCompatToolsON(bpy.types.Operator): - bl_label = 'Compatibility tools' + bl_label = 'Model creation' bl_idname = 'mbcrea.button_compat_tools_on' bl_description = 'All tools to make a model compatible with MB-Lab' bl_context = 'objectmode' @@ -3375,7 +3478,7 @@ def execute(self, context): return {'FINISHED'} class ButtonCompatToolsOFF(bpy.types.Operator): - bl_label = 'Compatibility tools' + bl_label = 'Model creation' bl_idname = 'mbcrea.button_compat_tools_off' bl_description = 'All tools to make a model compatible with MB-Lab' bl_context = 'objectmode' @@ -3440,9 +3543,9 @@ def execute(self, context): return {'FINISHED'} class ButtonMorphingON(bpy.types.Operator): - bl_label = 'Morph Creation' + bl_label = 'Simple Morph Creation' bl_idname = 'mbcrea.button_morphcreator_on' - bl_description = 'Morph creation panel' + bl_description = 'Simple morph creation panel' bl_context = 'objectmode' bl_options = {'REGISTER', 'INTERNAL'} @@ -3452,9 +3555,9 @@ def execute(self, context): return {'FINISHED'} class ButtonMorphingOFF(bpy.types.Operator): - bl_label = 'Morph Creation' + bl_label = 'Simple Morph Creation' bl_idname = 'mbcrea.button_morphcreator_off' - bl_description = 'Morph creation panel' + bl_description = 'Simple morph creation panel' bl_context = 'objectmode' bl_options = {'REGISTER', 'INTERNAL'} @@ -3464,7 +3567,7 @@ def execute(self, context): return {'FINISHED'} class ButtonMorphExpressionON(bpy.types.Operator): - bl_label = 'Base expressions' + bl_label = 'Base Expressions Creation' bl_idname = 'mbcrea.button_morphexpression_on' bl_description = 'Tool for morphing base expressions' bl_context = 'objectmode' @@ -3477,7 +3580,7 @@ def execute(self, context): return {'FINISHED'} class ButtonMorphExpressionOFF(bpy.types.Operator): - bl_label = 'Base expressions' + bl_label = 'Base Expressions Creation' bl_idname = 'mbcrea.button_morphexpression_off' bl_description = 'Tool for morphing base expressions' bl_context = 'objectmode' @@ -3490,7 +3593,7 @@ def execute(self, context): return {'FINISHED'} class ButtonCombineExpressionON(bpy.types.Operator): - bl_label = 'Facial expressions' + bl_label = 'Facial Expressions Creation' bl_idname = 'mbcrea.button_combinexpression_on' bl_description = 'Tool for combining base expressions' bl_context = 'objectmode' @@ -3503,7 +3606,7 @@ def execute(self, context): return {'FINISHED'} class ButtonCombineExpressionOFF(bpy.types.Operator): - bl_label = 'Facial expressions' + bl_label = 'Facial Expressions Creation' bl_idname = 'mbcrea.button_combinexpression_off' bl_description = 'Tool for combining base expressions' bl_context = 'objectmode' @@ -3735,6 +3838,20 @@ def execute(self, context): creation_tools_ops.init_project() return {'FINISHED'} +class Reset_expression_category(bpy.types.Operator): + """Reset the parameters for the currently selected category""" + bl_label = 'Reset expressions' + bl_idname = 'mbcrea.reset_expressionscategory' + bl_description = 'Reset the parameters for expressions' + bl_context = 'objectmode' + bl_options = {'REGISTER', 'INTERNAL', 'UNDO'} + + def execute(self, context): + global mblab_humanoid + scn = bpy.context.scene + mblab_humanoid.reset_category("Expressions") + return {'FINISHED'} + classes = ( ButtonParametersOff, @@ -3860,6 +3977,8 @@ def execute(self, context): ButtonSaveCompatProject, ButtonLoadCompatProject, FinalizeExpression, + FinalizeCombExpression, + Reset_expression_category, VIEW3D_PT_tools_MBCrea, ) diff --git a/algorithms.py b/algorithms.py index 9465e0fc..23f96b3c 100644 --- a/algorithms.py +++ b/algorithms.py @@ -1196,6 +1196,7 @@ def remove_censors(): return None +#Teto # ------------------------------------------------------------------------ # UI and UI init Functions # ------------------------------------------------------------------------ @@ -1206,10 +1207,33 @@ def remove_censors(): # we use things like bpy.types.scene.whatever. # Here, the trick is to keep the list of tuples used to # create the component somewhere, and check in it. -def get_enum_property_item(key, enum_property): +def get_enum_property_item(key, enum_property, index=1): value = None - for index in range(len(enum_property)): - if key in enum_property[index]: - value = enum_property[index] - return value[1] - return "" \ No newline at end of file + for ind in range(len(enum_property)): + if key in enum_property[ind]: + value = enum_property[ind] + return value[index] + return "" + +#create an enumProperty list of tuples, from a list. +def create_enum_property_items(values=[], key_length=3, tip_length=4): + if values == None or len(values) < 1: + return [("0", "NONE", "")] + return_list = [] + for i in range(len(values)): + return_list.append( + (str(i).zfill(key_length), + values[i], + str(values[i])[0:tip_length])) + return return_list + +def split_name(name, splitting_char=[], indexes=[]): + if len(splitting_char) < 1: + return name + if len(indexes) < 1: + indexes = [0]*len(splitting_char) + result = name + for i in range(len(splitting_char)): + result = result.split(splitting_char[i])[indexes[i]] + return result +#End Teto \ No newline at end of file diff --git a/creation_tools_ops.py b/creation_tools_ops.py index b2654ec6..12c5bdc7 100644 --- a/creation_tools_ops.py +++ b/creation_tools_ops.py @@ -42,7 +42,7 @@ "male_poses", "rest_poses"] forbiden_names = ["human", "humans", "anime", "male", "female", "anthropometry", "bbox", "expressions", - "exprs", "morphs", "hair", "joints", "offset", "measures", "extra", "polygs", "ptypes", + "exprs", "Expression", "morphs", "hair", "joints", "offset", "measures", "extra", "polygs", "ptypes", "poses", "rest", "specialtype", "anyme", "style", "type", "base", "transf", "verts", "vgroups", "muscles", "none"] diff --git a/expressionengine.py b/expressionengine.py index 6dc443be..f33c8130 100644 --- a/expressionengine.py +++ b/expressionengine.py @@ -60,9 +60,10 @@ def __init__(self): self.expressions_data = {} self.model_type = "NONE" self.has_data = True + #Teto + self.expression_creator = expressionscreator.ExpressionsCreator() def identify_model_type(self): - #Teto #self.model_type = "NONE" obj = algorithms.get_active_body() if obj: @@ -79,7 +80,7 @@ def identify_model_type(self): length = len(id)-4 tp_name = id[14:length].upper() if tp_name != self.model_type: - expressionscreator.reset_expressions_items() + self.expression_creator.reset_expressions_items() self.model_type = tp_name return self.model_type = "NONE" diff --git a/expressionscreator.py b/expressionscreator.py index 785833c1..a3814ae0 100644 --- a/expressionscreator.py +++ b/expressionscreator.py @@ -29,250 +29,341 @@ import bpy import numpy from . import algorithms - -standard_expressions_list = ["abdomExpansion_min", "abdomExpansion_max", - "browOutVertL_min", "browOutVertL_max", "browOutVertR_min", - "browOutVertR_max", "browsMidVert_min", "browsMidVert_max", - "browSqueezeL_min", "browSqueezeL_max", "browSqueezeR_min", - "browSqueezeR_max", "cheekSneerL_max", "cheekSneerR_max", - "chestExpansion_min", "chestExpansion_max", "deglutition_min", - "deglutition_max", "eyeClosedL_min", "eyeClosedL_max", - "eyeClosedPressureL_min", "eyeClosedPressureL_max", - "eyeClosedPressureR_min", "eyeClosedPressureR_max", - "eyeClosedR_min", "eyeClosedR_max", "eyesHoriz_min", - "eyesHoriz_max", "eyeSquintL_min", "eyeSquintL_max", - "eyeSquintR_min", "eyeSquintR_max", "eyesSmile_max", - "eyesVert_min", "eyesVert_max", "jawHoriz_min", "jawHoriz_max", - "jawOut_min", "jawOut_max", "mouthBite_min", "mouthBite_max", - "mouthChew_min", "mouthChew_max", "mouthClosed_min", - "mouthClosed_max", "mouthHoriz_min", "mouthHoriz_max", - "mouthInflated_min", "mouthInflated_max", "mouthLowerOut_min", - "mouthLowerOut_max", "mouthOpen_min", "mouthOpen_max", - "mouthOpenAggr_min", "mouthOpenAggr_max", "mouthOpenHalf_max", - "mouthOpenLarge_min", "mouthOpenLarge_max", "mouthOpenO_min", - "mouthOpenO_max", "mouthOpenTeethClosed_min", - "mouthOpenTeethClosed_max", "mouthSmile_min", "mouthSmile_max", - "mouthSmileL_max", "mouthSmileOpen_min", "mouthSmileOpen_max", - "mouthSmileOpen2_min", "mouthSmileOpen2_max", "mouthSmileR_max", - "nostrilsExpansion_min", "nostrilsExpansion_max", - "pupilsDilatation_min", "pupilsDilatation_max", "tongueHoriz_min", - "tongueHoriz_max", "tongueOut_min", "tongueOut_max", - "tongueOutPressure_max", "tongueTipUp_max", "tongueVert_min", - "tongueVert_max"] - -standard_expressions = [("AA", "abdomExpansion_min", "abdomen"), - ("AB", "abdomExpansion_max", "abdomen"), - ("AC", "browOutVertL_min", "brow"), - ("AD", "browOutVertL_max", "brow"), - ("AE", "browOutVertR_min", "brow"), - ("AF", "browOutVertR_max", "brow"), - ("AG", "browsMidVert_min", "brow"), - ("AH", "browsMidVert_max", "brow"), - ("AI", "browSqueezeL_min", "brow"), - ("AJ", "browSqueezeL_max", "brow"), - ("AK", "browSqueezeR_min", "brow"), - ("AL", "browSqueezeR_max", "brow"), - ("AM", "cheekSneerL_max", "cheek"), - ("AN", "cheekSneerR_max", "cheek"), - ("AO", "chestExpansion_min", "chest"), - ("AP", "chestExpansion_max", "chest"), - ("AQ", "deglutition_min", "deglutition"), - ("AR", "deglutition_max", "deglutition"), - ("AS", "eyeClosedL_min", "eye"), - ("AT", "eyeClosedL_max", "eye"), - ("AU", "eyeClosedPressureL_min", "eye"), - ("AV", "eyeClosedPressureL_max", "eye"), - ("AW", "eyeClosedPressureR_min", "eye"), - ("AX", "eyeClosedPressureR_max", "eye"), - ("AY", "eyeClosedR_min", "eye"), - ("AY", "eyeClosedR_max", "eye"), - ("AZ", "eyesHoriz_min", "eye"), - ("BA", "eyesHoriz_max", "eye"), - ("BB", "eyeSquintL_min", "eye"), - ("BC", "eyeSquintL_max", "eye"), - ("BD", "eyeSquintR_min", "eye"), - ("BE", "eyeSquintR_max", "eye"), - ("BF", "eyesSmile_max", "eye"), - ("BG", "eyesVert_min", "eye"), - ("BH", "eyesVert_max", "eye"), - ("BI", "jawHoriz_min", "jaw"), - ("BJ", "jawHoriz_max", "jaw"), - ("BK", "jawOut_min", "jaw"), - ("BL", "jawOut_max", "jaw"), - ("BM", "mouthBite_min", "mouth"), - ("BN", "mouthBite_max", "mouth"), - ("BO", "mouthChew_min", "mouth"), - ("BP", "mouthChew_max", "mouth"), - ("BQ", "mouthClosed_min", "mouth"), - ("BR", "mouthClosed_max", "mouth"), - ("BS", "mouthHoriz_min", "mouth"), - ("BT", "mouthHoriz_max", "mouth"), - ("BU", "mouthInflated_min", "mouth"), - ("BV", "mouthInflated_max", "mouth"), - ("BW", "mouthLowerOut_min", "mouth"), - ("BX", "mouthLowerOut_max", "mouth"), - ("BY", "mouthOpen_min", "mouth"), - ("BZ", "mouthOpen_max", "mouth"), - ("CA", "mouthOpenAggr_min", "mouth"), - ("CB", "mouthOpenAggr_max", "mouth"), - ("CC", "mouthOpenHalf_max", "mouth"), - ("CD", "mouthOpenLarge_min", "mouth"), - ("CE", "mouthOpenLarge_max", "mouth"), - ("CF", "mouthOpenO_min", "mouth"), - ("CG", "mouthOpenO_max", "mouth"), - ("CH", "mouthOpenTeethClosed_min", "mouth"), - ("CI", "mouthOpenTeethClosed_max", "mouth"), - ("CJ", "mouthSmile_min", "mouth"), - ("CK", "mouthSmile_max", "mouth"), - ("CL", "mouthSmileL_max", "mouth"), - ("CM", "mouthSmileOpen_min", "mouth"), - ("CN", "mouthSmileOpen_max", "mouth"), - ("CO", "mouthSmileOpen2_min", "mouth"), - ("CP", "mouthSmileOpen2_max", "mouth"), - ("CQ", "mouthSmileR_max", "mouth"), - ("CR", "nostrilsExpansion_min", "nostrils"), - ("CS", "nostrilsExpansion_max", "nostrils"), - ("CT", "pupilsDilatation_min", "pupils"), - ("CU", "pupilsDilatation_max", "pupils"), - ("CV", "tongueHoriz_min", "tongue"), - ("CW", "tongueHoriz_max", "tongue"), - ("CX", "tongueOut_min", "tongue"), - ("CY", "tongueOut_max", "tongue"), - ("CZ", "tongueOutPressure_max", "tongue"), - ("DA", "tongueTipUp_max", "tongue"), - ("DB", "tongueVert_min", "tongue"), - ("DC", "tongueVert_max", "tongue"), - ("NE", "NEW (overwrite below)", "new")] - -body_parts_expr = [ - ("AB", "abdom", ""), - ("BR", "brow", ""), - ("BS", "brows", ""), - ("CH", "cheek", ""), - ("CE", "chest", ""), - ("DE", "deglutition", ""), - ("EY", "eye", ""), - ("ES", "eyes", ""), - ("JA", "jaw", ""), - ("MO", "mouth", ""), - ("NO", "nostrils", ""), - ("PU", "pupils", ""), - ("TO", "tongue", ""), - ("NE", "NEW (below)", "")] - -min_max_expr = [("MI", "min", "min = 0"), - ("MA", "max", "max = 1")] - -expression_ID_list = [("HU", "Humans", "Standard in MB-Lab"), - ("AN", "Anime", "Standard in MB-Lab"), - ("OT", "OTHER", "For another model")] - -expression_name = ["", "", 0] -#the number is for autosaves. - -editor_expressions_items = [] -# To have quickly items for enum_property, -# and create them only when model is changed. - -#operator_for_base_expr = {} +from . import file_ops logger = logging.getLogger(__name__) -#--------------Play with variables -def get_standard_expressions_list(): - return standard_expressions_list - -def get_standard_base_expr(key=None): - if key == None: - return standard_expressions - value = None - for index in range(len(standard_expressions)): - if key in standard_expressions[index]: - value = standard_expressions[index] - return value[1] - return "" - -def get_body_parts_expr(key=None): - if key == None: - return body_parts_expr - value = None - for index in range(len(body_parts_expr)): - if key in body_parts_expr[index]: - value = body_parts_expr[index] - return value[1] - return "" - -def get_min_max_expr(key=None): - if key == None: - return min_max_expr - value = None - for index in range(len(min_max_expr)): - if key in min_max_expr[index]: - value = min_max_expr[index] - return value[1] - return "" - -def set_expression_name(name): - global expression_name - expression_name[0] = name - -def get_expression_name(): - return expression_name[0] - -def set_expression_ID(id): - global expression_name - expression_name[1] = "Expressions_ID" + id + "_max" - -def get_expression_ID(): - return expression_name[1] - -def get_expression_ID_list(): - return expression_ID_list +class ExpressionsCreator(): -def get_next_number(): - expression_name[2] += 1 - return str(expression_name[2]).zfill(3) + def __init__(self): + + self.standard_expressions_list = ["abdomExpansion_min", "abdomExpansion_max", + "browOutVertL_min", "browOutVertL_max", "browOutVertR_min", + "browOutVertR_max", "browsMidVert_min", "browsMidVert_max", + "browSqueezeL_min", "browSqueezeL_max", "browSqueezeR_min", + "browSqueezeR_max", "cheekSneerL_max", "cheekSneerR_max", + "chestExpansion_min", "chestExpansion_max", "deglutition_min", + "deglutition_max", "eyeClosedL_min", "eyeClosedL_max", + "eyeClosedPressureL_min", "eyeClosedPressureL_max", + "eyeClosedPressureR_min", "eyeClosedPressureR_max", + "eyeClosedR_min", "eyeClosedR_max", "eyesHoriz_min", + "eyesHoriz_max", "eyeSquintL_min", "eyeSquintL_max", + "eyeSquintR_min", "eyeSquintR_max", "eyesSmile_max", + "eyesVert_min", "eyesVert_max", "jawHoriz_min", "jawHoriz_max", + "jawOut_min", "jawOut_max", "mouthBite_min", "mouthBite_max", + "mouthChew_min", "mouthChew_max", "mouthClosed_min", + "mouthClosed_max", "mouthHoriz_min", "mouthHoriz_max", + "mouthInflated_min", "mouthInflated_max", "mouthLowerOut_min", + "mouthLowerOut_max", "mouthOpen_min", "mouthOpen_max", + "mouthOpenAggr_min", "mouthOpenAggr_max", "mouthOpenHalf_max", + "mouthOpenLarge_min", "mouthOpenLarge_max", "mouthOpenO_min", + "mouthOpenO_max", "mouthOpenTeethClosed_min", + "mouthOpenTeethClosed_max", "mouthSmile_min", "mouthSmile_max", + "mouthSmileL_max", "mouthSmileOpen_min", "mouthSmileOpen_max", + "mouthSmileOpen2_min", "mouthSmileOpen2_max", "mouthSmileR_max", + "nostrilsExpansion_min", "nostrilsExpansion_max", + "pupilsDilatation_min", "pupilsDilatation_max", "tongueHoriz_min", + "tongueHoriz_max", "tongueOut_min", "tongueOut_max", + "tongueOutPressure_max", "tongueTipUp_max", "tongueVert_min", + "tongueVert_max"] + + self.standard_expressions = [("AA", "abdomExpansion_min", "abdomen"), + ("AB", "abdomExpansion_max", "abdomen"), + ("AC", "browOutVertL_min", "brow"), + ("AD", "browOutVertL_max", "brow"), + ("AE", "browOutVertR_min", "brow"), + ("AF", "browOutVertR_max", "brow"), + ("AG", "browsMidVert_min", "brow"), + ("AH", "browsMidVert_max", "brow"), + ("AI", "browSqueezeL_min", "brow"), + ("AJ", "browSqueezeL_max", "brow"), + ("AK", "browSqueezeR_min", "brow"), + ("AL", "browSqueezeR_max", "brow"), + ("AM", "cheekSneerL_max", "cheek"), + ("AN", "cheekSneerR_max", "cheek"), + ("AO", "chestExpansion_min", "chest"), + ("AP", "chestExpansion_max", "chest"), + ("AQ", "deglutition_min", "deglutition"), + ("AR", "deglutition_max", "deglutition"), + ("AS", "eyeClosedL_min", "eye"), + ("AT", "eyeClosedL_max", "eye"), + ("AU", "eyeClosedPressureL_min", "eye"), + ("AV", "eyeClosedPressureL_max", "eye"), + ("AW", "eyeClosedPressureR_min", "eye"), + ("AX", "eyeClosedPressureR_max", "eye"), + ("AY", "eyeClosedR_min", "eye"), + ("AY", "eyeClosedR_max", "eye"), + ("AZ", "eyesHoriz_min", "eye"), + ("BA", "eyesHoriz_max", "eye"), + ("BB", "eyeSquintL_min", "eye"), + ("BC", "eyeSquintL_max", "eye"), + ("BD", "eyeSquintR_min", "eye"), + ("BE", "eyeSquintR_max", "eye"), + ("BF", "eyesSmile_max", "eye"), + ("BG", "eyesVert_min", "eye"), + ("BH", "eyesVert_max", "eye"), + ("BI", "jawHoriz_min", "jaw"), + ("BJ", "jawHoriz_max", "jaw"), + ("BK", "jawOut_min", "jaw"), + ("BL", "jawOut_max", "jaw"), + ("BM", "mouthBite_min", "mouth"), + ("BN", "mouthBite_max", "mouth"), + ("BO", "mouthChew_min", "mouth"), + ("BP", "mouthChew_max", "mouth"), + ("BQ", "mouthClosed_min", "mouth"), + ("BR", "mouthClosed_max", "mouth"), + ("BS", "mouthHoriz_min", "mouth"), + ("BT", "mouthHoriz_max", "mouth"), + ("BU", "mouthInflated_min", "mouth"), + ("BV", "mouthInflated_max", "mouth"), + ("BW", "mouthLowerOut_min", "mouth"), + ("BX", "mouthLowerOut_max", "mouth"), + ("BY", "mouthOpen_min", "mouth"), + ("BZ", "mouthOpen_max", "mouth"), + ("CA", "mouthOpenAggr_min", "mouth"), + ("CB", "mouthOpenAggr_max", "mouth"), + ("CC", "mouthOpenHalf_max", "mouth"), + ("CD", "mouthOpenLarge_min", "mouth"), + ("CE", "mouthOpenLarge_max", "mouth"), + ("CF", "mouthOpenO_min", "mouth"), + ("CG", "mouthOpenO_max", "mouth"), + ("CH", "mouthOpenTeethClosed_min", "mouth"), + ("CI", "mouthOpenTeethClosed_max", "mouth"), + ("CJ", "mouthSmile_min", "mouth"), + ("CK", "mouthSmile_max", "mouth"), + ("CL", "mouthSmileL_max", "mouth"), + ("CM", "mouthSmileOpen_min", "mouth"), + ("CN", "mouthSmileOpen_max", "mouth"), + ("CO", "mouthSmileOpen2_min", "mouth"), + ("CP", "mouthSmileOpen2_max", "mouth"), + ("CQ", "mouthSmileR_max", "mouth"), + ("CR", "nostrilsExpansion_min", "nostrils"), + ("CS", "nostrilsExpansion_max", "nostrils"), + ("CT", "pupilsDilatation_min", "pupils"), + ("CU", "pupilsDilatation_max", "pupils"), + ("CV", "tongueHoriz_min", "tongue"), + ("CW", "tongueHoriz_max", "tongue"), + ("CX", "tongueOut_min", "tongue"), + ("CY", "tongueOut_max", "tongue"), + ("CZ", "tongueOutPressure_max", "tongue"), + ("DA", "tongueTipUp_max", "tongue"), + ("DB", "tongueVert_min", "tongue"), + ("DC", "tongueVert_max", "tongue"), + ("OT", "other", "other")] + + # For enumProperty + self.body_parts_expr = [ + ("AB", "abdom", ""), + ("BR", "brow", ""), + ("BS", "brows", ""), + ("CH", "cheek", ""), + ("CE", "chest", ""), + ("DE", "deglutition", ""), + ("EY", "eye", ""), + ("ES", "eyes", ""), + ("JA", "jaw", ""), + ("MO", "mouth", ""), + ("NO", "nostrils", ""), + ("PU", "pupils", ""), + ("TO", "tongue", ""), + ("OT", "other", "")] + + # Simple list + self.body_parts_expr_list = ["abdom", "brow", "brows", + "cheek", "chest", "deglutition", "eye", "eyes", + "jaw", "mouth", "nostrils", "pupils", "tongue"] + + + self.min_max_expr = [("MI", "min", "min = 0"), + ("MA", "max", "max = 1")] + + self.expression_ID_list = [("HU", "Humans", "Standard in MB-Lab"), + ("AN", "Anime", "Standard in MB-Lab"), + ("OT", "OTHER", "For another model")] + + self.forbidden_char_list = ['-', '_', '²', '&', '=', '¨', '^', '$', + '£', '%', 'µ', ',', '?', ';', '!', '§', '+', '*', '/'] + + self.expression_name = ["", "", 0] + #the number is for autosaves. + + self.editor_expressions_items = [] + # To have quickly items for enum_property, + # and create them only when model is changed. + # Used only AFTER finalization of the model. + + self.expressions_sub_categories = [] + # The sub_categories for expressions (jaw, brows, ...) + + self.expressions_modifiers = {} + # To have quickly modifiers for enum_property, + # and create them only when model is changed. + # Used only BEFORE finalization of the model, + # in the Combined Expression Editor. + + self.humanoid = None + # Instance of class Humanoid + + #--------------Play with variables + def set_lab_version(self, lab_version): + self.lab_vers = list(lab_version) + + def get_standard_expressions_list(self): + return self.standard_expressions_list + + def get_standard_base_expr(self, key=None): + if key == None: + return self.standard_expressions + value = None + for index in range(len(self.standard_expressions)): + if key in self.standard_expressions[index]: + value = self.standard_expressions[index] + return value[1] + return "" + + def get_body_parts_expr(self, key=None): + if key == None: + return self.body_parts_expr + value = None + for index in range(len(self.body_parts_expr)): + if key in self.body_parts_expr[index]: + value = self.body_parts_expr[index] + return value[1] + return "" + + def get_min_max_expr(self, key=None): + if key == None: + return self.min_max_expr + value = None + for index in range(len(self.min_max_expr)): + if key in self.min_max_expr[index]: + value = self.min_max_expr[index] + return value[1] + return "" -#--------------EnumProperty for exepressions in UI + def set_expression_name(self, name): + self.expression_name[0] = name -def reset_expressions_items(): - global editor_expressions_items - editor_expressions_items = [] + def get_expression_name(self): + return self.expression_name[0] -def set_expressions_items(sorted_names): - if len(editor_expressions_items) > 0: - return - key = 0 - for s_n in sorted_names: - editor_expressions_items.append((str(key).zfill(3), s_n, "" )) - key += 1 + def set_expression_ID(self, id): + self.expression_name[1] = "Expressions_ID" + id + "_max" -def get_expressions_items(): - return editor_expressions_items + def get_expression_ID(self): + return self.expression_name[1] -def get_expressions_item(key): - return algorithms.get_enum_property_item(key, editor_expressions_items) + def get_expression_ID_list(self): + return self.expression_ID_list + + def get_next_number(self): + self.expression_name[2] += 1 + return str(self.expression_name[2]).zfill(3) + + #Methods about EnumProperty for combined expressions creation + #-------------BEFORE finalization of the character + def reset_expressions_items(self): + self.editor_expressions_items.clear() + self.expressions_modifiers.clear() + self.expressions_sub_categories.clear() + + def set_expressions_modifiers(self, huma): + self.humanoid = huma + if len(self.expressions_modifiers) > 0: + return self.expressions_modifiers + category = self.humanoid.get_category("Expressions") + self.expressions_modifiers = category.get_modifier_tiny_name(self.body_parts_expr_list) + + def get_expressions_modifiers(self): + return self.expressions_modifiers + + # To know the real name from a key in an enumProp + def get_modifier_item(self, key): + return algorithms.get_enum_property_item(key, self.get_expressions_sub_categories()) + + # Give items in sub categories, + # but knows difference between eyes and eyeS in name + def get_items_in_sub(self, key): + sub = algorithms.get_enum_property_item(key, self.get_expressions_sub_categories()) + cat = self.humanoid.get_category("Expressions") + tiny = cat.get_modifier_tiny_name(sub_categories=[sub], exclude_in_others=self.body_parts_expr_list) + items = tiny[sub] + return_items = [] + for item in items: + return_items.append(item[2]) + return return_items + + # Return an enumProperty with all sub-categories. + def get_expressions_sub_categories(self): + if len(self.expressions_sub_categories) > 0: + return self.expressions_sub_categories + if len(self.expressions_modifiers) < 1 and self.humanoid != None: + self.set_expressions_modifiers(self.humanoid) + sorted_list = sorted(list(self.expressions_modifiers.keys())) + self.expressions_sub_categories = algorithms.create_enum_property_items(sorted_list, tip_length=100) + return self.expressions_sub_categories -#--------------Loading data -def get_all_expression_files(data_path, data_type_path, body_type): - #Get all files in morphs directory, with standard ones. - #Used when the engine loads expressions librairies. - dir = os.path.join(data_path, data_type_path) - found_files = [] - body_type_split = body_type.split('_')[:2] - list = os.listdir(dir) - for item in list: - if item == body_type: - found_files += [os.path.join(dir, item)] - for item in list: - if item.split('_')[:2] == body_type_split and item != body_type: - found_files += [os.path.join(dir, item)] - return found_files - -"""def add_operator(name, operator): - operator_for_base_expr[name] = operator - -def reset_operator(): - global operator_for_base_expr - operator_for_base_expr.clear() -""" \ No newline at end of file + def is_comb_expression_exists(self, root_model, name): + if len(root_model) < 1 or len(name) < 1: + return False + try: + path = os.path.join(file_ops.get_data_path(), "expressions_comb", root_model+"_expressions") + for database_file in os.listdir(path): + the_item, extension = os.path.splitext(database_file) + if the_item == name: + return True + except: + return False + return False + + #--------------EnumProperty for expressions in UI + #--------------AFTER finalization of the character + + def set_expressions_items(self, sorted_names): + if len(self.editor_expressions_items) > 0: + return + key = 0 + for s_n in sorted_names: + self.editor_expressions_items.append((str(key).zfill(3), s_n, "" )) + key += 1 + + def get_expressions_items(self): + return self.editor_expressions_items + + def get_expressions_item(self, key): + return algorithms.get_enum_property_item(key, self.editor_expressions_items) + + #--------------Loading data + def get_all_expression_files(self, data_path, data_type_path, body_type): + #Get all files in morphs directory, with standard ones. + #Used when the engine loads expressions librairies. + dir = os.path.join(data_path, data_type_path) + found_files = [] + body_type_split = body_type.split('_')[:2] + list = os.listdir(dir) + for item in list: + if item == body_type: + found_files += [os.path.join(dir, item)] + for item in list: + if item.split('_')[:2] == body_type_split and item != body_type: + found_files += [os.path.join(dir, item)] + return found_files + + #--------------Saving all changed base expression in a filedef + def save_face_expression(self, filepath): + # Save all expression morphs as a new face expression + # in its dedicated file. + # If file already exists, it's replaced. + logger.info("Exporting character to {0}".format(file_ops.simple_path(filepath))) + obj = self.humanoid.get_object() + char_data = {"manuellab_vers": self.lab_vers, "structural": dict(), "metaproperties": dict(), "materialproperties": dict()} + + if obj: + for prop in self.humanoid.character_data.keys(): + if self.humanoid.character_data[prop] != 0.5 and prop.startswith("Expressions_"): + char_data["structural"][prop] = round(self.humanoid.character_data[prop], 4) + + output_file = open(filepath, 'w') + json.dump(char_data, output_file) + output_file.close() + diff --git a/humanoid.py b/humanoid.py index bf78fc03..d6a9575f 100644 --- a/humanoid.py +++ b/humanoid.py @@ -45,6 +45,9 @@ class HumanModifier: def __init__(self, name, obj_name): self.name = name + #Teto + self.short_name = name.split("_")[1] #can change after when added to a category + #End Teto self.obj_name = obj_name self.properties = [] @@ -56,7 +59,7 @@ def get_object(self): if self.obj_name in bpy.data.objects: return bpy.data.objects[self.obj_name] return None - + def add(self, prop): self.properties.append(prop) @@ -65,7 +68,7 @@ def __contains__(self, prop): if propx == prop: return True return False - + def get_properties(self): """ Return the properties contained in the @@ -131,6 +134,50 @@ def get_modifier(self, name): return modifier return None + #Teto + def get_modifier_short_name(self, name): + modif = self.get_modifier(name) + if modif == None: + return "" + return modif.short_name + + def get_modifier_tiny_name(self, sub_categories=[], exclude_in_others=[]): + # Return the short name minus the beginning + # of its name corresponding to sub_category + # The key is subcategory name. + # The value is [tiny, short, full] + # Method used only for expressions editor for now + # exclude_in_others means that the modifiers' name that are in this + # list can't be put in "other" category + if len(sub_categories) > 0: + tiny = {'other': []} + triple = [] + done = False + false_others = False + sub_categories = sorted(sub_categories, reverse = True) + for modif in self.modifiers: + for sub in sub_categories: + if not sub in tiny: + tiny[sub] = [] + if modif.short_name.startswith(sub): + triple = [modif.short_name.lstrip(sub), modif.short_name, modif.name] + tiny[sub].append(triple) + done = True + break + if done: + done = False + else: + for fo in exclude_in_others: + if modif.short_name.startswith(fo) or modif.short_name.startswith("ID"): + false_others = True + if false_others: + false_others = False + else: + tiny['other'].append([modif.short_name, modif.short_name, modif.name]) + return tiny + return {} + #End Teto + def get_all_properties(self): """ Return all properties involved in the category, @@ -154,7 +201,7 @@ def __lt__(self, other): return self.name < other.name def __repr__(self): - return "Category {0} with {1} modfiers".format( + return "Category {0} with {1} modifiers".format( self.name, len(self.modifiers)) @@ -169,6 +216,9 @@ def __init__(self, lab_version): self.lab_vers = list(lab_version) self.has_data = False self.obj_name = "" + #Teto + self.root_model_name = "" + #End Teto self.data_path = file_ops.get_data_path() self.characters_config = file_ops.get_configuration() self.lib_filepath = file_ops.get_blendlibrary_path() @@ -205,7 +255,10 @@ def init_database(self, obj, character_identifier, rigging_type): logger.info("Found the humanoid: {0}".format(character_identifier)) logger.info("Init the database...") - + + #Teto + self.root_model_name = "" + #End Teto self.no_categories = "BasisAsymTest" self.categories = {} self.bodydata_realtime_activated = True @@ -298,10 +351,34 @@ def get_armature(self): def load_transformation_database(self): self.transformations_data = file_ops.load_json_data(self.transformations_data_path, "Transformations database") - - def get_categories(self): - categories = self.categories.values() + + #Teto + + def get_categories(self, exlude_names=[]): + if exlude_names == []: + categories = self.categories.values() + return sorted(categories) + categories = [] + for key, value in self.categories.items(): + if key not in exlude_names: + categories.append(value) return sorted(categories) + + def get_root_model_name(self): + if len(self.root_model_name) > 0: + return self.root_model_name + if len(self.obj_name) < 1: + return "" + for name in self.get_category("Expressions").get_all_properties(): + if name.startswith("Expressions_"): + rmn = name.split("_")[1] + rmn = rmn[2:] + self.root_model_name = rmn.lower() + if self.root_model_name == "humans": # Dirty trick... + self.root_model_name = "human" + return self.root_model_name + + #End Teto def get_category(self, name): if name in self.categories: @@ -316,21 +393,24 @@ def init_character_data(self, morph_name): """ components = morph_name.split("_") if components[0][:4] not in self.no_categories: - if len(components) == 3: + if len(components) == 3: #Is that really necessary ? category_name = components[0] + # Expressions and regular morphs are mixed + # Both are considered as morphs (that is true) if category_name not in self.categories: + # if category doesn't exist yet category = HumanCategory(category_name) self.categories[category_name] = category else: category = self.categories[category_name] - + # The modifier is used to store properties modifier_name = components[0]+"_"+components[1] modifier = category.get_modifier(modifier_name) - if not modifier: + if not modifier: # Create a new property to the modifier modifier = HumanModifier(modifier_name, self.obj_name) category.add(modifier) - for element in components[1].split("-"): + # Now add property prop = components[0]+"_" + element if prop not in modifier: modifier.add(prop) diff --git a/morphcreator.py b/morphcreator.py index 3667d015..e48feaa2 100644 --- a/morphcreator.py +++ b/morphcreator.py @@ -65,12 +65,7 @@ spectrum = [("GE", "Gender", "For all males / females"), ("ET", "Ethnic group", "For a specific ethnic group")] -min_max = [("MI", "min", "min = 0"), - ("MA", "max", "max = 1"), - ("II", "min-min", "seen in f_an03\nbut what is this ?"), - ("IA", "min-max", "seen in f_an03\nbut what is this ?"), - ("AI", "max-min", "seen in f_an03\nbut what is this ?"), - ("AA", "max-max", "seen in f_an03\nbut what is this ?")] +min_max = [("MI", "min", "min = 0"), ("MA", "max", "max = 1")] morphs_names = ["", "", 0] #0 = get_model_and_gender() diff --git a/morphengine.py b/morphengine.py index 3036fdab..2894f1c5 100644 --- a/morphengine.py +++ b/morphengine.py @@ -29,7 +29,12 @@ import mathutils #Teto -from . import algorithms, utils, proxyengine, file_ops, morphcreator, expressionscreator +from . import algorithms +from . import expressionscreator +from . import file_ops +from . import morphcreator +from . import proxyengine +from . import utils #End Teto import time, json import operator @@ -91,14 +96,15 @@ def __init__(self, obj_name, character_config): #Teto self.user_shared_morph_data_path = morphcreator.get_all_morph_files(data_path, "morphs", self.shared_morphs_filename) self.user_morph_data_path = morphcreator.get_all_morph_files(data_path, "morphs", self.morphs_filename) - #here they are list[] + #here, they are list[] #End Teto self.bounding_box_path = os.path.join( data_path, "bboxes", self.shared_bbox_filename) #Teto - self.expressions_path = expressionscreator.get_all_expression_files(data_path, "expressions_morphs", self.expressions_filename) + self.expressionscreator = expressionscreator.ExpressionsCreator() + self.expressions_path = self.expressionscreator.get_all_expression_files(data_path, "expressions_morphs", self.expressions_filename) #here it's a list[] #End Teto self.vertices_path = os.path.join( @@ -411,3 +417,4 @@ def calculate_morph(self, morph_name, val, add_vertices_to_update=True): self.morph_values[morph_name] = val else: logger.debug("Morph data {0} not found".format(morph_name)) + \ No newline at end of file