sloargs.py :  » Game-2D-3D » CGKit » cgkit-2.0.0alpha9 » cgkit » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Game 2D 3D » CGKit 
CGKit » cgkit 2.0.0alpha9 » cgkit » sloargs.py
# ***** 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) 2008
# 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 *****

"""This module allows using a RenderMan sloargs library.
"""

import sys, os, os.path
import ctypes
import ctypes.util
from _slreturntypes import _ShaderInfo,_ShaderParam
import rmanlibutil

# This is the name of the C library that gets loaded by the PRMan SloArgs
# implementation to obtain a function pointer to the free() function
# (which is only required for reading meta data).
# As of Python 2.6 this name should work cross-platform. In previous versions,
# the caller could modify this name to pick up the correct version (such
# as "msvcrt"). If the free() function couldn't be obtained, reading meta
# data is disabled.
# The variable can also be set to None to disable reading meta data altogether
# (in case there is a problem with it).
_libc_name = "c"

#################### ctypes type declarations #######################

class _POINT(ctypes.Structure):
    _fields_ = [("xval", ctypes.c_float),
                ("yval", ctypes.c_float),
                ("zval", ctypes.c_float)]

class _DEFAULTVAL(ctypes.Union):
    _fields_ = [("pointval", ctypes.POINTER(_POINT)),
                ("scalarval", ctypes.POINTER(ctypes.c_float)),
                ("stringval", ctypes.c_char_p),
                ("matrixval", ctypes.POINTER(16*ctypes.c_float))
               ]

# PRMan 14
class _VISSYMDEF_prman(ctypes.Structure):
    _fields_ = [("name", ctypes.c_char_p),
                ("type", ctypes.c_int),
                ("storage", ctypes.c_int),
                ("detail", ctypes.c_int),
                ("spacename", ctypes.c_char_p),
                ("default", _DEFAULTVAL),
                ("defaultval", _POINT),
                ("valisvalid", ctypes.c_uint),
                ("arraylen", ctypes.c_int),
               ]

# 3Delight 7.0
class _VISSYMDEF_3delight(ctypes.Structure):
    _fields_ = [("name", ctypes.c_char_p),
                ("type", ctypes.c_int),
                ("storage", ctypes.c_int),
                ("detail", ctypes.c_int),
                ("spacename", ctypes.c_char_p),
                ("default", _DEFAULTVAL),
                ("valisvalid", ctypes.c_uint),
                ("arraylen", ctypes.c_int),
               ]

# Aqsis
class _VISSYMDEF_aqsis(ctypes.Structure):
    _fields_ = [("name", ctypes.c_char_p),
                ("type", ctypes.c_int),
                ("storage", ctypes.c_int),
                ("detail", ctypes.c_int),
                ("spacename", ctypes.c_char_p),
                ("arraylen", ctypes.c_int),
                ("default", _DEFAULTVAL),
               ]

######################## Base class ##############################

