- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Examples

# hlslShader.cpp

    //-
    // ==========================================================================
    // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+
    #ifndef HLSL_VERSION
    #define HLSL_VERSION  "1.0"
    #endif
    #define ERROR_LIMIT 20
    //#define VALIDATE_TECHNIQUE_LIST
    //#define VALIDATE_TECHNIQUES

    // Now, because we're rendering from system memory, DirectX can start to get
    // suspicious that the graphics driver has hung if we try and render too much
    // stuff in a single call. So, we'll take stability over performance and
    // split up the draw. Given we only do this for large amounts of geometry, the
    // hit shouldn't be too bad. Commenting this #define out will cause the plugin
    // to try and render everything in a single render call, no matter how large
    // it is (which seems to work on some graphics cards, but not others)
    #define MAX_SEGMENTED_BATCH_SIZE 40000

    #include "hlslShader.h"

    #include <maya/MGlobal.h>
    #include <maya/MGeometry.h>
    #include <maya/MGeometryData.h>
    #include <maya/MGeometryPrimitive.h>
    #include <maya/MGeometryList.h>
    #include <maya/MGeometryManager.h>
    #include <maya/MVaryingParameterList.h>
    #include <maya/MUniformParameter.h>
    #include <maya/MGlobal.h>
    #include <maya/MFnDependencyNode.h>
    #include <maya/MFnTypedAttribute.h>
    #include <maya/MFnStringData.h>
    #include <maya/MFnStringArrayData.h>
    #include <maya/MSceneMessage.h>
    #include <maya/MFileIO.h>
    #include <maya/MMatrix.h>
    #include <maya/MFileObject.h>
    #include <maya/MArgParser.h>
    #include <maya/MSyntax.h>

    #include <maya/MD3D9Renderer.h>
    #include <maya/MHwrCallback.h>
    #include <stdio.h>
    #include <new.h> // for fancy pants re-initialisation of existing memory version

    // For swatch rendering
    #include <maya/MHardwareRenderer.h>
    #include <maya/MHWShaderSwatchGenerator.h>
    #include <maya/MGeometryRequirements.h>
    #include <maya/MHwTextureManager.h>
    #include <maya/MImageFileInfo.h>

    #include <sys/stat.h>

    // Debugging
    #define DEBUG_TEXTURE_CACHE(X)

    template <class T> class ObjArray
    {
    public:
        inline      ObjArray() : fLength( 0), fCapacity( 0), fData( NULL) {}
        inline      ~ObjArray() { if( fData) { destroy( 0, fLength); delete[] ((char*)fData); } } // Don't call the destructors here, destroy did it!
        inline int  length() const { return fLength; }
        inline void length( int l) { if( l > fLength) { if( l > fCapacity) resize( l); create( fLength, l); } else if( l < fLength) destroy( l, fLength); fLength = l; }
        inline const T& operator[]( int i) const { return fData[ i]; }
        inline T&   operator[]( int i) { return fData[ i]; }
    private:
        inline void create( int s, int e) { for( ; s < e; s++) new ((void*)(fData + s)) T; }
        inline void destroy( int s, int e) { for( ; s < e; s++) (fData + s)->T::~T(); }
        enum
        {
            MIN_ARRAY_SIZE = 4
        };
        inline void resize( int l)
        {
            if( l < MIN_ARRAY_SIZE) l = MIN_ARRAY_SIZE;
            else if( l < fCapacity * 2) l = fCapacity * 2;
            T* newData = (T*)new char[ l * sizeof( T)];
            if( fData)
            {
                memcpy( newData, fData, fCapacity * sizeof( T));
                delete[] ((char*)fData);
            }
            fData = newData;
            fCapacity = l;
        }
        int         fLength;
        int         fCapacity;
        T*          fData;
    };

    // setup device callbacks as needed
    // currently this class defined in hlslShader.h
    //
    void hlslDeviceManager::deviceNew()
    {
        fState = kValid;
        //printf("calling setShader with name: %s\n", fShader.fShader.asChar() );
        fShader.setShader( fShader.fShader);
    }

    void hlslDeviceManager::deviceLost()
    {
        fState = kInvalid;
        //printf("devLost: releasing %s handles\n", fShader.fShader.asChar() );
        fShader.release();
    }
    void hlslDeviceManager::deviceDeleted()
    {
        fState = kInvalid;
        //printf("devDeleted: releasing %s handles\n", fShader.fShader.asChar() );
        fShader.release();
    }

    void hlslDeviceManager::deviceReset()
    {
        fState = kReset;
        //printf("devRest: releasing %s handles\n", fShader.fShader.asChar() );
    }

    void hlslDeviceManager::resetShader()
    {
        fState = kValid;
        fShader.setShader( fShader.fShader);
        //printf("resetShader: releasing %s handles\n", fShader.fShader.asChar() );
    }

    //
    // Lookup tables for the (default) names and internal semantics
    // that map to D3D's usage types
    //
    struct SemanticInfo
    {
        const char* Name;
        MVaryingParameter::MVaryingParameterSemantic Type;
        int         MinElements;
        int         MaxElements;
        BYTE        D3DType;
    };

    SemanticInfo gSemanticInfo[] =
    {
        "Position",             MVaryingParameter::kPosition,       2, 4,       D3DDECLTYPE_FLOAT3,
        "BlendWeight",          MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT4,
        "BlendIndices",         MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT4,
        "Normal",               MVaryingParameter::kNormal,         3, 4,       D3DDECLTYPE_FLOAT3,
        "PointSize",            MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT3,
        "UV",                   MVaryingParameter::kTexCoord,       2, 4,       D3DDECLTYPE_FLOAT2,
        "Tangent",              MVaryingParameter::kTangent,        3, 4,       D3DDECLTYPE_FLOAT3,
        "BiNormal",             MVaryingParameter::kBinormal,       3, 4,       D3DDECLTYPE_FLOAT3,
        "TesselateFactor",      MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT4,
        "PositionTransformed",  MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT4,
        "Color",                MVaryingParameter::kColor,          3, 4,       D3DDECLTYPE_FLOAT4,
        "Fog",                  MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT4,
        "Depth",                MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT4,
        "Sample",               MVaryingParameter::kNoSemantic,     1, 4,       D3DDECLTYPE_FLOAT4,
    };

    //
    // A wee state manager used to flip from DX to GL culling handed-ness
    //
    class HLSLStateManager : public ID3DXEffectStateManager
    {
    public:
        static HLSLStateManager sInstance;
        inline HLSLStateManager() : fShapeCullMode( MGeometryList::kCullCW), fShaderCullMode( D3DCULL_CCW) {}

        // Set the cull mode Maya needs
        inline void shapeCullMode( MGeometryList::MCullMode cullMode, bool ignoreShaderCullMode = false)
        {
            fShapeCullMode = cullMode;
            if( ignoreShaderCullMode) fShaderSetCullMode = false;
            setupCulling();
        }

        // Reverse the cull direction if we need "normal" OpenGL culling as DirectX
        // has the opposite "handedness" to GL
        STDMETHOD(SetRenderState)(THIS_ D3DRENDERSTATETYPE d3dRenderState, DWORD dwValue )
        {
            if( d3dRenderState == D3DRS_CULLMODE)
            {
                fShaderCullMode = dwValue;
                fShaderSetCullMode = true;
                return D3D_OK;
            }
            return fD3DDevice->SetRenderState( d3dRenderState, dwValue );
        }

        // Helper to setup a pass tracking whether cull direction was explicitly set, and reversing it if it was
        inline void BeginPass( LPD3DXEFFECT effect, int pass)
        {
            fShaderSetCullMode = false;
            effect->BeginPass( pass);
            setupCulling();
        }

        // Default implementations
        STDMETHOD(SetSamplerState)(THIS_ DWORD dwStage, D3DSAMPLERSTATETYPE d3dSamplerState, DWORD dwValue ) { return fD3DDevice->SetSamplerState( dwStage, d3dSamplerState, dwValue ); }
        STDMETHOD(SetTextureStageState)(THIS_ DWORD dwStage, D3DTEXTURESTAGESTATETYPE d3dTextureStageState, DWORD dwValue ) { return fD3DDevice->SetTextureStageState( dwStage, d3dTextureStageState, dwValue ); }
        STDMETHOD(SetTexture)(THIS_ DWORD dwStage, LPDIRECT3DBASETEXTURE9 pTexture ) { return fD3DDevice->SetTexture( dwStage, pTexture ); }
        STDMETHOD(SetVertexShader)(THIS_ LPDIRECT3DVERTEXSHADER9 pShader ) { return fD3DDevice->SetVertexShader( pShader ); }
        STDMETHOD(SetPixelShader)(THIS_ LPDIRECT3DPIXELSHADER9 pShader ) { return fD3DDevice->SetPixelShader( pShader ); }
        STDMETHOD(SetFVF)(THIS_ DWORD dwFVF ) { return fD3DDevice->SetFVF( dwFVF ); }
        STDMETHOD(SetTransform)(THIS_ D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix ) { return fD3DDevice->SetTransform( State, pMatrix ); }
        STDMETHOD(SetMaterial)(THIS_ CONST D3DMATERIAL9 *pMaterial ) { return fD3DDevice->SetMaterial( pMaterial ); }
        STDMETHOD(SetLight)(THIS_ DWORD Index, CONST D3DLIGHT9 *pLight ) { return fD3DDevice->SetLight( Index, pLight ); }
        STDMETHOD(LightEnable)(THIS_ DWORD Index, BOOL Enable ) { return fD3DDevice->LightEnable( Index, Enable ); }
        STDMETHOD(SetNPatchMode)(THIS_ FLOAT NumSegments ) { return fD3DDevice->SetNPatchMode( NumSegments ); }
        STDMETHOD(SetVertexShaderConstantF)(THIS_ UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount ) { return fD3DDevice->SetVertexShaderConstantF( RegisterIndex, pConstantData, RegisterCount ); }
        STDMETHOD(SetVertexShaderConstantI)(THIS_ UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount ) { return fD3DDevice->SetVertexShaderConstantI( RegisterIndex, pConstantData, RegisterCount ); }
        STDMETHOD(SetVertexShaderConstantB)(THIS_ UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount ) { return fD3DDevice->SetVertexShaderConstantB( RegisterIndex, pConstantData, RegisterCount ); }
        STDMETHOD(SetPixelShaderConstantF)(THIS_ UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount ) { return fD3DDevice->SetPixelShaderConstantF( RegisterIndex, pConstantData, RegisterCount ); }
        STDMETHOD(SetPixelShaderConstantI)(THIS_ UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount ) { return fD3DDevice->SetPixelShaderConstantI( RegisterIndex, pConstantData, RegisterCount ); }
        STDMETHOD(SetPixelShaderConstantB)(THIS_ UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount ) { return fD3DDevice->SetPixelShaderConstantB( RegisterIndex, pConstantData, RegisterCount ); }
        STDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) { if (iid == IID_IUnknown || iid == IID_ID3DXEffectStateManager) { *ppv = static_cast<ID3DXEffectStateManager*>(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(this)->AddRef(); return S_OK; }
        STDMETHOD_(ULONG, AddRef)(THIS) { /*return (ULONG)InterlockedIncrement( &m_lRef ); */ return 1L; }
        STDMETHOD_(ULONG, Release)(THIS) { /*if( 0L == InterlockedDecrement( &m_lRef ) ) { delete this; return 0L; } return m_lRef; */ return 1L; }
        LPDIRECT3DDEVICE9 fD3DDevice;
    protected:

        // Helper to work out the render cull setting for the current shape/shader cull modes
        // This code assumes that a pass render state selecting a cull direction OVERRIDES any
        // double-sidedness on the shape (given there's really no point in rendering a back or
        // front of the object when every point is part of the front and the back). You can
        // easily modify this code to preserve double sidedness in these cases by adding a
        // check for fShapeCullMode == kCullNone in the second two fShaderCullMode cases and
        // leave d3dCullMode as NONE to get the other behaviour.
        inline void setupCulling()
        {
            DWORD d3dCullMode = D3DCULL_NONE;
            if( !fShaderSetCullMode)
            {
                if( fShapeCullMode == MGeometryList::kCullCW)
                    d3dCullMode = D3DCULL_CW;
                else if( fShapeCullMode == MGeometryList::kCullCCW)
                    d3dCullMode = D3DCULL_CCW;
            }
            else if( fShaderCullMode == D3DCULL_CCW)
            {
                d3dCullMode = (fShapeCullMode == MGeometryList::kCullCCW) ? D3DCULL_CCW : D3DCULL_CW;
            }
            else if( fShaderCullMode == D3DCULL_CW)
            {
                d3dCullMode = (fShapeCullMode == MGeometryList::kCullCCW) ? D3DCULL_CW : D3DCULL_CCW;
            }
            fD3DDevice->SetRenderState( D3DRS_CULLMODE, d3dCullMode);
        }

        MGeometryList::MCullMode fShapeCullMode;
        DWORD       fShaderCullMode;
        bool        fShaderSetCullMode;
    };
    HLSLStateManager HLSLStateManager::sInstance;

    //
    // A wee texture cache for our HLSL shaders
    //
    class HLSLTextureManager : public MHwrCallback
    {
    public:
        static HLSLTextureManager sInstance;

        HLSLTextureManager()
        {
            addCallback( this);
            sceneCallbackIds[0] = MSceneMessage::addCallback( MSceneMessage::kBeforeNew, HLSLTextureManager::newSceneCallback, (void*)this);
            sceneCallbackIds[1] = MSceneMessage::addCallback( MSceneMessage::kBeforeOpen, HLSLTextureManager::newSceneCallback, (void*)this);
        }
        virtual ~HLSLTextureManager() { removeCallback( this); release(); for( int i = 0; i < 2; i++) MSceneMessage::removeCallback( sceneCallbackIds[ i]); }

        void bind( const MString& name, MUniformParameter& uniform, LPD3DXEFFECT d3dEffect, D3DXHANDLE d3dParameter)
        {
            // Try and find an existing entry for this parameter
            User* user = NULL;
            for( int i = users.length(); i--; )
            {
                if( users[ i].parameter == d3dParameter && users[ i].effect == d3dEffect)
                {
                    user = &users[ i];
                    break;
                }
            }

            // If there's no existing entry and we have a texture, create a new one
            if( !user && name.length() > 0)
            {
                int idx = users.length();
                users.length( idx + 1);
                user = &users[ idx];
                DEBUG_TEXTURE_CACHE( printf( "Registering user %s at %d\n", uniform.name().asChar(), (int)((long long)user)); )
                user->parameter = d3dParameter;
                user->effect = d3dEffect;
                user->uniform = uniform;
            }

            // If there is already an entry for this parameter ...
            if( user && user->texture)
            {
                // ... is it using the right texture?
                Texture* texture = user->texture;
                if( texture->name == name)
                {
                    // Same texture means reload!
                    DEBUG_TEXTURE_CACHE( printf( "Reloading %s\n", name.asChar()); )
                    if( texture->texture)
                    {
                        texture->texture->Release();
                        texture->texture = NULL;

                        // Now, we're going to be a little nasty here. If we have multiple shaders attached
                        // to this texture, they're all going to be trying to reload this texture, and given
                        // we don't pay attention to which frame the reload was performed in, we'll reload
                        // the texture once for every user. To avoid this, the easiest solution is simply to
                        // mark all the other users dirty (to make sure they pick up the new texture, and
                        // then delete their usage entries in our cache, so we see them as new users requesting
                        // the texture and don't reload the texture multiple times.
                        for( int i = users.length(); i--; )
                        {
                            if( &users[ i] != user && users[ i].texture == texture)
                            {
                                DEBUG_TEXTURE_CACHE( printf( "Breaking texture reference for shared entry %s to avoid duplicate reloads\n", users[ i].uniform.name().asChar()); )
                                users[ i].uniform.setDirty();
                                users[ i].texture = NULL;
                                texture->numUsers--;
                            }
                        }
                    }
                }
                else
                {
                    decUsage( texture);

                    // Now forget our texture so the code below will search for the new one
                    user->texture = NULL;
                }
            }

            // If we don't have a texture try and find an existing entry we can reuse
            if( user && !user->texture && name.length() > 0)
            {
                for( int i = textures.length(); i--; )
                {
                    if( textures[ i].name == name && textures[ i].type == uniform.type())
                    {
                        DEBUG_TEXTURE_CACHE( printf( "Sharing %s\n", textures[ i].name.asChar()); )
                        user->texture = &textures[ i];
                        user->texture->numUsers++;
                        break;
                    }
                }
            }

            // If we still don't have a texture, create a new entry
            if( user && !user->texture && name.length() > 0)
            {
                // Remember the current memory offset in case the array gets reallocated
                Texture* oldMem = &textures[ 0];

                // Create a new texture entry
                DEBUG_TEXTURE_CACHE( printf( "Creating %s\n", name.asChar()); )
                int idx = textures.length();
                textures.length( idx + 1);

                // Repair our texture pointers if the memory got reallocated
                Texture* newMem = &textures[ 0];
                if( oldMem != newMem)
                    for( int i = users.length(); i--; )
                        if( users[ i].texture)
                            users[ i].texture = (Texture*)((char*)users[ i].texture + ((char*)newMem - (char*)oldMem));

                // Now setup our new entry
                Texture* texture = &textures[ idx];
                texture->name = name;
                texture->type = uniform.type();
                texture->numUsers = 1;
                user->texture = texture;
            }

            // If our texture entry doesn't have a d3d texture, load one in!
            if( user && user->texture && !user->texture->texture)
            {
                // Create a new texture for this parameter
                MD3D9Renderer *pRenderer = MD3D9Renderer::theRenderer();
                if (pRenderer != NULL) {
                    IDirect3DDevice9* d3dDevice = pRenderer->getD3D9Device();
                    if(d3dDevice != NULL)
                    {
                        DEBUG_TEXTURE_CACHE( printf( "Loading %s\n", name.asChar()); )
                            if( uniform.type() == MUniformParameter::kTypeCubeTexture)
                            {
                                LPDIRECT3DCUBETEXTURE9 d3dTexture;
                                if( D3DXCreateCubeTextureFromFile(d3dDevice, name.asChar(), &d3dTexture) == D3D_OK)
                                    user->texture->texture = d3dTexture;
                            }
                            //else if( uniform.type() == MUniformParameter::kType3DTexture)
                            //{
                            //  LPDIRECT3DVOLUMETEXTURE9 d3dTexture;
                            //  if( D3DXCreateVolumeTextureFromFile( d3dDevice, name.asChar(), &d3dTexture) == D3D_OK)
                            //      user->texture->texture = d3dTexture;
                            //}
                            else
                            {
                                LPDIRECT3DTEXTURE9 d3dTexture;
                                if( D3DXCreateTextureFromFile(d3dDevice, name.asChar(), &d3dTexture) == D3D_OK)
                                    user->texture->texture = d3dTexture;
                            }
                    }
                }
            }

            // Finally, bind our texture to the parameter
            d3dEffect->SetTexture( d3dParameter, (user && user->texture) ? user->texture->texture : NULL);
        }

        // Drop all the textures being used by the specified effect
        void release( LPD3DXEFFECT effect)
        {
            for( int i = users.length(); i--; )
            {
                if( users[ i].effect == effect)
                {
                    DEBUG_TEXTURE_CACHE( printf( "Releasing effect usage %s\n", users[ i].uniform.name().asChar()); )
                    if( users[ i].texture) decUsage( users[ i].texture);
                    int newLength = users.length() - 1;
                    if( i != newLength)
                        users[ i] = users[ newLength];
                    users.length( newLength);
                }
            }
        }

        virtual void deviceNew() {}
        virtual void deviceLost() { release(); }
        virtual void deviceDeleted() { release(); }
        static void newSceneCallback( void* ptr) { ((HLSLTextureManager*)ptr)->release(); }

        struct Texture
        {
            inline          Texture() : numUsers( 0), texture( NULL) {}
            MString         name;
            MUniformParameter::DataType type;
            LPDIRECT3DBASETEXTURE9 texture;
            int             numUsers;
        };
        struct User
        {
            inline          User() : parameter( NULL), effect( NULL), texture( NULL) {}
            D3DXHANDLE      parameter;
            LPD3DXEFFECT    effect;
            MUniformParameter uniform;
            Texture*        texture;
        };

        ObjArray<Texture>   textures;
        ObjArray<User>      users;
        MCallbackId         sceneCallbackIds[ 2];

        void        release()
        {
            DEBUG_TEXTURE_CACHE( printf( "Releasing cache with %d textures and %d users\n", textures.length(), users.length()); )
            for( int i = users.length(); i--; )
                users[ i].uniform.setDirty();
            for( int i = textures.length(); i--; )
                if( textures[ i].texture)
                    textures[ i].texture->Release();
            users.length( 0);
            textures.length( 0);
        }

        void        decUsage( Texture* texture)
        {
            // It's currently using a different texture
            if( --texture->numUsers == 0)
            {
                // Drat! We're the last user of this texture so we need to remove it
                DEBUG_TEXTURE_CACHE( printf( "Unloading %s\n", texture->name.asChar()); )
                if( texture->texture)
                    texture->texture->Release();

                // If this isn't the last texture in our list, we need to shuffle our
                // textures around to fill the hole it leaves behind
                Texture* lastTexture = &textures[ textures.length() - 1];
                if( texture != lastTexture)
                {
                    *texture = *lastTexture;
                    for( int i = users.length(); i--; )
                        if( users[ i].texture == lastTexture)
                            users[ i].texture = texture;
                }

                // Now remove the unused texture (which is now guaranteed to be at the
                // end of the array) from our list
                textures.length( textures.length() - 1);
            }
            DEBUG_TEXTURE_CACHE( else printf( "Unsharing %s\n", texture->name.asChar()); )
        }
    };
    HLSLTextureManager HLSLTextureManager::sInstance;

    //
    // Find an annotation
    //
    bool hlslShader::GetAnnotation( D3DXHANDLE parameter, const char* name, LPCSTR& value)
    {
        D3DXHANDLE d3dSemantic = fD3DEffect->GetAnnotationByName( parameter, name);
        return d3dSemantic && fD3DEffect->GetString( d3dSemantic, &value) == D3D_OK && value != NULL;
    }
    bool hlslShader::GetBOOLAnnotation( D3DXHANDLE parameter, const char* name, BOOL& value)
    {
        D3DXHANDLE d3dSemantic = fD3DEffect->GetAnnotationByName( parameter, name);
        return d3dSemantic && fD3DEffect->GetBool( d3dSemantic, &value) == D3D_OK;
    }
    bool hlslShader::GetAnnotation( D3DXHANDLE parameter, const char* name, float& value)
    {
        D3DXHANDLE d3dSemantic = fD3DEffect->GetAnnotationByName( parameter, name);
        return d3dSemantic && fD3DEffect->GetFloat( d3dSemantic, &value) == D3D_OK;
    }
    bool hlslShader::GetAnnotation( D3DXHANDLE parameter, const char* name, int& value)
    {
        D3DXHANDLE d3dSemantic = fD3DEffect->GetAnnotationByName( parameter, name);
        return d3dSemantic && fD3DEffect->GetInt( d3dSemantic, &value) == D3D_OK;
    }

    //
    // Convert a DX type into a Maya type
    //
    MUniformParameter::DataType hlslShader::ConvertType( D3DXHANDLE parameter, D3DXPARAMETER_DESC& description)
    {
        MUniformParameter::DataType mtype = MUniformParameter::kTypeUnknown;
        switch( description.Type)
        {
            //case D3DXPT_VOID: break;
            case D3DXPT_BOOL: mtype = MUniformParameter::kTypeBool; break;
            case D3DXPT_INT: mtype = MUniformParameter::kTypeInt; break;
            case D3DXPT_FLOAT: mtype = MUniformParameter::kTypeFloat; break;
            case D3DXPT_STRING: mtype = MUniformParameter::kTypeString; break;
            case D3DXPT_TEXTURE1D: mtype = MUniformParameter::kType1DTexture; break;
            case D3DXPT_TEXTURE2D: mtype = MUniformParameter::kType2DTexture; break;
            case D3DXPT_TEXTURE3D: mtype = MUniformParameter::kType3DTexture; break;
            case D3DXPT_TEXTURECUBE: mtype = MUniformParameter::kTypeCubeTexture; break;
            case D3DXPT_TEXTURE:
            {
                // The shader hasn't used a typed texture, so first see if there's an annotation
                // that tells us which type to use.
                //
                LPCSTR textureType;
                if( ( GetAnnotation( parameter, "TextureType", textureType) || GetAnnotation( parameter, "ResourceType", textureType)) && textureType)
                {
                    // Grab the type off from the annotation
                    //
                         if( !_stricmp( textureType, "1D")) mtype = MUniformParameter::kType1DTexture;
                    else if( !_stricmp( textureType, "2D")) mtype = MUniformParameter::kType2DTexture;
                    else if( !_stricmp( textureType, "3D")) mtype = MUniformParameter::kType3DTexture;
                    else if( !_stricmp( textureType, "Cube")) mtype = MUniformParameter::kTypeCubeTexture;
                    else
                    {
                        fDiagnostics += "Unrecognised texture type semantic ";
                        fDiagnostics += textureType;
                        fDiagnostics += " on parameter ";
                        fDiagnostics += description.Name;
                        fDiagnostics += "\n";
                    }
                }

                if( mtype == MUniformParameter::kTypeUnknown)
                {
                    // No explicit type. At this stage, it would be nice to take a look at the
                    // sampler which uses the texture and grab the type of that, but I can't see
                    // any way to query for the sampler -> texture bindings through the effect
                    // API.
                    //
                    mtype = MUniformParameter::kType2DTexture;
                    fDiagnostics += "No texture type provided for ";
                    fDiagnostics += description.Name;
                    fDiagnostics += ", assuming 2D\n";
                }

                break;
            }
            //case D3DXPT_SAMPLER: break;
            //case D3DXPT_SAMPLER1D: break;
            //case D3DXPT_SAMPLER2D: break;
            //case D3DXPT_SAMPLER3D: break;
            //case D3DXPT_SAMPLERCUBE: break;
            //case D3DXPT_PIXELSHADER: break;
            //case D3DXPT_VERTEXSHADER: break;
            //case D3DXPT_PIXELFRAGMENT: break;
            //case D3DXPT_VERTEXFRAGMENT: break;
            default: break;
        }
        return mtype;
    }

    //
    // Convert a DX space into a Maya space
    //
    MUniformParameter::DataSemantic hlslShader::ConvertSpace( D3DXHANDLE parameter, D3DXPARAMETER_DESC& description, MUniformParameter::DataSemantic defaultSpace)
    {
        MUniformParameter::DataSemantic space = defaultSpace;
        LPCSTR ann;
        if( GetAnnotation( parameter, "Space", ann) && ann)
        {
                 if( !_stricmp( ann, "Object")) space = defaultSpace >= MUniformParameter::kSemanticObjectPos ? MUniformParameter::kSemanticObjectPos : MUniformParameter::kSemanticObjectDir;
            else if( !_stricmp( ann, "World")) space = defaultSpace >= MUniformParameter::kSemanticObjectPos ? MUniformParameter::kSemanticWorldPos : MUniformParameter::kSemanticWorldDir;
            else if( !_stricmp( ann, "View")) space = defaultSpace >= MUniformParameter::kSemanticObjectPos ? MUniformParameter::kSemanticViewPos : MUniformParameter::kSemanticViewDir;
            else if( !_stricmp( ann, "Camera")) space = defaultSpace >= MUniformParameter::kSemanticObjectPos ? MUniformParameter::kSemanticViewPos : MUniformParameter::kSemanticViewDir;
            else
            {
                fDiagnostics += "Unrecognised space ";
                fDiagnostics += ann;
                fDiagnostics += " on parameter ";
                fDiagnostics += description.Name;
                fDiagnostics += "\n";
            }
        }
        return space;
    }

    //
    // Convert a DX semantic into a Maya semantic
    //
    MUniformParameter::DataSemantic hlslShader::ConvertSemantic( D3DXHANDLE parameter, D3DXPARAMETER_DESC& description)
    {
        MUniformParameter::DataSemantic msemantic = MUniformParameter::kSemanticUnknown;

        // First try the explicit semantic
        if( description.Semantic && *description.Semantic)
        {
                 if( !_stricmp( description.Semantic, "World"))                             msemantic = MUniformParameter::kSemanticWorldMatrix;
            else if( !_stricmp( description.Semantic, "WorldInverse"))                      msemantic = MUniformParameter::kSemanticWorldInverseMatrix;
            else if( !_stricmp( description.Semantic, "WorldInverseTranspose"))             msemantic = MUniformParameter::kSemanticWorldInverseTransposeMatrix;
            else if( !_stricmp( description.Semantic, "View"))                              msemantic = MUniformParameter::kSemanticViewMatrix;
            else if( !_stricmp( description.Semantic, "ViewInverse"))                       msemantic = MUniformParameter::kSemanticViewInverseMatrix;
            else if( !_stricmp( description.Semantic, "ViewInverseTranspose"))              msemantic = MUniformParameter::kSemanticViewInverseTransposeMatrix;
            else if( !_stricmp( description.Semantic, "Projection"))                            msemantic = MUniformParameter::kSemanticProjectionMatrix;
            else if( !_stricmp( description.Semantic, "ProjectionInverse"))                 msemantic = MUniformParameter::kSemanticProjectionInverseMatrix;
            else if( !_stricmp( description.Semantic, "ProjectionInverseTranspose"))            msemantic = MUniformParameter::kSemanticProjectionInverseTransposeMatrix;
            else if( !_stricmp( description.Semantic, "WorldView"))                         msemantic = MUniformParameter::kSemanticWorldViewMatrix;
            else if( !_stricmp( description.Semantic, "WorldViewInverse"))                  msemantic = MUniformParameter::kSemanticWorldViewInverseMatrix;
            else if( !_stricmp( description.Semantic, "WorldViewInverseTranspose"))         msemantic = MUniformParameter::kSemanticWorldViewInverseTransposeMatrix;
            else if( !_stricmp( description.Semantic, "WorldViewProjection"))               msemantic = MUniformParameter::kSemanticWorldViewProjectionMatrix;
            else if( !_stricmp( description.Semantic, "WorldViewProjectionInverse"))            msemantic = MUniformParameter::kSemanticWorldViewProjectionInverseMatrix;
            else if( !_stricmp( description.Semantic, "WorldViewProjectionInverseTranspose"))msemantic = MUniformParameter::kSemanticWorldViewProjectionInverseTransposeMatrix;
            else if( !_stricmp( description.Semantic, "Diffuse"))                           msemantic = MUniformParameter::kSemanticColor;
            else if( !_stricmp( description.Semantic, "Specular"))                          msemantic = MUniformParameter::kSemanticColor;
            else if( !_stricmp( description.Semantic, "Ambient"))                           msemantic = MUniformParameter::kSemanticColor;
            else if( !_stricmp( description.Semantic, "Color"))                             msemantic = MUniformParameter::kSemanticColor;
            else if( !_stricmp( description.Semantic, "Normal"))                                msemantic = MUniformParameter::kSemanticNormal;
            else if( !_stricmp( description.Semantic, "Bump"))                              msemantic = MUniformParameter::kSemanticBump;
            else if( !_stricmp( description.Semantic, "Environment"))                       msemantic = MUniformParameter::kSemanticEnvironment;
            else if( !_stricmp( description.Semantic, "Time"))                              msemantic = MUniformParameter::kSemanticTime;
            else if( !_stricmp( description.Semantic, "Position"))                          msemantic = ConvertSpace( parameter, description, MUniformParameter::kSemanticWorldPos);
            else if( !_stricmp( description.Semantic, "Direction"))                         msemantic = ConvertSpace( parameter, description, MUniformParameter::kSemanticViewDir);
            else
            {
                fDiagnostics += "Unrecognised semantic ";
                fDiagnostics += description.Semantic;
                fDiagnostics += " on parameter ";
                fDiagnostics += description.Name;
                fDiagnostics += "\n";
            }
        }

        // Next, try annotation semantic
        if( msemantic == MUniformParameter::kSemanticUnknown)
        {
            LPCSTR sasSemantic;
            if( GetAnnotation( parameter, "SasBindAddress", sasSemantic) && *sasSemantic)
            {
                MString str( sasSemantic);
                     if( !_stricmp( sasSemantic, "Sas.Skeleton.MeshToJointToWorld[0]")) msemantic = MUniformParameter::kSemanticWorldMatrix;
                else if( !_stricmp( sasSemantic, "Sas.Camera.WorldToView"))             msemantic = MUniformParameter::kSemanticViewMatrix;
                else if( !_stricmp( sasSemantic, "Sas.Camera.Projection"))              msemantic = MUniformParameter::kSemanticProjectionMatrix;
                else if( !_stricmp( sasSemantic, "Sas.Time.Now"))                       msemantic = MUniformParameter::kSemanticTime;
                else if( str.rindexW( ".Position") >= 0)                                msemantic = ConvertSpace( parameter, description, MUniformParameter::kSemanticWorldPos);
                else if( str.rindexW( ".Direction") >= 0 && str.rindexW( ".Direction") != str.rindexW( ".Directional")) msemantic = ConvertSpace( parameter, description, MUniformParameter::kSemanticViewDir);
                else
                {
                    fDiagnostics += "Unrecognised semantic ";
                    fDiagnostics += sasSemantic;
                    fDiagnostics += " on parameter ";
                    fDiagnostics += description.Name;
                    fDiagnostics += "\n";
                }
            }
        }

        // Next try control type
        if( msemantic == MUniformParameter::kSemanticUnknown)
        {
            LPCSTR sasSemantic;
            if( GetAnnotation( parameter, "SasUiControl", sasSemantic) && *sasSemantic)
            {
                     if( !_stricmp( sasSemantic, "ColorPicker"))                            msemantic = MUniformParameter::kSemanticColor;
            }
        }

        // As a last ditch effort, look for an obvious parameter name
        if( msemantic == MUniformParameter::kSemanticUnknown)
        {
            MString name( description.Name);
            if( name.rindexW( "Position") >= 0)                                 msemantic = ConvertSpace( parameter, description, MUniformParameter::kSemanticWorldPos);
            else if( name.rindexW( "Direction") >= 0 && name.rindexW( "Direction") != name.rindexW( "Directional")) msemantic = ConvertSpace( parameter, description, MUniformParameter::kSemanticWorldDir);
            else if( name.rindexW( "Color") >= 0 || name.rindexW( "Colour") >= 0 || name.rindexW( "Diffuse") >= 0 || name.rindexW( "Specular") >= 0 || name.rindexW( "Ambient") >= 0)
                                                                                        msemantic = MUniformParameter::kSemanticColor;
        }

        return msemantic;
    }

    // This typeid must be unique across the universe of Maya plug-ins.
    //

    MTypeId     hlslShader::sId( 0xF3560C30 );

    // Our rendering profile
    //
    MRenderProfile hlslShader::sProfile;

    // Attribute declarations
    //
    MObject     hlslShader::sShader;
    MObject     hlslShader::sTechnique;
    MObject     hlslShader::sTechniques;
    MObject     hlslShader::sDescription;
    MObject     hlslShader::sDiagnostics;

    #define M_CHECK(assertion)  if (assertion) ; else throw ((hlsl::InternalError*)__LINE__)

    namespace hlsl
    {
    #ifdef _WIN32
        class InternalError;    // Never defined.  Used like this:
        //   throw (InternalError*)__LINE__;
    #else
        struct InternalError
        {
            char* message;
        };
        //   throw (InternalError*)__LINE__;
    #endif
    }

    //--------------------------------------------------------------------//
    // Constructor:
    //
    hlslShader::hlslShader()
    : fErrorCount( 0), fD3DEffect( NULL), fD3DTechnique( NULL),
      fD3DVertexDeclaration( NULL), fVertexStructure( "VertexStructure", MVaryingParameter::kStructure),
      fDeviceManager( me()),
      fTechniqueHasBlending( false )
    {
    }

    // Post-constructor
    void
    hlslShader::postConstructor()
    {
        // Don't create any default varying parameters (e.g. position + normal) for empty
        // shaders as this just bloats the cache with a useless structure that is currently
        // not flushed out of the cache until the geometry changes. Instead, just render
        // the default geometry directly off the position array in the cache
    }

    // Destructor:
    //
    hlslShader::~hlslShader()
    {
        release();
    }

    //
    // Release all our device handles
    //
    void hlslShader::release()
    {
        releaseVertexDeclaration();
        if( fD3DEffect)
        {
            HLSLTextureManager::sInstance.release( fD3DEffect);
            fD3DEffect->Release();
            fD3DEffect = NULL;
        }
        fD3DTechnique = NULL;
        fTechniqueHasBlending = false;
    }

    //
    // Release our vertex declaration
    //
    void hlslShader::releaseVertexDeclaration()
    {
        if( fD3DVertexDeclaration)
        {
            for( unsigned int p = 0; p < fD3DTechniqueDesc.Passes; p++)
                if( fD3DVertexDeclaration[ p])
                    fD3DVertexDeclaration[ p]->Release();
            delete[] fD3DVertexDeclaration;
            fD3DVertexDeclaration = NULL;
        }
    }

    // ========== hlslShader::creator ==========
    //
    //  Description:
    //      this method exists to give Maya a way to create new objects
    //      of this type.
    //
    //  Return Value:
    //      a new object of this type.
    //
    /* static */
    void* hlslShader::creator()
    {
        return new hlslShader();
    }

    // ========== hlslShader::initialize ==========
    //
    //  Description:
    //      This method is called to create and initialize all of the attributes
    //      and attribute dependencies for this node type.  This is only called
    //      once when the node type is registered with Maya.
    //
    //  Return Values:
    //      MS::kSuccess
    //      MS::kFailure
    //
    /* static */
    MStatus
    hlslShader::initialize()
    {
        MStatus ms;

        try
        {
            initializeNodeAttrs();
        }
        catch ( ... )
        {
            // [mashworth] Don't forget I18N
            MGlobal::displayError( "hlslShader internal error: Unhandled exception in initialize" );
            ms = MS::kFailure;
        }

        sProfile.addRenderer( MRenderProfile::kMayaD3D);

        return ms;
    }

    // Create all the attributes.
    /* static */
    void
    hlslShader::initializeNodeAttrs()
    {
        MFnTypedAttribute   typedAttr;
        MFnStringData       stringData;
        MFnStringArrayData  stringArrayData;
        MStatus             stat, stat2;

        // The shader attribute holds the name of the .fx file that defines
        // the shader
        //
        sShader = typedAttr.create("shader", "s", MFnData::kString, stringData.create(&stat2), &stat);
        M_CHECK( stat );
        typedAttr.setInternal( true);
        typedAttr.setKeyable( false);
        stat = addAttribute(sShader);
        M_CHECK( stat );

        //
        // technique
        //
        sTechnique = typedAttr.create("technique", "t", MFnData::kString, stringData.create(&stat2), &stat);
        M_CHECK( stat );
        typedAttr.setInternal( true);
        typedAttr.setKeyable( true);
        stat = addAttribute(sTechnique);
        M_CHECK( stat );

        //
        // technique list
        //
        sTechniques = typedAttr.create("techniques", "ts", MFnData::kStringArray, stringArrayData.create(&stat2), &stat);
        M_CHECK( stat );
        typedAttr.setInternal( true);
        typedAttr.setKeyable( false);
        typedAttr.setStorable( false);
        typedAttr.setWritable( false);
        stat = addAttribute(sTechniques);
        M_CHECK( stat );

        // The description field where we pass compile errors etc back for the user to see
        //
        sDescription = typedAttr.create("description", "desc", MFnData::kString, stringData.create(&stat2), &stat);
        M_CHECK( stat );
        typedAttr.setKeyable( false);
        typedAttr.setWritable( false);
        typedAttr.setStorable( false);
        stat = addAttribute(sDescription);
        M_CHECK( stat );

        // The feedback field where we pass compile errors etc back for the user to see
        //
        sDiagnostics = typedAttr.create("diagnostics", "diag", MFnData::kString, stringData.create(&stat2), &stat);
        M_CHECK( stat );
        typedAttr.setKeyable( false);
        typedAttr.setWritable( false);
        typedAttr.setStorable( false);
        stat = addAttribute(sDiagnostics);
        M_CHECK( stat );

        //
        // Specify our dependencies
        //
        attributeAffects( sShader, sTechniques);
        attributeAffects( sShader, sTechnique);
    }

    void
    hlslShader::copyInternalData( MPxNode* pSrc )
    {
        const hlslShader& src = *(hlslShader*)pSrc;
        fTechnique = src.fTechnique;
        fTechniqueHasBlending = src.fTechniqueHasBlending;
        setShader( src.fShader);
    }

    bool hlslShader::setInternalValueInContext( const MPlug& plug,
                                                  const MDataHandle& handle,
                                                  MDGContext& context)
    {
        bool retVal = true;

        try
        {
            if (plug == sShader)
            {
                setShader( handle.asString());
            }
            else if (plug == sTechnique)
            {
                setTechnique( handle.asString());
            }
            else
            {
                retVal = MPxHardwareShader::setInternalValue(plug, handle);
            }
        }
        catch ( ... )
        {
            reportInternalError( __FILE__, __LINE__ );
            retVal = false;
        }

        return retVal;
    }

    /* virtual */
    bool hlslShader::getInternalValueInContext( const MPlug& plug,
                                                  MDataHandle& handle,
                                                  MDGContext&)
    {
        bool retVal = true;

        try
        {
            if (plug == sShader)
            {
                handle.set( fShader);
            }
            else if (plug == sTechnique)
            {
                handle.set( fTechnique);
            }
            else if (plug == sTechniques)
            {
                handle.set( MFnStringArrayData().create( fTechniques));
            }
            else
            {
                retVal = MPxHardwareShader::getInternalValue(plug, handle);
            }
        }
        catch ( ... )
        {
            reportInternalError( __FILE__, __LINE__ );
            retVal = false;
        }

        return retVal;
    }

    // Override geometry rendering
    //
    MStatus hlslShader::render( MGeometryList& iter)
    {
        MD3D9Renderer *pRenderer = MD3D9Renderer::theRenderer();
        if (pRenderer == NULL)
        {
            // If there isn't a render, we can't render anything
            return MStatus::kFailure;
        }

        IDirect3DDevice9* d3dDevice = pRenderer->getD3D9Device();
        if ( NULL == d3dDevice )
        {
            // If there isn't a valid device, we can't render anything
            return MStatus::kFailure;
        }

        if (fDeviceManager.deviceState() == hlslDeviceManager::kReset)
        {
            fDeviceManager.resetShader();
        }

        // We have a device, now do we have a valid effect, or should we render
        // some stand-in geometry?
        //
        HLSLStateManager::sInstance.fD3DDevice = d3dDevice;
        if( fD3DEffect && fD3DVertexDeclaration)
        {
            UINT numPasses = 0;

            fD3DEffect->Begin( &numPasses, 0); // The 0 tells DX to save and restore all state modified by the effect ... hmmmm perf hit?
            bool firstShape = true;
            for( ; !iter.isDone(); iter.next())
            {
                // Setup our shape dependent parameters
                for( int u = fUniformParameters.length(); u--; )
                {
                    MUniformParameter uniform = fUniformParameters.getElement( u);
                    if( uniform.hasChanged( iter))
                    {
                        D3DXHANDLE d3dParameter = (D3DXHANDLE)uniform.userData();
                        if( d3dParameter)
                        {
                            switch( uniform.type())
                            {
                                case MUniformParameter::kTypeFloat:
                                    {
                                    const float* data = uniform.getAsFloatArray( iter);
                                    if( data) fD3DEffect->SetValue( d3dParameter, data, uniform.numElements() * sizeof( float));
                                    }
                                    break;
                                case MUniformParameter::kTypeInt:
                                    fD3DEffect->SetInt( d3dParameter, uniform.getAsInt( iter));
                                    break;
                                case MUniformParameter::kTypeBool:
                                    fD3DEffect->SetBool( d3dParameter, uniform.getAsBool( iter));
                                    break;
                                case MUniformParameter::kTypeString:
                                    fD3DEffect->SetString( d3dParameter, uniform.getAsString( iter).asChar());
                                    break;
                                default:
                                    if( uniform.isATexture())
                                        HLSLTextureManager::sInstance.bind( uniform.getAsString( iter).asChar(), uniform, fD3DEffect, d3dParameter);
                                    break;
                            }
                        }
                    }
                }

                MGeometry& geometry = iter.geometry( MGeometryList::kNone);
                const void* data;
                unsigned int elements, count;
                if( fVertexStructure.numElements() && fVertexStructure.getBuffer( geometry, data, elements, count) == MStatus::kSuccess)
                {
                    MGeometryPrimitive primitives = geometry.primitiveArray( 0);
                    HLSLStateManager::sInstance.shapeCullMode( iter.cullMode());
                    for( UINT p = 0; p < numPasses; p++)
                    {
                        if( !fD3DVertexDeclaration[ p]) continue;
                        if( numPasses > 1 || firstShape)
                            HLSLStateManager::sInstance.BeginPass( fD3DEffect, p);
                        else
                            fD3DEffect->CommitChanges(); // Make sure any shape-dependent uniforms get sent down the to effect when rendering a single pass effect!
                        firstShape = false;
                        d3dDevice->SetVertexDeclaration( fD3DVertexDeclaration[ p]);

    #ifdef MAX_SEGMENTED_BATCH_SIZE
                        unsigned int numPrimitives = primitives.elementCount() / 3;
                        unsigned int batchSize = numPrimitives / (numPrimitives / MAX_SEGMENTED_BATCH_SIZE + 1) + 1;
                        for( unsigned int start = 0; start < numPrimitives; )
                        {
                            unsigned int end = start + batchSize;
                            if( end > numPrimitives) end = numPrimitives;
                            d3dDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, count, end - start, ((const unsigned int*)primitives.data()) + (start * 3), D3DFMT_INDEX32, data, elements);
                            start = end;
                        }
    #else
                        d3dDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, count, primitives.elementCount() / 3, primitives.data(), D3DFMT_INDEX32, data, elements);
    #endif
                        if( numPasses > 1) fD3DEffect->EndPass();
                    }
                }
            }
            if( numPasses == 1) fD3DEffect->EndPass();
            fD3DEffect->End();
        }
        else
        {
            // Hokey DX draw
            // There is no lighting so the only color will be the emissive color
            D3DMATERIAL9 Material;
            Material.Emissive.r = 0.0f;
            Material.Emissive.g = 0.6f;
            Material.Emissive.b = 0.0f;
            Material.Emissive.a = 1.0f;

            d3dDevice->SetMaterial( &Material);
            d3dDevice->LightEnable( 0, false);
            d3dDevice->LightEnable( 1, false);
            d3dDevice->SetFVF( D3DFVF_XYZ );

            for( ; !iter.isDone(); iter.next())
            {
                MGeometry& geometry = iter.geometry( MGeometryList::kNone );
                const MGeometryData position = geometry.position();
                if( position.data())
                {
                    // set up the matrices as this aren't picked up automatically as may be
                    // different in the case of swatch rendering from the current directX state.
                    //
                    const MMatrix& mtm = iter.objectToWorldMatrix();
                    D3DXMATRIXA16 tm( (float)mtm.matrix[0][0], (float)mtm.matrix[0][1], (float)mtm.matrix[0][2], (float)mtm.matrix[0][3],
                                      (float)mtm.matrix[1][0], (float)mtm.matrix[1][1], (float)mtm.matrix[1][2], (float)mtm.matrix[1][3],
                                      (float)mtm.matrix[2][0], (float)mtm.matrix[2][1], (float)mtm.matrix[2][2], (float)mtm.matrix[2][3],
                                      (float)mtm.matrix[3][0], (float)mtm.matrix[3][1], (float)mtm.matrix[3][2], (float)mtm.matrix[3][3]);

                    d3dDevice->SetTransform( D3DTS_WORLD, &tm);

                    // Setup the camera for drawing new stuff
                    const MMatrix& mproject = iter.projectionMatrix();
                    D3DXMATRIXA16 projection( (float)mproject.matrix[0][0], (float)mproject.matrix[0][1], (float)mproject.matrix[0][2], (float)mproject.matrix[0][3],
                                      (float)mproject.matrix[1][0], (float)mproject.matrix[1][1], (float)mproject.matrix[1][2], (float)mproject.matrix[1][3],
                                      (float)mproject.matrix[2][0], (float)mproject.matrix[2][1], (float)mproject.matrix[2][2], (float)mproject.matrix[2][3],
                                      (float)mproject.matrix[3][0], (float)mproject.matrix[3][1], (float)mproject.matrix[3][2], (float)mproject.matrix[3][3]);
                    d3dDevice->SetTransform( D3DTS_PROJECTION, &projection );

                    const MMatrix& mview = iter.viewMatrix();
                    D3DXMATRIXA16 view( (float)mview.matrix[0][0], (float)mview.matrix[0][1], (float)mview.matrix[0][2], (float)mview.matrix[0][3],
                                      (float)mview.matrix[1][0], (float)mview.matrix[1][1], (float)mview.matrix[1][2], (float)mview.matrix[1][3],
                                      (float)mview.matrix[2][0], (float)mview.matrix[2][1], (float)mview.matrix[2][2], (float)mview.matrix[2][3],
                                      (float)mview.matrix[3][0], (float)mview.matrix[3][1], (float)mview.matrix[3][2], (float)mview.matrix[3][3]);
                    d3dDevice->SetTransform( D3DTS_VIEW, &view );

                    HLSLStateManager::sInstance.shapeCullMode( iter.cullMode(), true);
                    MGeometryPrimitive primitives = geometry.primitiveArray( 0);
                    d3dDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, position.elementCount(), primitives.elementCount() / 3, primitives.data(), D3DFMT_INDEX32, position.data(), 3 * sizeof( float));
                }
                else
                {
                     MStatus::kFailure;
                }
            }
        }
        return MStatus::kSuccess;
    }

    /* virtual */
    unsigned int hlslShader::transparencyOptions()
    {
        if (fTechniqueHasBlending)
            return ( kIsTransparent | kNoTransparencyFrontBackCull | kNoTransparencyPolygonSort );
        return 0;
    }

    // Query the renderers supported by this shader
    //
    const MRenderProfile& hlslShader::profile() { return sProfile; }

    void hlslShader::setupUniform( D3DXHANDLE d3dParameter, const MString& prefix)
    {
        if( d3dParameter)
        {
            D3DXPARAMETER_DESC parameterDesc;
            fD3DEffect->GetParameterDesc( d3dParameter, &parameterDesc);
            if( parameterDesc.Class == D3DXPC_STRUCT)
            {
                for( unsigned int p = 0; p < parameterDesc.StructMembers; p++)
                {
                    setupUniform( fD3DEffect->GetParameter( d3dParameter, p), prefix + MString( parameterDesc.Name) + ".");
                }
            }
            else
            {
                // Is this a special parameter?
                if( parameterDesc.Semantic && !_stricmp( parameterDesc.Semantic, "SasGlobal"))
                {
                    LPCSTR sasDescription;
                    if( GetAnnotation( d3dParameter, "SasEffectDescription", sasDescription) && *sasDescription)
                        fDescription += sasDescription;
                    return;
                }
                else if( parameterDesc.Name && !_stricmp( parameterDesc.Name, "description") && parameterDesc.Type == D3DXPT_STRING)
                {
                    LPCSTR Value;
                    if( fD3DEffect->GetString( d3dParameter, &Value) == D3D_OK)
                        fDescription += Value;
                    return;
                }

                MUniformParameter::DataType type = ConvertType( d3dParameter, parameterDesc);
                if( type != MUniformParameter::kTypeUnknown)
                {
                    MUniformParameter::DataSemantic semantic = ConvertSemantic( d3dParameter, parameterDesc);
                    int rows = parameterDesc.Rows;
                    int columns = parameterDesc.Columns;

                    MString uiName = prefix + parameterDesc.Name;
                    //Try replacing name by the uiname if it is specified
                    LPCSTR uiNameValue = NULL ;
                    if( GetAnnotation(d3dParameter, "UIName", uiNameValue) && uiNameValue )
                    {
                        uiName = uiNameValue;
                    }

                    MUniformParameter uniform( uiName, type, semantic, rows, columns, (void*)d3dParameter);

                    updateUIVisibilityFromAnnotation( d3dParameter, uniform );
                    updateRangeFromAnnotation( d3dParameter, uniform );

                    switch( type)
                    {
                        case MUniformParameter::kTypeFloat:
                            {
                                int length = rows * columns;
                                FLOAT* Value = new FLOAT[ length];
                                if( Value)
                                {
                                    if( fD3DEffect->GetFloatArray( d3dParameter, Value, length) == D3D_OK)
                                        uniform.setAsFloatArray( Value, length);
                                    delete [] Value;
                                }
                                break;
                            }
                        case MUniformParameter::kTypeString: { LPCSTR Value; if( fD3DEffect->GetString( d3dParameter, &Value) == D3D_OK) uniform.setAsString( MString( Value)); break; }
                        case MUniformParameter::kTypeBool: { BOOL Value; if( fD3DEffect->GetBool( d3dParameter, &Value) == D3D_OK) uniform.setAsBool( Value ? true : false); break; }
                        case MUniformParameter::kTypeInt: { INT Value; if( fD3DEffect->GetInt( d3dParameter, &Value) == D3D_OK) uniform.setAsInt( Value); break; }
                        default:
                            if( type >= MUniformParameter::kType1DTexture && type <= MUniformParameter::kTypeEnvTexture)
                            {
                                LPCSTR resource;
                                if( GetAnnotation( d3dParameter, "ResourceName", resource) && *resource)
                                    uniform.setAsString( findResource( MString( resource), fShader));
                                else if( GetAnnotation( d3dParameter, "SasResourceAddress", resource) && *resource)
                                    uniform.setAsString( findResource( MString( resource), fShader));
                            }
                    }
                    fUniformParameters.append( uniform);
                }
            }
        }
    }

    void hlslShader::updateUIVisibilityFromAnnotation( D3DXHANDLE d3dParameter, MUniformParameter& uniform )
    {
        BOOL visible = true;
        if( GetBOOLAnnotation( d3dParameter, "SasUiVisible", visible) )
        {
            uniform.setUIHidden(!visible);
        }
        else
        {
            LPCSTR uiTypeValue = NULL;
            bool  foundUIType = GetAnnotation( d3dParameter, "UIType", uiTypeValue);
            if (foundUIType && !_stricmp(uiTypeValue, "None"))
            {
                uniform.setUIHidden(true);
            }
            // As per NVidia SAS docs v1.0.3:
            LPCSTR uiWidgetValue = NULL;
            foundUIType = GetAnnotation( d3dParameter, "UIWidget", uiWidgetValue);
            if (foundUIType && !_stricmp(uiWidgetValue, "None"))
            {
                uniform.setUIHidden(true);
            }
        }
    }

    void hlslShader::updateRangeFromAnnotation( D3DXHANDLE d3dParameter, MUniformParameter& uniform)
    {
        switch(uniform.type())
        {
        case MUniformParameter::kTypeFloat:
            {
                float uiMinFloat = NULL;
                if(GetAnnotation(d3dParameter, "SasUiMin",uiMinFloat))
                {
                    double uiMin = uiMinFloat;
                    uniform.setRangeMin(uiMin);
                }
                else if(GetAnnotation(d3dParameter, "UIMin",uiMinFloat))
                {
                    double uiMin = uiMinFloat;
                    uniform.setRangeMin(uiMin);
                }
                else if(GetAnnotation(d3dParameter, "uimin",uiMinFloat))
                {
                    double uiMin = uiMinFloat;
                    uniform.setRangeMin(uiMin);
                }

                float uiMaxFloat = NULL;
                if(GetAnnotation(d3dParameter, "SasUiMax",uiMaxFloat))
                {
                    double uiMax = uiMaxFloat;
                    uniform.setRangeMax(uiMax);
                }
                else if(GetAnnotation(d3dParameter, "UIMax",uiMaxFloat))
                {
                    double uiMax = uiMaxFloat;
                    uniform.setRangeMax(uiMax);
                }
                else if(GetAnnotation(d3dParameter, "uimax",uiMaxFloat))
                {
                    double uiMax = uiMaxFloat;
                    uniform.setRangeMax(uiMax);
                }
            }
            break;
        case MUniformParameter::kTypeInt:
            {
                int uiMinInt = NULL;
                if(GetAnnotation(d3dParameter, "SasUiMin",uiMinInt))
                {
                    double uiMin = uiMinInt;
                    uniform.setRangeMin(uiMin);
                }
                else if(GetAnnotation(d3dParameter, "UIMin",uiMinInt))
                {
                    double uiMin = uiMinInt;
                    uniform.setRangeMin(uiMin);
                }
                else if(GetAnnotation(d3dParameter, "uimin",uiMinInt))
                {
                    double uiMin = uiMinInt;
                    uniform.setRangeMin(uiMin);
                }

                int uiMaxInt = NULL;
                if(GetAnnotation(d3dParameter, "SasUiMax",uiMaxInt))
                {
                    double uiMax = uiMaxInt;
                    uniform.setRangeMax(uiMax);
                }
                else if(GetAnnotation(d3dParameter, "UIMax",uiMaxInt))
                {
                    double uiMax = uiMaxInt;
                    uniform.setRangeMax(uiMax);
                }
                else if(GetAnnotation(d3dParameter, "uimax",uiMaxInt))
                {
                    double uiMax = uiMaxInt;
                    uniform.setRangeMax(uiMax);
                }
            }
            break;

        default:
            break;
        };
    }

    //
    // Set the shader we're using
    //
    MStatus hlslShader::setShader( const MString& shader)
    {
        fDiagnostics = "";
        fDescription = "";
        fShader = shader;

        MD3D9Renderer *pRenderer = MD3D9Renderer::theRenderer();
        if (pRenderer != NULL) {
            IDirect3DDevice9* pDevice = pRenderer->getD3D9Device();
            if( pDevice )
            {

                release();
                fTechniques.setLength( 0);
                LPD3DXBUFFER errors = NULL;

                // Make sure the shader's directory is current, as the compiler will try and resolve
                // everything relative to that
                //
                MFileObject fileObject;
                // we remove "/" or "\\" at the beginning of variable shader
                // if it is relative path
                struct stat statBuf;
                MString shaderSimpleName;
                if (stat(shader.asChar(), &statBuf) == -1)
                {
                    if(shader.index('/') == 0 || shader.index('\\') == 0)
                        shaderSimpleName = shader.substring(1, shader.length() - 1);
                    else
                        shaderSimpleName = shader;
                }
                else
                {
                    shaderSimpleName = shader;
                }

                fileObject.setRawFullName( shaderSimpleName);
                MString resolvedPath ;
                resolvedPath = fileObject.resolvedPath();

                // for bug 275673
                // if shader include directory name, for example /shader/...
                // resolvePath will combine the directory, so directory name in variable shader
                // is redundant. We remove directory name in variable shader
                int idx = shaderSimpleName.rindex('/');
                MString shaderName;
                if(idx == -1)
                {
                    shaderName = shaderSimpleName;
                }
                else
                {
                    shaderName = shaderSimpleName.substring(idx + 1, shaderSimpleName.length() - 1);
                }
                TCHAR pwd[ MAX_PATH];
                if( resolvedPath.length() > 0)
                {
                    ::GetCurrentDirectory( MAX_PATH, pwd);
                    ::SetCurrentDirectory( resolvedPath.asChar());
                }

                // Note that for some strange reason, DX9 doesn't support D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
                // so we try the old DX9 compiler separately. We can remove the second attempt once we move to DX10
                // (which doesn't support the D3DXSHADER_USE_LEGACY_D3DX9_31_DLL  flag)
                //
    #ifndef D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY
    #define D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY 0
    #endif
                if( FAILED( D3DXCreateEffectFromFile( pDevice, shaderName.asChar(), NULL, NULL, D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, NULL, &fD3DEffect, &errors))
    #ifdef D3DXSHADER_USE_LEGACY_D3DX9_31_DLL
                 && FAILED( D3DXCreateEffectFromFile( pDevice, shaderName.asChar(), NULL, NULL, D3DXSHADER_USE_LEGACY_D3DX9_31_DLL, NULL, &fD3DEffect, NULL))
    #endif
                    )
                {
                    fDiagnostics += "Error trying to create effect " + shader + ":\n\t";
                    if( errors)
                        fDiagnostics += (const char*)errors->GetBufferPointer();
                    fDiagnostics += "\n";
                }
                else
                {
                    if( errors)
                        fDiagnostics += (const char*)errors->GetBufferPointer();
                    fD3DEffect->SetStateManager( &HLSLStateManager::sInstance);
                    fD3DEffect->GetDesc( &fD3DEffectDesc);
                    for( unsigned int t = 0; t < fD3DEffectDesc.Techniques; t++)
                    {
                        D3DXHANDLE technique = fD3DEffect->GetTechnique( t);
                        if( technique)
                        {
    #ifdef VALIDATE_TECHNIQUE_LIST
                            if( fD3DEffect->ValidateTechnique( technique))
    #endif
                            {
                                D3DXTECHNIQUE_DESC techniqueDesc;
                                fD3DEffect->GetTechniqueDesc( technique, &techniqueDesc);
                                fTechniques.append( techniqueDesc.Name);
                            }
                        }
                    }
                }

                // Restore the previous working directory if we changed it
                if( resolvedPath.length() > 0)
                {
                    ::SetCurrentDirectory( pwd);
                }

                // Update our uniform parameters
                if( fD3DEffect)
                {
                    fUniformParameters.setLength( 0);
                    for( unsigned int p = 0; p < fD3DEffectDesc.Parameters; p++)
                    {
                        setupUniform( fD3DEffect->GetParameter( NULL, p), "");
                    }
                    setUniformParameters( fUniformParameters);

                    // And re-select our current technique (which triggers a UI change
                    // and also handles the case where the current technique doesn't
                    // exist in this effect
                    MPlug( thisMObject(), sTechnique).setValue( fTechnique);
                }
            }
        }

        // Update our shader info attributes
        MPlug diagnosticsPlug( thisMObject(), sDiagnostics);
        diagnosticsPlug.setValue( fDiagnostics);
        MPlug descriptionPlug( thisMObject(), sDescription);
        descriptionPlug.setValue( fDescription);

        return MS::kSuccess;
    }

    bool hlslShader::passHasTranparency( D3DXHANDLE d3dPass )
    {
        bool hasTransparency = false;

        //printf("Pass %s has %d annotations\n", passDesc.Name, passDesc.Annotations);
        BOOL boolParameter;
        INT intParameter1, intParameter2;
        boolParameter = false;
        {
            BOOL getAnnot = GetBOOLAnnotation( d3dPass, "Zenable", boolParameter);
            //printf("Get zenable %d, %d\n", getAnnot, boolParameter);
            getAnnot = GetBOOLAnnotation( d3dPass, "ZWriteEnable", boolParameter);
            //printf("Get zwriteenable %d, %d\n", getAnnot, boolParameter);
        }

        BOOL getAnnot = false;
        {
            D3DXHANDLE d3dSemantic = fD3DEffect->GetAnnotationByName( d3dPass, "AlphaBlendEnable");
            if (d3dSemantic)
            {
                fD3DEffect->GetBool( d3dSemantic, &boolParameter);
                getAnnot = true;
            }
        }

        //printf("Alpha blend found %d, is enabled %d !\n", getAnnot, boolParameter);
        if (getAnnot && boolParameter)
        {
            hasTransparency = true;

            {
                D3DXHANDLE d3dSemantic = fD3DEffect->GetAnnotationByName( d3dPass, "SrcBlend");
                if (d3dSemantic)
                    fD3DEffect->GetInt( d3dSemantic, &intParameter1);
            }
            {
                D3DXHANDLE d3dSemantic = fD3DEffect->GetAnnotationByName( d3dPass, "DestBlend");
                if (d3dSemantic)
                    fD3DEffect->GetInt( d3dSemantic, &intParameter2);
            }
        }

        return hasTransparency;
    }

    //
    // Set the technique we're using
    //
    MStatus hlslShader::setTechnique( const MString& technique)
    {
        // We're changing technique, blow away any existing vertex declaration
        releaseVertexDeclaration();

        // Now, if we have an effect update our internal data and parameter list
        // (otherwise just leave the current parameters in place)
        if( fD3DEffect && fTechniques.length())
        {
            // Try and switch to this technique
            fD3DTechnique = fD3DEffect->GetTechniqueByName( technique.asChar());
    #ifdef VALIDATE_TECHNIQUES
            if( !fD3DTechnique || !fD3DEffect->ValidateTechnique( fD3DTechnique))
    #else
            if( !fD3DTechnique)
    #endif
            {
                fD3DTechnique = fD3DEffect->GetTechniqueByName( fTechnique.asChar());
    #ifdef VALIDATE_TECHNIQUES
                if( !fD3DTechnique || !fD3DEffect->ValidateTechnique( fD3DTechnique))
    #else
                if( !fD3DTechnique)
    #endif
                {
    #ifdef VALIDATE_TECHNIQUES
                    if( FAILED( fD3DEffect->FindNextValidTechnique( fD3DTechnique, &fD3DTechnique)))
                    {
                        fD3DTechnique = NULL;
                    }
    #else
                    fD3DTechnique = fD3DEffect->GetTechnique( 0);
    #endif
                }
            }

            // Now suck the name back out
            fD3DEffect->GetTechniqueDesc( fD3DTechnique, &fD3DTechniqueDesc);
            fTechnique = fD3DTechniqueDesc.Name;

            // Make this the active technique
            fD3DEffect->SetTechnique( fD3DTechnique);

            // Update our passes and varying parameters
            fVertexStructure.removeElements();
            fD3DVertexDeclaration = new IDirect3DVertexDeclaration9*[ fD3DTechniqueDesc.Passes];

            int offset = 0;
            for( unsigned int p = 0; p < fD3DTechniqueDesc.Passes; p++)
            {
                D3DXHANDLE d3dPass = fD3DEffect->GetPass( fD3DTechnique, p);

    #if defined(_BLENDPARSING_READY_)
                if (p == 0)
                    fTechniqueHasBlending = passHasTranparency( d3dPass );
    #endif
                D3DXPASS_DESC passDesc;
                fD3DEffect->GetPassDesc( d3dPass, &passDesc);

                D3DXSEMANTIC Semantics[ MAXD3DDECLLENGTH];
                D3DVERTEXELEMENT9 VertexElements[ MAXD3DDECLLENGTH + 1];
                UINT NumSemantics = 0;
                D3DXGetShaderInputSemantics( passDesc.pVertexShaderFunction, Semantics, &NumSemantics);
                for( unsigned int s = 0; s < NumSemantics; s++)
                {
                    const SemanticInfo& Info = gSemanticInfo[ Semantics[ s].Usage];
                    MString Name = Info.Name; // Can't get the name through DX so use a semantic based name =(
                    if( Info.Type != MVaryingParameter::kPosition && Info.Type != MVaryingParameter::kNormal)
                        Name += Semantics[ s].UsageIndex;
                    MVaryingParameter varying( Name, MVaryingParameter::kFloat, Info.MinElements, Info.MaxElements, Info.Type, Info.Type == MVaryingParameter::kTexCoord ? true : false);
                    fVertexStructure.addElement( varying);

                    VertexElements[ s].Stream = 0;
                    VertexElements[ s].Offset = (WORD)offset;
                    VertexElements[ s].Type = Info.D3DType;
                    VertexElements[ s].Method = D3DDECLMETHOD_DEFAULT;
                    VertexElements[ s].Usage = (BYTE)Semantics[ s].Usage;
                    VertexElements[ s].UsageIndex = (BYTE)Semantics[ s].UsageIndex;
                    offset += varying.getMaximumStride();
                }
                static D3DVERTEXELEMENT9 VertexElementEnd = D3DDECL_END();
                VertexElements[ NumSemantics] = VertexElementEnd;

                // Make sure that the point is initialized in case something fails
                fD3DVertexDeclaration[p] = NULL;

                MD3D9Renderer* pRenderer = MD3D9Renderer::theRenderer();
                if (pRenderer != NULL) {
                    IDirect3DDevice9* d3dDevice = pRenderer->getD3D9Device();
                    if( d3dDevice) {
                        d3dDevice->CreateVertexDeclaration( VertexElements, &fD3DVertexDeclaration[ p]);
                    }
                }
            }
            MVaryingParameterList list;
            if( fVertexStructure.numElements() > 0)
            {
                // Only add our parameters if we have some - otherwise the empty structure
                // shows up in the UI which is a bit ugly
                list.append( fVertexStructure);
            }
            setVaryingParameters( list);
        }

        return MS::kSuccess;
    }

    // Error reporting
    void hlslShader::reportInternalError( const char* function, size_t errcode )
    {
        MString es = "hlslShader";

        try
        {
            if ( this )
            {
                if ( ++fErrorCount > ERROR_LIMIT )
                    return;
                MString s;
                s += "\"";
                s += name();
                s += "\": ";
                s += typeName();
                es = s;
            }
        }
        catch ( ... )
        {}
        es += " internal error ";
        es += (int)errcode;
        es += " in ";
        es += function;
        MGlobal::displayError( es );
    }                                      // hlslShader::reportInternalError

    void parameterRequirements(const MVaryingParameter& parameter, MGeometryRequirements& requirements)
    // recursive requirements
    {
        if (parameter.numElements() == 0) {

            switch( parameter.semantic())
            {

            case (MVaryingParameter::kTexCoord) :
                requirements.addTexCoord(parameter.name());
                break;
            case (MVaryingParameter::kPosition) :
                requirements.addPosition();
                break;
            case (MVaryingParameter::kNormal) :
                requirements.addNormal();
                break;
            case (MVaryingParameter::kColor) :
                requirements.addColor(parameter.name());
                break;
            default:
                // don't know what it is.
                break;
            }
        }
        else {
            for (unsigned int i = 0; i < parameter.numElements(); ++i) {
                parameterRequirements(parameter.getElement(i), requirements);
            }
        }
    }

    /* virtual */
    MStatus hlslShader::renderSwatchImage(MImage& image)
    // Override this method to draw a image for swatch rendering.
    {

    // #define _USE_OPENGL_
    #if defined _USE_OPENGL_    //

        MStatus status = MStatus::kFailure;

        // Get the hardware openGL renderer utility class
        MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
        if (pRenderer)
        {
            const MString& backEndStr = pRenderer->backEndString();

            // get the geometry requirements
            MDagPath path;                          // NULL path
            MGeometryRequirements requirements;
            requirements.addPosition();
            requirements.addNormal();
            requirements.addTexCoord(MString("map1"));
            requirements.addComponentId();

            MGeometryManager::GeometricShape shape = MGeometryManager::kDefaultSphere;
            MGeometryList* geomIter = MGeometryManager::referenceDefaultGeometry(shape, requirements);

            if(geomIter == NULL) {
                return MStatus::kFailure;
            }

            // Make the swatch context current
            // ===============================
            //
            unsigned int width, height;
            image.getSize(width, height);

            MStatus status2 = pRenderer->makeSwatchContextCurrent( backEndStr, width, height );

            glPushAttrib(GL_ALL_ATTRIB_BITS);
            glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);

            if( status2 != MS::kSuccess ) {
                MGeometryManager::dereferenceDefaultGeometry(geomIter);
            }

            // Get the light direction from the API, and use it
            // =============================================
            {
                static float  specular[] = { 0.7f, 0.7f, 0.7f, 1.0f};
                static float  shine[] = { 100.0f };
                static float  ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f };

                glEnable(GL_LIGHTING);
                glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
                glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
                glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);

                float lightPos[4];
                pRenderer->getSwatchLightDirection( lightPos[0], lightPos[1], lightPos[2], lightPos[3] );

                static float default_ambient_light[]={0.4f, 0.4f, 0.4f, 1.0f};
                static float default_diffuse_light[]={0.8f, 0.8f, 0.8f, 1.0f};
                static float default_specular_light[]={1.0f, 1.0f, 1.0f, 1.0f};

                glLightfv(GL_LIGHT0, GL_AMBIENT, default_ambient_light);
                glLightfv(GL_LIGHT0, GL_DIFFUSE, default_diffuse_light);
                glLightfv(GL_LIGHT0, GL_SPECULAR, default_specular_light);

                glPushMatrix();
                glLoadIdentity();
                glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
                glEnable(GL_LIGHT0);
                glPopMatrix();

            }

            // Get camera
            // ==========
            {
                // Get the camera frustum from the API
                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
                double l, r, b, t, n, f;
                pRenderer->getSwatchPerspectiveCameraSetting( l, r, b, t, n, f );
                glFrustum( l, r, b, t, n, f );

                // want to store these into the
            }

            // Get the default background color and clear the background
            //
            float r, g, b, a;
            MHWShaderSwatchGenerator::getSwatchBackgroundColor( r, g, b, a );
            glClearColor( r, g, b, a );
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            glShadeModel(GL_SMOOTH);
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LEQUAL);
            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

            for( ; !geomIter->isDone(); geomIter->next())
            {

                MGeometry& geometry = geomIter->geometry(MGeometryList::kNone);
                const MMatrix& mtm = geomIter->objectToWorldMatrix();

                glMatrixMode(GL_MODELVIEW);
                glLoadMatrixd(mtm[0]);

                // TO DO the geomIterator needs to give up the project matrix.

                MGeometryPrimitive primitives = geometry.primitiveArray(0);

                const MGeometryData pos = geometry.position();
                const MGeometryData normal = geometry.normal();

                // Draw The Swatch

                if (pos.data() != NULL && primitives.data() != NULL) {

                    glEnableClientState(GL_VERTEX_ARRAY);
                    glVertexPointer(3, GL_FLOAT, 0, (float*) pos.data());

                    if (normal.data()) {
                        glEnableClientState(GL_NORMAL_ARRAY);
                        glNormalPointer(GL_FLOAT, 0,  normal.data());
                    }

                    // get the first texture and blind it
                    bool boundTexture = false;
                    MString defaultMap("map1");
                    MGeometryData uvCoord = geometry.texCoord(defaultMap);

                    // Setup our shape dependent parameters
                    for( int u = fUniformParameters.length(); u--; ){
                        MUniformParameter uniform = fUniformParameters.getElement( u);

                        if ( uniform.isATexture()) {
                            // make sure that it a 2d texture as we can't handle anything else.
                            if( uniform.type() == MUniformParameter::kType2DTexture)
                            {
                                // Get the plug used for the first texture - but be careful to preserve
                                // the hasChanged state so that the shader correctly updates this parameter
                                // (otherwise it may never setup the texture parameter because we suppressed
                                // the initial hasChanged trigger)
                                bool wasDirty = uniform.hasChanged( *geomIter);
                                MPlug plug = uniform.getSource();
                                if( wasDirty) uniform.setDirty();

                                MImageFileInfo::MHwTextureType hwType;
                                if (MS::kSuccess == MHwTextureManager::glBind(plug, hwType))
                                {
                                    boundTexture = true;
                                    break;
                                }
                            }
                        }
                    }

                    if (uvCoord.data() && boundTexture) {

                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

                        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
                        glTexCoordPointer(2, GL_FLOAT, 0, uvCoord.data());

                        // Base colour is always white
                        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
                    }
                    else {

                        glDisable(GL_TEXTURE_2D);
                        glBindTexture(GL_TEXTURE_2D, 0);
                        glColor4f(0.1f, 0.7f, 0.7f, 1.0f);
                    }

                    MGeometryPrimitive primitives = geometry.primitiveArray(0);

                    // renderer
                    glDrawElements(GL_TRIANGLES, primitives.elementCount(), GL_UNSIGNED_INT, primitives.data());

                    // Read pixels back from swatch context to MImage
                    pRenderer->readSwatchContextPixels( backEndStr, image );

                    // Double check the outing going image size as image resizing
                    // was required to properly read from the swatch context
                    image.getSize( width, height );

                    glPopAttrib();
                    glPopClientAttrib();

                    status = MStatus::kSuccess;
                }
            }

            MGeometryManager::dereferenceDefaultGeometry(geomIter);
        }

    #else

        MStatus status = MStatus::kFailure;

        unsigned int width, height;
        image.setRGBA(true);
        image.getSize(width, height);

        // get the geometry requirements
        ShaderContext context;                  // NULL path and shading engine
        MGeometryRequirements requirements;
        populateRequirements(context, requirements);

        MD3D9Renderer*  Render = MD3D9Renderer::theRenderer();
        if (Render != NULL) {

            Render->makeSwatchContextCurrent(width, height);

            // render the back ground
            Render->setBackgroundColor(MColor(0.1f, 0.1f, 0.1f));

            MGeometryList* geomIter = MGeometryManager::referenceDefaultGeometry(MGeometryManager::kDefaultSphere,
                requirements);

            if (geomIter == NULL) {
                return status;
            }

            // set up lighting
            // TO DO

            // render the object this the shader and defaulting geometry
            if (render(*geomIter) != MStatus::kSuccess) {
                // revert to a simple fixed color material render
                // TO DO
            }

            // read back the image
            Render->readSwatchContextPixels(image);

            // let go of the geoemtry
            MGeometryManager::dereferenceDefaultGeometry(geomIter);

            status = MStatus::kSuccess;
        }

    #endif

        return status;
    }

    /* virtual */
    MStatus hlslShader::getAvailableImages( const MPxHardwareShader::ShaderContext &context,
                                            const MString &uvSetName,
                                            MStringArray &imageNames )
    {
        // Only scan for available images if we have a valid effect.
        //
        if( !fD3DEffect )
        {
            return MStatus::kNotImplemented;
        }

        // Locate the varying parameters whose source is 'uvSetName'
        //
        MStringArray uvParams;
        unsigned int nVarying = fVertexStructure.numElements();
        for( unsigned int i = 0; i < nVarying; i++ )
        {
            MVaryingParameter elem = fVertexStructure.getElement(i);
            if( elem.getSourceType() == MVaryingParameter::kTexCoord &&
                elem.getSourceSetName() == uvSetName )
            {
                uvParams.append( elem.name() );
            }
        }

        // Determine the default texture.
        //
        MString defaultTex;
        if( uvParams.length() > 0 )
        {
            // Only process the first entry of this UV set (if multiple exist).
            // There can only be one default, so we'll only consider the default
            // of the first varying input of this UV set.
            //
            MFnDependencyNode depFn( thisMObject() );
            MString attrName( uvParams[0] );
            attrName += "_DefaultTexture";
            MPlug defaultTexPlug = depFn.findPlug( attrName );
            if( !defaultTexPlug.isNull() )
            {
                defaultTexPlug.getValue( defaultTex );
            }
        }

        // Locate any texture UVLinks that point to these uvParams and record
        // those textures.
        //
        unsigned int nUniform = fUniformParameters.length();
        unsigned int nUVParams = uvParams.length();
        for( unsigned int i = 0; i < nUVParams; i++ )
        {
            const MString& varName = uvParams[i];
            for( unsigned int j = 0; j < nUniform; j++ )
            {
                MUniformParameter elem = fUniformParameters.getElement(j);
                if( elem.type() != MUniformParameter::kType2DTexture )
                {
                    continue;
                }

                LPCSTR uvLinkAnnot;
                D3DXHANDLE d3dParameter = (D3DXHANDLE) elem.userData();
                if( !GetAnnotation(d3dParameter, "UVLink", uvLinkAnnot) )
                {
                    continue;
                }

                MString uvLink( uvLinkAnnot );
                if( uvLink == varName )
                {
                    if( elem.name() == defaultTex )
                    {
                        imageNames.insert( elem.name(), 0 );
                    }
                    else
                    {
                        imageNames.append( elem.name() );
                    }
                }
            }
        }

        // If no UVLinks found, display all 2D textures.
        //
        if( imageNames.length() == 0 )
        {
            for( unsigned int i = 0; i < nUniform; i++ )
            {
                MUniformParameter elem = fUniformParameters.getElement(i);
                if( elem.type() == MUniformParameter::kType2DTexture )
                {
                    if( elem.name() == defaultTex )
                    {
                        imageNames.insert( elem.name(), 0 );
                    }
                    else
                    {
                        imageNames.append( elem.name() );
                    }
                }
            }
        }
        return (imageNames.length() > 0) ? MStatus::kSuccess : MStatus::kNotImplemented;
    }

    /* virtual */
    MStatus hlslShader::renderImage( const MPxHardwareShader::ShaderContext &context,
                                    const MString &imageName,
                                    floatRegion region,
                                    const MPxHardwareShader::RenderParameters& /*parameters*/,
                                    int &imageWidth,
                                    int &imageHeight )
    {

    #if defined _USE_OPENGL_    //

        MUniformParameter imageParam;
        bool foundParam = false;
        unsigned int nUniform = fUniformParameters.length();
        for( unsigned int i = 0; i < nUniform; i++ )
        {
            MUniformParameter elem = fUniformParameters.getElement(i);
            if( elem.name() == imageName )
            {
                imageParam = elem;
                foundParam = true;
                break;
            }
        }

        if( !foundParam )
        {
            return MStatus::kNotImplemented;
        }

        // Only supports 2D textures.
        //
        if( imageParam.type() != MUniformParameter::kType2DTexture )
        {
            return MStatus::kNotImplemented;
        }

        // Draw the texture (in OpenGL)
        //
        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT);

        // Use MHwTextureManager for our texture cache, since we need to
        // render in OpenGL.
        //
        MPlug plug = imageParam.getSource();
        MImageFileInfo::MHwTextureType hwType;
        if( MS::kSuccess != MHwTextureManager::glBind(plug, hwType) )
        {
            glPopAttrib();
            glPopClientAttrib();

            MFnDependencyNode fnNode( thisMObject() );
            MString sMsg = "hlslShader ";
            sMsg += fnNode.name();
            sMsg += " : failed to load texture \"";
            sMsg += imageName;
            sMsg += "\".";
            MGlobal::displayWarning( sMsg );

            return MStatus::kNotImplemented;
        }

        GLint width = 0;
        GLint height = 0;
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        glBegin( GL_QUADS );
        glTexCoord2f(region[0][0], region[0][1]);
        glVertex2f(region[0][0], region[0][1]);
        glTexCoord2f(region[0][0], region[1][1]);
        glVertex2f(region[0][0], region[1][1]);
        glTexCoord2f(region[1][0], region[1][1]);
        glVertex2f(region[1][0], region[1][1]);
        glTexCoord2f(region[1][0], region[0][1]);
        glVertex2f(region[1][0], region[0][1]);
        glEnd();

        glPopAttrib();
        glPopClientAttrib();

        imageWidth = (int) width;
        imageHeight = (int) height;

        return MStatus::kSuccess;

    #endif // _USE_OPENGL_

        return MStatus::kFailure;
    }

    MStatus hlslShader::uvLinks( const MString& varyingParamName,
                                MStringArray& uniformTextureNames )
    {
        if( !fD3DEffect )
        {
            return MS::kFailure;
        }

        // Parse through the uniform parameters for UVLink annotations
        // against 'varyingParamName'.
        //
        unsigned int nUniform = fUniformParameters.length();
        for( unsigned int i = 0; i < nUniform; i++ )
        {
            MUniformParameter elem = fUniformParameters.getElement(i);
            if( elem.type() != MUniformParameter::kType2DTexture )
            {
                continue;
            }

            LPCSTR uvLinkAnnot;
            D3DXHANDLE d3dParameter = (D3DXHANDLE) elem.userData();
            if( !GetAnnotation(d3dParameter, "UVLink", uvLinkAnnot) )
            {
                continue;
            }

            MString uvLink( uvLinkAnnot );
            if( uvLink == varyingParamName )
            {
                uniformTextureNames.append( elem.name() );
            }
        }
        return MS::kSuccess;
    }

    // hlslShaderCmd
    //
    #define kUVLinkFlag         "-uvl"
    #define kUVLinkFlagLong     "-uvLink"

    hlslShaderCmd::hlslShaderCmd()
    {}

    hlslShaderCmd::~hlslShaderCmd()
    {}

    MStatus hlslShaderCmd::doIt( const MArgList& args )
    {
        MStatus status;

        // Parse the shader node
        //
        MArgParser parser( syntax(), args );
        MString nodeName;
        parser.getCommandArgument( 0, nodeName );

        MSelectionList list;
        status = list.add( nodeName );
        MObject shaderNode;
        status = list.getDependNode( 0, shaderNode );
        if( status != MS::kSuccess )
        {
            displayError( "Invalid hlslShader node: " + nodeName );
            return MS::kFailure;
        }

        MFnDependencyNode depFn( shaderNode );
        if( depFn.typeId() != hlslShader::sId )
        {
            displayError( "Invalid hlslShader node: " + nodeName );
            return MS::kFailure;
        }

        hlslShader* shader = (hlslShader*) depFn.userNode();
        if( !shader )
        {
            displayError( "Invalid hlslShader node: " + nodeName );
            return MS::kFailure;
        }

        // Process flag arguments
        //
        if( parser.isFlagSet(kUVLinkFlag) )
        {
            MString paramName;
            parser.getFlagArgument( kUVLinkFlag, 0, paramName );

            MStringArray textures;
            shader->uvLinks( paramName, textures );
            setResult( textures );
        }

        return MS::kSuccess;
    }

    MSyntax hlslShaderCmd::newSyntax()
    {
        MSyntax syntax;
        syntax.addFlag( kUVLinkFlag, kUVLinkFlagLong, MSyntax::kString );
        syntax.addArg( MSyntax::kString );
        return syntax;
    }

    void* hlslShaderCmd::creator()
    {
        return new hlslShaderCmd;
    }