Fixed some evil bugs in the poly reducer messing up UV's every now and then.
authorCampbell Barton <ideasman42@gmail.com>
Thu, 18 May 2006 02:22:05 +0000 (02:22 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 18 May 2006 02:22:05 +0000 (02:22 +0000)
Added support for "Weighted Collapse" Before an edge could only collapse into its middle,
Now the edge collapses into a point bias'd by the 2 verts Concave/Convec "Pointyness" value as well as boundry weighting.
This works much better for boundry verts. - UV's Vcols and Weights are correctly interpolated into the new location.

Added a tool in the mesh menu for accessing the poly reduction tool.

release/scripts/bpymodules/BPyMesh.py
release/scripts/bpymodules/BPyMesh_redux.py
release/scripts/mesh_poly_reduce.py [new file with mode: 0644]

index b53a9ad9176901f5d57e295466c9f620e4ed4d62..6dbe7ba46fd8648cb70db837620f33dff68e35fe 100644 (file)
@@ -2,10 +2,10 @@ import Blender
 import BPyMesh_redux
 reload(BPyMesh_redux)
 
-def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGULATE=True):
+def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=5.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGULATE=True, DO_UV=True, DO_VCOL=True, DO_WEIGHTS=True):
        if REDUX<0 or REDUX>1.0:
                raise 'Error, factor must be between 0 and 1.0'
