From 4c7e2b5a9e5e208d9aa2d7547606477b1a9b2a2c Mon Sep 17 00:00:00 2001 From: Mikhail Rachinskiy Date: Tue, 4 Jan 2022 23:17:02 +0400 Subject: Revert "PLY: refactor exporter" This reverts commit 7f4c2d5e48657af8bf450da7d6a0587065cfa8b4. Issues with unit tests. --- io_mesh_ply/__init__.py | 2 +- io_mesh_ply/export_ply.py | 154 +++++++++++++++++++++++++++++++--------------- 2 files changed, 107 insertions(+), 49 deletions(-) diff --git a/io_mesh_ply/__init__.py b/io_mesh_ply/__init__.py index b75084a8..a3f08ebd 100644 --- a/io_mesh_ply/__init__.py +++ b/io_mesh_ply/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "Stanford PLY format", - "author": "Bruce Merry, Campbell Barton, Bastien Montagne, Mikhail Rachinsky", + "author": "Bruce Merry, Campbell Barton", "Bastien Montagne" "version": (2, 1, 0), "blender": (2, 90, 0), "location": "File > Import/Export", diff --git a/io_mesh_ply/export_ply.py b/io_mesh_ply/export_ply.py index a2acb1e8..d90c0e49 100644 --- a/io_mesh_ply/export_ply.py +++ b/io_mesh_ply/export_ply.py @@ -23,17 +23,23 @@ This script exports Stanford PLY files from Blender. It supports normals, colors, and texture coordinates per face or per vertex. """ -import bpy +class _PLYface: + __slots__ = "verts", "sides" -def _write_binary(fw, ply_verts: list, ply_faces: list) -> None: + def __init__(self, sides: int) -> None: + self.verts = [] + self.sides = sides + + +def _write_binary(fw, ply_verts: list, ply_faces: list[_PLYface], mesh_verts: list) -> None: from struct import pack # Vertex data # --------------------------- - for v, normal, uv_coords, color in ply_verts: - fw(pack("<3f", *v.co)) + for index, normal, uv_coords, color in ply_verts: + fw(pack("<3f", *mesh_verts[index].co)) if normal is not None: fw(pack("<3f", *normal)) if uv_coords is not None: @@ -45,21 +51,20 @@ def _write_binary(fw, ply_verts: list, ply_faces: list) -> None: # --------------------------- for pf in ply_faces: - length = len(pf) - fw(pack(f" None: +def _write_ascii(fw, ply_verts: list, ply_faces: list[_PLYface], mesh_verts: list) -> None: # Vertex data # --------------------------- - for v, normal, uv_coords, color in ply_verts: - fw(b"%.6f %.6f %.6f" % v.co[:]) + for index, normal, uv_coords, color in ply_verts: + fw(b"%.6f %.6f %.6f" % mesh_verts[index].co[:]) if normal is not None: - fw(b" %.6f %.6f %.6f" % normal[:]) + fw(b" %.6f %.6f %.6f" % normal) if uv_coords is not None: - fw(b" %.6f %.6f" % uv_coords[:]) + fw(b" %.6f %.6f" % uv_coords) if color is not None: fw(b" %u %u %u %u" % color) fw(b"\n") @@ -68,45 +73,93 @@ def _write_ascii(fw, ply_verts: list, ply_faces: list) -> None: # --------------------------- for pf in ply_faces: - fw(b"%d" % len(pf)) - for index in pf: + fw(b"%d" % pf.sides) + for index in pf.verts: fw(b" %d" % index) fw(b"\n") -def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): - uv_lay = bm.loops.layers.uv.active - col_lay = bm.loops.layers.color.active +def save_mesh(filepath, mesh, use_ascii, use_normals, use_uv_coords, use_colors): + import bpy - use_uv = use_uv and uv_lay is not None - use_color = use_color and col_lay is not None - uv = color = None + def rvec3d(v): + return round(v[0], 6), round(v[1], 6), round(v[2], 6) - ply_verts = [None for _ in range(len(bm.verts))] - ply_faces = [] + def rvec2d(v): + return round(v[0], 6), round(v[1], 6) - for f in bm.faces: - pf = [] - ply_faces.append(pf) + if use_uv_coords and mesh.uv_layers: + active_uv_layer = mesh.uv_layers.active.data + else: + use_uv_coords = False + + if use_colors and mesh.vertex_colors: + active_col_layer = mesh.vertex_colors.active.data + else: + use_colors = False - normal = None - if use_normals and not f.smooth: - normal = f.normal + # in case + color = uvcoord = uvcoord_key = normal = normal_key = None - for loop in f.loops: - v = loop.vert - pf.append(v.index) + mesh_verts = mesh.vertices + # vdict = {} # (index, normal, uv) -> new index + vdict = [{} for _ in range(len(mesh_verts))] + ply_verts = [] + ply_faces = [] + vert_count = 0 + + for f in mesh.polygons: - if not v.tag: - if use_normals and normal is None: - normal = v.normal - if use_uv: - uv = loop[uv_lay].uv - if use_color: - color = tuple(int(x * 255.0) for x in loop[col_lay]) + if use_normals: + smooth = f.use_smooth + if not smooth: + normal = f.normal[:] + normal_key = rvec3d(normal) + + if use_uv_coords: + uv = [ + active_uv_layer[l].uv[:] + for l in range(f.loop_start, f.loop_start + f.loop_total) + ] + if use_colors: + col = [ + active_col_layer[l].color[:] + for l in range(f.loop_start, f.loop_start + f.loop_total) + ] + + pf = _PLYface(f.loop_total) + for i, vidx in enumerate(f.vertices): + v = mesh_verts[vidx] + + if use_normals and smooth: + normal = v.normal[:] + normal_key = rvec3d(normal) + + if use_uv_coords: + uvcoord = uv[i][0], uv[i][1] + uvcoord_key = rvec2d(uvcoord) + + if use_colors: + color = col[i] + color = ( + int(color[0] * 255.0), + int(color[1] * 255.0), + int(color[2] * 255.0), + int(color[3] * 255.0), + ) + key = normal_key, uvcoord_key, color + + 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) + pf_vidx = vdict_local[key] = vert_count + ply_verts.append((vidx, normal, uvcoord, color)) + vert_count += 1 + + pf.verts.append(pf_vidx) - ply_verts[v.index] = (v, normal, uv, color) - v.tag = True + ply_faces.append(pf) with open(filepath, "wb") as file: fw = file.write @@ -131,12 +184,12 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): b"property float ny\n" b"property float nz\n" ) - if use_uv: + if use_uv_coords: fw( b"property float s\n" b"property float t\n" ) - if use_color: + if use_colors: fw( b"property uchar red\n" b"property uchar green\n" @@ -144,7 +197,7 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): b"property uchar alpha\n" ) - fw(b"element face %d\n" % len(ply_faces)) + fw(b"element face %d\n" % len(mesh.polygons)) fw(b"property list uchar uint vertex_indices\n") fw(b"end_header\n") @@ -152,9 +205,9 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): # --------------------------- if use_ascii: - _write_ascii(fw, ply_verts, ply_faces) + _write_ascii(fw, ply_verts, ply_faces, mesh_verts) else: - _write_binary(fw, ply_verts, ply_faces) + _write_binary(fw, ply_verts, ply_faces, mesh_verts) def save( @@ -169,6 +222,7 @@ def save( global_matrix=None, ): import time + import bpy import bmesh t = time.time() @@ -203,22 +257,26 @@ def save( if (ngons := [f for f in bm.faces if len(f.verts) > 255]): bmesh.ops.triangulate(bm, faces=ngons) + mesh = bpy.data.meshes.new("TMP PLY EXPORT") + bm.to_mesh(mesh) + bm.free() + if global_matrix is not None: - bm.transform(global_matrix) + mesh.transform(global_matrix) if use_normals: - bm.normal_update() + mesh.calc_normals() save_mesh( filepath, - bm, + mesh, use_ascii, use_normals, use_uv_coords, use_colors, ) - bm.free() + bpy.data.meshes.remove(mesh) t_delta = time.time() - t print(f"Export completed {filepath!r} in {t_delta:.3f}") -- cgit v1.2.3