Files
speckle-server/packages/fileimport-service/obj/obj_file.py
T

121 lines
4.4 KiB
Python

from mtl_file_collection import MtlFileCollection
import os
class ObjFile(object):
def __init__(self, file_path) -> None:
self.logged_unsupported = set()
self.mtl_files = MtlFileCollection(os.path.dirname(file_path))
self.crt_object = ''
self.crt_mtl = ''
self.vertices = []
self.vertex_colors = []
self.faces = []
# Constructed in the post-process phase
self.objects = {}
with open(file_path, 'r') as f:
while True:
line = f.readline()
if not line:
break
if not line.strip() or line.startswith('#'):
continue
parts = line.strip().split(' ')
if parts[0] == 'v':
self.on_v(parts[1:])
elif parts[0] == 'l':
self.on_l(parts[1:])
elif parts[0] == 'f':
self.on_f(parts[1:])
elif parts[0] == 'mtllib':
self.mtl_files.mtllib(' '.join(parts[1:]))
elif parts[0] == 'usemtl':
self.crt_mtl = ' '.join(parts[1:])
elif parts[0] == 'o':
self.crt_object = parts[1]
else:
if parts[0] not in self.logged_unsupported:
print('Unsupported OBJ directive: ' + parts[0])
self.logged_unsupported.add(parts[0])
self.post_process()
def flatten_vertices(self):
return [coord for point in self.vertices for coord in point]
def on_v(self, params):
r, g, b = None, None, None
w = 1.0
if len(params) == 3:
x, y, z = [float(param) for param in params]
if len(params) == 4:
x, y, z, w = [float(param) for param in params]
if len(params) == 6:
x, y, z, r, g, b = [float(param) for param in params]
self.vertices.append((x, z, y))
if r is None or g is None or b is None:
self.vertex_colors.append(None)
else:
self.vertex_colors.append((r, g, b))
def on_l(self, params):
# TODO: handle lines
pass
def on_f(self, params):
indices = []
for param in params:
# TODO: use texture coordinate index / use vertex normal index?
v_index = int(param.split('/')[0])
# If an index is positive then it refers to the offset in that vertex list, starting at 1.
# If an index is negative then it relatively refers to the end of the vertex list, -1 referring to the last element.
if v_index > 0:
v_index -= 1
indices.append(v_index)
self.faces.append({
'indices': indices,
'object': self.crt_object,
'mtl': self.crt_mtl
})
def post_process(self):
# Step 1: group into object_id/material_id/[faces_with_global_indices]
objects = {}
for face in self.faces:
if face['object'] not in objects:
objects[face['object']] = {}
obj = objects[face['object']]
if face['mtl'] not in obj:
obj[face['mtl']] = []
obj[face['mtl']].append(face['indices'])
# Step 2: construct final structure: object_id / [{material, local_vertices, vertex_colors, faces_with_local_indices}]
for object in objects:
self.objects[object] = []
for mtl in objects[object].keys():
material = self.mtl_files.get_material(mtl)
vertices = []
vertex_colors = []
faces = []
v_global2local_id = {}
for face in objects[object][mtl]:
for global_v in face:
if global_v not in v_global2local_id:
v_global2local_id[global_v] = len(vertices)
vertices.append(self.vertices[global_v])
vertex_colors.append(self.vertex_colors[global_v])
faces.append([v_global2local_id[global_id] for global_id in face])
self.objects[object].append({
'material': material,
'vertices': vertices,
'vertex_colors': vertex_colors,
'faces': faces
})