Skip to content
Snippets Groups Projects
Commit 3659c4da authored by Jacques Lucke's avatar Jacques Lucke
Browse files

port 'Stanford PLY format' addon to Blender 2.8

Here are a couple of example files for testing: https://people.sc.fsu.edu/~jburkardt/data/ply/ply.html

I removed the texture support that was implemented here: rBA471370
(it is no official part of the file format; the exporter did not support it; reimplementing it is a bit of a hassle)

Reviewers: brecht

Differential Revision: https://developer.blender.org/D3754
parent 608cf349
No related branches found
No related tags found
No related merge requests found
...@@ -22,7 +22,7 @@ bl_info = { ...@@ -22,7 +22,7 @@ bl_info = {
"name": "Stanford PLY format", "name": "Stanford PLY format",
"author": "Bruce Merry, Campbell Barton", "author": "Bruce Merry, Campbell Barton",
"version": (1, 0, 0), "version": (1, 0, 0),
"blender": (2, 74, 0), "blender": (2, 80, 0),
"location": "File > Import-Export", "location": "File > Import-Export",
"description": "Import-Export PLY mesh data withs UV's and vertex colors", "description": "Import-Export PLY mesh data withs UV's and vertex colors",
"warning": "", "warning": "",
...@@ -56,8 +56,8 @@ from bpy.props import ( ...@@ -56,8 +56,8 @@ from bpy.props import (
from bpy_extras.io_utils import ( from bpy_extras.io_utils import (
ImportHelper, ImportHelper,
ExportHelper, ExportHelper,
orientation_helper,
axis_conversion, axis_conversion,
orientation_helper
) )
...@@ -67,15 +67,15 @@ class ImportPLY(bpy.types.Operator, ImportHelper): ...@@ -67,15 +67,15 @@ class ImportPLY(bpy.types.Operator, ImportHelper):
bl_label = "Import PLY" bl_label = "Import PLY"
bl_options = {'UNDO'} bl_options = {'UNDO'}
files = CollectionProperty(name="File Path", files: CollectionProperty(name="File Path",
description="File path used for importing " description="File path used for importing "
"the PLY file", "the PLY file",
type=bpy.types.OperatorFileListElement) type=bpy.types.OperatorFileListElement)
directory = StringProperty() directory: StringProperty()
filename_ext = ".ply" filename_ext = ".ply"
filter_glob = StringProperty(default="*.ply", options={'HIDDEN'}) filter_glob: StringProperty(default="*.ply", options={'HIDDEN'})
def execute(self, context): def execute(self, context):
paths = [os.path.join(self.directory, name.name) paths = [os.path.join(self.directory, name.name)
...@@ -91,7 +91,6 @@ class ImportPLY(bpy.types.Operator, ImportHelper): ...@@ -91,7 +91,6 @@ class ImportPLY(bpy.types.Operator, ImportHelper):
return {'FINISHED'} return {'FINISHED'}
@orientation_helper(axis_forward='Y', axis_up='Z') @orientation_helper(axis_forward='Y', axis_up='Z')
class ExportPLY(bpy.types.Operator, ExportHelper): class ExportPLY(bpy.types.Operator, ExportHelper):
"""Export a single object as a Stanford PLY with normals, """ \ """Export a single object as a Stanford PLY with normals, """ \
...@@ -100,14 +99,14 @@ class ExportPLY(bpy.types.Operator, ExportHelper): ...@@ -100,14 +99,14 @@ class ExportPLY(bpy.types.Operator, ExportHelper):
bl_label = "Export PLY" bl_label = "Export PLY"
filename_ext = ".ply" filename_ext = ".ply"
filter_glob = StringProperty(default="*.ply", options={'HIDDEN'}) filter_glob: StringProperty(default="*.ply", options={'HIDDEN'})
use_mesh_modifiers = BoolProperty( use_mesh_modifiers: BoolProperty(
name="Apply Modifiers", name="Apply Modifiers",
description="Apply Modifiers to the exported mesh", description="Apply Modifiers to the exported mesh",
default=True, default=True,
) )
use_normals = BoolProperty( use_normals: BoolProperty(
name="Normals", name="Normals",
description="Export Normals for smooth and " description="Export Normals for smooth and "
"hard shaded faces " "hard shaded faces "
...@@ -115,18 +114,18 @@ class ExportPLY(bpy.types.Operator, ExportHelper): ...@@ -115,18 +114,18 @@ class ExportPLY(bpy.types.Operator, ExportHelper):
"as individual faces)", "as individual faces)",
default=True, default=True,
) )
use_uv_coords = BoolProperty( use_uv_coords: BoolProperty(
name="UVs", name="UVs",
description="Export the active UV layer", description="Export the active UV layer",
default=True, default=True,
) )
use_colors = BoolProperty( use_colors: BoolProperty(
name="Vertex Colors", name="Vertex Colors",
description="Export the active vertex color layer", description="Export the active vertex color layer",
default=True, default=True,
) )
global_scale = FloatProperty( global_scale: FloatProperty(
name="Scale", name="Scale",
min=0.01, max=1000.0, min=0.01, max=1000.0,
default=1.0, default=1.0,
...@@ -149,7 +148,7 @@ class ExportPLY(bpy.types.Operator, ExportHelper): ...@@ -149,7 +148,7 @@ class ExportPLY(bpy.types.Operator, ExportHelper):
)) ))
global_matrix = axis_conversion(to_forward=self.axis_forward, global_matrix = axis_conversion(to_forward=self.axis_forward,
to_up=self.axis_up, to_up=self.axis_up,
).to_4x4() * Matrix.Scale(self.global_scale, 4) ).to_4x4() @ Matrix.Scale(self.global_scale, 4)
keywords["global_matrix"] = global_matrix keywords["global_matrix"] = global_matrix
filepath = self.filepath filepath = self.filepath
......
...@@ -100,10 +100,8 @@ def save_mesh(filepath, ...@@ -100,10 +100,8 @@ def save_mesh(filepath,
col = active_col_layer[i] col = active_col_layer[i]
col = col.color1[:], col.color2[:], col.color3[:], col.color4[:] col = col.color1[:], col.color2[:], col.color3[:], col.color4[:]
f_verts = f.vertices
pf = ply_faces[i] pf = ply_faces[i]
for j, vidx in enumerate(f_verts): for j, vidx in enumerate(f.vertices):
v = mesh_verts[vidx] v = mesh_verts[vidx]
if smooth: if smooth:
...@@ -119,6 +117,7 @@ def save_mesh(filepath, ...@@ -119,6 +117,7 @@ def save_mesh(filepath,
color = (int(color[0] * 255.0), color = (int(color[0] * 255.0),
int(color[1] * 255.0), int(color[1] * 255.0),
int(color[2] * 255.0), int(color[2] * 255.0),
255
) )
key = normal_key, uvcoord_key, color key = normal_key, uvcoord_key, color
...@@ -193,7 +192,6 @@ def save(operator, ...@@ -193,7 +192,6 @@ def save(operator,
global_matrix=None global_matrix=None
): ):
scene = context.scene
obj = context.active_object obj = context.active_object
if global_matrix is None: if global_matrix is None:
...@@ -204,14 +202,15 @@ def save(operator, ...@@ -204,14 +202,15 @@ def save(operator,
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
if use_mesh_modifiers and obj.modifiers: if use_mesh_modifiers and obj.modifiers:
mesh = obj.to_mesh(scene, True, 'PREVIEW') mesh = obj.to_mesh(context.depsgraph, True)
else: else:
mesh = obj.data.copy() mesh = obj.data.copy()
if not mesh: if not mesh:
raise Exception("Error, could not get mesh data from active object") raise Exception("Error, could not get mesh data from active object")
mesh.transform(global_matrix * obj.matrix_world) mesh.transform(global_matrix @ obj.matrix_world)
if use_normals: if use_normals:
mesh.calc_normals() mesh.calc_normals()
...@@ -221,7 +220,6 @@ def save(operator, ...@@ -221,7 +220,6 @@ def save(operator,
use_colors=use_colors, use_colors=use_colors,
) )
if use_mesh_modifiers: bpy.data.meshes.remove(mesh)
bpy.data.meshes.remove(mesh)
return ret return ret
...@@ -126,7 +126,6 @@ class object_spec(object): ...@@ -126,7 +126,6 @@ class object_spec(object):
def read(filepath): def read(filepath):
format = b'' format = b''
texture = b''
version = b'1.0' version = b'1.0'
format_specs = {b'binary_little_endian': '<', format_specs = {b'binary_little_endian': '<',
b'binary_big_endian': '>', b'binary_big_endian': '>',
...@@ -168,13 +167,6 @@ def read(filepath): ...@@ -168,13 +167,6 @@ def read(filepath):
valid_header = True valid_header = True
break break
elif tokens[0] == b'comment': elif tokens[0] == b'comment':
if len(tokens) < 2:
continue
elif tokens[1] == b'TextureFile':
if len(tokens) < 4:
print('Invalid texture line')
else:
texture = tokens[2]
continue continue
elif tokens[0] == b'obj_info': elif tokens[0] == b'obj_info':
continue continue
...@@ -214,7 +206,7 @@ def read(filepath): ...@@ -214,7 +206,7 @@ def read(filepath):
obj = obj_spec.load(format_specs[format], plyf) obj = obj_spec.load(format_specs[format], plyf)
return obj_spec, obj, texture return obj_spec, obj
import bpy import bpy
...@@ -222,9 +214,8 @@ import bpy ...@@ -222,9 +214,8 @@ import bpy
def load_ply_mesh(filepath, ply_name): def load_ply_mesh(filepath, ply_name):
from bpy_extras.io_utils import unpack_face_list from bpy_extras.io_utils import unpack_face_list
# from bpy_extras.image_utils import load_image # UNUSED
obj_spec, obj, texture = read(filepath) obj_spec, obj = read(filepath)
if obj is None: if obj is None:
print('Invalid file') print('Invalid file')
return return
...@@ -346,35 +337,8 @@ def load_ply_mesh(filepath, ply_name): ...@@ -346,35 +337,8 @@ def load_ply_mesh(filepath, ply_name):
col[2] = ply_col[j][2] col[2] = ply_col[j][2]
col[3] = ply_col[j][3] col[3] = ply_col[j][3]
mesh.validate()
mesh.update() mesh.update()
mesh.validate()
if texture and uvindices:
import os
import sys
from bpy_extras.image_utils import load_image
encoding = sys.getfilesystemencoding()
encoded_texture = texture.decode(encoding=encoding)
name = bpy.path.display_name_from_filepath(texture)
image = load_image(encoded_texture, os.path.dirname(filepath), recursive=True, place_holder=True)
if image:
texture = bpy.data.textures.new(name=name, type='IMAGE')
texture.image = image
material = bpy.data.materials.new(name=name)
material.use_shadeless = True
mtex = material.texture_slots.add()
mtex.texture = texture
mtex.texture_coords = 'UV'
mtex.use_map_color_diffuse = True
mesh.materials.append(material)
for face in mesh.uv_textures[0].data:
face.image = image
return mesh return mesh
...@@ -389,12 +353,10 @@ def load_ply(filepath): ...@@ -389,12 +353,10 @@ def load_ply(filepath):
if not mesh: if not mesh:
return {'CANCELLED'} return {'CANCELLED'}
scn = bpy.context.scene
obj = bpy.data.objects.new(ply_name, mesh) obj = bpy.data.objects.new(ply_name, mesh)
scn.objects.link(obj) bpy.context.collection.objects.link(obj)
scn.objects.active = obj bpy.context.view_layer.objects.active = obj
obj.select = True obj.select_set("SELECT")
print('\nSuccessfully imported %r in %.3f sec' % (filepath, time.time() - t)) print('\nSuccessfully imported %r in %.3f sec' % (filepath, time.time() - t))
return {'FINISHED'} return {'FINISHED'}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment