From b2e05000674c8c7ea56414b2812fadcfb78dd446 Mon Sep 17 00:00:00 2001 From: TiberiumFusion <6332277+TiberiumFusion@users.noreply.github.com> Date: Wed, 7 Dec 2022 18:34:17 -0500 Subject: [PATCH] Additional controls over shape keys when exporting DMX: - Treat shape keys with _ underscores in their names as normal flexes (not as corrective shapes) - Automatically detect and set "stereo" to 1 for stereo flexes - Automatically rename MyShapeKeyL+MyShapeKeyR shape keys to MyShapeKey in exported data without changing their real names in the blend file Minor revisions to the layout of the shape keys section of the BST scene export panel Bump version --- io_scene_valvesource/GUI.py | 44 +++++++++++++++++++++------- io_scene_valvesource/__init__.py | 8 ++++- io_scene_valvesource/export_smd.py | 12 ++++++-- io_scene_valvesource/flex.py | 13 ++++---- io_scene_valvesource/translations.py | 18 ++++++++++++ io_scene_valvesource/utils.py | 30 +++++++++++++++++-- 6 files changed, 103 insertions(+), 22 deletions(-) diff --git a/io_scene_valvesource/GUI.py b/io_scene_valvesource/GUI.py index dbe0401..226e7e0 100644 --- a/io_scene_valvesource/GUI.py +++ b/io_scene_valvesource/GUI.py @@ -147,7 +147,7 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn row = layout.row(align=True) row.alignment='RIGHT' - num_shapes, num_correctives = countShapes(id) + num_shapes, num_correctives = countShapes(False, id) num_shapes += num_correctives if num_shapes > 0: row.label(str(num_shapes),icon='SHAPEKEY_DATA') @@ -426,23 +426,44 @@ def _makebox(): col.row().prop(item.vs,"flex_controller_mode",expand=True) + col.separator() + + col.prop(item.vs, "shapekey_simplify_lplusr_names_on_export") + if item.vs.flex_controller_mode == 'ADVANCED': - controller_source = col.row() - controller_source.alert = hasFlexControllerSource(item.vs.flex_controller_source) == False - controller_source.prop(item.vs,"flex_controller_source",text=get_id("exportables_flex_src"),icon = 'TEXT' if item.vs.flex_controller_source in bpy.data.texts else 'NONE') + col.separator() + + groupControllerDefinition = col.box().column() + + groupControllerFilePicker = groupControllerDefinition.row() + groupControllerFilePicker.alert = hasFlexControllerSource(item.vs.flex_controller_source) == False + groupControllerFilePicker.prop(item.vs,"flex_controller_source",text=get_id("exportables_flex_src"),icon = 'TEXT' if item.vs.flex_controller_source in bpy.data.texts else 'NONE') + + groupGenerateDef = groupControllerDefinition.column() + + groupGenerateDef.separator() - row = col.row(align=True) - row.operator(DmxWriteFlexControllers.bl_idname,icon='TEXT',text=get_id("exportables_flex_generate", True)) - row.operator("wm.url_open",text=get_id("exportables_flex_help", True),icon='HELP').url = "http://developer.valvesoftware.com/wiki/Blender_SMD_Tools_Help#Flex_properties" + groupGenerateTextButtons = groupGenerateDef.row(align=True) + groupGenerateTextButtons.operator(DmxWriteFlexControllers.bl_idname,icon='TEXT',text=get_id("exportables_flex_generate", True)) + groupGenerateTextButtons.operator("wm.url_open",text=get_id("exportables_flex_help", True),icon='HELP').url = "http://developer.valvesoftware.com/wiki/Blender_SMD_Tools_Help#Flex_properties" + + groupGenerateDef.prop(item.vs, "shapekey_underscore_denotes_corrective") + groupGenerateDef.prop(item.vs, "shapekey_lplusr_denotes_stereo") + + col.separator() col.operator(AddCorrectiveShapeDrivers.bl_idname, icon='DRIVER',text=get_id("gen_drivers",True)) datablocks_dispayed = [] + groupFlexBalance = col + for ob in [ob for ob in objects if ob.vs.export and ob.type in shape_types and ob.active_shape_key and ob.data not in datablocks_dispayed]: if not len(datablocks_dispayed): - col.label(text=get_id("exportables_flex_split")) - sharpness_col = col.column(align=True) + col.separator() + groupFlexBalance = col.box().column() + groupFlexBalance.label(text=get_id("exportables_flex_split")) + sharpness_col = groupFlexBalance.column(align=True) r = sharpness_col.split(0.33,align=True) r.label(text=ob.data.name + ":",icon=MakeObjectIcon(ob,suffix='_DATA'),translate=False) r2 = r.split(0.7,align=True) @@ -454,9 +475,10 @@ def _makebox(): r2.prop(ob.data.vs,"flex_stereo_mode",text="") datablocks_dispayed.append(ob.data) - num_shapes, num_correctives = countShapes(objects) - col.separator() + + num_shapes, num_correctives = countShapes(item.vs.shapekey_underscore_denotes_corrective, objects) + row = col.row() row.alignment = 'CENTER' row.label(icon='SHAPEKEY_DATA',text = get_id("exportables_flex_count", True).format(num_shapes)) diff --git a/io_scene_valvesource/__init__.py b/io_scene_valvesource/__init__.py index 9cdaf58..dc7c3a1 100644 --- a/io_scene_valvesource/__init__.py +++ b/io_scene_valvesource/__init__.py @@ -21,7 +21,7 @@ bl_info = { "name": "Blender Source Tools (BST-Fix+ for 2.79)", "author": "Tom Edwards (translators: Grigory Revzin), modified by TF", - "version": (2, 10, 2, 999, 6), + "version": (2, 10, 2, 999, 7), "blender": (2, 74, 0), "category": "Import-Export", "location": "File > Import/Export, Scene properties", @@ -205,6 +205,12 @@ class ExportableProps(): vertex_animations = CollectionProperty(name=get_id("vca_group_props"),type=ValveSource_VertexAnimation) active_vertex_animation = IntProperty(default=-1) + + shapekey_underscore_denotes_corrective = BoolProperty(name=get_id("shapekey_underscore_denotes_corrective"), description=get_id("shapekey_underscore_denotes_corrective_tip"),default=True) + + shapekey_lplusr_denotes_stereo = BoolProperty(name=get_id("shapekey_lplusr_denotes_stereo"), description=get_id("shapekey_lplusr_denotes_stereo_tip"), default=True) + + shapekey_simplify_lplusr_names_on_export = BoolProperty(name=get_id("shape_simplify_lplusr_name_on_export"), description=get_id("shape_simplify_lplusr_name_on_export_tip"), default=False) class ValveSource_ObjectProps(ExportableProps,PropertyGroup): action_filter = StringProperty(name=get_id("action_filter"),description=get_id("action_filter_tip")) diff --git a/io_scene_valvesource/export_smd.py b/io_scene_valvesource/export_smd.py index cedbd96..7da0d47 100644 --- a/io_scene_valvesource/export_smd.py +++ b/io_scene_valvesource/export_smd.py @@ -1434,7 +1434,15 @@ def writeBone(bone): if DmeCombinationOperator: root["combinationOperator"] = DmeCombinationOperator bench.report("Flex setup") - + + # Stage where we can easily rename flexes in the exported data + if (id.vs.shapekey_simplify_lplusr_names_on_export): + for dme in DmeCombinationOperator.datamodel._DataModel__elements: + if (dme._type == "DmeCombinationInputControl"): + (splitLName, splitRName, usesPlusConvention) = FindShapeKeyPairSplitNames(dme._name) + if (usesPlusConvention): + dme._name = splitLName[:-1] + for bake in [bake for bake in bake_results if bake.object.type != 'ARMATURE']: root["model"] = DmeModel @@ -1667,7 +1675,7 @@ def writeBone(bone): shape_names.append(shape_name) wrinkle_scale = 0 - corrective = "_" in shape_name + corrective = (id.vs.shapekey_underscore_denotes_corrective and "_" in shape_name) if corrective: num_correctives += 1 else: diff --git a/io_scene_valvesource/flex.py b/io_scene_valvesource/flex.py index 19ae439..e401988 100644 --- a/io_scene_valvesource/flex.py +++ b/io_scene_valvesource/flex.py @@ -49,23 +49,24 @@ def make_controllers(self,id): DmeCombinationOperator = dm.add_element("combinationOperator","DmeCombinationOperator",id=id.name+"controllers") root["combinationOperator"] = DmeCombinationOperator controls = DmeCombinationOperator["controls"] = datamodel.make_array([],datamodel.Element) - - def createController(namespace,name,deltas): + + def createController(namespace, name, deltas, isStereo=False): DmeCombinationInputControl = dm.add_element(name,"DmeCombinationInputControl",id=namespace + name + "inputcontrol") controls.append(DmeCombinationInputControl) DmeCombinationInputControl["rawControlNames"] = datamodel.make_array(deltas,str) - DmeCombinationInputControl["stereo"] = False + DmeCombinationInputControl["stereo"] = isStereo DmeCombinationInputControl["eyelid"] = False DmeCombinationInputControl["flexMax"] = 1.0 DmeCombinationInputControl["flexMin"] = 0.0 DmeCombinationInputControl["wrinkleScales"] = datamodel.make_array([0.0] * len(deltas),float) - + for ob in [ob for ob in objects if ob.data.shape_keys]: - for shape in [shape for shape in ob.data.shape_keys.key_blocks[1:] if not "_" in shape.name and shape.name not in shapes]: - createController(ob.name, shape.name, [shape.name]) + for shape in [shape for shape in ob.data.shape_keys.key_blocks[1:] if ((not id.vs.shapekey_underscore_denotes_corrective) or (not "_" in shape.name)) and shape.name not in shapes]: + (splitLName, splitRName, usesPlusConvention) = FindShapeKeyPairSplitNames(shape.name) + createController(ob.name, shape.name, [shape.name], usesPlusConvention) shapes.add(shape.name) for vca in id.vs.vertex_animations: diff --git a/io_scene_valvesource/translations.py b/io_scene_valvesource/translations.py index 6fecc38..8338c49 100644 --- a/io_scene_valvesource/translations.py +++ b/io_scene_valvesource/translations.py @@ -140,6 +140,24 @@ 'en': "DMX stereo split sharpness", 'ru': "Резкость разделения в стерео-контроллерах", }, +'shapekey_underscore_denotes_corrective': { + 'en': "Underscores denote corrective shapes", +}, +'shapekey_underscore_denotes_corrective_tip': { + 'en': "When generating the DMX flex controller text document, shape keys with 1 or more _ underscores in their name will be treated as corrective shapes instead of as normal flexes", +}, +'shapekey_lplusr_denotes_stereo': { + 'en': "MyShapeKeyL+MyShapeKeyR naming convention denotes stereo flexes", +}, +'shapekey_lplusr_denotes_stereo_tip': { + 'en': "When generating the DMX flex controller text document, shape keys with the MyShapeKeyL+MyShapeKeyR naming convention will automatically have \"stereo\" set to true", +}, +'shape_simplify_lplusr_name_on_export': { + 'en': "Simplify MyShapeKeyL+MyShapeKeyR names on export", +}, +'shape_simplify_lplusr_name_on_export_tip': { + 'en': "When exporting DMX meshes, simplify the names of shape keys with the MyShapeKeyL+MyShapeKeyR naming convention to MyShapeKey", +}, 'group_suppress': { 'ja': "ミュート", 'en': "Suppress", diff --git a/io_scene_valvesource/utils.py b/io_scene_valvesource/utils.py index d851894..db9d644 100644 --- a/io_scene_valvesource/utils.py +++ b/io_scene_valvesource/utils.py @@ -344,7 +344,7 @@ def _test(id_): else: return _test(id) -def countShapes(*objects): +def countShapes(underscoreDenotesCorrective, *objects): num_shapes = 0 num_correctives = 0 flattened_objects = [] @@ -357,7 +357,7 @@ def countShapes(*objects): flattened_objects.append(ob) for ob in [ob for ob in flattened_objects if ob.vs.export and hasShapes(ob)]: for shape in ob.data.shape_keys.key_blocks[1:]: - if "_" in shape.name: num_correctives += 1 + if ("_" in shape.name and underscoreDenotesCorrective): num_correctives += 1 else: num_shapes += 1 return num_shapes, num_correctives @@ -695,3 +695,29 @@ def execute(self,context): if target_state == None: target_state = not ob.vs.export ob.vs.export = target_state return {'FINISHED'} + +# Given the name of a shape key, determines if it uses the MyShapeKeyL+MyShapeKeyR naming convention and what the new names would be if this shape key was to be split into L and R halves +def FindShapeKeyPairSplitNames(originalShapeKeyName): + newLeftName = None + newRightName = None + usesPairNameConvention = False + if ('+' in originalShapeKeyName): + nameCuts = originalShapeKeyName.split("+") + if (nameCuts[0].lower()[-1] == "l" and nameCuts[1].lower()[-1] == "r"): + newLeftName = nameCuts[0] + newRightName = nameCuts[1] + usesPairNameConvention = True + elif (nameCuts[1].lower()[-1] == "l" and nameCuts[0].lower()[-1] == "r"): + newLeftName = nameCuts[1] + newRightName = nameCuts[0] + usesPairNameConvention = True + else: # shape key name has a + in it, but the string halves on either side of that + do not end in L and R + newLeftName = originalShapeKeyName + "L" + newRightName = originalShapeKeyName + "R" + usesPairNameConvention = False + else: + newLeftName = originalShapeKeyName + "L" + newRightName = originalShapeKeyName + "R" + usesPairNameConvention = False + + return (newLeftName, newRightName, usesPairNameConvention)