Added basic support of ply

This commit is contained in:
Thomas FORGIONE 2016-11-21 15:59:03 +01:00
parent 685b5e6f59
commit c5ef973914
5 changed files with 255 additions and 189 deletions

76
model.py Normal file
View File

@ -0,0 +1,76 @@
#!/usr/bin/env python3
class Vertex:
def __init__(self, x = None, y = None, z = None):
self.x = x
self.y = y
self.z = z
def from_array(self, arr):
self.x = arr[0] if len(arr) > 0 else None
self.y = arr[1] if len(arr) > 1 else None
self.z = arr[2] if len(arr) > 2 else None
return self
Normal = Vertex
TexCoord = Vertex
class FaceVertex:
def __init__(self, vertex = None, texture = None, normal = None):
self.vertex = vertex
self.texture = texture
self.normal = normal
def from_array(self, arr):
self.vertex = arr[0] if len(arr) > 0 else None
self.texture = arr[1] if len(arr) > 1 else None
self.normal = arr[2] if len(arr) > 2 else None
return self
class Face:
def __init__(self, a = None, b = None, c = None, mtl = None):
self.a = a
self.b = b
self.c = c
self.mtl = mtl
# Expects array of array
def from_array(self, arr):
self.a = FaceVertex().from_array(arr[0])
self.b = FaceVertex().from_array(arr[1])
self.c = FaceVertex().from_array(arr[2])
return self
class ModelParser:
def __init__(self):
self.vertices = []
self.normals = []
self.tex_coords = []
self.faces = []
def add_vertex(self, vertex):
self.vertices.append(vertex)
def add_tex_coord(self, tex_coord):
self.tex_coords.append(tex_coord)
def add_normal(self, normal):
self.normals.append(normal)
def addFace(self, face):
self.faces.append(face)
def parse_line(self, string):
pass
def parse_file(self, path):
with open(path) as f:
for line in f.readlines():
line = line.rstrip()
self.parse_line(line)
class Exporter:
def __init__(self, model):
self.model = model

View File

@ -1,188 +0,0 @@
#!/usr/bin/env python3
import sys
from functools import reduce
class Vertex:
def __init__(self, x = None, y = None, z = None):
self.x = x
self.y = y
self.z = z
def from_array(self, arr):
self.x = arr[0] if len(arr) > 0 else None
self.y = arr[1] if len(arr) > 1 else None
self.z = arr[2] if len(arr) > 2 else None
return self
Normal = Vertex
TexCoord = Vertex
class FaceVertex:
def __init__(self, vertex = None, texture = None, normal = None):
self.vertex = vertex
self.texture = texture
self.normal = normal
def from_array(self, arr):
self.vertex = arr[0] if len(arr) > 0 else None
self.texture = arr[1] if len(arr) > 1 else None
self.normal = arr[2] if len(arr) > 2 else None
return self
class Face:
def __init__(self, a = None, b = None, c = None, mtl = None):
self.a = a
self.b = b
self.c = c
self.mtl = mtl
# Expects array of array
def from_array(self, arr):
self.a = FaceVertex().from_array(arr[0])
self.b = FaceVertex().from_array(arr[1])
self.c = FaceVertex().from_array(arr[2])
return self
class ModelParser:
def __init__(self):
self.vertices = []
self.normals = []
self.tex_coords = []
self.faces = []
def add_vertex(self, vertex):
self.vertices.append(vertex)
def add_tex_coord(self, tex_coord):
self.tex_coords.append(tex_coord)
def add_normal(self, normal):
self.normals.append(normal)
def addFace(self, face):
self.faces.append(face)
def parse_line(self, string):
pass
def parse_file(self, path):
with open(path) as f:
for line in f.readlines():
line = line.rstrip()
self.parse_line(line)
class OBJParser(ModelParser):
def __init__(self):
super().__init__()
self.materials = []
def parse_line(self, string):
split = string.split(' ')
first = split[0]
split = split[1:]
if first == 'usemtl':
self.currentMaterial = split[0]
elif first == 'v':
self.add_vertex(Vertex().from_array(split))
elif first == 'vn':
self.add_normal(Normal().from_array(split))
elif first == 'vt':
self.add_tex_coord(TexCoord().from_array(split))
elif first == 'f':
splits = list(map(lambda x: x.split('/'), split))
for i in range(len(splits)):
for j in range(len(splits[i])):
if splits[i][j] is not '':
splits[i][j] = str(int(splits[i][j]) - 1)
self.addFace(Face().from_array(splits))
class Exporter:
def __init__(self, model):
self.model = model
class OBJExporter(Exporter):
def __init__(self, model):
super().__init__(model)
def __str__(self):
string = ""
for vertex in self.model.vertices:
string += "n " + ' '.join([vertex.x, vertex.y, vertex.z]) + "\n"
string += "\n"
for tex_coord in self.model.tex_coords:
string += "vt " + ' '.join([tex_coord.x, tex_coord.y]) + "\n"
string += "\n"
for normal in self.model.normals:
string += "vn " + ' '.join([normal.x, normal.y, normal.z]) + "\n"
string += "\n"
for face in self.model.faces:
string += "f "
arr = []
for v in [face.a, face.b, face.c]:
sub_arr = []
sub_arr.append(v.vertex)
if v.normal is None:
if v.texture is not None:
sub_arr.append('')
sub_arr.append(v.texture)
elif v.texture is not None:
sub_arr.append(v.texture)
if v.normal is not None:
sub_arr.append(v.normal)
arr.append('/'.join(sub_arr))
string += ' '.join(arr) + '\n'
return string
class PLYExporter(Exporter):
def __init__(self, model):
super().__init__(model)
def __str__(self):
# Header
string = "ply\nformat ascii 1.0\ncomment Automatically gnerated by model-converter\n"
# Types : vertices
string += "element vertex " + str(len(self.model.vertices)) +"\n"
string += "property float32 x\nproperty float32 y\nproperty float32 z\n"
# Types : faces
string += "element face " + str(len(self.model.faces)) + "\n"
string += "property list uint8 int32 vertex_indices\n"
# End header
string += "end_header\n"
# Content of the model
for vertex in self.model.vertices:
string += vertex.x + " " + vertex.y + " " + vertex.z + "\n"
for face in self.model.faces:
string += "3 " + face.a.vertex + " " + face.b.vertex + " " + face.c.vertex + "\n"
return string
if __name__ == '__main__':
parser = OBJParser()
parser.parse_file('./examples/cube.obj')
exporter = PLYExporter(parser)
string = str(exporter)
print(string)

