Integrated Freestyle to rendering pipeline
[blender.git] / release / scripts / bpymodules / meshtools.py
1 # $Id$
2 #
3 # +---------------------------------------------------------+
4 # | Copyright (c) 2001 Anthony D'Agostino                   |
5 # | http://www.redrival.com/scorpius                        |
6 # | scorpius@netzero.com                                    |
7 # | September 28, 2002                                      |
8 # +---------------------------------------------------------+
9 # | Common Functions & Global Variables For All IO Modules  |
10 # +---------------------------------------------------------+
11
12 # ***** BEGIN GPL LICENSE BLOCK *****
13 #
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software Foundation,
26 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27 #
28 # ***** END GPL LICENCE BLOCK *****
29
30 import Blender
31 import sys
32
33 show_progress = 1                       # Set to 0 for faster performance
34 average_vcols = 1                       # Off for per-face, On for per-vertex
35 overwrite_mesh_name = 0         # Set to 0 to increment object-name version
36
37 blender_version = Blender.Get('version')
38 blender_version_str = `blender_version`[0] + '.' + `blender_version`[1:]
39
40 try:
41         import operator
42 except:
43         msg = "Error: you need a full Python install to run this script."
44         meshtools.print_boxed(msg)
45         Blender.Draw.PupMenu("ERROR%t|"+msg)
46
47 # =================================
48 # === Append Faces To Face List ===
49 # =================================
50 def append_faces(mesh, faces, facesuv, uvcoords):
51         for i in xrange(len(faces)):
52                 if not i%100 and show_progress: Blender.Window.DrawProgressBar(float(i)/len(faces), "Generating Faces")
53                 numfaceverts=len(faces[i])
54                 if numfaceverts == 2: #This is not a face is an edge
55                         if mesh.edges == None:  #first run
56                                 mesh.addEdgeData()
57                         #rev_face = revert(cur_face)
58                         i1 = faces[i][0]
59                         i2 = faces[i][1]
60                         ee = mesh.addEdge(mesh.verts[i1],mesh.verts[i2])
61                         ee.flag |= Blender.NMesh.EdgeFlags.EDGEDRAW
62                         ee.flag |= Blender.NMesh.EdgeFlags.EDGERENDER
63                 elif numfaceverts in [3,4]:                             # This face is a triangle or quad
64                         face = Blender.NMesh.Face()
65                         for j in xrange(numfaceverts):
66                                 index = faces[i][j]
67                                 face.v.append(mesh.verts[index])
68                                 if len(uvcoords) > 1:
69                                         uvidx = facesuv[i][j]
70                                         face.uv.append(uvcoords[uvidx])
71                                         face.mode = 0
72                                         face.col = [Blender.NMesh.Col()]*4
73                         mesh.faces.append(face)
74                 else:                                                           # Triangulate n-sided convex polygon.
75                         a, b, c = 0, 1, 2                               # Indices of first triangle.
76                         for j in xrange(numfaceverts-2): # Number of triangles in polygon.
77                                 face = Blender.NMesh.Face()
78                                 face.v.append(mesh.verts[faces[i][a]])
79                                 face.v.append(mesh.verts[faces[i][b]])
80                                 face.v.append(mesh.verts[faces[i][c]])
81                                 b = c; c += 1
82                                 mesh.faces.append(face)
83                 #face.smooth = 1
84
85 # ===================================
86 # === Append Verts to Vertex List ===
87 # ===================================
88 def append_verts(mesh, verts, normals):
89         #print "Number of normals:", len(normals)
90         #print "Number of verts  :", len(verts)
91         for i in xrange(len(verts)):
92                 if not i%100 and show_progress: Blender.Window.DrawProgressBar(float(i)/len(verts), "Generating Verts")
93                 x, y, z = verts[i]
94                 mesh.verts.append(Blender.NMesh.Vert(x, y, z))
95                 if normals:
96                         mesh.verts[i].no[0] = normals[i][0]
97                         mesh.verts[i].no[1] = normals[i][1]
98                         mesh.verts[i].no[2] = normals[i][2]
99
100 # ===========================
101 # === Create Blender Mesh ===
102 # ===========================
103 def create_mesh(verts, faces, objname, facesuv=[], uvcoords=[], normals=[]):
104         if normals: normal_flag = 0
105         else: normal_flag = 1
106         mesh = Blender.NMesh.GetRaw()
107         append_verts(mesh, verts, normals)
108         append_faces(mesh, faces, facesuv, uvcoords)
109         if not overwrite_mesh_name:
110                 objname = versioned_name(objname)
111         ob= Blender.NMesh.PutRaw(mesh, objname, normal_flag)    # Name the Mesh
112         ob.name= objname                # Name the Object
113         Blender.Redraw()
114
115 # ==============================
116 # === Increment Name Version ===
117 # ==============================
118 def versioned_name(objname):
119         existing_names = []
120         for object in Blender.Object.Get():
121                 existing_names.append(object.name)
122                 existing_names.append(object.getData(name_only=1))
123         if objname in existing_names: # don't over-write other names
124                 try:
125                         name, ext = objname.split('.')
126                 except ValueError:
127                         name, ext = objname, ''
128                 try:
129                         num = int(ext)
130                         root = name
131                 except ValueError:
132                         root = objname
133                 for i in xrange(1, 1000):
134                         objname = "%s.%03d" % (root, i)
135                         if objname not in existing_names:
136                                 break
137         return objname
138
139 # ===========================
140 # === Print Text In A Box ===
141 # ===========================
142 def print_boxed(text):
143         lines = text.splitlines()
144         maxlinelen = max(map(len, lines))
145         if sys.platform[:3] == "win":
146                 print chr(218)+chr(196) + chr(196)*maxlinelen + chr(196)+chr(191)
147                 for line in lines:
148                         print chr(179) + ' ' + line.ljust(maxlinelen) + ' ' + chr(179)
149                 print chr(192)+chr(196) + chr(196)*maxlinelen + chr(196)+chr(217)
150         else:
151                 print '+-' + '-'*maxlinelen + '-+'
152                 for line in lines: print '| ' + line.ljust(maxlinelen) + ' |'
153                 print '+-' + '-'*maxlinelen + '-+'
154         print '\a\r', # beep when done
155
156 # ===============================================
157 # === Get euler angles from a rotation matrix ===
158 # ===============================================
159 def mat2euler(mat):
160         angle_y = -math.asin(mat[0][2])
161         c = math.cos(angle_y)
162         if math.fabs(c) > 0.005:
163                 angle_x = math.atan2(mat[1][2]/c, mat[2][2]/c)
164                 angle_z = math.atan2(mat[0][1]/c, mat[0][0]/c)
165         else:
166                 angle_x = 0.0
167                 angle_z = -math.atan2(mat[1][0], mat[1][1])
168         return (angle_x, angle_y, angle_z)
169
170 # ==========================
171 # === Transpose A Matrix ===
172 # ==========================
173 def transpose(A):
174         S = len(A)
175         T = len(A[0])
176         B = [[None]*S for i in xrange(T)]
177         for i in xrange(T):
178                 for j in xrange(S):
179                         B[i][j] = A[j][i]
180         return B
181
182 # =======================
183 # === Apply Transform ===
184 # =======================
185 def apply_transform(vertex, matrix):
186         x, y, z = vertex
187         xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]
188         xcomponent = x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc
189         ycomponent = x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc
190         zcomponent = x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc
191         vertex = [xcomponent, ycomponent, zcomponent]
192         return vertex
193
194 # =========================
195 # === Has Vertex Colors ===
196 # =========================
197 def has_vertex_colors(mesh):
198         # My replacement/workaround for hasVertexColours()
199         # The docs say:
200         # "Warning: If a mesh has both vertex colours and textured faces,
201         # this function will return False. This is due to the way Blender
202         # deals internally with the vertex colours array (if there are
203         # textured faces, it is copied to the textured face structure and
204         # the original array is freed/deleted)."
205         try:
206                 return mesh.faces[0].col[0]
207         except:
208                 return 0
209
210 # ===========================
211 # === Generate Edge Table ===
212 # ===========================
213 def generate_edgetable(mesh):
214         edge_table = {}
215         numfaces = len(mesh.faces)
216
217         for i in xrange(numfaces):
218                 if not i%100 and show_progress:
219                         Blender.Window.DrawProgressBar(float(i)/numfaces, "Generating Edge Table")
220                 if len(mesh.faces[i].v) == 4:   # Process Quadrilaterals
221                         generate_entry_from_quad(mesh, i, edge_table)
222                 elif len(mesh.faces[i].v) == 3: # Process Triangles
223                         generate_entry_from_tri(mesh, i, edge_table)
224                 else:                                                   # Skip This Face
225                         print "Face #", i, "was skipped."
226
227         # === Sort Edge_Table Keys & Add Edge Indices ===
228         i = 0
229         keys = edge_table.keys()
230         keys.sort()
231         for key in keys:
232                 edge_table[key][6] = i
233                 i += 1
234
235         # === Replace Tuples With Indices ===
236         for key in keys:
237                 for i in [2,3,4,5]:
238                         if edge_table.has_key(edge_table[key][i]):
239                                 edge_table[key][i] = edge_table[edge_table[key][i]][6]
240                         else:
241                                 keyrev = (edge_table[key][i][1], edge_table[key][i][0])
242                                 edge_table[key][i] = edge_table[keyrev][6]
243
244         return edge_table
245
246 # ================================
247 # === Generate Entry From Quad ===
248 # ================================
249 def generate_entry_from_quad(mesh, i, edge_table):
250         vertex4, vertex3, vertex2, vertex1 = mesh.faces[i].v
251
252         if has_vertex_colors(mesh):
253                 vcolor4, vcolor3, vcolor2, vcolor1 = mesh.faces[i].col
254                 Acol = (vcolor1.r/255.0, vcolor1.g/255.0, vcolor1.b/255.0)
255                 Bcol = (vcolor2.r/255.0, vcolor2.g/255.0, vcolor2.b/255.0)
256                 Ccol = (vcolor3.r/255.0, vcolor3.g/255.0, vcolor3.b/255.0)
257                 Dcol = (vcolor4.r/255.0, vcolor4.g/255.0, vcolor4.b/255.0)
258
259         # === verts are upper case, edges are lower case ===
260         A, B, C, D = vertex1.index, vertex2.index, vertex3.index, vertex4.index
261         a, b, c, d = (A, B), (B, C), (C, D), (D, A)
262
263         if edge_table.has_key((B, A)):
264                 edge_table[(B, A)][1] = i
265                 edge_table[(B, A)][4] = d
266                 edge_table[(B, A)][5] = b
267                 if has_vertex_colors(mesh): edge_table[(B, A)][8] = Bcol
268         else:
269                 if has_vertex_colors(mesh):
270                         edge_table[(A, B)] = [i, None, d, b, None, None, None, Bcol, None]
271                 else:
272                         edge_table[(A, B)] = [i, None, d, b, None, None, None]
273
274         if edge_table.has_key((C, B)):
275                 edge_table[(C, B)][1] = i
276                 edge_table[(C, B)][4] = a
277                 edge_table[(C, B)][5] = c
278                 if has_vertex_colors(mesh): edge_table[(C, B)][8] = Ccol
279         else:
280                 if has_vertex_colors(mesh):
281                         edge_table[(B, C)] = [i, None, a, c, None, None, None, Ccol, None]
282                 else:
283                         edge_table[(B, C)] = [i, None, a, c, None, None, None]
284
285         if edge_table.has_key((D, C)):
286                 edge_table[(D, C)][1] = i
287                 edge_table[(D, C)][4] = b
288                 edge_table[(D, C)][5] = d
289                 if has_vertex_colors(mesh): edge_table[(D, C)][8] = Dcol
290         else:
291                 if has_vertex_colors(mesh):
292                         edge_table[(C, D)] = [i, None, b, d, None, None, None, Dcol, None]
293                 else:
294                         edge_table[(C, D)] = [i, None, b, d, None, None, None]
295
296         if edge_table.has_key((A, D)):
297                 edge_table[(A, D)][1] = i
298                 edge_table[(A, D)][4] = c
299                 edge_table[(A, D)][5] = a
300                 if has_vertex_colors(mesh): edge_table[(A, D)][8] = Acol
301         else:
302                 if has_vertex_colors(mesh):
303                         edge_table[(D, A)] = [i, None, c, a, None, None, None, Acol, None]
304                 else:
305                         edge_table[(D, A)] = [i, None, c, a, None, None, None]
306
307 # ====================================
308 # === Generate Entry From Triangle ===
309 # ====================================
310 def generate_entry_from_tri(mesh, i, edge_table):
311         vertex3, vertex2, vertex1 = mesh.faces[i].v
312
313         if has_vertex_colors(mesh):
314                 vcolor3, vcolor2, vcolor1, _vcolor4_ = mesh.faces[i].col
315                 Acol = (vcolor1.r/255.0, vcolor1.g/255.0, vcolor1.b/255.0)
316                 Bcol = (vcolor2.r/255.0, vcolor2.g/255.0, vcolor2.b/255.0)
317                 Ccol = (vcolor3.r/255.0, vcolor3.g/255.0, vcolor3.b/255.0)
318
319         # === verts are upper case, edges are lower case ===
320         A, B, C = vertex1.index, vertex2.index, vertex3.index
321         a, b, c = (A, B), (B, C), (C, A)
322
323         if edge_table.has_key((B, A)):
324                 edge_table[(B, A)][1] = i
325                 edge_table[(B, A)][4] = c
326                 edge_table[(B, A)][5] = b
327                 if has_vertex_colors(mesh): edge_table[(B, A)][8] = Bcol
328         else:
329                 if has_vertex_colors(mesh):
330                         edge_table[(A, B)] = [i, None, c, b, None, None, None, Bcol, None]
331                 else:
332                         edge_table[(A, B)] = [i, None, c, b, None, None, None]
333
334         if edge_table.has_key((C, B)):
335                 edge_table[(C, B)][1] = i
336                 edge_table[(C, B)][4] = a
337                 edge_table[(C, B)][5] = c
338                 if has_vertex_colors(mesh): edge_table[(C, B)][8] = Ccol
339         else:
340                 if has_vertex_colors(mesh):
341                         edge_table[(B, C)] = [i, None, a, c, None, None, None, Ccol, None]
342                 else:
343                         edge_table[(B, C)] = [i, None, a, c, None, None, None]
344
345         if edge_table.has_key((A, C)):
346                 edge_table[(A, C)][1] = i
347                 edge_table[(A, C)][4] = b
348                 edge_table[(A, C)][5] = a
349                 if has_vertex_colors(mesh): edge_table[(A, C)][8] = Acol
350         else:
351                 if has_vertex_colors(mesh):
352                         edge_table[(C, A)] = [i, None, b, a, None, None, None, Acol, None]
353                 else:
354                         edge_table[(C, A)] = [i, None, b, a, None, None, None]
355