Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Rachinskiy <mikhail.rachinskiy@gmail.com>2022-01-03 23:24:29 +0300
committerMikhail Rachinskiy <mikhail.rachinskiy@gmail.com>2022-01-03 23:24:29 +0300
commit7f4c2d5e48657af8bf450da7d6a0587065cfa8b4 (patch)
tree909b94590ae6f5035948b4b8407b5025164e9d28 /io_mesh_ply
parent231186d45fe8998729e594a7683fd19d30ce8a2c (diff)
PLY: refactor exporter
New geometry traversal code, use bmesh data instead of mesh, much simpler logic. This also fixes two unreported issues with exporter: * Splitting faces when uv, vertex color or normals export options are enabled. * Disrupted vertex indices. On top of that there is a significant performance improvements, export Suzanne model with 4 subdivision levels: * Old 4.527s * New 1.029s
Diffstat (limited to 'io_mesh_ply')
-rw-r--r--io_mesh_ply/__init__.py2
-rw-r--r--io_mesh_ply/export_ply.py154
2 files changed, 49 insertions, 107 deletions
diff --git a/io_mesh_ply/__init__.py b/io_mesh_ply/__init__.py
index a3f08ebd..b75084a8 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"
+ "author": "Bruce Merry, Campbell Barton, Bastien Montagne, Mikhail Rachinsky",
"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 d90c0e49..a2acb1e8 100644
--- a/io_mesh_ply/export_ply.py
+++ b/io_mesh_ply/export_ply.py
@@ -23,23 +23,17 @@ 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 __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:
+def _write_binary(fw, ply_verts: list, ply_faces: list) -> None:
from struct import pack
# Vertex data
# ---------------------------
- for index, normal, uv_coords, color in ply_verts:
- fw(pack("<3f", *mesh_verts[index].co))
+ for v, normal, uv_coords, color in ply_verts:
+ fw(pack("<3f", *v.co))
if normal is not None:
fw(pack("<3f", *normal))
if uv_coords is not None:
@@ -51,20 +45,21 @@ def _write_binary(fw, ply_verts: list, ply_faces: list[_PLYface], mesh_verts: li
# ---------------------------
for pf in ply_faces:
- fw(pack(f"<B{pf.sides}I", pf.sides, *pf.verts))
+ length = len(pf)
+ fw(pack(f"<B{length}I", length, *pf))
-def _write_ascii(fw, ply_verts: list, ply_faces: list[_PLYface], mesh_verts: list) -> None:
+def _write_ascii(fw, ply_verts: list, ply_faces: list) -> None:
# Vertex data
# ---------------------------
- for index, normal, uv_coords, color in ply_verts:
- fw(b"%.6f %.6f %.6f" % mesh_verts[index].co[:])
+ for v, normal, uv_coords, color in ply_verts:
+ fw(b"%.6f %.6f %.6f" % v.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")
@@ -73,93 +68,45 @@ def _write_ascii(fw, ply_verts: list, ply_faces: list[_PLYface], mesh_verts: lis
# ---------------------------
for pf in ply_faces:
- fw(b"%d" % pf.sides)
- for index in pf.verts:
+ fw(b"%d" % len(pf))
+ for index in pf:
fw(b" %d" % index)
fw(b"\n")
-def save_mesh(filepath, mesh, use_ascii, use_normals, use_uv_coords, use_colors):
- import bpy
+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 rvec3d(v):
- return round(v[0], 6), round(v[1], 6), round(v[2], 6)
+ use_uv = use_uv and uv_lay is not None
+ use_color = use_color and col_lay is not None
+ uv = color = None
- def rvec2d(v):
- return round(v[0], 6), round(v[1], 6)
-
- 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
+ ply_verts = [None for _ in range(len(bm.verts))]
+ ply_faces = []
- # in case
- color = uvcoord = uvcoord_key = normal = normal_key = None
+ for f in bm.faces:
+ pf = []
+ ply_faces.append(pf)
- mesh_verts = mesh.vertices
- # vdict = {} # (index, normal, uv) -> new index
- vdict = [{} for _ in range(len(mesh_verts))]
- ply_verts = []
- ply_faces = []
- vert_count = 0
+ normal = None
+ if use_normals and not f.smooth:
+ normal = f.normal
- for f in mesh.polygons:
+ for loop in f.loops:
+ v = loop.vert
+ pf.append(v.index)
- 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)
+ 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])
- ply_faces.append(pf)
+ ply_verts[v.index] = (v, normal, uv, color)
+ v.tag = True
with open(filepath, "wb") as file:
fw = file.write
@@ -184,12 +131,12 @@ def save_mesh(filepath, mesh, use_ascii, use_normals, use_uv_coords, use_colors)
b"property float ny\n"
b"property float nz\n"
)
- if use_uv_coords:
+ if use_uv:
fw(
b"property float s\n"
b"property float t\n"
)
- if use_colors:
+ if use_color:
fw(
b"property uchar red\n"
b"property uchar green\n"
@@ -197,7 +144,7 @@ def save_mesh(filepath, mesh, use_ascii, use_normals, use_uv_coords, use_colors)
b"property uchar alpha\n"
)
- fw(b"element face %d\n" % len(mesh.polygons))
+ fw(b"element face %d\n" % len(ply_faces))
fw(b"property list uchar uint vertex_indices\n")
fw(b"end_header\n")
@@ -205,9 +152,9 @@ def save_mesh(filepath, mesh, use_ascii, use_normals, use_uv_coords, use_colors)
# ---------------------------
if use_ascii:
- _write_ascii(fw, ply_verts, ply_faces, mesh_verts)
+ _write_ascii(fw, ply_verts, ply_faces)
else:
- _write_binary(fw, ply_verts, ply_faces, mesh_verts)
+ _write_binary(fw, ply_verts, ply_faces)
def save(
@@ -222,7 +169,6 @@ def save(
global_matrix=None,
):
import time
- import bpy
import bmesh
t = time.time()
@@ -257,26 +203,22 @@ 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:
- mesh.transform(global_matrix)
+ bm.transform(global_matrix)
if use_normals:
- mesh.calc_normals()
+ bm.normal_update()
save_mesh(
filepath,
- mesh,
+ bm,
use_ascii,
use_normals,
use_uv_coords,
use_colors,
)
- bpy.data.meshes.remove(mesh)
+ bm.free()
t_delta = time.time() - t
print(f"Export completed {filepath!r} in {t_delta:.3f}")