//-----------------------------------------------------------------------------
CPUTResult CPUTRenderStateBlockOGL::ReadProperties(
    CPUTConfigFile                &file,
    const cString                 &blockName,
    const CPUTRenderStateMapEntry *pMap,
    void                          *pDest
)
{
  //  assert(false);
    CPUTConfigBlock *pProperties = file.GetBlockByName(blockName);
    if( !pProperties )
    {
        // Note: We choose not to assert here.  The nature of the parameter block is that
        // only the values that deviate from default need to be present.  It is very
        // common that blocks will be missing
        return CPUT_ERROR_PARAMETER_BLOCK_NOT_FOUND;
    }

    UINT count = pProperties->ValueCount();
    for( UINT ii=0; ii<count; ii++ )
    {
        // Get the next property
        CPUTConfigEntry *pValue = pProperties->GetValue(ii);
        ASSERT( pValue->IsValid(), _L("Invalid Value: '")+pValue->NameAsString()+_L("'.") );
        ReadValue( pValue, pMap, pDest );
    }
    return CPUT_SUCCESS;
} // CPUTRenderStateBlockOGL::ReadProperties()
//-----------------------------------------------------------------------------
bool CPUTShaderDX11::ShaderRequiresPerModelPayload( CPUTConfigBlock &properties )
{
    ID3D11ShaderReflection *pReflector = NULL;

    D3DReflect( mpBlob->GetBufferPointer(), mpBlob->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&pReflector);
    // Walk through the shader input bind descriptors.
    // If any of them begin with '@', then we need a unique material per model (i.e., we need to clone the material).
    int ii=0;
    D3D11_SHADER_INPUT_BIND_DESC desc;
    HRESULT hr = pReflector->GetResourceBindingDesc( ii++, &desc );
    while( SUCCEEDED(hr) )
    {
        cString tagName = s2ws(desc.Name);
        CPUTConfigEntry *pValue = properties.GetValueByName(tagName);
        if( !pValue->IsValid() )
        {
            // We didn't find our property in the file.  Is it in the global config block?
            pValue = CPUTMaterial::mGlobalProperties.GetValueByName(tagName);
        }
        cString boundName = pValue->ValueAsString();
        if( (boundName.length() > 0) && ((boundName[0] == '@') || (boundName[0] == '#')) )
        {
            return true;
        }
        hr = pReflector->GetResourceBindingDesc( ii++, &desc );
    }
    return false;
}
//-----------------------------------------------------------------------------
void CPUTMaterialDX11::BindConstantBuffers( CPUTShaderParameters &params, const CPUTModel *pModel, int meshIndex )
{
    CPUTConfigEntry *pValue;
    CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary();
    for(params.mConstantBufferCount=0; params.mConstantBufferCount < params.mConstantBufferParameterCount; params.mConstantBufferCount++)
    {
        cString constantBufferName;
        UINT constantBufferCount = params.mConstantBufferCount;

        cString tagName = params.mpConstantBufferParameterName[constantBufferCount];
        {
            pValue = mConfigBlock.GetValueByName(tagName);
            if( !pValue->IsValid() )
            {
                // We didn't find our property in the file.  Is it in the global config block?
                pValue = mGlobalProperties.GetValueByName(tagName);
            }
            ASSERT( pValue->IsValid(), L"Can't find constant buffer '" + tagName + L"'." ); //  TODO: fix message
            constantBufferName = pValue->ValueAsString();
        }
        UINT bindPoint = params.mpConstantBufferParameterBindPoint[constantBufferCount];
        ASSERT( bindPoint < CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS, _L("Constant buffer bind point out of range.") );

        const CPUTModel *pWhichModel = NULL;
        int              whichMesh   = -1;
        if( constantBufferName[0] == '@' )
        {
            pWhichModel = pModel;
            whichMesh   = meshIndex;
        } else if( constantBufferName[0] == '#' )
        {
            pWhichModel = pModel;
        }
        if( !mpConstantBuffer[constantBufferCount] )
        {
            if( pWhichModel )
            {
                mpConstantBuffer[constantBufferCount] = pAssetLibrary->GetConstantBuffer( constantBufferName, pWhichModel, whichMesh );
            } else
            {
                mpConstantBuffer[constantBufferCount] = pAssetLibrary->GetConstantBuffer( constantBufferName );
            }
            ASSERT( mpConstantBuffer[constantBufferCount], _L("Failed getting constant buffer ") + constantBufferName);
        }

        // If has constant buffer, then add to mppBindConstantBuffer
        params.mppBindConstantBuffers[bindPoint]   = ((CPUTBufferDX11*)mpConstantBuffer[constantBufferCount])->GetNativeBuffer();
        if( params.mppBindConstantBuffers[bindPoint] )  { params.mppBindConstantBuffers[bindPoint]->AddRef();}
    }
}
//-----------------------------------------------------------------------------
void CPUTMaterialDX11::BindUAVs( CPUTShaderParameters &params, const CPUTModel *pModel, int meshIndex )
{
    CPUTConfigEntry *pValue;
    CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary();
    memset( params.mppBindUAVs, 0, sizeof(params.mppBindUAVs) );
    for(params.mUAVCount=0; params.mUAVCount < params.mUAVParameterCount; params.mUAVCount++)
    {
        cString uavName;
        UINT uavCount = params.mUAVCount;

        cString tagName = params.mpUAVParameterName[uavCount];
        {
            pValue = mConfigBlock.GetValueByName(tagName);
            if( !pValue->IsValid() )
            {
                // We didn't find our property in the file.  Is it in the global config block?
                pValue = mGlobalProperties.GetValueByName(tagName);
            }
            ASSERT( pValue->IsValid(), L"Can't find UAV '" + tagName + L"'." ); //  TODO: fix message
            uavName = pValue->ValueAsString();
        }
        UINT bindPoint = params.mpUAVParameterBindPoint[uavCount];
        ASSERT( bindPoint < CPUT_MATERIAL_MAX_UAV_SLOTS, _L("UAV bind point out of range.") );

        const CPUTModel *pWhichModel = NULL;
        int              whichMesh   = -1;
        if( uavName[0] == '@' )
        {
            pWhichModel = pModel;
            whichMesh   = meshIndex;
        } else if( uavName[0] == '#' )
        {
            pWhichModel = pModel;
        }
        if( !mpUAV[uavCount] )
        {
            mpUAV[uavCount] = pAssetLibrary->GetBuffer( uavName, pWhichModel, whichMesh );
            ASSERT( mpUAV[uavCount], _L("Failed getting UAV ") + uavName);
        }
        // If has UAV, then add to mppBindUAV
        params.mppBindUAVs[bindPoint]   = ((CPUTBufferDX11*)mpUAV[uavCount])->GetUnorderedAccessView();
        if( params.mppBindUAVs[bindPoint] )  { params.mppBindUAVs[bindPoint]->AddRef();}
    }
}
//-----------------------------------------------------------------------------
void ReadMacrosFromConfigBlock(
    CPUTConfigBlock   *pMacrosBlock,
    CPUT_SHADER_MACRO  *pShaderMacros,
    CPUT_SHADER_MACRO **pUserSpecifiedMacros,
    int               *pNumUserSpecifiedMacros,
    CPUT_SHADER_MACRO **pFinalShaderMacros
    ){
        *pNumUserSpecifiedMacros = pMacrosBlock->ValueCount();

        // Count the number of macros passed in
        CPUT_SHADER_MACRO *pMacro = (CPUT_SHADER_MACRO*)pShaderMacros;
        int numPassedInMacros = 0;
        if( pMacro )
        {
            while( pMacro->Name )
            {
                ++numPassedInMacros;
                ++pMacro;
            }
        }

        // Allocate an array of macro pointer large enough to contain the passed-in macros plus those specified in the .mtl file.
        *pFinalShaderMacros = new CPUT_SHADER_MACRO[*pNumUserSpecifiedMacros + numPassedInMacros + 1];

        // Copy the passed-in macro pointers to the final array
        int jj;
        for( jj=0; jj<numPassedInMacros; jj++ )
        {
            (*pFinalShaderMacros)[jj] = *(CPUT_SHADER_MACRO*)&pShaderMacros[jj];
        }

        // Create a CPUT_SHADER_MACRO for each of the macros specified in the .mtl file.
        // And, add their pointers to the final array
        *pUserSpecifiedMacros = new CPUT_SHADER_MACRO[*pNumUserSpecifiedMacros];
        for( int kk=0; kk<*pNumUserSpecifiedMacros; kk++, jj++ )
        {
            CPUTConfigEntry *pValue   = pMacrosBlock->GetValue(kk);
            (*pUserSpecifiedMacros)[kk].Name       = ws2s(pValue->NameAsString());
            (*pUserSpecifiedMacros)[kk].Definition = ws2s(pValue->ValueAsString());
            (*pFinalShaderMacros)[jj] = (*pUserSpecifiedMacros)[kk];
        }
        (*pFinalShaderMacros)[jj].Name = NULL;
        (*pFinalShaderMacros)[jj].Definition = NULL;
}
//-----------------------------------------------------------------------------
void CPUTMaterialDX11::BindTextures( CPUTShaderParameters &params, const CPUTModel *pModel, int meshIndex )
{
    CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary();

    for(params.mTextureCount=0; params.mTextureCount < params.mTextureParameterCount; params.mTextureCount++)
    {
        cString textureName;
        UINT textureCount = params.mTextureCount;
        cString tagName = params.mpTextureParameterName[textureCount];
        CPUTConfigEntry *pValue = mConfigBlock.GetValueByName(tagName);
        if( !pValue->IsValid() )
        {
            // We didn't find our property in the file.  Is it in the global config block?
            pValue = mGlobalProperties.GetValueByName(tagName);
        }
        ASSERT( pValue->IsValid(), L"Can't find texture '" + tagName + L"'." ); //  TODO: fix message
        textureName = pValue->ValueAsString();
        // If the texture name not specified.  Load default.dds instead
        if( 0 == textureName.length() ) { textureName = _L("default.dds"); }

        UINT bindPoint = params.mpTextureParameterBindPoint[textureCount]; 
        ASSERT( bindPoint < CPUT_MATERIAL_MAX_TEXTURE_SLOTS, _L("Texture bind point out of range.") );

        if( textureName[0] == '@' )
        {
            // This is a per-mesh value.  Add to per-mesh list.
            textureName += ptoc(pModel) + itoc(meshIndex);
        } else if( textureName[0] == '#' )
        {
            // This is a per-mesh value.  Add to per-mesh list.
            textureName += ptoc(pModel);
        }

        // Get the sRGB flag (default to true)
        cString SRGBName = tagName+_L("sRGB");
        CPUTConfigEntry *pSRGBValue = mConfigBlock.GetValueByName(SRGBName);
        bool loadAsSRGB = pSRGBValue->IsValid() ?  loadAsSRGB = pSRGBValue->ValueAsBool() : true;

        if( !mpTexture[textureCount] )
        {
            mpTexture[textureCount] = pAssetLibrary->GetTexture( textureName, false, loadAsSRGB );
            ASSERT( mpTexture[textureCount], _L("Failed getting texture ") + textureName);
        }

        // The shader file (e.g. .fx) can specify the texture bind point (e.g., t0).  Those specifications 
        // might not be contiguous, and there might be gaps (bind points without assigned textures)
        // TODO: Warn about missing bind points?
        params.mppBindViews[bindPoint] = ((CPUTTextureDX11*)mpTexture[textureCount])->GetShaderResourceView();
		params.mppBindViews[bindPoint]->AddRef();
    }
}
//-----------------------------------------------------------------------------
CPUTResult CPUTAssetSetDX11::LoadAssetSet(cString name)
{
    CPUTResult result = CPUT_SUCCESS;

    // if not found, load the set file
    CPUTConfigFile ConfigFile;
    result = ConfigFile.LoadFile(name);
#if 1
    if( !CPUTSUCCESS(result) )
    {
        return result;
    }
    // ASSERT( CPUTSUCCESS(result), _L("Failed loading set file '") + name + _L("'.") );

    mAssetCount = ConfigFile.BlockCount() + 1; // Add one for the implied root node
    // mAssetCount = min(2, mAssetCount); // Add one for the implied root node
    mppAssetList = new CPUTRenderNode*[mAssetCount];
    mppAssetList[0] = mpRootNode;
    mpRootNode->AddRef();

    CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary();
    for(UINT ii=0; ii<mAssetCount-1; ii++) // Note: -1 because we added one for the root node (we don't load it)
    {
        CPUTConfigBlock *pBlock = ConfigFile.GetBlock(ii);
        int assetIndex = pBlock->GetNameValue();
        cString nodeType = pBlock->GetValueByName(_L("type"))->ValueAsString();
        CPUTRenderNode *pParentNode = NULL;

        // TODO: use Get*() instead of Load*() ?

        cString name = pBlock->GetValueByName(_L("name"))->ValueAsString();

        int parentIndex;
        CPUTRenderNode *pNode;
        if(0==nodeType.compare(_L("null")))
        {
            pNode  = pNode = new CPUTNullNode();
            result = ((CPUTNullNode*)pNode)->LoadNullNode(pBlock, &parentIndex);
            pParentNode = mppAssetList[parentIndex+1];
            cString &parentPrefix = pParentNode->GetPrefix();
            pNode->SetPrefix( parentPrefix + _L(".") + name );
            pAssetLibrary->AddNullNode(parentPrefix + name, (CPUTNullNode*)pNode);
            // Add this null's name to our prefix
            // Append this null's name to our parent's prefix
            pNode->SetParent( pParentNode );
            pParentNode->AddChild( pNode );
        }
        else if(0==nodeType.compare(_L("model")))
        {
            CPUTConfigEntry *pValue = pBlock->GetValueByName( _L("instance") );
            CPUTModelDX11 *pModel = new CPUTModelDX11();
            if( pValue == &CPUTConfigEntry::sNullConfigValue )
            {
                // Not found.  So, not an instance.
                pModel->LoadModel(pBlock, &parentIndex, NULL);
            }
            else
            {
                int instance = pValue->ValueAsInt();
                pModel->LoadModel(pBlock, &parentIndex, (CPUTModel*)mppAssetList[instance+1]);
            }
            pParentNode = mppAssetList[parentIndex+1];
            pModel->SetParent( pParentNode );
            pParentNode->AddChild( pModel );
            cString &parentPrefix = pParentNode->GetPrefix();
            pModel->SetPrefix( parentPrefix );
            pAssetLibrary->AddModel(parentPrefix + name, pModel);

            pModel->UpdateBoundsWorldSpace();

#ifdef SUPPORT_DRAWING_BOUNDING_BOXES
            // Create a mesh for rendering the bounding box
            // TODO: There is definitely a better way to do this.  But, want to see the bounding boxes!
            pModel->CreateBoundingBoxMesh();
#endif
            pNode = pModel;
        }
        else if(0==nodeType.compare(_L("light")))
        {
            pNode = new CPUTLight();
            ((CPUTLight*)pNode)->LoadLight(pBlock, &parentIndex);
            pParentNode = mppAssetList[parentIndex+1]; // +1 because we added a root node to the start
            pNode->SetParent( pParentNode );
            pParentNode->AddChild( pNode );
            cString &parentPrefix = pParentNode->GetPrefix();
            pNode->SetPrefix( parentPrefix );
            pAssetLibrary->AddLight(parentPrefix + name, (CPUTLight*)pNode);
        }
        else if(0==nodeType.compare(_L("camera")))
        {
            pNode = new CPUTCamera();
            ((CPUTCamera*)pNode)->LoadCamera(pBlock, &parentIndex);
            pParentNode = mppAssetList[parentIndex+1]; // +1 because we added a root node to the start
            pNode->SetParent( pParentNode );
            pParentNode->AddChild( pNode );
            cString &parentPrefix = pParentNode->GetPrefix();
            pNode->SetPrefix( parentPrefix );
            pAssetLibrary->AddCamera(parentPrefix + name, (CPUTCamera*)pNode);
            if( !mpFirstCamera ) { mpFirstCamera = (CPUTCamera*)pNode; mpFirstCamera->AddRef();}
            ++mCameraCount;
        }
        else
        {
            ASSERT(0,_L("Unsupported node type '") + nodeType + _L("'."));
        }

        // Add the node to our asset list (i.e., the linear list, not the hierarchical)
        mppAssetList[ii+1] = pNode;
        // Don't AddRef.Creating it set the refcount to 1.  We add it to the list, and then we're done with it.
        // Net effect is 0 (+1 to add to list, and -1 because we're done with it)
        // pNode->AddRef();
    }
#endif
    HEAPCHECK
	return result;
}
//-----------------------------------------------------------------------------
void CPUTMaterialEffectOGL::BindConstantBuffers( CPUTShaderParameters &params, const CPUTModel *pModel, int meshIndex )
{
    CPUTAssetLibraryOGL *pAssetLibrary = (CPUTAssetLibraryOGL*)CPUTAssetLibrary::GetAssetLibrary();
    
    for(params.mConstantBufferCount=0; params.mConstantBufferCount < params.mConstantBufferParameterCount; params.mConstantBufferCount++)
    {
        cString constantBufferName;
        UINT constantBufferCount = params.mConstantBufferCount;

// Dirty fix - you should work on std::string in all OS'es ! Why WSTRING ? Whyyyyyyy
#ifndef CPUT_OS_WINDOWS
        cString tagName = params.mConstantBufferParameterNames[constantBufferCount];
#else
        cString tagName = s2ws(params.mConstantBufferParameterNames[constantBufferCount].c_str());
#endif


        CPUTConfigEntry *pValue = mConfigBlock.GetValueByName(tagName);
        if( !pValue->IsValid() )
        {
            // We didn't find our property in the file.  Is it in the global config block?
            pValue = CPUTMaterial::mGlobalProperties.GetValueByName(tagName);
        }
		ASSERT( pValue->IsValid(), L"Can't find constant buffer '" + tagName + L"'." ); //  TODO: fix message
        
        constantBufferName = pValue->ValueAsString();
     
        UINT bindPoint = params.mConstantBufferBindPoints[constantBufferCount];
        ASSERT( bindPoint < CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS, _L("Constant buffer bind point out of range.") );

        params.mBindConstantBufferMin = std::min( params.mBindConstantBufferMin, bindPoint );
        params.mBindConstantBufferMax = std::max( params.mBindConstantBufferMax, bindPoint );

        if( constantBufferName[0] == '@' )
        {
            constantBufferName += ptoc(pModel) + itoc(meshIndex);
        }
        else if( constantBufferName[0] == '#' )
        {
            constantBufferName += ptoc(pModel);
        }
        if( !params.mpConstantBuffer[constantBufferCount] )
        {
            params.mpConstantBuffer[constantBufferCount] = pAssetLibrary->GetConstantBuffer( constantBufferName );
            ASSERT( params.mpConstantBuffer[constantBufferCount], _L("Failed getting constant buffer ") + constantBufferName);
        }

#ifndef CPUT_FOR_OGLES2
        ES3_COMPAT(glUniformBlockBinding(mShaderProgram, bindPoint, bindPoint)); // just use the index as the binding point
#else
#warning "Need to do something with uniform buffers here"
#endif
        // If has constant buffer, then add to mppBindConstantBuffer
//        params.mppBindConstantBuffers[bindPoint]   = ((CPUTBufferOGL*)mpConstantBuffer[constantBufferCount])->GetNativeBuffer();
//        if( params.mppBindConstantBuffers[bindPoint] ) {
//            params.mppBindConstantBuffers[bindPoint]->AddRef();
//        }
    }
	  DEBUG_PRINT(_L("Exit BindConstantBuffers"));

}
void CPUTMaterialEffectOGL::BindUAVs( CPUTShaderParameters &params, const CPUTModel *pModel, int meshIndex )
{
#ifdef CPUT_SUPPORT_IMAGE_STORE

    CPUTAssetLibraryOGL *pAssetLibrary = (CPUTAssetLibraryOGL*)CPUTAssetLibrary::GetAssetLibrary();

    for(params.mUAVCount=0; params.mUAVCount < params.mUAVParameterCount; params.mUAVCount++)
    {
        cString UAVName;
        unsigned int UAVCount = params.mUAVCount;

// Dirty fix
#ifndef CPUT_OS_WINDOWS
        cString tagName = params.mpUAVParameterNames[UAVCount];
#else
        cString tagName = s2ws(params.mpUAVParameterNames[UAVCount].c_str());
#endif

        CPUTConfigEntry *pValue = mConfigBlock.GetValueByName(tagName);
        if( !pValue->IsValid() )
        {
            // We didn't find our property in the file.  Is it in the global config block?
            pValue = CPUTMaterial::mGlobalProperties.GetValueByName(tagName);
        }
        ASSERT( pValue->IsValid(), L"Can't find UAV '" + tagName + L"'." ); //  TODO: fix message
        UAVName = pValue->ValueAsString();
        // If the UAV name not specified.  Load default.dds instead
        if( 0 == UAVName.length() ) 
        { 
            UAVName = _L("default.dds"); 
        }

        UINT bindPoint = params.mUAVCount;//params.mpUAVParameterBindPoints[UAVCount];
        params.mpUAVParameterBindPoints.push_back(bindPoint);
//        UINT bindPoint = params.mpUAVParameterBindPoint[UAVCount]; mpUAVParameterBindPoints
        ASSERT( bindPoint < CPUT_MATERIAL_MAX_UAV_SLOTS, _L("UAV bind point out of range.") );

        params.mBindViewMin = std::min( params.mBindViewMin, bindPoint );
        params.mBindViewMax = std::max( params.mBindViewMax, bindPoint );

        if( UAVName[0] == '@' )
        {
            // This is a per-mesh value.  Add to per-mesh list.
            UAVName += ptoc(pModel) + itoc(meshIndex);
        } else if( UAVName[0] == '#' )
        {
            // This is a per-mesh value.  Add to per-mesh list.
            UAVName += ptoc(pModel);
        }

        // Get the sRGB flag (default to true)
//#ifndef CPUT_OS_WINDOWS
        cString SRGBName = tagName + _L("sRGB");
//#else
//        cString SRGBName = tagName + L"sRGB";
//#endif
        CPUTConfigEntry *pSRGBValue = mConfigBlock.GetValueByName(SRGBName);
        bool loadAsSRGB = pSRGBValue->IsValid() ?  loadAsSRGB = pSRGBValue->ValueAsBool() : true;

        if( !params.mpUAV[UAVCount] )
        {
            params.mpUAV[UAVCount] = pAssetLibrary->GetTexture( UAVName, false, loadAsSRGB );
            ASSERT( params.mpUAV[UAVCount], _L("Failed getting UAV ") + UAVName);
        }

        cString ReadName = tagName + _L("READ");
        CPUTConfigEntry *pRead = mConfigBlock.GetValueByName(ReadName);
        bool read = pRead->IsValid() ? pRead->ValueAsBool() : true;
        cString WriteName = tagName + _L("WRITE");
        CPUTConfigEntry *pWrite = mConfigBlock.GetValueByName(WriteName);
        bool write = pWrite->IsValid() ? pWrite  ->ValueAsBool() : true;
        if(write && read)
            params.mpUAVMode[UAVCount] = GL_READ_WRITE;
        else if(read)
            params.mpUAVMode[UAVCount] = GL_READ_ONLY;
        else
            params.mpUAVMode[UAVCount] = GL_WRITE_ONLY;

        // The shader file (e.g. .fx) can specify the UAV bind point (e.g., t0).  Those specifications 
        // might not be contiguous, and there might be gaps (bind points without assigned UAVs)
        // TODO: Warn about missing bind points?
//        params.mppBindViews[bindPoint] = ((CPUTTextureOGL*)mpTexture[textureCount])->GetShaderResourceView();
//        params.mppBindViews[bindPoint]->AddRef();

        //
        // Match up the UAV name in any UAV samplers given in the renderstate file. If there wasn't
        // one specified for a particular UAV then it just uses the default sampler.
        //
        CPUTRenderStateOGL *pRenderState;
        pRenderState = ((CPUTRenderStateBlockOGL*)mpRenderStateBlock)->GetState();
        
        mSamplerIDs[UAVCount] = pRenderState->DefaultSamplerID;
        for (int renderStateIDX = 0; renderStateIDX < NUM_SAMPLERS_PER_RENDERSTATE; renderStateIDX++) {
			if(renderStateIDX<((CPUTRenderStateBlockOGL*)mpRenderStateBlock)->GetNumSamplers())
            {
                mSamplerIDs[UAVCount] = pRenderState->SamplerIDs[renderStateIDX];
            }
        }
    }
#endif

}
CPUTResult CPUTMaterialEffectOGL::LoadMaterialEffect(
    const cString   &fileName,
    const CPUTModel *pModel,
          int        meshIndex,
    const char     **pShaderMacros,
          int        externalCount,
          cString   *pExternalName,
          float4    *pExternals,
          int       *pExternalOffset,
          int       *pExternalSize
){
    CPUTResult result = CPUT_SUCCESS;
	CPUTConfigEntry *pValue;

    mMaterialName = fileName;
    mMaterialNameHash = CPUTComputeHash( mMaterialName );

    // Open/parse the file
    CPUTConfigFile file;
    result = file.LoadFile(fileName);
    if(CPUTFAILED(result))
    {
        return result;
    }

    // Make a local copy of all the parameters
    CPUTConfigBlock *pBlock = file.GetBlock(0);
    ASSERT( pBlock, _L("Error getting parameter block") );
    if( !pBlock )
    {
        return CPUT_ERROR_PARAMETER_BLOCK_NOT_FOUND;
    }
    mConfigBlock = *pBlock;

    // get necessary device and AssetLibrary pointers
    //ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice();
    CPUTAssetLibraryOGL *pAssetLibrary = (CPUTAssetLibraryOGL*)CPUTAssetLibrary::GetAssetLibrary();

   {
        CPUTConfigEntry *pEntryPointName, *pProfileName;
        int numFiles = 0;
        cString pBase = _L("VertexShaderFileOGL_");
        cString pName = pBase + to_cString(numFiles+1);
        std::vector<cString> filenames;
        while (mConfigBlock.GetValueByName(pName)->IsValid())
        {
            filenames.push_back(mConfigBlock.GetValueByName(pName)->ValueAsString());
            numFiles++;
            pName = pBase + to_cString(numFiles+1);
        }
        if(numFiles > 0)
        {
            pEntryPointName = mConfigBlock.GetValueByName(_L("VertexShaderMain"));
            pProfileName    = mConfigBlock.GetValueByName(_L("VertexShaderProfile"));
            pAssetLibrary->GetVertexShader(
                filenames,
                /*pD3dDevice,*/
                _L("VertexShaderMain"),
                pProfileName->ValueAsString(),
                &mpVertexShader,
                false,
                (CPUT_SHADER_MACRO*)pShaderMacros
            );
        }
    }

#ifdef CPUT_SUPPORT_TESSELLATION
    {
        int numFiles = 0;
        cString pBase = _L("ControlShaderFileOGL_");
        cString pName = pBase + to_cString(numFiles+1);
        std::vector<cString> filenames;
        while (mConfigBlock.GetValueByName(pName)->IsValid())
        {
            filenames.push_back(mConfigBlock.GetValueByName(pName)->ValueAsString());
            numFiles++;
            pName = pBase + to_cString(numFiles+1);
        }
        if(numFiles > 0) 
        {
			pAssetLibrary->GetHullShader(
                filenames,
                /* pD3dDevice,*/
                _L("ControlShaderMain"), 
                _L(""),
                &mpControlShader,
                false,
                (CPUT_SHADER_MACRO*)pShaderMacros
            );
        }
    }
    {
        int numFiles = 0;
        cString pBase = _L("EvaluationShaderFileOGL_");
        cString pName = pBase + to_cString(numFiles+1);
        std::vector<cString> filenames;
        while (mConfigBlock.GetValueByName(pName)->IsValid())
        {
            filenames.push_back(mConfigBlock.GetValueByName(pName)->ValueAsString());
            numFiles++;
            pName = pBase + to_cString(numFiles+1);
        }
        if(numFiles > 0) 
        {
			pAssetLibrary->GetDomainShader(
                filenames,
                /* pD3dDevice,*/
                _L("EvaluationShaderMain"), 
                _L(""),
                &mpEvaluationShader,
                false,
                (CPUT_SHADER_MACRO*)pShaderMacros
            );
        }
    }
#endif
    {
        int numFiles = 0;
        cString pBase = _L("GeometryShaderFileOGL_");
        cString pName = pBase + to_cString(numFiles+1);
        std::vector<cString> filenames;
        while (mConfigBlock.GetValueByName(pName)->IsValid())
        {
            filenames.push_back(mConfigBlock.GetValueByName(pName)->ValueAsString());
            numFiles++;
            pName = pBase + to_cString(numFiles+1);
        }
        if(numFiles > 0) 
        {
			pAssetLibrary->GetGeometryShader(
                filenames,
                /* pD3dDevice,*/
                _L("GeometryShaderMain"), 
                _L(""),
                &mpGeometryShader,
                false,
                (CPUT_SHADER_MACRO*)pShaderMacros
            );
        }
    }

    // load and store the pixel shader if it was specified
    {
        int numFiles = 0;
        cString pBase = _L("FragmentShaderFileOGL_");
        cString pName = pBase + to_cString(numFiles+1);
        std::vector<cString> filenames;
        while (mConfigBlock.GetValueByName(pName)->IsValid())
        {
            filenames.push_back(mConfigBlock.GetValueByName(pName)->ValueAsString());
            numFiles++;
            pName = pBase + to_cString(numFiles+1);
        }
        if(numFiles > 0) 
        {
            pAssetLibrary->GetPixelShader(
                filenames,
                /* pD3dDevice,*/
                _L("FragmentShaderMain"), //mConfigBlock.GetValueByName(_L("FragmentShaderMain"))->ValueAsString(),
                mConfigBlock.GetValueByName(_L("FragmentShaderProfile"))->ValueAsString(),
                &mpFragmentShader,
                false,
                (CPUT_SHADER_MACRO*)pShaderMacros
            );
        }
    }

    // load and store the render state file if it was specified
    pValue = mConfigBlock.GetValueByName(_L("RenderStateFile"));
    if( pValue->IsValid() )
    {
        mpRenderStateBlock = pAssetLibrary->GetRenderStateBlock(pValue->ValueAsString());
    }


    int IsLinked;
    char *shaderProgramInfoLog;
    int maxLength;
    
     mShaderProgram = glCreateProgram();
 
    // Attach our shaders to our program
    // Attach our shaders to our program
    if (mpVertexShader) {
        GL_CHECK(glAttachShader(mShaderProgram, mpVertexShader->GetShaderID()));
    }
    if (mpFragmentShader) {
        GL_CHECK(glAttachShader(mShaderProgram, mpFragmentShader->GetShaderID()));
    }
    if (mpControlShader) {
        GL_CHECK(glAttachShader(mShaderProgram, mpControlShader->GetShaderID()));
    }
    if (mpEvaluationShader) {
        GL_CHECK(glAttachShader(mShaderProgram, mpEvaluationShader->GetShaderID()));
    }
	if (mpGeometryShader) {
		GL_CHECK(glAttachShader(mShaderProgram, mpGeometryShader->GetShaderID()));
	}
    /*
    GL_CHECK(glBindAttribLocation(mShaderProgram, 0, "in_Position"));
    GL_CHECK(glBindAttribLocation(mShaderProgram, 1, "inNormal"));
    GL_CHECK(glBindAttribLocation(mShaderProgram, 2, "inBinormal"));
    GL_CHECK(glBindAttribLocation(mShaderProgram, 3, "inTangent"));
    GL_CHECK(glBindAttribLocation(mShaderProgram, 4, "inTex"));
      */          
    
    // Link our program
    // At this stage, the vertex and fragment programs are inspected, optimized and a binary code is generated for the shader.
    // The binary code is uploaded to the GPU, if there is no error.
    GL_CHECK(glLinkProgram(mShaderProgram));
 
    // Again, we must check and make sure that it linked. If it fails, it would mean either there is a mismatch between the vertex
    // and fragment shaders. It might be that you have surpassed your GPU's abilities. Perhaps too many ALU operations or
    // too many texel fetch instructions or too many interpolators or dynamic loops.
 
    GL_CHECK(glGetProgramiv(mShaderProgram, GL_LINK_STATUS, (int *)&IsLinked));
    if(IsLinked == false)
    {
        // Noticed that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv.
        glGetProgramiv(mShaderProgram, GL_INFO_LOG_LENGTH, &maxLength);
 
        // The maxLength includes the NULL character
        shaderProgramInfoLog = (char *)malloc(maxLength);
 
        // Notice that glGetProgramInfoLog, not glGetShaderInfoLog.
        glGetProgramInfoLog(mShaderProgram, maxLength, &maxLength, shaderProgramInfoLog);
        DEBUG_PRINT_ALWAYS((_L("Failed to link shader program:\n%s\n"), shaderProgramInfoLog));
        ASSERT(false, _L("glLinkProgram failed"));

        // Handle the error in an appropriate way such as displaying a message or writing to a log file.
        // In this simple program, we'll just leave
        //DEBUG_PRINT_ALWAYS(("Failed to link shader program:\n%s\n", shaderProgramInfoLog));
        free(shaderProgramInfoLog);
    }
    
    // Shader must be successfully linked before we can query uniform locations
    ReadShaderSamplersAndTextures( mShaderProgram, &mVertexShaderParameters );
    glUseProgram(0);
// For each of the shader stages, bind shaders and buffers
//    for( CPUTShaderParameters **pCur = mpShaderParametersList; *pCur; pCur++ ) // Bind textures and buffersfor each shader stage
    {
        BindTextures(        mVertexShaderParameters, pModel, meshIndex );
        //BindTextures(        **pCur, pModel, meshIndex );
        //BindBuffers(         **pCur, pModel, meshIndex );
        BindUAVs(            mVertexShaderParameters, pModel, meshIndex );
        BindConstantBuffers( mVertexShaderParameters, pModel, meshIndex );
    }
    
    return result;
}
//-----------------------------------------------------------------------------
CPUTResult CPUTAssetSet::LoadAssetSet(std::string name, int numSystemMaterials, std::string *pSystemMaterialNames)
{
    CPUTResult result = CPUT_SUCCESS;

    // if not found, load the set file
    CPUTConfigFile ConfigFile;
    result = ConfigFile.LoadFile(name);
    if( !CPUTSUCCESS(result) )
    {
        return result;
    }
    // ASSERT( CPUTSUCCESS(result), "Failed loading set file '" + name + "'." );

    mAssetCount = ConfigFile.BlockCount() + 1; // Add one for the implied root node
    mppAssetList = new CPUTRenderNode*[mAssetCount];
    mppAssetList[0] = mpRootNode;
    mpRootNode->AddRef();

    CPUTAssetLibrary *pAssetLibrary = (CPUTAssetLibrary*)CPUTAssetLibrary::GetAssetLibrary();

	CPUTAnimation *pDefaultAnimation = NULL;
    for(UINT ii=0; ii<mAssetCount-1; ii++) // Note: -1 because we added one for the root node (we don't load it)
    {
        CPUTConfigBlock *pBlock = ConfigFile.GetBlock(ii);
		ASSERT(pBlock != NULL, "Cannot find block");

        if( !pBlock )
        {
            return CPUT_ERROR_PARAMETER_BLOCK_NOT_FOUND;
        }

        std::string nodeType = pBlock->GetValueByName("type")->ValueAsString();
        CPUTRenderNode *pParentNode = NULL;

        // TODO: use Get*() instead of Load*() ?

        std::string name = pBlock->GetValueByName("name")->ValueAsString();

        int parentIndex;
        CPUTRenderNode *pNode = NULL;
        if(0==nodeType.compare("null"))
        {
            pNode  = pNode = CPUTNullNode::Create();
            result = ((CPUTNullNode*)pNode)->LoadNullNode(pBlock, &parentIndex);
            pParentNode = mppAssetList[parentIndex+1];
            std::string &parentPrefix = pParentNode->GetPrefix();
            std::string prefix = parentPrefix;
            prefix.append(".");
            prefix.append(name);
            pNode->SetPrefix(prefix);
      //      pNode->SetPrefix( parentPrefix + "." + name );
            pAssetLibrary->AddNullNode("", parentPrefix + name, "", (CPUTNullNode*)pNode);
            // Add this null's name to our prefix
            // Append this null's name to our parent's prefix
            pNode->SetParent( pParentNode );
            pParentNode->AddChild( pNode );
        }
        else if(0==nodeType.compare("model"))
        {
            CPUTConfigEntry *pValue = pBlock->GetValueByName( "instance" );
            CPUTModel *pModel = CPUTModel::Create();
            if( pValue == &CPUTConfigEntry::sNullConfigValue )
            {
                // Not found.  So, not an instance.
                pModel->LoadModel(pBlock, &parentIndex, NULL, numSystemMaterials, pSystemMaterialNames);
            }
            else
            {
                int instance = pValue->ValueAsInt();

                // If the current node is an instance of itself then it has no "master" model
                if(ii == instance)
                {
                    // TODO: create instance model object
                    pModel->LoadModel(pBlock, &parentIndex, NULL, numSystemMaterials, pSystemMaterialNames);
                }
                else
                {
                    pModel->LoadModel(pBlock, &parentIndex, (CPUTModel*)mppAssetList[instance+1], numSystemMaterials, pSystemMaterialNames);
                }
            }
            pParentNode = mppAssetList[parentIndex+1];
            pModel->SetParent( pParentNode );
            pParentNode->AddChild( pModel );
            std::string &parentPrefix = pParentNode->GetPrefix();
            pModel->SetPrefix( parentPrefix );
            pAssetLibrary->AddModel(name, parentPrefix, "", pModel);

            pModel->UpdateBoundsWorldSpace();

#ifdef  SUPPORT_DRAWING_BOUNDING_BOXES
            // Create a mesh for rendering the bounding box
            // TODO: There is definitely a better way to do this.  But, want to see the bounding boxes!
            pModel->CreateBoundingBoxMesh();
#endif
            pNode = pModel;
        }
        else if(0==nodeType.compare("light"))
        {
            pNode = CPUTLight::Create();
            ((CPUTLight*)pNode)->LoadLight(pBlock, &parentIndex);
            pParentNode = mppAssetList[parentIndex+1]; // +1 because we added a root node to the start
            pNode->SetParent( pParentNode );
            pParentNode->AddChild( pNode );
            std::string &parentPrefix = pParentNode->GetPrefix();
            pNode->SetPrefix( parentPrefix );
            pAssetLibrary->AddLight(name, parentPrefix, "", (CPUTLight*)pNode);
        }
        else if(0==nodeType.compare("camera"))
        {
            pNode = CPUTCamera::Create(CPUT_PERSPECTIVE);
            ((CPUTCamera*)pNode)->LoadCamera(pBlock, &parentIndex);
            pParentNode = mppAssetList[parentIndex+1]; // +1 because we added a root node to the start
            pNode->SetParent( pParentNode );
            pParentNode->AddChild( pNode );
            std::string &parentPrefix = pParentNode->GetPrefix();
            pNode->SetPrefix( parentPrefix );
            pAssetLibrary->AddCamera(name, parentPrefix, "", (CPUTCamera*)pNode);
            if( !mpFirstCamera ) { mpFirstCamera = (CPUTCamera*)pNode; mpFirstCamera->AddRef();}
            ++mCameraCount;
        }
        else
        {
            ASSERT(0,"Unsupported node type '" + nodeType + "'.");
        }

        CPUTConfigEntry *pAnimValue = pBlock->GetValueByName( "Animation0" );
		//int autoPlayAnimationIndex = pBlock->GetValueByName( "DefaultAnimation" );
        for(int i = 1; pAnimValue != &CPUTConfigEntry::sNullConfigValue; ++i )
        {
			CPUTAnimation *pCurrentAnimation = pAssetLibrary->GetAnimation(pAnimValue->ValueAsString());
			
            //First Animation will be default for now
			if(pCurrentAnimation != NULL && i == 1 /*autoPlayAnimationIndex*/)
			{
				pDefaultAnimation = pCurrentAnimation ;
                pCurrentAnimation->AddRef();
			}

            std::stringstream animName;
            animName << "Animation" << i;
            pAnimValue = pBlock->GetValueByName(animName.str());
            pNode->mAnimationList.push_back(pCurrentAnimation);
        }

        // Add the node to our asset list (i.e., the linear list, not the hierarchical)
        mppAssetList[ii+1] = pNode;
        // Don't AddRef.Creating it set the refcount to 1.  We add it to the list, and then we're done with it.
        // Net effect is 0 (+1 to add to list, and -1 because we're done with it)
        // pNode->AddRef();
    }

    //Set the default animation to the current Asset Set
	if(pDefaultAnimation != NULL && mAssetCount > 1)
	{
        //Apply Animation to root of the setfile
		mppAssetList[1]->SetAnimation(pDefaultAnimation);
	}
	SAFE_RELEASE(pDefaultAnimation);
    return result;
}
bool CPUTGUIElement::Load(CPUTConfigBlock* pBlock, int* pParent)
{
    CPUTConfigEntry* pEntry = NULL;

    pEntry = pBlock->GetValueByName(NAME);
    if (pEntry->IsValid())
        mName= pEntry->ValueAsString();

    pEntry = pBlock->GetValueByName(PARENT);
    if(pEntry->IsValid())
        *pParent = pEntry->ValueAsInt();

    pEntry = pBlock->GetValueByName(WIDTH);
    if(pEntry->IsValid())
        mWidth = pEntry->ValueAsInt();

    pEntry = pBlock->GetValueByName(HEIGHT);
    if(pEntry->IsValid())
        mHeight = pEntry->ValueAsInt();
    
    pEntry = pBlock->GetValueByName(POS_X);
    if(pEntry->IsValid())
        mRelX = pEntry->ValueAsInt();

    pEntry = pBlock->GetValueByName(POS_Y);
    if(pEntry->IsValid())
        mRelY = pEntry->ValueAsInt();
    
    pEntry = pBlock->GetValueByName(PARENT_RELATIVE);
    if(pEntry->IsValid())
        mParentRelative = pEntry->ValueAsBool();

    pEntry = pBlock->GetValueByName(VISIBLE);
    if(pEntry->IsValid())
        mVisible = pEntry->ValueAsBool();

    pEntry = pBlock->GetValueByName(FOREGROUND_COLOR);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsFloatArray((float*)&mForegroundColor, 4);
        mForegroundHighlightColor = mForegroundColor;
    }

    pEntry = pBlock->GetValueByName(BACKGROUND_COLOR);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsFloatArray((float*)&mBackgroundColor, 4);
        mBackgroundHighlightColor = mBackgroundColor;
    }
    pEntry = pBlock->GetValueByName(FOREGROUND_HIGHLIGHT_COLOR);
    if(pEntry->IsValid())
        pEntry->ValueAsFloatArray((float*)&mForegroundHighlightColor, 4);

    pEntry = pBlock->GetValueByName(BACKGROUND_HIGHLIGHT_COLOR);
    if(pEntry->IsValid())
        pEntry->ValueAsFloatArray((float*)&mBackgroundHighlightColor, 4);

    pEntry = pBlock->GetValueByName(MATERIAL);
    if(pEntry->IsValid())
    {
        cString materialName = pEntry->ValueAsString();
        mpSprite = CPUTSprite::CreateSprite(0.0, 0.0, 1.0, 1.0, materialName);
    }

    pEntry = pBlock->GetValueByName(TEXT_MATERIAL);
    if(pEntry->IsValid())
    {
        cString materialName = pEntry->ValueAsString();
        mpTextMaterial = CPUTAssetLibrary::GetAssetLibrary()->GetMaterial(materialName);
    }
    const cString FONT = _L("font");
    pEntry = pBlock->GetValueByName(FONT);
    if(pEntry->IsValid())
    {
        cString fontName;
        pEntry->ValueAsString(&fontName);
        mpFont = (CPUTFont*)CPUTAssetLibrary::GetAssetLibrary()->GetFontByName(fontName);
        if(mpFont == NULL)
        {
            DEBUG_PRINT(_L("Failed to load font: %s"), fontName.c_str());
        }
    }
    pEntry = pBlock->GetValueByName(TEXT);
    cString string;
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&string);
        SetText(string);
    }   

    pEntry = pBlock->GetValueByName(HIGHLIGHT);
    if(pEntry->IsValid())
    {
        mHighlighted = pEntry->ValueAsBool();
    }   

    pEntry = pBlock->GetValueByName(MOUSE_CLICK);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&mClick.key);
        mEventHandler = true;
    }
    pEntry = pBlock->GetValueByName(MOUSE_UP);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&mUp.key);
        mEventHandler = true;
    }
    pEntry = pBlock->GetValueByName(MOUSE_DOWN);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&mDown.key);
        mEventHandler = true;
    }
    pEntry = pBlock->GetValueByName(MOUSE_OUT);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&mOut.key);
        mEventHandler = true;
    }
    pEntry = pBlock->GetValueByName(MOUSE_IN);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&mIn.key);
        mEventHandler = true;
    }
    pEntry = pBlock->GetValueByName(HOTKEY);
    if(pEntry->IsValid())
    {
        cString key;
        pEntry->ValueAsString(&key);
        mHotkey = MapKey(key);
        mEventHandler = true;
    }
    pEntry = pBlock->GetValueByName(HOTKEY_UP);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&mKeyUp.key);
        mEventHandler = true;
    }
    pEntry = pBlock->GetValueByName(HOTKEY_DOWN);
    if(pEntry->IsValid())
    {
        pEntry->ValueAsString(&mKeyDown.key);
        mEventHandler = true;
    }

    mParentMatrix = float4x4Translation((float)mPosX, (float)mPosY, 0.0);
    return true;
}
//-----------------------------------------------------------------------------
CPUTResult CPUTMaterial::LoadMaterial(
    const cString   &fileName,
    const CPUTModel *pModel,
    int        meshIndex,
    const char     **pShaderMacros,
    int        systemMaterialCount,
    cString   *pSystemMaterialNames,
    int        externalCount,
    cString   *pExternalName,
    float4    *pExternals,
    int       *pExternalOffset,
    int       *pExternalSize
    ){
        CPUTResult result = CPUT_SUCCESS;

        mMaterialName = fileName;
        mMaterialNameHash = CPUTComputeHash( mMaterialName );

        // Open/parse the file
        CPUTConfigFile file;
        result = file.LoadFile(fileName);
        if(CPUTFAILED(result))
        {
            return result;
        }

        // Make a local copy of all the parameters
        CPUTConfigBlock *pBlock = file.GetBlock(0);
        ASSERT( pBlock, _L("Error getting parameter block") );
        if( !pBlock )
        {
            return CPUT_ERROR_PARAMETER_BLOCK_NOT_FOUND;
        }
        mConfigBlock = *pBlock;

        CPUTAssetLibrary *pAssetLibrary = (CPUTAssetLibrary*)CPUTAssetLibrary::GetAssetLibrary();


        mMaterialEffectCount = 0;
        if( mConfigBlock.GetValueByName( _L("MultiMaterial") )->ValueAsBool() )
        {
            // Count materials;
            for(;;)
            {
                CPUTConfigEntry *pValue = mConfigBlock.GetValueByName( _L("Material") + itoc(mMaterialEffectCount) );
                if( pValue->IsValid() )
                {
                    ++mMaterialEffectCount;
                } else
                {
                    break;
                }
            }
            ASSERT(mMaterialEffectCount != 0, _L("MultiMaterial specified, but no sub materials given.") );

            // Reserve space for "authored" materials plus system materials
            mpMaterialEffectNames = new cString[mMaterialEffectCount+systemMaterialCount];
            int ii;
            for( ii=0; ii<mMaterialEffectCount; ii++ )
            {
                CPUTConfigEntry *pValue = mConfigBlock.GetValueByName( _L("Material") + itoc(ii) );
                mpMaterialEffectNames[ii] = pAssetLibrary->GetMaterialEffectPath(pValue->ValueAsString(), false);
            }
        }
        else
        {
            mMaterialEffectCount = 1;
            mpMaterialEffectNames = new cString[mMaterialEffectCount+systemMaterialCount];
            mpMaterialEffectNames[0] = fileName;
        }


        CPUT_SHADER_MACRO *pFinalShaderMacros = (CPUT_SHADER_MACRO*)pShaderMacros;
        CPUT_SHADER_MACRO *pUserSpecifiedMacros = NULL;

        // The real material count includes the system material count
        mMaterialEffectCount += systemMaterialCount;
        for( int ii=0; ii<systemMaterialCount; ii++ )
        {
            // Read additional macros from .mtl file
            cString macroBlockName = _L("defines") + itoc(ii);
            CPUTConfigBlock *pMacrosBlock = file.GetBlockByName(macroBlockName);
            int numUserSpecifiedMacros = 0;
            if( pMacrosBlock )
            {
                ReadMacrosFromConfigBlock( pMacrosBlock, (CPUT_SHADER_MACRO*)pShaderMacros, &pUserSpecifiedMacros, &numUserSpecifiedMacros, &pFinalShaderMacros );
            } 

            // System materials "grow" from the end; the 1st system material is at the end of the list.
            mpMaterialEffectNames[mMaterialEffectCount-systemMaterialCount+ii] = pSystemMaterialNames[ii];

            for( int kk=0; kk<numUserSpecifiedMacros; kk++ )
            {
                // ReadMacrosFromConfigBlock allocates memory (ws2s does).  Delete it here.
                SAFE_DELETE(pUserSpecifiedMacros[kk].Name);
                SAFE_DELETE(pUserSpecifiedMacros[kk].Definition);
            }
            SAFE_DELETE_ARRAY( pFinalShaderMacros );
            SAFE_DELETE_ARRAY( pUserSpecifiedMacros );
        }
        mpMaterialEffects = new CPUTMaterialEffect*[mMaterialEffectCount+1];
        for( int ii=0; ii<mMaterialEffectCount; ii++ )
        {
            mpMaterialEffects[ii] = pAssetLibrary->GetMaterialEffect( mpMaterialEffectNames[ii], true, pModel, meshIndex,(const char**)pFinalShaderMacros );
        }
        mpMaterialEffects[mMaterialEffectCount] = NULL;

        return result; // This material is a multi-material, so we're done.
}