//-----------------------------------------------------------------------------
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()
//-----------------------------------------------------------------------------
CPUTResult CPUTRenderStateBlockDX11::LoadRenderStateBlock(const cString &fileName)
{
    // TODO: If already loaded, then Release() all the old members

    // use the fileName for now, maybe we'll add names later?
    mMaterialName = fileName;

    // Open/parse the file
    CPUTConfigFile file;
    CPUTResult result = file.LoadFile(fileName);
    ASSERT( !FAILED(result), _L("Failed loading file: '") + fileName + _L("'.") );

    // Note: We ignore "not found error" results for ReadProperties() calls.
    // These blocks are optional.
    UINT ii;
    for( ii=0; ii<8; ii++ )
    {
        wchar_t pBlockName[64];
        wsprintf( pBlockName, _L("RenderTargetBlendStateDX11_%d"), ii+1 );
        ReadProperties( file, cString(pBlockName), pRenderTargetBlendDescMap, &mStateDesc.BlendDesc.RenderTarget[ii] );
    }
    ReadProperties( file, _L("BlendStateDX11"),        pBlendDescMap,        &mStateDesc.BlendDesc );
    ReadProperties( file, _L("DepthStencilStateDX11"), pDepthStencilDescMap, &mStateDesc.DepthStencilDesc);
    ReadProperties( file, _L("rasterizerstateDX11"),   pRasterizerDescMap,   &mStateDesc.RasterizerDesc);

    mNumSamplers = 0;
    for( ii=0; ii<D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT; ii++ )
    {
        // TODO: Use sampler names from .fx file.  Already did this for texture names.
        // The challenge is that the renderstate file is independent from the material (and the shaders).
        // Another feature is that the artists don't name the samplers (in the CPUTSL source).  Though, arbitrary .fx files can.
        // TODO: Add sampler-state properties to CPUTSL source (e.g., filter modes).  Then, have ShaderGenerator output a .rs file.
        wchar_t pBlockName[64];
        wsprintf( pBlockName, _L("SamplerDX11_%d"), ii+1 );
        CPUTResult result = ReadProperties( file, cString(pBlockName), pSamplerDescMap, &mStateDesc.SamplerDesc[ii] );
        if( CPUT_SUCCESS != result )
        {
            break; // Reached last sampler spec
        }
        ++mNumSamplers;
    }
    CreateNativeResources();

    return CPUT_SUCCESS;
} // CPUTRenderStateBlockDX11::LoadRenderStateBlock()
//-----------------------------------------------------------------------------
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;
}
//-----------------------------------------------------------------------------
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;
}
//-----------------------------------------------------------------------------
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;
}
//-----------------------------------------------------------------------------
CPUTResult CPUTRenderStateBlockOGL::LoadRenderStateBlock(const cString &fileName)
{
    // TODO: If already loaded, then Release() all the old members

    // use the fileName for now, maybe we'll add names later?
    mMaterialName = fileName;

    // Open/parse the file
    CPUTConfigFile file;
    CPUTResult result = file.LoadFile(fileName);
    ASSERT( !FAILED(result), _L("Failed loading file: '") + fileName + _L("'.") );

    // Note: We ignore "not found error" results for ReadProperties() calls.
    // These blocks are optional.
//    for( UINT ii=0; ii<8; ii++ )
//    {
//        char pBlockName[64];
//#ifndef CPUT_OS_WINDOWS
//        sprintf( pBlockName, _L("RenderTargetBlendStateOGL_%d"), ii+1 );
//#else
//        sprintf( pBlockName, ("RenderTargetBlendStateOGL_%d"), ii+1 );
//#endif
//    }

//#ifndef CPUT_OS_WINDOWS
//    ReadProperties( file, "DepthStencilStateOGL", pDepthStencilDescMap, &mStateDesc.DepthStencilDesc);
//    ReadProperties( file, "RasterizerStateOGL",   pRasterizerDescMap,   &mStateDesc.RasterizerDesc);
//    ReadProperties( file, "RenderTargetBlendStateOGL",   pRenderTargetBlendDescMap,   &mStateDesc.RenderTargetBlendDesc);
//#else
    ReadProperties( file, _L("DepthStencilStateOGL"), pDepthStencilDescMap, &mStateDesc.DepthStencilDesc);
    ReadProperties( file, _L("RasterizerStateOGL"),   pRasterizerDescMap,   &mStateDesc.RasterizerDesc);
    ReadProperties( file, _L("RenderTargetBlendStateOGL"),   pRenderTargetBlendDescMap,   &mStateDesc.RenderTargetBlendDesc);
//#endif

    //
    // For each sampler we read, need to create an OGL sampler object
    //
#ifndef CPUT_FOR_OGLES2
    GL_CHECK(ES3_COMPAT(glGenSamplers(1, &mStateDesc.DefaultSamplerID)));
    GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.DefaultSamplerID, GL_TEXTURE_MIN_FILTER,   mStateDesc.DefaultSamplerDesc.MinFilter)));
    GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.DefaultSamplerID, GL_TEXTURE_MAG_FILTER,   mStateDesc.DefaultSamplerDesc.MagFilter)));
    GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.DefaultSamplerID, GL_TEXTURE_WRAP_S,       mStateDesc.DefaultSamplerDesc.AddressU)));
    GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.DefaultSamplerID, GL_TEXTURE_WRAP_T,       mStateDesc.DefaultSamplerDesc.AddressV)));
    GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.DefaultSamplerID, GL_TEXTURE_WRAP_R,       mStateDesc.DefaultSamplerDesc.AddressW)));
    GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.DefaultSamplerID, GL_TEXTURE_COMPARE_MODE, mStateDesc.DefaultSamplerDesc.ComparisonMode)));
    GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.DefaultSamplerID, GL_TEXTURE_COMPARE_FUNC, mStateDesc.DefaultSamplerDesc.ComparisonFunc)));
    GL_CHECK(ES3_COMPAT(glSamplerParameterf( mStateDesc.DefaultSamplerID, GL_TEXTURE_MIN_LOD,      mStateDesc.DefaultSamplerDesc.MinLOD)));
    GL_CHECK(ES3_COMPAT(glSamplerParameterf( mStateDesc.DefaultSamplerID, GL_TEXTURE_MAX_LOD,      mStateDesc.DefaultSamplerDesc.MaxLOD)));
