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):
133 # XXX need face normals
137 normal_key = rvec3d(normal)
140 uv = active_uv_layer[i]
141 uv = uv.uv1, uv.uv2, uv.uv3, uv.uv4 # XXX - crufty :/
143 col = active_col_layer[i]
144 col = col.color1, col.color2, col.color3, col.color4
146 f_verts= list(f.verts)
147 if not f_verts[3]: f_verts.pop() # XXX face length should be 3/4, not always 4
150 for j, vidx in enumerate(f_verts):
155 normal_key = rvec3d(normal)
157 normal_key = None # XXX
160 uvcoord= uv[j][0], 1.0-uv[j][1]
161 uvcoord_key = rvec2d(uvcoord)
163 uvcoord= v.uvco[0], 1.0-v.uvco[1]
164 uvcoord_key = rvec2d(uvcoord)
168 color= int(color[0]*255.0), int(color[1]*255.0), int(color[2]*255.0)
171 key = normal_key, uvcoord_key, color
173 vdict_local = vdict[vidx]
174 pf_vidx = vdict_local.get(key) # Will be None initially
176 if pf_vidx == None: # same as vdict_local.has_key(key)
177 pf_vidx = vdict_local[key] = vert_count;
178 ply_verts.append((vidx, normal, uvcoord, color))
184 file.write('format ascii 1.0\n')
185 version = "2.5" # Blender.Get('version')
186 file.write('comment Created by Blender3D %s - www.blender.org, source file: %s\n' % (version, bpy.data.filename.split('/')[-1].split('\\')[-1] ))
188 file.write('element vertex %d\n' % len(ply_verts))
190 file.write('property float x\n')
191 file.write('property float y\n')
192 file.write('property float z\n')
197 file.write('property float nx\n')
198 file.write('property float ny\n')
199 file.write('property float nz\n')
202 file.write('property float s\n')
203 file.write('property float t\n')
205 file.write('property uchar red\n')
206 file.write('property uchar green\n')
207 file.write('property uchar blue\n')
209 file.write('element face %d\n' % len(mesh.faces))
210 file.write('property list uchar uint vertex_indices\n')
211 file.write('end_header\n')
213 for i, v in enumerate(ply_verts):
214 file.write('%.6f %.6f %.6f ' % tuple(mesh_verts[v[0]].co)) # co
217 file.write('%.6f %.6f %.6f ' % v[1]) # no
219 if EXPORT_UV: file.write('%.6f %.6f ' % v[2]) # uv
220 if EXPORT_COLORS: file.write('%u %u %u' % v[3]) # col
224 if len(pf)==3: file.write('3 %d %d %d\n' % tuple(pf))
225 else: file.write('4 %d %d %d %d\n' % tuple(pf))
228 print("writing", filename, "done")
230 if EXPORT_APPLY_MODIFIERS:
231 bpy.data.remove_mesh(mesh)
236 Blender.Window.EditMode(1, '', 0)
239 class EXPORT_OT_ply(bpy.types.Operator):
241 Operator documentatuon text, will be used for the operator tooltip and python docs.
243 __idname__ = "export.ply"
244 __label__ = "Export PLY"
246 # List of operator properties, the attributes will be assigned
247 # to the class instance from the operator settings before calling.
250 bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default= ""),
251 bpy.props.BoolProperty(attr="use_modifiers", name="Apply Modifiers", description="Apply Modifiers to the exported mesh", default= True),
252 bpy.props.BoolProperty(attr="use_normals", name="Export Normals", description="Export Normals for smooth and hard shaded faces", default= True),
253 bpy.props.BoolProperty(attr="use_uvs", name="Export UVs", description="Exort the active UV layer", default= True),
254 bpy.props.BoolProperty(attr="use_colors", name="Export Vertex Colors", description="Exort the active vertex color layer", default= True)
257 def poll(self, context):
259 return context.active_object != None
261 def execute(self, context):
262 # print("Selected: " + context.active_object.name)
264 if not self.filename:
265 raise Exception("filename not set")
267 write(self.filename, context.scene, context.active_object,\
268 EXPORT_APPLY_MODIFIERS = self.use_modifiers,
269 EXPORT_NORMALS = self.use_normals,
270 EXPORT_UV = self.use_uvs,
271 EXPORT_COLORS = self.use_colors,
276 def invoke(self, context, event):
278 wm.add_fileselect(self.__operator__)
279 return ('RUNNING_MODAL',)
282 bpy.ops.add(EXPORT_OT_ply)
284 if __name__ == "__main__":
285 bpy.ops.EXPORT_OT_ply(filename="/tmp/test.ply")