2.50: svn merge https://svn.blender.org/svnroot/bf-blender/trunk/blender -r19323...
[blender.git] / release / scripts / mesh_poly_reduce_grid.py
1 #!BPY
2 """
3 Name: 'Poly Reduce Selection (Unsubsurf)'
4 Blender: 245
5 Group: 'Mesh'
6 Tooltip: 'pradictable mesh simplifaction maintaining face loops'
7 """
8
9 from Blender import Scene, Mesh, Window, sys
10 import BPyMessages
11 import bpy
12
13 # ***** BEGIN GPL LICENSE BLOCK *****
14 #
15 # Script copyright (C) Campbell J Barton
16 #
17 # This program is free software; you can redistribute it and/or
18 # modify it under the terms of the GNU General Public License
19 # as published by the Free Software Foundation; either version 2
20 # of the License, or (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 #
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software Foundation,
29 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
30 #
31 # ***** END GPL LICENCE BLOCK *****
32 # --------------------------------------------------------------------------
33
34
35 def my_mesh_util(me):
36         me_verts = me.verts
37         
38         vert_faces = [ [] for v in me_verts]
39         vert_faces_corner = [ [] for v in me_verts]
40         
41         
42         # Ignore topology where there are not 2 faces connected to an edge.
43         edge_count = {}
44         for f in me.faces:
45                 for edkey in f.edge_keys:
46                         try:
47                                 edge_count[edkey] += 1
48                         except:
49                                 edge_count[edkey]  = 1
50                                 
51         for edkey, count in edge_count.iteritems():
52                 
53                 # Ignore verts that connect to edges with more then 2 faces.
54                 if count != 2:
55                         vert_faces[edkey[0]] = None
56                         vert_faces[edkey[1]] = None
57         # Done
58         
59         
60         
61         def faces_set_verts(face_ls):
62                 unique_verts = set()
63                 for f in face_ls:
64                         for v in f:
65                                 unique_verts.add(v.index)
66                 return unique_verts
67         
68         for f in me.faces:
69                 for corner, v in enumerate(f):
70                         i = v.index
71                         if vert_faces[i] != None:
72                                 vert_faces[i].append(f)
73                                 vert_faces_corner[i].append( corner )
74         
75         grid_data_ls = []
76         
77         for vi, face_ls in enumerate(vert_faces):
78                 if face_ls != None:
79                         if len(face_ls) == 4:
80                                 if face_ls[0].sel and face_ls[1].sel and face_ls[2].sel and face_ls[3].sel:                                     
81                                         # Support triangles also
82                                         unique_vert_count = len(faces_set_verts(face_ls))
83                                         quads = 0
84                                         for f in face_ls:
85                                                 if len(f) ==4:
86                                                         quads += 1
87                                         if unique_vert_count==5+quads: # yay we have a grid
88                                                 grid_data_ls.append( (vi, face_ls) )
89                         
90                         elif len(face_ls) == 3:
91                                 if face_ls[0].sel and face_ls[1].sel and face_ls[2].sel:
92                                         unique_vert_count = len(faces_set_verts(face_ls))
93                                         if unique_vert_count==4: # yay we have 3 triangles to make into a bigger triangle
94                                                 grid_data_ls.append( (vi, face_ls) )
95                                 
96         
97         
98         # Now sort out which grid faces to use
99         
100         
101         # This list will be used for items we can convert, vertex is key, faces are values
102         grid_data_dict = {}
103         
104         if not grid_data_ls:
105                 print "doing nothing"
106                 return
107         
108         # quick lookup for the opposing corner of a qiad
109         quad_diag_mapping = 2,3,0,1
110         
111         verts_used = [0] * len(me_verts) # 0 == untouched, 1==should touch, 2==touched
112         verts_used[grid_data_ls[0][0]] = 1 # start touching 1!
113         
114         # From the corner vert, get the 2 edges that are not the corner or its opposing vert, this edge will make a new face
115         quad_edge_mapping = (1,3), (2,0), (1,3), (0,2) # hi-low, low-hi order is intended
116         tri_edge_mapping = (1,2), (0,2), (0,1)
117         
118         done_somthing = True
119         while done_somthing:
120                 done_somthing = False
121                 grid_data_ls_index = -1
122                 
123                 for vi, face_ls in grid_data_ls:
124                         grid_data_ls_index += 1
125                         if len(face_ls) == 3:
126                                 grid_data_dict[vi] = face_ls
127                                 grid_data_ls.pop( grid_data_ls_index )
128                                 break
129                         elif len(face_ls) == 4:
130                                 # print vi
131                                 if verts_used[vi] == 1:
132                                         verts_used[vi] = 2 # dont look at this again.
133                                         done_somthing = True
134                                         
135                                         grid_data_dict[vi] = face_ls
136                                         
137                                         # Tag all faces verts as used
138                                         
139                                         for i, f in enumerate(face_ls):
140                                                 # i == face index on vert, needed to recall which corner were on.
141                                                 v_corner = vert_faces_corner[vi][i]
142                                                 fv =f.v
143                                                 
144                                                 if len(f) == 4:
145                                                         v_other = quad_diag_mapping[v_corner]
146                                                         # get the 2 other corners
147                                                         corner1, corner2 = quad_edge_mapping[v_corner]
148                                                         if verts_used[fv[v_other].index] == 0:
149                                                                 verts_used[fv[v_other].index] = 1 # TAG for touching!
150                                                 else:
151                                                         corner1, corner2 = tri_edge_mapping[v_corner]
152                                                 
153                                                 verts_used[fv[corner1].index] = 2 # Dont use these, they are 
154                                                 verts_used[fv[corner2].index] = 2
155                                                 
156                                                 
157                                         # remove this since we have used it.
158                                         grid_data_ls.pop( grid_data_ls_index )
159                                         
160                                         break
161                 
162                 if done_somthing == False:
163                         # See if there are any that have not even been tagged, (probably on a different island), then tag them.
164                         
165                         for vi, face_ls in grid_data_ls:
166                                 if verts_used[vi] == 0:
167                                         verts_used[vi] = 1
168                                         done_somthing = True
169                                         break
170         
171         
172         # Now we have all the areas we will fill, calculate corner triangles we need to fill in.
173         new_faces = []
174         quad_del_vt_map = (1,2,3), (0,2,3), (0,1,3), (0,1,2)
175         for vi, face_ls in grid_data_dict.iteritems():
176                 for i, f in enumerate(face_ls):
177                         if len(f) == 4:
178                                 # i == face index on vert, needed to recall which corner were on.
179                                 v_corner = vert_faces_corner[vi][i]
180                                 v_other = quad_diag_mapping[v_corner]
181                                 fv =f.v
182                                 
183                                 #print verts_used[fv[v_other].index]
184                                 #if verts_used[fv[v_other].index] != 2: # DOSNT WORK ALWAYS
185                                 
186                                 if 1: # THIS IS LAzY - some of these faces will be removed after adding.
187                                         # Ok we are removing half of this face, add the other half
188                                         
189                                         # This is probably slower
190                                         # new_faces.append( [fv[ii].index for ii in (0,1,2,3) if ii != v_corner ] )
191                                         
192                                         # do this instead
193                                         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]]) )
194         
195         del grid_data_ls
196         
197         
198         # me.sel = 0
199         def faceCombine4(vi, face_ls):
200                 edges = []
201                 
202                 for i, f in enumerate(face_ls):
203                         fv = f.v
204                         v_corner = vert_faces_corner[vi][i]
205                         if len(f)==4:   ed = quad_edge_mapping[v_corner]
206                         else:                   ed = tri_edge_mapping[v_corner]
207                         
208                         edges.append( [fv[ed[0]].index, fv[ed[1]].index] )
209                 
210                 # get the face from the edges 
211                 face = edges.pop()
212                 while len(face) != 4:
213                         # print len(edges), edges, face
214                         for ed_idx, ed in enumerate(edges):
215                                 if face[-1] == ed[0] and (ed[1] != face[0]):
216                                         face.append(ed[1])
217                                 elif face[-1] == ed[1] and (ed[0] != face[0]):
218                                         face.append(ed[0])
219                                 else:
220                                         continue
221                                 
222                                 edges.pop(ed_idx) # we used the edge alredy
223                                 break
224                 
225                 return face     
226         
227         for vi, face_ls in grid_data_dict.iteritems():
228                 if len(face_ls) == 4:
229                         new_faces.append( faceCombine4(vi, face_ls) )
230                         #pass
231                 if len(face_ls) == 3: # 3 triangles
232                         face = list(faces_set_verts(face_ls))
233                         face.remove(vi)
234                         new_faces.append( face )
235                         
236         
237         # Now remove verts surounded by 3 triangles
238         
239
240                 
241         # print new_edges
242         # me.faces.extend(new_faces, ignoreDups=True)
243         
244         '''
245         faces_remove = []
246         for vi, face_ls in grid_data_dict.iteritems():
247                 faces_remove.extend(face_ls)
248         '''
249         
250         orig_facelen = len(me.faces)
251         
252         orig_faces = list(me.faces)
253         me.faces.extend(new_faces, ignoreDups=True)
254         new_faces = list(me.faces)[len(orig_faces):]
255         
256         
257         
258         
259         
260         if me.faceUV:
261                 uvnames = me.getUVLayerNames()
262                 act_uvlay = me.activeUVLayer
263                 
264                 vert_faces_uvs =        []
265                 vert_faces_images =     []
266                         
267                         
268                 act_uvlay = me.activeUVLayer
269                 
270                 for uvlay in uvnames:
271                         me.activeUVLayer = uvlay
272                         vert_faces_uvs[:] = [None] * len(me.verts)
273                         vert_faces_images[:] = vert_faces_uvs[:]
274                         
275                         for i,f in enumerate(orig_faces):
276                                 img = f.image
277                                 fv = f.v
278                                 uv = f.uv
279                                 mat = f.mat
280                                 for i,v in enumerate(fv):
281                                         vi = v.index
282                                         vert_faces_uvs[vi] = uv[i] # no nice averaging
283                                         vert_faces_images[vi] = img
284                                         
285                                         
286                         # Now copy UVs across
287                         for f in new_faces:     
288                                 fi = [v.index for v in f.v]
289                                 f.image = vert_faces_images[fi[0]]
290                                 uv = f.uv
291                                 for i,vi in enumerate(fi):
292                                         uv[i][:] = vert_faces_uvs[vi]
293                 
294                 if len(me.materials) > 1:
295                         vert_faces_mats = [None] * len(me.verts)
296                         for i,f in enumerate(orig_faces):
297                                 mat = f.mat
298                                 for i,v in enumerate(f.v):
299                                         vi = v.index
300                                         vert_faces_mats[vi] = mat
301                                 
302                         # Now copy UVs across
303                         for f in new_faces:
304                                 print vert_faces_mats[f.v[0].index]
305                                 f.mat = vert_faces_mats[f.v[0].index]
306                                 
307         
308         me.verts.delete(grid_data_dict.keys())
309         
310         # me.faces.delete(1, faces_remove)
311         
312         if me.faceUV:
313                 me.activeUVLayer = act_uvlay
314         
315         me.calcNormals()
316
317 def main():
318         
319         # Gets the current scene, there can be many scenes in 1 blend file.
320         sce = bpy.data.scenes.active
321         
322         # Get the active object, there can only ever be 1
323         # and the active object is always the editmode object.
324         ob_act = sce.objects.active
325         
326         if not ob_act or ob_act.type != 'Mesh':
327                 BPyMessages.Error_NoMeshActive()
328                 return 
329         
330         is_editmode = Window.EditMode()
331         if is_editmode: Window.EditMode(0)
332         
333         Window.WaitCursor(1)
334         me = ob_act.getData(mesh=1) # old NMesh api is default
335         t = sys.time()
336         
337         # Run the mesh editing function
338         my_mesh_util(me)
339         
340         # Restore editmode if it was enabled
341         if is_editmode: Window.EditMode(1)
342         
343         # Timing the script is a good way to be aware on any speed hits when scripting
344         print 'My Script finished in %.2f seconds' % (sys.time()-t)
345         Window.WaitCursor(0)
346         
347         
348 # This lets you can import the script without running it
349 if __name__ == '__main__':
350         main()
351