-       BPyMesh_redux.redux(ob, REDUX, BOUNDRY_WEIGHT, FACE_AREA_WEIGHT)
+       BPyMesh_redux.redux(ob, REDUX, BOUNDRY_WEIGHT, FACE_AREA_WEIGHT, FACE_TRIANGULATE, DO_UV, DO_VCOL, DO_WEIGHTS)
        
 def meshWeight2Dict(me):
        ''' Takes a mesh and return its group names and a list of dicts, one dict per vertex.
index 7c5e04687e1c4da1bb32a6832aa445c86a96dcab..db76da43a35be0d3c2c9211f0b9d5472cdcfbfd9 100644 (file)
@@ -1,5 +1,29 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# (C) Copyright 2006 MetaVR, Inc.
+# http://www.metavr.com
+# Written by 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 *****
+# --------------------------------------------------------------------------
+
 import Blender
 Vector= Blender.Mathutils.Vector
+Ang= Blender.Mathutils.AngleBetweenVecs
 LineIntersect= Blender.Mathutils.LineIntersect
 CrossVecs= Blender.Mathutils.CrossVecs
 import BPyMesh
@@ -8,21 +32,35 @@ import BPyMesh
 def uv_key(uv):
        return round(uv.x, 5), round(uv.y, 5)
        
+def uv_key_mix(uv1, uv2, w1, w2):
+       # Weighted mix. w1+w2==1.0
+       return w1*uv1[0]+w2*uv2[0], w1*uv1[1]+w2*uv2[1]
+
+def col_key(col):
+       return col.r, col.g, col.b
+       
+def col_key_mix(col1, col2,  w1, w2):
+       # Weighted mix. w1+w2==1.0
+       return int(w1*col1[0] + w2*col2[0]), int(w1*col1[1] + w2*col2[1]), int(w1*col1[2]+col2[2]*w2)
+
 def ed_key(ed):
        i1= ed.v1.index
        i2= ed.v2.index
        if i1<i2: return i1,i2
        return i2,i1
 
-def col_key(col):
-       return col.r, col.g, col.b
-
-def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGULATE=True):
+def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=5.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGULATE=True, DO_UV=True, DO_VCOL=True, DO_WEIGHTS=True):
        '''
        BOUNDRY_WEIGHT - 0 is no boundry weighting. 2.0 will make them twice as unlikely to collapse.
        FACE_AREA_WEIGHT - 0 is no weight. 1 is normal, 2.0 is higher.
        '''
        
+       """ # DEBUG!
+       if Blender.Get('rt') == 1000:
+               DEBUG=True
+       else:
+               DEBUG= False
+       """
        me= ob.getData(mesh=1)
        
        if REDUX>1.0 or REDUX<0.0 or len(me.faces)<4:
@@ -31,16 +69,39 @@ def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGUL
        if FACE_TRIANGULATE:
                me.quadToTriangle() 
        
+       if (not me.getVertGroupNames()) and DO_WEIGHTS:
+               DO_WEIGHTS= False
+       
        OLD_MESH_MODE= Blender.Mesh.Mode()
        Blender.Mesh.Mode(Blender.Mesh.SelectModes.VERTEX)
        
-       faceUV= me.faceUV
+       if (DO_UV or DO_VCOL) and not me.faceUV:
+               DO_VCOL= DO_UV= False
+               
        current_face_count= len(me.faces)
        target_face_count= int(current_face_count * REDUX)
        # % of the collapseable faces to collapse per pass.
        #collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
        collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
        
+       """# DEBUG!
+       if DEBUG:
+               COUNT= [0]
+               def rd():
+                       if COUNT[0]< 330:
+                               COUNT[0]+=1
+                               return
+                       me.update()
+                       Blender.Window.RedrawAll()
+                       print 'Press key for next, count "%s"' % COUNT[0]
+                       try: input()
+                       except KeyboardInterrupt:
+                               raise "Error"
+                       except:
+                               pass
+                               
+                       COUNT[0]+=1
+       """
        
        class collapseEdge(object):
                __slots__ = 'length', 'key', 'faces', 'collapse_loc', 'v1', 'v2','uv1', 'uv2', 'col1', 'col2', 'collapse_weight'
@@ -50,30 +111,31 @@ def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGUL
                        self.faces= []
                        self.v1= ed.v1
                        self.v2= ed.v2
-                       if faceUV:
+                       if DO_UV or DO_VCOL:
                                self.uv1= []
                                self.uv2= []
                                self.col1= []
                                self.col2= []
-                               # self.collapse_loc= None # new collapse location.
                                
-                               # Basic weighting.
-                               #self.collapse_weight= self.length *  (1+ ((ed.v1.no-ed.v2.no).length**2))
+                       # self.collapse_loc= None # new collapse location.
+                       # Basic weighting.
+                       #self.collapse_weight= self.length *  (1+ ((ed.v1.no-ed.v2.no).length**2))
+                       self.collapse_weight= 1.0
 
        class collapseFace(object):
-               __slots__ = 'verts', 'normal', 'area', 'index', 'orig_uv', 'orig_col', 'uv', 'col'
+               __slots__ = 'verts', 'normal', 'area', 'index', 'orig_uv', 'orig_col', 'uv', 'col' # , 'collapse_edge_count'
                def __init__(self, f):
                        self.verts= f.v
                        self.normal= f.no
                        self.area= f.area
                        self.index= f.index
-                       if faceUV:
+                       if DO_UV or DO_VCOL:
                                self.orig_uv= [uv_key(uv) for uv in f.uv]
-                               self.orig_col= [col_key(col) for col in f.col]
                                self.uv= f.uv
+                               self.orig_col= [col_key(col) for col in f.col]
                                self.col= f.col
-       
-       
+                       
+                       #self.collapse_edge_count= 0 # used so we know how many edges of the face are collapsed.
        
        for v in me.verts:
                v.hide=0
@@ -83,25 +145,36 @@ def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGUL
        while target_face_count <= len(me.faces):
                BPyMesh.meshCalcNormals(me)
                
-               for v in me.verts:
-                       v.sel= False
+               if DO_WEIGHTS:
+                       groupNames, vWeightDict= BPyMesh.meshWeight2Dict(me)
+               
+               # THIS CRASHES
+               #verts= list(me.verts)
+               #edges= list(me.edges)
+               #faces= list(me.faces)
                
-               # Backup colors
-               if faceUV:
-                       orig_texface= [[(uv_key(f.uv[i]), col_key(f.col[i])) for i in xrange(len(f.v))] for f in me.faces]
+               # THIS WORKS
+               verts= me.verts
+               edges= me.edges
+               faces= me.faces
                
-               collapse_faces= [collapseFace(f) for f in me.faces]
-               collapse_edges= [collapseEdge(ed) for ed in me.edges]
+               DOUBLE_CHECK= [0]*len(verts)
+               
+               for v in verts:
+                       v.sel= False
+               
+               collapse_faces= [collapseFace(f) for f in faces]
+               collapse_edges= [collapseEdge(ed) for ed in edges]
                collapse_edges_dict= dict( [(ced.key, ced) for ced in collapse_edges] )
                
                # Store verts edges.
-               vert_ed_users= [[] for i in xrange(len(me.verts))]
+               vert_ed_users= [[] for i in xrange(len(verts))]
                for ced in collapse_edges:
                        vert_ed_users[ced.v1.index].append(ced)
                        vert_ed_users[ced.v2.index].append(ced)
                
                # Store face users
-               vert_face_users= [[] for i in xrange(len(me.verts))]
+               vert_face_users= [[] for i in xrange(len(verts))]
                
                # Have decieded not to use this. area is better.
                #face_perim= [0.0]* len(me.faces)
@@ -119,72 +192,152 @@ def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGUL
                                else: ced= collapse_edges_dict[i1,i2]
                                
                                ced.faces.append(cfa)
-                               if faceUV:
-                                       ced.uv1.append( cfa.orig_uv[i] )
-                                       ced.uv2.append( cfa.orig_uv[i-1] )
+                               if DO_UV or DO_VCOL:
+                                       # if the edge is flipped from its order in the face then we need to flip the order indicies.
+                                       if cfa.verts[i]==ced.v1:        i1,i2 = i, i-1
+                                       else:                                           i1,i2 = i-1, i
+                               
+                               if DO_UV:
+                                       ced.uv1.append( cfa.orig_uv[i1] )
+                                       ced.uv2.append( cfa.orig_uv[i2] )
+                               
+                               if DO_VCOL:
+                                       ced.col1.append( cfa.orig_col[i1] )
+                                       ced.col2.append( cfa.orig_col[i2] )
                                        
-                                       ced.col1.append( cfa.orig_col[i] )
-                                       ced.col2.append( cfa.orig_col[i-1] )
                                
                                # PERIMITER
                                #face_perim[ii]+= ced.length
                
                
+               
+               # How weight the verts by the area of their faces * the normal difference.
+               # when the edge collapses, to vert weights are taken into account 
+               
+               vert_weights= [0.5] * len(verts)
+               
+               for ii, vert_faces in enumerate(vert_face_users):
+                       for f in vert_faces:
+                               try:
+                                       no_ang= (Ang(verts[ii].no, f[1].normal)/180) * f[1].area
+                               except:
+                                       no_ang= 1.0
+                               
+                               vert_weights[ii] += no_ang
+               
+               
+               # BOUNDRY CHECKING AND WEIGHT EDGES. CAN REMOVE
+               # Now we know how many faces link to an edge. lets get all the boundry verts
+               if BOUNDRY_WEIGHT > 0:
+                       verts_boundry= [1] * len(verts)
+                       #for ed_idxs, faces_and_uvs in edge_faces_and_uvs.iteritems():
+                       for ced in collapse_edges:
+                               if len(ced.faces) < 2:
+                                       verts_boundry[ced.key[0]]= 2
+                                       verts_boundry[ced.key[1]]= 2
+                       
+                       for ced in collapse_edges:
+                               if verts_boundry[ced.v1.index] != verts_boundry[ced.v2.index]:
+                                       # Edge has 1 boundry and 1 non boundry vert. weight higher
+                                       ced.collapse_weight= BOUNDRY_WEIGHT
+                       
+                       # weight the verts by their boundry status
+                       
+                       for ii, boundry in enumerate(verts_boundry):
+                               if boundry==2:
+                                       vert_weights[ii] *= BOUNDRY_WEIGHT
+                       
+                       vert_collapsed= verts_boundry
+                       del verts_boundry
+               else:
+                       vert_collapsed= [1] * len(verts)
+               
+               
+               
+               
                def ed_set_collapse_loc(ced):
                        v1co= ced.v1.co
                        v2co= ced.v2.co
                        v1no= ced.v1.co
                        v2no= ced.v2.co
-                       length= ced.length
-                       between= (v1co + v2co) * 0.5
-                       # Collapse
-                       # new_location = between # Replace tricky code below. this code predicts the best collapse location.
-                       
-                       # Make lines at right angles to the normals- these 2 lines will intersect and be
-                       # the point of collapsing.
                        
-                       # Enlarge so we know they intersect:  ced.length*2
-                       cv1= CrossVecs(v1no, CrossVecs(v1no, v1co-v2co))
-                       cv2= CrossVecs(v2no, CrossVecs(v2no, v2co-v1co))
+                       # Use the vertex weights to bias the new location.
+                       w1= vert_weights[ced.v1.index]
+                       w2= vert_weights[ced.v2.index]
                        
-                       # Scale to be less then the edge lengths.
-                       cv1.normalize()
-                       cv2.normalize()
-                       cv1 = cv1 * length* 0.333
-                       cv2 = cv2 * length* 0.333
+                       # normalize the weights of each vert - se we can use them as scalers.
+                       wscale= w1+w2
+                       if not wscale: # no scale?
+                               w1=w2= 0.5
+                       else:
+                               w1/=wscale
+                               w2/=wscale
                        
+                       length= ced.length
                        
-                       ced.collapse_loc = between + (cv1 + cv2)
-                       if ced.collapse_loc.x != ced.collapse_loc.x: # NAN LOCATION, revert to between
+                       if 0:
+                               between= ((v1co*w1) + (v2co*w2))
                                ced.collapse_loc= between
-               
+                       else:
+                               between= (v1co+v2co) * 0.5
+                               
+                               # Collapse
+                               # new_location = between # Replace tricky code below. this code predicts the best collapse location.
+                               
+                               # Make lines at right angles to the normals- these 2 lines will intersect and be
+                               # the point of collapsing.
+                               
+                               # Enlarge so we know they intersect:  ced.length*2
+                               cv1= CrossVecs(v1no, CrossVecs(v1no, v1co-v2co))
+                               cv2= CrossVecs(v2no, CrossVecs(v2no, v2co-v1co))
+                               
+                               # Scale to be less then the edge lengths.
+                               cv1.normalize()
+                               cv2.normalize()
+                               cv1 = cv1 * (length* 0.4)
+                               cv2 = cv2 * (length* 0.4)
+                               
+                               smart_offset_loc= between + (cv1 + cv2)
+                               
+                               # Now we need to blend between smart_offset_loc and w1/w2
+                               # you see were blending between a vert and the edges midpoint, so we cant use a normal weighted blend.
+                               if w1 > 0.5: # between v1 and smart_offset_loc
+                                       #ced.collapse_loc= v1co*(w2+0.5) + smart_offset_loc*(w1-0.5)
+                                       w2*=2
+                                       w1= 1-w2
+                                       
+                                       
+                                       ced.collapse_loc= v1co*w1 + smart_offset_loc*w2
+                               else: # w between v2 and smart_offset_loc
+                                       w1*=2
+                                       w2= 1-w1
+                                       ced.collapse_loc= v2co*w2 + smart_offset_loc*w1
+                                       
+                               if ced.collapse_loc.x != ced.collapse_loc.x: # NAN LOCATION, revert to between
+                                       ced.collapse_loc= between
+                               
                
                # Best method, no quick hacks here, Correction. Should be the best but needs tweaks.
                def ed_set_collapse_error(ced):
-                       Ang= Blender.Mathutils.AngleBetweenVecs
-                       
                        i1= ced.v1.index
                        i2= ced.v1.index
-                       test_faces= set()
                        
-                       for i in (i1,i2):
+                       test_faces= set()
+                       for i in (i1,i2): # faster then LC's
                                for f in vert_face_users[i]:
                                        test_faces.add(f[1].index)
                        
                        for f in ced.faces:
                                test_faces.remove(f.index)
                        
-                       test_faces= tuple(test_faces) # keep order
-                       
-                       # test_faces is now faces used by ed.v1 and ed.v2 that will not be removed in the collapse.
-                       # orig_nos= [face_normals[i] for i in test_faces]
+                       # test_faces= tuple(test_faces) # keep order
                        
                        v1_orig= Vector(ced.v1.co)
                        v2_orig= Vector(ced.v2.co)
                        
                        ced.v1.co= ced.v2.co= ced.collapse_loc
                        
-                       new_nos= [me.faces[i].no for i in test_faces]
+                       new_nos= [faces[i].no for i in test_faces]
                        
                        ced.v1.co= v1_orig
                        ced.v2.co= v2_orig
@@ -205,59 +358,45 @@ def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGUL
                                        pass
                        
                        # This is very arbirary, feel free to modify
-                       try:
-                               no_ang= (Ang(ced.v1.no, ced.v2.no)/180) + 1
-                       except:
-                               no_ang= 2.0
-                       ced.collapse_weight=  (no_ang * ced.length) * (1-(1/angle_diff))# / max(len(test_faces), 1)
+                       try:            no_ang= (Ang(ced.v1.no, ced.v2.no)/180) + 1
+                       except:         no_ang= 2.0
+                               
+                       # do *= because we face the boundry weight to initialize the weight. 1.0 default.
+                       ced.collapse_weight*=  ((no_ang * ced.length) * (1-(1/angle_diff)))# / max(len(test_faces), 1)
                
                # We can calculate the weights on __init__ but this is higher qualuity.
                for ced in collapse_edges:
-                       ed_set_collapse_loc(ced)
-                       ed_set_collapse_error(ced)
+                       if ced.faces: # dont collapse faceless edges.
+                               ed_set_collapse_loc(ced)
+                               ed_set_collapse_error(ced)
                
                # Wont use the function again.
                del ed_set_collapse_error
                del ed_set_collapse_loc
-               
-               
-               # BOUNDRY CHECKING AND WEIGHT EDGES. CAN REMOVE
-               # Now we know how many faces link to an edge. lets get all the boundry verts
-               if BOUNDRY_WEIGHT > 0:
-                       verts_boundry= [1] * len(me.verts)
-                       #for ed_idxs, faces_and_uvs in edge_faces_and_uvs.iteritems():
-                       for ced in collapse_edges:
-                               if len(ced.faces) < 2:
-                                       verts_boundry[ced.key[0]]= 2
-                                       verts_boundry[ced.key[1]]= 2
-                       
-                       for ced in collapse_edges:
-                               if verts_boundry[ced.v1.index] != verts_boundry[ced.v2.index]:
-                                       # Edge has 1 boundry and 1 non boundry vert. weight higher
-                                       ced.collapse_weight*=BOUNDRY_WEIGHT
-                       vert_collapsed= verts_boundry
-                       del verts_boundry
-               else:
-                       vert_collapsed= [1] * len(me.verts)
-
                # END BOUNDRY. Can remove
                
                # sort by collapse weight
                collapse_edges.sort(lambda ced1, ced2: cmp(ced1.collapse_weight, ced2.collapse_weight)) # edges will be used for sorting
                
+               vert_collapsed= [0]*len(verts)
+               
+               collapse_edges_to_collapse= []
+               
                # Make a list of the first half edges we can collapse,
                # these will better edges to remove.
                collapse_count=0
                for ced in collapse_edges:
-                       v1= ced.v1
-                       v2= ced.v2
-                       # Use vert selections 
-                       if vert_collapsed[v1.index]==0 or vert_collapsed[v2.index]==0:
-                               pass
-                       else:
-                               # Now we know the verts havnyt been collapsed.
-                               vert_collapsed[v1.index]= vert_collapsed[v2.index]= 0 # Dont collapse again.
-                               collapse_count+=1
+                       if ced.faces:
+                               i1= ced.v1.index
+                               i2= ced.v2.index
+                               # Use vert selections 
+                               if vert_collapsed[i1] or vert_collapsed[i2]:
+                                       pass
+                               else:
+                                       # Now we know the verts havnyt been collapsed.
+                                       vert_collapsed[i2]= vert_collapsed[i1]= 1 # Dont collapse again.
+                                       collapse_count+=1
+                                       collapse_edges_to_collapse.append(ced)
                
                # Get a subset of the entire list- the first "collapse_per_pass", that are best to collapse.
                if collapse_count > 4:
@@ -265,59 +404,110 @@ def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGUL
                else:
                        collapse_count = len(collapse_edges)
                # We know edge_container_list_collapse can be removed.
-               for ced in collapse_edges:
+               for ced in collapse_edges_to_collapse:
+                       """# DEBUG!
+                       if DEBUG:
+                               if DOUBLE_CHECK[ced.v1.index] or\
+                               DOUBLE_CHECK[ced.v2.index]:
+                                       raise 'Error'
+                               else:
+                                       DOUBLE_CHECK[ced.v1.index]=1
+                                       DOUBLE_CHECK[ced.v2.index]=1
+                               
+                               tmp= (ced.v1.co+ced.v2.co)*0.5
+                               Blender.Window.SetCursorPos(tmp.x, tmp.y, tmp.z)
+                               Blender.Window.RedrawAll()
+                       """
+                       
                        # Chech if we have collapsed our quota.
                        collapse_count-=1
                        if not collapse_count:
-                               ced.collapse_loc= None
                                break
-                               
+                       
                        current_face_count -= len(ced.faces)
-                       if faceUV:
+                       
+                       # Interpolate the bone weights.
+                       if DO_WEIGHTS:
+                               i1= ced.v1.index
+                               i2= ced.v2.index
+                               w1= vert_weights[i1]
+                               w2= vert_weights[i2]
+                               
+                               # Normalize weights
+                               wscale= w1+w2
+                               if not wscale: # no scale?
+                                       w1=w2= 0.5
+                               else:
+                                       w1/= wscale
+                                       w2/= wscale
+                               wd= vWeightDict[i1] # v1 weight dict
+                               for group_key, weight_value in wd.iteritems():
+                                       wd[group_key]= weight_value*w1
+                               
+                               wd= vWeightDict[i2] # v1 weight dict
+                               for group_key, weight_value in wd.iteritems():
+                                       wd[group_key]= weight_value*w2
+                       
+                       
+                       if DO_UV or DO_VCOL:
                                # Handel UV's and vert Colors!
-                               v1= ced.v1
-                               v2= ced.v2
-                               for v, edge_my_uvs, edge_other_uvs, edge_my_cols, edge_other_cols in ((v2, ced.uv1, ced.uv2, ced.col1, ced.col2),(v1, ced.uv2, ced.uv1, ced.col2, ced.col1)):
+                               for v, my_weight, other_weight, edge_my_uvs, edge_other_uvs, edge_my_cols, edge_other_cols in (\
+                               ( ced.v1, vert_weights[ced.v1.index], vert_weights[ced.v2.index], ced.uv1, ced.uv2, ced.col1, ced.col2),\
+                               ( ced.v2, vert_weights[ced.v2.index], vert_weights[ced.v1.index], ced.uv2, ced.uv1, ced.col2, ced.col1)\
+                               ):
+                                       
+                                       # Normalize weights
+                                       wscale= my_weight+other_weight
+                                       if not wscale: # no scale?
+                                               my_weight=other_weight= 0.5
+                                       else:
+                                               my_weight/= wscale
+                                               other_weight/= wscale
+                                       
+                                       uvs_mixed=   [ uv_key_mix(edge_my_uvs[iii],   edge_other_uvs[iii],  my_weight, other_weight)  for iii in xrange(len(edge_my_uvs))  ]
+                                       cols_mixed=  [ col_key_mix(edge_my_cols[iii], edge_other_cols[iii], my_weight, other_weight) for iii in xrange(len(edge_my_cols)) ]
+                                       #print 'FACE, USWERS', len(vert_face_users[v.index])
                                        for face_vert_index, cfa in vert_face_users[v.index]:
-                                               uvk=  cfa.orig_uv[face_vert_index] 
-                                               colk= cfa.orig_col[face_vert_index] 
+                                               if len(cfa.verts)==3 and cfa not in ced.faces: # if the face is apart of this edge then dont bother finding the uvs since the face will be removed anyway.
                                                
-                                               # UV COORDS
-                                               tex_index= None
-                                               try:
-                                                       tex_index= edge_my_uvs.index(uvk)
-                                               except ValueError:
-                                                       pass
-                                               
-                                               if tex_index != None:
-                                                       # This face uses a uv in the collapsing face. - do a merge
-                                                       other_uv= edge_other_uvs[tex_index]
-                                                       uv_vec= cfa.uv[face_vert_index]
-                                                       uv_vec.x= (uvk[0] + other_uv[0])*0.5
-                                                       uv_vec.y= (uvk[1] + other_uv[1])*0.5
-                                               
-                                               # TEXFACE COLOURS
-                                               tex_index= None
-                                               try:
-                                                       tex_index= edge_my_cols.index(colk)
-                                               except ValueError:
-                                                       pass
-                                               
-                                               if tex_index != None:
-                                                       # Col
-                                                       other_col= edge_other_cols[tex_index]
-                                                       # f= me.faces[cfa.index]
-                                                       col_ob= cfa.col[face_vert_index]
-                                                       # col_ob= me.faces[cfa.index].col[face_vert_index]
+                                                       if DO_UV:
+                                                               # UV COORDS
+                                                               uvk=  cfa.orig_uv[face_vert_index] 
+                                                               try:
+                                                                       tex_index= edge_my_uvs.index(uvk)
+                                                               except:
+                                                                       tex_index= None
+                                                                       """ # DEBUG!
+                                                                       if DEBUG:
+                                                                               print 'not found', uvk, 'in', edge_my_uvs, 'ed index', ii, '\nwhat about', edge_other_uvs
+                                                                       """
+                                                               if tex_index != None: # This face uses a uv in the collapsing face. - do a merge
+                                                                       other_uv= edge_other_uvs[tex_index]
+                                                                       uv_vec= cfa.uv[face_vert_index]
+                                                                       uv_vec.x, uv_vec.y= uvs_mixed[tex_index]
                                                        
-                                                       col_ob.r = int((colk[0] + other_col[0])*0.5)
-                                                       col_ob.g = int((colk[1] + other_col[1])*0.5)
-                                                       col_ob.b = int((colk[2] + other_col[2])*0.5)
-                       
+                                                       # TEXFACE COLORS
+                                                       if DO_VCOL:
+                                                               colk= cfa.orig_col[face_vert_index] 
+                                                               try:    tex_index= edge_my_cols.index(colk)
+                                                               except: pass
+                                                               if tex_index != None:
+                                                                       other_col= edge_other_cols[tex_index]
+                                                                       col_ob= cfa.col[face_vert_index]
+                                                                       col_ob.r, col_ob.g, col_ob.b= cols_mixed[tex_index]
+                                                       
+                                                       # DEBUG! if DEBUG: rd()
+                               
+                               # Execute the collapse
+                               ced.v1.sel= ced.v2.sel= True
+                               ced.v1.co= ced.v2.co=  ced.collapse_loc
+                               
+                               # DEBUG! if DEBUG: rd()
                        if current_face_count <= target_face_count:
                                ced.collapse_loc= None
                                break
-               
+                               
+               '''
                # Execute the collapse
                for ced in collapse_edges:
                        # Since the list is ordered we can stop once the first non collapsed edge if sound.
