[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [14526] branches/apricot/release/scripts/ mesh_poly_reduce_grid.py: Request from Chris Plush, poly reduction tool that maintains face loops and gives pradictable results for terrain , this could evolve into an UnSubsurf tool.

Campbell Barton ideasman42 at gmail.com
Wed Apr 23 12:17:05 CEST 2008


Revision: 14526
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=14526
Author:   campbellbarton
Date:     2008-04-23 12:16:59 +0200 (Wed, 23 Apr 2008)

Log Message:
-----------
Request from Chris Plush, poly reduction tool that maintains face loops and gives pradictable results for terrain, this could evolve into an UnSubsurf tool.

Currently face flipping is incorrect and UV's are lost.

Added Paths:
-----------
    branches/apricot/release/scripts/mesh_poly_reduce_grid.py

Added: branches/apricot/release/scripts/mesh_poly_reduce_grid.py
===================================================================
--- branches/apricot/release/scripts/mesh_poly_reduce_grid.py	                        (rev 0)
+++ branches/apricot/release/scripts/mesh_poly_reduce_grid.py	2008-04-23 10:16:59 UTC (rev 14526)
@@ -0,0 +1,267 @@
+#!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
+
+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 = (3,1), (2,0), (1,3), (0,2)
+	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) )
+		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)
+	'''
+	
+	me.faces.extend(new_faces, ignoreDups=True)
+	me.verts.delete(grid_data_dict.keys())
+	
+	# me.faces.delete(1, faces_remove)
+	
+
+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