//-----------------------------------------------------------------------------
void CPUTAssetLibrary::AddAsset(const cString &name, void *pAsset, CPUTAssetListEntry **pHead)
{
    // convert string to lowercase
    cString lowercaseName = name;
    std::transform(lowercaseName.begin(), lowercaseName.end(), lowercaseName.begin(), ::tolower);

    // Do we already have one by this name?
    CPUTAssetListEntry *pTail = *pHead;
    // TODO:  Save explicit tail pointer instead of iterating to find the null.
    for( CPUTAssetListEntry *pCur=*pHead; NULL!=pCur; pCur=pCur->pNext )
    {
        // Assert that we haven't added one with this name
        ASSERT( 0 != _wcsicmp( pCur->name.data(), name.data() ), _L("Warning: asset ")+name+_L(" already exists") );
        pTail = pCur;
    }
    CPUTAssetListEntry **pDest = pTail ? &pTail->pNext : pHead;
    *pDest = new CPUTAssetListEntry();
    (*pDest)->hash = CPUTComputeHash(name);
    (*pDest)->name = name;
    (*pDest)->pData = pAsset;
    (*pDest)->pNext = NULL;

    // TODO: Our assets are not yet all derived from CPUTRenderNode.
    // TODO: For now, rely on caller performing the AddRef() as it knows the assets type.
    ((CPUTRefCount*)pAsset)->AddRef();
}
void CPUTAssetLibrary::AddAsset(
	const std::string &name, 
	const std::string &prefixDecoration, 
	const std::string &suffixDecoration, 
	T* pAsset, 
	std::vector<CPUTAsset<T>>& pHead) {

#ifdef DEBUG
	// Do we already have one by this name?
	auto existingAsset = std::find_if(std::begin(pHead), std::end(pHead), [&name](CPUTAsset<T> const &currentAsset) {
		return (currentAsset.name == name);
	});

	if (existingAsset != std::end(pHead))  {
		DEBUG_PRINT("WARNING: Adding asset with duplicate name: %s\n", name.c_str());
	}
#endif

	std::string fileName = CPUTFileSystem::basename(name);

	CPUTAsset<T> newAsset;
	newAsset.hash = CPUTComputeHash(prefixDecoration + name + suffixDecoration);
	newAsset.name = prefixDecoration + name + suffixDecoration;
	newAsset.pData = pAsset;
	newAsset.fileName = fileName;

	((CPUTRefCount*)newAsset.pData)->AddRef();
	pHead.push_back(newAsset);
}
//-----------------------------------------------------------------------------
CPUTMaterial *CPUTMaterialDX11::CloneMaterial(const cString &fileName, const CPUTModel *pModel, int meshIndex)
{
    CPUTMaterialDX11 *pMaterial = new CPUTMaterialDX11();

    // TODO: move texture to base class.  All APIs have textures.
    pMaterial->mpPixelShader    = mpPixelShader;    if(mpPixelShader)    mpPixelShader->AddRef();   
    pMaterial->mpComputeShader  = mpComputeShader;  if(mpComputeShader)  mpComputeShader->AddRef();
    pMaterial->mpVertexShader   = mpVertexShader;   if(mpVertexShader)   mpVertexShader->AddRef();
    pMaterial->mpGeometryShader = mpGeometryShader; if(mpGeometryShader) mpGeometryShader->AddRef();
    pMaterial->mpHullShader     = mpHullShader;     if(mpHullShader)     mpHullShader->AddRef();
    pMaterial->mpDomainShader   = mpDomainShader;   if(mpDomainShader)   mpDomainShader->AddRef();

    mPixelShaderParameters.CloneShaderParameters(    &pMaterial->mPixelShaderParameters );
    mComputeShaderParameters.CloneShaderParameters(  &pMaterial->mComputeShaderParameters );
    mVertexShaderParameters.CloneShaderParameters(   &pMaterial->mVertexShaderParameters );
    mGeometryShaderParameters.CloneShaderParameters( &pMaterial->mGeometryShaderParameters );
    mHullShaderParameters.CloneShaderParameters(     &pMaterial->mHullShaderParameters );
    mDomainShaderParameters.CloneShaderParameters(   &pMaterial->mDomainShaderParameters );

    pMaterial->mpShaderParametersList[0] =  &pMaterial->mPixelShaderParameters,
    pMaterial->mpShaderParametersList[1] =  &pMaterial->mComputeShaderParameters,
    pMaterial->mpShaderParametersList[2] =  &pMaterial->mVertexShaderParameters,
    pMaterial->mpShaderParametersList[3] =  &pMaterial->mGeometryShaderParameters,
    pMaterial->mpShaderParametersList[4] =  &pMaterial->mHullShaderParameters,
    pMaterial->mpShaderParametersList[5] =  &pMaterial->mDomainShaderParameters,
    pMaterial->mpShaderParametersList[6] =  NULL;

    pMaterial->mpRenderStateBlock = mpRenderStateBlock; if( mpRenderStateBlock ) mpRenderStateBlock->AddRef();

    pMaterial->mMaterialName      = mMaterialName + ptoc(pModel) + itoc(meshIndex);
    pMaterial->mMaterialNameHash  = CPUTComputeHash(pMaterial->mMaterialName);
    pMaterial->mConfigBlock       = mConfigBlock;
    pMaterial->mBufferCount       = mBufferCount;

    // For each of the shader stages, bind shaders and buffers
    for( CPUTShaderParameters **pCur = pMaterial->mpShaderParametersList; *pCur; pCur++ ) // Bind textures and buffersfor each shader stage
    {
        pMaterial->BindTextures(        **pCur, pModel, meshIndex );
        pMaterial->BindBuffers(         **pCur, pModel, meshIndex );
        pMaterial->BindUAVs(            **pCur, pModel, meshIndex );
        pMaterial->BindConstantBuffers( **pCur, pModel, meshIndex );
    }
    // Append this clone to our clone list
    if( mpMaterialNextClone )
    {
        // Already have a list, so add to the end of it.
        ((CPUTMaterialDX11*)mpMaterialLastClone)->mpMaterialNextClone = pMaterial;
    } else
    {
        // No list yet, so start it with this material.
        mpMaterialNextClone = pMaterial;
        mpMaterialLastClone = pMaterial;
    }
    pMaterial->mpModel    = pModel;
    pMaterial->mMeshIndex = meshIndex;
    return pMaterial;
}
// Find an asset in a specific library
// ** Does not Addref() returned items **
// Asset library doesn't care if we're using absolute paths for names or not, it
// just adds/finds/deletes the matching string literal.
//-----------------------------------------------------------------------------
void *CPUTAssetLibrary::FindAsset(const cString &name, CPUTAssetListEntry *pList, bool nameIsFullPathAndFilename)
{
    cString absolutePathAndFilename;
    CPUTOSServices *pServices = CPUTOSServices::GetOSServices();
    pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename ? name : (mAssetSetDirectoryName + name), &absolutePathAndFilename);
    absolutePathAndFilename = nameIsFullPathAndFilename ? name : absolutePathAndFilename;

    UINT hash = CPUTComputeHash( absolutePathAndFilename );
    while(NULL!=pList)
    {
        if( hash == pList->hash && (0 == _wcsicmp( absolutePathAndFilename.data(), pList->name.data() )) )
        {
            return (void*)pList->pData;
        }
        pList = pList->pNext;
    }
    return NULL;
}
// Find an asset in a specific library
// ** Does not Addref() returned items **
// Asset library doesn't care if we're using absolute paths for names or not, it
// just adds/finds/deletes the matching string literal.
//-----------------------------------------------------------------------------
void *CPUTAssetLibrary::FindAsset(
    const cString &name,
    CPUTAssetListEntry *pList,
    bool nameIsFullPathAndFilename,
    const char **pShaderMacros,
    const CPUTModel *pModel,
    int meshIndex
){
    cString absolutePathAndFilename;
    if( !nameIsFullPathAndFilename )
    {
        CPUTFileSystem::ResolveAbsolutePathAndFilename( mAssetSetDirectoryName + name, &absolutePathAndFilename);
    }
    else
    {
        absolutePathAndFilename = name;
    }
    UINT hash = CPUTComputeHash( absolutePathAndFilename );

    while(NULL!=pList)
    {
        cString szNameLower = pList->name;
        std::transform(szNameLower.begin(), szNameLower.end(), szNameLower.begin(), tolow);
		std::transform(absolutePathAndFilename.begin(), absolutePathAndFilename.end(), absolutePathAndFilename.begin(), tolow);
    
        if(    pModel    == pList->pModel
            && meshIndex == pList->meshIndex
            && hash      == pList->hash
//fixme
#ifdef UNICODE
            && (0 == _wcsicmp( absolutePathAndFilename.data(), pList->name.data() ))
#endif
            && ShaderDefinesMatch( (char**)pShaderMacros, pList->pShaderMacros )
        )
        {
            return (void*)pList->pData;
        }
        pList = pList->pNext;
    }
    return NULL;
}
T* CPUTAssetLibrary::FindAsset( const std::string &name, std::vector<CPUTAsset<T>> const& pList, bool nameIsFullPathAndFilename ) {
	std::string absolutePathAndFilename;
	if (!nameIsFullPathAndFilename)
	{
		CPUTFileSystem::ResolveAbsolutePathAndFilename(mAssetSetDirectoryName + name, &absolutePathAndFilename);
	}
	else
	{
		absolutePathAndFilename = name;
	}

	UINT hash = CPUTComputeHash(absolutePathAndFilename);

	// Use a inline lambda function for the asset lookup. Pull this out into a method if lookup occurs elsewhere
	auto foundAsset = std::find_if(std::begin(pList), std::end(pList), [&](CPUTAsset<T> const& item) {
		return ((hash == item.hash)
			&& (absolutePathAndFilename == item.name));
	});

	if (foundAsset != std::end(pList)) {
		return (*foundAsset).pData;
	}
	return nullptr;
}
//-----------------------------------------------------------------------------
CPUTResult CPUTMaterialDX11::LoadMaterial(const cString &fileName, const CPUTModel *pModel, int meshIndex)
{
    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
    mConfigBlock = *file.GetBlock(0);

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

    // TODO:  The following code is very repetitive.  Consider generalizing so we can call a function instead.
    // see if there are any pixel/vertex/geo shaders to load
    CPUTConfigEntry *pValue, *pEntryPointName, *pProfileName;
    pValue   = mConfigBlock.GetValueByName(_L("VertexShaderFile"));
    if( pValue->IsValid() )
    {
        pEntryPointName = mConfigBlock.GetValueByName(_L("VertexShaderMain"));
        pProfileName    = mConfigBlock.GetValueByName(_L("VertexShaderProfile"));
        pAssetLibrary->GetVertexShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpVertexShader );
        ReadShaderSamplersAndTextures( mpVertexShader->GetBlob(), &mVertexShaderParameters );
    }

    // load and store the pixel shader if it was specified
    pValue  = mConfigBlock.GetValueByName(_L("PixelShaderFile"));
    if( pValue->IsValid() )
    {
        pEntryPointName = mConfigBlock.GetValueByName(_L("PixelShaderMain"));
        pProfileName    = mConfigBlock.GetValueByName(_L("PixelShaderProfile"));
        pAssetLibrary->GetPixelShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpPixelShader);
        ReadShaderSamplersAndTextures( mpPixelShader->GetBlob(), &mPixelShaderParameters );
    }

    // load and store the compute shader if it was specified
    pValue = mConfigBlock.GetValueByName(_L("ComputeShaderFile"));
    if( pValue->IsValid() )
    {
        pEntryPointName = mConfigBlock.GetValueByName(_L("ComputeShaderMain"));
        pProfileName = mConfigBlock.GetValueByName(_L("ComputeShaderProfile"));
        pAssetLibrary->GetComputeShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpComputeShader);
        ReadShaderSamplersAndTextures( mpComputeShader->GetBlob(), &mComputeShaderParameters );
    }

    // load and store the geometry shader if it was specified
    pValue = mConfigBlock.GetValueByName(_L("GeometryShaderFile"));
    if( pValue->IsValid() )
    {
        pEntryPointName = mConfigBlock.GetValueByName(_L("GeometryShaderMain"));
        pProfileName = mConfigBlock.GetValueByName(_L("GeometryShaderProfile"));
        pAssetLibrary->GetGeometryShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpGeometryShader);
        ReadShaderSamplersAndTextures( mpGeometryShader->GetBlob(), &mGeometryShaderParameters );
    }

    // load and store the hull shader if it was specified
    pValue = mConfigBlock.GetValueByName(_L("HullShaderFile"));
    if( pValue->IsValid() )
    {
        pEntryPointName = mConfigBlock.GetValueByName(_L("HullShaderMain"));
        pProfileName = mConfigBlock.GetValueByName(_L("HullShaderProfile"));
        pAssetLibrary->GetHullShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpHullShader);
        ReadShaderSamplersAndTextures( mpHullShader->GetBlob(), &mHullShaderParameters );
    }

    // load and store the domain shader if it was specified
    pValue = mConfigBlock.GetValueByName(_L("DomainShaderFile"));
    if( pValue->IsValid() )
    {
        pEntryPointName = mConfigBlock.GetValueByName(_L("DomainShaderMain"));
        pProfileName = mConfigBlock.GetValueByName(_L("DomainShaderProfile"));
        pAssetLibrary->GetDomainShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpDomainShader);
        ReadShaderSamplersAndTextures( mpDomainShader->GetBlob(), &mDomainShaderParameters );
    }

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

    // For each of the shader stages, bind shaders and buffers
    for( CPUTShaderParameters **pCur = mpShaderParametersList; *pCur; pCur++ ) // Bind textures and buffersfor each shader stage
    {
        BindTextures(        **pCur, pModel, meshIndex );
        BindBuffers(         **pCur, pModel, meshIndex );
        BindUAVs(            **pCur, pModel, meshIndex );
        BindConstantBuffers( **pCur, pModel, meshIndex );
    }

    return result;
}
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;
}
//-----------------------------------------------------------------------------
void CPUTAssetLibrary::AddAsset(
    const cString       &name,
	const cString       &prefixDecoration,
	const cString       &suffixDecoration,
    void                *pAsset,
    CPUTAssetListEntry **pHead,
    CPUTAssetListEntry **pTail,
    const char         **pShaderMacros,
    const CPUTModel     *pModel,
    int                  meshIndex
){
    // convert string to lowercase
    cString lowercaseName = name;
    std::transform(lowercaseName.begin(), lowercaseName.end(), lowercaseName.begin(), tolow);

#ifdef DEBUG
    // Do we already have one by this name?
    for( CPUTAssetListEntry *pCur=*pHead; NULL!=pCur; pCur=pCur->pNext )
    {
        // Assert that we haven't added one with this name
        ASSERT( !((0 == _wcsicmp( pCur->name.data(), name.data() )) && (pCur->pModel == pModel) && (pCur->meshIndex == meshIndex) ), _L("Warning: asset ")+name+_L(" already exists") );
    }
#endif
	cString fileName;
//FIXME string
#ifdef UNICODE
	cString::size_type index = lowercaseName.rfind(L"/");
	if  (index == cString::npos) {
		index = lowercaseName.rfind(L"\\");
	}
	if  (index != cString::npos) {
		fileName = lowercaseName.substr(index + 1); // add one to skip the forward or backward slash
	}
#endif
    CPUTAssetListEntry **pNext = *pTail ? &(*pTail)->pNext : pHead;

    *pTail = new CPUTAssetListEntry(); // TODO: init via constructor
    if(!*pNext ) *pNext = *pTail;

    (*pTail)->hash       = CPUTComputeHash(prefixDecoration + name + suffixDecoration);
    (*pTail)->name       = prefixDecoration + name + suffixDecoration;
    (*pTail)->pData      = pAsset;
    (*pTail)->pModel     = pModel;
    (*pTail)->meshIndex  = meshIndex;
    (*pTail)->pNext      = NULL;
	(*pTail)->fileName   = fileName;

    // Copy the shader macros.  If two assets (shaders only?) have different macros, then they're different assets.
    // Fix me: add support for shader macros
 
    char **p1=(char**)pShaderMacros;
    int count = 0;
    if( p1 )
    {
        // Count the macros
        while( *p1 )
        {
            ++count;
            ++p1;
        }
        (*pTail)->pShaderMacros = new char *[count+2]; // Add 2 for NULL terminator (1 for macro's name and 1 for its value)
        p1 = (char**)pShaderMacros;
        char **p2 = (*pTail)->pShaderMacros;
        while( *p1 && *p2 )
        {
            size_t len = strlen(*p1);
            *p2 = new char[len+1]; // +1 for NULL terminator
#ifdef CPUT_OS_WINDOWS
            strncpy_s( *p2, len+1, *p1, len+1 );
#else
			strcpy(*p2, *p1);
#endif
            ++p1;
            ++p2;
        }
        // Set the macro's name and value to NULL
        *(p2++) = NULL;
        *p2 = NULL;
    } else
    {
        (*pTail)->pShaderMacros = NULL;
    }

    pTail = &(*pTail)->pNext;

    // TODO: Our assets are not yet all derived from CPUTRenderNode.
    // TODO: For now, rely on caller performing the AddRef() as it knows the assets type.
    ((CPUTRefCount*)pAsset)->AddRef();
}
//-----------------------------------------------------------------------------
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.
}