Skip to content
Snippets Groups Projects
Commit 4c7e2b5a authored by Mikhail Rachinskiy's avatar Mikhail Rachinskiy
Browse files

Revert "PLY: refactor exporter"

This reverts commit 7f4c2d5e.

Issues with unit tests.
parent 3fc46407
No related branches found
No related tags found
No related merge requests found
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
bl_info = { bl_info = {
"name": "Stanford PLY format", "name": "Stanford PLY format",
"author": "Bruce Merry, Campbell Barton, Bastien Montagne, Mikhail Rachinsky", "author": "Bruce Merry, Campbell Barton", "Bastien Montagne"
"version": (2, 1, 0), "version": (2, 1, 0),
"blender": (2, 90, 0), "blender": (2, 90, 0),
"location": "File > Import/Export", "location": "File > Import/Export",
......
...@@ -23,17 +23,23 @@ This script exports Stanford PLY files from Blender. It supports normals, ...@@ -23,17 +23,23 @@ This script exports Stanford PLY files from Blender. It supports normals,
colors, and texture coordinates per face or per vertex. 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 from struct import pack
# Vertex data # Vertex data
# --------------------------- # ---------------------------
for v, normal, uv_coords, color in ply_verts: for index, normal, uv_coords, color in ply_verts:
fw(pack("<3f", *v.co)) fw(pack("<3f", *mesh_verts[index].co))
if normal is not None: if normal is not None:
fw(pack("<3f", *normal)) fw(pack("<3f", *normal))
if uv_coords is not None: if uv_coords is not None:
...@@ -45,21 +51,20 @@ def _write_binary(fw, ply_verts: list, ply_faces: list) -> None: ...@@ -45,21 +51,20 @@ def _write_binary(fw, ply_verts: list, ply_faces: list) -> None:
# --------------------------- # ---------------------------
for pf in ply_faces: for pf in ply_faces:
length = len(pf) fw(pack(f"<B{pf.sides}I", pf.sides, *pf.verts))
fw(pack(f"<B{length}I", length, *pf))
def _write_ascii(fw, ply_verts: list, ply_faces: list) -> None: def _write_ascii(fw, ply_verts: list, ply_faces: list[_PLYface], mesh_verts: list) -> None:
# Vertex data # Vertex data
# --------------------------- # ---------------------------
for v, normal, uv_coords, color in ply_verts: for index, normal, uv_coords, color in ply_verts:
fw(b"%.6f %.6f %.6f" % v.co[:]) fw(b"%.6f %.6f %.6f" % mesh_verts[index].co[:])
if normal is not None: if normal is not None:
fw(b" %.6f %.6f %.6f" % normal[:]) fw(b" %.6f %.6f %.6f" % normal)
if uv_coords is not None: if uv_coords is not None:
fw(b" %.6f %.6f" % uv_coords[:]) fw(b" %.6f %.6f" % uv_coords)
if color is not None: if color is not None:
fw(b" %u %u %u %u" % color) fw(b" %u %u %u %u" % color)
fw(b"\n") fw(b"\n")
...@@ -68,45 +73,93 @@ def _write_ascii(fw, ply_verts: list, ply_faces: list) -> None: ...@@ -68,45 +73,93 @@ def _write_ascii(fw, ply_verts: list, ply_faces: list) -> None:
# --------------------------- # ---------------------------
for pf in ply_faces: for pf in ply_faces:
fw(b"%d" % len(pf)) fw(b"%d" % pf.sides)
for index in pf: for index in pf.verts:
fw(b" %d" % index) fw(b" %d" % index)
fw(b"\n") fw(b"\n")
def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): def save_mesh(filepath, mesh, use_ascii, use_normals, use_uv_coords, use_colors):
uv_lay = bm.loops.layers.uv.active import bpy
col_lay = bm.loops.layers.color.active
use_uv = use_uv and uv_lay is not None def rvec3d(v):
use_color = use_color and col_lay is not None return round(v[0], 6), round(v[1], 6), round(v[2], 6)
uv = color = None
ply_verts = [None for _ in range(len(bm.verts))] def rvec2d(v):
ply_faces = [] return round(v[0], 6), round(v[1], 6)
for f in bm.faces: if use_uv_coords and mesh.uv_layers:
pf = [] active_uv_layer = mesh.uv_layers.active.data
ply_faces.append(pf) 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 # in case
if use_normals and not f.smooth: color = uvcoord = uvcoord_key = normal = normal_key = None
normal = f.normal
for loop in f.loops: mesh_verts = mesh.vertices
v = loop.vert # vdict = {} # (index, normal, uv) -> new index
pf.append(v.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:
if use_normals and normal is None: smooth = f.use_smooth
normal = v.normal if not smooth:
if use_uv: normal = f.normal[:]
uv = loop[uv_lay].uv normal_key = rvec3d(normal)
if use_color:
color = tuple(int(x * 255.0) for x in loop[col_lay]) 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) ply_faces.append(pf)
v.tag = True
with open(filepath, "wb") as file: with open(filepath, "wb") as file:
fw = file.write fw = file.write
...@@ -131,12 +184,12 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): ...@@ -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 ny\n"
b"property float nz\n" b"property float nz\n"
) )
if use_uv: if use_uv_coords:
fw( fw(
b"property float s\n" b"property float s\n"
b"property float t\n" b"property float t\n"
) )
if use_color: if use_colors:
fw( fw(
b"property uchar red\n" b"property uchar red\n"
b"property uchar green\n" b"property uchar green\n"
...@@ -144,7 +197,7 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): ...@@ -144,7 +197,7 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color):
b"property uchar alpha\n" 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"property list uchar uint vertex_indices\n")
fw(b"end_header\n") fw(b"end_header\n")
...@@ -152,9 +205,9 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color): ...@@ -152,9 +205,9 @@ def save_mesh(filepath, bm, use_ascii, use_normals, use_uv, use_color):
# --------------------------- # ---------------------------
if use_ascii: if use_ascii:
_write_ascii(fw, ply_verts, ply_faces) _write_ascii(fw, ply_verts, ply_faces, mesh_verts)
else: else:
_write_binary(fw, ply_verts, ply_faces) _write_binary(fw, ply_verts, ply_faces, mesh_verts)
def save( def save(
...@@ -169,6 +222,7 @@ def save( ...@@ -169,6 +222,7 @@ def save(
global_matrix=None, global_matrix=None,
): ):
import time import time
import bpy
import bmesh import bmesh
t = time.time() t = time.time()
...@@ -203,22 +257,26 @@ def save( ...@@ -203,22 +257,26 @@ def save(
if (ngons := [f for f in bm.faces if len(f.verts) > 255]): if (ngons := [f for f in bm.faces if len(f.verts) > 255]):
bmesh.ops.triangulate(bm, faces=ngons) 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: if global_matrix is not None:
bm.transform(global_matrix) mesh.transform(global_matrix)
if use_normals: if use_normals:
bm.normal_update() mesh.calc_normals()
save_mesh( save_mesh(
filepath, filepath,
bm, mesh,
use_ascii, use_ascii,
use_normals, use_normals,
use_uv_coords, use_uv_coords,
use_colors, use_colors,
) )
bm.free() bpy.data.meshes.remove(mesh)
t_delta = time.time() - t t_delta = time.time() - t
print(f"Export completed {filepath!r} in {t_delta:.3f}") print(f"Export completed {filepath!r} in {t_delta:.3f}")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment