Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2009-04-02 15:30:27 +0400
committerCampbell Barton <ideasman42@gmail.com>2009-04-02 15:30:27 +0400
commit2178fcea6e2412db4d919f00b00cd5965bcbe27e (patch)
tree9db0faf8ddab84ed47fd08088d6f0059eefb88e7
parentda39179afd3e66caef8d0d29b67291c7d71af0dc (diff)
Script written for apricot, poly reduces based on known topologies, keeping edge loops unlike edge collapsing poly reduction.
Example http://www.graphicall.org/ftp/ideasman42/unsubsurf.gif
-rw-r--r--release/scripts/mesh_poly_reduce_grid.py351
1 files changed, 351 insertions, 0 deletions
diff --git a/release/scripts/mesh_poly_reduce_grid.py b/release/scripts/mesh_poly_reduce_grid.py
new file mode 100644
index 00000000000..3741a47723a
--- /dev/null
+++ b/release/scripts/mesh_poly_reduce_grid.py
@@ -0,0 +1,351 @@
+#!BPY
+"""
+Name: 'Poly Reduce Selection (Unsubsurf)'
+Blender: 245
+Group: 'Mesh'
+Tooltip: 'pradictable mesh simplifaction maintaining face loops'
+"""
+
+from Blender import Scene, Mesh, Window, sys
+import BPyMessages
+import bpy
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Campbell J 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 *****
+# --------------------------------------------------------------------------
+
+
+def my_mesh_util(me):
+ me_verts = me.verts
+
+ vert_faces = [ [] for v in me_verts]
+ vert_faces_corner = [ [] for v in me_verts]
+
+
+ # Ignore topology where there are not 2 faces connected to an edge.
+ edge_count = {}
+ for f in me.faces:
+ for edkey in f.edge_keys:
+ try:
+ edge_count[edkey] += 1
+ except:
+ edge_count[edkey] = 1
+
+ for edkey, count in edge_count.iteritems():
+
+ # Ignore verts that connect to edges with more then 2 faces.
+ if count != 2:
+ vert_faces[edkey[0]] = None
+ vert_faces[edkey[1]] = None
+ # Done
+
+
+
+ def faces_set_verts(face_ls):
+ unique_verts = set()
+ for f in face_ls:
+ for v in f:
+ unique_verts.add(v.index)
+ return unique_verts
+
+ for f in me.faces:
+ for corner, v in enumerate(f):
+ i = v.index
+ if vert_faces[i] != None:
+ vert_faces[i].append(f)
+ vert_faces_corner[i].append( corner )
+
+ grid_data_ls = []
+
+ for vi, face_ls in enumerate(vert_faces):
+ if face_ls != None:
+ if len(face_ls) == 4:
+ if face_ls[0].sel and face_ls[1].sel and face_ls[2].sel and face_ls[3].sel:
+ # Support triangles also
+ unique_vert_count = len(faces_set_verts(face_ls))
+ quads = 0
+ for f in face_ls:
+ if len(f) ==4:
+ quads += 1
+ if unique_vert_count==5+quads: # yay we have a grid
+ grid_data_ls.append( (vi, face_ls) )
+
+ elif len(face_ls) == 3:
+ if face_ls[0].sel and face_ls[1].sel and face_ls[2].sel:
+ unique_vert_count = len(faces_set_verts(face_ls))
+ if unique_vert_count==4: # yay we have 3 triangles to make into a bigger triangle
+ grid_data_ls.append( (vi, face_ls) )
+
+
+
+ # Now sort out which grid faces to use
+
+
+ # This list will be used for items we can convert, vertex is key, faces are values
+ grid_data_dict = {}
+
+ if not grid_data_ls:
+ print "doing nothing"
+ return
+
+ # quick lookup for the opposing corner of a qiad
+ quad_diag_mapping = 2,3,0,1
+
+ verts_used = [0] * len(me_verts) # 0 == untouched, 1==should touch, 2==touched
+ verts_used[grid_data_ls[0][0]] = 1 # start touching 1!
+
+ # From the corner vert, get the 2 edges that are not the corner or its opposing vert, this edge will make a new face
+ quad_edge_mapping = (1,3), (2,0), (1,3), (0,2) # hi-low, low-hi order is intended
+ tri_edge_mapping = (1,2), (0,2), (0,1)
+
+ done_somthing = True
+ while done_somthing:
+ done_somthing = False
+ grid_data_ls_index = -1
+
+ for vi, face_ls in grid_data_ls:
+ grid_data_ls_index += 1
+ if len(face_ls) == 3:
+ grid_data_dict[vi] = face_ls
+ grid_data_ls.pop( grid_data_ls_index )
+ break
+ elif len(face_ls) == 4:
+ # print vi
+ if verts_used[vi] == 1:
+ verts_used[vi] = 2 # dont look at this again.
+ done_somthing = True
+
+ grid_data_dict[vi] = face_ls
+
+ # Tag all faces verts as used
+
+ for i, f in enumerate(face_ls):
+ # i == face index on vert, needed to recall which corner were on.
+ v_corner = vert_faces_corner[vi][i]
+ fv =f.v
+
+ if len(f) == 4:
+ v_other = quad_diag_mapping[v_corner]
+ # get the 2 other corners
+ corner1, corner2 = quad_edge_mapping[v_corner]
+ if verts_used[fv[v_other].index] == 0:
+ verts_used[fv[v_other].index] = 1 # TAG for touching!
+ else:
+ corner1, corner2 = tri_edge_mapping[v_corner]
+
+ verts_used[fv[corner1].index] = 2 # Dont use these, they are
+ verts_used[fv[corner2].index] = 2
+
+
+ # remove this since we have used it.
+ grid_data_ls.pop( grid_data_ls_index )
+
+ break
+
+ if done_somthing == False:
+ # See if there are any that have not even been tagged, (probably on a different island), then tag them.
+
+ for vi, face_ls in grid_data_ls:
+ if verts_used[vi] == 0:
+ verts_used[vi] = 1
+ done_somthing = True
+ break
+
+
+ # Now we have all the areas we will fill, calculate corner triangles we need to fill in.
+ new_faces = []
+ quad_del_vt_map = (1,2,3), (0,2,3), (0,1,3), (0,1,2)
+ for vi, face_ls in grid_data_dict.iteritems():
+ for i, f in enumerate(face_ls):
+ if len(f) == 4:
+ # i == face index on vert, needed to recall which corner were on.
+ v_corner = vert_faces_corner[vi][i]
+ v_other = quad_diag_mapping[v_corner]
+ fv =f.v
+
+ #print verts_used[fv[v_other].index]
+ #if verts_used[fv[v_other].index] != 2: # DOSNT WORK ALWAYS
+
+ if 1: # THIS IS LAzY - some of these faces will be removed after adding.
+ # Ok we are removing half of this face, add the other half
+
+ # This is probably slower
+ # new_faces.append( [fv[ii].index for ii in (0,1,2,3) if ii != v_corner ] )
+
+ # do this instead
+ new_faces.append( (fv[quad_del_vt_map[v_corner][0]], fv[quad_del_vt_map[v_corner][1]], fv[quad_del_vt_map[v_corner][2]]) )
+
+ del grid_data_ls
+
+
+ # me.sel = 0
+ def faceCombine4(vi, face_ls):
+ edges = []
+
+ for i, f in enumerate(face_ls):
+ fv = f.v
+ v_corner = vert_faces_corner[vi][i]
+ if len(f)==4: ed = quad_edge_mapping[v_corner]
+ else: ed = tri_edge_mapping[v_corner]
+
+ edges.append( [fv[ed[0]].index, fv[ed[1]].index] )
+
+ # get the face from the edges
+ face = edges.pop()
+ while len(face) != 4:
+ # print len(edges), edges, face
+ for ed_idx, ed in enumerate(edges):
+ if face[-1] == ed[0] and (ed[1] != face[0]):
+ face.append(ed[1])
+ elif face[-1] == ed[1] and (ed[0] != face[0]):
+ face.append(ed[0])
+ else:
+ continue
+
+ edges.pop(ed_idx) # we used the edge alredy
+ break
+
+ return face
+
+ for vi, face_ls in grid_data_dict.iteritems():
+ if len(face_ls) == 4:
+ new_faces.append( faceCombine4(vi, face_ls) )
+ #pass
+ if len(face_ls) == 3: # 3 triangles
+ face = list(faces_set_verts(face_ls))
+ face.remove(vi)
+ new_faces.append( face )
+
+
+ # Now remove verts surounded by 3 triangles
+
+
+
+ # print new_edges
+ # me.faces.extend(new_faces, ignoreDups=True)
+
+ '''
+ faces_remove = []
+ for vi, face_ls in grid_data_dict.iteritems():
+ faces_remove.extend(face_ls)
+ '''
+
+ orig_facelen = len(me.faces)
+
+ orig_faces = list(me.faces)
+ me.faces.extend(new_faces, ignoreDups=True)
+ new_faces = list(me.faces)[len(orig_faces):]
+
+
+
+
+
+ if me.faceUV:
+ uvnames = me.getUVLayerNames()
+ act_uvlay = me.activeUVLayer
+
+ vert_faces_uvs = []
+ vert_faces_images = []
+
+
+ act_uvlay = me.activeUVLayer
+
+ for uvlay in uvnames:
+ me.activeUVLayer = uvlay
+ vert_faces_uvs[:] = [None] * len(me.verts)
+ vert_faces_images[:] = vert_faces_uvs[:]
+
+ for i,f in enumerate(orig_faces):
+ img = f.image
+ fv = f.v
+ uv = f.uv
+ mat = f.mat
+ for i,v in enumerate(fv):
+ vi = v.index
+ vert_faces_uvs[vi] = uv[i] # no nice averaging
+ vert_faces_images[vi] = img
+
+
+ # Now copy UVs across
+ for f in new_faces:
+ fi = [v.index for v in f.v]
+ f.image = vert_faces_images[fi[0]]
+ uv = f.uv
+ for i,vi in enumerate(fi):
+ uv[i][:] = vert_faces_uvs[vi]
+
+ if len(me.materials) > 1:
+ vert_faces_mats = [None] * len(me.verts)
+ for i,f in enumerate(orig_faces):
+ mat = f.mat
+ for i,v in enumerate(f.v):
+ vi = v.index
+ vert_faces_mats[vi] = mat
+
+ # Now copy UVs across
+ for f in new_faces:
+ print vert_faces_mats[f.v[0].index]
+ f.mat = vert_faces_mats[f.v[0].index]
+
+
+ me.verts.delete(grid_data_dict.keys())
+
+ # me.faces.delete(1, faces_remove)
+
+ if me.faceUV:
+ me.activeUVLayer = act_uvlay
+
+ me.calcNormals()
+
+def main():
+
+ # Gets the current scene, there can be many scenes in 1 blend file.
+ sce = bpy.data.scenes.active
+
+ # Get the active object, there can only ever be 1
+ # and the active object is always the editmode object.
+ ob_act = sce.objects.active
+
+ if not ob_act or ob_act.type != 'Mesh':
+ BPyMessages.Error_NoMeshActive()
+ return
+
+ is_editmode = Window.EditMode()
+ if is_editmode: Window.EditMode(0)
+
+ Window.WaitCursor(1)
+ me = ob_act.getData(mesh=1) # old NMesh api is default
+ t = sys.time()
+
+ # Run the mesh editing function
+ my_mesh_util(me)
+
+ # Restore editmode if it was enabled
+ if is_editmode: Window.EditMode(1)
+
+ # Timing the script is a good way to be aware on any speed hits when scripting
+ print 'My Script finished in %.2f seconds' % (sys.time()-t)
+ Window.WaitCursor(0)
+
+
+# This lets you can import the script without running it
+if __name__ == '__main__':
+ main()
+