@@ -325,27 +515,27 @@ def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, FACE_AREA_WEIGHT=1.0, FACE_TRIANGUL
                                break
                        ced.v1.sel= ced.v2.sel= True
                        ced.v1.co= ced.v2.co=  ced.collapse_loc
+               '''
+                       
+               # Copy weights back to the mesh before we remove doubles.
+               if DO_WEIGHTS:
+                       BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
                
                doubles= me.remDoubles(0.0001) 
                me= ob.getData(mesh=1)
                current_face_count= len(me.faces)
+               #break
+               
                if doubles==0: # should never happen.
                        break
-                       
+               
                if current_face_count <= target_face_count:
                        ced.collapse_loc= None
                        break
        
        # Cleanup. BUGGY?
-       '''
-       vert_face_user_count= [0]*len(me.verts)
-       for f in me.faces:
-               for v in f.v:
-                       vert_face_user_count[v.index] +=1
        
-       del_verts= [i for i in xrange(len(me.verts)) if not vert_face_user_count[i]]
-       me.verts.delete( del_verts )
-       '''
+       
        me.update()
        Blender.Mesh.Mode(OLD_MESH_MODE)
 
@@ -356,7 +546,7 @@ def main():
        scn= Blender.Scene.GetCurrent()
        active_ob= scn.getActiveObject()
        t= Blender.sys.time()
-       redux(active_ob, 0.5)
+       redux(active_ob, 0.4)
        print '%.4f' % (Blender.sys.time()-t)
 
 if __name__=='__main__':
diff --git a/release/scripts/mesh_poly_reduce.py b/release/scripts/mesh_poly_reduce.py
new file mode 100644 (file)
index 0000000..7efa916
--- /dev/null
@@ -0,0 +1,70 @@
+#!BPY
+"""
+Name: 'Poly Reducer'
+Blender: 241
+Group: 'Mesh'
+Tooltip: 'Removed polygons from a mesh while maintaining the shape, textures and weights.'
+"""
+
+from Blender import Draw, Window, Scene, Mesh, Mathutils, sys, Object
+import BPyMesh
+reload(BPyMesh)
+
+       
+def main():
+       scn = Scene.GetCurrent()
+       act_ob= scn.getActiveObject()
+       if act_ob.getType()!='Mesh':
+               act_ob= None
+       
+       sel= [ob for ob in Object.GetSelected() if ob.getType()=='Mesh' if ob != act_ob]
+       if not sel and not act_ob:
+               Draw.PupMenu('Error, select a mesh as your active object')
+               return
+       
+       # Defaults
+       PREF_REDUX= Draw.Create(0.5)
+       PREF_BOUNDRY_WEIGHT= Draw.Create(5.0)
+       PREF_FACE_AREA_WEIGHT= Draw.Create(1.0)
+       PREF_FACE_TRIANGULATE= Draw.Create(1)
+       PREF_DO_UV= Draw.Create(1)
+       PREF_DO_VCOL= Draw.Create(1)
+       PREF_DO_WEIGHTS= Draw.Create(1)
+       
+       pup_block = [\
+       ('Poly Reduce:', PREF_REDUX, 0.05, 0.95, 'Scale the meshes poly count by this value.'),\
+       ('Boundry Weight:', PREF_BOUNDRY_WEIGHT, 0.0, 20.0, 'Weight boundry verts by this scale, 0.0 for no boundry weighting.'),\
+       ('Area Weight:', PREF_FACE_AREA_WEIGHT, 0.0, 20.0, 'Collapse edges effecting lower area faces first.'),\
+       ('Triangulate', PREF_FACE_TRIANGULATE, 'Convert quads to tris before reduction, for more choices of edges to collapse.'),\
+       ('UV Coords', PREF_DO_UV, 'Interpolate UV Coords.'),\
+       ('Vert Colors', PREF_DO_VCOL, 'Interpolate Vertex Colors'),\
+       ('Vert Weights', PREF_DO_WEIGHTS, 'Interpolate Vertex Weights'),\
+       ]
+       
+       if not Draw.PupBlock("X Mirror mesh tool", pup_block):
+               return
+       
+       PREF_REDUX= PREF_REDUX.val
+       PREF_BOUNDRY_WEIGHT= PREF_BOUNDRY_WEIGHT.val
+       PREF_FACE_AREA_WEIGHT= PREF_FACE_AREA_WEIGHT.val
+       PREF_FACE_TRIANGULATE= PREF_FACE_TRIANGULATE.val
+       PREF_DO_UV= PREF_DO_UV.val
+       PREF_DO_VCOL= PREF_DO_VCOL.val
+       PREF_DO_WEIGHTS= PREF_DO_WEIGHTS.val
+       
+       t= sys.time()
+       
+       is_editmode = Window.EditMode() # Exit Editmode.
+       if is_editmode: Window.EditMode(0)
+       Window.WaitCursor(1)    
+       
+       BPyMesh.redux(act_ob, PREF_REDUX, PREF_BOUNDRY_WEIGHT, PREF_FACE_AREA_WEIGHT, PREF_FACE_TRIANGULATE, PREF_DO_UV, PREF_DO_VCOL, PREF_DO_WEIGHTS)
+       
+       if is_editmode: Window.EditMode(1)
+       Window.WaitCursor(0)
+       Window.RedrawAll()
+       
+       print 'Reduction done in %.6f sec.' % (sys.time()-t)
+
+if __name__ == '__main__':
+       main()
\ No newline at end of file