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 / rmshader.py < prev    next >
Encoding:
Python Source  |  2007-01-11  |  23.6 KB  |  674 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. # $Id: rmshader.py,v 1.9 2006/05/26 21:33:29 mbaas Exp $
  36.  
  37. import sys, os, os.path, re, types, StringIO, copy
  38. import protocols
  39. from Interfaces import *
  40. import ri
  41. import material, lightsource
  42. import ribexport
  43. import slparams
  44. from slots import *
  45. from cgtypes import *
  46.  
  47. # RMShader
  48. class RMShader(object):
  49.     """RenderMan shader source file.
  50.  
  51.     This class encapsulates an external RenderMan shader source file.
  52.     The shader is parsed in the constructor and for each parameter
  53.     a corresponding slot is created. The parameter values themselves
  54.     are also made accessible via normal attribute access.
  55.  
  56.     When the shader is to be instantiated, you can get the shader name
  57.     by calling shaderName() and the parameter dictionary for the
  58.     current time by calling params(). The type of the shader is returned
  59.     by the method shaderType().
  60.  
  61.     The source code for the material class can be obtained by calling
  62.     loadShaderSource().
  63.  
  64.     The class can also be used to just store a shader name. In this
  65.     case you only pass the shader name instead of the file name to
  66.     the constructor. 
  67.  
  68.     This class has to be used in conjunction with the RMMaterial or
  69.     RMLightSource class.
  70.  
  71.     \see RMMaterial, RMLightSource
  72.     """
  73.     
  74.     def __init__(self,
  75.                  shader = None,
  76.                  transform = mat4(1),
  77.                  cpp = None,
  78.                  cpperrstream = sys.stderr,
  79.                  includedirs = None,
  80.                  defines = None,
  81.                  params = None,
  82.                  **keyargs):
  83.         """Constructor.
  84.  
  85.         The first argument is the shader name or file name. cpp determines
  86.         the preprocessor that should be used when extracting parameters.
  87.         cpperrstream is used to output errors from the preprocessor. 
  88.         includedirs is a list of strings that contain directories where to
  89.         look for include files. defines is a list of tuples (name, value)
  90.         that specify the predefined symbols to use (see the function 
  91.         slparams.slparams() for details).
  92.         params can be used to declare parameters if the shader source
  93.         is not available. The value must be a dictionary that contains
  94.         token/value pairs. The token may contain an inline declaration.
  95.         Any additional keyword argument is also considered to be a shader
  96.         parameter. However, this parameter cannot have an inline declaration,
  97.         so it is recommended to declare the parameter afterwards using
  98.         the declare() method.
  99.         
  100.         \param name (\c str) Shader file name or shader name
  101.         """
  102.       
  103.         # Shader file or None
  104.         self.filename = None
  105.         # Shader name (without path and extension)
  106.         self.shadername = None
  107.         # Shader type (surface, displacement, ...)
  108.         self.shadertype = None
  109.         # Shader transformation
  110.         self.transform = transform
  111.         # Shader parameters as dictionary.
  112.         # Key:Parameter name   Value:Declaration
  113.         self.shaderparams = {}
  114.  
  115.         # String parameters are not stored as slots because they can either
  116.         # contain a string or a RenderPass object
  117.         # str_params: Key:Parameter name /  Value:Value
  118.         self.str_params = {}
  119.  
  120.         # Params for which there is no declaration
  121.         # Key: Param name   Value:Value
  122.         self.undeclared = keyargs
  123.  
  124.         if params!=None:
  125.             for param in params:
  126.                 f = param.split()
  127.                 # Was the parameter an empty string? then ignore
  128.                 if len(f)==0:
  129.                     continue
  130.                 # Add the param to the 'undeclared' dict
  131.                 # (even if there is a declaration this will ensure that the
  132.                 # value is taken as default value during the declaration)
  133.                 paramname = f[-1]
  134.                 if paramname not in self.undeclared:
  135.                     self.undeclared[paramname] = params[param]
  136.                 if len(f)>1:
  137.                     self.declare(param)
  138.             
  139.         
  140.         # If there is no extension then name is just the shader name
  141.         # and not the shader file
  142.         if shader!=None:
  143.             if os.path.splitext(shader)[1]=="":
  144.                 self.shadername = shader
  145.             else:
  146.                 self.filename = shader
  147.  
  148.         # Read the shader parameters from the shader source file...
  149.         if self.filename!=None:
  150.             slinfo = slparams.slparams(shader, cpp=cpp, cpperrstream=cpperrstream, includedirs=includedirs, defines=defines)
  151.             if len(slinfo)==0:
  152.                 raise ValueError, "no shader found in %s"%shader
  153.             if len(slinfo)>1:
  154.                 print "WARNING: There is more than one shader in %s"%shader
  155.  
  156.             # Declare the variables...
  157.             self.shadertype, self.shadername, params = slinfo[0]
  158.             for p in params:
  159.                 cls = p[1]
  160.                 type = p[2]
  161.                 arraysize = p[3]
  162.                 name = p[4]
  163.                 default = p[6]
  164.                 self.declare(name, type, cls, arraysize, default)
  165.  
  166.     # getattr
  167.     def __getattr__(self, name):
  168.         # Shader parameter?
  169.         slot = self.__dict__.get("%s_slot"%name, None)
  170.         if slot!=None:
  171.             # Array slot or normal slot?
  172.             if isinstance(slot, IArraySlot):
  173.                 return list(slot)
  174.             else:
  175.                 return slot.getValue()
  176.  
  177.         # String parameter?
  178.         str_params = self.__dict__.get("str_params", {})
  179.         if name in str_params:
  180.             return str_params[name]
  181.     
  182.         raise AttributeError, 'shader "%s" has no attribute "%s"'%(self.shadername, name)
  183.  
  184.     # setattr
  185.     def __setattr__(self, name, val):
  186.         # Shader parameter?
  187.         slot = self.__dict__.get("%s_slot"%name, None)
  188.         if slot!=None:
  189.             # Array slot or normal slot?
  190.             if isinstance(slot, IArraySlot):
  191.                 for i,v in enumerate(val):
  192.                     slot[i] = v
  193.             else:
  194.                 slot.setValue(val)
  195.             return
  196.  
  197.         # String parameter?
  198.         str_params = self.__dict__.get("str_params", {})
  199.         if name in str_params:
  200.             str_params[name] = val
  201.             return
  202.             
  203.         object.__setattr__(self, name, val)
  204.  
  205.  
  206.     # shaderName
  207.     def shaderName(self):
  208.         """Return the shader name.
  209.  
  210.         \return Shader name
  211.         """
  212.         return self.shadername
  213.  
  214.     # shaderType
  215.     def shaderType(self):
  216.         """Return the shader type.
  217.  
  218.         None is returned if only the shader name was specified.
  219.  
  220.         \return Shader type ("surface", "light", ...)
  221.         """
  222.         return self.shadertype
  223.  
  224.     # params
  225.     def params(self, passes=None):
  226.         """Return the parameter dictionary for the current time.
  227.  
  228.         If available the parameters will contain inline declarations.
  229.  
  230.         \return Dictionary containing all parameters.
  231.         """
  232.         
  233.         res = copy.copy(self.undeclared)
  234.  
  235.         for name in self.shaderparams:
  236.             decl = self.shaderparams[name]
  237.             p = "%s %s"%(decl, name)
  238.             val = getattr(self, name)
  239.             if isinstance(val, ribexport.RenderPass):
  240.                 if val.done():
  241.                     mapname = val.realFilename(val.output[0][0])
  242. #                    mapname = os.path.splitext(mapname)[0]+".map"
  243.                     val = mapname
  244.                 else:
  245.                     val = ""
  246.             res[p] = val
  247.  
  248.         return res
  249.  
  250.     # getPasses
  251.     def getPasses(self):
  252.         """Return a list containing the passes required for this shader.
  253.         """
  254.         res = []
  255.         for val in self.str_params.values():
  256.             if isinstance(val, ribexport.RenderPass):
  257.                 res.append(val)
  258.         return res
  259.  
  260.     # declare
  261.     def declare(self, name, type=None, cls=None, arraysize=None, default=None):
  262.         """Declare a shader parameter.
  263.  
  264.         name is the parameter name. name may also contain the entire
  265.         declaration in SL syntax. In this case, all other arguments
  266.         are ignored, otherwise they provide the missing information.
  267.         type is the only parameter that is mandatory if name does not
  268.         contain the entire declaration.
  269.  
  270.         When a parameter is declared it is added to the list of known
  271.         parameters and a corresponding slot (<name>_slot) is created.
  272.  
  273.         Examples:
  274.  
  275.         shader.declare('uniform float Ka=0.5')
  276.         shader.declare('uniform float Ka')
  277.         shader.declare('float Ka')
  278.         shader.declare('Ka', type='float')
  279.         """
  280.  
  281. #        print 'declare("%s", type=%s, cls=%s, arraysize=%s, default=%s)'%(name, type, cls, arraysize, default)
  282.         
  283.         # Create a "dummy shader" which will be passed to slparams to parse
  284.         # the declaration in name
  285.         shd = "surface spam(%s) {}"%name
  286.         try:
  287.             # Force a syntax error when name contains no declaration
  288.             if " " not in name:
  289.                 raise slparams.SyntaxError()
  290.             slinfo = slparams.slparams(StringIO.StringIO(shd))
  291.             shdtype, shdname, params = slinfo[0]
  292.         except slparams.SyntaxError, e:
  293.             # Check if name is only a single name or if there was an attempt
  294.             # to specify the entire declaration
  295.             invalid = " []():;'\"'"
  296.             for inv in invalid:
  297.                 if inv in name:
  298.                     raise ValueError, 'Invalid declaration: "%s"'%name
  299.             # It's probably really just the name, so use the remaining
  300.             # arguments to create a parameter tuple...
  301.             if cls==None:
  302.                 cls = "uniform"
  303.             if type==None:
  304.                 raise ValueError, 'No type for parameter "%s" specified'%name
  305.             if type not in ["float", "string", "color", "point", "vector",
  306.                             "normal", "matrix"]:
  307.                 raise ValueError, 'Invalid type for parameter "%s": %s'%(name, type)
  308.             params = [("", cls, type, arraysize, name, "", str(default))]
  309.  
  310.         typelut = {"float":"double",
  311.                    "string":"str",
  312.                    "color":"vec3",
  313.                    "point":"vec3",
  314.                    "vector":"vec3",
  315.                    "normal":"vec3",
  316.                    "matrix":"mat4"}
  317.  
  318.         # Iterate over all parameters (as there could be several when name
  319.         # contains something like "float Ka; float Kd")...
  320.         for p in params:
  321.             ptype = p[2]
  322.             parraylen = p[3]
  323.             pname = p[4]
  324.             pdefault = slparams.convertdefault(p)
  325.             slottype = typelut[ptype]
  326.             if parraylen==None:
  327.                 decl = "%s %s"%(p[1], ptype)
  328.             else:
  329.                 decl = "%s %s[%d]"%(p[1], ptype, parraylen)
  330.             # Check if this variable was already declared...
  331.             # (it's ok if the new and old declarations are identical)
  332.             if pname in self.shaderparams:
  333.                 if decl!=self.shaderparams[pname]:
  334.                     raise ValueError, '"%s" is already declared as "%s"'%(pname, self.shaderparams[pname])
  335.                 continue
  336.             # Check if the parameter was specified in the constructor.
  337.             # If so, use the value to initialize the slot            
  338.             if pname in self.undeclared:
  339.                 pytype = slottype
  340.                 if pytype=="double":
  341.                     pytype = "float"
  342.  
  343.                 # Don't cast strings because they might contain a RenderPass
  344.                 if pytype=="str":
  345.                     pdefault = self.undeclared[pname]
  346.                 else:
  347.                     pdefault = eval("%s(%s)"%(pytype, repr(self.undeclared[pname])))
  348.                 del self.undeclared[pname]
  349.                 
  350.             # Create the slot and add the variable to the params dictionary
  351.             if ptype=="string":
  352.                 self.str_params[pname] = pdefault
  353.             else:
  354.                 self.createSlot(pname, slottype, parraylen, pdefault)
  355.             self.shaderparams[pname] = decl
  356.         
  357.     # loadShaderSource
  358.     def loadShaderSource(self):
  359.         """Load shader source and replace the shader name.
  360.  
  361.         This method loads a shader file, replaces the shader name
  362.         with "$SHADERNAME" and returns the source code as a string.
  363.         None is returned if \a filename is None.
  364.  
  365.         \return Shader source code or None
  366.         """
  367.  
  368.         if self.filename==None:
  369.             return None
  370.         
  371.         f = file(self.filename)
  372.         src = f.read()
  373.         # Search for <shader type> + one or more white space + <shadername>..
  374.         match = re.search("%s\s+%s"%(self.shadertype, self.shadername), src)
  375.         if match!=None:
  376.             s, e = match.start(), match.end()
  377.             src = src[:e-len(self.shadername)] + "$SHADERNAME" + src[e:]
  378.         else:
  379.             print 'Shader name "%s" not found in %s'%(self.shadername, self.filename)
  380.  
  381.         return src
  382.         
  383.  
  384.     ## protected:
  385.  
  386.     # createSlot
  387.     def createSlot(self, name, type, arraylen, default):
  388.         """Create a slot for a shader parameter.
  389.  
  390.         \param name (\c str) Parameter name
  391.         \param type (\c str) Slot type ("double", "vec3", ...)
  392.         \param arraylen (\c int) Array length or None if the parameter is not an array
  393.         \param default Default value or None
  394.         """
  395.         
  396.         if arraylen==None:
  397.             exec "slot = %sSlot()"%type.capitalize()
  398.             if default!=None:
  399.                 slot.setValue(default)
  400.         else:
  401.             exec "slot = %sArraySlot()"%type.capitalize()
  402.             slot.resize(arraylen)
  403.             if default!=None:
  404.                 for i,v in enumerate(default):
  405.                     slot[i] = v
  406.                 
  407.         setattr(self, "%s_slot"%name, slot)
  408.  
  409.         
  410.  
  411. # RMMaterial
  412. class RMMaterial(material.Material):
  413.     """RenderMan material that takes shader source files as input.
  414.  
  415.     Use this material class if you want to write the RenderMan shaders
  416.     yourself in external source files or if you want to use external shaders
  417.     that you will compile manually.
  418.  
  419.     The material may consist of a surface shader, a displacement shader
  420.     and an interior shader.
  421.  
  422.     The shader source files (or only the shader names) are passed via
  423.     RMShader instances as arguments to the constructor. If the RMShader
  424.     instance points to a file, the material object will take care of the
  425.     compilation of the file. Otherwise, it is up to you to compile the
  426.     shader and make sure that the renderer can find it.
  427.  
  428.     The parameters of the shaders are made available as attributes of
  429.     the material objects. The corresponding slots can be obtained
  430.     by adding the suffix \c _slot to the name. Attribute names in the
  431.     surface shader have priority over the attributes in the displacement
  432.     shader which in turn has priority over the interior shader. This means,
  433.     if there are identical parameter names in all shaders you will access
  434.     the parameter of the surface shader. You can also access the attributes
  435.     of each shader via the \c surface, \c displacement and \c interior
  436.     attributes which contain the corresponding RMShader instances.
  437.  
  438.     Example:
  439.     
  440.     \code
  441.     mat = RMMaterial(surface = RMShader("mysurface.sl"),
  442.                      displacement = RMShader("c:\\shaders\\dented.sl"),
  443.                      displacementbound = ("current", 1.0),
  444.                      color = (1,0.5,0.8)
  445.                      )
  446.     ...
  447.     Sphere(pos=(1,2,3), radius=0.5, material=mat)
  448.     \endcode 
  449.  
  450.     In this example, the material uses the surface shader \c mysurface.sl
  451.     and the displacement shader \c c:\shaders\dented.sl. The shaders will
  452.     be compiled automatically because the shader source files are given
  453.     (instead of just the shader names).
  454.  
  455.     \see RMShader
  456.     """
  457.  
  458.     protocols.advise(instancesProvide=[ribexport.IMaterial])
  459.  
  460.     def __init__(self,
  461.                  name = "RMMaterial",
  462.                  surface = None,
  463.                  displacement = None,
  464.                  displacementbound = ("current", 0.0),
  465.                  interior = None,
  466.                  color = (1,1,1),
  467.                  opacity = (1,1,1)):
  468.         
  469.         material.Material.__init__(self, name, 0)
  470.  
  471.         if isinstance(surface, types.StringTypes):
  472.             surface = RMShader(surface)
  473.         if isinstance(displacement, types.StringTypes):
  474.             displacement = RMShader(displacement)
  475.         if isinstance(interior, types.StringTypes):
  476.             interior = RMShader(interior)
  477.  
  478.         self.surface = surface
  479.         self.displacement = displacement
  480.         self.displacementbound = displacementbound
  481.         self.interior = interior
  482.  
  483.         self._color = color
  484.         self._opacity = opacity
  485.  
  486.         if self.surface==None:
  487.             self.surface = RMShader()
  488.         if self.displacement==None:
  489.             self.displacement = RMShader()
  490.         if self.interior==None:
  491.             self.interior = RMShader()
  492.  
  493.     def __getattr__(self, name):
  494.         if name[-5:]=="_slot":
  495.             slotname = name
  496.         else:
  497.             slotname = "%s_slot"%name
  498.         if hasattr(self.surface, slotname):
  499.             return getattr(self.surface, name)
  500.         elif hasattr(self.displacement, slotname):
  501.             return getattr(self.displacement, name)
  502.         elif hasattr(self.interior, slotname):
  503.             return getattr(self.interior, name)            
  504.         else:
  505.             raise AttributeError, 'material "%s" has no attribute "%s"'%(self.name, name)
  506.  
  507.  
  508.     def __setattr__(self, name, val):
  509.         slotname = "%s_slot"%name
  510.         srf = self.__dict__.get("surface", None)
  511.         dsp = self.__dict__.get("displacement", None)
  512.         itr = self.__dict__.get("itr", None)
  513.         if hasattr(srf, slotname):
  514.             setattr(srf, name, val)
  515.         elif hasattr(dsp, slotname):
  516.             setattr(dsp, name, val)
  517.         elif hasattr(itr, slotname):
  518.             setattr(itr, name, val)
  519.         else:
  520.             material.Material.__setattr__(self, name, val)
  521.  
  522.  
  523.     def createPasses(self):
  524.         """Returns a list of RenderPass objects."""
  525.         res = self.surface.getPasses()
  526.         res += self.displacement.getPasses()
  527.         res += self.interior.getPasses()
  528.         return res
  529.  
  530.     def preProcess(self, exporter):
  531.         """Preprocessing method."""
  532.         pass
  533.  
  534.     def color(self):
  535.         """Return the color for the RiColor() call or None.
  536.         """
  537.         return self._color
  538.  
  539.     def opacity(self):
  540.         """Return the opacity for the RiOpacity() call or None.
  541.         """
  542.         return self._opacity
  543.  
  544.     # surfaceShaderName
  545.     def surfaceShaderName(self):
  546.         """Returns the name of the corresponding surface shader or None.
  547.         """
  548.         return self.surface.shaderName()
  549.  
  550.     def surfaceShaderSource(self):
  551.         return self.surface.loadShaderSource()
  552.  
  553.     def surfaceShaderParams(self, passes):
  554.         """Return a dictionary with shader parameters and their values."""
  555.         return self.surface.params(passes)
  556.  
  557.     def surfaceShaderTransform(self):
  558.         return self.surface.transform
  559.  
  560.     # displacementShaderName
  561.     def displacementShaderName(self):
  562.         return self.displacement.shaderName()
  563.     
  564.     def displacementShaderSource(self):
  565.         return self.displacement.loadShaderSource()
  566.     
  567.     def displacementShaderParams(self, passes):
  568.         return self.displacement.params(passes)
  569.  
  570.     def displacementBound(self):
  571.         return self.displacementbound
  572.  
  573.     def displacementShaderTransform(self):
  574.         return self.displacement.transform
  575.  
  576.     # interiorShaderName
  577.     def interiorShaderName(self):
  578.         return self.interior.shaderName()
  579.     
  580.     def interiorShaderSource(self):
  581.         return self.interior.loadShaderSource()
  582.     
  583.     def interiorShaderParams(self, passes):
  584.         return self.interior.params(passes)
  585.  
  586.     def interiorShaderTransform(self):
  587.         return self.interior.transform
  588.  
  589. # RMLightSource
  590. class RMLightSource(lightsource.LightSource):
  591.     """RenderMan light source.
  592.  
  593.     Use this light source class if you want to write the RenderMan light shader
  594.     yourself in an external source file or if you want to use an external
  595.     shader that you will compile manually.
  596.  
  597.     The shader source file (or only the shader name) is passed via a
  598.     RMShader instances as argument to the constructor. If the RMShader
  599.     instance points to a file, the light source will take care of the
  600.     compilation of the file. Otherwise, it is up to you to compile the
  601.     shader and make sure that the renderer can find it.
  602.  
  603.     The parameters of the shader are made available as attributes of
  604.     the light source object. The corresponding slots can be obtained
  605.     by adding the suffix \c _slot to the name. 
  606.  
  607.     \see RMShader
  608.     """
  609.  
  610.     protocols.advise(instancesProvide=[ISceneItem, ribexport.ILightSource])
  611.  
  612.     def __init__(self,
  613.                  name = "RMLightSource",
  614.                  shader = None,
  615.                  **params):
  616.         
  617.         lightsource.LightSource.__init__(self, name=name, **params)
  618.  
  619.         if isinstance(shader, types.StringTypes):
  620.             shader = RMShader(shader)
  621.  
  622.         self.shader = shader
  623.  
  624.         if self.shader==None:
  625.             self.shader = RMShader()
  626.  
  627.     def protocols(self):
  628.         return [ISceneItem, ribexport.ILightSource]
  629.  
  630.     def __getattr__(self, name):
  631. #        if name in self.__dict__:
  632. #            return self.__dict__.get(name)
  633.         
  634.         if name[-5:]=="_slot":
  635.             slotname = name
  636.         else:
  637.             slotname = "%s_slot"%name
  638.         if hasattr(self.shader, slotname):
  639.             return getattr(self.shader, name)
  640.         else:
  641.             raise AttributeError, 'light source "%s" has no attribute "%s"'%(self.name, name)
  642.  
  643.  
  644.     def __setattr__(self, name, val):
  645.         slotname = "%s_slot"%name
  646.         shd = self.__dict__.get("shader", None)
  647.         if hasattr(shd, slotname):
  648.             setattr(srf, name, val)
  649.         else:
  650.             material.Material.__setattr__(self, name, val)
  651.  
  652.  
  653.     def createPasses(self):
  654.         """Returns a list of RenderPass objects."""
  655.         return self.shader.getPasses()
  656.  
  657.     # shaderName
  658.     def shaderName(self):
  659.         """Returns the name of the corresponding shader or None.
  660.         """
  661.         return self.shader.shaderName()
  662.  
  663.     # surfaceShaderSource
  664.     def shaderSource(self):
  665.         return self.shader.loadShaderSource()
  666.  
  667.     # surfaceShaderParams
  668.     def shaderParams(self, passes):
  669.         """Return a dictionary with shader parameters and their values."""
  670.         return self.shader.params(passes)
  671.  
  672.  
  673.         
  674.