#ifndef CPUT_FOR_OGLES
    GL_CHECK(glSamplerParameterf( mStateDesc.DefaultSamplerID, GL_TEXTURE_LOD_BIAS,     mStateDesc.DefaultSamplerDesc.MipLODBias));
    GL_CHECK(glSamplerParameterfv(mStateDesc.DefaultSamplerID, GL_TEXTURE_BORDER_COLOR, mStateDesc.DefaultSamplerDesc.BorderColor));
#endif
#else
#warning "Need to do something with samplers here"
#endif
        
    mNumSamplers = 0;
    for( UINT ii=0; ii<NUM_SAMPLERS_PER_RENDERSTATE; ii++ )
    {
        // TODO: Use sampler names from .fx file.  Already did this for texture names.
        // The challenge is that the renderstate file is independent from the material (and the shaders).
        // Another feature is that the artists don't name the samplers (in the CPUTSL source).  Though, arbitrary .fx files can.
        // TODO: Add sampler-state properties to CPUTSL source (e.g., filter modes).  Then, have ShaderGenerator output a .rs file.
        char pBlockName[64];
#ifndef CPUT_OS_WINDOWS
        sprintf( pBlockName, _L("SamplerOGL_%d"), ii+1 );
        CPUTResult result = ReadProperties( file, cString(pBlockName), pSamplerDescMap, &mStateDesc.SamplerDesc[ii] );
#else
        sprintf( pBlockName, ("SamplerOGL_%d"), ii+1 );
        CPUTResult result = ReadProperties( file, cString(s2ws(pBlockName)), pSamplerDescMap, &mStateDesc.SamplerDesc[ii] );
#endif

        if( CPUT_SUCCESS != result )
        {
            break; // Reached last sampler spec
        }

#ifndef CPUT_FOR_OGLES2
        //
        // For each sampler we read, need to create an OGL sampler object
        //
        GL_CHECK(ES3_COMPAT(glGenSamplers(1, &mStateDesc.SamplerIDs[ii])));
        GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.SamplerIDs[ii], GL_TEXTURE_MIN_FILTER,   mStateDesc.SamplerDesc[ii].MinFilter)));
        GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.SamplerIDs[ii], GL_TEXTURE_MAG_FILTER,   mStateDesc.SamplerDesc[ii].MagFilter)));
        GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.SamplerIDs[ii], GL_TEXTURE_WRAP_S,       mStateDesc.SamplerDesc[ii].AddressU)));
        GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.SamplerIDs[ii], GL_TEXTURE_WRAP_T,       mStateDesc.SamplerDesc[ii].AddressV)));
        GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.SamplerIDs[ii], GL_TEXTURE_WRAP_R,       mStateDesc.SamplerDesc[ii].AddressW)));
#ifndef CPUT_FOR_OGLES
        GL_CHECK(glSamplerParameterf( mStateDesc.SamplerIDs[ii], GL_TEXTURE_LOD_BIAS,     mStateDesc.SamplerDesc[ii].MipLODBias));
        GL_CHECK(glSamplerParameterfv(mStateDesc.SamplerIDs[ii], GL_TEXTURE_BORDER_COLOR, mStateDesc.SamplerDesc[ii].BorderColor));
#endif
        GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.SamplerIDs[ii], GL_TEXTURE_COMPARE_MODE, mStateDesc.SamplerDesc[ii].ComparisonMode)));
        GL_CHECK(ES3_COMPAT(glSamplerParameteri( mStateDesc.SamplerIDs[ii], GL_TEXTURE_COMPARE_FUNC, mStateDesc.SamplerDesc[ii].ComparisonFunc)));
        GL_CHECK(ES3_COMPAT(glSamplerParameterf( mStateDesc.SamplerIDs[ii], GL_TEXTURE_MIN_LOD,      mStateDesc.SamplerDesc[ii].MinLOD)));
        GL_CHECK(ES3_COMPAT(glSamplerParameterf( mStateDesc.SamplerIDs[ii], GL_TEXTURE_MAX_LOD,      mStateDesc.SamplerDesc[ii].MaxLOD)));
#else
#warning "Need to do something with samplers here"
#endif

        ++mNumSamplers;
    }
 //   CreateNativeResources();

    return CPUT_SUCCESS;
} // CPUTRenderStateBlockOGL::LoadRenderStateBlock()
//-----------------------------------------------------------------------------
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.
}