Skip to content

Commit

Permalink
Additional controls over shape keys when exporting DMX:
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
TiberiumFusion committed Dec 7, 2022
1 parent cdfc49e commit b2e0500
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 22 deletions.
44 changes: 33 additions & 11 deletions io_scene_valvesource/GUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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)
Expand All @@ -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))
Expand Down
8 changes: 7 additions & 1 deletion io_scene_valvesource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"))
Expand Down
12 changes: 10 additions & 2 deletions io_scene_valvesource/export_smd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down
13 changes: 7 additions & 6 deletions io_scene_valvesource/flex.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
18 changes: 18 additions & 0 deletions io_scene_valvesource/translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
30 changes: 28 additions & 2 deletions io_scene_valvesource/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand All @@ -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

Expand Down Expand Up @@ -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)

0 comments on commit b2e0500

Please sign in to comment.