[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [19502] trunk/blender/release/scripts/ mesh_poly_reduce_grid.py: Script written for apricot, poly reduces based on known topologies, keeping edge loops unlike edge collapsing poly reduction.

Campbell Barton ideasman42 at gmail.com
Thu Apr 2 13:30:27 CEST 2009


Revision: 19502
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=19502
Author:   campbellbarton
Date:     2009-04-02 13:30:27 +0200 (Thu, 02 Apr 2009)

Log Message:
-----------
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

Added Paths:
-----------
    trunk/blender/release/scripts/mesh_poly_reduce_grid.py

Added: trunk/blender/release/scripts/mesh_poly_reduce_grid.py
===================================================================
--- trunk/blender/release/scripts/mesh_poly_reduce_grid.py	                        (rev 0)
+++ trunk/blender/release/scripts/mesh_poly_reduce_grid.py	2009-04-02 11:30:27 UTC (rev 19502)
@@ -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()
+





More information about the Bf-blender-cvs mailing list