Added binary little endian ply support
This commit is contained in:
parent
41a13e6014
commit
c0ced36370
|
@ -8,3 +8,6 @@ Ka 0.5 0.5 0.
|
||||||
Kd 0.9 0.9 0.9
|
Kd 0.9 0.9 0.9
|
||||||
Ks 0.0 0.0 0.0
|
Ks 0.0 0.0 0.0
|
||||||
map_Kd cube.png
|
map_Kd cube.png
|
||||||
|
|
||||||
|
newmtl fuckyou
|
||||||
|
Ka 1 1 1
|
||||||
|
|
|
@ -29,3 +29,6 @@ f 6/1/1 5/3/1 7/4/1 8/2/1
|
||||||
f 5/1/6 1/3/6 3/4/6 7/2/6
|
f 5/1/6 1/3/6 3/4/6 7/2/6
|
||||||
f 4/1/2 8/3/2 7/4/2 3/2/2
|
f 4/1/2 8/3/2 7/4/2 3/2/2
|
||||||
f 2/1/5 1/3/5 5/4/5 6/2/5
|
f 2/1/5 1/3/5 5/4/5 6/2/5
|
||||||
|
|
||||||
|
usemtl fuckyou
|
||||||
|
|
||||||
|
|
|
@ -134,8 +134,18 @@ class ModelParser:
|
||||||
|
|
||||||
self.current_part.add_face(face)
|
self.current_part.add_face(face)
|
||||||
|
|
||||||
def parse_bytes(self, bytes):
|
def parse_file(self, path, chunk_size = 512):
|
||||||
pass
|
"""Sets the path of the model and parse bytes by chunk
|
||||||
|
"""
|
||||||
|
self.path = path
|
||||||
|
byte_counter = 0
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
while True:
|
||||||
|
bytes = f.read(chunk_size)
|
||||||
|
if bytes == b'':
|
||||||
|
return
|
||||||
|
self.parse_bytes(bytes, byte_counter)
|
||||||
|
byte_counter += chunk_size
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
"""Draws each part of the model with OpenGL
|
"""Draws each part of the model with OpenGL
|
||||||
|
|
|
@ -1,11 +1,65 @@
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import PIL
|
import PIL
|
||||||
|
import struct
|
||||||
from ..basemodel import ModelParser, TextModelParser, Exporter, Vertex, Face, FaceVertex, TexCoord, Material
|
from ..basemodel import ModelParser, TextModelParser, Exporter, Vertex, Face, FaceVertex, TexCoord, Material
|
||||||
|
|
||||||
|
class UnkownTypeError(Exception):
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = message
|
||||||
|
|
||||||
def is_ply(filename):
|
def is_ply(filename):
|
||||||
return filename[-4:] == '.ply'
|
return filename[-4:] == '.ply'
|
||||||
|
|
||||||
class PLYParser(TextModelParser):
|
# List won't work with this function
|
||||||
|
def _ply_type_size(type):
|
||||||
|
if type == 'char' or type == 'uchar':
|
||||||
|
return 1
|
||||||
|
elif type == 'short' or type == 'ushort':
|
||||||
|
return 2
|
||||||
|
elif type == 'int' or type == 'uint':
|
||||||
|
return 4
|
||||||
|
elif type == 'float':
|
||||||
|
return 4
|
||||||
|
elif type == 'double':
|
||||||
|
return 8
|
||||||
|
else:
|
||||||
|
raise UnkownTypeError('Type ' + type + ' is unknown')
|
||||||
|
|
||||||
|
def ply_type_size(type):
|
||||||
|
split = type.split()
|
||||||
|
|
||||||
|
if len(split) == 1:
|
||||||
|
return [_ply_type_size(type)]
|
||||||
|
else:
|
||||||
|
if split[0] != 'list':
|
||||||
|
print('You have multiple types but it\'s not a list...', file=sys.stderr)
|
||||||
|
sys.exit(-1)
|
||||||
|
else:
|
||||||
|
return list(map(lambda a: _ply_type_size(a), split[1:]))
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_to_element(type, bytes, byteorder = 'little'):
|
||||||
|
if type == 'char':
|
||||||
|
return ord(struct.unpack('<b', bytes)[0])
|
||||||
|
if type == 'uchar':
|
||||||
|
return ord(struct.unpack('<c', bytes)[0])
|
||||||
|
elif type == 'short':
|
||||||
|
return struct.unpack('<h', bytes)[0]
|
||||||
|
elif type == 'ushort':
|
||||||
|
return struct.unpack('<H', bytes)[0]
|
||||||
|
elif type == 'int':
|
||||||
|
return struct.unpack('<i', bytes)[0]
|
||||||
|
elif type == 'uint':
|
||||||
|
return struct.unpack('<I', bytes)[0]
|
||||||
|
elif type == 'float':
|
||||||
|
return struct.unpack('<f', bytes)[0]
|
||||||
|
elif type == 'double':
|
||||||
|
return struct.unpack('<d', bytes)[0]
|
||||||
|
else:
|
||||||
|
raise UnkownTypeError('Type ' + type + ' is unknown')
|
||||||
|
|
||||||
|
class PLYParser(ModelParser):
|
||||||
|
|
||||||
def __init__(self, up_conversion = None):
|
def __init__(self, up_conversion = None):
|
||||||
super().__init__(up_conversion)
|
super().__init__(up_conversion)
|
||||||
|
@ -13,14 +67,35 @@ class PLYParser(TextModelParser):
|
||||||
self.elements = []
|
self.elements = []
|
||||||
self.materials = []
|
self.materials = []
|
||||||
self.inner_parser = PLYHeaderParser(self)
|
self.inner_parser = PLYHeaderParser(self)
|
||||||
|
self.beginning_of_line = ''
|
||||||
|
self.header_finished = False
|
||||||
|
|
||||||
def parse_line(self, string):
|
def parse_bytes(self, bytes, byte_counter):
|
||||||
self.inner_parser.parse_line(string)
|
if self.header_finished:
|
||||||
|
self.inner_parser.parse_bytes(self.beginning_of_line + bytes, byte_counter - len(self.beginning_of_line))
|
||||||
|
self.beginning_of_line = b''
|
||||||
|
return
|
||||||
|
|
||||||
|
# Build lines for header and use PLYHeaderParser
|
||||||
|
current_line = self.beginning_of_line
|
||||||
|
for (i, c) in enumerate(bytes):
|
||||||
|
char = chr(c)
|
||||||
|
if char == '\n':
|
||||||
|
self.inner_parser.parse_line(current_line)
|
||||||
|
if current_line == 'end_header':
|
||||||
|
self.header_finished = True
|
||||||
|
self.beginning_of_line = bytes[i+1:]
|
||||||
|
return
|
||||||
|
current_line = ''
|
||||||
|
else:
|
||||||
|
current_line += chr(c)
|
||||||
|
self.beginning_of_line = current_line
|
||||||
|
|
||||||
class PLYHeaderParser:
|
class PLYHeaderParser:
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.current_element = None
|
self.current_element = None
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
self.content_parser = None
|
||||||
|
|
||||||
def parse_line(self, string):
|
def parse_line(self, string):
|
||||||
split = string.split()
|
split = string.split()
|
||||||
|
@ -28,8 +103,19 @@ class PLYHeaderParser:
|
||||||
return
|
return
|
||||||
|
|
||||||
elif split[0] == 'format':
|
elif split[0] == 'format':
|
||||||
if split[1] != 'ascii' or split[2] != '1.0':
|
if split[2] != '1.0':
|
||||||
print('Only ascii 1.0 format is supported', file=sys.stderr)
|
print('Only format 1.0 is supported', file=sys.stderr)
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if split[1] == 'ascii':
|
||||||
|
self.content_parser = PLY_ASCII_ContentParser(self.parent)
|
||||||
|
elif split[1] == 'binary_little_endian':
|
||||||
|
self.content_parser = PLYLittleEndianContentParser(self.parent)
|
||||||
|
elif split[1] == 'binary_big_endian':
|
||||||
|
self.content_parser = PLYBigEndianContentParser(self.parent)
|
||||||
|
else:
|
||||||
|
print('Only ascii, binary_little_endian and binary_big_endian are supported', \
|
||||||
|
file=sys.stderr)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
elif split[0] == 'element':
|
elif split[0] == 'element':
|
||||||
|
@ -37,10 +123,10 @@ class PLYHeaderParser:
|
||||||
self.parent.elements.append(self.current_element)
|
self.parent.elements.append(self.current_element)
|
||||||
|
|
||||||
elif split[0] == 'property':
|
elif split[0] == 'property':
|
||||||
self.current_element.add_property(split[-1], (split[1:][:-1]))
|
self.current_element.add_property(split[-1], ' '.join(split[1:-1]))
|
||||||
|
|
||||||
elif split[0] == 'end_header':
|
elif split[0] == 'end_header':
|
||||||
self.parent.inner_parser = PLYContentParser(self.parent)
|
self.parent.inner_parser = self.content_parser
|
||||||
|
|
||||||
elif split[0] == 'comment' and split[1] == 'TextureFile':
|
elif split[0] == 'comment' and split[1] == 'TextureFile':
|
||||||
material = Material('mat' + str(len(self.parent.materials)))
|
material = Material('mat' + str(len(self.parent.materials)))
|
||||||
|
@ -48,11 +134,9 @@ class PLYHeaderParser:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
material.map_Kd = PIL.Image.open(os.path.join(os.path.dirname(self.parent.path), split[2]))
|
material.map_Kd = PIL.Image.open(os.path.join(os.path.dirname(self.parent.path), split[2]))
|
||||||
except:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PLYElement:
|
class PLYElement:
|
||||||
def __init__(self, name, number):
|
def __init__(self, name, number):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -62,22 +146,31 @@ class PLYElement:
|
||||||
def add_property(self, name, type):
|
def add_property(self, name, type):
|
||||||
self.properties.append((name, type))
|
self.properties.append((name, type))
|
||||||
|
|
||||||
class PLYVertex(Vertex):
|
class PLY_ASCII_ContentParser:
|
||||||
def __init__(self, string):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add_to_parser(self, parser):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class PLYContentParser:
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.element_index = 0
|
self.element_index = 0
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
self.current_element = self.parent.elements[0]
|
self.current_element = None
|
||||||
|
self.beginning_of_line = ''
|
||||||
|
|
||||||
|
def parse_bytes(self, bytes, byte_counter):
|
||||||
|
current_line = self.beginning_of_line
|
||||||
|
for (i, c) in enumerate(bytes):
|
||||||
|
char = chr(c)
|
||||||
|
if char == '\n':
|
||||||
|
self.parse_line(current_line)
|
||||||
|
current_line = ''
|
||||||
|
else:
|
||||||
|
current_line += chr(c)
|
||||||
|
self.beginning_of_line = current_line
|
||||||
|
|
||||||
|
|
||||||
def parse_line(self, string):
|
def parse_line(self, string):
|
||||||
|
|
||||||
|
if self.current_element is None:
|
||||||
|
self.current_element = self.parent.elements[0]
|
||||||
|
|
||||||
split = string.split()
|
split = string.split()
|
||||||
|
|
||||||
if self.current_element.name == 'vertex':
|
if self.current_element.name == 'vertex':
|
||||||
|
@ -119,6 +212,125 @@ class PLYContentParser:
|
||||||
if self.element_index < len(self.parent.elements):
|
if self.element_index < len(self.parent.elements):
|
||||||
self.current_element = self.parent.elements[self.element_index]
|
self.current_element = self.parent.elements[self.element_index]
|
||||||
|
|
||||||
|
class PLYLittleEndianContentParser:
|
||||||
|
def __init__(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
self.previous_bytes = b''
|
||||||
|
self.element_index = 0
|
||||||
|
self.counter = 0
|
||||||
|
self.current_element = None
|
||||||
|
self.started = False
|
||||||
|
|
||||||
|
# Serves for debugging purposes
|
||||||
|
# self.current_byte = 0
|
||||||
|
|
||||||
|
def parse_bytes(self, bytes, byte_counter):
|
||||||
|
|
||||||
|
if not self.started:
|
||||||
|
# self.current_byte = byte_counter
|
||||||
|
self.started = True
|
||||||
|
|
||||||
|
if self.current_element is None:
|
||||||
|
self.current_element = self.parent.elements[0]
|
||||||
|
|
||||||
|
bytes = self.previous_bytes + bytes
|
||||||
|
current_byte_index = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
property_values = []
|
||||||
|
|
||||||
|
beginning_byte_index = current_byte_index
|
||||||
|
|
||||||
|
for property in self.current_element.properties:
|
||||||
|
|
||||||
|
size = ply_type_size(property[1])
|
||||||
|
|
||||||
|
if current_byte_index + size[0] > len(bytes):
|
||||||
|
self.previous_bytes = bytes[beginning_byte_index:]
|
||||||
|
# self.current_byte -= len(self.previous_bytes)
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(size) == 1:
|
||||||
|
|
||||||
|
size = size[0]
|
||||||
|
|
||||||
|
current_property_bytes = bytes[current_byte_index:current_byte_index+size]
|
||||||
|
property_values.append(bytes_to_element(property[1], current_property_bytes))
|
||||||
|
current_byte_index += size
|
||||||
|
# self.current_byte += size
|
||||||
|
|
||||||
|
elif len(size) == 2:
|
||||||
|
|
||||||
|
types = property[1].split()[1:]
|
||||||
|
current_property_bytes = bytes[current_byte_index:current_byte_index+size[0]]
|
||||||
|
number_of_elements = bytes_to_element(types[0], current_property_bytes)
|
||||||
|
current_byte_index += size[0]
|
||||||
|
# self.current_byte += size[0]
|
||||||
|
|
||||||
|
property_values.append([])
|
||||||
|
|
||||||
|
# Parse list
|
||||||
|
for i in range(number_of_elements):
|
||||||
|
|
||||||
|
if current_byte_index + size[1] > len(bytes):
|
||||||
|
|
||||||
|
self.previous_bytes = bytes[beginning_byte_index:]
|
||||||
|
# self.current_byte -= len(self.previous_bytes)
|
||||||
|
return
|
||||||
|
|
||||||
|
current_property_bytes = bytes[current_byte_index:current_byte_index+size[1]]
|
||||||
|
property_values[-1].append(bytes_to_element(types[1], current_property_bytes))
|
||||||
|
current_byte_index += size[1]
|
||||||
|
# self.current_byte += size[1]
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
print('I have not idea what this means', file=sys.stderr)
|
||||||
|
|
||||||
|
# Add element
|
||||||
|
if self.current_element.name == 'vertex':
|
||||||
|
self.parent.add_vertex(Vertex().from_array(property_values))
|
||||||
|
|
||||||
|
|
||||||
|
elif self.current_element.name == 'face':
|
||||||
|
|
||||||
|
# Create texture coords
|
||||||
|
for i in range(0,6,2):
|
||||||
|
tex_coord = TexCoord(*property_values[1][i:i+2])
|
||||||
|
self.parent.add_tex_coord(tex_coord)
|
||||||
|
|
||||||
|
face = Face(\
|
||||||
|
FaceVertex(property_values[0][0], len(self.parent.tex_coords)-3), \
|
||||||
|
FaceVertex(property_values[0][1], len(self.parent.tex_coords)-2), \
|
||||||
|
FaceVertex(property_values[0][2], len(self.parent.tex_coords)-1))
|
||||||
|
|
||||||
|
face.material = self.parent.materials[property_values[2]]
|
||||||
|
|
||||||
|
self.parent.add_face(face)
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.counter += 1
|
||||||
|
|
||||||
|
if self.counter == self.current_element.number:
|
||||||
|
self.next_element()
|
||||||
|
|
||||||
|
def next_element(self):
|
||||||
|
self.counter = 0
|
||||||
|
self.element_index += 1
|
||||||
|
if self.element_index < len(self.parent.elements):
|
||||||
|
self.current_element = self.parent.elements[self.element_index]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PLYBigEndianContentParser(PLYLittleEndianContentParser):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(self, parent)
|
||||||
|
|
||||||
|
def parse_bytes(self, bytes):
|
||||||
|
# Reverse bytes, and then
|
||||||
|
super().parse_bytes(self, bytes)
|
||||||
|
|
||||||
class PLYExporter(Exporter):
|
class PLYExporter(Exporter):
|
||||||
def __init__(self, model):
|
def __init__(self, model):
|
||||||
super().__init__(model)
|
super().__init__(model)
|
||||||
|
@ -132,7 +344,7 @@ class PLYExporter(Exporter):
|
||||||
|
|
||||||
# Types : vertices
|
# Types : vertices
|
||||||
string += "element vertex " + str(len(self.model.vertices)) +"\n"
|
string += "element vertex " + str(len(self.model.vertices)) +"\n"
|
||||||
string += "property float32 x\nproperty float32 y\nproperty float32 z\n"
|
string += "property float x\nproperty float y\nproperty float z\n"
|
||||||
|
|
||||||
# Types : faces
|
# Types : faces
|
||||||
string += "element face " + str(len(faces)) + "\n"
|
string += "element face " + str(len(faces)) + "\n"
|
||||||
|
|
|
@ -24,6 +24,7 @@ class Material:
|
||||||
try:
|
try:
|
||||||
ix, iy, image = self.map_Kd.size[0], self.map_Kd.size[1], self.map_Kd.tobytes("raw", "RGBA", 0, -1)
|
ix, iy, image = self.map_Kd.size[0], self.map_Kd.size[1], self.map_Kd.tobytes("raw", "RGBA", 0, -1)
|
||||||
except:
|
except:
|
||||||
|
print('Humm')
|
||||||
ix, iy, image = self.map_Kd.size[0], self.map_Kd.size[1], self.map_Kd.tobytes("raw", "RGBX", 0, -1)
|
ix, iy, image = self.map_Kd.size[0], self.map_Kd.size[1], self.map_Kd.tobytes("raw", "RGBX", 0, -1)
|
||||||
|
|
||||||
self.id = gl.glGenTextures(1)
|
self.id = gl.glGenTextures(1)
|
||||||
|
|
Loading…
Reference in New Issue