mesh_mirror_tool.py code cleanup, updated UI, tooltips added docstrig.

3ds_import.py minimal updates- changelog.
This commit is contained in:
Campbell Barton 2006-07-04 10:09:21 +00:00
parent ab5dffc1cd
commit 518fef7f29
3 changed files with 156 additions and 66 deletions

@ -18,6 +18,11 @@ This script imports a 3ds file and the materials into Blender for editing.
Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen).
0.97 by Campbell Barton<br>
- Strip material names of spaces
- Added import as instance to import the 3ds into its own
scene and add a group instance to the current scene
- New option to scale down imported objects so they are within a limited bounding area.
0.96 by Campbell Barton<br>
- Added workaround for bug in setting UV's for Zero vert index UV faces.

@ -1,20 +1,70 @@
#!BPY
"""
Name: 'Mirror Verts Loc & Weight'
Name: 'Mirror Vertex Locations & Weight'
Blender: 241
Group: 'Mesh'
Tooltip: 'Move verts so they snap to their mirrored locations.'
Tooltip: 'Snap Verticies to X mirrord locations and weights.'
"""
__author__= ['Campbell Barton']
__url__= ["blender", "elysiun", "http://members.iinet.net.au/~cpbarton/ideasman/"]
__version__= '1.0'
__bpydoc__= '''\
This script is used to mirror vertex locations and weights
It is usefull if you have a model that was made symetrical
but has verts that have moved from their mirrored locations slightly,
casuing blenders X-Mirror options not to work.
Weights can be mirrored too, this is usefull if you want to model 1 side of a mesh, copy the mesh and flip it.
You can then use this script to mirror to the copy, even creating new flipped vertex groups, renaming group name left to right or .L to .R
Vertex positions are mirrored by doing a locational lookup,
finding matching verts on both sides of a mesh and moving to the left/right or mid location.
The vertex weights work differently, they are mirrored my location also,
but they mirror in pairs, rather it works by finding the closest vertex on the flip side and using its weight.
When a location mirror is finished, verts that have not been mirrored will remain selected.
a good way to check both sides are mirrord is to select the mirrored parts,
run this script with default options and then see of there are any selected verts.
For details on each option read the tooltips.
'''
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (C) Campbell Barton
#
# 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 *****
# --------------------------------------------------------------------------
from Blender import Draw, Window, Scene, Mesh, Mathutils, sys, Object
import BPyMesh
reload(BPyMesh)
def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES):
BIGNUM= 1<<30
def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XMID_SNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES):
'''
PREF_MIRROR_LOCATION, Will we mirror locations?
PREF_XMID_SNAP, Should we snap verts to X-0?
PREF_MAX_DIST, Maximum distance to test snapping verts.
PREF_XZERO_THRESH, How close verts must be to the middle before they are considered X-Zero verts.
PREF_MODE, 0:middle, 1: Left. 2:Right.
PREF_NOR_WEIGHT, use normal angle difference to weight distances.
PREF_SEL_ONLY, only snap the selection
PREF_EDGE_USERS, match only verts with the same number of edge users.
PREF_MIRROR_LOCATION,
@ -34,19 +84,22 @@ def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_
edge_users[ed.v2.index]+=1
if PREF_XSNAP:
if PREF_XMID_SNAP: # Do we snap locations at all?
for v in me.verts:
if v.sel:
if abs(v.co.x) <= PREF_XZERO_THRESH:
v.co.x= 0
v.sel= 0
# alredy de-selected verts.
neg_vts = [v for v in me.verts if v.sel]
pos_vts = [v for v in me.verts if v.sel]
# alredy de-selected verts
neg_vts = [v for v in me.verts if v.sel and v.co.x < 0]
pos_vts = [v for v in me.verts if v.sel and v.co.x > 0]
else:
neg_vts = [v for v in me.verts if v.sel if v.co.x > PREF_XZERO_THRESH]
pos_vts = [v for v in me.verts if v.sel if v.co.x < -PREF_XZERO_THRESH]
# Use a small margin verts must be outside before we mirror them.
neg_vts = [v for v in me.verts if v.sel if v.co.x < -PREF_XZERO_THRESH]
pos_vts = [v for v in me.verts if v.sel if v.co.x > PREF_XZERO_THRESH]
@ -57,36 +110,46 @@ def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_
flipvec= Mathutils.Vector()
len_neg_vts= float(len(neg_vts))
for i1, nv in enumerate(neg_vts):
nv_co= nv.co
for i2, pv in enumerate(pos_vts):
# Enforce edge users.
if not PREF_EDGE_USERS or edge_users[i1]==edge_users[i2]:
flipvec[:]= pv.co
flipvec.x= -flipvec.x
l= (nv_co-flipvec).length
# Record a match.
if l<=PREF_MAX_DIST:
# We can adjust the length by the normal, now we know the length is under the limit.
if PREF_NOR_WEIGHT>0:
# Get the normal and flipm reuse flipvec
flipvec[:]= pv.no
if nv.sel: # we may alredy be mirrored, if so well be deselected
nv_co= nv.co
for i2, pv in enumerate(pos_vts):
if pv.sel:
# Enforce edge users.
if not PREF_EDGE_USERS or edge_users[i1]==edge_users[i2]:
flipvec[:]= pv.co
flipvec.x= -flipvec.x
try:
ang= Mathutils.AngleBetweenVecs(nv.no, flipvec)/180.0
except: # on rare occasions angle between vecs will fail.- zero length vec.
ang= 0
l= (nv_co-flipvec).length
l=l*(1+(ang*PREF_NOR_WEIGHT))
mirror_pairs.append((l, nv, pv))
# Update every 20 loops
if i1 % 10 == 0:
Window.DrawProgressBar(0.8 * (i1/len_neg_vts), 'Mirror verts %i of %i' % (i1, len_neg_vts))
if l==0.0: # Both are alredy mirrored so we dont need to think about them.
# De-Select so we dont use again/
pv.sel= nv.sel= 0
# Record a match.
elif l<=PREF_MAX_DIST:
# We can adjust the length by the normal, now we know the length is under the limit.
# DISABLED, WASNT VERY USEFULL
'''
if PREF_NOR_WEIGHT>0:
# Get the normal and flipm reuse flipvec
flipvec[:]= pv.no
flipvec.x= -flipvec.x
try:
ang= Mathutils.AngleBetweenVecs(nv.no, flipvec)/180.0
except: # on rare occasions angle between vecs will fail.- zero length vec.
ang= 0
l=l*(1+(ang*PREF_NOR_WEIGHT))
'''
# Record the pairs for sorting to see who will get joined
mirror_pairs.append((l, nv, pv))
# Update every 20 loops
if i1 % 10 == 0:
Window.DrawProgressBar(0.8 * (i1/len_neg_vts), 'Mirror verts %i of %i' % (i1, len_neg_vts))
Window.DrawProgressBar(0.9, 'Mirror verts: Updating locations')
# Now we have a list of the pairs we might use, lets find the best and do them first.
# de-selecting as we go. so we can makke sure not to mess it up.
mirror_pairs.sort(lambda a,b: cmp(a[0], b[0]))
@ -96,16 +159,16 @@ def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_
if PREF_MODE==0: # Middle
flipvec[:]= v2.co # positive
flipvec.x= -flipvec.x # negatve
v2.co[:]= v1.co[:]= (flipvec+v1.co)*0.5 # midway
v2.co= v1.co= (flipvec+v1.co)*0.5 # midway
v2.co.x= -v2.co.x
elif PREF_MODE==2: # Left
v2.co[:]= v1.co
v2.co= v1.co
v2.co.x= -v2.co.x
elif PREF_MODE==1: # Right
v1.co[:]= v2.co
v1.co= v2.co
v1.co.x= -v1.co.x
v1.sel= 0
v2.sel= 0
v1.sel= v2.sel= 0
#*Mirror Weights**********************************************************#
if PREF_MIRROR_WEIGHTS:
@ -118,19 +181,25 @@ def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_
flipvec= Mathutils.Vector()
len_neg_vts= float(len(neg_vts))
# Here we make a tuple to look through, if were middle well need to look through both.
if PREF_MODE==0: # Middle
find_set= ((neg_vts, pos_vts, mirror_pairs_l2r), (pos_vts, neg_vts, mirror_pairs_r2l))
elif PREF_MODE==1: # Left
find_set= ((neg_vts, pos_vts, mirror_pairs_l2r), )
elif PREF_MODE==2: # Right
find_set= ((pos_vts, neg_vts, mirror_pairs_r2l), )
for vtls_A, vtls_B, pair_ls in ((neg_vts, pos_vts, mirror_pairs_l2r), (pos_vts, neg_vts, mirror_pairs_r2l)):
# Do a locational lookup again :/ - This isnt that good form but if we havnt mirrored weights well need to do it anyway.
# The Difference with this is that we dont need to have 1:1 match for each vert- just get each vert to find another mirrored vert
# and use its weight.
# Use "find_set" so we can do a flipped search L>R and R>L without duplicate code.
for vtls_A, vtls_B, pair_ls in find_set:
for i1, vA in enumerate(vtls_A):
best_len=1<<30
best_len=1<<30 # BIGNUM
best_idx=-1
# Find the BEST match.
# Find the BEST match
vA_co= vA.co
for i2, vB in enumerate(vtls_B):
# Enforce edge users.
@ -166,7 +235,7 @@ def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_
vWeightDict[i2], groupNames= BPyMesh.dictWeightFlipGroups(vWeightDict[i1], groupNames, PREF_CREATE_FLIP_NAMES)
else:
for i1, i2 in mirror_pairs_l2r:
vWeightDict[i2]= vWeightDict[i1] # Warning Multiple instances of the same data
vWeightDict[i2]= vWeightDict[i1] # Warning Multiple instances of the same data, its ok in this case but dont modify later.
elif PREF_MODE==2: # Right
if PREF_FLIP_NAMES:
@ -174,7 +243,7 @@ def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_
vWeightDict[i2], groupNames= BPyMesh.dictWeightFlipGroups(vWeightDict[i1], groupNames, PREF_CREATE_FLIP_NAMES)
else:
for i1, i2 in mirror_pairs_r2l:
vWeightDict[i2]= vWeightDict[i1]
vWeightDict[i2]= vWeightDict[i1] # Warning, ditto above
BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
@ -194,11 +263,14 @@ def main():
# Defaults
PREF_EDITMESH_ONLY= Draw.Create(1)
PREF_MIRROR_LOCATION= Draw.Create(1)
PREF_XSNAP= Draw.Create(1)
PREF_XMID_SNAP= Draw.Create(1)
PREF_MAX_DIST= Draw.Create(0.02)
PREF_XZERO_THRESH= Draw.Create(0.002)
PREF_MODE= Draw.Create(0)
PREF_NOR_WEIGHT= Draw.Create(0.0)
#PREF_MODE= Draw.Create(0) # THIS IS TOOO CONFUSING, HAVE 2 BUTTONS AND MAKE THE MODE FROM THEM.
PREF_MODE_L2R= Draw.Create(1)
PREF_MODE_R2L= Draw.Create(0)
PREF_SEL_ONLY= Draw.Create(1)
PREF_EDGE_USERS= Draw.Create(0)
# Weights
@ -208,32 +280,46 @@ def main():
pup_block = [\
('EditMesh Only', PREF_EDITMESH_ONLY, 'If disabled, will mirror all selected meshes.'),\
'Left (-), Right (+)',\
('Left > Right', PREF_MODE_L2R, 'Copy from the Left to Right of the mesh. Enable Both for a mid loc/weight.'),\
('Right > Left', PREF_MODE_R2L, 'Copy from the Right to Left of the mesh. Enable Both for a mid loc/weight.'),\
'',\
('MaxDist:', PREF_MAX_DIST, 0.0, 1.0, 'Generate interpolated verts so closer vert weights can be copied.'),\
('XZero limit:', PREF_XZERO_THRESH, 0.0, 1.0, 'Tolerence for excluding verts from mirror and locking to X Zero.'),\
('Mode:', PREF_MODE, 0, 2, 'New Location/Weight (0:AverageL/R, 1:Left>Right 2:Right>Left)'),\
('NorWeight:', PREF_NOR_WEIGHT, 0.0, 1.0, 'Generate interpolated verts so closer vert weights can be copied.'),\
('XZero limit:', PREF_XZERO_THRESH, 0.0, 1.0, 'Mirror verts above this distance from the middle, else lock to X/zero.'),\
('Sel Verts Only', PREF_SEL_ONLY, 'Only mirror selected verts. Else try and mirror all'),\
('Edge Users', PREF_EDGE_USERS, 'Only match up verts that have the same number of edge users.'),\
'Locations',\
'Location Prefs',\
('Mirror Location', PREF_MIRROR_LOCATION, 'Mirror vertex locations.'),\
('XMidSnap Verts', PREF_XSNAP, 'Snap middle verts to X Zero (uses XZero limit)'),\
'Weights',\
('XMidSnap Verts', PREF_XMID_SNAP, 'Snap middle verts to X Zero (uses XZero limit)'),\
'Weight Prefs',\
('Mirror Weights', PREF_MIRROR_WEIGHTS, 'Mirror vertex locations.'),\
('Flip Groups', PREF_FLIP_NAMES, 'Mirror flip names.'),\
('New Flip Groups', PREF_CREATE_FLIP_NAMES, 'Make new groups for flipped names.'),\
]
if not Draw.PupBlock("X Mirror mesh tool", pup_block):
return
return
# WORK OUT THE MODE 0
# PREF_MODE, 0:middle, 1: Left. 2:Right.
PREF_MODE_R2L= PREF_MODE_R2L.val
PREF_MODE_L2R= PREF_MODE_L2R.val
if PREF_MODE_R2L and PREF_MODE_L2R:
PREF_MODE= 0 # Middle
elif not PREF_MODE_R2L and PREF_MODE_L2R:
PREF_MODE= 1 # Left to Right
elif PREF_MODE_R2L and not PREF_MODE_L2R:
PREF_MODE= 2 # Right to Left
else: # Neither Selected. Do middle anyway
PREF_MODE= 0
PREF_EDITMESH_ONLY= PREF_EDITMESH_ONLY.val
PREF_MIRROR_LOCATION= PREF_MIRROR_LOCATION.val
PREF_XSNAP= PREF_XSNAP.val
PREF_XMID_SNAP= PREF_XMID_SNAP.val
PREF_MAX_DIST= PREF_MAX_DIST.val
PREF_XZERO_THRESH= PREF_XZERO_THRESH.val
PREF_MODE= PREF_MODE.val
PREF_NOR_WEIGHT= PREF_NOR_WEIGHT.val
PREF_SEL_ONLY= PREF_SEL_ONLY.val
PREF_EDGE_USERS= PREF_EDGE_USERS.val
# weights
@ -248,12 +334,11 @@ def main():
Mesh.Mode(Mesh.SelectModes['VERTEX'])
Window.WaitCursor(1)
if act_ob:
mesh_mirror(act_ob.getData(mesh=1), PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES)
mesh_mirror(act_ob.getData(mesh=1), PREF_MIRROR_LOCATION, PREF_XMID_SNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES)
if (not PREF_EDITMESH_ONLY) and sel:
for ob in sel:
mesh_mirror(ob.getData(mesh=1), PREF_MIRROR_LOCATION, PREF_XSNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES)
mesh_mirror(ob.getData(mesh=1), PREF_MIRROR_LOCATION, PREF_XMID_SNAP, PREF_MAX_DIST, PREF_XZERO_THRESH, PREF_MODE, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES)
if is_editmode: Window.EditMode(1)
Window.WaitCursor(0)
@ -263,4 +348,4 @@ def main():
print 'Mirror done in %.6f sec.' % (sys.time()-t)
if __name__ == '__main__':
main()
main()

@ -8,7 +8,7 @@ Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.'
"""
__author__= "Campbell Barton"
__url__= ["blender", "elysiun"]
__url__= ["blender", "elysiun", "http://members.iinet.net.au/~cpbarton/ideasman/"]
__version__= "1.0"
__bpydoc__= """\