Moved ngon creating function from obj_import into BPyMesh so other importers can...
[blender.git] / release / scripts / bpymodules / BPyMesh.py
1 import Blender
2
3 def meshWeight2Dict(me):
4         ''' Takes a mesh and return its group names and a list of dicts, one dict per vertex.
5         using the group as a key and a float value for the weight.
6         These 2 lists can be modified and then used with dict2MeshWeight to apply the changes.
7         '''
8         
9         vWeightDict= [dict() for i in xrange(len(me.verts))] # Sync with vertlist.
10         
11         # Clear the vert group.
12         groupNames= me.getVertGroupNames()
13                 
14         for group in groupNames:
15                 for index, weight in me.getVertsFromGroup(group, 1): # (i,w)  tuples.
16                         vWeightDict[index][group]= weight
17         
18         # removed this because me may be copying teh vertex groups.
19         #for group in groupNames:
20         #       me.removeVertGroup(group)
21         
22         return groupNames, vWeightDict
23
24
25 def dict2MeshWeight(me, groupNames, vWeightDict):
26         ''' Takes a list of groups and a list of vertex Weight dicts as created by meshWeight2Dict
27         and applys it to the mesh.'''
28         
29         if len(vWeightDict) != len(me.verts):
30                 raise 'Error, Lists Differ in size, do not modify your mesh.verts before updating the weights'
31         
32         # Clear the vert group.
33         currentGroupNames= me.getVertGroupNames()
34         for group in currentGroupNames:
35                 if group not in groupNames:
36                         me.removeVertGroup(group) # messes up the active group.
37                 else:
38                         me.removeVertsFromGroup(group)
39         
40         # Add clean unused vert groupNames back
41         currentGroupNames= me.getVertGroupNames()
42         for group in groupNames:
43                 if group not in currentGroupNames:
44                         me.addVertGroup(group)
45         
46         add_ = Blender.Mesh.AssignModes.ADD
47         
48         vertList= [None]
49         for i, v in enumerate(me.verts):
50                 vertList[0]= i
51                 for group, weight in vWeightDict[i].iteritems():
52                         try:
53                                 me.assignVertsToGroup(group, vertList, min(1, max(0, weight)), add_)
54                         except:
55                                 pass # vert group is not used anymore.
56         
57         me.update()
58
59
60
61 def getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None):
62         '''
63         ob - the object that you want to get the mesh from
64         container_mesh - a Blender.Mesh type mesh that is reused to avoid a new datablock per call to getMeshFromObject
65         apply_modifiers - if enabled, subsurf bones etc. will be applied to the returned mesh. disable to get a copy of the mesh.
66         vgroup - For mesh objects only, apply the vgroup to the the copied mesh. (slower)
67         scn - Scene type. avoids getting the current scene each time getMeshFromObject is called.
68         
69         Returns Mesh or None
70         '''
71         
72         if not scn:
73                 scn= Blender.Scene.GetCurrent()
74         if not container_mesh:
75                 mesh = Blender.Mesh.New()       
76         else:
77                 mesh= container_mesh
78                 mesh.verts= None
79         
80         
81         type = ob.getType()
82         dataname = ob.getData(1)
83         tempob= None
84         if apply_modifiers or type != 'Mesh':
85                 try:
86                         mesh.getFromObject(ob.name)
87                 except:
88                         return None
89         
90         else:
91                 '''
92                 Dont apply modifiers, copy the mesh. 
93                 So we can transform the data. its easiest just to get a copy of the mesh. 
94                 '''
95                 tempob= Blender.Object.New('Mesh')
96                 tempob.shareFrom(ob)
97                 scn.link(tempob)
98                 mesh.getFromObject(tempob.name)
99                 scn.unlink(tempob)
100         
101         if type == 'Mesh':
102                 tempMe = ob.getData(mesh=1)
103                 mesh.materials = tempMe.materials
104                 mesh.degr = tempMe.degr
105                 try: mesh.mode = tempMe.mode # Mesh module needs fixing.
106                 except: pass
107                 if vgroups:
108                         if tempob==None:
109                                 tempob= Blender.Object.New('Mesh')
110                         tempob.link(mesh)
111                         try:
112                                 # Copy the influences if possible.
113                                 groupNames, vWeightDict= meshWeight2Dict(tempMe)
114                                 dict2MeshWeight(mesh, groupNames, vWeightDict)
115                         except:
116                                 # if the modifier changes the vert count then it messes it up for us.
117                                 pass
118                 
119         else:
120                 try:
121                         # Will only work for curves!!
122                         # Text- no material access in python interface.
123                         # Surf- no python interface
124                         # MBall- no material access in python interface.
125                         
126                         data = ob.getData()
127                         materials = data.getMaterials()
128                         mesh.materials = materials
129                         print 'assigning materials for non mesh'
130                 except:
131                         print 'Cant assign materials to', type
132         
133         return mesh
134
135 type_tuple= type( (0,) )
136 type_list= type( [] )
137 def ngon(from_data, indices):
138         '''
139         takes a polyline of indices (fgon)
140         and returns a list of face indicie lists.
141         Designed to be used for importers that need indices for an fgon to create from existing verts.
142         
143         from_data is either a mesh, or a list/tuple of vectors.
144         '''
145         Mesh= Blender.Mesh
146         Window= Blender.Window
147         Scene= Blender.Scene
148         Object= Blender.Object
149         
150         if len(indices) < 4:
151                 return [indices]
152         temp_mesh_name= '~NGON_TEMP~'
153         is_editmode= Window.EditMode()
154         if is_editmode:
155                 Window.EditMode(0)
156         try:
157                 temp_mesh = Mesh.Get(temp_mesh_name)
158                 if temp_mesh.users!=0:
159                         temp_mesh = Mesh.New(temp_mesh_name)
160         except:
161                 temp_mesh = Mesh.New(temp_mesh_name)
162                 
163         if type(from_data) in (type_tuple, type_list):
164                 # From a list/tuple of vectors
165                 temp_mesh.verts.extend( [from_data[i] for i in indices] )
166                 temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
167         else:
168                 # From a mesh
169                 temp_mesh.verts.extend( [from_data.verts[i].co for i in indices] )
170                 temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
171         
172         
173         oldmode = Mesh.Mode()
174         Mesh.Mode(Mesh.SelectModes['VERTEX'])
175         for v in temp_mesh.verts:
176                 v.sel= 1
177         
178         # Must link to scene
179         scn= Scene.GetCurrent()
180         temp_ob= Object.New('Mesh')
181         temp_ob.link(temp_mesh)
182         scn.link(temp_ob)
183         temp_mesh.fill()
184         scn.unlink(temp_ob)
185         Mesh.Mode(oldmode)
186         
187         new_indices= [ [v.index for v in f.v]  for f in temp_mesh.faces ]
188         
189         if not new_indices: # JUST DO A FAN, Cant Scanfill
190                 print 'Warning Cannot scanfill!- Fallback on a triangle fan.'
191                 new_indices = [ [indices[0], indices[i-1], indices[i]] for i in xrange(2, len(indices)) ]
192         else:
193                 # Use real scanfill.
194                 # See if its flipped the wrong way.
195                 flip= None
196                 for fi in new_indices:
197                         if flip != None:
198                                 break
199                         for i, vi in enumerate(fi):
200                                 if vi==0 and fi[i-1]==1:
201                                         flip= False
202                                         break
203                                 elif vi==1 and fi[i-1]==0:
204                                         flip= True
205                                         break
206                 
207                 if not flip:
208                         for fi in new_indices:
209                                 fi.reverse()
210         
211         if is_editmode:
212                 Window.EditMode(1)
213                 
214         # Save some memory and forget about the verts.
215         # since we cant unlink the mesh.
216         temp_mesh.verts= None 
217         
218         return new_indices
219         
220
221
222 # EG
223 '''
224 scn= Scene.GetCurrent()
225 me = scn.getActiveObject().getData(mesh=1)
226 ind= [v.index for v in me.verts if v.sel] # Get indices
227
228 indices = ngon(me, ind) # fill the ngon.
229
230 # Extand the faces to show what the scanfill looked like.
231 print len(indices)
232 me.faces.extend([[me.verts[ii] for ii in i] for i in indices])
233 '''
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253 # NMesh wrapper
254 Vector= Blender.Mathutils.Vector
255 class NMesh(object):
256         __slots__= 'verts', 'faces', 'edges', 'faceUV', 'materials', 'realmesh'
257         def __init__(self, mesh):
258                 '''
259                 This is an NMesh wrapper that
260                 mesh is an Mesh as returned by Blender.Mesh.New()
261                 This class wraps NMesh like access into Mesh
262                 
263                 Running NMesh.update() - with this wrapper,
264                 Will update the realmesh.
265                 '''
266                 self.verts= []
267                 self.faces= []
268                 self.edges= []
269                 self.faceUV= False
270                 self.materials= []
271                 self.realmesh= mesh
272         
273         def addFace(self, nmf):
274                 self.faces.append(nmf)
275         
276         def Face(self, v=[]):
277                 return NMFace(v)
278         def Vert(self, x,y,z):
279                 return NMVert(x,y,z)
280         
281         def hasFaceUV(self, flag):
282                 if flag:
283                         self.faceUV= True
284                 else:
285                         self.faceUV= False
286         
287         def addMaterial(self, mat):
288                 self.materials.append(mat)
289         
290         def update(self, recalc_normals=False): # recalc_normals is dummy
291                 mesh= self.realmesh
292                 mesh.verts= None # Clears the 
293                 
294                 # Add in any verts from faces we may have not added.
295                 for nmf in self.faces:
296                         for nmv in nmf.v:
297                                 if nmv.index==-1:
298                                         nmv.index= len(self.verts)
299                                         self.verts.append(nmv)
300                                         
301                 
302                 mesh.verts.extend([nmv.co for nmv in self.verts])
303                 for i, nmv in enumerate(self.verts):
304                         nmv.index= i
305                         mv= mesh.verts[i]
306                         mv.sel= nmv.sel
307                 
308                 good_faces= [nmf for nmf in self.faces if len(nmf.v) in (3,4)]
309                 #print len(good_faces), 'AAA'
310                 
311                 
312                 #mesh.faces.extend([nmf.v for nmf in self.faces])
313                 mesh.faces.extend([[mesh.verts[nmv.index] for nmv in nmf.v] for nmf in good_faces])
314                 if len(mesh.faces):
315                         if self.faceUV:
316                                 mesh.faceUV= 1
317                         
318                         #for i, nmf in enumerate(self.faces):
319                         for i, nmf in enumerate(good_faces):
320                                 mf= mesh.faces[i]
321                                 if self.faceUV:
322                                         if len(nmf.uv) == len(mf.v):
323                                                 mf.uv= [Vector(uv[0], uv[1]) for uv in nmf.uv]
324                                         if len(nmf.col) == len(mf.v):
325                                                 for c, i in enumerate(mf.col):
326                                                         c.r, c.g, c.b= nmf.col[i].r, nmf.col[i].g, nmf.col[i].b
327                                         if nmf.image:
328                                                 mf.image= nmf.image
329                 
330                 mesh.materials= self.materials[:16]
331
332 class NMVert(object):
333         __slots__= 'co', 'index', 'no', 'sel', 'uvco'
334         def __init__(self, x,y,z):
335                 self.co= Vector(x,y,z)
336                 self.index= None # set on appending.
337                 self.no= Vector(0,0,1) # dummy
338                 self.sel= 0
339                 self.uvco= None
340 class NMFace(object):
341         __slots__= 'col', 'flag', 'hide', 'image', 'mat', 'materialIndex', 'mode', 'normal',\
342         'sel', 'smooth', 'transp', 'uv', 'v'
343         
344         def __init__(self, v=[]):
345                 self.col= []
346                 self.flag= 0
347                 self.hide= 0
348                 self.image= None
349                 self.mat= 0 # materialIndex needs support too.
350                 self.mode= 0
351                 self.normal= Vector(0,0,1)
352                 self.uv= []
353                 self.sel= 0
354                 self.smooth= 0
355                 self.transp= 0
356                 self.uv= []
357                 self.v= [] # a list of nmverts.
358         
359 class NMCol(object):
360         __slots__ = 'r', 'g', 'b', 'a'
361         def __init__(self):
362                 self.r= 255
363                 self.g= 255
364                 self.b= 255
365                 self.a= 255