Initial revision
[blender.git] / intern / python / modules / Blender / Mesh.py
1 """The Blender Mesh module
2
3   This module provides routines for more extensive mesh manipulation.
4   Later, this Mesh type will also allow interactive access (like in
5   EditMode).
6   In the Publisher, Ngons will also be supported (and converted to
7   triangles on mesh.update(). The following code demonstrates
8   creation of an Ngon.
9
10   Example::
11
12         from Blender import Mesh, Object, Scene
13
14         m = Mesh.New()  # new empty mesh
15         vlist = []
16         vlist.append(m.addVert((-0.0, -1.0, 0.0)))
17         vlist.append(m.addVert((1.0, 0.0, 0.0)))
18         vlist.append(m.addVert((1.0, 1.0, 0.0)))
19         vlist.append(m.addVert((0.0, 3.0, 0.0)))
20         vlist.append(m.addVert((-1.0, 2.0, 0.0)))
21         vlist.append(m.addVert((-3.0, 1.0, 0.0)))
22         vlist.append(m.addVert((-3.0, 3.0, 0.0)))
23         vlist.append(m.addVert((-4.0, 3.0, 0.0)))
24         vlist.append(m.addVert((-4.0, 0.0, 0.0)))
25
26         f = m.addFace(vlist)
27
28         # do some calculations: top project vertex coordinates to
29         # UV coordinates and normalize them to the square [0.0, 1.0]*[0.0, 1.0]
30
31         uvlist = map(lambda x: (x.co[0], x.co[1]), vlist)
32         maxx = max(map(lambda x: x[0], uvlist))
33         maxy = max(map(lambda x: x[1], uvlist))
34         minx = min(map(lambda x: x[0], uvlist))
35         miny = min(map(lambda x: x[1], uvlist))
36
37         len = max((maxx - minx), (maxy - miny))
38         offx = -minx / len
39         offy = -miny / len
40
41         f.uv = map(lambda x: (x[0]/len + offx, x[1]/len + offy), uvlist)  # assign UV coordinates by 'top' projection
42
43         m.update()  # update and triangulate mesh
44
45         ob = Object.New('Mesh')    # create new Object
46         ob.link(m)                 # link mesh data
47         sc = Scene.getCurrent()    # get current Scene
48         sc.link(ob)                # link Object to scene
49 """
50
51 from Blender.Types import NMFaceType
52 import Blender.Material as Material
53
54 from _Blender import NMesh as _NMesh
55
56 FACEFLAGS = _NMesh.Const
57 DEFAULTFLAGS = FACEFLAGS.LIGHT + FACEFLAGS.DYNAMIC
58
59 import shadow
60
61 def makeFace(f):
62         face = _NMesh.Face()
63         for v in f:
64                 face.v.append(v)
65                 face.uv.append((v.uvco[0], v.uvco[1]))
66         return face
67
68 def toTriangles(ngon):
69         from utils import tesselation
70         # This should be a Publisher only feature...once the tesselation
71         # is improved. The GLU tesselator of Mesa < 4.0 is crappy...
72         if len(ngon.uv) == len(ngon.v):
73                 i = 0
74                 for v in ngon.v:
75                         v.uvco = ngon.uv[i]
76                         i += 1
77
78         return tesselation.NgonAsTriangles(ngon, makeFace) # return triangles
79
80 def Color(r, g, b, a = 1.0):
81         return _NMesh.Col(255 * r, 255 * g, 255 * b, 255 * a)
82
83 class Vert:  #shadow NMVert class for the tesselator
84         """Vertex wrapper class
85 This class emulates a float coordinate vector triple
86 """
87         def __init__(self):
88                 self.vert = None
89                 self.uv = []
90         def __len__(self):
91                 return 3
92         def __setitem__(self, i, val):
93                 self.vert[i] = val
94         def __getitem__(self, i):
95                 return self.vert.co[i]
96
97 class Face:
98         """Face wrapper class
99 This class emulates a list of vertex references
100 """
101         def __init__(self, vlist):
102                 self.v= vlist
103                 self.uv = []
104
105         def __len__(self):
106                 return len(self.v)
107
108         def __setitem__(self, i, val):
109                 self.v[i] = val
110
111         def __getitem__(self, i):
112                 return self.v[i]
113
114 # override:
115
116 Vert = _NMesh.Vert
117 Face = _NMesh.Face
118
119 class rawMesh:
120         """Wrapper for raw Mesh data"""
121         def __init__(self, object = None):
122                 if object:
123                         self._object = object
124                 else:   
125                         self._object = _NMesh.GetRaw()
126
127                 self.flags = DEFAULTFLAGS
128                 self.smooth = 0
129                 self.recalc_normals = 1
130                 self.faces = self._object.faces[:]
131
132         def __getattr__(self, name):
133                 if name == 'vertices':
134                         return self._object.verts
135                 elif name == 'has_col':
136                         return self._object.hasVertexColours()
137                 elif name == 'has_uv':
138                         return self._object.hasFaceUV()
139                 else:
140                         return getattr(self._object, name)
141
142         def __repr__(self):
143                 return "Mesh: %d faces, %d vertices" % (len(self.faces), len(self.verts))
144
145         def hasFaceUV(self, true = None):
146                 """Sets the per-face UV texture flag, if 'true' specified (either
147                 0 or 1). Returns the texture flag in any case."""
148                 if true == None:
149                         return self._object.hasFaceUV()
150                 return self._object.hasFaceUV(true)
151
152         def hasVertexUV(self, true = None):
153                 """Sets the per-vertex UV texture flag, if 'true' specified (either
154                 0 or 1). Returns the texture flag in any case."""
155                 if true == None:
156                         return self._object.hasVertexUV()
157                 return self._object.hasVertexUV(true)
158
159         def hasVertexColours(self, true = None):
160                 """Sets the per-face UV texture flag, if 'true' specified (either
161                 0 or 1). Returns the texture flag in any case."""
162                 if true == None:
163                         return self._object.hasVertexColours()
164                 return self._object.hasVertexColours(true)
165
166         def addVert(self, v):
167                 """Adds a vertex to the mesh and returns a reference to it. 'v' can
168 be a float triple or any data type emulating a sequence, containing the
169 coordinates of the vertex. Note that the returned value references an
170 *owned* vertex"""
171                 vert = _NMesh.Vert(v[0], v[1], v[2]) 
172                 self._object.verts.append(vert)
173                 return vert
174
175         def addFace(self, vlist, flags = None, makedefaultUV = 0):
176                 """Adds a face to the mesh and returns a reference to it. 'vlist' 
177 must be a list of vertex references returned by addVert(). 
178 Note that the returned value references an *owned* face"""
179                 if type(vlist) == NMFaceType:
180                         face = vlist
181                 else:   
182                         n = len(vlist)
183                         face = _NMesh.Face(vlist)
184                         if makedefaultUV:
185                                 face.uv = defaultUV[:n]
186
187                 self.faces.append(face) 
188                 # turn on default flags:
189                 if not flags:
190                         face.mode = self.flags
191                 else:
192                         face.mode = flags
193                 return face
194
195         def update(self):
196                 """Updates the mesh datablock in Blender"""
197                 o = self._object
198                 o = self._object
199                 o.faces = []
200                 smooth = self.smooth
201                 for f in self.faces:
202                         if len(f) > 4: #it's a NGON 
203                                 faces = toTriangles(f)
204                                 for nf in faces:
205                                         nf.smooth = smooth
206                                         o.faces.append(nf)
207                         else:
208                                 o.faces.append(f)
209                 o.update()
210
211         def link(self, material):
212                 """Link material 'material' with the mesh. Note that a mesh can
213 currently have up to 16 materials, which are referenced by 
214 Face().materialIndex"""
215                 mats = self._object.materials
216                 if material in mats:
217                         print "material already assigned to mesh"
218                         return
219                 mats.append(material._object)
220
221         def unlink(self, material):
222                 """Unlink (remove) material 'material' from the mesh. Note
223 that the material indices per face need to be updated."""
224                 self._object.materials.remove(material._object)
225
226         def setMaterials(self, materials = []):
227                 """Sets materials. 'materials' must be a list of valid material objects
228 Note that a mesh can currently have up to 16 materials, which are referenced 
229 by Face().materialIndex"""
230
231                 self._object.materials = (map(lambda x: x._object, materials))
232
233         def getMaterials(self, materials = []):
234                 """Returns materials assigned to the mesh"""
235                 return shadow._List(self._object.materials, Material.Material)
236
237 def New():
238         return rawMesh()
239
240 def get(name = None):
241         """If 'name' given, the Mesh 'name' is returned if existing, 'None' otherwise."""
242         if name:
243                 ob = _NMesh.GetRaw(name)
244                 if ob:
245                         return rawMesh(ob)
246                 else:
247                         return None
248         else:
249                 raise SystemError, "get() for Meshes is not yet supported"
250