So I’ve been rigging a character lately and I really needed a way of copying individually selected vertex positions from one shape key to another. What actually happenned was I had to fix a topology error and bmesh screwed up the positions of the edited virtices in all the shape keys. I started to manually edit the position of each vertice to put it back to the basis position and this took forever so I wrote this script to fix each one.
What it does:
it adds an operator to the mesh toolshelf that takes the selected vertices and moves them to the position of the basis for the shape key. I made it an operator because often I sculpt or edit a shape key with mirror on unintentionally and this allows me to select the parts that I didn’t intend to move and put them back to basis position.
RECENTLY ADDED: Select only verts modified by the shape key. This button is extremely helpful for identifying and fixing shape keys with unwanted modifications to the mesh in them.
I thought I would turn this into an official add on (with formatting and such) so that myself or others could add functionality as needed for helping to create shape keys.
the code is kind of slow, but hey it works.
Here’s the code:
# ##### 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 #####
# <pep8 compliant>
bl_info = {
"name": "Shape Key Tools",
"author": "Nick Keeline(nrk)",
"version": (0, 0),
"blender": (2, 6, 3),
"location": "View3D > Tool Shelf > Shape Key Tools",
"description": "Lets you modify Shape Keys (e.g. copy paste vert positions etc)",
"wiki_url": "None yet"
"NA",
"tracker_url": "NA"
"",
"category": "Mesh"}
import bpy
import bmesh
#from bpy.props import BoolProperty, EnumProperty
from bpy.types import Operator, Panel
def getCurrentSkeyandBasisKeyIndexes(obj, mesh):
#Find the indices for current key and it's basis
SKeyIndex = obj.active_shape_key_index
skey = mesh.shape_keys
skeys = skey.key_blocks.keys()
#Thiskey = skeys[SKeyIndex]
rkey = skey.reference_key
i=0
for s in skeys:
if s.title() == rkey.name:
BasisIndex = i
i+=1
#print(BasisIndex)
#print(SKeyIndex)
return BasisIndex, SKeyIndex
def getBMeshForSkeyIndex(mesh, SKeyIndex):
bm = bmesh.new()
bm.from_mesh(mesh, use_shape_key=True, shape_key_index=SKeyIndex)
bmv = bm.verts
bmv.index_update()
return bm, bmv
class VIEW3D_PT_tools_shapetools(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_label = "Shape Key Tools"
bl_context = "mesh_edit"
def draw(self, context):
active_obj = context.active_object
layout = self.layout
col = layout.column(align=True)
col.operator("shapetools.to_basis_pos", text="Move Verts To Basis Pos")
col.operator("shapetools.select_dir_basis_pos", text="Select Verts diff from basis pos")
class SelectDifferenceFromBasisPos(Operator):
"""Various Tools for working with Shape Keys"""
bl_idname = "shapetools.select_dir_basis_pos"
bl_label = "Select Verts different from basis pos"
bl_register = True
bl_undo = True
@classmethod
def poll(cls, context):
if not context.active_object:
return False
else:
return (context.active_object.type == 'MESH')
def execute(self, context):
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.editmode_toggle()
bpy.ops.object.editmode_toggle()
#print('Another Run')
myobj = bpy.context.active_object
mymesh = myobj.data
(BIndex, SIndex) = getCurrentSkeyandBasisKeyIndexes(myobj,mymesh)
verts = []
(bmBasis, bmvBasis) = getBMeshForSkeyIndex(mymesh, BIndex)
(bcShape2Edit, bcvShape2Edit) = getBMeshForSkeyIndex(mymesh, SIndex)
i = 0
for v in bmvBasis:
verts.append(v.co)
#print(v.co)
for v in bcvShape2Edit:
if v.co != verts[i]:
#print(v.co)
v.select = True
i += 1
bpy.ops.object.editmode_toggle()
bcShape2Edit.to_mesh(mymesh)
bpy.ops.object.editmode_toggle()
bcShape2Edit.free()
bmBasis.free()
return {'FINISHED'}
class MoveToBasisPos(Operator):
"""Various Tools for working with Shape Keys"""
bl_idname = "shapetools.to_basis_pos"
bl_label = "Move Verts To Basis Pos"
bl_register = True
bl_undo = True
@classmethod
def poll(cls, context):
if not context.active_object:
return False
else:
return (context.active_object.type == 'MESH')
def execute(self, context):
bpy.ops.object.editmode_toggle()
bpy.ops.object.editmode_toggle()
#print('Another Run')
myobj = bpy.context.active_object
mymesh = myobj.data
(BIndex, SIndex) = getCurrentSkeyandBasisKeyIndexes(myobj,mymesh)
verts = []
(bmBasis, bmvBasis) = getBMeshForSkeyIndex(mymesh, BIndex)
(bcShape2Edit, bcvShape2Edit) = getBMeshForSkeyIndex(mymesh, SIndex)
i = 0
for v in bmvBasis:
if v.select == True:
#print(v.co)
verts.append(v.co)
for v in bcvShape2Edit:
if v.select == True:
#print(v.co)
v.co = verts[i]
i += 1
bpy.ops.object.editmode_toggle()
bcShape2Edit.to_mesh(mymesh)
bpy.ops.object.editmode_toggle()
bcShape2Edit.free()
bmBasis.free()
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()