176 lines
6.8 KiB
Python
Executable File
176 lines
6.8 KiB
Python
Executable File
|
|
import types
|
|
import os
|
|
|
|
class WFObj(object):
|
|
def __init__(self, source, fileloader=None):
|
|
self.fileloader = fileloader
|
|
self.vertices = []
|
|
self.texcoords = []
|
|
self.normals = []
|
|
self.faces = {}
|
|
self.materials = {}
|
|
if type(source) == types.StringType:
|
|
self.source_path = source
|
|
if self.fileloader is not None:
|
|
fh = self.fileloader(source)
|
|
self.load(fh)
|
|
else:
|
|
fh = open(source, 'r')
|
|
self.load(fh)
|
|
fh.close()
|
|
else:
|
|
self.source_path = ''
|
|
self.load(source)
|
|
|
|
def load(self, fh):
|
|
lineno = 0
|
|
current_material_name = ''
|
|
|
|
def add_face(*vrefs):
|
|
def split_vref(vref):
|
|
parts = map(lambda x: 0 if x == '' else int(x), vref.split('/'))
|
|
while len(parts) < 3:
|
|
parts.append(0)
|
|
return parts
|
|
vrefs_split = map(split_vref, vrefs)
|
|
for vs in vrefs_split:
|
|
if vs[0] < 1 or vs[0] > len(self.vertices):
|
|
raise ValueError('Vertex index %d out of range on line %d' % (vs[0], lineno))
|
|
if vs[1] < 0 or vs[1] > len(self.texcoords):
|
|
raise ValueError('Texture coordinate index %d out of range on line %d' % (vs[1], lineno))
|
|
if vs[2] < 0 or vs[2] > len(self.normals):
|
|
raise ValueError('Normal index %d out of range on line %d' % (vs[2], lineno))
|
|
for i in range(3):
|
|
vs[i] -= 1
|
|
self.faces[current_material_name].append(vrefs_split)
|
|
|
|
for line in iter(fh.readline, ''):
|
|
lineno += 1
|
|
line = line.strip()
|
|
if line.startswith('#'):
|
|
continue
|
|
parts = line.split()
|
|
if len(parts) == 0:
|
|
continue
|
|
if parts[0] == 'v':
|
|
# process vertex
|
|
if len(parts[1:]) < 3:
|
|
raise ValueError('Not enough components to vertex definition on line %d' % lineno)
|
|
self.vertices.append(map(float, parts[1:]))
|
|
elif parts[0] == 'vt':
|
|
# process texture coordinates
|
|
if len(parts[1:]) < 2:
|
|
raise ValueError('Not enough components to texture coordinates on line %d' % lineno)
|
|
self.texcoords.append(map(float, parts[1:]))
|
|
elif parts[0] == 'vn':
|
|
# process normal
|
|
if len(parts[1:]) < 3:
|
|
raise ValueError('Not enough components to normal vector on line %d' % lineno)
|
|
self.normals.append(map(float, parts[1:]))
|
|
elif parts[0] == 'f':
|
|
if current_material_name not in self.faces:
|
|
self.faces[current_material_name] = []
|
|
if len(parts[1:]) == 3:
|
|
# face is a triangle
|
|
add_face(*parts[1:])
|
|
elif len(parts[1:]) == 4:
|
|
# split the quad into two triangles
|
|
add_face(*parts[1:3])
|
|
add_face(parts[1], parts[3], parts[4])
|
|
else:
|
|
raise ValueError('Faces can only have 3 or 4 vertices on line %d' % lineno)
|
|
elif parts[0] == 'mtllib':
|
|
if len(parts[1:]) < 1:
|
|
raise ValueError('mtllib directive requires material library name')
|
|
self.load_material_library(parts[1])
|
|
elif parts[0] == 'usemtl':
|
|
if len(parts[1:]) < 1:
|
|
raise ValueError('usemtl directive requires material name')
|
|
current_material_name = parts[1]
|
|
elif parts[0] == 'o':
|
|
# ignore
|
|
pass
|
|
elif parts[0] == 'g':
|
|
# ignore group name
|
|
pass
|
|
elif parts[0] == 's':
|
|
# ignore smoothing
|
|
pass
|
|
else:
|
|
raise ValueError('Unknown .obj directive "%s" on line %d' % (parts[0], lineno))
|
|
|
|
class Material(object):
|
|
def __init__(self):
|
|
self.texture = ''
|
|
self.ambient = (1, 1, 1, 1)
|
|
self.diffuse = (1, 1, 1, 1)
|
|
self.specular = (1, 1, 1, 1)
|
|
self.shininess = 50
|
|
|
|
def load_material_library(self, name):
|
|
dn = os.path.dirname(self.source_path)
|
|
if dn != '':
|
|
name = dn + os.path.sep + name
|
|
if self.fileloader is not None:
|
|
fh = self.fileloader(name)
|
|
else:
|
|
fh = open(name, 'r')
|
|
material = None
|
|
material_name = ''
|
|
lineno = 0
|
|
for line in iter(fh.readline, ''):
|
|
lineno += 1
|
|
line = line.strip()
|
|
if line.startswith('#'):
|
|
continue
|
|
parts = line.split()
|
|
if len(parts) < 1:
|
|
continue
|
|
if parts[0] == 'newmtl':
|
|
if len(parts[1:]) < 1:
|
|
raise ValueError('newmtl directive requires argument on line %d' % lineno)
|
|
material = self.Material()
|
|
material_name = parts[1]
|
|
self.materials[material_name] = material
|
|
continue
|
|
else:
|
|
if material is None:
|
|
raise ValueError('Material directive preceeding "newmtl" on line %d' % lineno)
|
|
if parts[0] == 'Ns':
|
|
material.shininess = float(parts[1])
|
|
elif parts[0] == 'Ka':
|
|
if len(parts[1:]) < 3:
|
|
raise ValueError('Ka requires 3 arguments on line %d' % lineno)
|
|
material.ambient = map(float, parts[1:])
|
|
if len(material.ambient) < 4:
|
|
material.ambient.append(1.0)
|
|
elif parts[0] == 'Kd':
|
|
if len(parts[1:]) < 3:
|
|
raise ValueError('Kd requires 3 arguments on line %d' % lineno)
|
|
material.diffuse = map(float, parts[1:])
|
|
if len(material.diffuse) < 4:
|
|
material.diffuse.append(1.0)
|
|
elif parts[0] == 'Ks':
|
|
if len(parts[1:]) < 3:
|
|
raise ValueError('Ks requires 3 arguments on line %d' % lineno)
|
|
material.specular = map(float, parts[1:])
|
|
if len(material.specular) < 4:
|
|
material.specular.append(1.0)
|
|
elif parts[0] == 'Ni':
|
|
# ignore
|
|
pass
|
|
elif parts[0] == 'd':
|
|
# ignore
|
|
pass
|
|
elif parts[0] == 'illum':
|
|
# ignore
|
|
pass
|
|
elif parts[0] == 'map_Kd':
|
|
material.texture = parts[1]
|
|
else:
|
|
raise ValueError('Unknown material directive "%s" on line %d' % (parts[0], lineno))
|
|
|
|
if self.fileloader is None:
|
|
fh.close()
|