From ca97ede0cd800eb60c04f9b9e73e4be5c5dc77b7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jan 2007 05:28:37 +0000 Subject: fixes spelling of filename. Window.c was calling undo_push_mesh, even for non mesh objects, exiting editmode would crash for any non mesh object (found with the armature symmetry script), bad juju. --- release/scripts/armature_symetry.py | 5 +- release/scripts/armature_symmetry.py | 325 +++++++++++++++++++++++++++++++++++ 2 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 release/scripts/armature_symmetry.py (limited to 'release') diff --git a/release/scripts/armature_symetry.py b/release/scripts/armature_symetry.py index 4caf028631a..1e4484d0efc 100644 --- a/release/scripts/armature_symetry.py +++ b/release/scripts/armature_symetry.py @@ -195,10 +195,13 @@ def armature_symetry(\ if abs(edit_bone.head.x) + abs(edit_bone.tail.x) <= PREF_XZERO_THRESH/2: # This is a center bone, clamp and remove from the bone list so we dont use again. + if edit_bone.tail.x or edit_bone.head.x: + tot_editbones_modified += 1 + edit_bone.tail.x= edit_bone.head.x= 0 del bones[eb_idx] - tot_editbones_modified+=1 + bone_comparisons= [] diff --git a/release/scripts/armature_symmetry.py b/release/scripts/armature_symmetry.py new file mode 100644 index 00000000000..1e4484d0efc --- /dev/null +++ b/release/scripts/armature_symmetry.py @@ -0,0 +1,325 @@ +#!BPY + +""" +Name: 'Armature Symmetry' +Blender: 242 +Group: 'Animation' +Tooltip: 'Make an Armature symetrical' +""" + +__author__ = "Campbell Barton" +__url__ = ("blender", "blenderartist") +__version__ = "1.0 2006-7-26" + +__doc__ = """\ +This script creates perfectly symmetrical armatures, +based on the best fit when comparing the mirrored locations of 2 bones. +Hidden bones are ignored, and optionally only operate on selected bones. +""" + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Campbell J Barton 2006 +# +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + +import Blender +from Blender import Scene +Vector= Blender.Mathutils.Vector + + +def VecXFlip(vec): + ''' + Return a copy of this vector x flipped. + ''' + x,y,z= vec + return Vector(-x,y,z) + +def editbone_mirror_diff(editbone1, editbone2): + ''' + X Mirror bone compare + return a float representing the difference between the 2 bones + the smaller the better the match + ''' + h1= editbone1.head + h2= editbone2.head + + t1= editbone1.tail + t2= editbone2.tail + + # Mirror bone2's location + h2= VecXFlip(h2) + t2= VecXFlip(t2) + + #return (h1-h2).length + (t1-t2).length # returns the length only + + # For this function its easier to return the bones also + return ((h1-h2).length + (t1-t2).length)/2, editbone1, editbone2 + +def editbone_mirror_merge(editbone1, editbone2, PREF_MODE_L2R, PREF_MODE_R2L): + ''' + Merge these 2 bones to their mirrored locations + ''' + h1= editbone1.head + h2= editbone2.head + + t1= editbone1.tail + t2= editbone2.tail + + if PREF_MODE_L2R and PREF_MODE_R2L: + # Median, flip bone2's locations and average, then apply to editbone1, flip and apply to editbone2 + h2_f= VecXFlip(h2) + t2_f= VecXFlip(t2) + + h_med= (h1+h2_f)*0.5 # middle between t1 and flipped t2 + t_med= (t1+t2_f)*0.5 # middle between h1 and flipped h2 + + # Apply the median to editbone1 + editbone1.head= h_med + editbone1.tail= t_med + + # Flip in place for editbone2 + h_med.x= -h_med.x + t_med.x= -t_med.x + + # Apply the median to editbone2 + editbone2.head= h_med + editbone2.tail= t_med + + # Average the roll, this might need some logical work, but looks good for now. + r1= editbone1.roll + r2= -editbone2.roll + # print 'rolls are', r1,r2 + r_med= (r1+r2)/2 + # print 'new roll is', r_med + editbone1.roll= r_med + editbone2.roll= -r_med # mirror roll + + else: # Copy from 1 side to another + + # Crafty function we can use so L>R and R>L can use the same code + def IS_XMIRROR_SOURCE(xval): + '''Source means is this the value we want to copy from''' + + if PREF_MODE_L2R: + if xval<0: return True + else: return False + else: # PREF_MODE_R2L + if xval<0: return False + else: return True + + if IS_XMIRROR_SOURCE( h1.x ):# head bone 1s negative, so copy it to h2 + editbone2.head= VecXFlip(h1) + else: + ''' + assume h2.x<0 - not a big deal if were wrong, + its unlikely to ever happen because the bones would both be on the same side. + ''' + + # head bone 2s negative, so copy it to h1 + editbone1.head= VecXFlip(h2) + + # Same as above for tail + if IS_XMIRROR_SOURCE(t1.x): + editbone2.tail= VecXFlip(t1) + else: + editbone1.tail= VecXFlip(t2) + + # Copy roll from 1 bone to another, use the head's location to decide which side it's on. + if IS_XMIRROR_SOURCE(editbone1.head): + editbone2.roll= -editbone1.roll + else: + editbone1.roll= -editbone2.roll + + +def armature_symetry(\ + arm_ob,\ + PREF_MAX_DIST,\ + PREF_XMID_SNAP,\ + PREF_XZERO_THRESH,\ + PREF_MODE_L2R,\ + PREF_MODE_R2L,\ + PREF_SEL_ONLY): + + ''' + Main function that does all the work, + return the number of + ''' + arm_data= arm_ob.data + arm_data.makeEditable() + + # Get the bones + bones= [] + HIDDEN_EDIT= Blender.Armature.HIDDEN_EDIT + BONE_SELECTED= Blender.Armature.BONE_SELECTED + + if PREF_SEL_ONLY: + for eb in arm_data.bones.values(): + options= eb.options + if HIDDEN_EDIT not in options and BONE_SELECTED in options: + bones.append(eb) + else: + # All non hidden bones + for eb in arm_data.bones.values(): + options= eb.options + if HIDDEN_EDIT not in options: + bones.append(eb) + + del HIDDEN_EDIT # remove temp variables + del BONE_SELECTED + + # Store the numder of bones we have modified for a message + tot_editbones= len(bones) + tot_editbones_modified= 0 + + if PREF_XMID_SNAP: + # Remove bones that are in the middle (X Zero) + # reverse loop so we can remove items in the list. + for eb_idx in xrange(len(bones)-1, -1, -1): + edit_bone= bones[eb_idx] + if abs(edit_bone.head.x) + abs(edit_bone.tail.x) <= PREF_XZERO_THRESH/2: + + # This is a center bone, clamp and remove from the bone list so we dont use again. + if edit_bone.tail.x or edit_bone.head.x: + tot_editbones_modified += 1 + + edit_bone.tail.x= edit_bone.head.x= 0 + del bones[eb_idx] + + + + + bone_comparisons= [] + + # Compare every bone with every other bone, shouldn't be too slow. + # These 2 "for" loops only compare once + for eb_idx_a in xrange(len(bones)-1, -1, -1): + edit_bone_a= bones[eb_idx_a] + for eb_idx_b in xrange(eb_idx_a-1, -1, -1): + edit_bone_b= bones[eb_idx_b] + # Error float the first value from editbone_mirror_diff() so we can sort the resulting list. + bone_comparisons.append(editbone_mirror_diff(edit_bone_a, edit_bone_b)) + + + bone_comparisons.sort() # best matches first + + # Make a dict() of bone names that have been used so we dont mirror more then once + bone_mirrored= {} + + for error, editbone1, editbone2 in bone_comparisons: + # print 'Trying to merge at error %.3f' % error + if error > PREF_MAX_DIST: + # print 'breaking, max error limit reached PREF_MAX_DIST: %.3f' % PREF_MAX_DIST + break + + if not bone_mirrored.has_key(editbone1.name) and not bone_mirrored.has_key(editbone2.name): + # Were not used, execute the mirror + editbone_mirror_merge(editbone1, editbone2, PREF_MODE_L2R, PREF_MODE_R2L) + # print 'Merging bones' + + # Add ourselves so we aren't touched again + bone_mirrored[editbone1.name] = None # dummy value, would use sets in python 2.4 + bone_mirrored[editbone2.name] = None + + # If both options are enabled, then we have changed 2 bones + tot_editbones_modified+= PREF_MODE_L2R + PREF_MODE_R2L + + arm_data.update() # get out of armature editmode + return tot_editbones, tot_editbones_modified + + +def main(): + ''' + User interface function that gets the options and calls armature_symetry() + ''' + + scn= Scene.GetCurrent() + arm_ob= scn.objects.active + + if not arm_ob or arm_ob.type!='Armature': + Blender.Draw.PupMenu('No Armature object selected.') + return + + # Cant be in editmode for armature.makeEditable() + is_editmode= Blender.Window.EditMode() + if is_editmode: Blender.Window.EditMode(0) + Draw= Blender.Draw + + # Defaults for the user input + PREF_XMID_SNAP= Draw.Create(1) + PREF_MAX_DIST= Draw.Create(0.4) + PREF_XZERO_THRESH= Draw.Create(0.02) + + PREF_MODE_L2R= Draw.Create(1) + PREF_MODE_R2L= Draw.Create(0) + PREF_SEL_ONLY= Draw.Create(1) + + pup_block = [\ + 'Left (-), Right (+)',\ + ('Left > Right', PREF_MODE_L2R, 'Copy from the Left to Right of the mesh. Enable Both for a mid loc.'),\ + ('Right > Left', PREF_MODE_R2L, 'Copy from the Right to Left of the mesh. Enable Both for a mid loc.'),\ + '',\ + ('MaxDist:', PREF_MAX_DIST, 0.0, 4.0, 'Maximum difference in mirror bones to match up pairs.'),\ + ('XZero limit:', PREF_XZERO_THRESH, 0.0, 2.0, 'Tolerance for locking bones into the middle (X/zero).'),\ + ('XMidSnap Bones', PREF_XMID_SNAP, 'Snap middle verts to X Zero (uses XZero limit)'),\ + ('Selected Only', PREF_SEL_ONLY, 'Only xmirror selected bones.'),\ + ] + + # Popup, exit if the user doesn't click OK + if not Draw.PupBlock("X Mirror mesh tool", pup_block): + return + + # Replace the variables with their button values. + PREF_XMID_SNAP= PREF_XMID_SNAP.val + PREF_MAX_DIST= PREF_MAX_DIST.val + PREF_MODE_L2R= PREF_MODE_L2R.val + PREF_MODE_R2L= PREF_MODE_R2L.val + PREF_XZERO_THRESH= PREF_XZERO_THRESH.val + PREF_SEL_ONLY= PREF_SEL_ONLY.val + + # If both are off assume mid-point and enable both + if not PREF_MODE_R2L and not PREF_MODE_L2R: + PREF_MODE_R2L= PREF_MODE_L2R= True + + + tot_editbones, tot_editbones_modified = armature_symetry(\ + arm_ob,\ + PREF_MAX_DIST,\ + PREF_XMID_SNAP,\ + PREF_XZERO_THRESH,\ + PREF_MODE_L2R,\ + PREF_MODE_R2L,\ + PREF_SEL_ONLY) + + if is_editmode: Blender.Window.EditMode(1) + + # Redraw all views before popup + Blender.Window.RedrawAll() + + # Print results + if PREF_SEL_ONLY: + msg= 'moved %i bones of %i selected' % (tot_editbones_modified, tot_editbones) + else: + msg= 'moved %i bones of %i visible' % (tot_editbones_modified, tot_editbones) + + + Blender.Draw.PupMenu(msg) + +# Check for __main__ so this function can be imported by other scripts without running the script. +if __name__=='__main__': + main() -- cgit v1.2.3