class _SloArgs:
    """Provides functionality to read shader parameters using the C sloargs interface.

    Because of differences between renderers, the sloargs interface is not
    exposed directly. Instead, all information about a shader is returned
    by the getShaderInfo() method.
    
    This is the base class for the renderer-specific versions.
    """
    
    def __init__(self, libName, VISSYMDEF):
        """Constructor.
        
        libName is the name of the library that implements the sloargs interface.
        The name can either be just the base lib name or an absolute file name.
        VISSYMDEF is a ctype Structure object that describes the VISSYMDEF
        struct for this particular renderer.
        """
        
        self._VISSYMDEF = VISSYMDEF

        libName = rmanlibutil.resolveRManLib(libName)
        self.libName = libName
        
        # Load the library...
        try:
            self._sloargs = self._loadLibrary(libName)
        except:
            raise ValueError('Failed to load library "%s": %s'%(libName, sys.exc_info()[1]))
        try:
            self._declareFunctions(self._sloargs)
        except:
            raise ValueError('Library "%s" does not implement the sloargs interface: %s'%(libName, sys.exc_info()[1]))

    @staticmethod
    def defaultLibName():
        raise NotImplementedError("defaultLibName() must be implemented by derived classes")

    def _loadLibrary(self, libName):
        """Load the library providing the sloargs interface.
        
        libName is the (resolved) file name of the library to load.
        The return value is the ctypes library that must contain the
        sloargs functions as defined by PRMan.
        """
        sloargs = ctypes.cdll.LoadLibrary(libName)
        return sloargs
    
    def _declareFunctions(self, sloargs):
        """Declare the arguments and return types of the library functions.
        
        sloargs is the ctypes library as returned by LoadLibrary().
        This method can be overridden by derived classes to add renderer-specific
        functions. The derived class must call the inherited function.
        """
        VISSYMDEF = self._VISSYMDEF
        sloargs.Slo_SetPath.argtypes = [ctypes.c_char_p]
        sloargs.Slo_SetPath.restype = ctypes.c_int
        sloargs.Slo_SetShader.argtypes = [ctypes.c_char_p]
        sloargs.Slo_SetShader.restype = ctypes.c_int
        sloargs.Slo_GetName.argtypes = []
        sloargs.Slo_GetName.restype = ctypes.c_char_p
        sloargs.Slo_GetType.argtypes = []
        sloargs.Slo_GetType.restype = ctypes.c_int
        sloargs.Slo_GetNArgs.argtypes = []
        sloargs.Slo_GetNArgs.restype = ctypes.c_int
        sloargs.Slo_GetArgById.argtypes = [ctypes.c_int]
        sloargs.Slo_GetArgById.restype = ctypes.POINTER(VISSYMDEF)
        sloargs.Slo_GetArgByName.argtypes = [ctypes.c_char_p]
        sloargs.Slo_GetArgByName.restype = ctypes.POINTER(VISSYMDEF)
        sloargs.Slo_GetArrayArgElement.argtypes = [ctypes.POINTER(VISSYMDEF), ctypes.c_int]
        sloargs.Slo_GetArrayArgElement.restype = ctypes.POINTER(VISSYMDEF)
        sloargs.Slo_EndShader.argtypes = []
        sloargs.Slo_EndShader.restype = None
        sloargs.Slo_TypetoStr.argtypes = [ctypes.c_int]
        sloargs.Slo_TypetoStr.restype = ctypes.c_char_p
        sloargs.Slo_StortoStr.argtypes = [ctypes.c_int]
        sloargs.Slo_StortoStr.restype = ctypes.c_char_p
        sloargs.Slo_DetailtoStr.argtypes = [ctypes.c_int]
        sloargs.Slo_DetailtoStr.restype = ctypes.c_char_p
        
    def _getMetaData(self):
        """Return the meta data for the current shader.
        
        The function is called after Slo_SetShader() but before
        Slo_EndShader() has been called. The return value should
        be the meta data for the shader (preferably as a dict).
        """
        return None

    def getShaderInfo(self, shader):
        """Read the shader parameters from a given shader.
        """
        sloargs = self._sloargs
        
        # Split path and file name
        shaderPath,shaderFileName = os.path.split(shader)
        if shaderPath=="":
            shaderPath = "."
        # Remove the extension
        shader = os.path.splitext(shaderFileName)[0]

        sloargs.Slo_SetPath(shaderPath)
        # Try the shader name with extension first...
        if sloargs.Slo_SetShader(shaderFileName)!=0:
            # If the above failed, try the shader name without extension (prman)...
            if sloargs.Slo_SetShader(shader)!=0:
                raise IOError('Failed to open shader "%s"'%shader)
        
        shaderName = sloargs.Slo_GetName()
        shaderType = sloargs.Slo_TypetoStr(sloargs.Slo_GetType())
        metaData = self._getMetaData()
        params = []
        numParams = sloargs.Slo_GetNArgs()
        for i in range(1, numParams+1):
            res = sloargs.Slo_GetArgById(i)
            symdef = res.contents
            
            if sloargs.Slo_StortoStr(symdef.storage).startswith("output"):
                output = "output"
            else:
                output = ""
            storage = sloargs.Slo_DetailtoStr(symdef.detail)
            paramType = sloargs.Slo_TypetoStr(symdef.type)
            arrLen = symdef.arraylen
            if arrLen==0:
                arrLen = None
            name = symdef.name
            space = self._getSpace(symdef)
            if space=="":
                space = None
            defaultVal = self._getDefaultVal(symdef)
            params.append(_ShaderParam(output,storage,paramType,arrLen,name,space,defaultVal))
            
        sloargs.Slo_EndShader()
        return [_ShaderInfo(type=shaderType, name=shaderName, params=params, meta=metaData)]
        
    def _getSpace(self, symdef):
        if symdef.arraylen>0:
            res = []
            for i in range(symdef.arraylen):
                psd = self._sloargs.Slo_GetArrayArgElement(symdef, i)
                if bool(psd):
                    space = self._getSpace(psd.contents)
                    if space=="":
                        space = None
                else:
                    print >>sys.stderr, "Warning: Slo_GetArrayArgElement(%s) returned a NULL pointer"%i
                    space = None
                res.append(space)
            # Did we only get a list of None objects?
            # Then just return None (as this has probably been a type
            # that has no spaces associated with it)
            if res==len(res)*[None]:
                res = None
            return res
        else:
            return symdef.spacename
        
    def _getDefaultVal(self, symdef):
        if symdef.arraylen>0:
            res = []
            for i in range(symdef.arraylen):
                psd = self._sloargs.Slo_GetArrayArgElement(symdef, i)
                if bool(psd):
                    res.append(self._getDefaultVal(psd.contents))
                else:
                    print >>sys.stderr, "Warning: Slo_GetArrayArgElement(%s) returned a NULL pointer"%i
                    res.append(None)
            return res
        
        typ = self._sloargs.Slo_TypetoStr(symdef.type)
        # Scalar?
        if typ=="float":
            return symdef.default.scalarval.contents.value
        # Color, Point, Normal, Vector?
        elif typ in ["color", "point", "vector", "normal"]:
            v = symdef.default.pointval.contents
            return (v.xval, v.yval, v.zval)
        # String?
        elif typ=="string":
            return symdef.default.stringval
        # Matrix?
        elif typ=="matrix":
            return tuple(symdef.default.matrixval.contents)
        
        return None
        

