| /****************************************************************************** | |
| @File PVRTPFXParser.cpp | |
| @Title PVRTPFXParser | |
| @Version | |
| @Copyright Copyright (c) Imagination Technologies Limited. | |
| @Platform ANSI compatible | |
| @Description PFX file parser. | |
| ******************************************************************************/ | |
| /***************************************************************************** | |
| ** Includes | |
| ******************************************************************************/ | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include "PVRTGlobal.h" | |
| #include "PVRTContext.h" | |
| #include "PVRTMatrix.h" | |
| #include "PVRTFixedPoint.h" | |
| #include "PVRTMisc.h" | |
| #include "PVRTPFXParser.h" | |
| #include "PVRTResourceFile.h" | |
| #include "PVRTString.h" | |
| #include "PVRTMisc.h" // Used for POT functions | |
| /**************************************************************************** | |
| ** Constants | |
| ****************************************************************************/ | |
| const char* c_pszLinear = "LINEAR"; | |
| const char* c_pszNearest = "NEAREST"; | |
| const char* c_pszNone = "NONE"; | |
| const char* c_pszClamp = "CLAMP"; | |
| const char* c_pszRepeat = "REPEAT"; | |
| const char* c_pszCurrentView = "PFX_CURRENTVIEW"; | |
| const unsigned int CPVRTPFXParser::VIEWPORT_SIZE = 0xAAAA; | |
| const char* c_ppszFilters[eFilter_Size] = | |
| { | |
| c_pszNearest, // eFilter_Nearest | |
| c_pszLinear, // eFilter_Linear | |
| c_pszNone, // eFilter_None | |
| }; | |
| const char* c_ppszWraps[eWrap_Size] = | |
| { | |
| c_pszClamp, // eWrap_Clamp | |
| c_pszRepeat // eWrap_Repeat | |
| }; | |
| #define NEWLINE_TOKENS "\r\n" | |
| #define DELIM_TOKENS " \t" | |
| #define DEFAULT_EFFECT_NUM_TEX 100 | |
| #define DEFAULT_EFFECT_NUM_UNIFORM 100 | |
| #define DEFAULT_EFFECT_NUM_ATTRIB 100 | |
| /**************************************************************************** | |
| ** Data tables | |
| ****************************************************************************/ | |
| /**************************************************************************** | |
| ** CPVRTPFXParserReadContext Class | |
| ****************************************************************************/ | |
| class CPVRTPFXParserReadContext | |
| { | |
| public: | |
| char **ppszEffectFile; | |
| int *pnFileLineNumber; | |
| unsigned int nNumLines, nMaxLines; | |
| public: | |
| CPVRTPFXParserReadContext(); | |
| ~CPVRTPFXParserReadContext(); | |
| }; | |
| /*!*************************************************************************** | |
| @Function CPVRTPFXParserReadContext | |
| @Description Initialises values. | |
| *****************************************************************************/ | |
| CPVRTPFXParserReadContext::CPVRTPFXParserReadContext() | |
| { | |
| nMaxLines = 5000; | |
| nNumLines = 0; | |
| ppszEffectFile = new char*[nMaxLines]; | |
| pnFileLineNumber = new int[nMaxLines]; | |
| } | |
| /*!*************************************************************************** | |
| @Function ~CPVRTPFXParserReadContext | |
| @Description Frees allocated memory | |
| *****************************************************************************/ | |
| CPVRTPFXParserReadContext::~CPVRTPFXParserReadContext() | |
| { | |
| // free effect file | |
| for(unsigned int i = 0; i < nNumLines; i++) | |
| { | |
| FREE(ppszEffectFile[i]); | |
| } | |
| delete [] ppszEffectFile; | |
| delete [] pnFileLineNumber; | |
| } | |
| /*!*************************************************************************** | |
| @Function IgnoreWhitespace | |
| @Input pszString | |
| @Output pszString | |
| @Description Skips space, tab, new-line and return characters. | |
| *****************************************************************************/ | |
| static void IgnoreWhitespace(char **pszString) | |
| { | |
| while( *pszString[0] == '\t' || | |
| *pszString[0] == '\n' || | |
| *pszString[0] == '\r' || | |
| *pszString[0] == ' ' ) | |
| { | |
| (*pszString)++; | |
| } | |
| } | |
| /*!*************************************************************************** | |
| @Function ReadEOLToken | |
| @Input pToken | |
| @Output char* | |
| @Description Reads next strings to the end of the line and interperts as | |
| a token. | |
| *****************************************************************************/ | |
| static char* ReadEOLToken(char* pToken) | |
| { | |
| char* pReturn = NULL; | |
| char szDelim[2] = {'\n', 0}; // try newline | |
| pReturn = strtok(pToken, szDelim); | |
| if(pReturn == NULL) | |
| { | |
| szDelim[0] = '\r'; | |
| pReturn = strtok (pToken, szDelim); // try linefeed | |
| } | |
| return pReturn; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetSemanticDataFromString | |
| @Output pDataItem | |
| @Modified pszArgumentString | |
| @Input eType | |
| @Output pError error message | |
| @Return true if successful | |
| @Description Extracts the semantic data from the string and stores it | |
| in the output SPVRTSemanticDefaultData parameter. | |
| *****************************************************************************/ | |
| static bool GetSemanticDataFromString(SPVRTSemanticDefaultData *pDataItem, const char * const pszArgumentString, ESemanticDefaultDataType eType, CPVRTString *pError) | |
| { | |
| char *pszString = (char *)pszArgumentString; | |
| char *pszTmp; | |
| IgnoreWhitespace(&pszString); | |
| if(pszString[0] != '(') | |
| { | |
| *pError = CPVRTString("Missing '(' after ") + c_psSemanticDefaultDataTypeInfo[eType].pszName; | |
| return false; | |
| } | |
| pszString++; | |
| IgnoreWhitespace(&pszString); | |
| if(!strlen(pszString)) | |
| { | |
| *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments"); | |
| return false; | |
| } | |
| pszTmp = pszString; | |
| switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType) | |
| { | |
| case eFloating: | |
| pDataItem->pfData[0] = (float)strtod(pszString, &pszTmp); | |
| break; | |
| case eInteger: | |
| pDataItem->pnData[0] = (int)strtol(pszString, &pszTmp, 10); | |
| break; | |
| case eBoolean: | |
| if(strncmp(pszString, "true", 4) == 0) | |
| { | |
| pDataItem->pbData[0] = true; | |
| pszTmp = &pszString[4]; | |
| } | |
| else if(strncmp(pszString, "false", 5) == 0) | |
| { | |
| pDataItem->pbData[0] = false; | |
| pszTmp = &pszString[5]; | |
| } | |
| break; | |
| } | |
| if(pszString == pszTmp) | |
| { | |
| size_t n = strcspn(pszString, ",\t "); | |
| char *pszError = (char *)malloc(n + 1); | |
| strcpy(pszError, ""); | |
| strncat(pszError, pszString, n); | |
| *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | |
| FREE(pszError); | |
| return false; | |
| } | |
| pszString = pszTmp; | |
| IgnoreWhitespace(&pszString); | |
| for(unsigned int i = 1; i < c_psSemanticDefaultDataTypeInfo[eType].nNumberDataItems; i++) | |
| { | |
| if(!strlen(pszString)) | |
| { | |
| *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments"); | |
| return false; | |
| } | |
| if(pszString[0] != ',') | |
| { | |
| size_t n = strcspn(pszString, ",\t "); | |
| char *pszError = (char *)malloc(n + 1); | |
| strcpy(pszError, ""); | |
| strncat(pszError, pszString, n); | |
| *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | |
| FREE(pszError); | |
| return false; | |
| } | |
| pszString++; | |
| IgnoreWhitespace(&pszString); | |
| if(!strlen(pszString)) | |
| { | |
| *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments"); | |
| return false; | |
| } | |
| pszTmp = pszString; | |
| switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType) | |
| { | |
| case eFloating: | |
| pDataItem->pfData[i] = (float)strtod(pszString, &pszTmp); | |
| break; | |
| case eInteger: | |
| pDataItem->pnData[i] = (int)strtol(pszString, &pszTmp, 10); | |
| break; | |
| case eBoolean: | |
| if(strncmp(pszString, "true", 4) == 0) | |
| { | |
| pDataItem->pbData[i] = true; | |
| pszTmp = &pszString[4]; | |
| } | |
| else if(strncmp(pszString, "false", 5) == 0) | |
| { | |
| pDataItem->pbData[i] = false; | |
| pszTmp = &pszString[5]; | |
| } | |
| break; | |
| } | |
| if(pszString == pszTmp) | |
| { | |
| size_t n = strcspn(pszString, ",\t "); | |
| char *pszError = (char *)malloc(n + 1); | |
| strcpy(pszError, ""); | |
| strncat(pszError, pszString, n); | |
| *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | |
| FREE(pszError); | |
| return false; | |
| } | |
| pszString = pszTmp; | |
| IgnoreWhitespace(&pszString); | |
| } | |
| if(pszString[0] != ')') | |
| { | |
| size_t n = strcspn(pszString, "\t )"); | |
| char *pszError = (char *)malloc(n + 1); | |
| strcpy(pszError, ""); | |
| strncat(pszError, pszString, n); | |
| *pError = CPVRTString("'") + pszError + "' found when expecting ')' for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | |
| FREE(pszError); | |
| return false; | |
| } | |
| pszString++; | |
| IgnoreWhitespace(&pszString); | |
| if(strlen(pszString)) | |
| { | |
| *pError = CPVRTString("'") + pszString + "' unexpected after ')'"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ConcatenateLinesUntil | |
| @Output pszOut output text | |
| @Output nLine end line number | |
| @Input nLine start line number | |
| @Input ppszLines input text - one array element per line | |
| @Input nLimit number of lines input | |
| @Input pszEnd end string | |
| @Return true if successful | |
| @Description Outputs a block of text starting from nLine and ending | |
| when the string pszEnd is found. | |
| *****************************************************************************/ | |
| static bool ConcatenateLinesUntil(CPVRTString& Out, int &nLine, const char * const * const ppszLines, const unsigned int nLimit, const char * const pszEnd) | |
| { | |
| unsigned int i, j; | |
| size_t nLen; | |
| nLen = 0; | |
| for(i = nLine; i < nLimit; ++i) | |
| { | |
| if(strcmp(ppszLines[i], pszEnd) == 0) | |
| break; | |
| nLen += strlen(ppszLines[i]) + 1; | |
| } | |
| if(i == nLimit) | |
| { | |
| return false; | |
| } | |
| if(nLen) | |
| { | |
| ++nLen; | |
| Out.reserve(nLen); | |
| for(j = nLine; j < i; ++j) | |
| { | |
| Out.append(ppszLines[j]); | |
| Out.append("\n"); | |
| } | |
| } | |
| nLine = i; | |
| return true; | |
| } | |
| /**************************************************************************** | |
| ** SPVRTPFXParserEffect Struct | |
| ****************************************************************************/ | |
| SPVRTPFXParserEffect::SPVRTPFXParserEffect() : | |
| Uniforms(DEFAULT_EFFECT_NUM_UNIFORM), | |
| Attributes(DEFAULT_EFFECT_NUM_ATTRIB), | |
| Textures(DEFAULT_EFFECT_NUM_TEX) | |
| { | |
| } | |
| /**************************************************************************** | |
| ** SPVRTPFXRenderPass Class | |
| ****************************************************************************/ | |
| SPVRTPFXRenderPass::SPVRTPFXRenderPass() : | |
| eRenderPassType(eNULL_PASS), | |
| eViewType(eVIEW_NONE), | |
| uiFormatFlags(0), | |
| pEffect(NULL), | |
| pTexture(NULL) | |
| { | |
| } | |
| /**************************************************************************** | |
| ** SPVRTPFXParserShader Class | |
| ****************************************************************************/ | |
| SPVRTPFXParserShader::SPVRTPFXParserShader() | |
| : | |
| pszGLSLfile(NULL), | |
| pszGLSLBinaryFile(NULL), | |
| pszGLSLcode(NULL), | |
| pbGLSLBinary(NULL) | |
| { | |
| } | |
| SPVRTPFXParserShader::~SPVRTPFXParserShader() | |
| { | |
| FREE(pszGLSLfile); | |
| FREE(pszGLSLcode); | |
| FREE(pszGLSLBinaryFile); | |
| FREE(pbGLSLBinary); | |
| } | |
| SPVRTPFXParserShader::SPVRTPFXParserShader(const SPVRTPFXParserShader& rhs) | |
| { | |
| Copy(rhs); | |
| } | |
| SPVRTPFXParserShader& SPVRTPFXParserShader::operator=(const SPVRTPFXParserShader& rhs) | |
| { | |
| if(&rhs != this) | |
| Copy(rhs); | |
| return *this; | |
| } | |
| void SPVRTPFXParserShader::Copy(const SPVRTPFXParserShader& rhs) | |
| { | |
| Name = rhs.Name; | |
| PVRTPFXCreateStringCopy(&pszGLSLfile, rhs.pszGLSLfile); | |
| PVRTPFXCreateStringCopy(&pszGLSLBinaryFile, rhs.pszGLSLBinaryFile); | |
| PVRTPFXCreateStringCopy(&pszGLSLcode, rhs.pszGLSLcode); | |
| PVRTPFXCreateStringCopy(&pbGLSLBinary, rhs.pbGLSLBinary); | |
| bUseFileName = rhs.bUseFileName; | |
| nGLSLBinarySize = rhs.nGLSLBinarySize; | |
| nFirstLineNumber= rhs.nFirstLineNumber; | |
| nLastLineNumber = rhs.nLastLineNumber; | |
| } | |
| /**************************************************************************** | |
| ** SPVRTSemanticDefaultData Struct | |
| ****************************************************************************/ | |
| SPVRTSemanticDefaultData::SPVRTSemanticDefaultData() | |
| : | |
| eType(eDataTypeNone) | |
| { | |
| } | |
| SPVRTSemanticDefaultData::SPVRTSemanticDefaultData(const SPVRTSemanticDefaultData& rhs) | |
| { | |
| Copy(rhs); | |
| } | |
| SPVRTSemanticDefaultData& SPVRTSemanticDefaultData::operator=(const SPVRTSemanticDefaultData& rhs) | |
| { | |
| if(&rhs != this) | |
| Copy(rhs); | |
| return *this; | |
| } | |
| void SPVRTSemanticDefaultData::Copy(const SPVRTSemanticDefaultData& rhs) | |
| { | |
| memcpy(pfData, rhs.pfData, sizeof(pfData)); | |
| memcpy(pnData, rhs.pnData, sizeof(pnData)); | |
| memcpy(pbData, rhs.pbData, sizeof(pbData)); | |
| eType = rhs.eType; | |
| } | |
| /**************************************************************************** | |
| ** SPVRTPFXParserSemantic Struct | |
| ****************************************************************************/ | |
| SPVRTPFXParserSemantic::SPVRTPFXParserSemantic() | |
| : | |
| pszName(NULL), | |
| pszValue(NULL) | |
| { | |
| } | |
| SPVRTPFXParserSemantic::~SPVRTPFXParserSemantic() | |
| { | |
| FREE(pszName); | |
| FREE(pszValue); | |
| } | |
| SPVRTPFXParserSemantic::SPVRTPFXParserSemantic(const SPVRTPFXParserSemantic& rhs) | |
| { | |
| Copy(rhs); | |
| } | |
| SPVRTPFXParserSemantic& SPVRTPFXParserSemantic::operator=(const SPVRTPFXParserSemantic& rhs) | |
| { | |
| if(&rhs != this) | |
| Copy(rhs); | |
| return *this; | |
| } | |
| void SPVRTPFXParserSemantic::Copy(const SPVRTPFXParserSemantic& rhs) | |
| { | |
| PVRTPFXCreateStringCopy(&pszName, rhs.pszName); | |
| PVRTPFXCreateStringCopy(&pszValue, rhs.pszValue); | |
| nIdx = rhs.nIdx; | |
| sDefaultValue = rhs.sDefaultValue; | |
| } | |
| /**************************************************************************** | |
| ** CPVRTPFXParser Class | |
| ****************************************************************************/ | |
| /*!*************************************************************************** | |
| @Function CPVRTPFXParser | |
| @Description Sets initial values. | |
| *****************************************************************************/ | |
| CPVRTPFXParser::CPVRTPFXParser() | |
| { | |
| m_szFileName.assign(""); | |
| // NOTE: Temp hardcode viewport size | |
| m_uiViewportWidth = 640; | |
| m_uiViewportHeight = 480; | |
| } | |
| /*!*************************************************************************** | |
| @Function ~CPVRTPFXParser | |
| @Description Frees memory used. | |
| *****************************************************************************/ | |
| CPVRTPFXParser::~CPVRTPFXParser() | |
| { | |
| } | |
| /*!*************************************************************************** | |
| @Function Parse | |
| @Output pReturnError error string | |
| @Return bool true for success parsing file | |
| @Description Parses a loaded PFX file. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::Parse(CPVRTString * const pReturnError) | |
| { | |
| enum eCmd | |
| { | |
| eCmds_Header, | |
| eCmds_Texture, | |
| eCmds_Target, | |
| eCmds_Textures, | |
| eCmds_VertexShader, | |
| eCmds_FragmentShader, | |
| eCmds_Effect, | |
| eCmds_Size | |
| }; | |
| const CPVRTHash ParserCommands[] = | |
| { | |
| "[HEADER]", // eCmds_Header | |
| "[TEXTURE]", // eCmds_Texture | |
| "[TARGET]", // eCmds_Target | |
| "[TEXTURES]", // eCmds_Textures | |
| "[VERTEXSHADER]", // eCmds_VertexShader | |
| "[FRAGMENTSHADER]", // eCmds_FragmentShader | |
| "[EFFECT]", // eCmds_Effect | |
| }; | |
| PVRTCOMPILEASSERT(ParserCommands, sizeof(ParserCommands) / sizeof(ParserCommands[0]) == eCmds_Size); | |
| int nEndLine = 0; | |
| int nHeaderCounter = 0, nTexturesCounter = 0; | |
| unsigned int i,j,k; | |
| // Loop through the file | |
| for(unsigned int nLine=0; nLine < m_psContext->nNumLines; nLine++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[nLine]) | |
| continue; | |
| CPVRTHash Cmd(m_psContext->ppszEffectFile[nLine]); | |
| if(Cmd == ParserCommands[eCmds_Header]) | |
| { | |
| if(nHeaderCounter>0) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("[HEADER] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| if(GetEndTag("HEADER", nLine, &nEndLine)) | |
| { | |
| if(ParseHeader(nLine, nEndLine, pReturnError)) | |
| nHeaderCounter++; | |
| else | |
| return false; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing [/HEADER] tag after [HEADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| nLine = nEndLine; | |
| } | |
| else if(Cmd == ParserCommands[eCmds_Texture]) | |
| { | |
| if(GetEndTag("TEXTURE", nLine, &nEndLine)) | |
| { | |
| if(!ParseTexture(nLine, nEndLine, pReturnError)) | |
| return false; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing [/TEXTURE] tag after [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| nLine = nEndLine; | |
| } | |
| else if(Cmd == ParserCommands[eCmds_Target]) | |
| { | |
| if(GetEndTag("TARGET", nLine, &nEndLine)) | |
| { | |
| if(!ParseTarget(nLine, nEndLine, pReturnError)) | |
| return false; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing [/TARGET] tag after [TARGET] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| nLine = nEndLine; | |
| } | |
| else if(Cmd == ParserCommands[eCmds_Textures]) | |
| { | |
| if(nTexturesCounter>0) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("[TEXTURES] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| if(GetEndTag("TEXTURES", nLine, &nEndLine)) | |
| { | |
| if(ParseTextures(nLine, nEndLine, pReturnError)) | |
| nTexturesCounter++; | |
| else | |
| return false; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing [/TEXTURES] tag after [TEXTURES] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| nLine = nEndLine; | |
| } | |
| else if(Cmd == ParserCommands[eCmds_VertexShader]) | |
| { | |
| if(GetEndTag("VERTEXSHADER", nLine, &nEndLine)) | |
| { | |
| SPVRTPFXParserShader VertexShader; | |
| if(ParseShader(nLine, nEndLine, pReturnError, VertexShader, "VERTEXSHADER")) | |
| m_psVertexShader.Append(VertexShader); | |
| else | |
| return false; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing [/VERTEXSHADER] tag after [VERTEXSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| nLine = nEndLine; | |
| } | |
| else if(Cmd == ParserCommands[eCmds_FragmentShader]) | |
| { | |
| if(GetEndTag("FRAGMENTSHADER", nLine, &nEndLine)) | |
| { | |
| SPVRTPFXParserShader FragShader; | |
| if(ParseShader(nLine, nEndLine, pReturnError, FragShader, "FRAGMENTSHADER")) | |
| m_psFragmentShader.Append(FragShader); | |
| else | |
| return false; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing [/FRAGMENTSHADER] tag after [FRAGMENTSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| nLine = nEndLine; | |
| } | |
| else if(Cmd == ParserCommands[eCmds_Effect]) | |
| { | |
| if(GetEndTag("EFFECT", nLine, &nEndLine)) | |
| { | |
| SPVRTPFXParserEffect Effect; | |
| if(ParseEffect(Effect, nLine, nEndLine, pReturnError)) | |
| m_psEffect.Append(Effect); | |
| else | |
| return false; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing [/EFFECT] tag after [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| nLine = nEndLine; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("'%s' unexpected on line %d\n", m_psContext->ppszEffectFile[nLine], m_psContext->pnFileLineNumber[nLine]); | |
| return false; | |
| } | |
| } | |
| if(m_psEffect.GetSize() < 1) | |
| { | |
| *pReturnError = CPVRTString("No [EFFECT] found. PFX file must have at least one defined.\n"); | |
| return false; | |
| } | |
| if(m_psFragmentShader.GetSize() < 1) | |
| { | |
| *pReturnError = CPVRTString("No [FRAGMENTSHADER] found. PFX file must have at least one defined.\n");; | |
| return false; | |
| } | |
| if(m_psVertexShader.GetSize() < 1) | |
| { | |
| *pReturnError = CPVRTString("No [VERTEXSHADER] found. PFX file must have at least one defined.\n"); | |
| return false; | |
| } | |
| // Loop Effects | |
| for(i = 0; i < m_psEffect.GetSize(); ++i) | |
| { | |
| // Loop Textures in Effects | |
| for(j = 0; j < m_psEffect[i].Textures.GetSize(); ++j) | |
| { | |
| // Loop Textures in whole PFX | |
| unsigned int uiTexSize = m_psTexture.GetSize(); | |
| for(k = 0; k < uiTexSize; ++k) | |
| { | |
| if(m_psTexture[k]->Name == m_psEffect[i].Textures[j].Name) | |
| break; | |
| } | |
| // Texture mismatch. Report error. | |
| if(!uiTexSize || k == uiTexSize) | |
| { | |
| *pReturnError = "Error: TEXTURE '" + m_psEffect[i].Textures[j].Name.String() + "' is not defined in [TEXTURES].\n"; | |
| return false; | |
| } | |
| } | |
| } | |
| DetermineRenderPassDependencies(pReturnError); | |
| if(pReturnError->compare("")) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseFromMemory | |
| @Input pszScript PFX script | |
| @Output pReturnError error string | |
| @Return EPVRTError PVR_SUCCESS for success parsing file | |
| PVR_FAIL if file doesn't exist or is invalid | |
| @Description Parses a PFX script from memory. | |
| *****************************************************************************/ | |
| EPVRTError CPVRTPFXParser::ParseFromMemory(const char * const pszScript, CPVRTString * const pReturnError) | |
| { | |
| CPVRTPFXParserReadContext context; | |
| char pszLine[512]; | |
| const char *pszEnd, *pszCurr; | |
| int nLineCounter; | |
| unsigned int nLen; | |
| unsigned int nReduce; | |
| bool bDone; | |
| if(!pszScript) | |
| return PVR_FAIL; | |
| m_psContext = &context; | |
| // Find & process each line | |
| nLineCounter = 0; | |
| bDone = false; | |
| pszCurr = pszScript; | |
| while(!bDone) | |
| { | |
| nLineCounter++; | |
| while(*pszCurr == '\r') | |
| ++pszCurr; | |
| // Find length of line | |
| pszEnd = strchr(pszCurr, '\n'); | |
| if(pszEnd) | |
| { | |
| nLen = (unsigned int)(pszEnd - pszCurr); | |
| } | |
| else | |
| { | |
| nLen = (unsigned int)strlen(pszCurr); | |
| bDone = true; | |
| } | |
| nReduce = 0; // Tells how far to go back because of '\r'. | |
| while(nLen - nReduce > 0 && pszCurr[nLen - 1 - nReduce] == '\r') | |
| nReduce++; | |
| // Ensure pszLine will not be not overrun | |
| if(nLen+1-nReduce > sizeof(pszLine) / sizeof(*pszLine)) | |
| nLen = sizeof(pszLine) / sizeof(*pszLine) - 1 + nReduce; | |
| // Copy line into pszLine | |
| strncpy(pszLine, pszCurr, nLen - nReduce); | |
| pszLine[nLen - nReduce] = 0; | |
| pszCurr += nLen + 1; | |
| _ASSERT(strchr(pszLine, '\r') == 0); | |
| _ASSERT(strchr(pszLine, '\n') == 0); | |
| // Ignore comments | |
| char *tmp = strstr(pszLine, "//"); | |
| if(tmp != NULL) *tmp = '\0'; | |
| // Reduce whitespace to one character. | |
| ReduceWhitespace(pszLine); | |
| // Store the line, even if blank lines (to get correct errors from GLSL compiler). | |
| if(m_psContext->nNumLines < m_psContext->nMaxLines) | |
| { | |
| m_psContext->pnFileLineNumber[m_psContext->nNumLines] = nLineCounter; | |
| m_psContext->ppszEffectFile[m_psContext->nNumLines] = (char *)malloc((strlen(pszLine) + 1) * sizeof(char)); | |
| strcpy(m_psContext->ppszEffectFile[m_psContext->nNumLines], pszLine); | |
| m_psContext->nNumLines++; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Too many lines of text in file (maximum is %d)\n", m_psContext->nMaxLines); | |
| return PVR_FAIL; | |
| } | |
| } | |
| return Parse(pReturnError) ? PVR_SUCCESS : PVR_FAIL; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseFromFile | |
| @Input pszFileName PFX file name | |
| @Output pReturnError error string | |
| @Return EPVRTError PVR_SUCCESS for success parsing file | |
| PVR_FAIL if file doesn't exist or is invalid | |
| @Description Reads the PFX file and calls the parser. | |
| *****************************************************************************/ | |
| EPVRTError CPVRTPFXParser::ParseFromFile(const char * const pszFileName, CPVRTString * const pReturnError) | |
| { | |
| CPVRTResourceFile PfxFile(pszFileName); | |
| if (!PfxFile.IsOpen()) | |
| { | |
| *pReturnError = CPVRTString("Unable to open file ") + pszFileName; | |
| return PVR_FAIL; | |
| } | |
| CPVRTString PfxFileString; | |
| const char* pPfxData = (const char*) PfxFile.DataPtr(); | |
| // Is our shader resource file data null terminated? | |
| if(pPfxData[PfxFile.Size()-1] != '\0') | |
| { | |
| // If not create a temporary null-terminated string | |
| PfxFileString.assign(pPfxData, PfxFile.Size()); | |
| pPfxData = PfxFileString.c_str(); | |
| } | |
| m_szFileName.assign(pszFileName); | |
| return ParseFromMemory(pPfxData, pReturnError); | |
| } | |
| /*!*************************************************************************** | |
| @Function SetViewportSize | |
| @Input uiWidth New viewport width | |
| @Input uiHeight New viewport height | |
| @Return bool True on success | |
| @Description Allows the current viewport size to be set. This value | |
| is used for calculating relative texture resolutions | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::SetViewportSize(unsigned int uiWidth, unsigned int uiHeight) | |
| { | |
| if(uiWidth > 0 && uiHeight > 0) | |
| { | |
| m_uiViewportWidth = uiWidth; | |
| m_uiViewportHeight = uiHeight; | |
| return true; | |
| } | |
| else | |
| { | |
| return false; | |
| } | |
| } | |
| /*!*************************************************************************** | |
| @Function RetrieveRenderPassDependencies | |
| @Output aRequiredRenderPasses | |
| @Output aszActiveEffectStrings | |
| @Return bool | |
| @Description Returns a list of dependencies associated with the pass. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::RetrieveRenderPassDependencies(CPVRTArray<SPVRTPFXRenderPass*> &aRequiredRenderPasses, CPVRTArray<CPVRTStringHash> &aszActiveEffectStrings) | |
| { | |
| unsigned int ui(0), uj(0), uk(0), ul(0); | |
| const SPVRTPFXParserEffect* pTempEffect(NULL); | |
| if(aRequiredRenderPasses.GetSize() > 0) | |
| { | |
| /* aRequiredRenderPasses should be empty when it is passed in */ | |
| return false; | |
| } | |
| for(ui = 0; ui < (unsigned int)aszActiveEffectStrings.GetSize(); ++ui) | |
| { | |
| if(aszActiveEffectStrings[ui].String().empty()) | |
| { | |
| // Empty strings are not valid | |
| return false; | |
| } | |
| // Find the specified effect | |
| for(uj = 0, pTempEffect = NULL; uj < (unsigned int)m_psEffect.GetSize(); ++uj) | |
| { | |
| if(aszActiveEffectStrings[ui] == m_psEffect[uj].Name) | |
| { | |
| // Effect found | |
| pTempEffect = &m_psEffect[uj]; | |
| break; | |
| } | |
| } | |
| if(pTempEffect == NULL) | |
| { | |
| // Effect not found | |
| return false; | |
| } | |
| for(uj = 0; uj < m_renderPassSkipGraph.GetNumNodes(); ++uj) | |
| { | |
| if(m_renderPassSkipGraph[uj]->pEffect == pTempEffect) | |
| { | |
| m_renderPassSkipGraph.RetreiveSortedDependencyList(aRequiredRenderPasses, uj); | |
| return true; | |
| } | |
| } | |
| /* | |
| The effect wasn't a post-process. Check to see if it has any non-post-process dependencies, | |
| e.g. RENDER CAMERA textures. | |
| */ | |
| // Loop Effects | |
| for(uj = 0; uj < (unsigned int)m_psEffect.GetSize(); ++uj) | |
| { | |
| if(aszActiveEffectStrings[ui] != m_psEffect[uj].Name) | |
| continue; | |
| // Loop Textures in Effect | |
| for(uk = 0; uk < m_psEffect[uj].Textures.GetSize();++uk) | |
| { | |
| // Loop Render Passes for whole PFX | |
| for(ul = 0; ul < m_RenderPasses.GetSize(); ++ul) | |
| { | |
| // Check that the name of this render pass output texture matches a provided texture in an Effect | |
| if(m_RenderPasses[ul].pTexture->Name == m_psEffect[uj].Textures[uk].Name) | |
| aRequiredRenderPasses.Append(&m_RenderPasses[ul]); | |
| } | |
| } | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetEndTag | |
| @Input pszTagName tag name | |
| @Input nStartLine start line | |
| @Output pnEndLine line end tag found | |
| @Return true if tag found | |
| @Description Searches for end tag pszTagName from line nStartLine. | |
| Returns true and outputs the line number of the end tag if | |
| found, otherwise returning false. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::GetEndTag(const char* pszTagName, int nStartLine, int *pnEndLine) | |
| { | |
| char pszEndTag[100]; | |
| strcpy(pszEndTag, "[/"); | |
| strcat(pszEndTag, pszTagName); | |
| strcat(pszEndTag, "]"); | |
| for(unsigned int i = nStartLine; i < m_psContext->nNumLines; i++) | |
| { | |
| if(strcmp(pszEndTag, m_psContext->ppszEffectFile[i]) == 0) | |
| { | |
| *pnEndLine = i; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| /*!*************************************************************************** | |
| @Function ReduceWhitespace | |
| @Output line output text | |
| @Input line input text | |
| @Description Reduces all white space characters in the string to one | |
| blank space. | |
| *****************************************************************************/ | |
| void CPVRTPFXParser::ReduceWhitespace(char *line) | |
| { | |
| // convert tabs and newlines to ' ' | |
| char *tmp = strpbrk (line, "\t\n"); | |
| while(tmp != NULL) | |
| { | |
| *tmp = ' '; | |
| tmp = strpbrk (line, "\t\n"); | |
| } | |
| // remove all whitespace at start | |
| while(line[0] == ' ') | |
| { | |
| // move chars along to omit whitespace | |
| int counter = 0; | |
| do{ | |
| line[counter] = line[counter+1]; | |
| counter++; | |
| }while(line[counter] != '\0'); | |
| } | |
| // step through chars of line remove multiple whitespace | |
| for(int i=0; i < (int)strlen(line); i++) | |
| { | |
| // whitespace found | |
| if(line[i] == ' ') | |
| { | |
| // count number of whitespace chars | |
| int numWhiteChars = 0; | |
| while(line[i+1+numWhiteChars] == ' ') | |
| { | |
| numWhiteChars++; | |
| } | |
| // multiple whitespace chars found | |
| if(numWhiteChars>0) | |
| { | |
| // move chars along to omit whitespace | |
| int counter=1; | |
| while(line[i+counter] != '\0') | |
| { | |
| line[i+counter] = line[i+numWhiteChars+counter]; | |
| counter++; | |
| } | |
| } | |
| } | |
| } | |
| // If there is no string then do not remove terminating white symbols | |
| if(!strlen(line)) | |
| return; | |
| // remove all whitespace from end | |
| while(line[strlen(line)-1] == ' ') | |
| { | |
| // move chars along to omit whitespace | |
| line[strlen(line)-1] = '\0'; | |
| } | |
| } | |
| /*!*************************************************************************** | |
| @Function FindParameter | |
| @Output | |
| @Input | |
| @Description Finds the parameter after the specified delimiting character and | |
| returns the parameter as a string. An empty string is returned | |
| if a parameter cannot be found | |
| *****************************************************************************/ | |
| CPVRTString CPVRTPFXParser::FindParameter(char *aszSourceString, const CPVRTString ¶meterTag, const CPVRTString &delimiter) | |
| { | |
| CPVRTString returnString(""); | |
| char* aszTagStart = strstr(aszSourceString, parameterTag.c_str()); | |
| // Tag was found, so search for parameter | |
| if(aszTagStart) | |
| { | |
| char* aszDelimiterStart = strstr(aszTagStart, delimiter.c_str()); | |
| char* aszSpaceStart = strstr(aszTagStart, " "); | |
| // Delimiter found | |
| if(aszDelimiterStart && (!aszSpaceStart ||(aszDelimiterStart < aszSpaceStart))) | |
| { | |
| // Create a string from the delimiter to the next space | |
| size_t strCount(strcspn(aszDelimiterStart, " ")); | |
| aszDelimiterStart++; // Skip = | |
| returnString.assign(aszDelimiterStart, strCount-1); | |
| } | |
| } | |
| return returnString; | |
| } | |
| /*!*************************************************************************** | |
| @Function ReadStringToken | |
| @Input pszSource Parameter string to process | |
| @Output output Processed string | |
| @Output ErrorStr String containing errors | |
| @Return Returns true on success | |
| @Description Processes the null terminated char array as if it's a | |
| formatted string array. Quote marks are determined to be | |
| start and end of strings. If no quote marks are found the | |
| string is delimited by whitespace. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ReadStringToken(char* pszSource, CPVRTString& output, CPVRTString &ErrorStr, int i, const char* pCaller) | |
| { | |
| if(*pszSource == '\"') // Quote marks. Continue parsing until end mark or NULL | |
| { | |
| pszSource++; // Skip past first quote | |
| while(*pszSource != '\"') | |
| { | |
| if(*pszSource == '\0') | |
| { | |
| ErrorStr = PVRTStringFromFormattedStr("Incomplete argument in [%s] on line %d: %s\n", pCaller,m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| output.push_back(*pszSource); | |
| pszSource++; | |
| } | |
| pszSource++; // Skip past final quote. | |
| } | |
| else // No quotes. Read until space | |
| { | |
| pszSource = strtok(pszSource, DELIM_TOKENS NEWLINE_TOKENS); | |
| output = pszSource; | |
| pszSource += strlen(pszSource); | |
| } | |
| // Check that there's nothing left on this line | |
| pszSource = strtok(pszSource, NEWLINE_TOKENS); | |
| if(pszSource) | |
| { | |
| ErrorStr = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d: %s\n", pszSource, pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseHeader | |
| @Input nStartLine start line number | |
| @Input nEndLine end line number | |
| @Output pReturnError error string | |
| @Return bool true if parse is successful | |
| @Description Parses the HEADER section of the PFX file. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseHeader(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | |
| { | |
| enum eCmd | |
| { | |
| eCmds_Version, | |
| eCmds_Description, | |
| eCmds_Copyright, | |
| eCmds_Size | |
| }; | |
| const CPVRTHash HeaderCommands[] = | |
| { | |
| "VERSION", // eCmds_Version | |
| "DESCRIPTION", // eCmds_Description | |
| "COPYRIGHT", // eCmds_Copyright | |
| }; | |
| PVRTCOMPILEASSERT(HeaderCommands, sizeof(HeaderCommands) / sizeof(HeaderCommands[0]) == eCmds_Size); | |
| for(int i = nStartLine+1; i < nEndLine; i++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[i]) | |
| continue; | |
| char *str = strtok (m_psContext->ppszEffectFile[i]," "); | |
| if(str != NULL) | |
| { | |
| CPVRTHash Cmd(str); | |
| if(Cmd == HeaderCommands[eCmds_Version]) | |
| { | |
| str += (strlen(str)+1); | |
| m_sHeader.Version = str; | |
| } | |
| else if(Cmd == HeaderCommands[eCmds_Description]) | |
| { | |
| str += (strlen(str)+1); | |
| m_sHeader.Description = str; | |
| } | |
| else if(Cmd == HeaderCommands[eCmds_Copyright]) | |
| { | |
| str += (strlen(str)+1); | |
| m_sHeader.Copyright = str; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [HEADER] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [HEADER] on line %d : %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseGenericSurface | |
| @Input nStartLine start line number | |
| @Input nEndLine end line number | |
| @Output uiWrapS | |
| @Output uiWrapT | |
| @Output uiWrapR | |
| @Output uiMin | |
| @Output uiMag | |
| @Output uiMip | |
| @Output pReturnError error string | |
| @Return bool true if parse is successful | |
| @Description Parses generic data from TARGET and TEXTURE blocks. Namely | |
| wrapping and filter commands. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseGenericSurface(int nStartLine, int nEndLine, SPVRTPFXParserTexture& Params, CPVRTArray<CPVRTHash>& KnownCmds, | |
| const char* pCaller, CPVRTString * const pReturnError) | |
| { | |
| const unsigned int INVALID_TYPE = 0xAC1DBEEF; | |
| enum eCmd | |
| { | |
| eCmds_Min, | |
| eCmds_Mag, | |
| eCmds_Mip, | |
| eCmds_WrapS, | |
| eCmds_WrapT, | |
| eCmds_WrapR, | |
| eCmds_Filter, | |
| eCmds_Wrap, | |
| eCmds_Resolution, | |
| eCmds_Surface, | |
| eCmds_Size | |
| }; | |
| const CPVRTHash GenericSurfCommands[] = | |
| { | |
| "MINIFICATION", // eCmds_Min | |
| "MAGNIFICATION", // eCmds_Mag | |
| "MIPMAP", // eCmds_Mip | |
| "WRAP_S", // eCmds_WrapS | |
| "WRAP_T", // eCmds_WrapT | |
| "WRAP_R", // eCmds_WrapR | |
| "FILTER", // eCmds_Filter | |
| "WRAP", // eCmds_Wrap | |
| "RESOLUTION", // eCmds_Resolution | |
| "SURFACETYPE", // eCmds_Surface | |
| }; | |
| PVRTCOMPILEASSERT(GenericSurfCommands, sizeof(GenericSurfCommands) / sizeof(GenericSurfCommands[0]) == eCmds_Size); | |
| struct SSurfacePair | |
| { | |
| CPVRTHash Name; | |
| PVRTPixelType eType; | |
| unsigned int BufferType; | |
| }; | |
| const SSurfacePair SurfacePairs[] = | |
| { | |
| { "RGBA8888", OGL_RGBA_8888, PVRPFXTEX_COLOUR }, | |
| { "RGBA4444", OGL_RGBA_4444, PVRPFXTEX_COLOUR }, | |
| { "RGB888", OGL_RGB_888, PVRPFXTEX_COLOUR }, | |
| { "RGB565", OGL_RGB_565, PVRPFXTEX_COLOUR }, | |
| { "INTENSITY8", OGL_I_8, PVRPFXTEX_COLOUR }, | |
| { "DEPTH24", OGL_RGB_888, PVRPFXTEX_DEPTH }, | |
| { "DEPTH16", OGL_RGB_565, PVRPFXTEX_DEPTH }, | |
| { "DEPTH8", OGL_I_8, PVRPFXTEX_DEPTH }, | |
| }; | |
| const unsigned int uiNumSurfTypes = sizeof(SurfacePairs) / sizeof(SurfacePairs[0]); | |
| for(int i = nStartLine+1; i < nEndLine; i++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[i]) | |
| continue; | |
| // Need to make a copy so we can use strtok and not affect subsequent parsing | |
| size_t lineLen = strlen(m_psContext->ppszEffectFile[i]); | |
| char* pBlockCopy = new char[lineLen+1]; | |
| strcpy(pBlockCopy, m_psContext->ppszEffectFile[i]); | |
| char *str = strtok (pBlockCopy, NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!str) | |
| { | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| CPVRTHash Cmd(str); | |
| const char** ppFilters = NULL; | |
| bool bKnown = false; | |
| // --- Verbose filtering flags | |
| if(Cmd == GenericSurfCommands[eCmds_Min] || Cmd == GenericSurfCommands[eCmds_Mag] || Cmd == GenericSurfCommands[eCmds_Mip]) | |
| { | |
| ppFilters = c_ppszFilters; | |
| bKnown = true; | |
| } | |
| // --- Verbose wrapping flags | |
| else if(Cmd == GenericSurfCommands[eCmds_WrapS] || Cmd == GenericSurfCommands[eCmds_WrapT] || Cmd == GenericSurfCommands[eCmds_WrapR]) | |
| { | |
| ppFilters = c_ppszWraps; | |
| bKnown = true; | |
| } | |
| // --- Inline filtering flags | |
| else if(Cmd == GenericSurfCommands[eCmds_Filter]) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing FILTER arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| unsigned int* pFlags[3] = | |
| { | |
| &Params.nMin, | |
| &Params.nMag, | |
| &Params.nMIP, | |
| }; | |
| if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszFilters, eFilter_Size, pReturnError, i)) | |
| { | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| bKnown = true; | |
| } | |
| // --- Inline wrapping flags | |
| else if(Cmd == GenericSurfCommands[eCmds_Wrap]) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing WRAP arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| unsigned int* pFlags[3] = | |
| { | |
| &Params.nWrapS, | |
| &Params.nWrapT, | |
| &Params.nWrapR, | |
| }; | |
| if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszWraps, eWrap_Size, pReturnError, i)) | |
| { | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| bKnown = true; | |
| } | |
| // --- Resolution | |
| else if(Cmd == GenericSurfCommands[eCmds_Resolution]) | |
| { | |
| char* pszRemaining; | |
| unsigned int* uiVals[2] = { &Params.uiWidth, &Params.uiHeight }; | |
| // There should be precisely TWO arguments for resolution (width and height) | |
| for(unsigned int uiIndex = 0; uiIndex < 2; ++uiIndex) | |
| { | |
| pszRemaining = strtok(NULL, DELIM_TOKENS NEWLINE_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing RESOLUTION argument(s) (requires width AND height) in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| int val = atoi(pszRemaining); | |
| if( (val == 0 && *pszRemaining != '0') // Make sure they haven't explicitly set the value to be 0 as this might be a valid use-case. | |
| || (val < 0)) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Invalid RESOLUTION argument \"%s\" in [TEXTURE] on line %d\n", pszRemaining, m_psContext->pnFileLineNumber[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| *(uiVals[uiIndex]) = (unsigned int)val; | |
| } | |
| bKnown = true; | |
| } | |
| // --- Surface type | |
| else if(Cmd == GenericSurfCommands[eCmds_Surface]) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing SURFACETYPE arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| CPVRTHash hashType(pszRemaining); | |
| for(unsigned int uiIndex = 0; uiIndex < uiNumSurfTypes; ++uiIndex) | |
| { | |
| if(hashType == SurfacePairs[uiIndex].Name) | |
| { | |
| Params.uiFlags = SurfacePairs[uiIndex].eType | SurfacePairs[uiIndex].BufferType; | |
| break; | |
| } | |
| } | |
| bKnown = true; | |
| } | |
| // Valid Verbose command | |
| if(ppFilters) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| unsigned int Type = INVALID_TYPE; | |
| for(unsigned int uiIndex = 0; uiIndex < 3; ++uiIndex) | |
| { | |
| if(strcmp(pszRemaining, ppFilters[uiIndex]) == 0) | |
| { | |
| Type = uiIndex; // Yup, it's valid. | |
| break; | |
| } | |
| } | |
| // Tell the user it's invalid. | |
| if(Type == INVALID_TYPE) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d: %s\n", pszRemaining, pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| if(Cmd == GenericSurfCommands[eCmds_Min]) Params.nMin = Type; | |
| else if(Cmd == GenericSurfCommands[eCmds_Mag]) Params.nMag = Type; | |
| else if(Cmd == GenericSurfCommands[eCmds_Mip]) Params.nMIP = Type; | |
| else if(Cmd == GenericSurfCommands[eCmds_WrapR]) Params.nWrapR = Type; | |
| else if(Cmd == GenericSurfCommands[eCmds_WrapS]) Params.nWrapS = Type; | |
| else if(Cmd == GenericSurfCommands[eCmds_WrapT]) Params.nWrapT = Type; | |
| } | |
| if(bKnown) | |
| { | |
| KnownCmds.Append(Cmd); | |
| // Make sure nothing else exists on the line that hasn't been parsed. | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | |
| if(pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [%s] on line %d: %s\n", pszRemaining, pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| delete[] pBlockCopy; | |
| return false; | |
| } | |
| } | |
| delete [] pBlockCopy; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseTexture | |
| @Input nStartLine start line number | |
| @Input nEndLine end line number | |
| @Output pReturnError error string | |
| @Return bool true if parse is successful | |
| @Description Parses the TEXTURE section of the PFX file. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseTexture(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | |
| { | |
| enum eCmd | |
| { | |
| eCmds_Name, | |
| eCmds_Path, | |
| eCmds_View, | |
| eCmds_Camera, | |
| eCmds_Size | |
| }; | |
| const CPVRTHash TextureCmds[] = | |
| { | |
| "NAME", // eTextureCmds_Name | |
| "PATH", // eTextureCmds_Path | |
| "VIEW", // eTextureCmds_View | |
| "CAMERA", // eTextureCmds_Camera | |
| }; | |
| PVRTCOMPILEASSERT(TextureCmds, sizeof(TextureCmds) / sizeof(TextureCmds[0]) == eCmds_Size); | |
| SPVRTPFXParserTexture TexDesc; | |
| TexDesc.nMin = eFilter_Default; | |
| TexDesc.nMag = eFilter_Default; | |
| TexDesc.nMIP = eFilter_MipDefault; | |
| TexDesc.nWrapS = eWrap_Default; | |
| TexDesc.nWrapT = eWrap_Default; | |
| TexDesc.nWrapR = eWrap_Default; | |
| TexDesc.uiWidth = VIEWPORT_SIZE; | |
| TexDesc.uiHeight = VIEWPORT_SIZE; | |
| TexDesc.uiFlags = OGL_RGBA_8888 | PVRPFXTEX_COLOUR; | |
| CPVRTArray<CPVRTHash> KnownCmds; | |
| if(!ParseGenericSurface(nStartLine, nEndLine, TexDesc, KnownCmds, "TEXTURE", pReturnError)) | |
| return false; | |
| CPVRTString texName, filePath, viewName; | |
| for(int i = nStartLine+1; i < nEndLine; i++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[i]) | |
| continue; | |
| char *str = strtok (m_psContext->ppszEffectFile[i], NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!str) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| CPVRTHash texCmd(str); | |
| // --- Texture Name | |
| if(texCmd == TextureCmds[eCmds_Name]) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing NAME arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| texName = pszRemaining; | |
| } | |
| // --- Texture Path | |
| else if(texCmd == TextureCmds[eCmds_Path]) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing PATH arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| if(!ReadStringToken(pszRemaining, filePath, *pReturnError, i, "TEXTURE")) | |
| { | |
| return false; | |
| } | |
| } | |
| // --- View/Camera Name | |
| else if(texCmd == TextureCmds[eCmds_View] || texCmd == TextureCmds[eCmds_Camera]) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); // String component. Get the rest of the line. | |
| if(!pszRemaining || strlen(pszRemaining) == 0) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing VIEW argument in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| if(!ReadStringToken(pszRemaining, viewName, *pReturnError, i, "TEXTURE")) | |
| { | |
| return false; | |
| } | |
| } | |
| else if(KnownCmds.Contains(texCmd)) | |
| { | |
| // Remove from 'unknown' list. | |
| for(unsigned int uiIndex = 0; uiIndex < KnownCmds.GetSize(); ++uiIndex) | |
| { | |
| if(KnownCmds[uiIndex] == texCmd) | |
| { | |
| KnownCmds.Remove(uiIndex); | |
| break; | |
| } | |
| } | |
| continue; // This line has already been processed. | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURE] on line %d: %s\n", str, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | |
| if(pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [TEXTURE] on line %d: %s\n", pszRemaining, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| } | |
| if(texName.empty()) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("No NAME tag specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| if(!filePath.empty() && !viewName.empty()) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Both PATH and VIEW tags specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| if(filePath.empty() && viewName.empty()) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("No PATH or VIEW tag specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| bool bRTT = (viewName.empty() ? false : true); | |
| if(bRTT) | |
| { | |
| filePath = texName; // RTT doesn't have a physical file. | |
| } | |
| // Create a new texture and copy over the vals. | |
| SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture(); | |
| pTex->Name = CPVRTStringHash(texName); | |
| pTex->FileName = CPVRTStringHash(filePath); | |
| pTex->bRenderToTexture = bRTT; | |
| pTex->nMin = TexDesc.nMin; | |
| pTex->nMag = TexDesc.nMag; | |
| pTex->nMIP = TexDesc.nMIP; | |
| pTex->nWrapS = TexDesc.nWrapS; | |
| pTex->nWrapT = TexDesc.nWrapT; | |
| pTex->nWrapR = TexDesc.nWrapR; | |
| pTex->uiWidth = TexDesc.uiWidth; | |
| pTex->uiHeight = TexDesc.uiHeight; | |
| pTex->uiFlags = TexDesc.uiFlags; | |
| m_psTexture.Append(pTex); | |
| if(bRTT) | |
| { | |
| unsigned int uiPassIdx = m_RenderPasses.Append(); | |
| m_RenderPasses[uiPassIdx].SemanticName = texName; | |
| if(viewName == c_pszCurrentView) | |
| { | |
| m_RenderPasses[uiPassIdx].eViewType = eVIEW_CURRENT; | |
| } | |
| else | |
| { | |
| m_RenderPasses[uiPassIdx].eViewType = eVIEW_POD_CAMERA; | |
| m_RenderPasses[uiPassIdx].NodeName = viewName; | |
| } | |
| m_RenderPasses[uiPassIdx].eRenderPassType = eCAMERA_PASS; // Textures are always 'camera' passes | |
| // Set render pass texture to the newly created texture. | |
| m_RenderPasses[uiPassIdx].pTexture = pTex; | |
| m_RenderPasses[uiPassIdx].uiFormatFlags = TexDesc.uiFlags; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseTarget | |
| @Input nStartLine start line number | |
| @Input nEndLine end line number | |
| @Output pReturnError error string | |
| @Return bool true if parse is successful | |
| @Description Parses the TARGET section of the PFX file. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseTarget(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | |
| { | |
| enum eCmd | |
| { | |
| eCmds_Name, | |
| eCmds_Size | |
| }; | |
| const CPVRTHash TargetCommands[] = | |
| { | |
| "NAME", // eCmds_Name | |
| }; | |
| PVRTCOMPILEASSERT(TargetCommands, sizeof(TargetCommands) / sizeof(TargetCommands[0]) == eCmds_Size); | |
| CPVRTString targetName; | |
| SPVRTPFXParserTexture TexDesc; | |
| TexDesc.nMin = eFilter_Default; | |
| TexDesc.nMag = eFilter_Default; | |
| TexDesc.nMIP = eFilter_MipDefault; | |
| TexDesc.nWrapS = eWrap_Default; | |
| TexDesc.nWrapT = eWrap_Default; | |
| TexDesc.nWrapR = eWrap_Default; | |
| TexDesc.uiWidth = VIEWPORT_SIZE; | |
| TexDesc.uiHeight = VIEWPORT_SIZE; | |
| TexDesc.uiFlags = OGL_RGBA_8888 | PVRPFXTEX_COLOUR; | |
| CPVRTArray<CPVRTHash> KnownCmds; | |
| if(!ParseGenericSurface(nStartLine, nEndLine, TexDesc, KnownCmds, "TARGET", pReturnError)) | |
| return false; | |
| for(int i = nStartLine+1; i < nEndLine; i++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[i]) | |
| continue; | |
| char *str = strtok (m_psContext->ppszEffectFile[i], NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!str) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| CPVRTHash texCmd(str); | |
| // --- Target Name | |
| if(texCmd == TargetCommands[eCmds_Name]) | |
| { | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | |
| if(!pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing NAME arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| targetName = pszRemaining; | |
| } | |
| else if(KnownCmds.Contains(texCmd)) | |
| { | |
| // Remove from 'unknown' list. | |
| for(unsigned int uiIndex = 0; uiIndex < KnownCmds.GetSize(); ++uiIndex) | |
| { | |
| if(KnownCmds[uiIndex] == texCmd) | |
| { | |
| KnownCmds.Remove(uiIndex); | |
| break; | |
| } | |
| } | |
| continue; // This line has already been processed. | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TARGET] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | |
| if(pszRemaining) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [TARGET] on line %d\n", pszRemaining, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| } | |
| // Create a new texture and copy over the vals. | |
| SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture(); | |
| pTex->Name = CPVRTStringHash(targetName); | |
| pTex->FileName = CPVRTStringHash(targetName); | |
| pTex->bRenderToTexture = true; | |
| pTex->nMin = TexDesc.nMin; | |
| pTex->nMag = TexDesc.nMag; | |
| pTex->nMIP = TexDesc.nMIP; | |
| pTex->nWrapS = TexDesc.nWrapS; | |
| pTex->nWrapT = TexDesc.nWrapT; | |
| pTex->nWrapR = TexDesc.nWrapR; | |
| pTex->uiWidth = TexDesc.uiWidth; | |
| pTex->uiHeight = TexDesc.uiHeight; | |
| pTex->uiFlags = TexDesc.uiFlags; | |
| m_psTexture.Append(pTex); | |
| // Copy to render pass struct | |
| unsigned int uiPassIdx = m_RenderPasses.Append(); | |
| m_RenderPasses[uiPassIdx].SemanticName = targetName; | |
| m_RenderPasses[uiPassIdx].eViewType = eVIEW_NONE; | |
| m_RenderPasses[uiPassIdx].eRenderPassType = ePOSTPROCESS_PASS; // Targets are always post-process passes. | |
| m_RenderPasses[uiPassIdx].pTexture = pTex; | |
| m_RenderPasses[uiPassIdx].uiFormatFlags = TexDesc.uiFlags; | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseTextures ** DEPRECATED ** | |
| @Input nStartLine start line number | |
| @Input nEndLine end line number | |
| @Output pReturnError error string | |
| @Return bool true if parse is successful | |
| @Description Parses the TEXTURE section of the PFX file. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseTextures(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | |
| { | |
| char *pszName(NULL), *pszFile(NULL), *pszKeyword(NULL); | |
| char *pszRemaining(NULL), *pszTemp(NULL); | |
| bool bReturnVal(false); | |
| for(int i = nStartLine+1; i < nEndLine; i++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[i]) | |
| continue; | |
| char *str = strtok (m_psContext->ppszEffectFile[i]," "); | |
| if(str != NULL) | |
| { | |
| // Set defaults | |
| unsigned int uiMin(eFilter_Default), uiMag(eFilter_Default), uiMip(eFilter_MipDefault); | |
| unsigned int uiWrapS(eWrap_Default), uiWrapT(eWrap_Default), uiWrapR(eWrap_Default); | |
| unsigned int uiFlags = 0; | |
| unsigned int uiWidth = CPVRTPFXParser::VIEWPORT_SIZE; | |
| unsigned int uiHeight = CPVRTPFXParser::VIEWPORT_SIZE; | |
| // Reset variables | |
| FREE(pszName) pszName = NULL; | |
| FREE(pszFile) pszFile = NULL; | |
| FREE(pszKeyword) pszKeyword = NULL; | |
| FREE(pszTemp) pszTemp = NULL; | |
| pszRemaining = NULL; | |
| // Compare against all valid keywords | |
| if((strcmp(str, "FILE") != 0) && (strcmp(str, "RENDER") != 0)) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | |
| goto fail_release_return; | |
| } | |
| #if 1 | |
| if((strcmp(str, "RENDER") == 0)) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("RENDER tag no longer supported in [TEXTURES] block. Use new [TARGET] block instead\n"); | |
| goto fail_release_return; | |
| } | |
| #endif | |
| pszKeyword = (char *)malloc( ((int)strlen(str)+1) * sizeof(char)); | |
| strcpy(pszKeyword, str); | |
| str = strtok (NULL, " "); | |
| if(str != NULL) | |
| { | |
| pszName = (char *)malloc( ((int)strlen(str)+1) * sizeof(char)); | |
| strcpy(pszName, str); | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| goto fail_release_return; | |
| } | |
| /* | |
| The pszRemaining string is used to look for remaining flags. | |
| This has the advantage of allowing flags to be order independent | |
| and makes it easier to ommit some flags, but still pick up others | |
| (the previous method made it diffifult to retrieve filtering info | |
| if flags before it were missing) | |
| */ | |
| pszRemaining = strtok(NULL, "\n"); | |
| if(pszRemaining == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Incomplete definition in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| goto fail_release_return; | |
| } | |
| else if(strcmp(pszKeyword, "FILE") == 0) | |
| { | |
| pszTemp = (char *)malloc( ((int)strlen(pszRemaining)+1) * sizeof(char)); | |
| strcpy(pszTemp, pszRemaining); | |
| str = strtok (pszTemp, " "); | |
| if(str != NULL) | |
| { | |
| pszFile = (char *)malloc( ((int)strlen(str)+1) * sizeof(char)); | |
| strcpy(pszFile, str); | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| goto fail_release_return; | |
| } | |
| } | |
| if(strcmp(pszKeyword, "FILE") == 0) | |
| { | |
| // --- Filter flags | |
| { | |
| unsigned int* pFlags[3] = | |
| { | |
| &uiMin, | |
| &uiMag, | |
| &uiMip, | |
| }; | |
| if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszFilters, eFilter_Size, pReturnError, i)) | |
| goto fail_release_return; | |
| } | |
| // --- Wrap flags | |
| { | |
| unsigned int* pFlags[3] = | |
| { | |
| &uiWrapS, | |
| &uiWrapT, | |
| &uiWrapR, | |
| }; | |
| if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszWraps, eWrap_Size, pReturnError, i)) | |
| goto fail_release_return; | |
| } | |
| SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture(); | |
| pTex->Name = CPVRTStringHash(pszName); | |
| pTex->FileName = CPVRTStringHash(pszFile); | |
| pTex->bRenderToTexture = false; | |
| pTex->nMin = uiMin; | |
| pTex->nMag = uiMag; | |
| pTex->nMIP = uiMip; | |
| pTex->nWrapS = uiWrapS; | |
| pTex->nWrapT = uiWrapT; | |
| pTex->nWrapR = uiWrapR; | |
| pTex->uiWidth = uiWidth; | |
| pTex->uiHeight = uiHeight; | |
| pTex->uiFlags = uiFlags; | |
| m_psTexture.Append(pTex); | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]);; | |
| goto fail_release_return; | |
| } | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| goto fail_release_return; | |
| } | |
| } | |
| /* | |
| Should only reach here if there have been no issues | |
| */ | |
| bReturnVal = true; | |
| goto release_return; | |
| fail_release_return: | |
| bReturnVal = false; | |
| release_return: | |
| FREE(pszKeyword); | |
| FREE(pszName); | |
| FREE(pszFile); | |
| FREE(pszTemp); | |
| return bReturnVal; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseTextureFlags | |
| @Input c_pszCursor | |
| @Output pFlagsOut | |
| @Input uiNumFlags | |
| @Input ppszFlagNames | |
| @Input uiNumFlagNames | |
| @Input pReturnError | |
| @Input iLineNum | |
| @Return bool | |
| @Description Parses the texture flag sections. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseTextureFlags( const char* c_pszRemainingLine, unsigned int** ppFlagsOut, unsigned int uiNumFlags, const char** c_ppszFlagNames, unsigned int uiNumFlagNames, | |
| CPVRTString * const pReturnError, int iLineNum) | |
| { | |
| const unsigned int INVALID_TYPE = 0xAC1DBEEF; | |
| unsigned int uiIndex; | |
| const char* c_pszCursor; | |
| const char* c_pszResult; | |
| // --- Find the first flag | |
| uiIndex = 0; | |
| c_pszCursor = strstr(c_pszRemainingLine, c_ppszFlagNames[uiIndex++]); | |
| while(uiIndex < uiNumFlagNames) | |
| { | |
| c_pszResult = strstr(c_pszRemainingLine, c_ppszFlagNames[uiIndex++]); | |
| if(((c_pszResult < c_pszCursor) || !c_pszCursor) && c_pszResult) | |
| c_pszCursor = c_pszResult; | |
| } | |
| if(!c_pszCursor) | |
| return true; // No error, but just return as no flags specified. | |
| // Quick error check - make sure that the first flag found is valid. | |
| if(c_pszCursor != c_pszRemainingLine) | |
| { | |
| if(*(c_pszCursor-1) == '-') // Yeah this shouldn't be there. Must be invalid first tag. | |
| { | |
| char szBuffer[128]; // Find out the tag. | |
| memset(szBuffer, 0, sizeof(szBuffer)); | |
| const char* pszStart = c_pszCursor-1; | |
| while(pszStart != c_pszRemainingLine && *pszStart != ' ') pszStart--; | |
| pszStart++; // Escape the space. | |
| unsigned int uiNumChars = (unsigned int) ((c_pszCursor-1) - pszStart); | |
| strncpy(szBuffer, pszStart, uiNumChars); | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d: %s\n", szBuffer, m_psContext->pnFileLineNumber[iLineNum], m_psContext->ppszEffectFile[iLineNum]); | |
| return false; | |
| } | |
| } | |
| unsigned int uiFlagsFound = 0; | |
| unsigned int uiBufferIdx; | |
| char szBuffer[128]; // Buffer to hold the token | |
| while(*c_pszCursor != ' ' && *c_pszCursor != 0 && uiFlagsFound < uiNumFlags) | |
| { | |
| memset(szBuffer, 0, sizeof(szBuffer)); // Clear the buffer | |
| uiBufferIdx = 0; | |
| while(*c_pszCursor != '-' && *c_pszCursor != 0 && *c_pszCursor != ' ' && uiBufferIdx < 128) // - = delim. token | |
| szBuffer[uiBufferIdx++] = *c_pszCursor++; | |
| // Check if the buffer content is a valid flag name. | |
| unsigned int Type = INVALID_TYPE; | |
| for(unsigned int uiIndex = 0; uiIndex < uiNumFlagNames; ++uiIndex) | |
| { | |
| if(strcmp(szBuffer, c_ppszFlagNames[uiIndex]) == 0) | |
| { | |
| Type = uiIndex; // Yup, it's valid. uiIndex here would translate to one of the enums that matches the string array of flag names passed in. | |
| break; | |
| } | |
| } | |
| // Tell the user it's invalid. | |
| if(Type == INVALID_TYPE) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d: %s\n", szBuffer, m_psContext->pnFileLineNumber[iLineNum], m_psContext->ppszEffectFile[iLineNum]); | |
| return false; | |
| } | |
| // Set the flag to the enum type. | |
| *ppFlagsOut[uiFlagsFound++] = Type; | |
| if(*c_pszCursor == '-') c_pszCursor++; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseShader | |
| @Input nStartLine start line number | |
| @Input nEndLine end line number | |
| @Output pReturnError error string | |
| @Output shader shader data object | |
| @Input pszBlockName name of block in PFX file | |
| @Return bool true if parse is successful | |
| @Description Parses the VERTEXSHADER or FRAGMENTSHADER section of the | |
| PFX file. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseShader(int nStartLine, int nEndLine, CPVRTString * const pReturnError, SPVRTPFXParserShader &shader, const char * const pszBlockName) | |
| { | |
| enum eCmd | |
| { | |
| eCmds_GLSLCode, | |
| eCmds_Name, | |
| eCmds_File, | |
| eCmds_BinaryFile, | |
| eCmds_Size | |
| }; | |
| const CPVRTHash ShaderCommands[] = | |
| { | |
| "[GLSL_CODE]", | |
| "NAME", | |
| "FILE", | |
| "BINARYFILE", | |
| }; | |
| PVRTCOMPILEASSERT(ShaderCommands, sizeof(ShaderCommands) / sizeof(ShaderCommands[0]) == eCmds_Size); | |
| bool glslcode=0, glslfile=0, bName=0; | |
| shader.bUseFileName = false; | |
| shader.pszGLSLfile = NULL; | |
| shader.pszGLSLcode = NULL; | |
| shader.pszGLSLBinaryFile= NULL; | |
| shader.pbGLSLBinary = NULL; | |
| shader.nFirstLineNumber = 0; | |
| shader.nLastLineNumber = 0; | |
| for(int i = nStartLine+1; i < nEndLine; i++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[i]) | |
| continue; | |
| char *str = strtok (m_psContext->ppszEffectFile[i]," "); | |
| if(str != NULL) | |
| { | |
| CPVRTHash Cmd(str); | |
| // Check for [GLSL_CODE] tags first and remove those lines from loop. | |
| if(Cmd == ShaderCommands[eCmds_GLSLCode]) | |
| { | |
| if(glslcode) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| if(glslfile && shader.pbGLSLBinary==NULL ) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] not allowed with FILE in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| shader.nFirstLineNumber = m_psContext->pnFileLineNumber[i]; | |
| // Skip the block-start | |
| i++; | |
| CPVRTString GLSLCode; | |
| if(!ConcatenateLinesUntil( | |
| GLSLCode, | |
| i, | |
| m_psContext->ppszEffectFile, | |
| m_psContext->nNumLines, | |
| "[/GLSL_CODE]")) | |
| { | |
| return false; | |
| } | |
| shader.nLastLineNumber = m_psContext->pnFileLineNumber[i]; | |
| shader.pszGLSLcode = (char*)malloc((GLSLCode.size()+1) * sizeof(char)); | |
| strcpy(shader.pszGLSLcode, GLSLCode.c_str()); | |
| shader.bUseFileName = false; | |
| glslcode = 1; | |
| } | |
| else if(Cmd == ShaderCommands[eCmds_Name]) | |
| { | |
| if(bName) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("NAME redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| str = ReadEOLToken(NULL); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("NAME missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| shader.Name.assign(str); | |
| bName = true; | |
| } | |
| else if(Cmd == ShaderCommands[eCmds_File]) | |
| { | |
| if(glslfile) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("FILE redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| if(glslcode) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("FILE not allowed with [GLSL_CODE] in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| str = ReadEOLToken(NULL); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("FILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| shader.pszGLSLfile = (char*)malloc((strlen(str)+1) * sizeof(char)); | |
| strcpy(shader.pszGLSLfile, str); | |
| CPVRTResourceFile GLSLFile(str); | |
| if(!GLSLFile.IsOpen()) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| shader.pszGLSLcode = (char*)malloc((GLSLFile.Size()+1) * sizeof(char)); | |
| memcpy(shader.pszGLSLcode, (const char*) GLSLFile.DataPtr(), GLSLFile.Size()); | |
| shader.pszGLSLcode[GLSLFile.Size()] = '\0'; | |
| shader.nFirstLineNumber = m_psContext->pnFileLineNumber[i]; // Mark position where GLSL file is defined. | |
| shader.bUseFileName = true; | |
| glslfile = 1; | |
| } | |
| else if(Cmd == ShaderCommands[eCmds_BinaryFile]) | |
| { | |
| str = ReadEOLToken(NULL); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("BINARYFILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| shader.pszGLSLBinaryFile = (char*)malloc((strlen(str)+1) * sizeof(char)); | |
| strcpy(shader.pszGLSLBinaryFile, str); | |
| CPVRTResourceFile GLSLFile(str); | |
| if(!GLSLFile.IsOpen()) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| shader.pbGLSLBinary = new char[GLSLFile.Size()]; | |
| shader.nGLSLBinarySize = (unsigned int)GLSLFile.Size(); | |
| memcpy(shader.pbGLSLBinary, GLSLFile.DataPtr(), GLSLFile.Size()); | |
| shader.bUseFileName = true; | |
| glslfile = 1; | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| str = strtok (NULL, " "); | |
| if(str != NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unexpected data in [%s] on line %d: '%s'\n", pszBlockName, m_psContext->pnFileLineNumber[i], str); | |
| return false; | |
| } | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [%s] on line %d: %s\n", pszBlockName, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| } | |
| if(!bName) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("NAME not found in [%s] on line %d.\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| if(!glslfile && !glslcode) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("No Shader File or Shader Code specified in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseSemantic | |
| @Output semantic semantic data object | |
| @Input nStartLine start line number | |
| @Output pReturnError error string | |
| @Return bool true if parse is successful | |
| @Description Parses a semantic. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseSemantic(SPVRTPFXParserSemantic &semantic, const int nStartLine, CPVRTString * const pReturnError) | |
| { | |
| char *str; | |
| semantic.pszName = 0; | |
| semantic.pszValue = 0; | |
| semantic.sDefaultValue.eType = eDataTypeNone; | |
| semantic.nIdx = 0; | |
| str = strtok (NULL, " "); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("UNIFORM missing name in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| semantic.pszName = (char*)malloc((strlen(str)+1) * sizeof(char)); | |
| strcpy(semantic.pszName, str); | |
| str = strtok (NULL, " "); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("UNIFORM missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| FREE(semantic.pszName); | |
| return false; | |
| } | |
| /* | |
| If the final digits of the semantic are a number they are | |
| stripped off and used as the index, with the remainder | |
| used as the semantic. | |
| */ | |
| { | |
| size_t idx, len; | |
| len = strlen(str); | |
| idx = len; | |
| while(idx) | |
| { | |
| --idx; | |
| if(strcspn(&str[idx], "0123456789") != 0) | |
| { | |
| break; | |
| } | |
| } | |
| if(idx == 0) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Semantic contains only numbers in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| FREE(semantic.pszName); | |
| return false; | |
| } | |
| ++idx; | |
| // Store the semantic index | |
| if(len == idx) | |
| { | |
| semantic.nIdx = 0; | |
| } | |
| else | |
| { | |
| semantic.nIdx = atoi(&str[idx]); | |
| } | |
| // Chop off the index from the string containing the semantic | |
| str[idx] = 0; | |
| } | |
| // Store a copy of the semantic name | |
| semantic.pszValue = (char*)malloc((strlen(str)+1) * sizeof(char)); | |
| strcpy(semantic.pszValue, str); | |
| /* | |
| Optional default semantic value | |
| */ | |
| char pszString[2048]; | |
| strcpy(pszString,""); | |
| str = strtok (NULL, " "); | |
| if(str != NULL) | |
| { | |
| // Get all ramainning arguments | |
| while(str != NULL) | |
| { | |
| strcat(pszString, str); | |
| strcat(pszString, " "); | |
| str = strtok (NULL, " "); | |
| } | |
| // default value | |
| int i; | |
| for(i = 0; i < eNumDefaultDataTypes; i++) | |
| { | |
| if(strncmp(pszString, c_psSemanticDefaultDataTypeInfo[i].pszName, strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)) == 0) | |
| { | |
| if(!GetSemanticDataFromString( &semantic.sDefaultValue, | |
| &pszString[strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)], | |
| c_psSemanticDefaultDataTypeInfo[i].eType, | |
| pReturnError | |
| )) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr(" on line %d.\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| FREE(semantic.pszValue); | |
| FREE(semantic.pszName); | |
| return false; | |
| } | |
| semantic.sDefaultValue.eType = c_psSemanticDefaultDataTypeInfo[i].eType; | |
| break; | |
| } | |
| } | |
| // invalid data type | |
| if(i == eNumDefaultDataTypes) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("'%s' unknown on line %d.\n", pszString, m_psContext->pnFileLineNumber[nStartLine]); | |
| FREE(semantic.pszValue); | |
| FREE(semantic.pszName); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function ParseEffect | |
| @Output effect effect data object | |
| @Input nStartLine start line number | |
| @Input nEndLine end line number | |
| @Output pReturnError error string | |
| @Return bool true if parse is successful | |
| @Description Parses the EFFECT section of the PFX file. | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::ParseEffect(SPVRTPFXParserEffect &effect, const int nStartLine, const int nEndLine, CPVRTString * const pReturnError) | |
| { | |
| enum eCmds | |
| { | |
| eCmds_Annotation, | |
| eCmds_VertexShader, | |
| eCmds_FragmentShader, | |
| eCmds_Texture, | |
| eCmds_Uniform, | |
| eCmds_Attribute, | |
| eCmds_Name, | |
| eCmds_Target, | |
| eCmds_Size | |
| }; | |
| const CPVRTHash EffectCommands[] = | |
| { | |
| "[ANNOTATION]", | |
| "VERTEXSHADER", | |
| "FRAGMENTSHADER", | |
| "TEXTURE", | |
| "UNIFORM", | |
| "ATTRIBUTE", | |
| "NAME", | |
| "TARGET", | |
| }; | |
| PVRTCOMPILEASSERT(EffectCommands, sizeof(EffectCommands) / sizeof(EffectCommands[0]) == eCmds_Size); | |
| bool bName = false; | |
| bool bVertShader = false; | |
| bool bFragShader = false; | |
| for(int i = nStartLine+1; i < nEndLine; i++) | |
| { | |
| // Skip blank lines | |
| if(!*m_psContext->ppszEffectFile[i]) | |
| continue; | |
| char *str = strtok (m_psContext->ppszEffectFile[i]," "); | |
| if(str != NULL) | |
| { | |
| CPVRTHash Cmd(str); | |
| if(Cmd == EffectCommands[eCmds_Annotation]) | |
| { | |
| if(!effect.Annotation.empty()) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("ANNOTATION redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| i++; // Skip the block-start | |
| if(!ConcatenateLinesUntil( | |
| effect.Annotation, | |
| i, | |
| m_psContext->ppszEffectFile, | |
| m_psContext->nNumLines, | |
| "[/ANNOTATION]")) | |
| { | |
| return false; | |
| } | |
| } | |
| else if(Cmd == EffectCommands[eCmds_VertexShader]) | |
| { | |
| if(bVertShader) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| str = ReadEOLToken(NULL); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| effect.VertexShaderName.assign(str); | |
| bVertShader = true; | |
| } | |
| else if(Cmd == EffectCommands[eCmds_FragmentShader]) | |
| { | |
| if(bFragShader) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| str = ReadEOLToken(NULL); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| effect.FragmentShaderName.assign(str); | |
| bFragShader = true; | |
| } | |
| else if(Cmd == EffectCommands[eCmds_Texture]) | |
| { | |
| unsigned int uiTexIdx = effect.Textures.Append(); | |
| // texture number | |
| str = strtok(NULL, " "); | |
| if(str != NULL) | |
| effect.Textures[uiTexIdx].nNumber = atoi(str); | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| // texture name | |
| str = strtok(NULL, " "); | |
| if(str != NULL) | |
| { | |
| effect.Textures[uiTexIdx].Name = CPVRTStringHash(str); | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| } | |
| else if(Cmd == EffectCommands[eCmds_Uniform]) | |
| { | |
| unsigned int uiUniformIdx = effect.Uniforms.Append(); | |
| if(!ParseSemantic(effect.Uniforms[uiUniformIdx], i, pReturnError)) | |
| return false; | |
| } | |
| else if(Cmd == EffectCommands[eCmds_Attribute]) | |
| { | |
| unsigned int uiAttribIdx = effect.Attributes.Append(); | |
| if(!ParseSemantic(effect.Attributes[uiAttribIdx], i, pReturnError)) | |
| return false; | |
| } | |
| else if(Cmd == EffectCommands[eCmds_Name]) | |
| { | |
| if(bName) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("NAME redefined in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| str = strtok (NULL, " "); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("NAME missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| effect.Name.assign(str); | |
| bName = true; | |
| } | |
| else if(Cmd == EffectCommands[eCmds_Target]) | |
| { | |
| unsigned int uiIndex = effect.Targets.Append(); | |
| // Target requires 2 components | |
| CPVRTString* pVals[] = { &effect.Targets[uiIndex].BufferType, &effect.Targets[uiIndex].TargetName }; | |
| for(unsigned int uiVal = 0; uiVal < 2; ++uiVal) | |
| { | |
| str = strtok (NULL, " "); | |
| if(str == NULL) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("TARGET missing value(s) in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| *(pVals[uiVal]) = str; | |
| } | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [EFFECT] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | |
| return false; | |
| } | |
| } | |
| else | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr( "Missing arguments in [EFFECT] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | |
| return false; | |
| } | |
| } | |
| // Check that every TEXTURE has a matching UNIFORM | |
| for(unsigned int uiTex = 0; uiTex < effect.Textures.GetSize(); ++uiTex) | |
| { | |
| unsigned int uiTexUnit = effect.Textures[uiTex].nNumber; | |
| const CPVRTStringHash& texName = effect.Textures[uiTex].Name; | |
| // Find UNIFORM associated with the TexUnit (e.g TEXTURE0). | |
| bool bFound = false; | |
| for(unsigned int uiUniform = 0; uiUniform < effect.Uniforms.GetSize(); ++uiUniform) | |
| { | |
| const SPVRTPFXParserSemantic& Sem = effect.Uniforms[uiUniform]; | |
| if(strcmp(Sem.pszValue, "TEXTURE") == 0 && Sem.nIdx == uiTexUnit) | |
| { | |
| bFound = true; | |
| break; | |
| } | |
| } | |
| if(!bFound) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("TEXTURE %s missing matching UNIFORM in [EFFECT] on line %d\n", texName.c_str(), m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| } | |
| if(!bName) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("No 'NAME' found in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | |
| return false; | |
| } | |
| if(!bVertShader) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("No 'VERTEXSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]); | |
| return false; | |
| } | |
| if(!bFragShader) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("No 'FRAGMENTSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]); | |
| return false; | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function DetermineRenderPassDependencies | |
| @Return True if dependency tree is valid. False if there are errors | |
| in the dependency tree (e.g. recursion) | |
| @Description Looks through all of the effects in the .pfx and determines | |
| the order of render passes that have been declared with | |
| the RENDER tag (found in [TEXTURES] | |
| *****************************************************************************/ | |
| bool CPVRTPFXParser::DetermineRenderPassDependencies(CPVRTString * const pReturnError) | |
| { | |
| unsigned int ui(0), uj(0), uk(0); | |
| if(m_RenderPasses.GetSize() == 0) | |
| return true; | |
| // --- Add all render pass nodes to the skip graph. | |
| for(ui = 0; ui < m_RenderPasses.GetSize(); ++ui) | |
| { | |
| SPVRTPFXRenderPass& Pass = m_RenderPasses[ui]; | |
| bool bFound = false; | |
| // Search all EFFECT blocks for matching TARGET. This is for post-processes behavior. | |
| for(unsigned int uiEffect = 0; uiEffect < m_psEffect.GetSize(); ++uiEffect) | |
| { | |
| SPVRTPFXParserEffect& Effect = m_psEffect[uiEffect]; | |
| // Search all TARGETs in this effect | |
| for(unsigned int uiTargets = 0; uiTargets < Effect.Targets.GetSize(); ++uiTargets) | |
| { | |
| const SPVRTTargetPair& Target = Effect.Targets[uiTargets]; | |
| if(Target.TargetName == Pass.SemanticName) | |
| { | |
| // Match. This EFFECT block matches the pass name. | |
| Pass.pEffect = &Effect; | |
| bFound = true; | |
| // This is now a post-process pass. Set relevant values. | |
| Pass.eRenderPassType = ePOSTPROCESS_PASS; | |
| m_aszPostProcessNames.Append(Pass.SemanticName); | |
| // Check that the surface type and output match are relevant (i.e DEPTH != RGBA8888). | |
| if( (Target.BufferType.find_first_of("DEPTH") != CPVRTString::npos && !(Pass.uiFormatFlags & PVRPFXTEX_DEPTH)) | |
| || (Target.BufferType.find_first_of("COLOR") != CPVRTString::npos && !(Pass.uiFormatFlags & PVRPFXTEX_COLOUR)) ) | |
| { | |
| *pReturnError = PVRTStringFromFormattedStr("Surface type mismatch in [EFFECT]. \"%s\" has different type than \"%s\"\n", Target.TargetName.c_str(), Pass.SemanticName.c_str()); | |
| return false; | |
| } | |
| break; | |
| } | |
| } | |
| if(bFound) | |
| break; | |
| } | |
| // Add a pointer to the post process | |
| m_renderPassSkipGraph.AddNode(&Pass); | |
| } | |
| // --- Loop through all created render passes in the skip graph and determine their dependencies | |
| for(ui = 0; ui < m_renderPassSkipGraph.GetNumNodes(); ++ui) | |
| { | |
| // Loop through all other nodes in the skip graph | |
| SPVRTPFXRenderPass* pPass = m_renderPassSkipGraph[ui]; | |
| SPVRTPFXRenderPass* pTestPass = NULL; | |
| for(uj = 0; uj < m_RenderPasses.GetSize(); ++uj) | |
| { | |
| pTestPass = m_renderPassSkipGraph[uj]; | |
| // No self compare | |
| if(pPass == pTestPass) | |
| continue; | |
| // No effect associated. | |
| if(!pPass->pEffect) | |
| continue; | |
| // Is the node a render pass I rely on? | |
| for(uk = 0; uk < pPass->pEffect->Textures.GetSize(); ++uk) | |
| { | |
| /* | |
| If the texture names match, add a new node | |
| */ | |
| if(pTestPass->pTexture->Name == pPass->pEffect->Textures[uk].Name) | |
| { | |
| m_renderPassSkipGraph.AddNodeDependency(pPass, pTestPass); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| /*!*************************************************************************** | |
| @Function FindTextureIndex | |
| @Input TextureName | |
| @Return unsigned int Index in to the effect.Texture array. | |
| @Description Returns the index in to the texture array within the effect | |
| block where the given texture resides. | |
| *****************************************************************************/ | |
| unsigned int CPVRTPFXParser::FindTextureIndex( const CPVRTStringHash& TextureName, unsigned int uiEffect ) const | |
| { | |
| for(unsigned int uiIndex = 0; uiIndex < m_psEffect[uiEffect].Textures.GetSize(); ++uiIndex) | |
| { | |
| const SPVRTPFXParserEffectTexture& Tex = m_psEffect[uiEffect].Textures[uiIndex]; | |
| if(Tex.Name == TextureName) | |
| { | |
| return uiIndex; | |
| } | |
| } | |
| return 0xFFFFFFFF; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetNumberRenderPasses | |
| @Return unsigned int | |
| @Description Returns the number of render passes within this PFX. | |
| *****************************************************************************/ | |
| unsigned int CPVRTPFXParser::GetNumberRenderPasses() const | |
| { | |
| return m_RenderPasses.GetSize(); | |
| } | |
| /*!*************************************************************************** | |
| @Function GetNumberRenderPasses | |
| @Input unsigned int The render pass index. | |
| @Return SPVRTPFXRenderPass* | |
| @Description Returns the given render pass. | |
| *****************************************************************************/ | |
| const SPVRTPFXRenderPass& CPVRTPFXParser::GetRenderPass( unsigned int uiIndex ) const | |
| { | |
| _ASSERT(uiIndex >= 0 && uiIndex < GetNumberRenderPasses()); | |
| return m_RenderPasses[uiIndex]; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetPFXFileName | |
| @Return const CPVRTString & | |
| @Description Returns the PFX file name associated with this object. | |
| *****************************************************************************/ | |
| const CPVRTString& CPVRTPFXParser::GetPFXFileName() const | |
| { | |
| return m_szFileName; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetPostProcessNames | |
| @Return const CPVRTArray<CPVRTString>& | |
| @Description Returns a list of prost process effect names. | |
| *****************************************************************************/ | |
| const CPVRTArray<CPVRTString>& CPVRTPFXParser::GetPostProcessNames() const | |
| { | |
| return m_aszPostProcessNames; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetNumberFragmentShaders | |
| @Return unsigned int Number of fragment shaders. | |
| @Description Returns the number of fragment shaders referenced in the PFX. | |
| *****************************************************************************/ | |
| unsigned int CPVRTPFXParser::GetNumberFragmentShaders() const | |
| { | |
| return m_psFragmentShader.GetSize(); | |
| } | |
| /*!*************************************************************************** | |
| @Function GetFragmentShader | |
| @Input unsigned int The index of this shader. | |
| @Return const SPVRTPFXParserShader& The PFX fragment shader. | |
| @Description Returns a given fragment shader. | |
| *****************************************************************************/ | |
| SPVRTPFXParserShader& CPVRTPFXParser::GetFragmentShader( unsigned int uiIndex ) | |
| { | |
| _ASSERT(uiIndex < GetNumberFragmentShaders()); | |
| return m_psFragmentShader[uiIndex]; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetNumberVertexShaders | |
| @Return unsigned int Number of vertex shaders. | |
| @Description Returns the number of vertex shaders referenced in the PFX. | |
| *****************************************************************************/ | |
| unsigned int CPVRTPFXParser::GetNumberVertexShaders() const | |
| { | |
| return m_psVertexShader.GetSize(); | |
| } | |
| /*!*************************************************************************** | |
| @Function GetVertexShader | |
| @Input unsigned int The index of this shader. | |
| @Return const SPVRTPFXParserShader& The PFX vertex shader. | |
| @Description Returns a given vertex shader. | |
| *****************************************************************************/ | |
| SPVRTPFXParserShader& CPVRTPFXParser::GetVertexShader( unsigned int uiIndex ) | |
| { | |
| _ASSERT(uiIndex < GetNumberVertexShaders()); | |
| return m_psVertexShader[uiIndex]; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetNumberEffects | |
| @Return unsigned int Number of effects. | |
| @Description Returns the number of effects referenced in the PFX. | |
| *****************************************************************************/ | |
| unsigned int CPVRTPFXParser::GetNumberEffects() const | |
| { | |
| return m_psEffect.GetSize(); | |
| } | |
| /*!*************************************************************************** | |
| @Function GetEffect | |
| @Input uiIndex The index of this effect. | |
| @Return The PFX effect. | |
| @Description Returns a given effect. | |
| *****************************************************************************/ | |
| const SPVRTPFXParserEffect& CPVRTPFXParser::GetEffect(unsigned int uiIndex) const | |
| { | |
| _ASSERT(uiIndex < GetNumberEffects()); | |
| return m_psEffect[uiIndex]; | |
| } | |
| /*!*************************************************************************** | |
| @Function GetNumberTextures | |
| @Return unsigned int Number of effects. | |
| @Description Returns the number of textures referenced in the PFX. | |
| *****************************************************************************/ | |
| unsigned int CPVRTPFXParser::GetNumberTextures() const | |
| { | |
| return m_psTexture.GetSize(); | |
| } | |
| /*!*************************************************************************** | |
| @Function GetTexture | |
| @Input unsigned int The index of this texture | |
| @Return const SPVRTPFXParserEffect& The PFX texture. | |
| @Description Returns a given texture. | |
| *****************************************************************************/ | |
| const SPVRTPFXParserTexture* CPVRTPFXParser::GetTexture( unsigned int uiIndex ) const | |
| { | |
| _ASSERT(uiIndex < GetNumberTextures()); | |
| return m_psTexture[uiIndex]; | |
| } | |
| /*!*************************************************************************** | |
| @Function FindEffectByName | |
| @Input Name | |
| @Return int | |
| @Description Returns the index of the given string. Returns -1 on failure. | |
| *****************************************************************************/ | |
| int CPVRTPFXParser::FindEffectByName(const CPVRTStringHash& Name) const | |
| { | |
| if(Name.Hash() == 0) | |
| return -1; | |
| for(unsigned int uiIndex = 0; uiIndex < GetNumberEffects(); ++uiIndex) | |
| { | |
| if(GetEffect(uiIndex).Name == Name) | |
| { | |
| return (int)uiIndex; | |
| } | |
| } | |
| return -1; | |
| } | |
| /*!*************************************************************************** | |
| @Function FindTextureByName | |
| @Input Name Name of the texture. | |
| @Return int | |
| @Description Returns the index of the given texture. Returns -1 on failure. | |
| *****************************************************************************/ | |
| int CPVRTPFXParser::FindTextureByName(const CPVRTStringHash& Name) const | |
| { | |
| if(Name.Hash() == 0) | |
| return -1; | |
| for(unsigned int uiIndex = 0; uiIndex < GetNumberTextures(); ++uiIndex) | |
| { | |
| if(GetTexture(uiIndex)->Name == Name) | |
| { | |
| return (int)uiIndex; | |
| } | |
| } | |
| return -1; | |
| } | |
| /*!*************************************************************************** | |
| @Function PVRTPFXCreateStringCopy | |
| @Return void | |
| @Description Safely copies a C string. | |
| *****************************************************************************/ | |
| void PVRTPFXCreateStringCopy(char** ppDst, const char* pSrc) | |
| { | |
| if(pSrc) | |
| { | |
| FREE(*ppDst); | |
| *ppDst = (char*)malloc((strlen(pSrc)+1) * sizeof(char)); | |
| strcpy(*ppDst, pSrc); | |
| } | |
| } | |
| /***************************************************************************** | |
| End of file (PVRTPFXParser.cpp) | |
| *****************************************************************************/ | |