3 __author__ = "Bruce Merry"
6 This script exports Stanford PLY files from Blender. It supports normals,
7 colours, and texture coordinates per face or per vertex.
8 Only one mesh can be exported at a time.
11 # Copyright (C) 2004, 2005: Bruce Merry, bmerry@cs.uct.ac.za
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; either version 2
16 # of the License, or (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software Foundation,
25 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 # Vector rounding se we can use as keys
28 # Updated on Aug 11, 2008 by Campbell Barton
29 # - added 'comment' prefix to comments - Needed to comply with the PLY spec.
31 # Updated on Jan 1, 2007 by Gabe Ghearing
32 # - fixed normals so they are correctly smooth/flat
33 # - fixed crash when the model doesn't have uv coords or vertex colors
34 # - fixed crash when the model has vertex colors but doesn't have uv coords
35 # - changed float32 to float and uint8 to uchar for compatibility
36 # Errata/Notes as of Jan 1, 2007
37 # - script exports texture coords if they exist even if TexFace isn't selected (not a big deal to me)
38 # - ST(R) should probably be renamed UV(T) like in most PLY files (importer needs to be updated to take either)
40 # Updated on Jan 3, 2007 by Gabe Ghearing
41 # - fixed "sticky" vertex UV exporting
42 # - added pupmenu to enable/disable exporting normals, uv coords, and colors
43 # Errata/Notes as of Jan 3, 2007
44 # - ST(R) coords should probably be renamed UV(T) like in most PLY files (importer needs to be updated to take either)
45 # - edges should be exported since PLY files support them
46 # - code is getting spaghettish, it should be refactored...
50 def rvec3d(v): return round(v[0], 6), round(v[1], 6), round(v[2], 6)
51 def rvec2d(v): return round(v[0], 6), round(v[1], 6)
53 def write(filename, scene, ob, \
54 EXPORT_APPLY_MODIFIERS= True,\
55 EXPORT_NORMALS= True,\
60 if not filename.lower().endswith('.ply'):
64 raise Exception("Error, Select 1 active object")
67 file = open(filename, 'wb')
70 #EXPORT_EDGES = Draw.Create(0)
72 is_editmode = Blender.Window.EditMode()
74 Blender.Window.EditMode(0, '', 0)
79 #mesh = BPyMesh.getMeshFromObject(ob, None, EXPORT_APPLY_MODIFIERS, False, scn) # XXX
80 if EXPORT_APPLY_MODIFIERS:
81 mesh = ob.create_render_mesh(scene)
86 raise ("Error, could not get mesh data from active object")
89 # mesh.transform(ob.matrixWorld) # XXX
91 faceUV = len(mesh.uv_layers) > 0
92 vertexUV = len(mesh.sticky) > 0
93 vertexColors = len(mesh.vcol_layers) > 0
95 if (not faceUV) and (not vertexUV): EXPORT_UV = False
96 if not vertexColors: EXPORT_COLORS = False
98 if not EXPORT_UV: faceUV = vertexUV = False
99 if not EXPORT_COLORS: vertexColors = False
102 active_uv_layer = None
103 for lay in mesh.uv_layers:
105 active_uv_layer= lay.data
107 if not active_uv_layer:
112 active_col_layer = None
113 for lay in mesh.vcol_layers:
115 active_col_layer= lay.data
116 if not active_col_layer:
117 EXPORT_COLORS = False
121 color = uvcoord = uvcoord_key = normal = normal_key = None
123 mesh_verts = mesh.verts # save a lookup
124 ply_verts = [] # list of dictionaries
125 # vdict = {} # (index, normal, uv) -> new index
126 vdict = [{} for i in xrange(len(mesh_verts))]
127 ply_faces = [[] for f in xrange(len(mesh.faces))]
129 for i, f in enumerate(mesh.faces):
134 normal = tuple(f.normal)
135 normal_key = rvec3d(normal)
138 uv = active_uv_layer[i]
139 uv = uv.uv1, uv.uv2, uv.uv3, uv.uv4 # XXX - crufty :/
141 col = active_col_layer[i]
142 col = col.color1, col.color2, col.color3, col.color4
144 f_verts= list(f.verts)
145 if not f_verts[3]: f_verts.pop() # XXX face length should be 3/4, not always 4
148 for j, vidx in enumerate(f_verts):
152 normal= tuple(v.normal)
153 normal_key = rvec3d(normal)
156 uvcoord= uv[j][0], 1.0-uv[j][1]
157 uvcoord_key = rvec2d(uvcoord)
159 uvcoord= v.uvco[0], 1.0-v.uvco[1]
160 uvcoord_key = rvec2d(uvcoord)
164 color= int(color[0]*255.0), int(color[1]*255.0), int(color[2]*255.0)
167 key = normal_key, uvcoord_key, color
169 vdict_local = vdict[vidx]
170 pf_vidx = vdict_local.get(key) # Will be None initially
172 if pf_vidx == None: # same as vdict_local.has_key(key)
173 pf_vidx = vdict_local[key] = vert_count;
174 ply_verts.append((vidx, normal, uvcoord, color))
180 file.write('format ascii 1.0\n')
181 version = "2.5" # Blender.Get('version')
182 file.write('comment Created by Blender3D %s - www.blender.org, source file: %s\n' % (version, bpy.data.filename.split('/')[-1].split('\\')[-1] ))
184 file.write('element vertex %d\n' % len(ply_verts))
186 file.write('property float x\n')
187 file.write('property float y\n')
188 file.write('property float z\n')
193 file.write('property float nx\n')
194 file.write('property float ny\n')
195 file.write('property float nz\n')
198 file.write('property float s\n')
199 file.write('property float t\n')
201 file.write('property uchar red\n')
202 file.write('property uchar green\n')
203 file.write('property uchar blue\n')
205 file.write('element face %d\n' % len(mesh.faces))
206 file.write('property list uchar uint vertex_indices\n')
207 file.write('end_header\n')
209 for i, v in enumerate(ply_verts):
210 file.write('%.6f %.6f %.6f ' % tuple(mesh_verts[v[0]].co)) # co
213 file.write('%.6f %.6f %.6f ' % v[1]) # no
215 if EXPORT_UV: file.write('%.6f %.6f ' % v[2]) # uv
216 if EXPORT_COLORS: file.write('%u %u %u' % v[3]) # col
220 if len(pf)==3: file.write('3 %d %d %d\n' % tuple(pf))
221 else: file.write('4 %d %d %d %d\n' % tuple(pf))
224 print("writing", filename, "done")
226 if EXPORT_APPLY_MODIFIERS:
227 bpy.data.remove_mesh(mesh)
232 Blender.Window.EditMode(1, '', 0)
235 class EXPORT_OT_ply(bpy.types.Operator):
237 Operator documentatuon text, will be used for the operator tooltip and python docs.
239 __idname__ = "export.ply"
240 __label__ = "Export PLY"
242 # List of operator properties, the attributes will be assigned
243 # to the class instance from the operator settings before calling.
246 bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default= ""),
247 bpy.props.BoolProperty(attr="use_modifiers", name="Apply Modifiers", description="Apply Modifiers to the exported mesh", default= True),
248 bpy.props.BoolProperty(attr="use_normals", name="Export Normals", description="Export Normals for smooth and hard shaded faces", default= True),
249 bpy.props.BoolProperty(attr="use_uvs", name="Export UVs", description="Exort the active UV layer", default= True),
250 bpy.props.BoolProperty(attr="use_colors", name="Export Vertex Colors", description="Exort the active vertex color layer", default= True)
253 def poll(self, context):
255 return context.active_object != None
257 def execute(self, context):
258 # print("Selected: " + context.active_object.name)
260 if not self.filename:
261 raise Exception("filename not set")
263 write(self.filename, context.scene, context.active_object,\
264 EXPORT_APPLY_MODIFIERS = self.use_modifiers,
265 EXPORT_NORMALS = self.use_normals,
266 EXPORT_UV = self.use_uvs,
267 EXPORT_COLORS = self.use_colors,
272 def invoke(self, context, event):
274 wm.add_fileselect(self.__operator__)
275 return ('RUNNING_MODAL',)
278 bpy.ops.add(EXPORT_OT_ply)
280 if __name__ == "__main__":
281 bpy.ops.EXPORT_OT_ply(filename="/tmp/test.ply")