home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / cgkit / slparams.py < prev    next >
Encoding:
Python Source  |  2007-01-11  |  16.1 KB  |  472 lines

  1. # ***** BEGIN LICENSE BLOCK *****
  2. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. #
  4. # The contents of this file are subject to the Mozilla Public License Version
  5. # 1.1 (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. # http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS IS" basis,
  10. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. # for the specific language governing rights and limitations under the
  12. # License.
  13. #
  14. # The Original Code is the Python Computer Graphics Kit.
  15. #
  16. # The Initial Developer of the Original Code is Matthias Baas.
  17. # Portions created by the Initial Developer are Copyright (C) 2004
  18. # the Initial Developer. All Rights Reserved.
  19. #
  20. # Contributor(s):
  21. #
  22. # Alternatively, the contents of this file may be used under the terms of
  23. # either the GNU General Public License Version 2 or later (the "GPL"), or
  24. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  25. # in which case the provisions of the GPL or the LGPL are applicable instead
  26. # of those above. If you wish to allow use of your version of this file only
  27. # under the terms of either the GPL or the LGPL, and not to allow others to
  28. # use your version of this file under the terms of the MPL, indicate your
  29. # decision by deleting the provisions above and replace them with the notice
  30. # and other provisions required by the GPL or the LGPL. If you do not delete
  31. # the provisions above, a recipient may use your version of this file under
  32. # the terms of any one of the MPL, the GPL or the LGPL.
  33. #
  34. # ***** END LICENSE BLOCK *****
  35. # -------------------------------------------------------------
  36. # The RenderMan (R) Interface Procedures and Protocol are:
  37. # Copyright 1988, 1989, 2000, Pixar
  38. # All Rights Reserved
  39. #
  40. # RenderMan (R) is a registered trademark of Pixar
  41. # -------------------------------------------------------------
  42. # $Id: slparams.py,v 1.4 2006/02/14 19:29:39 mbaas Exp $
  43.  
  44. """Extract shader parameters from a RenderMan shader source file.
  45.  
  46. Functions:
  47.  
  48. - slparams()
  49. - convertdefault()
  50. """
  51.  
  52. import os, os.path, sys, string, StringIO, sltokenize, types
  53. import cgtypes, math, sl, simplecpp
  54. import _slparser
  55.  
  56. class SLParamsError(Exception):
  57.     pass
  58.  
  59. class PreprocessorNotFound(SLParamsError):
  60.     pass
  61.  
  62. class SyntaxError(SLParamsError):
  63.     pass
  64.  
  65. class NoMoreTokens(SLParamsError):
  66.     pass
  67.  
  68.  
  69. # Parser class (subclassed from the generated Yapps parser)
  70. class _SLParser(_slparser._SLParserBase):
  71.     """SL parser class.
  72.  
  73.     This class is derived from the generated parser and implements
  74.     some missing methods.
  75.     """
  76.     def __init__(self, scanner):
  77.         _slparser._SLParserBase.__init__(self, scanner)
  78.  
  79.         # Current filename
  80.         self.filename = "?"
  81.         # Offset which has to be subtracted from the line number to
  82.         # get the true line number within the file.
  83.         self.linenroffset = 0
  84.  
  85.         # Parameter attributes...
  86.         self.output   = ""
  87.         self.detail   = ""
  88.         self.type     = ""
  89.         self.arraylen = None
  90.         self.name     = ""
  91.         self.space    = None
  92.         self.default  = ""
  93.         self.spaces   = []
  94.  
  95.         # Shader parameters
  96.         self.params = []
  97.  
  98.     def newParams(self):
  99.         """Start a new shader.
  100.  
  101.         The parameter list is cleared.
  102.         """
  103.         self.params = []
  104.  
  105.     def newType(self):
  106.         """Clear all type parameters.
  107.  
  108.         This is called when a new type is declared (which is not equivalent
  109.         to a new parameter, e.g. "float a,b,c")
  110.         """
  111.         self.output   = ""
  112.         self.detail   = "uniform"
  113.         self.type     = ""
  114.         self.arraylen = None
  115.         self.name     = ""
  116.         self.space    = None
  117.         self.default  = ""
  118.         self.spaces   = []
  119.  
  120.     def defaultSpace(self, typ):
  121.         """Return the default space for typ."""
  122.         if typ in ["point", "vector", "normal", "matrix"]:
  123.             return "current"
  124.         elif typ=="color":
  125.             return "rgb"
  126.         else:
  127.             return None
  128.         
  129.     def appendSpace(self):
  130.         """Append self.space to self.spaces."""
  131.         if self.space!=None:
  132.             self.spaces.append(self.space)
  133.         else:
  134.             self.spaces.append(self.defaultSpace(self.type))
  135.                 
  136.         self.space = None
  137.  
  138.     def storeParam(self):
  139.         """Store the current set of attributes as a new parameter.
  140.  
  141.         The attributes are reset so that a new parameter can begin.
  142.         """
  143.         if self.arraylen==None:
  144.             if self.space==None:
  145.                 self.space = self.defaultSpace(self.type)
  146.             self.params.append((self.output, self.detail, self.type, None,
  147.                             self.name, self.space, self.default))
  148.         else:
  149.             spaces = self.spaces
  150.             if self.defaultSpace(self.type)==None:
  151.                 spaces = None
  152.             self.params.append((self.output, self.detail, self.type,
  153.                                self.arraylen, self.name, spaces, self.default))
  154.         self.arraylen = None
  155.         self.name = ""
  156.         self.space = None
  157.         self.default = ""
  158.         self.spaces   = []
  159.         
  160.  
  161.     def switchFile(self, cppline):
  162.         """Switch to another file.
  163.  
  164.         This method is called when a preprocessor line (starting with #)
  165.         is read. This line contains the current file name and the line number.
  166.         """
  167.         f = cppline.strip().split(" ")
  168.         linenr = int(f[1])
  169.         filename = f[2][1:-1]
  170.         self.filename = filename
  171.         self.linenroffset = self._scanner.get_line_number()-linenr+1
  172.  
  173.         
  174. # _SLfilter
  175. class _SLfilter:
  176.     """Used by the sltokenizer to filter the Shading Language source.
  177.  
  178.     Only the shader and function definitions remain, the bodies will
  179.     be dropped. The filtered result will be used as input for the
  180.     actual parser.
  181.     """
  182.     def __init__(self):
  183.         # Current {}-depth (0 = outside any {..})
  184.         self.curly_depth = 0
  185.         # Current ()-depth
  186.         self.bracket_depth = 0
  187.         # Receives the source code that'll get passed to the parser
  188.         self.SLsource = ""
  189.         self.stream_enabled = True
  190.  
  191.         self.current_filename = None
  192.         
  193.     def eater(self, type, tok, start, end, line, filename):
  194.         """Record only tokens with depth 0."""
  195.         if filename!=self.current_filename:
  196.             self.current_filename = filename
  197.             if self.SLsource!="" and self.SLsource[-1]!="\n":
  198.                 self.SLsource+="\n"
  199.             self.SLsource+='# %d "%s"\n'%(start[0],filename)
  200.         
  201.         if tok=="}":
  202.             self.curly_depth-=1
  203.             if self.curly_depth==0 and self.bracket_depth==0:
  204.                 self.stream_enabled = True
  205.         elif tok==")":
  206.             self.bracket_depth-=1
  207.  
  208.         # Always record newlines so that line numbers won't get messed up
  209.         if self.stream_enabled or tok=="\n":
  210.             self.SLsource+=tok
  211.             
  212.         if tok=="{":
  213.             if self.curly_depth==0 and self.bracket_depth==0:
  214.                 self.stream_enabled = False
  215.             self.curly_depth+=1
  216.         elif tok=="(":
  217.             self.bracket_depth+=1
  218.  
  219.  
  220. # slparams
  221. def slparams(slfile=None, cpp=None, cpperrstream=sys.stderr, slname=None, includedirs=None, defines=None):
  222.     """Extracts the shader parameters from a Shading Language source file.
  223.  
  224.     The argument slfile is either the name of the shader source file
  225.     (*.sl) or it is a file-like object that provides the shader
  226.     sources.  cpp determines how the shader source is preprocessed. It
  227.     can either be a string containing the name of an external
  228.     preprocessor tool (such as 'cpp') that must take the file name as
  229.     parameter and dump the preprocessed output to stdout or it can be
  230.     a callable that takes slfile and cpperrstream as input and returns
  231.     the preprocessed sources as a string. If the external
  232.     preprocessor does not produce any data a PreprocessorNotFound
  233.     exception is thrown.
  234.     The error stream of the preprocessor is written to the object
  235.     that's specified by cpperrstream which must have a write()
  236.     method. If cpperrstream is None, the error stream is ignored.
  237.  
  238.     If cpp is None a simple internal preprocessor based on the
  239.     simplecpp module is used.
  240.     The slname argument is an alias for slfile, it's only available
  241.     for backwards compatibility.
  242.     
  243.     includedirs is a list of strings that contain directories where to
  244.     look for include files. defines is a list of tuples (name, value)
  245.     that specify the predefined symbols to use.
  246.     
  247.     The function returns a list of 3-tuples, one for each shader found
  248.     in the file. The tuple contains the type, the name and the
  249.     parameters of the shader. The parameters are given as a list of
  250.     7-tuples describing each parameter. The tuple contains the
  251.     following information (in the given order):
  252.  
  253.     - The output specifier (either "output" or an empty string)
  254.     - The storage class ("uniform" or "varying")
  255.     - The parameter type
  256.     - The array length or None if the parameter is not an array
  257.     - The name of the parameter
  258.     - The space in which a point-like type was defined
  259.     - The default value (always given as a string)
  260.     """
  261.  
  262.     if slname!=None:
  263.         slfile = slname
  264.     if slfile==None:
  265.         return []
  266.  
  267.     # Run the preprocessor on the input file...
  268.     
  269.     slsrc = preprocess(cpp, slfile, cpperrstream=cpperrstream, defines=defines, includedirs=includedirs)
  270.     f = StringIO.StringIO(slsrc)
  271.     
  272.     # ...and filter it, so that only the shader and function
  273.     # definitions remain...
  274.     filter = _SLfilter()
  275.     sltokenize.tokenize(f.readline, filter.eater)
  276.     f.close()
  277.  
  278. #    print filter.SLsource
  279.  
  280.     # Parse the filtered source code...
  281.     scanner = _slparser._SLParserBaseScanner(filter.SLsource)
  282.     parser = _SLParser(scanner)
  283.     
  284. #    return wrap_error_reporter(parser, "definitions")
  285.  
  286.     try:
  287.         return getattr(parser, "definitions")()
  288.     except _slparser.NoMoreTokens, err:
  289.         raise NoMoreTokens, "No more tokens"
  290.     except _slparser.SyntaxError, err:
  291.         scanner = parser._scanner
  292.         input = scanner.input
  293.         cpplineno = scanner.get_line_number()
  294.         lineno = cpplineno-parser.linenroffset
  295.         # '+1' at the end to exclude the newline. If no newline is found
  296.         # rfind() returns -1, so by adding +1 we get 0 which is what we want.
  297.         start = input.rfind("\n", 0, err.charpos)+1
  298.         end   = input.find("\n", err.charpos, -1)
  299.         origline = input[start:end].replace("\t", " ")
  300.         line = " "+origline.lstrip()
  301.         errpos = err.charpos-start-(len(origline)-len(line))
  302. #        print "%s^"%((errpos)*" ")
  303.         msg = 'Syntax error in "%s", line %d:\n'%(parser.filename, lineno)
  304.         msg += '\n%s\n%s^'%(line, errpos*" ")
  305.         exc = SyntaxError(msg)
  306.         exc.filename = parser.filename
  307.         exc.lineno = lineno
  308.         exc.line = line
  309.         exc.errpos = errpos
  310.         raise exc
  311.  
  312. # preprocess
  313. def preprocess(cpp, file, cpperrstream=sys.stderr, defines=None, includedirs=None):
  314.     """Preprocess an input file.
  315.  
  316.     cpp is either a string containing the name of an external preprocessor
  317.     command (e.g. 'cpp'), a callable function that takes file and
  318.     cpperrstream as input and returns the preprocessed source code as
  319.     a string or None in which case the built-in preprocessor is used.
  320.     If the external preprocessor produces no data it is assumed that
  321.     it was not found a a PreprocessorNotFound exception is thrown.
  322.  
  323.     file is either a string containing the input file name or it's a
  324.     file-like object.
  325.     If the input file doesn't exist, an IOError is thrown.
  326.  
  327.     cpperrstream is a stream that receives any error messages from
  328.     the preprocessor. If it's None, error messages are ignored.
  329.  
  330.     defines is a list of tuples (name, value) that specify the predefined
  331.     symbols. includedirs is a list of strings with include paths.
  332.  
  333.     The function returns the preprocessed sources as a string.
  334.     """
  335.  
  336.     if cpp==None:
  337.         cpp = simplecpp.PreProcessor(defines=defines, includedirs=includedirs)
  338.         return cpp(file, cpperrstream)
  339.     # Is cpp a callable then invoke it and return the result
  340.     elif callable(cpp):
  341.         return cpp(file, cpperrstream)
  342.  
  343.     # no callable, so cpp contains the name of an external preprocessor tool
  344.  
  345.     # Variables:
  346.     #
  347.     # cmd: The command that is used to execute the preprocessor.
  348.     #      It either contains only the cpp name or the cpp name + input file
  349.     # slsrc: (string) Unprocessed SL source if it should be passed via stdin
  350.     #      otherwise it's None.
  351.     # ppslsrc: Preprocessed SL source (this is the result).
  352.  
  353.     # Is file a string? Then it's a file name...
  354.     if isinstance(file, types.StringTypes):
  355.         cmdtoks = [cpp]
  356.         if defines!=None:
  357.             for name,value in defines:
  358.                 if value==None:
  359.                     cmdtoks.append("-D%s"%name)
  360.                 else:
  361.                     cmdtoks.append("-D%s=%s"%(name,value))
  362.         if includedirs!=None:
  363.             cmdtoks.extend(map(lambda dir: "-I%s"%dir, includedirs))
  364.         cmdtoks.append(file)
  365.         cmd = " ".join(cmdtoks)
  366.         print cmd
  367.         slsrc = None
  368.         # Check if the file exists and can be accessed (by trying to open it)
  369.         # If the file doesn't exist, an exception is thrown.
  370.         dummy = open(file)
  371.         dummy.close()
  372.     # ...otherwise it's a file-like object
  373.     else:
  374.         cmd = "%s"%cpp
  375.         slsrc = file.read()
  376.  
  377.     # The preprocessed data will be in slsrc.
  378.     stdin, stdout, stderr = os.popen3(cmd)
  379.     if slsrc!=None:
  380.         stdin.write(slsrc)
  381.     stdin.close()
  382.     ppslsrc = stdout.read()
  383.     stdout.close()
  384.     if cpperrstream!=None:
  385.         errs = stderr.read()
  386.         cpperrstream.write(errs)
  387.     # No data? Then it's assumed that the preprocessor couldn't be found
  388.     if len(ppslsrc)==0:
  389.         raise PreprocessorNotFound("Calling '%s' didn't produce any data."%cmd)
  390.  
  391.     return ppslsrc
  392.  
  393.  
  394. # Setup local namespace for convertdefault()
  395. _local_namespace = {}
  396. exec "from cgkit.sl import *" in _local_namespace
  397.    
  398. def convertdefault(paramtuple):
  399.     """Converts the default value of a shader parameter into a Python type.
  400.  
  401.     paramtuple must be a 7-tuple as returned by slparams(). The
  402.     function returns a Python object that corresponds to the default
  403.     value of the parameter. If the default value can't be converted
  404.     then None is returned. Only the functions that are present in the
  405.     sl module are evaluated. If a default value calls a user defined
  406.     function then None is returned.
  407.  
  408.     The SL types will be converted into the following Python types:
  409.  
  410.     - float  -> float
  411.     - string -> string
  412.     - color  -> vec3
  413.     - point  -> vec3
  414.     - vector -> vec3
  415.     - normal -> vec3
  416.     - matrix -> mat4
  417.  
  418.     Arrays will be converted into lists of the corresponding type.    
  419.     """
  420.     global _local_namespace
  421.     
  422.     typ = paramtuple[2]
  423.     arraylen = paramtuple[3]
  424.     defstr = paramtuple[6]
  425.  
  426.     # Replace {} with [] so that SL arrays look like Python lists
  427.     defstr = defstr.replace("{","[").replace("}","]")
  428.     # If the parameter is not an array, then create an array with one
  429.     # element (to unify further processing). It will be unwrapped in the end
  430.     if arraylen==None:
  431.         defstr = "[%s]"%defstr
  432.     # Evaluate the string to create "raw" Python types (lists and tuples)
  433.     try:
  434.         rawres = eval(defstr, globals(), _local_namespace)
  435.     except:
  436.         return None
  437.  
  438.     # Convert into the appropriate type...
  439.     if typ=="float":
  440.         try:
  441.             res = map(lambda x: float(x), rawres)
  442.         except:
  443.             return None
  444.     elif typ=="color" or typ=="point" or typ=="vector" or typ=="normal":
  445.         try:
  446.             res = map(lambda x: cgtypes.vec3(x), rawres)
  447.         except:
  448.             return None
  449.     elif typ=="matrix":
  450.         try:
  451.             res = map(lambda x: cgtypes.mat4(x), rawres)
  452.         except:
  453.             return None
  454.     elif typ=="string":
  455.         try:
  456.             res = map(lambda x: str(x), rawres)
  457.         except:
  458.             return None
  459.  
  460.     if arraylen==None:
  461.         if len(res)==0:
  462.             return None
  463.         else:
  464.             res = res[0]
  465.  
  466.     return res
  467.  
  468. ######################################################################
  469.  
  470. if __name__=="__main__":
  471.     pass
  472.