pywfobj/wfobj.py

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[0] > 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()