From 5e45d029b462840d4acf323d91f9cb0c9466f47a Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 8 Jul 2012 21:02:04 -0400 Subject: [PATCH] initial work on WFObj class --- wfobj.py | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100755 wfobj.py diff --git a/wfobj.py b/wfobj.py new file mode 100755 index 0000000..aad92b8 --- /dev/null +++ b/wfobj.py @@ -0,0 +1,166 @@ + +import types + +class WFObj(object): + def __init__(source, fileloader=None): + self.fileloader = fileloader + self.vertices = [] + self.texcoords = [] + self.normals = [] + self.faces = {} + self.materials = {} + if type(source) == types.StringType: + 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.load(source) + + def load(fh): + lineno = 0 + current_material_name = '' + + def add_face(*vrefs): + def split_vref(vref): + parts = map(int, 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(name): + if self.fileloader is not None: + fh = self.fileloader(name) + else: + fh = open(name, 'r') + material = None + material_name = '' + for line in iter(fh.readline, ''): + line = line.strip() + if line.startswith('#'): + next + parts = line.split() + if len(parts) < 1: + next + if parts[0] == 'newmtl': + if len(parts[1:]) < 1: + raise ValueError('newmtl directive requires argument') + material = Material() + material_name = parts[1] + self.materials[material_name] = material + else: + if material is None: + raise ValueError('Material directive preceeding "newmtl"') + if parts[0] == 'Ns': + material.shininess = float(parts[1]) + elif parts[0] == 'Ka': + if len(parts[1:]) < 3: + raise ValueError('Ka requires 3 arguments') + 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') + 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') + 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"' % parts[0]) + + if self.fileloader is None: + fh.close()