# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Python Computer Graphics Kit.
#
# The Initial Developer of the Original Code is Matthias Baas.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
# $Id: objmtl.py,v 1.3 2006/03/20 19:32:12 mbaas Exp $
from cgtypes import *
# WavefrontReaderBase
class WavefrontReaderBase:
"""Wavefront OBJ/MTL reader base class.
This is the base class for the OBJ and the MTL reader.
"""
def __init__(self):
"""Constructor.
"""
self.linenr = 0
self.line = ""
# read
def read(self, f):
"""Read the content of a file.
f is a file like object that can be used to read the content
of the file.
The file is read and for each keyword a handle_<keyword>() method
is called with the data as argument (the number of arguments depends
on the keyword). Each argument is of type str.
A syntax error is generated if such a handler method is not available.
The task of these handler methods is to preprocess the data, check
for errors and invoke the final handler methods which are just called
after the keyword.
Before the file is read, the begin() method is called. At the end,
the end() method is called.
"""
self.linenr = 0
self.begin()
for self.line in f:
self.linenr+=1
line2 = self.line.strip()
# Ignore empty lines and comments
if line2=="" or line2[0] in ['#', '$', '!', '@']:
continue
a = line2.split()
cmd = a[0]
args = a[1:]
handler = getattr(self, "handle_%s"%cmd, None)
if handler!=None:
handler(*args)
else:
self.handleUnknown(cmd, args)
self.end()
# begin
def begin(self):
"""Begin reading a file.
This method is called before the file is read.
"""
pass
# end
def end(self):
"""End of reading.
This method is called after the file was read.
"""
pass
# handleUnknown
def handleUnknown(self, cmd, arglist):
"""Handle unknown keywords.
cmd is the command keyword (the 1st argument in the current line)
and arglist is the data (the remaining arguments).
The default implementation raises a SyntaxError exception.
"""
raise SyntaxError, "Unknown statement in line %d: %s"%(self.linenr, self.line)
# MTLReader
class MTLReader(WavefrontReaderBase):
"""Wavefront MTL reader.
This class can be used as base class to read a MTL file.
"""
def __init__(self):
"""Constructor.
"""
WavefrontReaderBase.__init__(self)
# def handleUnknown(self, cmd, arglist):
# pass
# print "Unknown:",cmd,arglist
# parseMapArgs
def parseMapArgs(self, args):
"""Parse the arguments from a map_xyz line into options and names.
args must be a list of strings containing the arguments (already
split).
Returns a 2-tuple (options, names) where options is a dictionary
that contains the options that were present in args (key is
the option string). names is a list of strings that were no option.
"""
options = {}
names = []
while len(args)>0:
a = args[0]
# Is this an option?
if a[0]=="-":
if a=="-s":
val = tuple(map(lambda x: float(x), args[1:4]))
options[a] = val
args = args[4:]
elif a=="-t":
val = tuple(map(lambda x: float(x), args[1:4]))
options[a] = val
args = args[4:]
elif a=="-o":
val = tuple(map(lambda x: float(x), args[1:4]))
options[a] = val
args = args[4:]
elif a=="-mm":
val = tuple(map(lambda x: float(x), args[1:3]))
options[a] = val
args = args[3:]
elif a=="-bm":
options[a] = float(args[1])
args = args[2:]
elif a=="-type":
options[a] = args[1]
args = args[2:]
elif a=="-clamp" or a=="-blendu" or a=="-blendv":
options[a] = args[1].lower()=="on"
args = args[2:]
else:
raise SyntaxError, "Unknown map option in line %d: %s"%(self.linenr, a)
else: # no option
names.append(a)
args = args[1:]
return options, names
# Pre handler methods (they must be called "handle_<keyword>")
def handle_newmtl(self, *name):
"""New material command.
name is the name of the material (all names are concatenated).
"""
self.newmtl(" ".join(name))
def handle_illum(self, model):
"""Illumination model."""
self.illum(int(model))
def handle_Ns(self, shininess):
"""Shininess coefficient.
"""
self.Ns(float(shininess))
def handle_Ka(self, r, g, b):
"""Ambient color.
"""
self.Ka(vec3(float(r), float(g), float(b)))
def handle_Kd(self, r, g, b):
"""Diffuse color.
"""
self.Kd(vec3(float(r), float(g), float(b)))
def handle_Ks(self, r, g, b):
"""Specular color.
"""
self.Ks(vec3(float(r), float(g), float(b)))
def handle_Ke(self, r, g, b):
"""Emissive color.
"""
self.Ke(vec3(float(r), float(g), float(b)))
def handle_Tr(self, alpha):
"""Transparency."""
self.Tr(float(alpha))
def handle_d(self, alpha):
"""Dissolve factor (transparency)."""
self.d(float(alpha))
def handle_Tf(self, *args):
"""Transparency (as color)."""
if len(args)==1:
v = vec3(float(args[0]))
else:
r,g,b = args
v = vec3(float(r), float(g), float(b))
self.Tf(v)
def handle_Ni(self, ref):
"""Refraction index."""
self.Ni(float(ref))
def handle_sharpness(self, v):
"""Sharpness value."""
self.sharpness(float(v))
def handle_map_Ka(self, *mapargs):
"""Ambient texture file."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.map_Ka(args[0], opts)
def handle_map_Kd(self, *mapargs):
"""Diffuse texture file."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.map_Kd(args[0], opts)
def handle_map_Ks(self, *mapargs):
"""Specular texture file."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.map_Ks(args[0], opts)
def handle_map_Ke(self, *mapargs):
"""Emission texture file."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.map_Ke(args[0], opts)
def handle_map_Ns(self, *mapargs):
"""Shininess texture file."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.map_Ns(args[0], opts)
def handle_map_d(self, *mapargs):
"""Opacity texture file."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.map_d(args[0], opts)
def handle_map_Bump(self, *mapargs):
"""Bump map texture file."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.map_Bump(args[0], opts)
def handle_refl(self, *mapargs):
"""Reflection map."""
opts, args = self.parseMapArgs(mapargs)
if len(args)>0:
self.refl(args[0], opts)
# Handler methods
def newmtl(self, name):
pass
def illum(self, model):
"""Illumination model."""
pass
def Ns(self, shininess):
"""Shininess."""
pass
def Ka(self, c):
"""Ambient color.
c is a vec3 containing the color.
"""
pass
def Kd(self, c):
"""Diffuse color.
c is a vec3 containing the color.
"""
pass
def Ks(self, c):
"""Specular color.
c is a vec3 containing the color.
"""
pass
def Ke(self, c):
"""Emissive color.
c is a vec3 containing the color.
"""
pass
def Tr(self, alpha):
"""Transparency."""
pass
def d(self, alpha):
"""Dissolve factor (transparency)."""
pass
def Tf(self, c):
"""Transparency.
c is a vec3 containing a color.
"""
pass
def Ni(self, ref):
"""Refraction index."""
pass
def sharpness(self, v):
"""Sharpness value."""
pass
def map_Ka(self, mapname, options):
pass
def map_Kd(self, mapname, options):
pass
def map_Ks(self, mapname, options):
pass
def map_Ke(self, mapname, options):
pass
def map_Ns(self, mapname, options):
pass
def map_d(self, mapname, options):
pass
def map_Bump(self, mapname, options):
pass
def refl(self, mapname, options):
pass
# OBJReader
class OBJReader(WavefrontReaderBase):
"""Wavefront OBJ reader.
This class can be used as base class to read an OBJ file.
"""
def __init__(self):
"""Constructor.
"""
WavefrontReaderBase.__init__(self)
self.v_count = 0
self.vp_count = 0
self.vt_count = 0
self.vn_count = 0
# read
def read(self, f):
self.v_count = 0
self.vp_count = 0
self.vt_count = 0
self.vn_count = 0
WavefrontReaderBase.read(self, f)
# Pre handler methods (they must be called "handle_<keyword>")
def handle_mtllib(self, *files):
"""Material library command.
files contains the filenames.
"""
if len(files)==0:
raise SyntaxError, "No material library given in line %d: %s"%(self.linenr, self.line)
self.mtllib(*files)
def handle_usemtl(self, *name):
"""Material name.
(all names are concatenated)
"""
self.usemtl(" ".join(name))
def handle_g(self, *groups):
"""Group command.
groups are the group names that the following geometry belongs to.
"""
if len(groups)==0:
groups = ("default",)
# raise SyntaxError, "No group name given in line %d: %s"%(self.linenr, self.line)
self.g(*groups)
def handle_s(self, group_number):
"""Smoothing group command.
group_number is a string that contains the smoothing group number
or "off".
"""
group_number = group_number.lower()
if group_number=="off":
group_number = 0
try:
group_number = int(group_number)
except:
raise SyntaxError, "Invalid smoothing group number in line %d: %s"%(self.linenr, self.line)
self.s(group_number)
def handle_v(self, x, y, z, w=1):
"""Vertex definition.
"""
self.v_count += 1
self.v(vec4(float(x), float(y), float(z), float(w)))
def handle_vp(self, u, v=0, w=1):
"""Vertex in parameter space.
"""
self.vp_count += 1
self.vp(vec3(float(u), float(v), float(w)))
def handle_vn(self, x, y, z):
"""Normal.
"""
self.vn_count += 1
self.vn(vec3(float(x), float(y), float(z)))
def handle_vt(self, u, v=0, w=0):
"""Texture vertex.
"""
self.vt_count += 1
self.vt(vec3(float(u), float(v), float(w)))
def handle_p(self, *verts):
"""Points."""
if len(verts)==0:
raise SyntaxError, "At least 1 vertex required in line %d: %s"%(self.linenr, self.line)
vlist = []
for s in verts:
vert = int(s)
if vert<0:
vert = self.v_count+vert+1
if vert==0:
raise ValueError, "0-index in line %d: %s"%(self.linenr, self.line)
vlist.append(vert)
self.p(*vlist)
def handle_l(self, *verts):
"""Line."""
if len(verts)<2:
raise SyntaxError, "At least 2 vertices required in line %d: %s"%(self.linenr, self.line)
vlist = []
for s in verts:
a = s.split("/")
if len(a)==0 or len(a)>2:
raise SyntaxError, "Syntax error in line %d: %s"%(self.linenr, self.line)
vert = int(a[0])
if vert<0:
vert = self.v_count+vert+1
tvert = None
if len(a)>1:
if a[1]!="":
tvert = int(a[1])
if tvert<0:
tvert = self.vt_count+tvert+1
if vert==0 or tvert==0:
raise ValueError, "0-index in line %d: %s"%(self.linenr, self.line)
vlist.append((vert, tvert))
self.l(*vlist)
def handle_f(self, *verts):
"""Polygonal face.
"""
if len(verts)<3:
raise SyntaxError, "At least 3 vertices required in line %d: %s"%(self.linenr, self.line)
vlist = []
for s in verts:
a = s.split("/")
if len(a)==0 or len(a)>3:
raise SyntaxError, "Syntax error in line %d: %s"%(self.linenr, self.line)
vert = int(a[0])
if vert<0:
vert = self.v_count+vert+1
tvert = None
normal = None
if len(a)>1:
if a[1]!="":
tvert = int(a[1])
if tvert<0:
tvert = self.vt_count+tvert+1
if len(a)>2 and a[2]!="":
normal = int(a[2])
if normal<0:
normal = self.vn_count+normal+1
if vert==0 or tvert==0 or normal==0:
raise ValueError, "0-index in line %d: %s"%(self.linenr, self.line)
vlist.append((vert, tvert, normal))
self.f(*vlist)
def handle_o(self, name):
"""Object name.
"""
self.o(name)
def handle_bevel(self, on_off):
"""Bevel interpolation on/off.
"""
self.bevel(on_off)
def handle_c_interp(self, on_off):
"""Color interpolation on/off.
"""
self.c_interp(on_off)
def handle_d_interp(self, on_off):
"""Dissolve interpolation on/off.
"""
self.d_interp(on_off)
def handle_lod(self, level):
"""Level of Detail.
"""
self.lod(int(level))
def handle_shadow_obj(self, filename):
"""Shadow object.
"""
self.shadow_obj(filename)
def handle_trace_obj(self, filename):
"""Shadow object.
"""
self.trace_obj(filename)
# Handler methods
def call(self, filename, *args):
pass
def csh(self, cmd):
pass
def mtllib(self, *files):
"""Specification of material libraries.
files is a sequence of file names that contain material definitions.
"""
pass
def usemtl(self, name):
"""Material name.
name is a string containing the name of the material to use for
the following elements.
"""
pass
def g(self, *groups):
"""Grouping statement.
groups is a sequence of group names that the following geometry
belongs to.
"""
pass
def s(self, group_number):
"""Smoothing group.
group_number is an integer containing the smoothing group number.
Smoothing groups should be turned off if the group number is 0.
"""
pass
def v(self, vert):
"""Geometric vertex.
vert is always a vec4."""
pass
def vp(self, vert):
"""A point in parameter space.
This vertex is used for free-form curves or surfaces.
vert is always a vec3.
"""
pass
def vn(self, normal):
"""Normal vector.
normal is a vec3.
"""
pass
def vt(self, tvert):
"""Texture vertex.
tvert is always a vec3.
"""
pass
def p(self, *verts):
"""Points.
verts is a list of vertex indices. The indices are always >0
(negative indices are automatically converted to their
corresponding positive indices).
All indices are 1-based. If an index in the file was 0, an exception
was already thrown.
"""
print "POINT",verts
def l(self, *verts):
"""Line.
verts contains 2-tuples (vert, tvert) which contains the indices
to the vertexa and the texture vertex. tvert may be None.
The indices are always >0 (negative indices are automatically
converted to their corresponding positive indices).
All indices are 1-based. If an index in the file was 0, an exception
was already thrown.
"""
pass
def f(self, *verts):
"""Polygonal face.
verts contains 3-tuples (vert, tvert, normal) which contains
the indices to the vertex, the texture vertex and the normal.
tvert and normal may be None, otherwise the values are always >0
(negative indices are automatically converted to their corresponding
positive indices).
All indices are 1-based. If an index in the file was 0, an exception
was already thrown.
"""
pass
def o(self, name):
"""Optional object name.
name is a string containing the specified name for the elements
following this statement.
"""
pass
def bevel(self, on_off):
pass
def c_interp(self, on_off):
pass
def d_interp(self, on_off):
pass
def lod(self, level):
"""
level is an integer.
"""
pass
def shadow_obj(self, filename):
pass
def trace_obj(self, filename):
pass
|