75
obj.py Normal file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
from model import ModelParser, Exporter, Vertex, TexCoord, Normal, FaceVertex, Face
from functools import reduce
class OBJParser(ModelParser):
def __init__(self):
super().__init__()
self.materials = []
def parse_line(self, string):
split = string.split(' ')
first = split[0]
split = split[1:]
if first == 'usemtl':
self.currentMaterial = split[0]
elif first == 'v':
self.add_vertex(Vertex().from_array(split))
elif first == 'vn':
self.add_normal(Normal().from_array(split))
elif first == 'vt':
self.add_tex_coord(TexCoord().from_array(split))
elif first == 'f':
splits = list(map(lambda x: x.split('/'), split))
for i in range(len(splits)):
for j in range(len(splits[i])):
if splits[i][j] is not '':
splits[i][j] = str(int(splits[i][j]) - 1)
self.addFace(Face().from_array(splits))
class OBJExporter(Exporter):
def __init__(self, model):
super().__init__(model)
def __str__(self):
string = ""
for vertex in self.model.vertices:
string += "n " + ' '.join([vertex.x, vertex.y, vertex.z]) + "\n"
string += "\n"
for tex_coord in self.model.tex_coords:
string += "vt " + ' '.join([tex_coord.x, tex_coord.y]) + "\n"
string += "\n"
for normal in self.model.normals:
string += "vn " + ' '.join([normal.x, normal.y, normal.z]) + "\n"
string += "\n"
for face in self.model.faces:
string += "f "
arr = []
for v in [face.a, face.b, face.c]:
sub_arr = []
sub_arr.append(v.vertex)
if v.normal is None:
if v.texture is not None:
sub_arr.append('')
sub_arr.append(v.texture)
elif v.texture is not None:
sub_arr.append(v.texture)
if v.normal is not None:
sub_arr.append(v.normal)
arr.append('/'.join(sub_arr))
string += ' '.join(arr) + '\n'
return string

View File

@ -3,7 +3,8 @@
import argparse import argparse
import os import os
from modelconverter import OBJParser, PLYExporter from obj import OBJParser
from ply import PLYExporter
from functools import partial from functools import partial
def check_path(path, should_exist): def check_path(path, should_exist):

102
ply.py Normal file
View File

@ -0,0 +1,102 @@
#!/usr/bin/env python3
from model import ModelParser, Exporter, Vertex, Face
class PLYParser(ModelParser):
def __init__(self):
super().__init__()
self.counter = 0
self.elements = []
self.inner_parser = PLYHeaderParser(self)
def parse_line(self, string):
self.inner_parser.parse_line(self, string)
class PLYHeaderParser:
def __init__(self, parent):
self.current_element = None
self.parent = parent
def parse_line(self, string):
split = string.split(' ')
if string == 'ply':
return
elif split[0] == 'format':
if split[1] != 'ascii' or split[2] != '1.0':
print('Only ascii 1.0 format is supported', file=sys.stderr)
sys.exit(-1)
elif split[0] == 'element':
self.current_element = PLYElement(split[1], int(split[2]))
self.parent.elements.append(self.current_element)
elif split[0] == 'property':
self.current_element.add_property(split[2], split[1])
elif split[0] == 'end_header':
self.parent.inner_parser = PLYContentParser(self.parent)
class PLYElement:
def __init__(self, name, number):
self.name = name
self.number = number
self.properties = []
def add_property(name, type):
self.properties.append((name, type))
class PLYContentParser:
def __init__(self, parent):
self.parent = parent
self.element_index = 0
self.counter = 0
self.current_element = self.parent.elements[0]
def parse_line(self, string):
self.counter += 1
split = string.split(' ')
if self.current_element.name == 'vertex':
self.parent.add_vertex(Vertex.from_array(split))
elif self.current_element.name == 'face':
self.parent.add_face(Face(split[1], split[2], split[3]))
self.counter += 1
def next_element(self):
self.element_index += 1
self.current_element = self.parent.elements[self.element_index]
class PLYExporter(Exporter):
def __init__(self, model):
super().__init__(model)
def __str__(self):
# Header
string = "ply\nformat ascii 1.0\ncomment Automatically gnerated by model-converter\n"
# Types : vertices
string += "element vertex " + str(len(self.model.vertices)) +"\n"
string += "property float32 x\nproperty float32 y\nproperty float32 z\n"
# Types : faces
string += "element face " + str(len(self.model.faces)) + "\n"
string += "property list uint8 int32 vertex_indices\n"
# End header
string += "end_header\n"
# Content of the model
for vertex in self.model.vertices:
string += vertex.x + " " + vertex.y + " " + vertex.z + "\n"
for face in self.model.faces:
string += "3 " + face.a.vertex + " " + face.b.vertex + " " + face.c.vertex + "\n"
return string