############################# PRMan ##################################

class _SloArgs_PRMan(_SloArgs):
    def __init__(self, libName):
        global _libc_name
        
        _SloArgs.__init__(self, libName=libName, VISSYMDEF=_VISSYMDEF_prman)
        
        # Try to get a pointer to the C free() function (required for freeing
        # the result returned by getting the shader meta data).
        # We just try to load the "c" library (as of Python 2.6 this also works
        # on Windows).
        self._free = None
        if _libc_name is not None:
            libcName = ctypes.util.find_library(str(_libc_name))
            try:
                lib = ctypes.cdll.LoadLibrary(libcName)
                self._free = lib.free
                self._free.argtypes = [ctypes.c_void_p]
                self._free.restype = None
            except:
                pass

    @staticmethod
    def defaultLibName():
        return "prman"

    def _declareFunctions(self, sloargs):
        _SloArgs._declareFunctions(self, sloargs)
        # PRMan-specific functions
        sloargs.Slo_GetAllMetaData.argtypes = []
        sloargs.Slo_GetAllMetaData.restype = ctypes.POINTER(ctypes.c_char_p*1000)

    def _getMetaData(self):
        """Return the shader meta data.
        """
        # If we couldn't obtain the free() function in the constructor we
        # just return an empty dict (otherwise we would have to create a memory
        # leak further down).
        if self._free is None:
            return {}
        
        res = {}
        data = self._sloargs.Slo_GetAllMetaData()
        i = 0
        while data.contents[i]!=None:
            key = data.contents[i]
            val = data.contents[i+1]
            res[key] = val
            i += 2

        self._free(data)
        return res

############################# 3Delight ##################################

class _SloArgs_3Delight(_SloArgs):
    def __init__(self, libName):
        _SloArgs.__init__(self, libName=libName, VISSYMDEF=_VISSYMDEF_3delight)

    @staticmethod
    def defaultLibName():
        return "3delight"

    def _declareFunctions(self, sloargs):
        _SloArgs._declareFunctions(self, sloargs)
        # 3Delight-specific functions
        sloargs.Slo_GetNAnnotations.argtypes = []
        sloargs.Slo_GetNAnnotations.restype = ctypes.c_int
        sloargs.Slo_GetAnnotationKeyById.argtypes = [ctypes.c_int]
        sloargs.Slo_GetAnnotationKeyById.restype = ctypes.c_char_p
        sloargs.Slo_GetAnnotationByKey.argtypes = [ctypes.c_char_p]
        sloargs.Slo_GetAnnotationByKey.restype = ctypes.c_char_p
    
    def _getMetaData(self):
        res = {}
        n = self._sloargs.Slo_GetNAnnotations()
        for i in range(1, n+1):
            key = self._sloargs.Slo_GetAnnotationKeyById(i)
            if key is None:
                continue
            ann = self._sloargs.Slo_GetAnnotationByKey(key)
            if ann is None:
                continue
            res[key] = ann
        return res

