//----------------------------------------------------------------------------- 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 ¤tAsset) { 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. }