135 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| from .basemodel import ModelParser, Exporter, Vertex, TexCoord, Normal, FaceVertex, Face
 | |
| from .mesh import Material, MeshPart
 | |
| from functools import reduce
 | |
| import os.path
 | |
| import PIL.Image
 | |
| 
 | |
| 
 | |
| def is_obj(filename):
 | |
|     return filename[-4:] == '.obj'
 | |
| 
 | |
| class OBJParser(ModelParser):
 | |
| 
 | |
|     def __init__(self):
 | |
|         super().__init__()
 | |
|         self.current_material = None
 | |
| 
 | |
|     def parse_line(self, string):
 | |
|         if string == '':
 | |
|             return
 | |
| 
 | |
|         split = string.split()
 | |
|         first = split[0]
 | |
|         split = split[1:]
 | |
| 
 | |
|         if first == 'usemtl':
 | |
|             self.current_material = self.mtl[split[0]]
 | |
|         elif first == 'mtllib':
 | |
|             path = os.path.join(os.path.dirname(self.path), split[0])
 | |
|             self.mtl = MTLParser(self)
 | |
|             self.mtl.parse_file(path)
 | |
|         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] = int(splits[i][j]) - 1
 | |
| 
 | |
|             face = Face().from_array(splits)
 | |
|             face.material = self.current_material
 | |
|             self.add_face(face)
 | |
| 
 | |
| class MTLParser:
 | |
| 
 | |
|     def __init__(self, parent):
 | |
|         self.parent = parent
 | |
|         self.materials = []
 | |
|         self.current_mtl = None
 | |
| 
 | |
|     def parse_line(self, string):
 | |
| 
 | |
|         if string == '':
 | |
|             return
 | |
| 
 | |
|         split = string.split()
 | |
|         first = split[0]
 | |
|         split = split[1:]
 | |
| 
 | |
|         if first == 'newmtl':
 | |
|             self.current_mtl = Material(split[0])
 | |
|             self.materials.append(self.current_mtl)
 | |
|         elif first == 'Ka':
 | |
|             self.current_mtl.Ka = Vertex().from_array(split)
 | |
|         elif first == 'Kd':
 | |
|             self.current_mtl.Kd = Vertex().from_array(split)
 | |
|         elif first == 'Ks':
 | |
|             self.current_mtl.Ks = Vertex().from_array(split)
 | |
|         elif first == 'map_Kd':
 | |
|             self.current_mtl.map_Kd = PIL.Image.open(os.path.join(os.path.dirname(self.parent.path), split[0]))
 | |
| 
 | |
| 
 | |
|     def parse_file(self, path):
 | |
|         with open(path) as f:
 | |
|             for line in f.readlines():
 | |
|                 line = line.rstrip()
 | |
|                 self.parse_line(line)
 | |
| 
 | |
|     def __getitem__(self, key):
 | |
|         for material in self.materials:
 | |
|             if material.name == key:
 | |
|                 return material
 | |
| 
 | |
| 
 | |
| class OBJExporter(Exporter):
 | |
|     def __init__(self, model):
 | |
|         super().__init__(model)
 | |
| 
 | |
|     def __str__(self):
 | |
|         string = ""
 | |
| 
 | |
|         for vertex in self.model.vertices:
 | |
|             string += "v " + ' '.join([str(vertex.x), str(vertex.y), str(vertex.z)]) + "\n"
 | |
| 
 | |
|         string += "\n"
 | |
| 
 | |
|         if len(self.model.tex_coords) > 0:
 | |
|             for tex_coord in self.model.tex_coords:
 | |
|                 string += "vt " + ' '.join([str(tex_coord.x), str(tex_coord.y)]) + "\n"
 | |
| 
 | |
|             string += "\n"
 | |
| 
 | |
|         if len(self.model.normals) > 0:
 | |
|             for normal in self.model.normals:
 | |
|                 string += "vn " + ' '.join([str(normal.x), str(normal.y), str(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(str(v.vertex + 1))
 | |
|                 if v.normal is None:
 | |
|                     if v.texture is not None:
 | |
|                         sub_arr.append('')
 | |
|                         sub_arr.append(str(v.texture + 1))
 | |
|                 elif v.texture is not None:
 | |
|                     sub_arr.append(str(v.texture + 1))
 | |
|                     if v.normal is not None:
 | |
|                         sub_arr.append(str(v.normal + 1))
 | |
|                 arr.append('/'.join(sub_arr))
 | |
| 
 | |
|             string += ' '.join(arr) + '\n'
 | |
|         return string
 | |
| 
 |