############################# Aqsis ##################################

class _SloArgs_Aqsis(_SloArgs):
    def __init__(self, libName):
        _SloArgs.__init__(self, libName=libName, VISSYMDEF=_VISSYMDEF_aqsis)

    @staticmethod
    def defaultLibName():
        return "slxargs"


############################# Pixie ##################################

class _UDefaultVal(ctypes.Union):
    pass
_UDefaultVal._fields_ = [("matrix", ctypes.POINTER(16*ctypes.c_float)),
                         ("vector", ctypes.POINTER(3*ctypes.c_float)),
                         ("scalar", ctypes.c_float),
                         ("string", ctypes.c_char_p),
                         ("array", ctypes.POINTER(_UDefaultVal))
                        ]

class _TSdrParameter(ctypes.Structure):
    pass
_TSdrParameter._fields_ = [("name", ctypes.c_char_p),
                           ("type", ctypes.c_int),
                           ("container", ctypes.c_int),
                           ("writable", ctypes.c_int),
                           ("numItems", ctypes.c_int),
                           ("space", ctypes.c_char_p),
                           ("defaultValue", _UDefaultVal),
                           ("next", ctypes.POINTER(_TSdrParameter))
                          ]

class _TSdrShader(ctypes.Structure):
    _fields_ = [("name", ctypes.c_char_p),
                ("type", ctypes.c_int),
                ("parameters", ctypes.POINTER(_TSdrParameter))
               ]

class _SloArgs_Pixie(_SloArgs):
    """Pixie version.
    
    This class does derive from _SloArgs but doesn't use the sloargs interface
    as Pixie has its own interface.
    """
    
    def __init__(self, libName):
        _SloArgs.__init__(self, libName=libName, VISSYMDEF=None)
        self._shaderType = {0:"surface", 1:"displacement", 2:"volume", 3:"light", 4:"imager"}
        self._container = {0:"constant", 1:"uniform", 2:"varying", 3:"vertex"}
        self._paramType = {0:"float", 1:"vector", 2:"normal", 3:"point", 4:"color", 5:"matrix", 6:"string"}

    @staticmethod
    def defaultLibName():
        return "sdr"

    def _declareFunctions(self, sloargs):
        # We don't call the inherited method as Pixie's library does not
        # provide the same interface than PRMan
        
        sloargs.sdrGet.argtypes = [ctypes.c_char_p]
        sloargs.sdrGet.restype = ctypes.POINTER(_TSdrShader)
        sloargs.sdrDelete.argtypes = [ctypes.POINTER(_TSdrShader)]

    def getShaderInfo(self, shader):
        sdr = self._sloargs
        shaderInfo = sdr.sdrGet(shader)
        if not shaderInfo:
            raise IOError('Failed to open shader "%s"'%shader)
        
        try:
            # Strip path and extension (as Pixie doesn't return the shader name but the file name)
            shaderName = os.path.splitext(os.path.split(shaderInfo.contents.name)[1])[0]
            shaderType = self._shaderType.get(shaderInfo.contents.type, "?")
            params = []
            param = shaderInfo.contents.parameters
            while param:
                param = param.contents
                
                if param.writable:
                    output = "output"
                else:
                    output = ""
                    
                storage = self._container.get(param.container, "?")
                paramType = self._paramType.get(param.type, "?")
                arrLen = param.numItems
                if arrLen==1:
                    arrLen = None
                name = param.name
                space = param.space
                # Set space to "current" if Pixie didn't return a space name for a type that requires a space
                if paramType in ["point", "vector", "normal", "color", "matrix"] and space is None:
                    space = "current"
                if arrLen is None:
                    defaultVal = self._getDefaultVal(paramType, param.defaultValue)
                else:
                    defaultVal = []
                    for i in range(arrLen):
                        defaultVal.append(self._getDefaultVal(paramType, param.defaultValue.array[i]))
                
                params.append((output,storage,paramType,arrLen,name,space,defaultVal))
                param = param.next
        finally:
            sdr.sdrDelete(shaderInfo)
          
        # Pixie reports the parameters in reverse order, so reverse the list
        params.reverse()
        return [(shaderType, shaderName, params)]  

    def _getDefaultVal(self, paramType, defval):
        if paramType=="float":
            return defval.scalar
        elif paramType in ["vector", "normal", "point", "color"]:
            return tuple(defval.vector.contents)
        elif paramType=="string":
            defaultVal = defval.string
        elif paramType=="matrix":
            return tuple(defval.matrix.contents)
        return None

