home *** CD-ROM | disk | FTP | other *** search
- # ***** 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 *****
- # -------------------------------------------------------------
- # The RenderMan (R) Interface Procedures and Protocol are:
- # Copyright 1988, 1989, 2000, Pixar
- # All Rights Reserved
- #
- # RenderMan (R) is a registered trademark of Pixar
- # -------------------------------------------------------------
- # $Id: slparams.py,v 1.4 2006/02/14 19:29:39 mbaas Exp $
-
- """Extract shader parameters from a RenderMan shader source file.
-
- Functions:
-
- - slparams()
- - convertdefault()
- """
-
- import os, os.path, sys, string, StringIO, sltokenize, types
- import cgtypes, math, sl, simplecpp
- import _slparser
-
- class SLParamsError(Exception):
- pass
-
- class PreprocessorNotFound(SLParamsError):
- pass
-
- class SyntaxError(SLParamsError):
- pass
-
- class NoMoreTokens(SLParamsError):
- pass
-
-
- # Parser class (subclassed from the generated Yapps parser)
- class _SLParser(_slparser._SLParserBase):
- """SL parser class.
-
- This class is derived from the generated parser and implements
- some missing methods.
- """
- def __init__(self, scanner):
- _slparser._SLParserBase.__init__(self, scanner)
-
- # Current filename
- self.filename = "?"
- # Offset which has to be subtracted from the line number to
- # get the true line number within the file.
- self.linenroffset = 0
-
- # Parameter attributes...
- self.output = ""
- self.detail = ""
- self.type = ""
- self.arraylen = None
- self.name = ""
- self.space = None
- self.default = ""
- self.spaces = []
-
- # Shader parameters
- self.params = []
-
- def newParams(self):
- """Start a new shader.
-
- The parameter list is cleared.
- """
- self.params = []
-
- def newType(self):
- """Clear all type parameters.
-
- This is called when a new type is declared (which is not equivalent
- to a new parameter, e.g. "float a,b,c")
- """
- self.output = ""
- self.detail = "uniform"
- self.type = ""
- self.arraylen = None
- self.name = ""
- self.space = None
- self.default = ""
- self.spaces = []
-
- def defaultSpace(self, typ):
- """Return the default space for typ."""
- if typ in ["point", "vector", "normal", "matrix"]:
- return "current"
- elif typ=="color":
- return "rgb"
- else:
- return None
-
- def appendSpace(self):
- """Append self.space to self.spaces."""
- if self.space!=None:
- self.spaces.append(self.space)
- else:
- self.spaces.append(self.defaultSpace(self.type))
-
- self.space = None
-
- def storeParam(self):
- """Store the current set of attributes as a new parameter.
-
- The attributes are reset so that a new parameter can begin.
- """
- if self.arraylen==None:
- if self.space==None:
- self.space = self.defaultSpace(self.type)
- self.params.append((self.output, self.detail, self.type, None,
- self.name, self.space, self.default))
- else:
- spaces = self.spaces
- if self.defaultSpace(self.type)==None:
- spaces = None
- self.params.append((self.output, self.detail, self.type,
- self.arraylen, self.name, spaces, self.default))
- self.arraylen = None
- self.name = ""
- self.space = None
- self.default = ""
- self.spaces = []
-
-
- def switchFile(self, cppline):
- """Switch to another file.
-
- This method is called when a preprocessor line (starting with #)
- is read. This line contains the current file name and the line number.
- """
- f = cppline.strip().split(" ")
- linenr = int(f[1])
- filename = f[2][1:-1]
- self.filename = filename
- self.linenroffset = self._scanner.get_line_number()-linenr+1
-
-
- # _SLfilter
- class _SLfilter:
- """Used by the sltokenizer to filter the Shading Language source.
-
- Only the shader and function definitions remain, the bodies will
- be dropped. The filtered result will be used as input for the
- actual parser.
- """
- def __init__(self):
- # Current {}-depth (0 = outside any {..})
- self.curly_depth = 0
- # Current ()-depth
- self.bracket_depth = 0
- # Receives the source code that'll get passed to the parser
- self.SLsource = ""
- self.stream_enabled = True
-
- self.current_filename = None
-
- def eater(self, type, tok, start, end, line, filename):
- """Record only tokens with depth 0."""
- if filename!=self.current_filename:
- self.current_filename = filename
- if self.SLsource!="" and self.SLsource[-1]!="\n":
- self.SLsource+="\n"
- self.SLsource+='# %d "%s"\n'%(start[0],filename)
-
- if tok=="}":
- self.curly_depth-=1
- if self.curly_depth==0 and self.bracket_depth==0:
- self.stream_enabled = True
- elif tok==")":
- self.bracket_depth-=1
-
- # Always record newlines so that line numbers won't get messed up
- if self.stream_enabled or tok=="\n":
- self.SLsource+=tok
-
- if tok=="{":
- if self.curly_depth==0 and self.bracket_depth==0:
- self.stream_enabled = False
- self.curly_depth+=1
- elif tok=="(":
- self.bracket_depth+=1
-
-
- # slparams
- def slparams(slfile=None, cpp=None, cpperrstream=sys.stderr, slname=None, includedirs=None, defines=None):
- """Extracts the shader parameters from a Shading Language source file.
-
- The argument slfile is either the name of the shader source file
- (*.sl) or it is a file-like object that provides the shader
- sources. cpp determines how the shader source is preprocessed. It
- can either be a string containing the name of an external
- preprocessor tool (such as 'cpp') that must take the file name as
- parameter and dump the preprocessed output to stdout or it can be
- a callable that takes slfile and cpperrstream as input and returns
- the preprocessed sources as a string. If the external
- preprocessor does not produce any data a PreprocessorNotFound
- exception is thrown.
- The error stream of the preprocessor is written to the object
- that's specified by cpperrstream which must have a write()
- method. If cpperrstream is None, the error stream is ignored.
-
- If cpp is None a simple internal preprocessor based on the
- simplecpp module is used.
- The slname argument is an alias for slfile, it's only available
- for backwards compatibility.
-
- includedirs is a list of strings that contain directories where to
- look for include files. defines is a list of tuples (name, value)
- that specify the predefined symbols to use.
-
- The function returns a list of 3-tuples, one for each shader found
- in the file. The tuple contains the type, the name and the
- parameters of the shader. The parameters are given as a list of
- 7-tuples describing each parameter. The tuple contains the
- following information (in the given order):
-
- - The output specifier (either "output" or an empty string)
- - The storage class ("uniform" or "varying")
- - The parameter type
- - The array length or None if the parameter is not an array
- - The name of the parameter
- - The space in which a point-like type was defined
- - The default value (always given as a string)
- """
-
- if slname!=None:
- slfile = slname
- if slfile==None:
- return []
-
- # Run the preprocessor on the input file...
-
- slsrc = preprocess(cpp, slfile, cpperrstream=cpperrstream, defines=defines, includedirs=includedirs)
- f = StringIO.StringIO(slsrc)
-
- # ...and filter it, so that only the shader and function
- # definitions remain...
- filter = _SLfilter()
- sltokenize.tokenize(f.readline, filter.eater)
- f.close()
-
- # print filter.SLsource
-
- # Parse the filtered source code...
- scanner = _slparser._SLParserBaseScanner(filter.SLsource)
- parser = _SLParser(scanner)
-
- # return wrap_error_reporter(parser, "definitions")
-
- try:
- return getattr(parser, "definitions")()
- except _slparser.NoMoreTokens, err:
- raise NoMoreTokens, "No more tokens"
- except _slparser.SyntaxError, err:
- scanner = parser._scanner
- input = scanner.input
- cpplineno = scanner.get_line_number()
- lineno = cpplineno-parser.linenroffset
- # '+1' at the end to exclude the newline. If no newline is found
- # rfind() returns -1, so by adding +1 we get 0 which is what we want.
- start = input.rfind("\n", 0, err.charpos)+1
- end = input.find("\n", err.charpos, -1)
- origline = input[start:end].replace("\t", " ")
- line = " "+origline.lstrip()
- errpos = err.charpos-start-(len(origline)-len(line))
- # print "%s^"%((errpos)*" ")
- msg = 'Syntax error in "%s", line %d:\n'%(parser.filename, lineno)
- msg += '\n%s\n%s^'%(line, errpos*" ")
- exc = SyntaxError(msg)
- exc.filename = parser.filename
- exc.lineno = lineno
- exc.line = line
- exc.errpos = errpos
- raise exc
-
- # preprocess
- def preprocess(cpp, file, cpperrstream=sys.stderr, defines=None, includedirs=None):
- """Preprocess an input file.
-
- cpp is either a string containing the name of an external preprocessor
- command (e.g. 'cpp'), a callable function that takes file and
- cpperrstream as input and returns the preprocessed source code as
- a string or None in which case the built-in preprocessor is used.
- If the external preprocessor produces no data it is assumed that
- it was not found a a PreprocessorNotFound exception is thrown.
-
- file is either a string containing the input file name or it's a
- file-like object.
- If the input file doesn't exist, an IOError is thrown.
-
- cpperrstream is a stream that receives any error messages from
- the preprocessor. If it's None, error messages are ignored.
-
- defines is a list of tuples (name, value) that specify the predefined
- symbols. includedirs is a list of strings with include paths.
-
- The function returns the preprocessed sources as a string.
- """
-
- if cpp==None:
- cpp = simplecpp.PreProcessor(defines=defines, includedirs=includedirs)
- return cpp(file, cpperrstream)
- # Is cpp a callable then invoke it and return the result
- elif callable(cpp):
- return cpp(file, cpperrstream)
-
- # no callable, so cpp contains the name of an external preprocessor tool
-
- # Variables:
- #
- # cmd: The command that is used to execute the preprocessor.
- # It either contains only the cpp name or the cpp name + input file
- # slsrc: (string) Unprocessed SL source if it should be passed via stdin
- # otherwise it's None.
- # ppslsrc: Preprocessed SL source (this is the result).
-
- # Is file a string? Then it's a file name...
- if isinstance(file, types.StringTypes):
- cmdtoks = [cpp]
- if defines!=None:
- for name,value in defines:
- if value==None:
- cmdtoks.append("-D%s"%name)
- else:
- cmdtoks.append("-D%s=%s"%(name,value))
- if includedirs!=None:
- cmdtoks.extend(map(lambda dir: "-I%s"%dir, includedirs))
- cmdtoks.append(file)
- cmd = " ".join(cmdtoks)
- print cmd
- slsrc = None
- # Check if the file exists and can be accessed (by trying to open it)
- # If the file doesn't exist, an exception is thrown.
- dummy = open(file)
- dummy.close()
- # ...otherwise it's a file-like object
- else:
- cmd = "%s"%cpp
- slsrc = file.read()
-
- # The preprocessed data will be in slsrc.
- stdin, stdout, stderr = os.popen3(cmd)
- if slsrc!=None:
- stdin.write(slsrc)
- stdin.close()
- ppslsrc = stdout.read()
- stdout.close()
- if cpperrstream!=None:
- errs = stderr.read()
- cpperrstream.write(errs)
- # No data? Then it's assumed that the preprocessor couldn't be found
- if len(ppslsrc)==0:
- raise PreprocessorNotFound("Calling '%s' didn't produce any data."%cmd)
-
- return ppslsrc
-
-
- # Setup local namespace for convertdefault()
- _local_namespace = {}
- exec "from cgkit.sl import *" in _local_namespace
-
- def convertdefault(paramtuple):
- """Converts the default value of a shader parameter into a Python type.
-
- paramtuple must be a 7-tuple as returned by slparams(). The
- function returns a Python object that corresponds to the default
- value of the parameter. If the default value can't be converted
- then None is returned. Only the functions that are present in the
- sl module are evaluated. If a default value calls a user defined
- function then None is returned.
-
- The SL types will be converted into the following Python types:
-
- - float -> float
- - string -> string
- - color -> vec3
- - point -> vec3
- - vector -> vec3
- - normal -> vec3
- - matrix -> mat4
-
- Arrays will be converted into lists of the corresponding type.
- """
- global _local_namespace
-
- typ = paramtuple[2]
- arraylen = paramtuple[3]
- defstr = paramtuple[6]
-
- # Replace {} with [] so that SL arrays look like Python lists
- defstr = defstr.replace("{","[").replace("}","]")
- # If the parameter is not an array, then create an array with one
- # element (to unify further processing). It will be unwrapped in the end
- if arraylen==None:
- defstr = "[%s]"%defstr
- # Evaluate the string to create "raw" Python types (lists and tuples)
- try:
- rawres = eval(defstr, globals(), _local_namespace)
- except:
- return None
-
- # Convert into the appropriate type...
- if typ=="float":
- try:
- res = map(lambda x: float(x), rawres)
- except:
- return None
- elif typ=="color" or typ=="point" or typ=="vector" or typ=="normal":
- try:
- res = map(lambda x: cgtypes.vec3(x), rawres)
- except:
- return None
- elif typ=="matrix":
- try:
- res = map(lambda x: cgtypes.mat4(x), rawres)
- except:
- return None
- elif typ=="string":
- try:
- res = map(lambda x: str(x), rawres)
- except:
- return None
-
- if arraylen==None:
- if len(res)==0:
- return None
- else:
- res = res[0]
-
- return res
-
- ######################################################################
-
- if __name__=="__main__":
- pass
-