diff options
Diffstat (limited to 'io_mesh_ply/export_ply.py')
-rw-r--r-- | io_mesh_ply/export_ply.py | 248 |
1 files changed, 127 insertions, 121 deletions
diff --git a/io_mesh_ply/export_ply.py b/io_mesh_ply/export_ply.py index db79e950..812aeb54 100644 --- a/io_mesh_ply/export_ply.py +++ b/io_mesh_ply/export_ply.py @@ -21,20 +21,12 @@ """ This script exports Stanford PLY files from Blender. It supports normals, colors, and texture coordinates per face or per vertex. -Only one mesh can be exported at a time. """ -import bpy -import os - -def save_mesh( - filepath, - mesh, - use_normals=True, - use_uv_coords=True, - use_colors=True, -): +def save_mesh(filepath, mesh, use_normals=True, use_uv_coords=True, use_colors=True): + import os + import bpy def rvec3d(v): return round(v[0], 6), round(v[1], 6), round(v[2], 6) @@ -42,47 +34,26 @@ def save_mesh( def rvec2d(v): return round(v[0], 6), round(v[1], 6) - file = open(filepath, "w", encoding="utf8", newline="\n") - fw = file.write - - has_uv = bool(mesh.uv_layers) - has_vcol = bool(mesh.vertex_colors) - - if not has_uv: + if use_uv_coords and mesh.uv_layers: + active_uv_layer = mesh.uv_layers.active.data + else: use_uv_coords = False - if not has_vcol: - use_colors = False - if not use_uv_coords: - has_uv = False - if not use_colors: - has_vcol = False - - if has_uv: - active_uv_layer = mesh.uv_layers.active - if not active_uv_layer: - use_uv_coords = False - has_uv = False - else: - active_uv_layer = active_uv_layer.data - - if has_vcol: - active_col_layer = mesh.vertex_colors.active - if not active_col_layer: - use_colors = False - has_vcol = False - else: - active_col_layer = active_col_layer.data + if use_colors and mesh.vertex_colors: + active_col_layer = mesh.vertex_colors.active.data + else: + use_colors = False # in case color = uvcoord = uvcoord_key = normal = normal_key = None - mesh_verts = mesh.vertices # save a lookup - ply_verts = [] # list of dictionaries + mesh_verts = mesh.vertices # vdict = {} # (index, normal, uv) -> new index vdict = [{} for i in range(len(mesh_verts))] + ply_verts = [] ply_faces = [[] for f in range(len(mesh.polygons))] vert_count = 0 + for i, f in enumerate(mesh.polygons): smooth = not use_normals or f.use_smooth @@ -90,12 +61,12 @@ def save_mesh( normal = f.normal[:] normal_key = rvec3d(normal) - if has_uv: + if use_uv_coords: uv = [ active_uv_layer[l].uv[:] for l in range(f.loop_start, f.loop_start + f.loop_total) ] - if has_vcol: + if use_colors: col = [ active_col_layer[l].color[:] for l in range(f.loop_start, f.loop_start + f.loop_total) @@ -109,11 +80,11 @@ def save_mesh( normal = v.normal[:] normal_key = rvec3d(normal) - if has_uv: + if use_uv_coords: uvcoord = uv[j][0], uv[j][1] uvcoord_key = rvec2d(uvcoord) - if has_vcol: + if use_colors: color = col[j] color = ( int(color[0] * 255.0), @@ -126,106 +97,141 @@ def save_mesh( vdict_local = vdict[vidx] pf_vidx = vdict_local.get(key) # Will be None initially - if pf_vidx is None: # same as vdict_local.has_key(key) + if pf_vidx is None: # Same as vdict_local.has_key(key) pf_vidx = vdict_local[key] = vert_count ply_verts.append((vidx, normal, uvcoord, color)) vert_count += 1 pf.append(pf_vidx) - fw("ply\n") - fw("format ascii 1.0\n") - fw("comment Created by Blender %s - " - "www.blender.org, source file: %r\n" % - (bpy.app.version_string, os.path.basename(bpy.data.filepath))) - - fw("element vertex %d\n" % len(ply_verts)) - - fw("property float x\n" - "property float y\n" - "property float z\n") - - if use_normals: - fw("property float nx\n" - "property float ny\n" - "property float nz\n") - if use_uv_coords: - fw("property float s\n" - "property float t\n") - if use_colors: - fw("property uchar red\n" - "property uchar green\n" - "property uchar blue\n" - "property uchar alpha\n") - - fw("element face %d\n" % len(mesh.polygons)) - fw("property list uchar uint vertex_indices\n") - fw("end_header\n") - - for i, v in enumerate(ply_verts): - fw("%.6f %.6f %.6f" % mesh_verts[v[0]].co[:]) # co + with open(filepath, "w", encoding="utf-8", newline="\n") as file: + fw = file.write + + # Header + # --------------------------- + + fw("ply\n") + fw("format ascii 1.0\n") + fw( + f"comment Created by Blender {bpy.app.version_string} - " + f"www.blender.org, source file: {os.path.basename(bpy.data.filepath)!r}\n" + ) + + fw(f"element vertex {len(ply_verts)}\n") + fw( + "property float x\n" + "property float y\n" + "property float z\n" + ) if use_normals: - fw(" %.6f %.6f %.6f" % v[1]) # no + fw( + "property float nx\n" + "property float ny\n" + "property float nz\n" + ) if use_uv_coords: - fw(" %.6f %.6f" % v[2]) # uv + fw( + "property float s\n" + "property float t\n" + ) if use_colors: - fw(" %u %u %u %u" % v[3]) # col - fw("\n") - - for pf in ply_faces: - # fw(f"{len(pf)} {' '.join(str(x) for x in pf)}\n") - fw("%d" % len(pf)) - for v in pf: - fw(" %d" % v) - fw("\n") - - file.close() - print("writing %r done" % filepath) + fw( + "property uchar red\n" + "property uchar green\n" + "property uchar blue\n" + "property uchar alpha\n" + ) + + fw(f"element face {len(mesh.polygons)}\n") + fw("property list uchar uint vertex_indices\n") + + fw("end_header\n") + + # Vertex data + # --------------------------- + + for i, v in enumerate(ply_verts): + fw("%.6f %.6f %.6f" % mesh_verts[v[0]].co[:]) + if use_normals: + fw(" %.6f %.6f %.6f" % v[1]) + if use_uv_coords: + fw(" %.6f %.6f" % v[2]) + if use_colors: + fw(" %u %u %u %u" % v[3]) + fw("\n") + + # Face data + # --------------------------- + + for pf in ply_faces: + fw(f"{len(pf)}") + for v in pf: + fw(f" {v}") + fw("\n") + + print(f"Writing {filepath!r} done") return {'FINISHED'} def save( - operator, - context, - filepath="", - use_mesh_modifiers=True, - use_normals=True, - use_uv_coords=True, - use_colors=True, - global_matrix=None + operator, + context, + filepath="", + use_selection=False, + use_mesh_modifiers=True, + use_normals=True, + use_uv_coords=True, + use_colors=True, + global_matrix=None ): - obj = context.active_object - - if global_matrix is None: - from mathutils import Matrix - global_matrix = Matrix() + import bpy + import bmesh if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') - mesh_owner_object = None - if use_mesh_modifiers and obj.modifiers: - depsgraph = context.evaluated_depsgraph_get() - mesh_owner_object = obj.evaluated_get(depsgraph) - mesh = mesh_owner_object.to_mesh() + if use_selection: + obs = context.selected_objects else: - mesh_owner_object = obj - mesh = mesh_owner_object.to_mesh() + obs = context.scene.objects - if not mesh: - raise Exception("Error, could not get mesh data from active object") + depsgraph = context.evaluated_depsgraph_get() + bm = bmesh.new() + + for ob in obs: + if use_mesh_modifiers: + ob_eval = ob.evaluated_get(depsgraph) + else: + ob_eval = ob + + try: + me = ob_eval.to_mesh() + except RuntimeError: + continue + + me.transform(ob.matrix_world) + bm.from_mesh(me) + ob_eval.to_mesh_clear() + + mesh = bpy.data.meshes.new("TMP PLY EXPORT") + bm.to_mesh(mesh) + bm.free() + + if global_matrix is not None: + mesh.transform(global_matrix) - mesh.transform(global_matrix @ obj.matrix_world) if use_normals: mesh.calc_normals() - ret = save_mesh(filepath, mesh, - use_normals=use_normals, - use_uv_coords=use_uv_coords, - use_colors=use_colors, - ) + ret = save_mesh( + filepath, + mesh, + use_normals=use_normals, + use_uv_coords=use_uv_coords, + use_colors=use_colors, + ) - mesh_owner_object.to_mesh_clear() + bpy.data.meshes.remove(mesh) return ret |