############################################################################

# The registered SloArgs classes.
# Key: Compiled Shader Suffix - Value: SloArgs class
_sloArgsClasses = {}

# The already instantiated SloArgs objects (so that they can be reused).
# Key: Compiled Shader Suffix - Value: SloArgs instance
_sloArgsInstances = {}

# The names of the libraries that should be used for reading the parameters
# Key: Compiled Shader Suffix - Value: libName
_sloLibNames = {}

def registerSloArgs(sloSuffix, sloArgsCls):
    """Register a :class:`SloArgs` class for a particular renderer.
    
    *sloSuffix* is the suffix (without dot) that is used for the compiled
    shaders of this renderer. *sloArgsCls* is the :class:`SloArgs` class that
    can read compiled shaders for this renderer.
    """
    global _sloArgsClasses
    
    sloSuffix = sloSuffix.lower()
    _sloArgsClasses[sloSuffix] = sloArgsCls

def getSloLib(sloSuffix):
    """Return the library name that manages a particular type of compiled shaders.
    
    *sloSuffix* is the suffix of the compiled shader.
    The return value is the library name that is used to read the parameters
    from compiled shaders of the given suffix.
    
    When called before a shader was parsed, the return value is the
    library name that will be used when the library is loaded. This value can be
    set by calling :func:`setSloLib()`. When the function is called after a
    shader was already parsed, the return value is the absolute path to the
    actual library that is used to read parameters.
    """
    global _sloArgsClasses, _sloArgsInstances
    
    sloSuffix = sloSuffix.lower()
    if sloSuffix in _sloArgsInstances:
        return _sloArgsInstances[sloSuffix].libName
    elif sloSuffix in _sloArgsClasses:
        return _sloArgsClasses[sloSuffix].defaultLibName()
    else:
        raise ValueError, "Unknown compiled shader extension: %s"%sloSuffix
    
def setSloLib(sloSuffix, libName):
    """Set the library name that should be used for reading shader parameters.
    
    Shaders with the suffix *sloSuffix* (case-insensitive) will be handled by
    the library *libName*. This function has no effect if a shader of the given
    suffix has already been read. You must call this function at the beginning
    of your application when :func:`slparams()` hasn't been called yet.
    """
    global _sloLibNames
    
    _sloLibNames[sloSuffix.lower()] = libName

def slparams(shader):
    """Read shader parameters.
    
    See :func:`slparams.slparams()<cgkit.slparams.slparams>` for more details.
    """
    global _sloArgsClasses, _sloArgsInstances, _sloLibNames
    
    if not os.path.exists(shader):
        raise IOError('Shader file "%s" does not exist'%shader)

    # Get the SloArgs class for the renderer that produced the compiled
    # shader (based on the shader suffix).
    shaderBase,ext = os.path.splitext(shader)
    ext = ext.lower()[1:]
    if ext in _sloArgsInstances:
        sloArgs = _sloArgsInstances[ext]
    elif ext in _sloArgsClasses:
        SloArgs = _sloArgsClasses[ext]
        libName = _sloLibNames.get(ext, SloArgs.defaultLibName())
        sloArgs = SloArgs(libName=libName)
        _sloArgsInstances[ext] = sloArgs
    else:
        raise ValueError, "Unknown compiled shader extension: %s"%shader
    
    return sloArgs.getShaderInfo(shader) 


# Register the built-in SloArgs classes
registerSloArgs("slo", _SloArgs_PRMan)
registerSloArgs("sdl", _SloArgs_3Delight)
registerSloArgs("slx", _SloArgs_Aqsis)
registerSloArgs("sdr", _SloArgs_Pixie)
        
### TEST ###
if __name__=="__main__":
    import cgkit.slparams
    
    if 0:
        info = cgkit.slparams.slparams("foo.sl")
        if len(info)>0:
            type,name,params = info[0]
            print "Shader:",name
            print "Type  :",type
            for param in params:
                print param, cgkit.slparams.convertdefault(param)
    
    info = slparams("foo.sdr")
    if len(info)>0:
        type,name,params = info[0]
        print "Shader:",name
        print "Type  :",type
        for param in params:
            print param
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.