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>2020-07-22 06:28:43 +0300
committerMikhail Rachinskiy <mikhail.rachinskiy@gmail.com>2020-07-22 06:28:43 +0300
commit85173fa5263a34924154f26c0c457dd5e79621ea (patch)
tree0361a4ec00d77194730ad6a4e4cd28a4732864a8
parentdbb4c80f22f7004c4f72e22b3a47e69f3cd782d2 (diff)
PLY: binary export
Thanks to Adrian Vogelsgesang (@vogelsgesang) and his binary ply export implementation proposal D4252. I did not reuse any code from his patch, but it gave me a good starting point as I had no idea how to work with binary data. In this commit: * Implement export to binary little-endian file format. * Remove information about blend filename from exported file, it has no purpose. * Binary is the default format from now on. I cannot see any reason to implement big-endian option, if there is, please let me know. Below you will find performance comparison between ASCII and binary formats. Test geometry: * Verts 379 000 * Faces 378 000 Export: * ASCII (old) 8.0 sec * ASCII 3.0 sec * Binary 2.4 sec Note: difference between old and new ASCII export is due to avoiding unnecessary normal claculation when export normals is disabled. Import: ASCII 5.75 sec Binary 4.9 sec File sizes: * ASCII 20.6 MB * Binary 10.4 MB
-rw-r--r--io_mesh_ply/__init__.py24
-rw-r--r--io_mesh_ply/export_ply.py154
-rw-r--r--io_mesh_ply/import_ply.py16
3 files changed, 120 insertions, 74 deletions
diff --git a/io_mesh_ply/__init__.py b/io_mesh_ply/__init__.py
index 835ae60e..2cfb09c3 100644
--- a/io_mesh_ply/__init__.py
+++ b/io_mesh_ply/__init__.py
@@ -21,9 +21,9 @@
bl_info = {
"name": "Stanford PLY format",
"author": "Bruce Merry, Campbell Barton",
- "version": (1, 1, 0),
- "blender": (2, 82, 0),
- "location": "File > Import-Export",
+ "version": (2, 0, 0),
+ "blender": (2, 90, 0),
+ "location": "File > Import/Export",
"description": "Import-Export PLY mesh data with UVs and vertex colors",
"doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/mesh_ply.html",
"support": 'OFFICIAL',
@@ -107,6 +107,10 @@ class ExportPLY(bpy.types.Operator, ExportHelper):
filename_ext = ".ply"
filter_glob: StringProperty(default="*.ply", options={'HIDDEN'})
+ use_ascii: BoolProperty(
+ name="ASCII",
+ description="Export using ASCII file format, otherwise use binary",
+ )
use_selection: BoolProperty(
name="Selection Only",
description="Export selected objects only",
@@ -164,10 +168,20 @@ class ExportPLY(bpy.types.Operator, ExportHelper):
filepath = self.filepath
filepath = bpy.path.ensure_ext(filepath, self.filename_ext)
- return export_ply.save(self, context, **keywords)
+ export_ply.save(context, **keywords)
+
+ return {'FINISHED'}
def draw(self, context):
- pass
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ sfile = context.space_data
+ operator = sfile.active_operator
+
+ col = layout.column(heading="Format")
+ col.prop(operator, "use_ascii")
class PLY_PT_export_include(bpy.types.Panel):
diff --git a/io_mesh_ply/export_ply.py b/io_mesh_ply/export_ply.py
index 812aeb54..060b3d02 100644
--- a/io_mesh_ply/export_ply.py
+++ b/io_mesh_ply/export_ply.py
@@ -24,8 +24,55 @@ colors, and texture coordinates per face or per vertex.
"""
-def save_mesh(filepath, mesh, use_normals=True, use_uv_coords=True, use_colors=True):
- import os
+def _write_binary(fw, ply_verts, ply_faces, mesh_verts):
+ from struct import pack
+
+ # Vertex data
+ # ---------------------------
+
+ 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:
+ fw(pack("<2f", *uv_coords))
+ if color is not None:
+ fw(pack("<4B", *color))
+
+ # Face data
+ # ---------------------------
+
+ for pf in ply_faces:
+ length = len(pf)
+ fw(pack("<B%dI" % length, length, *pf))
+
+
+def _write_ascii(fw, ply_verts, ply_faces, mesh_verts):
+
+ # Vertex data
+ # ---------------------------
+
+ 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)
+ if uv_coords is not None:
+ fw(b" %.6f %.6f" % uv_coords)
+ if color is not None:
+ fw(b" %u %u %u %u" % color)
+ fw(b"\n")
+
+ # Face data
+ # ---------------------------
+
+ for pf in ply_faces:
+ 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 rvec3d(v):
@@ -56,10 +103,11 @@ def save_mesh(filepath, mesh, use_normals=True, use_uv_coords=True, use_colors=T
for i, f in enumerate(mesh.polygons):
- smooth = not use_normals or f.use_smooth
- if not smooth:
- normal = f.normal[:]
- normal_key = rvec3d(normal)
+ if use_normals:
+ smooth = f.use_smooth
+ if not smooth:
+ normal = f.normal[:]
+ normal_key = rvec3d(normal)
if use_uv_coords:
uv = [
@@ -76,7 +124,7 @@ def save_mesh(filepath, mesh, use_normals=True, use_uv_coords=True, use_colors=T
for j, vidx in enumerate(f.vertices):
v = mesh_verts[vidx]
- if smooth:
+ if use_normals and smooth:
normal = v.normal[:]
normal_key = rvec3d(normal)
@@ -104,90 +152,72 @@ def save_mesh(filepath, mesh, use_normals=True, use_uv_coords=True, use_colors=T
pf.append(pf_vidx)
- with open(filepath, "w", encoding="utf-8", newline="\n") as file:
+ with open(filepath, "wb") as file:
fw = file.write
+ file_format = b"ascii" if use_ascii else b"binary_little_endian"
# 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(b"ply\n")
+ fw(b"format %s 1.0\n" % file_format)
+ fw(b"comment Created by Blender %s - www.blender.org\n" % bpy.app.version_string.encode("utf-8"))
- fw(f"element vertex {len(ply_verts)}\n")
+ fw(b"element vertex %d\n" % len(ply_verts))
fw(
- "property float x\n"
- "property float y\n"
- "property float z\n"
+ b"property float x\n"
+ b"property float y\n"
+ b"property float z\n"
)
if use_normals:
fw(
- "property float nx\n"
- "property float ny\n"
- "property float nz\n"
+ b"property float nx\n"
+ b"property float ny\n"
+ b"property float nz\n"
)
if use_uv_coords:
fw(
- "property float s\n"
- "property float t\n"
+ b"property float s\n"
+ b"property float t\n"
)
if use_colors:
fw(
- "property uchar red\n"
- "property uchar green\n"
- "property uchar blue\n"
- "property uchar alpha\n"
+ b"property uchar red\n"
+ b"property uchar green\n"
+ b"property uchar blue\n"
+ b"property uchar alpha\n"
)
- fw(f"element face {len(mesh.polygons)}\n")
- fw("property list uchar uint vertex_indices\n")
-
- fw("end_header\n")
+ fw(b"element face %d\n" % len(mesh.polygons))
+ fw(b"property list uchar uint vertex_indices\n")
+ fw(b"end_header\n")
- # Vertex data
+ # Geometry
# ---------------------------
- 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'}
+ if use_ascii:
+ _write_ascii(fw, ply_verts, ply_faces, mesh_verts)
+ else:
+ _write_binary(fw, ply_verts, ply_faces, mesh_verts)
def save(
- operator,
context,
filepath="",
+ use_ascii=False,
use_selection=False,
use_mesh_modifiers=True,
use_normals=True,
use_uv_coords=True,
use_colors=True,
- global_matrix=None
+ global_matrix=None,
):
+ import time
import bpy
import bmesh
+ t = time.time()
+
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='OBJECT')
@@ -224,14 +254,16 @@ def save(
if use_normals:
mesh.calc_normals()
- ret = save_mesh(
+ save_mesh(
filepath,
mesh,
- use_normals=use_normals,
- use_uv_coords=use_uv_coords,
- use_colors=use_colors,
+ use_ascii,
+ use_normals,
+ use_uv_coords,
+ use_colors,
)
bpy.data.meshes.remove(mesh)
- return ret
+ t_delta = time.time() - t
+ print(f"Export completed {filepath!r} in {t_delta:.3f}")
diff --git a/io_mesh_ply/import_ply.py b/io_mesh_ply/import_ply.py
index 6df2ec81..2bc67746 100644
--- a/io_mesh_ply/import_ply.py
+++ b/io_mesh_ply/import_ply.py
@@ -272,20 +272,20 @@ def load_ply_mesh(filepath, ply_name):
if len(colindices) == 3:
mesh_colors.extend([
(
- vertices[index][colindices[0]] * colmultiply[0],
- vertices[index][colindices[1]] * colmultiply[1],
- vertices[index][colindices[2]] * colmultiply[2],
- 1.0,
+ vertices[index][colindices[0]] * colmultiply[0],
+ vertices[index][colindices[1]] * colmultiply[1],
+ vertices[index][colindices[2]] * colmultiply[2],
+ 1.0,
)
for index in indices
])
elif len(colindices) == 4:
mesh_colors.extend([
(
- vertices[index][colindices[0]] * colmultiply[0],
- vertices[index][colindices[1]] * colmultiply[1],
- vertices[index][colindices[2]] * colmultiply[2],
- vertices[index][colindices[3]] * colmultiply[3],
+ vertices[index][colindices[0]] * colmultiply[0],
+ vertices[index][colindices[1]] * colmultiply[1],
+ vertices[index][colindices[2]] * colmultiply[2],
+ vertices[index][colindices[3]] * colmultiply[3],
)
for index in indices
])