# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # import bpy from bpy.types import Operator from bpy.props import ( BoolProperty, FloatProperty, ) import math # ---------------------CREATE SHAPES---------------- def DefSplitShapes(self, ACTIVESHAPE, LAYOUTCOMPAT): bpy.ops.object.mode_set(mode='OBJECT', toggle=False) ACTOBJ = bpy.context.active_object has_keys = hasattr(getattr(ACTOBJ.data, "shape_keys", None), "key_blocks") if has_keys: INDEX = ACTOBJ.active_shape_key_index if LAYOUTCOMPAT: for SHAPE in ACTOBJ.data.shape_keys.key_blocks: if len(SHAPE.name) > 7: SHAPE.name = SHAPE.name[:8] if ACTIVESHAPE: ACTOBJ.active_shape_key_index = INDEX AS = ACTOBJ.active_shape_key AS.value = 1 SHAPE = ACTOBJ.shape_key_add(name=AS.name[:8] + "_L", from_mix=True) SHAPE.vertex_group = "_L" SHAPE2 = ACTOBJ.shape_key_add(name=AS.name[:8] + "_R", from_mix=True) SHAPE2.vertex_group = "_R" bpy.ops.object.shape_key_clear() else: for SHAPE in ACTOBJ.data.shape_keys.key_blocks[1:]: SHAPE.value = 1 SHAPE1 = ACTOBJ.shape_key_add( name=SHAPE.name[:8] + "_L", from_mix=True) SHAPE1.vertex_group = "_L" SHAPE2 = ACTOBJ.shape_key_add( name=SHAPE.name[:8] + "_R", from_mix=True) SHAPE2.vertex_group = "_R" bpy.ops.object.shape_key_clear() ACTOBJ.active_shape_key_index = INDEX return has_keys class CreaShapes(Operator): """Divide on left and right the diffenrent Shapekeys. “Create Mix Groups” its required""" bl_idname = "mesh.split_lr_shapes_osc" bl_label = "Split LR Shapes" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): return (context.active_object is not None and context.active_object.type in {'MESH', 'SURFACE', 'CURVE'}) activeshape = BoolProperty( name="Only Active Shape", default=False ) layoutcompat = BoolProperty( name="Layout Compatible", default=True ) def execute(self, context): is_done = DefSplitShapes(self, self.activeshape, self.layoutcompat) if not is_done: self.report({'INFO'}, message="Active object doesn't have shape keys") return {'CANCELLED'} return {'FINISHED'} # ----------------------------SHAPES LAYOUT----------------------- class CreaShapesLayout(Operator): """Creates an interface to control the Shapekeys of symmetrical Objects. “Create Mix Groups” its required""" bl_idname = "mesh.create_symmetrical_layout_osc" bl_label = "Symmetrical Layout" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): return (context.active_object is not None and context.active_object.type in {'MESH', 'SURFACE', 'CURVE'}) def execute(self, context): SEL_OBJ = bpy.context.active_object has_keys = hasattr(getattr(SEL_OBJ.data, "shape_keys", None), "key_blocks") if has_keys: LISTA_KEYS = bpy.context.active_object.data.shape_keys.key_blocks[1:] EDITMODE = "bpy.ops.object.mode_set(mode='EDIT')" OBJECTMODE = "bpy.ops.object.mode_set(mode='OBJECT')" POSEMODE = "bpy.ops.object.mode_set(mode='POSE')" amt = bpy.data.armatures.new("ArmatureData") ob = bpy.data.objects.new("RIG_LAYOUT_" + SEL_OBJ.name, amt) scn = bpy.context.scene scn.objects.link(ob) scn.objects.active = ob ob.select = True verticess = [(-1, 1, 0), (1, 1, 0), (1, -1, 0), (-1, -1, 0)] edgess = [(0, 1), (1, 2), (2, 3), (3, 0)] mesh = bpy.data.meshes.new("%s_data_container" % (SEL_OBJ)) object = bpy.data.objects.new("GRAPHIC_CONTAINER", mesh) bpy.context.scene.objects.link(object) mesh.from_pydata(verticess, edgess, []) gx = 0 gy = 0 for keyblock in LISTA_KEYS: if keyblock.name[-2:] != "_L": if keyblock.name[-2:] != "_R": scn.objects.active = ob eval(EDITMODE) bone = amt.edit_bones.new(keyblock.name) bone.head = (gx, 0, 0) bone.tail = (gx, 0, 1) bonectrl = amt.edit_bones.new(keyblock.name + "_CTRL") bonectrl.head = (gy, 0, 0) bonectrl.tail = (gy, 0, 0.2) ob.data.edit_bones[ bonectrl.name].parent = ob.data.edit_bones[ bone.name] bpy.context.scene.objects.active = ob for SIDE in ["L", "R"]: DR = SEL_OBJ.data.shape_keys.key_blocks[ keyblock.name + "_" + SIDE].driver_add("value") if SIDE == "L": DR.driver.expression = "var+var_001" else: DR.driver.expression = "-var+var_001" VAR1 = DR.driver.variables.new() VAR2 = DR.driver.variables.new() VAR1.targets[0].id = ob VAR1.type = 'TRANSFORMS' VAR1.targets[0].bone_target = bonectrl.name VAR1.targets[0].transform_space = "LOCAL_SPACE" VAR1.targets[0].transform_type = "LOC_X" VAR2.targets[0].id = ob VAR2.type = 'TRANSFORMS' VAR2.targets[0].bone_target = bonectrl.name VAR2.targets[0].transform_space = "LOCAL_SPACE" VAR2.targets[0].transform_type = "LOC_Y" eval(POSEMODE) ob.pose.bones[keyblock.name].custom_shape = object ob.pose.bones[ keyblock.name + "_CTRL"].custom_shape = object CNS = ob.pose.bones[ keyblock.name + "_CTRL"].constraints.new( type='LIMIT_LOCATION') CNS.min_x = -1 CNS.use_min_x = 1 CNS.min_z = 0 CNS.use_min_z = 1 CNS.min_y = -1 CNS.use_min_y = 1 CNS.max_x = 1 CNS.use_max_x = 1 CNS.max_z = 0 CNS.use_max_z = 1 CNS.max_y = 1 CNS.use_max_y = 1 CNS.owner_space = "LOCAL" CNS.use_transform_limit = True eval(OBJECTMODE) bpy.ops.object.text_add(location=(gx, 0, 0)) gx = gx + 2.2 gy = gy + 2.2 texto = bpy.context.object texto.data.body = keyblock.name texto.name = "TEXTO_" + keyblock.name texto.rotation_euler[0] = math.pi / 2 texto.location.x = -1 texto.location.z = -1 texto.data.size = .2 CNS = texto.constraints.new(type="COPY_LOCATION") CNS.target = ob CNS.subtarget = ob.pose.bones[keyblock.name].name CNS.use_offset = True else: self.report({'INFO'}, message="Active object doesn't have shape keys") return {'CANCELLED'} return {'FINISHED'} # ----------------------------CREATE LMR GROUPS------------------- def createLMRGroups(self, FACTORVG, ADDVG): bpy.context.window.screen.scene.tool_settings.mesh_select_mode = ( True, False, False) ACTOBJ = bpy.context.active_object bpy.ops.object.mode_set(mode="EDIT", toggle=False) bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.mode_set(mode="OBJECT") GRUPOS = ["_L", "_R"] MIRRORINDEX = 0 for LADO in GRUPOS: if MIRRORINDEX == 0: bpy.ops.object.vertex_group_add() bpy.ops.object.mode_set(mode='EDIT', toggle=False) bpy.ops.mesh.select_all(action='SELECT') bpy.ops.object.vertex_group_assign() bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.mode_set(mode='WEIGHT_PAINT', toggle=False) for VERTICE in ACTOBJ.data.vertices: VERTICE.groups[-1].weight = (VERTICE.co[0] * FACTORVG) + ADDVG ACTOBJ.vertex_groups[-1].name = LADO else: bpy.ops.object.vertex_group_add() bpy.ops.object.mode_set(mode='EDIT', toggle=False) bpy.ops.mesh.select_all(action='SELECT') bpy.ops.object.vertex_group_assign() bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.mode_set(mode='WEIGHT_PAINT', toggle=False) for VERTICE in ACTOBJ.data.vertices: VERTICE.groups[-1].weight = (-VERTICE.co[0] * FACTORVG) + ADDVG ACTOBJ.vertex_groups[-1].name = LADO MIRRORINDEX += 1 ACTOBJ.vertex_groups.active_index = len(ACTOBJ.vertex_groups) class CreaGrupos(Operator): """It creates a vertex group in symmetrical objects, ideal for smoothly mixing shapekeys""" bl_idname = "mesh.create_lmr_groups_osc" bl_label = "Create Mix groups" bl_options = {'REGISTER', 'UNDO'} FACTORVG = FloatProperty( name="Factor", default=1, min=0, max=1000 ) ADDVG = FloatProperty( name="Addition", default=.5, min=0, max=1000 ) @classmethod def poll(cls, context): return (context.active_object is not None and context.active_object.type == 'MESH') def execute(self, context): createLMRGroups(self, self.FACTORVG, self.ADDVG) return {'FINISHED'} # ------------------------ SHAPES LAYOUT SYMMETRICA ------------------------ class CreateLayoutAsymmetrical(Operator): """Creates an interface to control the Shapekeys of symmetrical Objects. “Create Mix Groups” its required""" bl_idname = "mesh.create_asymmetrical_layout_osc" bl_label = "Asymmetrical Layout" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): return (context.active_object is not None and context.active_object.type in {'MESH', 'SURFACE', 'CURVE'}) def execute(self, context): SEL_OBJ = bpy.context.active_object has_keys = hasattr(getattr(SEL_OBJ.data, "shape_keys", None), "key_blocks") if has_keys: LISTA_KEYS = bpy.context.active_object.data.shape_keys.key_blocks[1:] EDITMODE = "bpy.ops.object.mode_set(mode='EDIT')" OBJECTMODE = "bpy.ops.object.mode_set(mode='OBJECT')" POSEMODE = "bpy.ops.object.mode_set(mode='POSE')" amtas = bpy.data.armatures.new("ArmatureData") obas = bpy.data.objects.new("RIG_LAYOUT_" + SEL_OBJ.name, amtas) scn = bpy.context.scene scn.objects.link(obas) scn.objects.active = obas obas.select = True verticess = [(-.1, 1, 0), (.1, 1, 0), (.1, 0, 0), (-.1, 0, 0)] edgess = [(0, 1), (1, 2), (2, 3), (3, 0)] mesh = bpy.data.meshes.new("%s_data_container" % (SEL_OBJ)) object = bpy.data.objects.new("GRAPHIC_CONTAINER_AS", mesh) bpy.context.scene.objects.link(object) mesh.from_pydata(verticess, edgess, []) eval(EDITMODE) gx = 0 gy = 0 for keyblock in LISTA_KEYS: if keyblock.name[-2:] != "_L": if keyblock.name[-2:] != "_R": scn.objects.active = obas eval(EDITMODE) bone = amtas.edit_bones.new(keyblock.name) bone.head = (gx, 0, 0) bone.tail = (gx, 0, 1) bonectrl = amtas.edit_bones.new(keyblock.name + "_CTRL") bonectrl.head = (gy, 0, 0) bonectrl.tail = (gy, 0, 0.2) obas.data.edit_bones[ bonectrl.name].parent = obas.data.edit_bones[ bone.name] bpy.context.scene.objects.active = obas bpy.ops.armature.select_all(action="DESELECT") DR1 = keyblock.driver_add("value") DR1.driver.expression = "var" VAR2 = DR1.driver.variables.new() VAR2.targets[0].id = obas VAR2.targets[0].bone_target = bonectrl.name VAR2.type = 'TRANSFORMS' VAR2.targets[0].transform_space = "LOCAL_SPACE" VAR2.targets[0].transform_type = "LOC_Y" eval(POSEMODE) obas.pose.bones[keyblock.name].custom_shape = object obas.pose.bones[ keyblock.name + "_CTRL"].custom_shape = object bpy.data.objects[ "RIG_LAYOUT_" + SEL_OBJ.name].data.bones.active = bpy.data.objects[ "RIG_LAYOUT_" + SEL_OBJ.name].data.bones[ keyblock.name + "_CTRL"] eval(POSEMODE) CNS = obas.pose.bones[ keyblock.name + "_CTRL"].constraints.new( type='LIMIT_LOCATION') CNS.min_x = 0 CNS.use_min_x = 1 CNS.min_z = 0 CNS.use_min_z = 1 CNS.min_y = 0 CNS.use_min_y = 1 CNS.max_x = 0 CNS.use_max_x = 1 CNS.max_z = 0 CNS.use_max_z = 1 CNS.max_y = 1 CNS.use_max_y = 1 CNS.owner_space = "LOCAL" CNS.use_transform_limit = True eval(OBJECTMODE) bpy.ops.object.text_add(location=(0, 0, 0)) gx = gx + 2.2 gy = gy + 2.2 texto = bpy.context.object texto.data.body = keyblock.name texto.name = "TEXTO_" + keyblock.name texto.rotation_euler[0] = math.pi / 2 texto.location.x = -.15 texto.location.z = -.15 texto.data.size = .2 CNS = texto.constraints.new(type="COPY_LOCATION") CNS.target = obas CNS.subtarget = obas.pose.bones[keyblock.name].name CNS.use_offset = True else: self.report({'INFO'}, message="Active object doesn't have shape keys") return {'CANCELLED'} return {'FINISHED'} # ---------------------------SHAPES TO OBJECTS------------------ class ShapeToObjects(Operator): """It creates a new object for every shapekey in the selected object, ideal to export to other 3D software Apps""" bl_idname = "object.shape_key_to_objects_osc" bl_label = "Shapes To Objects" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): return (context.active_object is not None and context.active_object.type in {'MESH', 'SURFACE', 'CURVE'}) def execute(self, context): OBJACT = bpy.context.active_object has_keys = hasattr(getattr(OBJACT.data, "shape_keys", None), "key_blocks") if has_keys: for SHAPE in OBJACT.data.shape_keys.key_blocks[:]: print(SHAPE.name) bpy.ops.object.shape_key_clear() SHAPE.value = 1 mesh = OBJACT.to_mesh(bpy.context.scene, True, 'PREVIEW') object = bpy.data.objects.new(SHAPE.name, mesh) bpy.context.scene.objects.link(object) else: self.report({'INFO'}, message="Active object doesn't have shape keys") return {'CANCELLED'} return {'FINISHED'}