more ui api changes.
[blender.git] / release / scripts / io / export_ply.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 import bpy
20
21 __author__ = "Bruce Merry"
22 __version__ = "0.93"
23 __bpydoc__ = """\
24 This script exports Stanford PLY files from Blender. It supports normals, 
25 colours, and texture coordinates per face or per vertex.
26 Only one mesh can be exported at a time.
27 """
28
29 # Copyright (C) 2004, 2005: Bruce Merry, bmerry@cs.uct.ac.za
30 #
31 # This program is free software; you can redistribute it and/or
32 # modify it under the terms of the GNU General Public License
33 # as published by the Free Software Foundation; either version 2
34 # of the License, or (at your option) any later version.
35 #
36 # This program is distributed in the hope that it will be useful,
37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
39 # GNU General Public License for more details.
40 #
41 # You should have received a copy of the GNU General Public License
42 # along with this program; if not, write to the Free Software Foundation,
43 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
44 # Vector rounding se we can use as keys
45 #
46 # Updated on Aug 11, 2008 by Campbell Barton
47 #    - added 'comment' prefix to comments - Needed to comply with the PLY spec.
48 #
49 # Updated on Jan 1, 2007 by Gabe Ghearing
50 #    - fixed normals so they are correctly smooth/flat
51 #    - fixed crash when the model doesn't have uv coords or vertex colors
52 #    - fixed crash when the model has vertex colors but doesn't have uv coords
53 #    - changed float32 to float and uint8 to uchar for compatibility
54 # Errata/Notes as of Jan 1, 2007
55 #    - script exports texture coords if they exist even if TexFace isn't selected (not a big deal to me)
56 #    - ST(R) should probably be renamed UV(T) like in most PLY files (importer needs to be updated to take either)
57 #
58 # Updated on Jan 3, 2007 by Gabe Ghearing
59 #    - fixed "sticky" vertex UV exporting
60 #    - added pupmenu to enable/disable exporting normals, uv coords, and colors
61 # Errata/Notes as of Jan 3, 2007
62 #    - ST(R) coords should probably be renamed UV(T) like in most PLY files (importer needs to be updated to take either)
63 #    - edges should be exported since PLY files support them
64 #    - code is getting spaghettish, it should be refactored...
65 #
66
67
68 def rvec3d(v):  return round(v[0], 6), round(v[1], 6), round(v[2], 6)
69 def rvec2d(v):  return round(v[0], 6), round(v[1], 6)
70
71 def write(filename, scene, ob, \
72                 EXPORT_APPLY_MODIFIERS= True,\
73                 EXPORT_NORMALS= True,\
74                 EXPORT_UV= True,\
75                 EXPORT_COLORS= True\
76         ):
77         
78         if not filename.lower().endswith('.ply'):
79                 filename += '.ply'
80         
81         if not ob:
82                 raise Exception("Error, Select 1 active object")
83                 return
84         
85         file = open(filename, 'w')
86         
87         
88         #EXPORT_EDGES = Draw.Create(0)
89         """
90         is_editmode = Blender.Window.EditMode()
91         if is_editmode:
92                 Blender.Window.EditMode(0, '', 0)
93         
94         Window.WaitCursor(1)
95         """
96         
97         #mesh = BPyMesh.getMeshFromObject(ob, None, EXPORT_APPLY_MODIFIERS, False, scn) # XXX
98         if EXPORT_APPLY_MODIFIERS:
99                 mesh = ob.create_mesh(True, 'PREVIEW')
100         else:
101                 mesh = ob.data
102         
103         if not mesh:
104                 raise ("Error, could not get mesh data from active object")
105                 return
106         
107         # mesh.transform(ob.matrixWorld) # XXX
108         
109         faceUV = len(mesh.uv_textures) > 0
110         vertexUV = len(mesh.sticky) > 0
111         vertexColors = len(mesh.vertex_colors) > 0
112         
113         if (not faceUV) and (not vertexUV):     EXPORT_UV = False
114         if not vertexColors:                                    EXPORT_COLORS = False
115         
116         if not EXPORT_UV:                                               faceUV = vertexUV = False
117         if not EXPORT_COLORS:                                   vertexColors = False
118                 
119         if faceUV:
120                 active_uv_layer = None
121                 for lay in mesh.uv_textures:
122                         if lay.active:
123                                 active_uv_layer= lay.data
124                                 break
125                 if not active_uv_layer:
126                         EXPORT_UV = False
127                         faceUV = None
128         
129         if vertexColors:
130                 active_col_layer = None
131                 for lay in mesh.vertex_colors:
132                         if lay.active:
133                                 active_col_layer= lay.data
134                 if not active_col_layer:
135                         EXPORT_COLORS = False
136                         vertexColors = None
137         
138         # incase
139         color = uvcoord = uvcoord_key = normal = normal_key = None
140         
141         mesh_verts = mesh.verts # save a lookup
142         ply_verts = [] # list of dictionaries
143         # vdict = {} # (index, normal, uv) -> new index
144         vdict = [{} for i in range(len(mesh_verts))]
145         ply_faces = [[] for f in range(len(mesh.faces))]
146         vert_count = 0
147         for i, f in enumerate(mesh.faces):
148                 
149                 
150                 smooth = f.smooth
151                 if not smooth:
152                         normal = tuple(f.normal)
153                         normal_key = rvec3d(normal)
154                 
155                 if faceUV:
156                         uv = active_uv_layer[i]
157                         uv = uv.uv1, uv.uv2, uv.uv3, uv.uv4 # XXX - crufty :/
158                 if vertexColors:
159                         col = active_col_layer[i]
160                         col = col.color1, col.color2, col.color3, col.color4
161                 
162                 f_verts= f.verts
163                 
164                 pf= ply_faces[i]
165                 for j, vidx in enumerate(f_verts):
166                         v = mesh_verts[vidx]
167                         
168                         if smooth:
169                                 normal=         tuple(v.normal)
170                                 normal_key = rvec3d(normal)
171                         
172                         if faceUV:
173                                 uvcoord=        uv[j][0], 1.0-uv[j][1]
174                                 uvcoord_key = rvec2d(uvcoord)
175                         elif vertexUV:
176                                 uvcoord=        v.uvco[0], 1.0-v.uvco[1]
177                                 uvcoord_key = rvec2d(uvcoord)
178                         
179                         if vertexColors:
180                                 color=          col[j]
181                                 color= int(color[0]*255.0), int(color[1]*255.0), int(color[2]*255.0)
182                         
183                         
184                         key = normal_key, uvcoord_key, color
185                         
186                         vdict_local = vdict[vidx]
187                         pf_vidx = vdict_local.get(key) # Will be None initially
188                         
189                         if pf_vidx == None: # same as vdict_local.has_key(key)
190                                 pf_vidx = vdict_local[key] = vert_count;
191                                 ply_verts.append((vidx, normal, uvcoord, color))
192                                 vert_count += 1
193                         
194                         pf.append(pf_vidx)
195         
196         file.write('ply\n')
197         file.write('format ascii 1.0\n')
198         version = "2.5" # Blender.Get('version')
199         file.write('comment Created by Blender3D %s - www.blender.org, source file: %s\n' % (version, bpy.data.filename.split('/')[-1].split('\\')[-1] ))
200         
201         file.write('element vertex %d\n' % len(ply_verts))
202         
203         file.write('property float x\n')
204         file.write('property float y\n')
205         file.write('property float z\n')
206         
207         # XXX 
208         """
209         if EXPORT_NORMALS:
210                 file.write('property float nx\n')
211                 file.write('property float ny\n')
212                 file.write('property float nz\n')
213         """
214         if EXPORT_UV:
215                 file.write('property float s\n')
216                 file.write('property float t\n')
217         if EXPORT_COLORS:
218                 file.write('property uchar red\n')
219                 file.write('property uchar green\n')
220                 file.write('property uchar blue\n')
221         
222         file.write('element face %d\n' % len(mesh.faces))
223         file.write('property list uchar uint vertex_indices\n')
224         file.write('end_header\n')
225
226         for i, v in enumerate(ply_verts):
227                 file.write('%.6f %.6f %.6f ' % tuple(mesh_verts[v[0]].co)) # co
228                 """
229                 if EXPORT_NORMALS:
230                         file.write('%.6f %.6f %.6f ' % v[1]) # no
231                 """
232                 if EXPORT_UV:                   file.write('%.6f %.6f ' % v[2]) # uv
233                 if EXPORT_COLORS:               file.write('%u %u %u' % v[3]) # col
234                 file.write('\n')
235         
236         for pf in ply_faces:
237                 if len(pf)==3:          file.write('3 %d %d %d\n' % tuple(pf))
238                 else:                           file.write('4 %d %d %d %d\n' % tuple(pf))
239         
240         file.close()
241         print("writing", filename, "done")
242         
243         if EXPORT_APPLY_MODIFIERS:
244                 bpy.data.remove_mesh(mesh)
245         
246         # XXX
247         """
248         if is_editmode:
249                 Blender.Window.EditMode(1, '', 0)
250         """
251
252 from bpy.props import *
253
254
255 class ExportPLY(bpy.types.Operator):
256         '''Export a single object as a stanford PLY with normals, colours and texture coordinates.'''
257         bl_idname = "export.ply"
258         bl_label = "Export PLY"
259         
260         # List of operator properties, the attributes will be assigned
261         # to the class instance from the operator settings before calling.
262         
263         
264         path = StringProperty(name="File Path", description="File path used for exporting the PLY file", maxlen= 1024, default= "")
265         use_modifiers = BoolProperty(name="Apply Modifiers", description="Apply Modifiers to the exported mesh", default= True)
266         use_normals = BoolProperty(name="Export Normals", description="Export Normals for smooth and hard shaded faces", default= True)
267         use_uvs = BoolProperty(name="Export UVs", description="Exort the active UV layer", default= True)
268         use_colors = BoolProperty(name="Export Vertex Colors", description="Exort the active vertex color layer", default= True)
269         
270         
271         def poll(self, context):
272                 return context.active_object != None
273         
274         def execute(self, context):
275                 # print("Selected: " + context.active_object.name)
276
277                 if not self.properties.path:
278                         raise Exception("filename not set")
279                         
280                 write(self.properties.path, context.scene, context.active_object,\
281                         EXPORT_APPLY_MODIFIERS = self.properties.use_modifiers,
282                         EXPORT_NORMALS = self.properties.use_normals,
283                         EXPORT_UV = self.properties.use_uvs,
284                         EXPORT_COLORS = self.properties.use_colors,
285                 )
286
287                 return ('FINISHED',)
288         
289         def invoke(self, context, event):       
290                 wm = context.manager
291                 wm.add_fileselect(self)
292                 return ('RUNNING_MODAL',)
293
294
295 bpy.ops.add(ExportPLY)
296
297 import dynamic_menu
298
299 def menu_func(self, context):
300     default_path = bpy.data.filename.replace(".blend", ".ply")
301     self.layout.operator(ExportPLY.bl_idname, text="Stanford (.ply)...").path = default_path
302
303 menu_item = dynamic_menu.add(bpy.types.INFO_MT_file_export, menu_func)
304
305 if __name__ == "__main__":
306         bpy.ops.export.ply(path="/tmp/test.ply")