| /****************************************************************************** |
| |
| @File PVRTModelPOD.cpp |
| |
| @Title PVRTModelPOD |
| |
| @Version |
| |
| @Copyright Copyright (c) Imagination Technologies Limited. |
| |
| @Platform ANSI compatible |
| |
| @Description Code to load POD files - models exported from MAX. |
| |
| ******************************************************************************/ |
| #include <math.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "PVRTGlobal.h" |
| #if defined(BUILD_DX11) |
| #include "PVRTContext.h" |
| #endif |
| #include "PVRTFixedPoint.h" |
| #include "PVRTMatrix.h" |
| #include "PVRTQuaternion.h" |
| #include "PVRTVertex.h" |
| #include "PVRTBoneBatch.h" |
| #include "PVRTModelPOD.h" |
| #include "PVRTMisc.h" |
| #include "PVRTResourceFile.h" |
| #include "PVRTTrans.h" |
| |
| /**************************************************************************** |
| ** Defines |
| ****************************************************************************/ |
| #define PVRTMODELPOD_TAG_MASK (0x80000000) |
| #define PVRTMODELPOD_TAG_START (0x00000000) |
| #define PVRTMODELPOD_TAG_END (0x80000000) |
| |
| #define CFAH (1024) |
| |
| /**************************************************************************** |
| ** Enumerations |
| ****************************************************************************/ |
| /*!**************************************************************************** |
| @Struct EPODFileName |
| @Brief Enum for the binary pod blocks |
| ******************************************************************************/ |
| enum EPODFileName |
| { |
| ePODFileVersion = 1000, |
| ePODFileScene, |
| ePODFileExpOpt, |
| ePODFileHistory, |
| ePODFileEndiannessMisMatch = -402456576, |
| |
| ePODFileColourBackground = 2000, |
| ePODFileColourAmbient, |
| ePODFileNumCamera, |
| ePODFileNumLight, |
| ePODFileNumMesh, |
| ePODFileNumNode, |
| ePODFileNumMeshNode, |
| ePODFileNumTexture, |
| ePODFileNumMaterial, |
| ePODFileNumFrame, |
| ePODFileCamera, // Will come multiple times |
| ePODFileLight, // Will come multiple times |
| ePODFileMesh, // Will come multiple times |
| ePODFileNode, // Will come multiple times |
| ePODFileTexture, // Will come multiple times |
| ePODFileMaterial, // Will come multiple times |
| ePODFileFlags, |
| ePODFileFPS, |
| ePODFileUserData, |
| ePODFileUnits, |
| |
| ePODFileMatName = 3000, |
| ePODFileMatIdxTexDiffuse, |
| ePODFileMatOpacity, |
| ePODFileMatAmbient, |
| ePODFileMatDiffuse, |
| ePODFileMatSpecular, |
| ePODFileMatShininess, |
| ePODFileMatEffectFile, |
| ePODFileMatEffectName, |
| ePODFileMatIdxTexAmbient, |
| ePODFileMatIdxTexSpecularColour, |
| ePODFileMatIdxTexSpecularLevel, |
| ePODFileMatIdxTexBump, |
| ePODFileMatIdxTexEmissive, |
| ePODFileMatIdxTexGlossiness, |
| ePODFileMatIdxTexOpacity, |
| ePODFileMatIdxTexReflection, |
| ePODFileMatIdxTexRefraction, |
| ePODFileMatBlendSrcRGB, |
| ePODFileMatBlendSrcA, |
| ePODFileMatBlendDstRGB, |
| ePODFileMatBlendDstA, |
| ePODFileMatBlendOpRGB, |
| ePODFileMatBlendOpA, |
| ePODFileMatBlendColour, |
| ePODFileMatBlendFactor, |
| ePODFileMatFlags, |
| ePODFileMatUserData, |
| |
| ePODFileTexName = 4000, |
| |
| ePODFileNodeIdx = 5000, |
| ePODFileNodeName, |
| ePODFileNodeIdxMat, |
| ePODFileNodeIdxParent, |
| ePODFileNodePos, |
| ePODFileNodeRot, |
| ePODFileNodeScale, |
| ePODFileNodeAnimPos, |
| ePODFileNodeAnimRot, |
| ePODFileNodeAnimScale, |
| ePODFileNodeMatrix, |
| ePODFileNodeAnimMatrix, |
| ePODFileNodeAnimFlags, |
| ePODFileNodeAnimPosIdx, |
| ePODFileNodeAnimRotIdx, |
| ePODFileNodeAnimScaleIdx, |
| ePODFileNodeAnimMatrixIdx, |
| ePODFileNodeUserData, |
| |
| ePODFileMeshNumVtx = 6000, |
| ePODFileMeshNumFaces, |
| ePODFileMeshNumUVW, |
| ePODFileMeshFaces, |
| ePODFileMeshStripLength, |
| ePODFileMeshNumStrips, |
| ePODFileMeshVtx, |
| ePODFileMeshNor, |
| ePODFileMeshTan, |
| ePODFileMeshBin, |
| ePODFileMeshUVW, // Will come multiple times |
| ePODFileMeshVtxCol, |
| ePODFileMeshBoneIdx, |
| ePODFileMeshBoneWeight, |
| ePODFileMeshInterleaved, |
| ePODFileMeshBoneBatches, |
| ePODFileMeshBoneBatchBoneCnts, |
| ePODFileMeshBoneBatchOffsets, |
| ePODFileMeshBoneBatchBoneMax, |
| ePODFileMeshBoneBatchCnt, |
| ePODFileMeshUnpackMatrix, |
| |
| ePODFileLightIdxTgt = 7000, |
| ePODFileLightColour, |
| ePODFileLightType, |
| ePODFileLightConstantAttenuation, |
| ePODFileLightLinearAttenuation, |
| ePODFileLightQuadraticAttenuation, |
| ePODFileLightFalloffAngle, |
| ePODFileLightFalloffExponent, |
| |
| ePODFileCamIdxTgt = 8000, |
| ePODFileCamFOV, |
| ePODFileCamFar, |
| ePODFileCamNear, |
| ePODFileCamAnimFOV, |
| |
| ePODFileDataType = 9000, |
| ePODFileN, |
| ePODFileStride, |
| ePODFileData |
| }; |
| |
| /**************************************************************************** |
| ** Structures |
| ****************************************************************************/ |
| struct SPVRTPODImpl |
| { |
| VERTTYPE fFrame; /*!< Frame number */ |
| VERTTYPE fBlend; /*!< Frame blend (AKA fractional part of animation frame number) */ |
| int nFrame; /*!< Frame number (AKA integer part of animation frame number) */ |
| |
| VERTTYPE *pfCache; /*!< Cache indicating the frames at which the matrix cache was filled */ |
| PVRTMATRIX *pWmCache; /*!< Cache of world matrices */ |
| PVRTMATRIX *pWmZeroCache; /*!< Pre-calculated frame 0 matrices */ |
| |
| bool bFromMemory; /*!< Was the mesh data loaded from memory? */ |
| |
| #ifdef _DEBUG |
| PVRTint64 nWmTotal, nWmCacheHit, nWmZeroCacheHit; |
| float fHitPerc, fHitPercZero; |
| #endif |
| }; |
| |
| /**************************************************************************** |
| ** Local code: Memory allocation |
| ****************************************************************************/ |
| |
| /*!*************************************************************************** |
| @Function SafeAlloc |
| @Input cnt |
| @Output ptr |
| @Return false if memory allocation failed |
| @Description Allocates a block of memory. |
| *****************************************************************************/ |
| template <typename T> |
| bool SafeAlloc(T* &ptr, size_t cnt) |
| { |
| _ASSERT(!ptr); |
| if(cnt) |
| { |
| ptr = (T*)calloc(cnt, sizeof(T)); |
| _ASSERT(ptr); |
| if(!ptr) |
| return false; |
| } |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function SafeRealloc |
| @Modified ptr |
| @Input cnt |
| @Description Changes the size of a memory allocation. |
| *****************************************************************************/ |
| template <typename T> |
| void SafeRealloc(T* &ptr, size_t cnt) |
| { |
| ptr = (T*)realloc(ptr, cnt * sizeof(T)); |
| _ASSERT(ptr); |
| } |
| |
| /**************************************************************************** |
| ** Class: CPODData |
| ****************************************************************************/ |
| /*!*************************************************************************** |
| @Function Reset |
| @Description Resets the POD Data to NULL |
| *****************************************************************************/ |
| void CPODData::Reset() |
| { |
| eType = EPODDataFloat; |
| n = 0; |
| nStride = 0; |
| FREE(pData); |
| } |
| |
| // check32BitType and check16BitType are structs where only the specialisations have a standard declaration (complete type) |
| // if this struct is instantiated with a different type then the compiler will choke on it |
| // Place a line like: " check32BitType<channelType>(); " in a template function |
| // to ensure it won't be called using a type of the wrong size. |
| template<class T> struct check32BitType; |
| template<> struct check32BitType<unsigned int> {}; |
| template<> struct check32BitType<int> {}; |
| template<> struct check32BitType<float> {}; |
| template<class T> struct check16BitType; |
| template<> struct check16BitType<unsigned short> {}; |
| template<> struct check16BitType<short> {}; |
| |
| /*!*************************************************************************** |
| Class: CSource |
| *****************************************************************************/ |
| class CSource |
| { |
| public: |
| /*!*************************************************************************** |
| @Function ~CSource |
| @Description Destructor |
| *****************************************************************************/ |
| virtual ~CSource() {}; |
| virtual bool Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead) = 0; |
| virtual bool Skip(const unsigned int nBytes) = 0; |
| |
| template <typename T> |
| bool Read(T &n) |
| { |
| return Read(&n, sizeof(T)); |
| } |
| |
| template <typename T> |
| bool Read32(T &n) |
| { |
| unsigned char ub[4]; |
| |
| if(Read(&ub, 4)) |
| { |
| unsigned int *pn = (unsigned int*) &n; |
| *pn = (unsigned int) ((ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0]); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| template <typename T> |
| bool Read16(T &n) |
| { |
| unsigned char ub[2]; |
| |
| if(Read(&ub, 2)) |
| { |
| unsigned short *pn = (unsigned short*) &n; |
| *pn = (unsigned short) ((ub[1] << 8) | ub[0]); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ReadMarker(unsigned int &nName, unsigned int &nLen); |
| |
| template <typename T> |
| bool ReadAfterAlloc(T* &lpBuffer, const unsigned int dwNumberOfBytesToRead) |
| { |
| if(!SafeAlloc(lpBuffer, dwNumberOfBytesToRead)) |
| return false; |
| return Read(lpBuffer, dwNumberOfBytesToRead); |
| } |
| |
| template <typename T> |
| bool ReadAfterAlloc32(T* &lpBuffer, const unsigned int dwNumberOfBytesToRead) |
| { |
| check32BitType<T>(); |
| if(!SafeAlloc(lpBuffer, dwNumberOfBytesToRead/4)) |
| return false; |
| return ReadArray32((unsigned int*) lpBuffer, dwNumberOfBytesToRead / 4); |
| } |
| |
| template <typename T> |
| bool ReadArray32(T* pn, const unsigned int i32Size) |
| { |
| check32BitType<T>(); |
| bool bRet = true; |
| |
| for(unsigned int i = 0; i < i32Size; ++i) |
| bRet &= Read32(pn[i]); |
| |
| return bRet; |
| } |
| |
| template <typename T> |
| bool ReadAfterAlloc16(T* &lpBuffer, const unsigned int dwNumberOfBytesToRead) |
| { |
| check16BitType<T>(); |
| if(!SafeAlloc(lpBuffer, dwNumberOfBytesToRead/2 )) |
| return false; |
| return ReadArray16((unsigned short*) lpBuffer, dwNumberOfBytesToRead / 2); |
| } |
| |
| bool ReadArray16(unsigned short* pn, unsigned int i32Size) |
| { |
| bool bRet = true; |
| |
| for(unsigned int i = 0; i < i32Size; ++i) |
| bRet &= Read16(pn[i]); |
| |
| return bRet; |
| } |
| }; |
| |
| bool CSource::ReadMarker(unsigned int &nName, unsigned int &nLen) |
| { |
| if(!Read32(nName)) |
| return false; |
| if(!Read32(nLen)) |
| return false; |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| Class: CSourceStream |
| *****************************************************************************/ |
| class CSourceStream : public CSource |
| { |
| protected: |
| CPVRTResourceFile* m_pFile; |
| size_t m_BytesReadCount; |
| |
| public: |
| /*!*************************************************************************** |
| @Function CSourceStream |
| @Description Constructor |
| *****************************************************************************/ |
| CSourceStream() : m_pFile(0), m_BytesReadCount(0) {} |
| |
| /*!*************************************************************************** |
| @Function ~CSourceStream |
| @Description Destructor |
| *****************************************************************************/ |
| virtual ~CSourceStream(); |
| |
| bool Init(const char * const pszFileName); |
| bool Init(const char * const pData, const size_t i32Size); |
| |
| virtual bool Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead); |
| virtual bool Skip(const unsigned int nBytes); |
| }; |
| |
| /*!*************************************************************************** |
| @Function ~CSourceStream |
| @Description Destructor |
| *****************************************************************************/ |
| CSourceStream::~CSourceStream() |
| { |
| delete m_pFile; |
| } |
| |
| /*!*************************************************************************** |
| @Function Init |
| @Input pszFileName Source file |
| @Description Initialises the source stream with a file at the specified |
| directory. |
| *****************************************************************************/ |
| bool CSourceStream::Init(const char * const pszFileName) |
| { |
| m_BytesReadCount = 0; |
| if (m_pFile) |
| { |
| delete m_pFile; |
| m_pFile = 0; |
| } |
| |
| if(!pszFileName) |
| return false; |
| |
| m_pFile = new CPVRTResourceFile(pszFileName); |
| if (!m_pFile->IsOpen()) |
| { |
| delete m_pFile; |
| m_pFile = 0; |
| return false; |
| } |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function Init |
| @Input pData Address of the source data |
| @Input i32Size Size of the data (in bytes) |
| @Description Initialises the source stream with the data at the specified |
| directory. |
| *****************************************************************************/ |
| bool CSourceStream::Init(const char * pData, size_t i32Size) |
| { |
| m_BytesReadCount = 0; |
| if (m_pFile) delete m_pFile; |
| |
| m_pFile = new CPVRTResourceFile(pData, i32Size); |
| if (!m_pFile->IsOpen()) |
| { |
| delete m_pFile; |
| m_pFile = 0; |
| return false; |
| } |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function Read |
| @Modified lpBuffer Buffer to write the data into |
| @Input dwNumberOfBytesToRead Number of bytes to read |
| @Description Reads specified number of bytes from the source stream |
| into the output buffer. |
| *****************************************************************************/ |
| bool CSourceStream::Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead) |
| { |
| _ASSERT(lpBuffer); |
| _ASSERT(m_pFile); |
| |
| if (m_BytesReadCount + dwNumberOfBytesToRead > m_pFile->Size()) return false; |
| |
| memcpy(lpBuffer, &((char*) m_pFile->DataPtr())[m_BytesReadCount], dwNumberOfBytesToRead); |
| |
| m_BytesReadCount += dwNumberOfBytesToRead; |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function Skip |
| @Input nBytes The number of bytes to skip |
| @Description Skips the specified number of bytes of the source stream. |
| *****************************************************************************/ |
| bool CSourceStream::Skip(const unsigned int nBytes) |
| { |
| if (m_BytesReadCount + nBytes > m_pFile->Size()) return false; |
| m_BytesReadCount += nBytes; |
| return true; |
| } |
| |
| #if defined(_WIN32) |
| /*!*************************************************************************** |
| Class: CSourceResource |
| *****************************************************************************/ |
| class CSourceResource : public CSource |
| { |
| protected: |
| const unsigned char *m_pData; |
| unsigned int m_nSize, m_nReadPos; |
| |
| public: |
| bool Init(const TCHAR * const pszName); |
| virtual bool Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead); |
| virtual bool Skip(const unsigned int nBytes); |
| }; |
| |
| /*!*************************************************************************** |
| @Function Init |
| @Input pszName The file extension of the resource file |
| @Description Initialises the source resource from the data at the |
| specified file extension. |
| *****************************************************************************/ |
| bool CSourceResource::Init(const TCHAR * const pszName) |
| { |
| HRSRC hR; |
| HGLOBAL hG; |
| |
| // Find the resource |
| hR = FindResource(GetModuleHandle(NULL), pszName, RT_RCDATA); |
| if(!hR) |
| return false; |
| |
| // How big is the resource? |
| m_nSize = SizeofResource(NULL, hR); |
| if(!m_nSize) |
| return false; |
| |
| // Get a pointer to the resource data |
| hG = LoadResource(NULL, hR); |
| if(!hG) |
| return false; |
| |
| m_pData = (unsigned char*)LockResource(hG); |
| if(!m_pData) |
| return false; |
| |
| m_nReadPos = 0; |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function Read |
| @Modified lpBuffer The buffer to write to |
| @Input dwNumberOfBytesToRead The number of bytes to read |
| @Description Reads data from the resource to the specified output buffer. |
| *****************************************************************************/ |
| bool CSourceResource::Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead) |
| { |
| if(m_nReadPos + dwNumberOfBytesToRead > m_nSize) |
| return false; |
| |
| _ASSERT(lpBuffer); |
| memcpy(lpBuffer, &m_pData[m_nReadPos], dwNumberOfBytesToRead); |
| m_nReadPos += dwNumberOfBytesToRead; |
| return true; |
| } |
| |
| bool CSourceResource::Skip(const unsigned int nBytes) |
| { |
| if(m_nReadPos + nBytes > m_nSize) |
| return false; |
| |
| m_nReadPos += nBytes; |
| return true; |
| } |
| |
| #endif /* _WIN32 */ |
| |
| /**************************************************************************** |
| ** Local code: File writing |
| ****************************************************************************/ |
| |
| /*!*************************************************************************** |
| @Function WriteFileSafe |
| @Input pFile |
| @Input lpBuffer |
| @Input nNumberOfBytesToWrite |
| @Return true if successful |
| @Description Writes data to a file, checking return codes. |
| *****************************************************************************/ |
| static bool WriteFileSafe(FILE *pFile, const void * const lpBuffer, const unsigned int nNumberOfBytesToWrite) |
| { |
| if(nNumberOfBytesToWrite) |
| { |
| size_t count = fwrite(lpBuffer, nNumberOfBytesToWrite, 1, pFile); |
| return count == 1; |
| } |
| return true; |
| } |
| |
| static bool WriteFileSafe16(FILE *pFile, const unsigned short * const lpBuffer, const unsigned int nSize) |
| { |
| if(nSize) |
| { |
| unsigned char ub[2]; |
| bool bRet = true; |
| |
| for(unsigned int i = 0; i < nSize; ++i) |
| { |
| ub[0] = (unsigned char) lpBuffer[i]; |
| ub[1] = lpBuffer[i] >> 8; |
| |
| bRet &= (fwrite(ub, 2, 1, pFile) == 1); |
| } |
| |
| return bRet; |
| } |
| return true; |
| } |
| |
| static bool WriteFileSafe32(FILE *pFile, const unsigned int * const lpBuffer, const unsigned int nSize) |
| { |
| if(nSize) |
| { |
| unsigned char ub[4]; |
| bool bRet = true; |
| |
| for(unsigned int i = 0; i < nSize; ++i) |
| { |
| ub[0] = (unsigned char) (lpBuffer[i]); |
| ub[1] = (unsigned char) (lpBuffer[i] >> 8); |
| ub[2] = (unsigned char) (lpBuffer[i] >> 16); |
| ub[3] = (unsigned char) (lpBuffer[i] >> 24); |
| |
| bRet &= (fwrite(ub, 4, 1, pFile) == 1); |
| } |
| |
| return bRet; |
| } |
| return true; |
| } |
| /*!*************************************************************************** |
| @Function WriteMarker |
| @Input pFile |
| @Input nName |
| @Input bEnd |
| @Input nLen |
| Return true if successful |
| @Description Write a marker to a POD file. If bEnd if false, it's a |
| beginning marker, otherwise it's an end marker. |
| *****************************************************************************/ |
| static bool WriteMarker( |
| FILE * const pFile, |
| const unsigned int nName, |
| const bool bEnd, |
| const unsigned int nLen = 0) |
| { |
| unsigned int nMarker; |
| bool bRet; |
| |
| _ASSERT((nName & ~PVRTMODELPOD_TAG_MASK) == nName); |
| nMarker = nName | (bEnd ? PVRTMODELPOD_TAG_END : PVRTMODELPOD_TAG_START); |
| |
| bRet = WriteFileSafe32(pFile, &nMarker, 1); |
| bRet &= WriteFileSafe32(pFile, &nLen, 1); |
| |
| return bRet; |
| } |
| |
| /*!*************************************************************************** |
| @Function WriteData |
| @Input pFile |
| @Input nName |
| @Input pData |
| @Input nLen |
| @Return true if successful |
| @Description Write nLen bytes of data from pData, bracketed by an nName |
| begin/end markers. |
| *****************************************************************************/ |
| static bool WriteData( |
| FILE * const pFile, |
| const unsigned int nName, |
| const void * const pData, |
| const unsigned int nLen) |
| { |
| if(pData) |
| { |
| _ASSERT(nLen); |
| if(!WriteMarker(pFile, nName, false, nLen)) return false; |
| if(!WriteFileSafe(pFile, pData, nLen)) return false; |
| if(!WriteMarker(pFile, nName, true)) return false; |
| } |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function WriteData16 |
| @Input pFile |
| @Input nName |
| @Input pData |
| @Input i32Size |
| @Return true if successful |
| @Description Write i32Size no. of unsigned shorts from pData, bracketed by |
| an nName begin/end markers. |
| *****************************************************************************/ |
| template <typename T> |
| static bool WriteData16( |
| FILE * const pFile, |
| const unsigned int nName, |
| const T * const pData, |
| int i32Size = 1) |
| { |
| if(pData) |
| { |
| if(!WriteMarker(pFile, nName, false, 2 * i32Size)) return false; |
| if(!WriteFileSafe16(pFile, (unsigned short*) pData, i32Size)) return false; |
| if(!WriteMarker(pFile, nName, true)) return false; |
| } |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function WriteData32 |
| @Input pFile |
| @Input nName |
| @Input pData |
| @Input i32Size |
| @Return true if successful |
| @Description Write i32Size no. of unsigned ints from pData, bracketed by |
| an nName begin/end markers. |
| *****************************************************************************/ |
| template <typename T> |
| static bool WriteData32( |
| FILE * const pFile, |
| const unsigned int nName, |
| const T * const pData, |
| int i32Size = 1) |
| { |
| if(pData) |
| { |
| if(!WriteMarker(pFile, nName, false, 4 * i32Size)) return false; |
| if(!WriteFileSafe32(pFile, (unsigned int*) pData, i32Size)) return false; |
| if(!WriteMarker(pFile, nName, true)) return false; |
| } |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function WriteData |
| @Input pFile |
| @Input nName |
| @Input n |
| @Return true if successful |
| @Description Write the value n, bracketed by an nName begin/end markers. |
| *****************************************************************************/ |
| template <typename T> |
| static bool WriteData( |
| FILE * const pFile, |
| const unsigned int nName, |
| const T &n) |
| { |
| unsigned int nSize = sizeof(T); |
| |
| bool bRet = WriteData(pFile, nName, (void*)&n, nSize); |
| |
| return bRet; |
| } |
| |
| /*!*************************************************************************** |
| @Function WriteCPODData |
| @Input pFile |
| @Input nName |
| @Input n |
| @Input nEntries |
| @Input bValidData |
| @Return true if successful |
| @Description Write the value n, bracketed by an nName begin/end markers. |
| *****************************************************************************/ |
| static bool WriteCPODData( |
| FILE * const pFile, |
| const unsigned int nName, |
| const CPODData &n, |
| const unsigned int nEntries, |
| const bool bValidData) |
| { |
| if(!WriteMarker(pFile, nName, false)) return false; |
| if(!WriteData32(pFile, ePODFileDataType, &n.eType)) return false; |
| if(!WriteData32(pFile, ePODFileN, &n.n)) return false; |
| if(!WriteData32(pFile, ePODFileStride, &n.nStride)) return false; |
| if(bValidData) |
| { |
| switch(PVRTModelPODDataTypeSize(n.eType)) |
| { |
| case 1: if(!WriteData(pFile, ePODFileData, n.pData, nEntries * n.nStride)) return false; break; |
| case 2: if(!WriteData16(pFile, ePODFileData, n.pData, nEntries * (n.nStride / 2))) return false; break; |
| case 4: if(!WriteData32(pFile, ePODFileData, n.pData, nEntries * (n.nStride / 4))) return false; break; |
| default: { _ASSERT(false); } |
| }; |
| } |
| else |
| { |
| unsigned int offset = (unsigned int) (size_t) n.pData; |
| if(!WriteData32(pFile, ePODFileData, &offset)) return false; |
| } |
| if(!WriteMarker(pFile, nName, true)) return false; |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function WriteInterleaved |
| @Input pFile |
| @Input mesh |
| @Return true if successful |
| @Description Write out the interleaved data to file. |
| *****************************************************************************/ |
| static bool WriteInterleaved(FILE * const pFile, SPODMesh &mesh) |
| { |
| if(!mesh.pInterleaved) |
| return true; |
| |
| unsigned int i; |
| unsigned int ui32CPODDataSize = 0; |
| CPODData **pCPODData = new CPODData*[7 + mesh.nNumUVW]; |
| |
| if(mesh.sVertex.n) pCPODData[ui32CPODDataSize++] = &mesh.sVertex; |
| if(mesh.sNormals.n) pCPODData[ui32CPODDataSize++] = &mesh.sNormals; |
| if(mesh.sTangents.n) pCPODData[ui32CPODDataSize++] = &mesh.sTangents; |
| if(mesh.sBinormals.n) pCPODData[ui32CPODDataSize++] = &mesh.sBinormals; |
| if(mesh.sVtxColours.n) pCPODData[ui32CPODDataSize++] = &mesh.sVtxColours; |
| if(mesh.sBoneIdx.n) pCPODData[ui32CPODDataSize++] = &mesh.sBoneIdx; |
| if(mesh.sBoneWeight.n) pCPODData[ui32CPODDataSize++] = &mesh.sBoneWeight; |
| |
| for(i = 0; i < mesh.nNumUVW; ++i) |
| if(mesh.psUVW[i].n) pCPODData[ui32CPODDataSize++] = &mesh.psUVW[i]; |
| |
| // Bubble sort pCPODData based on the vertex element offsets |
| bool bSwap = true; |
| unsigned int ui32Size = ui32CPODDataSize; |
| |
| while(bSwap) |
| { |
| bSwap = false; |
| |
| for(i = 0; i < ui32Size - 1; ++i) |
| { |
| if(pCPODData[i]->pData > pCPODData[i + 1]->pData) |
| { |
| PVRTswap(pCPODData[i], pCPODData[i + 1]); |
| bSwap = true; |
| } |
| } |
| |
| --ui32Size; |
| } |
| |
| // Write out the data |
| if(!WriteMarker(pFile, ePODFileMeshInterleaved, false, mesh.nNumVertex * mesh.sVertex.nStride)) return false; |
| |
| for(i = 0; i < mesh.nNumVertex; ++i) |
| { |
| unsigned char* pVtxStart = mesh.pInterleaved + (i * mesh.sVertex.nStride); |
| |
| for(unsigned int j = 0; j < ui32CPODDataSize; ++j) |
| { |
| unsigned char* pData = pVtxStart + (size_t) pCPODData[j]->pData; |
| |
| switch(PVRTModelPODDataTypeSize(pCPODData[j]->eType)) |
| { |
| case 1: if(!WriteFileSafe(pFile, pData, pCPODData[j]->n)) return false; break; |
| case 2: if(!WriteFileSafe16(pFile, (unsigned short*) pData, pCPODData[j]->n)) return false; break; |
| case 4: if(!WriteFileSafe32(pFile, (unsigned int*) pData, pCPODData[j]->n)) return false; break; |
| default: { _ASSERT(false); } |
| }; |
| |
| // Write out the padding |
| size_t padding; |
| |
| if(j != ui32CPODDataSize - 1) |
| padding = ((size_t)pCPODData[j + 1]->pData - (size_t)pCPODData[j]->pData) - PVRTModelPODDataStride(*pCPODData[j]); |
| else |
| padding = (pCPODData[j]->nStride - (size_t)pCPODData[j]->pData) - PVRTModelPODDataStride(*pCPODData[j]); |
| |
| fwrite("\0\0\0\0", padding, 1, pFile); |
| } |
| } |
| |
| if(!WriteMarker(pFile, ePODFileMeshInterleaved, true)) return false; |
| |
| // Delete our CPOD data array |
| delete[] pCPODData; |
| |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODGetAnimArraySize |
| @Input pAnimDataIdx |
| @Input ui32Frames |
| @Input ui32Components |
| @Return Size of the animation array |
| @Description Calculates the size of an animation array |
| *****************************************************************************/ |
| PVRTuint32 PVRTModelPODGetAnimArraySize(PVRTuint32 *pAnimDataIdx, PVRTuint32 ui32Frames, PVRTuint32 ui32Components) |
| { |
| if(pAnimDataIdx) |
| { |
| // Find the largest index value |
| PVRTuint32 ui32Max = 0; |
| for(unsigned int i = 0; i < ui32Frames; ++i) |
| { |
| if(ui32Max < pAnimDataIdx[i]) |
| ui32Max = pAnimDataIdx[i]; |
| } |
| |
| return ui32Max + ui32Components; |
| } |
| |
| return ui32Frames * ui32Components; |
| } |
| |
| /*!*************************************************************************** |
| @Function WritePOD |
| @Output The file referenced by pFile |
| @Input s The POD Scene to write |
| @Input pszExpOpt Exporter options |
| @Return true if successful |
| @Description Write a POD file |
| *****************************************************************************/ |
| static bool WritePOD( |
| FILE * const pFile, |
| const char * const pszExpOpt, |
| const char * const pszHistory, |
| const SPODScene &s) |
| { |
| unsigned int i, j; |
| |
| // Save: file version |
| { |
| char *pszVersion = (char*)PVRTMODELPOD_VERSION; |
| |
| if(!WriteData(pFile, ePODFileVersion, pszVersion, (unsigned int)strlen(pszVersion) + 1)) return false; |
| } |
| |
| // Save: exporter options |
| if(pszExpOpt && *pszExpOpt) |
| { |
| if(!WriteData(pFile, ePODFileExpOpt, pszExpOpt, (unsigned int)strlen(pszExpOpt) + 1)) return false; |
| } |
| |
| // Save: .pod file history |
| if(pszHistory && *pszHistory) |
| { |
| if(!WriteData(pFile, ePODFileHistory, pszHistory, (unsigned int)strlen(pszHistory) + 1)) return false; |
| } |
| |
| // Save: scene descriptor |
| if(!WriteMarker(pFile, ePODFileScene, false)) return false; |
| |
| { |
| if(!WriteData32(pFile, ePODFileUnits, &s.fUnits)) return false; |
| if(!WriteData32(pFile, ePODFileColourBackground, s.pfColourBackground, sizeof(s.pfColourBackground) / sizeof(*s.pfColourBackground))) return false; |
| if(!WriteData32(pFile, ePODFileColourAmbient, s.pfColourAmbient, sizeof(s.pfColourAmbient) / sizeof(*s.pfColourAmbient))) return false; |
| if(!WriteData32(pFile, ePODFileNumCamera, &s.nNumCamera)) return false; |
| if(!WriteData32(pFile, ePODFileNumLight, &s.nNumLight)) return false; |
| if(!WriteData32(pFile, ePODFileNumMesh, &s.nNumMesh)) return false; |
| if(!WriteData32(pFile, ePODFileNumNode, &s.nNumNode)) return false; |
| if(!WriteData32(pFile, ePODFileNumMeshNode, &s.nNumMeshNode)) return false; |
| if(!WriteData32(pFile, ePODFileNumTexture, &s.nNumTexture)) return false; |
| if(!WriteData32(pFile, ePODFileNumMaterial, &s.nNumMaterial)) return false; |
| if(!WriteData32(pFile, ePODFileNumFrame, &s.nNumFrame)) return false; |
| |
| if(s.nNumFrame) |
| { |
| if(!WriteData32(pFile, ePODFileFPS, &s.nFPS)) return false; |
| } |
| |
| if(!WriteData32(pFile, ePODFileFlags, &s.nFlags)) return false; |
| if(!WriteData(pFile, ePODFileUserData, s.pUserData, s.nUserDataSize)) return false; |
| |
| // Save: cameras |
| for(i = 0; i < s.nNumCamera; ++i) |
| { |
| if(!WriteMarker(pFile, ePODFileCamera, false)) return false; |
| if(!WriteData32(pFile, ePODFileCamIdxTgt, &s.pCamera[i].nIdxTarget)) return false; |
| if(!WriteData32(pFile, ePODFileCamFOV, &s.pCamera[i].fFOV)) return false; |
| if(!WriteData32(pFile, ePODFileCamFar, &s.pCamera[i].fFar)) return false; |
| if(!WriteData32(pFile, ePODFileCamNear, &s.pCamera[i].fNear)) return false; |
| if(!WriteData32(pFile, ePODFileCamAnimFOV, s.pCamera[i].pfAnimFOV, s.nNumFrame)) return false; |
| if(!WriteMarker(pFile, ePODFileCamera, true)) return false; |
| } |
| // Save: lights |
| for(i = 0; i < s.nNumLight; ++i) |
| { |
| if(!WriteMarker(pFile, ePODFileLight, false)) return false; |
| if(!WriteData32(pFile, ePODFileLightIdxTgt, &s.pLight[i].nIdxTarget)) return false; |
| if(!WriteData32(pFile, ePODFileLightColour, s.pLight[i].pfColour, sizeof(s.pLight[i].pfColour) / sizeof(*s.pLight[i].pfColour))) return false; |
| if(!WriteData32(pFile, ePODFileLightType, &s.pLight[i].eType)) return false; |
| |
| if(s.pLight[i].eType != ePODDirectional) |
| { |
| if(!WriteData32(pFile, ePODFileLightConstantAttenuation, &s.pLight[i].fConstantAttenuation)) return false; |
| if(!WriteData32(pFile, ePODFileLightLinearAttenuation, &s.pLight[i].fLinearAttenuation)) return false; |
| if(!WriteData32(pFile, ePODFileLightQuadraticAttenuation, &s.pLight[i].fQuadraticAttenuation)) return false; |
| } |
| |
| if(s.pLight[i].eType == ePODSpot) |
| { |
| if(!WriteData32(pFile, ePODFileLightFalloffAngle, &s.pLight[i].fFalloffAngle)) return false; |
| if(!WriteData32(pFile, ePODFileLightFalloffExponent, &s.pLight[i].fFalloffExponent)) return false; |
| } |
| |
| if(!WriteMarker(pFile, ePODFileLight, true)) return false; |
| } |
| |
| // Save: materials |
| for(i = 0; i < s.nNumMaterial; ++i) |
| { |
| if(!WriteMarker(pFile, ePODFileMaterial, false)) return false; |
| |
| if(!WriteData32(pFile, ePODFileMatFlags, &s.pMaterial[i].nFlags)) return false; |
| if(!WriteData(pFile, ePODFileMatName, s.pMaterial[i].pszName, (unsigned int)strlen(s.pMaterial[i].pszName)+1)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexDiffuse, &s.pMaterial[i].nIdxTexDiffuse)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexAmbient, &s.pMaterial[i].nIdxTexAmbient)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexSpecularColour, &s.pMaterial[i].nIdxTexSpecularColour)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexSpecularLevel, &s.pMaterial[i].nIdxTexSpecularLevel)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexBump, &s.pMaterial[i].nIdxTexBump)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexEmissive, &s.pMaterial[i].nIdxTexEmissive)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexGlossiness, &s.pMaterial[i].nIdxTexGlossiness)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexOpacity, &s.pMaterial[i].nIdxTexOpacity)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexReflection, &s.pMaterial[i].nIdxTexReflection)) return false; |
| if(!WriteData32(pFile, ePODFileMatIdxTexRefraction, &s.pMaterial[i].nIdxTexRefraction)) return false; |
| if(!WriteData32(pFile, ePODFileMatOpacity, &s.pMaterial[i].fMatOpacity)) return false; |
| if(!WriteData32(pFile, ePODFileMatAmbient, s.pMaterial[i].pfMatAmbient, sizeof(s.pMaterial[i].pfMatAmbient) / sizeof(*s.pMaterial[i].pfMatAmbient))) return false; |
| if(!WriteData32(pFile, ePODFileMatDiffuse, s.pMaterial[i].pfMatDiffuse, sizeof(s.pMaterial[i].pfMatDiffuse) / sizeof(*s.pMaterial[i].pfMatDiffuse))) return false; |
| if(!WriteData32(pFile, ePODFileMatSpecular, s.pMaterial[i].pfMatSpecular, sizeof(s.pMaterial[i].pfMatSpecular) / sizeof(*s.pMaterial[i].pfMatSpecular))) return false; |
| if(!WriteData32(pFile, ePODFileMatShininess, &s.pMaterial[i].fMatShininess)) return false; |
| if(!WriteData(pFile, ePODFileMatEffectFile, s.pMaterial[i].pszEffectFile, s.pMaterial[i].pszEffectFile ? ((unsigned int)strlen(s.pMaterial[i].pszEffectFile)+1) : 0)) return false; |
| if(!WriteData(pFile, ePODFileMatEffectName, s.pMaterial[i].pszEffectName, s.pMaterial[i].pszEffectName ? ((unsigned int)strlen(s.pMaterial[i].pszEffectName)+1) : 0)) return false; |
| if(!WriteData32(pFile, ePODFileMatBlendSrcRGB, &s.pMaterial[i].eBlendSrcRGB))return false; |
| if(!WriteData32(pFile, ePODFileMatBlendSrcA, &s.pMaterial[i].eBlendSrcA)) return false; |
| if(!WriteData32(pFile, ePODFileMatBlendDstRGB, &s.pMaterial[i].eBlendDstRGB))return false; |
| if(!WriteData32(pFile, ePODFileMatBlendDstA, &s.pMaterial[i].eBlendDstA)) return false; |
| if(!WriteData32(pFile, ePODFileMatBlendOpRGB, &s.pMaterial[i].eBlendOpRGB)) return false; |
| if(!WriteData32(pFile, ePODFileMatBlendOpA, &s.pMaterial[i].eBlendOpA)) return false; |
| if(!WriteData32(pFile, ePODFileMatBlendColour, s.pMaterial[i].pfBlendColour, sizeof(s.pMaterial[i].pfBlendColour) / sizeof(*s.pMaterial[i].pfBlendColour))) return false; |
| if(!WriteData32(pFile, ePODFileMatBlendFactor, s.pMaterial[i].pfBlendFactor, sizeof(s.pMaterial[i].pfBlendFactor) / sizeof(*s.pMaterial[i].pfBlendFactor))) return false; |
| if(!WriteData(pFile, ePODFileMatUserData, s.pMaterial[i].pUserData, s.pMaterial[i].nUserDataSize)) return false; |
| |
| if(!WriteMarker(pFile, ePODFileMaterial, true)) return false; |
| } |
| |
| // Save: meshes |
| for(i = 0; i < s.nNumMesh; ++i) |
| { |
| if(!WriteMarker(pFile, ePODFileMesh, false)) return false; |
| |
| if(!WriteData32(pFile, ePODFileMeshNumVtx, &s.pMesh[i].nNumVertex)) return false; |
| if(!WriteData32(pFile, ePODFileMeshNumFaces, &s.pMesh[i].nNumFaces)) return false; |
| if(!WriteData32(pFile, ePODFileMeshNumUVW, &s.pMesh[i].nNumUVW)) return false; |
| if(!WriteData32(pFile, ePODFileMeshStripLength, s.pMesh[i].pnStripLength, s.pMesh[i].nNumStrips)) return false; |
| if(!WriteData32(pFile, ePODFileMeshNumStrips, &s.pMesh[i].nNumStrips)) return false; |
| if(!WriteInterleaved(pFile, s.pMesh[i])) return false; |
| if(!WriteData32(pFile, ePODFileMeshBoneBatchBoneMax,&s.pMesh[i].sBoneBatches.nBatchBoneMax)) return false; |
| if(!WriteData32(pFile, ePODFileMeshBoneBatchCnt, &s.pMesh[i].sBoneBatches.nBatchCnt)) return false; |
| if(!WriteData32(pFile, ePODFileMeshBoneBatches, s.pMesh[i].sBoneBatches.pnBatches, s.pMesh[i].sBoneBatches.nBatchBoneMax * s.pMesh[i].sBoneBatches.nBatchCnt)) return false; |
| if(!WriteData32(pFile, ePODFileMeshBoneBatchBoneCnts, s.pMesh[i].sBoneBatches.pnBatchBoneCnt, s.pMesh[i].sBoneBatches.nBatchCnt)) return false; |
| if(!WriteData32(pFile, ePODFileMeshBoneBatchOffsets, s.pMesh[i].sBoneBatches.pnBatchOffset,s.pMesh[i].sBoneBatches.nBatchCnt)) return false; |
| if(!WriteData32(pFile, ePODFileMeshUnpackMatrix, s.pMesh[i].mUnpackMatrix.f, 16)) return false; |
| |
| if(!WriteCPODData(pFile, ePODFileMeshFaces, s.pMesh[i].sFaces, PVRTModelPODCountIndices(s.pMesh[i]), true)) return false; |
| if(!WriteCPODData(pFile, ePODFileMeshVtx, s.pMesh[i].sVertex, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| if(!WriteCPODData(pFile, ePODFileMeshNor, s.pMesh[i].sNormals, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| if(!WriteCPODData(pFile, ePODFileMeshTan, s.pMesh[i].sTangents, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| if(!WriteCPODData(pFile, ePODFileMeshBin, s.pMesh[i].sBinormals, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| |
| for(j = 0; j < s.pMesh[i].nNumUVW; ++j) |
| if(!WriteCPODData(pFile, ePODFileMeshUVW, s.pMesh[i].psUVW[j], s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| |
| if(!WriteCPODData(pFile, ePODFileMeshVtxCol, s.pMesh[i].sVtxColours, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| if(!WriteCPODData(pFile, ePODFileMeshBoneIdx, s.pMesh[i].sBoneIdx, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| if(!WriteCPODData(pFile, ePODFileMeshBoneWeight, s.pMesh[i].sBoneWeight, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false; |
| |
| if(!WriteMarker(pFile, ePODFileMesh, true)) return false; |
| } |
| |
| int iTransformationNo; |
| // Save: node |
| for(i = 0; i < s.nNumNode; ++i) |
| { |
| if(!WriteMarker(pFile, ePODFileNode, false)) return false; |
| |
| { |
| if(!WriteData32(pFile, ePODFileNodeIdx, &s.pNode[i].nIdx)) return false; |
| if(!WriteData(pFile, ePODFileNodeName, s.pNode[i].pszName, (unsigned int)strlen(s.pNode[i].pszName)+1)) return false; |
| if(!WriteData32(pFile, ePODFileNodeIdxMat, &s.pNode[i].nIdxMaterial)) return false; |
| if(!WriteData32(pFile, ePODFileNodeIdxParent, &s.pNode[i].nIdxParent)) return false; |
| if(!WriteData32(pFile, ePODFileNodeAnimFlags, &s.pNode[i].nAnimFlags)) return false; |
| |
| if(s.pNode[i].pnAnimPositionIdx) |
| { |
| if(!WriteData32(pFile, ePODFileNodeAnimPosIdx, s.pNode[i].pnAnimPositionIdx, s.nNumFrame)) return false; |
| } |
| |
| iTransformationNo = s.pNode[i].nAnimFlags & ePODHasPositionAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimPositionIdx, s.nNumFrame, 3) : 3; |
| if(!WriteData32(pFile, ePODFileNodeAnimPos, s.pNode[i].pfAnimPosition, iTransformationNo)) return false; |
| |
| if(s.pNode[i].pnAnimRotationIdx) |
| { |
| if(!WriteData32(pFile, ePODFileNodeAnimRotIdx, s.pNode[i].pnAnimRotationIdx, s.nNumFrame)) return false; |
| } |
| |
| iTransformationNo = s.pNode[i].nAnimFlags & ePODHasRotationAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimRotationIdx, s.nNumFrame, 4) : 4; |
| if(!WriteData32(pFile, ePODFileNodeAnimRot, s.pNode[i].pfAnimRotation, iTransformationNo)) return false; |
| |
| if(s.pNode[i].pnAnimScaleIdx) |
| { |
| if(!WriteData32(pFile, ePODFileNodeAnimScaleIdx, s.pNode[i].pnAnimScaleIdx, s.nNumFrame)) return false; |
| } |
| |
| iTransformationNo = s.pNode[i].nAnimFlags & ePODHasScaleAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimScaleIdx, s.nNumFrame, 7) : 7; |
| if(!WriteData32(pFile, ePODFileNodeAnimScale, s.pNode[i].pfAnimScale, iTransformationNo)) return false; |
| |
| if(s.pNode[i].pnAnimMatrixIdx) |
| { |
| if(!WriteData32(pFile, ePODFileNodeAnimMatrixIdx, s.pNode[i].pnAnimMatrixIdx, s.nNumFrame)) return false; |
| } |
| |
| iTransformationNo = s.pNode[i].nAnimFlags & ePODHasMatrixAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimMatrixIdx, s.nNumFrame, 16) : 16; |
| if(!WriteData32(pFile, ePODFileNodeAnimMatrix,s.pNode[i].pfAnimMatrix, iTransformationNo)) return false; |
| |
| if(!WriteData(pFile, ePODFileNodeUserData, s.pNode[i].pUserData, s.pNode[i].nUserDataSize)) return false; |
| } |
| |
| if(!WriteMarker(pFile, ePODFileNode, true)) return false; |
| } |
| |
| // Save: texture |
| for(i = 0; i < s.nNumTexture; ++i) |
| { |
| if(!WriteMarker(pFile, ePODFileTexture, false)) return false; |
| if(!WriteData(pFile, ePODFileTexName, s.pTexture[i].pszName, (unsigned int)strlen(s.pTexture[i].pszName)+1)) return false; |
| if(!WriteMarker(pFile, ePODFileTexture, true)) return false; |
| } |
| } |
| if(!WriteMarker(pFile, ePODFileScene, true)) return false; |
| |
| return true; |
| } |
| |
| /**************************************************************************** |
| ** Local code: File reading |
| ****************************************************************************/ |
| /*!*************************************************************************** |
| @Function ReadCPODData |
| @Modified s The CPODData to read into |
| @Input src CSource object to read data from. |
| @Input nSpec |
| @Input bValidData |
| @Return true if successful |
| @Description Read a CPODData block in from a pod file |
| *****************************************************************************/ |
| static bool ReadCPODData( |
| CPODData &s, |
| CSource &src, |
| const unsigned int nSpec, |
| const bool bValidData) |
| { |
| unsigned int nName, nLen, nBuff; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| if(nName == (nSpec | PVRTMODELPOD_TAG_END)) |
| return true; |
| |
| switch(nName) |
| { |
| case ePODFileDataType: if(!src.Read32(s.eType)) return false; break; |
| case ePODFileN: if(!src.Read32(s.n)) return false; break; |
| case ePODFileStride: if(!src.Read32(s.nStride)) return false; break; |
| case ePODFileData: |
| if(bValidData) |
| { |
| switch(PVRTModelPODDataTypeSize(s.eType)) |
| { |
| case 1: if(!src.ReadAfterAlloc(s.pData, nLen)) return false; break; |
| case 2: |
| { // reading 16bit data but have 8bit pointer |
| PVRTuint16 *p16Pointer=NULL; |
| if(!src.ReadAfterAlloc16(p16Pointer, nLen)) return false; |
| s.pData = (unsigned char*)p16Pointer; |
| break; |
| } |
| case 4: |
| { // reading 32bit data but have 8bit pointer |
| PVRTuint32 *p32Pointer=NULL; |
| if(!src.ReadAfterAlloc32(p32Pointer, nLen)) return false; |
| s.pData = (unsigned char*)p32Pointer; |
| break; |
| } |
| default: |
| { _ASSERT(false);} |
| } |
| } |
| else |
| { |
| if(src.Read32(nBuff)) |
| { |
| s.pData = (unsigned char*) (size_t) nBuff; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| break; |
| |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadCamera |
| @Modified s The SPODCamera to read into |
| @Input src CSource object to read data from. |
| @Return true if successful |
| @Description Read a camera block in from a pod file |
| *****************************************************************************/ |
| static bool ReadCamera( |
| SPODCamera &s, |
| CSource &src) |
| { |
| unsigned int nName, nLen; |
| s.pfAnimFOV = 0; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileCamera | PVRTMODELPOD_TAG_END: return true; |
| |
| case ePODFileCamIdxTgt: if(!src.Read32(s.nIdxTarget)) return false; break; |
| case ePODFileCamFOV: if(!src.Read32(s.fFOV)) return false; break; |
| case ePODFileCamFar: if(!src.Read32(s.fFar)) return false; break; |
| case ePODFileCamNear: if(!src.Read32(s.fNear)) return false; break; |
| case ePODFileCamAnimFOV: if(!src.ReadAfterAlloc32(s.pfAnimFOV, nLen)) return false; break; |
| |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadLight |
| @Modified s The SPODLight to read into |
| @Input src CSource object to read data from. |
| @Return true if successful |
| @Description Read a light block in from a pod file |
| *****************************************************************************/ |
| static bool ReadLight( |
| SPODLight &s, |
| CSource &src) |
| { |
| unsigned int nName, nLen; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileLight | PVRTMODELPOD_TAG_END: return true; |
| |
| case ePODFileLightIdxTgt: if(!src.Read32(s.nIdxTarget)) return false; break; |
| case ePODFileLightColour: if(!src.ReadArray32(s.pfColour, 3)) return false; break; |
| case ePODFileLightType: if(!src.Read32(s.eType)) return false; break; |
| case ePODFileLightConstantAttenuation: if(!src.Read32(s.fConstantAttenuation)) return false; break; |
| case ePODFileLightLinearAttenuation: if(!src.Read32(s.fLinearAttenuation)) return false; break; |
| case ePODFileLightQuadraticAttenuation: if(!src.Read32(s.fQuadraticAttenuation)) return false; break; |
| case ePODFileLightFalloffAngle: if(!src.Read32(s.fFalloffAngle)) return false; break; |
| case ePODFileLightFalloffExponent: if(!src.Read32(s.fFalloffExponent)) return false; break; |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadMaterial |
| @Modified s The SPODMaterial to read into |
| @Input src CSource object to read data from. |
| @Return true if successful |
| @Description Read a material block in from a pod file |
| *****************************************************************************/ |
| static bool ReadMaterial( |
| SPODMaterial &s, |
| CSource &src) |
| { |
| unsigned int nName, nLen; |
| |
| // Set texture IDs to -1 |
| s.nIdxTexDiffuse = -1; |
| s.nIdxTexAmbient = -1; |
| s.nIdxTexSpecularColour = -1; |
| s.nIdxTexSpecularLevel = -1; |
| s.nIdxTexBump = -1; |
| s.nIdxTexEmissive = -1; |
| s.nIdxTexGlossiness = -1; |
| s.nIdxTexOpacity = -1; |
| s.nIdxTexReflection = -1; |
| s.nIdxTexRefraction = -1; |
| |
| // Set defaults for blend modes |
| s.eBlendSrcRGB = s.eBlendSrcA = ePODBlendFunc_ONE; |
| s.eBlendDstRGB = s.eBlendDstA = ePODBlendFunc_ZERO; |
| s.eBlendOpRGB = s.eBlendOpA = ePODBlendOp_ADD; |
| |
| memset(s.pfBlendColour, 0, sizeof(s.pfBlendColour)); |
| memset(s.pfBlendFactor, 0, sizeof(s.pfBlendFactor)); |
| |
| // Set default for material flags |
| s.nFlags = 0; |
| |
| // Set default for user data |
| s.pUserData = 0; |
| s.nUserDataSize = 0; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileMaterial | PVRTMODELPOD_TAG_END: return true; |
| |
| case ePODFileMatFlags: if(!src.Read32(s.nFlags)) return false; break; |
| case ePODFileMatName: if(!src.ReadAfterAlloc(s.pszName, nLen)) return false; break; |
| case ePODFileMatIdxTexDiffuse: if(!src.Read32(s.nIdxTexDiffuse)) return false; break; |
| case ePODFileMatIdxTexAmbient: if(!src.Read32(s.nIdxTexAmbient)) return false; break; |
| case ePODFileMatIdxTexSpecularColour: if(!src.Read32(s.nIdxTexSpecularColour)) return false; break; |
| case ePODFileMatIdxTexSpecularLevel: if(!src.Read32(s.nIdxTexSpecularLevel)) return false; break; |
| case ePODFileMatIdxTexBump: if(!src.Read32(s.nIdxTexBump)) return false; break; |
| case ePODFileMatIdxTexEmissive: if(!src.Read32(s.nIdxTexEmissive)) return false; break; |
| case ePODFileMatIdxTexGlossiness: if(!src.Read32(s.nIdxTexGlossiness)) return false; break; |
| case ePODFileMatIdxTexOpacity: if(!src.Read32(s.nIdxTexOpacity)) return false; break; |
| case ePODFileMatIdxTexReflection: if(!src.Read32(s.nIdxTexReflection)) return false; break; |
| case ePODFileMatIdxTexRefraction: if(!src.Read32(s.nIdxTexRefraction)) return false; break; |
| case ePODFileMatOpacity: if(!src.Read32(s.fMatOpacity)) return false; break; |
| case ePODFileMatAmbient: if(!src.ReadArray32(s.pfMatAmbient, sizeof(s.pfMatAmbient) / sizeof(*s.pfMatAmbient))) return false; break; |
| case ePODFileMatDiffuse: if(!src.ReadArray32(s.pfMatDiffuse, sizeof(s.pfMatDiffuse) / sizeof(*s.pfMatDiffuse))) return false; break; |
| case ePODFileMatSpecular: if(!src.ReadArray32(s.pfMatSpecular, sizeof(s.pfMatSpecular) / sizeof(*s.pfMatSpecular))) return false; break; |
| case ePODFileMatShininess: if(!src.Read32(s.fMatShininess)) return false; break; |
| case ePODFileMatEffectFile: if(!src.ReadAfterAlloc(s.pszEffectFile, nLen)) return false; break; |
| case ePODFileMatEffectName: if(!src.ReadAfterAlloc(s.pszEffectName, nLen)) return false; break; |
| case ePODFileMatBlendSrcRGB: if(!src.Read32(s.eBlendSrcRGB)) return false; break; |
| case ePODFileMatBlendSrcA: if(!src.Read32(s.eBlendSrcA)) return false; break; |
| case ePODFileMatBlendDstRGB: if(!src.Read32(s.eBlendDstRGB)) return false; break; |
| case ePODFileMatBlendDstA: if(!src.Read32(s.eBlendDstA)) return false; break; |
| case ePODFileMatBlendOpRGB: if(!src.Read32(s.eBlendOpRGB)) return false; break; |
| case ePODFileMatBlendOpA: if(!src.Read32(s.eBlendOpA)) return false; break; |
| case ePODFileMatBlendColour: if(!src.ReadArray32(s.pfBlendColour, sizeof(s.pfBlendColour) / sizeof(*s.pfBlendColour))) return false; break; |
| case ePODFileMatBlendFactor: if(!src.ReadArray32(s.pfBlendFactor, sizeof(s.pfBlendFactor) / sizeof(*s.pfBlendFactor))) return false; break; |
| |
| case ePODFileMatUserData: |
| if(!src.ReadAfterAlloc(s.pUserData, nLen)) |
| return false; |
| else |
| { |
| s.nUserDataSize = nLen; |
| break; |
| } |
| |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTFixInterleavedEndiannessUsingCPODData |
| @Modified pInterleaved - The interleaved data |
| @Input data - The CPODData. |
| @Return ui32Size - Number of elements in pInterleaved |
| @Description Called multiple times and goes through the interleaved data |
| correcting the endianness. |
| *****************************************************************************/ |
| static void PVRTFixInterleavedEndiannessUsingCPODData(unsigned char* pInterleaved, CPODData &data, unsigned int ui32Size) |
| { |
| if(!data.n) |
| return; |
| |
| size_t ui32TypeSize = PVRTModelPODDataTypeSize(data.eType); |
| |
| unsigned char ub[4]; |
| unsigned char *pData = pInterleaved + (size_t) data.pData; |
| |
| switch(ui32TypeSize) |
| { |
| case 1: return; |
| case 2: |
| { |
| for(unsigned int i = 0; i < ui32Size; ++i) |
| { |
| for(unsigned int j = 0; j < data.n; ++j) |
| { |
| ub[0] = pData[ui32TypeSize * j + 0]; |
| ub[1] = pData[ui32TypeSize * j + 1]; |
| |
| ((unsigned short*) pData)[j] = (unsigned short) ((ub[1] << 8) | ub[0]); |
| } |
| |
| pData += data.nStride; |
| } |
| } |
| break; |
| case 4: |
| { |
| for(unsigned int i = 0; i < ui32Size; ++i) |
| { |
| for(unsigned int j = 0; j < data.n; ++j) |
| { |
| ub[0] = pData[ui32TypeSize * j + 0]; |
| ub[1] = pData[ui32TypeSize * j + 1]; |
| ub[2] = pData[ui32TypeSize * j + 2]; |
| ub[3] = pData[ui32TypeSize * j + 3]; |
| |
| ((unsigned int*) pData)[j] = (unsigned int) ((ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0]); |
| } |
| |
| pData += data.nStride; |
| } |
| } |
| break; |
| default: { _ASSERT(false); } |
| }; |
| } |
| |
| static void PVRTFixInterleavedEndianness(SPODMesh &s) |
| { |
| if(!s.pInterleaved || PVRTIsLittleEndian()) |
| return; |
| |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sVertex, s.nNumVertex); |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sNormals, s.nNumVertex); |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sTangents, s.nNumVertex); |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sBinormals, s.nNumVertex); |
| |
| for(unsigned int i = 0; i < s.nNumUVW; ++i) |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.psUVW[i], s.nNumVertex); |
| |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sVtxColours, s.nNumVertex); |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sBoneIdx, s.nNumVertex); |
| PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sBoneWeight, s.nNumVertex); |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadMesh |
| @Modified s The SPODMesh to read into |
| @Input src CSource object to read data from. |
| @Return true if successful |
| @Description Read a mesh block in from a pod file |
| *****************************************************************************/ |
| static bool ReadMesh( |
| SPODMesh &s, |
| CSource &src) |
| { |
| unsigned int nName, nLen; |
| unsigned int nUVWs=0; |
| |
| PVRTMatrixIdentity(s.mUnpackMatrix); |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileMesh | PVRTMODELPOD_TAG_END: |
| if(nUVWs != s.nNumUVW) |
| return false; |
| PVRTFixInterleavedEndianness(s); |
| return true; |
| |
| case ePODFileMeshNumVtx: if(!src.Read32(s.nNumVertex)) return false; break; |
| case ePODFileMeshNumFaces: if(!src.Read32(s.nNumFaces)) return false; break; |
| case ePODFileMeshNumUVW: if(!src.Read32(s.nNumUVW)) return false; if(!SafeAlloc(s.psUVW, s.nNumUVW)) return false; break; |
| case ePODFileMeshStripLength: if(!src.ReadAfterAlloc32(s.pnStripLength, nLen)) return false; break; |
| case ePODFileMeshNumStrips: if(!src.Read32(s.nNumStrips)) return false; break; |
| case ePODFileMeshInterleaved: if(!src.ReadAfterAlloc(s.pInterleaved, nLen)) return false; break; |
| case ePODFileMeshBoneBatches: if(!src.ReadAfterAlloc32(s.sBoneBatches.pnBatches, nLen)) return false; break; |
| case ePODFileMeshBoneBatchBoneCnts: if(!src.ReadAfterAlloc32(s.sBoneBatches.pnBatchBoneCnt, nLen)) return false; break; |
| case ePODFileMeshBoneBatchOffsets: if(!src.ReadAfterAlloc32(s.sBoneBatches.pnBatchOffset, nLen)) return false; break; |
| case ePODFileMeshBoneBatchBoneMax: if(!src.Read32(s.sBoneBatches.nBatchBoneMax)) return false; break; |
| case ePODFileMeshBoneBatchCnt: if(!src.Read32(s.sBoneBatches.nBatchCnt)) return false; break; |
| case ePODFileMeshUnpackMatrix: if(!src.ReadArray32(&s.mUnpackMatrix.f[0], 16)) return false; break; |
| |
| case ePODFileMeshFaces: if(!ReadCPODData(s.sFaces, src, ePODFileMeshFaces, true)) return false; break; |
| case ePODFileMeshVtx: if(!ReadCPODData(s.sVertex, src, ePODFileMeshVtx, s.pInterleaved == 0)) return false; break; |
| case ePODFileMeshNor: if(!ReadCPODData(s.sNormals, src, ePODFileMeshNor, s.pInterleaved == 0)) return false; break; |
| case ePODFileMeshTan: if(!ReadCPODData(s.sTangents, src, ePODFileMeshTan, s.pInterleaved == 0)) return false; break; |
| case ePODFileMeshBin: if(!ReadCPODData(s.sBinormals, src, ePODFileMeshBin, s.pInterleaved == 0)) return false; break; |
| case ePODFileMeshUVW: if(!ReadCPODData(s.psUVW[nUVWs++], src, ePODFileMeshUVW, s.pInterleaved == 0)) return false; break; |
| case ePODFileMeshVtxCol: if(!ReadCPODData(s.sVtxColours, src, ePODFileMeshVtxCol, s.pInterleaved == 0)) return false; break; |
| case ePODFileMeshBoneIdx: if(!ReadCPODData(s.sBoneIdx, src, ePODFileMeshBoneIdx, s.pInterleaved == 0)) return false; break; |
| case ePODFileMeshBoneWeight: if(!ReadCPODData(s.sBoneWeight, src, ePODFileMeshBoneWeight, s.pInterleaved == 0)) return false; break; |
| |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadNode |
| @Modified s The SPODNode to read into |
| @Input src CSource object to read data from. |
| @Return true if successful |
| @Description Read a node block in from a pod file |
| *****************************************************************************/ |
| static bool ReadNode( |
| SPODNode &s, |
| CSource &src) |
| { |
| unsigned int nName, nLen; |
| bool bOldNodeFormat = false; |
| VERTTYPE fPos[3] = {0,0,0}; |
| VERTTYPE fQuat[4] = {0,0,0,f2vt(1)}; |
| VERTTYPE fScale[7] = {f2vt(1),f2vt(1),f2vt(1),0,0,0,0}; |
| |
| // Set default for user data |
| s.pUserData = 0; |
| s.nUserDataSize = 0; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileNode | PVRTMODELPOD_TAG_END: |
| if(bOldNodeFormat) |
| { |
| if(s.pfAnimPosition) |
| s.nAnimFlags |= ePODHasPositionAni; |
| else |
| { |
| s.pfAnimPosition = (VERTTYPE*) malloc(sizeof(fPos)); |
| memcpy(s.pfAnimPosition, fPos, sizeof(fPos)); |
| } |
| |
| if(s.pfAnimRotation) |
| s.nAnimFlags |= ePODHasRotationAni; |
| else |
| { |
| s.pfAnimRotation = (VERTTYPE*) malloc(sizeof(fQuat)); |
| memcpy(s.pfAnimRotation, fQuat, sizeof(fQuat)); |
| } |
| |
| if(s.pfAnimScale) |
| s.nAnimFlags |= ePODHasScaleAni; |
| else |
| { |
| s.pfAnimScale = (VERTTYPE*) malloc(sizeof(fScale)); |
| memcpy(s.pfAnimScale, fScale, sizeof(fScale)); |
| } |
| } |
| return true; |
| |
| case ePODFileNodeIdx: if(!src.Read32(s.nIdx)) return false; break; |
| case ePODFileNodeName: if(!src.ReadAfterAlloc(s.pszName, nLen)) return false; break; |
| case ePODFileNodeIdxMat: if(!src.Read32(s.nIdxMaterial)) return false; break; |
| case ePODFileNodeIdxParent: if(!src.Read32(s.nIdxParent)) return false; break; |
| case ePODFileNodeAnimFlags:if(!src.Read32(s.nAnimFlags))return false; break; |
| |
| case ePODFileNodeAnimPosIdx: if(!src.ReadAfterAlloc32(s.pnAnimPositionIdx, nLen)) return false; break; |
| case ePODFileNodeAnimPos: if(!src.ReadAfterAlloc32(s.pfAnimPosition, nLen)) return false; break; |
| |
| case ePODFileNodeAnimRotIdx: if(!src.ReadAfterAlloc32(s.pnAnimRotationIdx, nLen)) return false; break; |
| case ePODFileNodeAnimRot: if(!src.ReadAfterAlloc32(s.pfAnimRotation, nLen)) return false; break; |
| |
| case ePODFileNodeAnimScaleIdx: if(!src.ReadAfterAlloc32(s.pnAnimScaleIdx, nLen)) return false; break; |
| case ePODFileNodeAnimScale: if(!src.ReadAfterAlloc32(s.pfAnimScale, nLen)) return false; break; |
| |
| case ePODFileNodeAnimMatrixIdx: if(!src.ReadAfterAlloc32(s.pnAnimMatrixIdx, nLen)) return false; break; |
| case ePODFileNodeAnimMatrix:if(!src.ReadAfterAlloc32(s.pfAnimMatrix, nLen)) return false; break; |
| |
| case ePODFileNodeUserData: |
| if(!src.ReadAfterAlloc(s.pUserData, nLen)) |
| return false; |
| else |
| { |
| s.nUserDataSize = nLen; |
| break; |
| } |
| |
| // Parameters from the older pod format |
| case ePODFileNodePos: if(!src.ReadArray32(&fPos[0], 3)) return false; bOldNodeFormat = true; break; |
| case ePODFileNodeRot: if(!src.ReadArray32(&fQuat[0], 4)) return false; bOldNodeFormat = true; break; |
| case ePODFileNodeScale: if(!src.ReadArray32(&fScale[0], 3)) return false; bOldNodeFormat = true; break; |
| |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadTexture |
| @Modified s The SPODTexture to read into |
| @Input src CSource object to read data from. |
| @Return true if successful |
| @Description Read a texture block in from a pod file |
| *****************************************************************************/ |
| static bool ReadTexture( |
| SPODTexture &s, |
| CSource &src) |
| { |
| unsigned int nName, nLen; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileTexture | PVRTMODELPOD_TAG_END: return true; |
| |
| case ePODFileTexName: if(!src.ReadAfterAlloc(s.pszName, nLen)) return false; break; |
| |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadScene |
| @Modified s The SPODScene to read into |
| @Input src CSource object to read data from. |
| @Return true if successful |
| @Description Read a scene block in from a pod file |
| *****************************************************************************/ |
| static bool ReadScene( |
| SPODScene &s, |
| CSource &src) |
| { |
| unsigned int nName, nLen; |
| unsigned int nCameras=0, nLights=0, nMaterials=0, nMeshes=0, nTextures=0, nNodes=0; |
| s.nFPS = 30; |
| s.fUnits = 1.0f; |
| |
| // Set default for user data |
| s.pUserData = 0; |
| s.nUserDataSize = 0; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileScene | PVRTMODELPOD_TAG_END: |
| if(nCameras != s.nNumCamera) return false; |
| if(nLights != s.nNumLight) return false; |
| if(nMaterials != s.nNumMaterial) return false; |
| if(nMeshes != s.nNumMesh) return false; |
| if(nTextures != s.nNumTexture) return false; |
| if(nNodes != s.nNumNode) return false; |
| return true; |
| |
| case ePODFileUnits: if(!src.Read32(s.fUnits)) return false; break; |
| case ePODFileColourBackground: if(!src.ReadArray32(&s.pfColourBackground[0], sizeof(s.pfColourBackground) / sizeof(*s.pfColourBackground))) return false; break; |
| case ePODFileColourAmbient: if(!src.ReadArray32(&s.pfColourAmbient[0], sizeof(s.pfColourAmbient) / sizeof(*s.pfColourAmbient))) return false; break; |
| case ePODFileNumCamera: if(!src.Read32(s.nNumCamera)) return false; if(!SafeAlloc(s.pCamera, s.nNumCamera)) return false; break; |
| case ePODFileNumLight: if(!src.Read32(s.nNumLight)) return false; if(!SafeAlloc(s.pLight, s.nNumLight)) return false; break; |
| case ePODFileNumMesh: if(!src.Read32(s.nNumMesh)) return false; if(!SafeAlloc(s.pMesh, s.nNumMesh)) return false; break; |
| case ePODFileNumNode: if(!src.Read32(s.nNumNode)) return false; if(!SafeAlloc(s.pNode, s.nNumNode)) return false; break; |
| case ePODFileNumMeshNode: if(!src.Read32(s.nNumMeshNode)) return false; break; |
| case ePODFileNumTexture: if(!src.Read32(s.nNumTexture)) return false; if(!SafeAlloc(s.pTexture, s.nNumTexture)) return false; break; |
| case ePODFileNumMaterial: if(!src.Read32(s.nNumMaterial)) return false; if(!SafeAlloc(s.pMaterial, s.nNumMaterial)) return false; break; |
| case ePODFileNumFrame: if(!src.Read32(s.nNumFrame)) return false; break; |
| case ePODFileFPS: if(!src.Read32(s.nFPS)) return false; break; |
| case ePODFileFlags: if(!src.Read32(s.nFlags)) return false; break; |
| |
| case ePODFileCamera: if(!ReadCamera(s.pCamera[nCameras++], src)) return false; break; |
| case ePODFileLight: if(!ReadLight(s.pLight[nLights++], src)) return false; break; |
| case ePODFileMaterial: if(!ReadMaterial(s.pMaterial[nMaterials++], src)) return false; break; |
| case ePODFileMesh: if(!ReadMesh(s.pMesh[nMeshes++], src)) return false; break; |
| case ePODFileNode: if(!ReadNode(s.pNode[nNodes++], src)) return false; break; |
| case ePODFileTexture: if(!ReadTexture(s.pTexture[nTextures++], src)) return false; break; |
| |
| case ePODFileUserData: |
| if(!src.ReadAfterAlloc(s.pUserData, nLen)) |
| return false; |
| else |
| { |
| s.nUserDataSize = nLen; |
| break; |
| } |
| |
| default: |
| if(!src.Skip(nLen)) return false; |
| } |
| } |
| return false; |
| } |
| |
| /*!*************************************************************************** |
| @Function Read |
| @Output pS SPODScene data. May be NULL. |
| @Input src CSource object to read data from. |
| @Output pszExpOpt Export options. |
| @Input count Data size. |
| @Output pszHistory Export history. |
| @Input historyCount History data size. |
| @Description Loads the specified ".POD" file; returns the scene in |
| pScene. This structure must later be destroyed with |
| PVRTModelPODDestroy() to prevent memory leaks. |
| ".POD" files are exported from 3D Studio MAX using a |
| PowerVR plugin. pS may be NULL if only the export options |
| are required. |
| *****************************************************************************/ |
| static bool Read( |
| SPODScene * const pS, |
| CSource &src, |
| char * const pszExpOpt, |
| const size_t count, |
| char * const pszHistory, |
| const size_t historyCount) |
| { |
| unsigned int nName, nLen; |
| bool bVersionOK = false, bDone = false; |
| bool bNeedOptions = pszExpOpt != 0; |
| bool bNeedHistory = pszHistory != 0; |
| bool bLoadingOptionsOrHistory = bNeedOptions || bNeedHistory; |
| |
| while(src.ReadMarker(nName, nLen)) |
| { |
| switch(nName) |
| { |
| case ePODFileVersion: |
| { |
| char *pszVersion = NULL; |
| if(nLen != strlen(PVRTMODELPOD_VERSION)+1) return false; |
| if(!SafeAlloc(pszVersion, nLen)) return false; |
| if(!src.Read(pszVersion, nLen)) return false; |
| if(strcmp(pszVersion, PVRTMODELPOD_VERSION) != 0) return false; |
| bVersionOK = true; |
| FREE(pszVersion); |
| } |
| continue; |
| |
| case ePODFileScene: |
| if(pS) |
| { |
| if(!ReadScene(*pS, src)) |
| return false; |
| bDone = true; |
| } |
| continue; |
| |
| case ePODFileExpOpt: |
| if(bNeedOptions) |
| { |
| if(!src.Read(pszExpOpt, PVRT_MIN(nLen, (unsigned int) count))) |
| return false; |
| |
| bNeedOptions = false; |
| |
| if(count < nLen) |
| nLen -= (unsigned int) count ; // Adjust nLen as the read has moved our position |
| else |
| nLen = 0; |
| } |
| break; |
| |
| case ePODFileHistory: |
| if(bNeedHistory) |
| { |
| if(!src.Read(pszHistory, PVRT_MIN(nLen, (unsigned int) historyCount))) |
| return false; |
| |
| bNeedHistory = false; |
| |
| if(count < nLen) |
| nLen -= (unsigned int) historyCount; // Adjust nLen as the read has moved our position |
| else |
| nLen = 0; |
| } |
| break; |
| |
| case ePODFileScene | PVRTMODELPOD_TAG_END: |
| return bVersionOK == true && bDone == true; |
| |
| case (unsigned int) ePODFileEndiannessMisMatch: |
| PVRTErrorOutputDebug("Error: Endianness mismatch between the .pod file and the platform.\n"); |
| return false; |
| |
| } |
| |
| if(bLoadingOptionsOrHistory && !bNeedOptions && !bNeedHistory) |
| return true; // The options and/or history has been loaded |
| |
| // Unhandled data, skip it |
| if(!src.Skip(nLen)) |
| return false; |
| } |
| |
| if(bLoadingOptionsOrHistory) |
| return true; |
| |
| if(!pS) |
| return false; |
| |
| /* |
| Convert data to fixed or float point as this build desires |
| */ |
| #ifdef PVRT_FIXED_POINT_ENABLE |
| if(!(pS->nFlags & PVRTMODELPODSF_FIXED)) |
| { |
| PVRTErrorOutputDebug("Error: The tools have been compiled with fixed point enabled but the POD file isn't in fixed point format.\n"); |
| #else |
| if(pS->nFlags & PVRTMODELPODSF_FIXED) |
| { |
| PVRTErrorOutputDebug("Error: The POD file is in fixed point format but the tools haven't been compiled with fixed point enabled.\n"); |
| #endif |
| return false; |
| } |
| |
| |
| return bVersionOK == true && bDone == true; |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadFromSourceStream |
| @Output pS CPVRTModelPOD data. May not be NULL. |
| @Input src CSource object to read data from. |
| @Output pszExpOpt Export options. |
| @Input count Data size. |
| @Output pszHistory Export history. |
| @Input historyCount History data size. |
| @Description Loads the ".POD" data from the source stream; returns the scene |
| in pS. |
| *****************************************************************************/ |
| static EPVRTError ReadFromSourceStream( |
| CPVRTModelPOD * const pS, |
| CSourceStream &src, |
| char * const pszExpOpt, |
| const size_t count, |
| char * const pszHistory, |
| const size_t historyCount) |
| { |
| memset(pS, 0, sizeof(*pS)); |
| if(!Read(pszExpOpt || pszHistory ? NULL : pS, src, pszExpOpt, count, pszHistory, historyCount)) |
| return PVR_FAIL; |
| |
| if(pS->InitImpl() != PVR_SUCCESS) |
| return PVR_FAIL; |
| |
| return PVR_SUCCESS; |
| } |
| |
| /**************************************************************************** |
| ** Class: CPVRTModelPOD |
| ****************************************************************************/ |
| |
| /*!*************************************************************************** |
| @Function ReadFromFile |
| @Input pszFileName Filename to load |
| @Output pszExpOpt String in which to place exporter options |
| @Input count Maximum number of characters to store. |
| @Output pszHistory String in which to place the pod file history |
| @Input historyCount Maximum number of characters to store. |
| @Return PVR_SUCCESS if successful, PVR_FAIL if not |
| @Description Loads the specified ".POD" file; returns the scene in |
| pScene. This structure must later be destroyed with |
| PVRTModelPODDestroy() to prevent memory leaks. |
| ".POD" files are exported using the PVRGeoPOD exporters. |
| If pszExpOpt is NULL, the scene is loaded; otherwise the |
| scene is not loaded and pszExpOpt is filled in. The same |
| is true for pszHistory. |
| *****************************************************************************/ |
| EPVRTError CPVRTModelPOD::ReadFromFile( |
| const char * const pszFileName, |
| char * const pszExpOpt, |
| const size_t count, |
| char * const pszHistory, |
| const size_t historyCount) |
| { |
| CSourceStream src; |
| |
| if(!src.Init(pszFileName)) |
| return PVR_FAIL; |
| |
| return ReadFromSourceStream(this, src, pszExpOpt, count, pszHistory, historyCount); |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadFromMemory |
| @Input pData Data to load |
| @Input i32Size Size of data |
| @Output pszExpOpt String in which to place exporter options |
| @Input count Maximum number of characters to store. |
| @Output pszHistory String in which to place the pod file history |
| @Input historyCount Maximum number of characters to store. |
| @Return PVR_SUCCESS if successful, PVR_FAIL if not |
| @Description Loads the supplied pod data. This data can be exported |
| directly to a header using one of the pod exporters. |
| If pszExpOpt is NULL, the scene is loaded; otherwise the |
| scene is not loaded and pszExpOpt is filled in. The same |
| is true for pszHistory. |
| *****************************************************************************/ |
| EPVRTError CPVRTModelPOD::ReadFromMemory( |
| const char * pData, |
| const size_t i32Size, |
| char * const pszExpOpt, |
| const size_t count, |
| char * const pszHistory, |
| const size_t historyCount) |
| { |
| CSourceStream src; |
| |
| if(!src.Init(pData, i32Size)) |
| return PVR_FAIL; |
| |
| return ReadFromSourceStream(this, src, pszExpOpt, count, pszHistory, historyCount); |
| } |
| |
| /*!*************************************************************************** |
| @Function ReadFromMemory |
| @Input scene Scene data from the header file |
| @Return PVR_SUCCESS if successful, PVR_FAIL if not |
| @Description Sets the scene data from the supplied data structure. Use |
| when loading from .H files. |
| *****************************************************************************/ |
| EPVRTError CPVRTModelPOD::ReadFromMemory( |
| const SPODScene &scene) |
| { |
| Destroy(); |
| |
| memset(this, 0, sizeof(*this)); |
| |
| *(SPODScene*)this = scene; |
| |
| if(InitImpl() != PVR_SUCCESS) |
| return PVR_FAIL; |
| |
| m_pImpl->bFromMemory = true; |
| |
| return PVR_SUCCESS; |
| } |
| |
| /*!*************************************************************************** |
| @Function CopyFromMemory |
| @Input scene Scene data |
| @Return PVR_SUCCESS if successful, PVR_FAIL if not |
| @Description Sets the scene data from the supplied data structure. |
| *****************************************************************************/ |
| EPVRTError CPVRTModelPOD::CopyFromMemory(const SPODScene &scene) |
| { |
| Destroy(); |
| |
| unsigned int i; |
| |
| // SPODScene |
| nNumFrame = scene.nNumFrame; |
| nFPS = scene.nFPS; |
| nFlags = scene.nFlags; |
| fUnits = scene.fUnits; |
| |
| for(i = 0; i < 3; ++i) |
| { |
| pfColourBackground[i] = scene.pfColourBackground[i]; |
| pfColourAmbient[i] = scene.pfColourAmbient[i]; |
| } |
| |
| // Nodes |
| if(scene.nNumNode && SafeAlloc(pNode, scene.nNumNode)) |
| { |
| nNumNode = scene.nNumNode; |
| nNumMeshNode = scene.nNumMeshNode; |
| |
| for(i = 0; i < nNumNode; ++i) |
| PVRTModelPODCopyNode(scene.pNode[i], pNode[i], scene.nNumFrame); |
| } |
| |
| // Meshes |
| if(scene.nNumMesh && SafeAlloc(pMesh, scene.nNumMesh)) |
| { |
| nNumMesh = scene.nNumMesh; |
| |
| for(i = 0; i < nNumMesh; ++i) |
| PVRTModelPODCopyMesh(scene.pMesh[i], pMesh[i]); |
| } |
| |
| // Cameras |
| if(scene.nNumCamera && SafeAlloc(pCamera, scene.nNumCamera)) |
| { |
| nNumCamera = scene.nNumCamera; |
| |
| for(i = 0; i < nNumCamera; ++i) |
| PVRTModelPODCopyCamera(scene.pCamera[i], pCamera[i], scene.nNumFrame); |
| } |
| |
| // Lights |
| if(scene.nNumLight && SafeAlloc(pLight, scene.nNumLight)) |
| { |
| nNumLight = scene.nNumLight; |
| |
| for(i = 0; i < nNumLight; ++i) |
| PVRTModelPODCopyLight(scene.pLight[i], pLight[i]); |
| } |
| |
| // Textures |
| if(scene.nNumTexture && SafeAlloc(pTexture, scene.nNumTexture)) |
| { |
| nNumTexture = scene.nNumTexture; |
| |
| for(i = 0; i < nNumTexture; ++i) |
| PVRTModelPODCopyTexture(scene.pTexture[i], pTexture[i]); |
| } |
| |
| // Materials |
| if(scene.nNumMaterial && SafeAlloc(pMaterial, scene.nNumMaterial)) |
| { |
| nNumMaterial = scene.nNumMaterial; |
| |
| for(i = 0; i < nNumMaterial; ++i) |
| PVRTModelPODCopyMaterial(scene.pMaterial[i], pMaterial[i]); |
| } |
| |
| if(scene.pUserData && SafeAlloc(pUserData, scene.nUserDataSize)) |
| { |
| memcpy(pUserData, scene.pUserData, nUserDataSize); |
| nUserDataSize = scene.nUserDataSize; |
| } |
| |
| if(InitImpl() != PVR_SUCCESS) |
| return PVR_FAIL; |
| |
| return PVR_SUCCESS; |
| } |
| |
| #if defined(_WIN32) |
| /*!*************************************************************************** |
| @Function ReadFromResource |
| @Input pszName Name of the resource to load from |
| @Return PVR_SUCCESS if successful, PVR_FAIL if not |
| @Description Loads the specified ".POD" file; returns the scene in |
| pScene. This structure must later be destroyed with |
| PVRTModelPODDestroy() to prevent memory leaks. |
| ".POD" files are exported from 3D Studio MAX using a |
| PowerVR plugin. |
| *****************************************************************************/ |
| EPVRTError CPVRTModelPOD::ReadFromResource( |
| const TCHAR * const pszName) |
| { |
| CSourceResource src; |
| |
| if(!src.Init(pszName)) |
| return PVR_FAIL; |
| |
| memset(this, 0, sizeof(*this)); |
| if(!Read(this, src, NULL, 0, NULL, 0)) |
| return PVR_FAIL; |
| if(InitImpl() != PVR_SUCCESS) |
| return PVR_FAIL; |
| return PVR_SUCCESS; |
| } |
| #endif /* WIN32 */ |
| |
| /*!*********************************************************************** |
| @Function InitImpl |
| @Description Used by the Read*() fns to initialise implementation |
| details. Should also be called by applications which |
| manually build data in the POD structures for rendering; |
| in this case call it after the data has been created. |
| Otherwise, do not call this function. |
| *************************************************************************/ |
| EPVRTError CPVRTModelPOD::InitImpl() |
| { |
| // Allocate space for implementation data |
| delete m_pImpl; |
| m_pImpl = new SPVRTPODImpl; |
| if(!m_pImpl) |
| return PVR_FAIL; |
| |
| // Zero implementation data |
| memset(m_pImpl, 0, sizeof(*m_pImpl)); |
| |
| #ifdef _DEBUG |
| m_pImpl->nWmTotal = 0; |
| #endif |
| |
| // Allocate world-matrix cache |
| m_pImpl->pfCache = new VERTTYPE[nNumNode]; |
| m_pImpl->pWmCache = new PVRTMATRIX[nNumNode]; |
| m_pImpl->pWmZeroCache = new PVRTMATRIX[nNumNode]; |
| FlushCache(); |
| |
| return PVR_SUCCESS; |
| } |
| |
| /*!*********************************************************************** |
| @Function DestroyImpl |
| @Description Used to free memory allocated by the implementation. |
| *************************************************************************/ |
| void CPVRTModelPOD::DestroyImpl() |
| { |
| if(m_pImpl) |
| { |
| if(m_pImpl->pfCache) delete [] m_pImpl->pfCache; |
| if(m_pImpl->pWmCache) delete [] m_pImpl->pWmCache; |
| if(m_pImpl->pWmZeroCache) delete [] m_pImpl->pWmZeroCache; |
| |
| delete m_pImpl; |
| m_pImpl = 0; |
| } |
| } |
| |
| /*!*********************************************************************** |
| @Function FlushCache |
| @Description Clears the matrix cache; use this if necessary when you |
| edit the position or animation of a node. |
| *************************************************************************/ |
| void CPVRTModelPOD::FlushCache() |
| { |
| // Pre-calc frame zero matrices |
| SetFrame(0); |
| for(unsigned int i = 0; i < nNumNode; ++i) |
| GetWorldMatrixNoCache(m_pImpl->pWmZeroCache[i], pNode[i]); |
| |
| // Load cache with frame-zero data |
| memcpy(m_pImpl->pWmCache, m_pImpl->pWmZeroCache, nNumNode * sizeof(*m_pImpl->pWmCache)); |
| memset(m_pImpl->pfCache, 0, nNumNode * sizeof(*m_pImpl->pfCache)); |
| } |
| |
| /*!*********************************************************************** |
| @Function IsLoaded |
| @Description Boolean to check whether a POD file has been loaded. |
| *************************************************************************/ |
| bool CPVRTModelPOD::IsLoaded() |
| { |
| return (m_pImpl!=NULL); |
| } |
| |
| /*!*************************************************************************** |
| @Function Constructor |
| @Description Initializes the pointer to scene data to NULL |
| *****************************************************************************/ |
| CPVRTModelPOD::CPVRTModelPOD() : m_pImpl(NULL) |
| {} |
| |
| /*!*************************************************************************** |
| @Function Destructor |
| @Description Frees the memory allocated to store the scene in pScene. |
| *****************************************************************************/ |
| CPVRTModelPOD::~CPVRTModelPOD() |
| { |
| Destroy(); |
| } |
| |
| /*!*************************************************************************** |
| @Function Destroy |
| @Description Frees the memory allocated to store the scene in pScene. |
| *****************************************************************************/ |
| void CPVRTModelPOD::Destroy() |
| { |
| unsigned int i; |
| |
| if(m_pImpl != NULL) |
| { |
| /* |
| Only attempt to free this memory if it was actually allocated at |
| run-time, as opposed to compiled into the app. |
| */ |
| if(!m_pImpl->bFromMemory) |
| { |
| |
| for(i = 0; i < nNumCamera; ++i) |
| FREE(pCamera[i].pfAnimFOV); |
| FREE(pCamera); |
| |
| FREE(pLight); |
| |
| for(i = 0; i < nNumMaterial; ++i) |
| { |
| FREE(pMaterial[i].pszName); |
| FREE(pMaterial[i].pszEffectFile); |
| FREE(pMaterial[i].pszEffectName); |
| FREE(pMaterial[i].pUserData); |
| } |
| FREE(pMaterial); |
| |
| for(i = 0; i < nNumMesh; ++i) { |
| FREE(pMesh[i].sFaces.pData); |
| FREE(pMesh[i].pnStripLength); |
| if(pMesh[i].pInterleaved) |
| { |
| FREE(pMesh[i].pInterleaved); |
| } |
| else |
| { |
| FREE(pMesh[i].sVertex.pData); |
| FREE(pMesh[i].sNormals.pData); |
| FREE(pMesh[i].sTangents.pData); |
| FREE(pMesh[i].sBinormals.pData); |
| for(unsigned int j = 0; j < pMesh[i].nNumUVW; ++j) |
| FREE(pMesh[i].psUVW[j].pData); |
| FREE(pMesh[i].sVtxColours.pData); |
| FREE(pMesh[i].sBoneIdx.pData); |
| FREE(pMesh[i].sBoneWeight.pData); |
| } |
| FREE(pMesh[i].psUVW); |
| pMesh[i].sBoneBatches.Release(); |
| } |
| FREE(pMesh); |
| |
| for(i = 0; i < nNumNode; ++i) { |
| FREE(pNode[i].pszName); |
| FREE(pNode[i].pfAnimPosition); |
| FREE(pNode[i].pnAnimPositionIdx); |
| FREE(pNode[i].pfAnimRotation); |
| FREE(pNode[i].pnAnimRotationIdx); |
| FREE(pNode[i].pfAnimScale); |
| FREE(pNode[i].pnAnimScaleIdx); |
| FREE(pNode[i].pfAnimMatrix); |
| FREE(pNode[i].pnAnimMatrixIdx); |
| FREE(pNode[i].pUserData); |
| pNode[i].nAnimFlags = 0; |
| } |
| |
| FREE(pNode); |
| |
| for(i = 0; i < nNumTexture; ++i) |
| FREE(pTexture[i].pszName); |
| FREE(pTexture); |
| |
| FREE(pUserData); |
| } |
| |
| // Free the working space used by the implementation |
| DestroyImpl(); |
| } |
| |
| memset(this, 0, sizeof(*this)); |
| } |
| |
| /*!*************************************************************************** |
| @Function SetFrame |
| @Input fFrame Frame number |
| @Description Set the animation frame for which subsequent Get*() calls |
| should return data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::SetFrame(const VERTTYPE fFrame) |
| { |
| if(nNumFrame) { |
| /* |
| Limit animation frames. |
| |
| Example: If there are 100 frames of animation, the highest frame |
| number allowed is 98, since that will blend between frames 98 and |
| 99. (99 being of course the 100th frame.) |
| */ |
| _ASSERT(fFrame <= f2vt((float)(nNumFrame-1))); |
| m_pImpl->nFrame = (int)vt2f(fFrame); |
| m_pImpl->fBlend = fFrame - f2vt(m_pImpl->nFrame); |
| } |
| else |
| { |
| m_pImpl->fBlend = 0; |
| m_pImpl->nFrame = 0; |
| } |
| |
| m_pImpl->fFrame = fFrame; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetRotationMatrix |
| @Output mOut Rotation matrix |
| @Input node Node to get the rotation matrix from |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetRotationMatrix( |
| PVRTMATRIX &mOut, |
| const SPODNode &node) const |
| { |
| PVRTQUATERNION q; |
| |
| if(node.pfAnimRotation) |
| { |
| if(node.nAnimFlags & ePODHasRotationAni) |
| { |
| if(node.pnAnimRotationIdx) |
| { |
| PVRTMatrixQuaternionSlerp( |
| q, |
| (PVRTQUATERNION&)node.pfAnimRotation[node.pnAnimRotationIdx[m_pImpl->nFrame]], |
| (PVRTQUATERNION&)node.pfAnimRotation[node.pnAnimRotationIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend); |
| } |
| else |
| { |
| PVRTMatrixQuaternionSlerp( |
| q, |
| (PVRTQUATERNION&)node.pfAnimRotation[4*m_pImpl->nFrame], |
| (PVRTQUATERNION&)node.pfAnimRotation[4*(m_pImpl->nFrame+1)], m_pImpl->fBlend); |
| } |
| |
| PVRTMatrixRotationQuaternion(mOut, q); |
| } |
| else |
| { |
| PVRTMatrixRotationQuaternion(mOut, *(PVRTQUATERNION*)node.pfAnimRotation); |
| } |
| } |
| else |
| { |
| PVRTMatrixIdentity(mOut); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function GetRotationMatrix |
| @Input node Node to get the rotation matrix from |
| @Returns Rotation matrix |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| PVRTMat4 CPVRTModelPOD::GetRotationMatrix(const SPODNode &node) const |
| { |
| PVRTMat4 mOut; |
| GetRotationMatrix(mOut,node); |
| return mOut; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetScalingMatrix |
| @Output mOut Scaling matrix |
| @Input node Node to get the rotation matrix from |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetScalingMatrix( |
| PVRTMATRIX &mOut, |
| const SPODNode &node) const |
| { |
| PVRTVECTOR3 v; |
| |
| if(node.pfAnimScale) |
| { |
| if(node.nAnimFlags & ePODHasScaleAni) |
| { |
| if(node.pnAnimScaleIdx) |
| { |
| PVRTMatrixVec3Lerp( |
| v, |
| (PVRTVECTOR3&)node.pfAnimScale[node.pnAnimScaleIdx[m_pImpl->nFrame+0]], |
| (PVRTVECTOR3&)node.pfAnimScale[node.pnAnimScaleIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend); |
| } |
| else |
| { |
| PVRTMatrixVec3Lerp( |
| v, |
| (PVRTVECTOR3&)node.pfAnimScale[7*(m_pImpl->nFrame+0)], |
| (PVRTVECTOR3&)node.pfAnimScale[7*(m_pImpl->nFrame+1)], m_pImpl->fBlend); |
| } |
| |
| PVRTMatrixScaling(mOut, v.x, v.y, v.z); |
| } |
| else |
| { |
| PVRTMatrixScaling(mOut, node.pfAnimScale[0], node.pfAnimScale[1], node.pfAnimScale[2]); |
| } |
| } |
| else |
| { |
| PVRTMatrixIdentity(mOut); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function GetScalingMatrix |
| @Input node Node to get the rotation matrix from |
| @Returns Scaling matrix |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| PVRTMat4 CPVRTModelPOD::GetScalingMatrix(const SPODNode &node) const |
| { |
| PVRTMat4 mOut; |
| GetScalingMatrix(mOut, node); |
| return mOut; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetTranslation |
| @Output V Translation vector |
| @Input node Node to get the translation vector from |
| @Description Generates the translation vector for the given Mesh |
| Instance. Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetTranslation( |
| PVRTVECTOR3 &V, |
| const SPODNode &node) const |
| { |
| if(node.pfAnimPosition) |
| { |
| if(node.nAnimFlags & ePODHasPositionAni) |
| { |
| if(node.pnAnimPositionIdx) |
| { |
| PVRTMatrixVec3Lerp(V, |
| (PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+0]], |
| (PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend); |
| } |
| else |
| { |
| PVRTMatrixVec3Lerp(V, |
| (PVRTVECTOR3&)node.pfAnimPosition[3 * (m_pImpl->nFrame+0)], |
| (PVRTVECTOR3&)node.pfAnimPosition[3 * (m_pImpl->nFrame+1)], m_pImpl->fBlend); |
| } |
| } |
| else |
| { |
| V = *(PVRTVECTOR3*) node.pfAnimPosition; |
| } |
| } |
| else |
| { |
| _ASSERT(false); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function GetTranslation |
| @Input node Node to get the translation vector from |
| @Returns Translation vector |
| @Description Generates the translation vector for the given Mesh |
| Instance. Uses animation data. |
| *****************************************************************************/ |
| PVRTVec3 CPVRTModelPOD::GetTranslation(const SPODNode &node) const |
| { |
| PVRTVec3 vOut; |
| GetTranslation(vOut, node); |
| return vOut; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetTranslationMatrix |
| @Output mOut Translation matrix |
| @Input node Node to get the translation matrix from |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetTranslationMatrix( |
| PVRTMATRIX &mOut, |
| const SPODNode &node) const |
| { |
| PVRTVECTOR3 v; |
| |
| if(node.pfAnimPosition) |
| { |
| if(node.nAnimFlags & ePODHasPositionAni) |
| { |
| if(node.pnAnimPositionIdx) |
| { |
| PVRTMatrixVec3Lerp(v, |
| (PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+0]], |
| (PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend); |
| } |
| else |
| { |
| PVRTMatrixVec3Lerp(v, |
| (PVRTVECTOR3&)node.pfAnimPosition[3*(m_pImpl->nFrame+0)], |
| (PVRTVECTOR3&)node.pfAnimPosition[3*(m_pImpl->nFrame+1)], m_pImpl->fBlend); |
| } |
| |
| PVRTMatrixTranslation(mOut, v.x, v.y, v.z); |
| } |
| else |
| { |
| PVRTMatrixTranslation(mOut, node.pfAnimPosition[0], node.pfAnimPosition[1], node.pfAnimPosition[2]); |
| } |
| } |
| else |
| { |
| PVRTMatrixIdentity(mOut); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function GetTranslationMatrix |
| @Input node Node to get the translation matrix from |
| @Returns Translation matrix |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| PVRTMat4 CPVRTModelPOD::GetTranslationMatrix(const SPODNode &node) const |
| { |
| PVRTMat4 mOut; |
| GetTranslationMatrix(mOut, node); |
| return mOut; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetTransformationMatrix |
| @Output mOut Transformation matrix |
| @Input node Node to get the transformation matrix from |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetTransformationMatrix(PVRTMATRIX &mOut, const SPODNode &node) const |
| { |
| if(node.pfAnimMatrix) |
| { |
| if(node.nAnimFlags & ePODHasMatrixAni) |
| { |
| if(node.pnAnimMatrixIdx) |
| mOut = *((PVRTMATRIX*) &node.pfAnimMatrix[node.pnAnimMatrixIdx[m_pImpl->nFrame]]); |
| else |
| mOut = *((PVRTMATRIX*) &node.pfAnimMatrix[16*m_pImpl->nFrame]); |
| } |
| else |
| { |
| mOut = *((PVRTMATRIX*) node.pfAnimMatrix); |
| } |
| } |
| else |
| { |
| PVRTMatrixIdentity(mOut); |
| } |
| } |
| /*!*************************************************************************** |
| @Function GetWorldMatrixNoCache |
| @Output mOut World matrix |
| @Input node Node to get the world matrix from |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetWorldMatrixNoCache( |
| PVRTMATRIX &mOut, |
| const SPODNode &node) const |
| { |
| PVRTMATRIX mTmp; |
| |
| if(node.pfAnimMatrix) // The transformations are stored as matrices |
| GetTransformationMatrix(mOut, node); |
| else |
| { |
| // Scale |
| GetScalingMatrix(mOut, node); |
| |
| // Rotation |
| GetRotationMatrix(mTmp, node); |
| PVRTMatrixMultiply(mOut, mOut, mTmp); |
| |
| // Translation |
| GetTranslationMatrix(mTmp, node); |
| PVRTMatrixMultiply(mOut, mOut, mTmp); |
| } |
| |
| // Do we have to worry about a parent? |
| if(node.nIdxParent < 0) |
| return; |
| |
| // Apply parent's transform too. |
| GetWorldMatrixNoCache(mTmp, pNode[node.nIdxParent]); |
| PVRTMatrixMultiply(mOut, mOut, mTmp); |
| } |
| |
| /*!*************************************************************************** |
| @Function GetWorldMatrixNoCache |
| @Input node Node to get the world matrix from |
| @Returns World matrix |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| PVRTMat4 CPVRTModelPOD::GetWorldMatrixNoCache(const SPODNode& node) const |
| { |
| PVRTMat4 mWorld; |
| GetWorldMatrixNoCache(mWorld,node); |
| return mWorld; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetWorldMatrix |
| @Output mOut World matrix |
| @Input node Node to get the world matrix from |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetWorldMatrix( |
| PVRTMATRIX &mOut, |
| const SPODNode &node) const |
| { |
| unsigned int nIdx; |
| |
| #ifdef _DEBUG |
| ++m_pImpl->nWmTotal; |
| m_pImpl->fHitPerc = (float)m_pImpl->nWmCacheHit / (float)m_pImpl->nWmTotal; |
| m_pImpl->fHitPercZero = (float)m_pImpl->nWmZeroCacheHit / (float)m_pImpl->nWmTotal; |
| #endif |
| |
| // Calculate a node index |
| nIdx = (unsigned int)(&node - pNode); |
| |
| // There is a dedicated cache for frame 0 data |
| if(m_pImpl->fFrame == 0) |
| { |
| mOut = m_pImpl->pWmZeroCache[nIdx]; |
| #ifdef _DEBUG |
| ++m_pImpl->nWmZeroCacheHit; |
| #endif |
| return; |
| } |
| |
| // Has this matrix been calculated & cached? |
| if(m_pImpl->fFrame == m_pImpl->pfCache[nIdx]) |
| { |
| mOut = m_pImpl->pWmCache[nIdx]; |
| #ifdef _DEBUG |
| ++m_pImpl->nWmCacheHit; |
| #endif |
| return; |
| } |
| |
| GetWorldMatrixNoCache(mOut, node); |
| |
| // Cache the matrix |
| m_pImpl->pfCache[nIdx] = m_pImpl->fFrame; |
| m_pImpl->pWmCache[nIdx] = mOut; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetWorldMatrix |
| @Input node Node to get the world matrix from |
| @Returns World matrix |
| @Description Generates the world matrix for the given Mesh Instance; |
| applies the parent's transform too. Uses animation data. |
| *****************************************************************************/ |
| PVRTMat4 CPVRTModelPOD::GetWorldMatrix(const SPODNode& node) const |
| { |
| PVRTMat4 mWorld; |
| GetWorldMatrix(mWorld,node); |
| return mWorld; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetBoneWorldMatrix |
| @Output mOut Bone world matrix |
| @Input NodeMesh Mesh to take the bone matrix from |
| @Input NodeBone Bone to take the matrix from |
| @Description Generates the world matrix for the given bone. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetBoneWorldMatrix( |
| PVRTMATRIX &mOut, |
| const SPODNode &NodeMesh, |
| const SPODNode &NodeBone) |
| { |
| PVRTMATRIX mTmp; |
| VERTTYPE fFrame; |
| |
| fFrame = m_pImpl->fFrame; |
| |
| SetFrame(0); |
| |
| // Transform by object matrix |
| GetWorldMatrix(mOut, NodeMesh); |
| |
| // Back transform bone from frame 0 position |
| GetWorldMatrix(mTmp, NodeBone); |
| PVRTMatrixInverse(mTmp, mTmp); |
| PVRTMatrixMultiply(mOut, mOut, mTmp); |
| |
| // The bone origin should now be at the origin |
| |
| SetFrame(fFrame); |
| |
| // Transform bone into frame fFrame position |
| GetWorldMatrix(mTmp, NodeBone); |
| PVRTMatrixMultiply(mOut, mOut, mTmp); |
| } |
| |
| /*!*************************************************************************** |
| @Function GetBoneWorldMatrix |
| @Input NodeMesh Mesh to take the bone matrix from |
| @Input NodeBone Bone to take the matrix from |
| @Returns Bone world matrix |
| @Description Generates the world matrix for the given bone. |
| *****************************************************************************/ |
| PVRTMat4 CPVRTModelPOD::GetBoneWorldMatrix( |
| const SPODNode &NodeMesh, |
| const SPODNode &NodeBone) |
| { |
| PVRTMat4 mOut; |
| GetBoneWorldMatrix(mOut,NodeMesh,NodeBone); |
| return mOut; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetCamera |
| @Output vFrom Position of the camera |
| @Output vTo Target of the camera |
| @Output vUp Up direction of the camera |
| @Input nIdx Camera number |
| @Return Camera horizontal FOV |
| @Description Calculate the From, To and Up vectors for the given |
| camera. Uses animation data. |
| Note that even if the camera has a target, *pvTo is not |
| the position of that target. *pvTo is a position in the |
| correct direction of the target, one unit away from the |
| camera. |
| *****************************************************************************/ |
| VERTTYPE CPVRTModelPOD::GetCamera( |
| PVRTVECTOR3 &vFrom, |
| PVRTVECTOR3 &vTo, |
| PVRTVECTOR3 &vUp, |
| const unsigned int nIdx) const |
| { |
| PVRTMATRIX mTmp; |
| VERTTYPE *pfData; |
| SPODCamera *pCam; |
| const SPODNode *pNd; |
| |
| _ASSERT(nIdx < nNumCamera); |
| |
| // Camera nodes are after the mesh and light nodes in the array |
| pNd = &pNode[nNumMeshNode + nNumLight + nIdx]; |
| |
| pCam = &pCamera[pNd->nIdx]; |
| |
| GetWorldMatrix(mTmp, *pNd); |
| |
| // View position is 0,0,0,1 transformed by world matrix |
| vFrom.x = mTmp.f[12]; |
| vFrom.y = mTmp.f[13]; |
| vFrom.z = mTmp.f[14]; |
| |
| // View direction is 0,-1,0,1 transformed by world matrix |
| vTo.x = -mTmp.f[4] + mTmp.f[12]; |
| vTo.y = -mTmp.f[5] + mTmp.f[13]; |
| vTo.z = -mTmp.f[6] + mTmp.f[14]; |
| |
| #if defined(BUILD_DX11) |
| /* |
| When you rotate the camera from "straight forward" to "straight down", in |
| D3D the UP vector will be [0, 0, 1] |
| */ |
| vUp.x = mTmp.f[ 8]; |
| vUp.y = mTmp.f[ 9]; |
| vUp.z = mTmp.f[10]; |
| #endif |
| |
| #if defined(BUILD_OGL) || defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) |
| /* |
| When you rotate the camera from "straight forward" to "straight down", in |
| OpenGL the UP vector will be [0, 0, -1] |
| */ |
| vUp.x = -mTmp.f[ 8]; |
| vUp.y = -mTmp.f[ 9]; |
| vUp.z = -mTmp.f[10]; |
| #endif |
| |
| /* |
| Find & calculate FOV value |
| */ |
| if(pCam->pfAnimFOV) { |
| pfData = &pCam->pfAnimFOV[m_pImpl->nFrame]; |
| |
| return pfData[0] + m_pImpl->fBlend * (pfData[1] - pfData[0]); |
| } else { |
| return pCam->fFOV; |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function GetCameraPos |
| @Output vFrom Position of the camera |
| @Output vTo Target of the camera |
| @Input nIdx Camera number |
| @Return Camera horizontal FOV |
| @Description Calculate the position of the camera and its target. Uses |
| animation data. |
| If the queried camera does not have a target, *pvTo is |
| not changed. |
| *****************************************************************************/ |
| VERTTYPE CPVRTModelPOD::GetCameraPos( |
| PVRTVECTOR3 &vFrom, |
| PVRTVECTOR3 &vTo, |
| const unsigned int nIdx) const |
| { |
| PVRTMATRIX mTmp; |
| VERTTYPE *pfData; |
| SPODCamera *pCam; |
| const SPODNode *pNd; |
| |
| _ASSERT(nIdx < nNumCamera); |
| |
| // Camera nodes are after the mesh and light nodes in the array |
| pNd = &pNode[nNumMeshNode + nNumLight + nIdx]; |
| |
| // View position is 0,0,0,1 transformed by world matrix |
| GetWorldMatrix(mTmp, *pNd); |
| vFrom.x = mTmp.f[12]; |
| vFrom.y = mTmp.f[13]; |
| vFrom.z = mTmp.f[14]; |
| |
| pCam = &pCamera[pNd->nIdx]; |
| if(pCam->nIdxTarget >= 0) |
| { |
| // View position is 0,0,0,1 transformed by world matrix |
| GetWorldMatrix(mTmp, pNode[pCam->nIdxTarget]); |
| vTo.x = mTmp.f[12]; |
| vTo.y = mTmp.f[13]; |
| vTo.z = mTmp.f[14]; |
| } |
| |
| /* |
| Find & calculate FOV value |
| */ |
| if(pCam->pfAnimFOV) { |
| pfData = &pCam->pfAnimFOV[m_pImpl->nFrame]; |
| |
| return pfData[0] + m_pImpl->fBlend * (pfData[1] - pfData[0]); |
| } else { |
| return pCam->fFOV; |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function GetLight |
| @Output vPos Position of the light |
| @Output vDir Direction of the light |
| @Input nIdx Light number |
| @Description Calculate the position and direction of the given Light. |
| Uses animation data. |
| *****************************************************************************/ |
| void CPVRTModelPOD::GetLight( |
| PVRTVECTOR3 &vPos, |
| PVRTVECTOR3 &vDir, |
| const unsigned int nIdx) const |
| { |
| PVRTMATRIX mTmp; |
| const SPODNode *pNd; |
| |
| _ASSERT(nIdx < nNumLight); |
| |
| // Light nodes are after the mesh nodes in the array |
| pNd = &pNode[nNumMeshNode + nIdx]; |
| |
| GetWorldMatrix(mTmp, *pNd); |
| |
| // View position is 0,0,0,1 transformed by world matrix |
| vPos.x = mTmp.f[12]; |
| vPos.y = mTmp.f[13]; |
| vPos.z = mTmp.f[14]; |
| |
| // View direction is 0,-1,0,0 transformed by world matrix |
| vDir.x = -mTmp.f[4]; |
| vDir.y = -mTmp.f[5]; |
| vDir.z = -mTmp.f[6]; |
| } |
| |
| /*!*************************************************************************** |
| @Function GetLightPositon |
| @Input u32Idx Light number |
| @Return PVRTVec4 position of light with w set correctly |
| @Description Calculates the position of the given light. Uses animation data |
| *****************************************************************************/ |
| PVRTVec4 CPVRTModelPOD::GetLightPosition(const unsigned int u32Idx) const |
| { // TODO: make this a real function instead of just wrapping GetLight() |
| PVRTVec3 vPos, vDir; |
| GetLight(vPos,vDir,u32Idx); |
| |
| _ASSERT(u32Idx < nNumLight); |
| _ASSERT(pLight[u32Idx].eType!=ePODDirectional); |
| return PVRTVec4(vPos,1); |
| } |
| |
| /*!*************************************************************************** |
| @Function GetLightDirection |
| @Input u32Idx Light number |
| @Return PVRTVec4 direction of light with w set correctly |
| @Description Calculate the direction of the given Light. Uses animation data. |
| *****************************************************************************/ |
| PVRTVec4 CPVRTModelPOD::GetLightDirection(const unsigned int u32Idx) const |
| { // TODO: make this a real function instead of just wrapping GetLight() |
| PVRTVec3 vPos, vDir; |
| GetLight(vPos,vDir,u32Idx); |
| |
| _ASSERT(u32Idx < nNumLight); |
| _ASSERT(pLight[u32Idx].eType!=ePODPoint); |
| return PVRTVec4(vDir,0); |
| } |
| |
| /*!*************************************************************************** |
| @Function CreateSkinIdxWeight |
| @Output pIdx Four bytes containing matrix indices for vertex (0..255) (D3D: use UBYTE4) |
| @Output pWeight Four bytes containing blend weights for vertex (0.0 .. 1.0) (D3D: use D3DCOLOR) |
| @Input nVertexBones Number of bones this vertex uses |
| @Input pnBoneIdx Pointer to 'nVertexBones' indices |
| @Input pfBoneWeight Pointer to 'nVertexBones' blend weights |
| @Description Creates the matrix indices and blend weights for a boned |
| vertex. Call once per vertex of a boned mesh. |
| *****************************************************************************/ |
| EPVRTError CPVRTModelPOD::CreateSkinIdxWeight( |
| char * const pIdx, // Four bytes containing matrix indices for vertex (0..255) (D3D: use UBYTE4) |
| char * const pWeight, // Four bytes containing blend weights for vertex (0.0 .. 1.0) (D3D: use D3DCOLOR) |
| const int nVertexBones, // Number of bones this vertex uses |
| const int * const pnBoneIdx, // Pointer to 'nVertexBones' indices |
| const VERTTYPE * const pfBoneWeight) // Pointer to 'nVertexBones' blend weights |
| { |
| int i, nSum; |
| int nIdx[4]; |
| int nWeight[4]; |
| |
| for(i = 0; i < nVertexBones; ++i) |
| { |
| nIdx[i] = pnBoneIdx[i]; |
| nWeight[i] = (int)vt2f((VERTTYPEMUL(f2vt(255.0f), pfBoneWeight[i]))); |
| |
| if(nIdx[i] > 255) |
| { |
| PVRTErrorOutputDebug("Too many bones (highest index is 255).\n"); |
| return PVR_FAIL; |
| } |
| |
| nWeight[i] = PVRT_MAX(nWeight[i], 0); |
| nWeight[i] = PVRT_MIN(nWeight[i], 255); |
| } |
| |
| for(; i < 4; ++i) |
| { |
| nIdx[i] = 0; |
| nWeight[i] = 0; |
| } |
| |
| if(nVertexBones) |
| { |
| // It's important the weights sum to 1 |
| nSum = 0; |
| for(i = 0; i < 4; ++i) |
| nSum += nWeight[i]; |
| |
| if(!nSum) |
| return PVR_FAIL; |
| |
| _ASSERT(nSum <= 255); |
| |
| i = 0; |
| while(nSum < 255) |
| { |
| if(nWeight[i]) { |
| ++nWeight[i]; |
| ++nSum; |
| } |
| |
| if(++i > 3) |
| i = 0; |
| } |
| |
| _ASSERT(nSum == 255); |
| } |
| |
| #if defined(BUILD_DX11) |
| *(unsigned int*)pIdx = ((unsigned int)(((nIdx[3]&0xff)<<24)|((nIdx[2]&0xff)<<16)|((nIdx[1]&0xff)<<8)|(nIdx[0]&0xff))); // UBYTE4 is WZYX |
| *(unsigned int*)pWeight = ((unsigned int)(((nWeight[3]&0xff)<<24)|((nWeight[0]&0xff)<<16)|((nWeight[1]&0xff)<<8)|(nWeight[2]&0xff))); // D3DCOLORs are WXYZ |
| #endif |
| |
| #if defined(BUILD_OGL) || defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) |
| // Return indices and weights as bytes |
| for(i = 0; i < 4; ++i) |
| { |
| pIdx[i] = (char) nIdx[i]; |
| pWeight[i] = (char) nWeight[i]; |
| } |
| #endif |
| |
| return PVR_SUCCESS; |
| } |
| |
| /*!*************************************************************************** |
| @Function SavePOD |
| @Input pszFilename Filename to save to |
| @Input pszExpOpt A string containing the options used by the exporter |
| @Description Save a binary POD file (.POD). |
| *****************************************************************************/ |
| EPVRTError CPVRTModelPOD::SavePOD(const char * const pszFilename, const char * const pszExpOpt, const char * const pszHistory) |
| { |
| FILE *pFile; |
| bool bRet; |
| |
| pFile = fopen(pszFilename, "wb+"); |
| if(!pFile) |
| return PVR_FAIL; |
| |
| bRet = WritePOD(pFile, pszExpOpt, pszHistory, *this); |
| |
| // Done |
| fclose(pFile); |
| return bRet ? PVR_SUCCESS : PVR_FAIL; |
| } |
| |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODDataTypeSize |
| @Input type Type to get the size of |
| @Return Size of the data element |
| @Description Returns the size of each data element. |
| *****************************************************************************/ |
| PVRTuint32 PVRTModelPODDataTypeSize(const EPVRTDataType type) |
| { |
| switch(type) |
| { |
| default: |
| _ASSERT(false); |
| return 0; |
| case EPODDataFloat: |
| return static_cast<PVRTuint32>(sizeof(float)); |
| case EPODDataInt: |
| case EPODDataUnsignedInt: |
| return static_cast<PVRTuint32>(sizeof(int)); |
| case EPODDataShort: |
| case EPODDataShortNorm: |
| case EPODDataUnsignedShort: |
| case EPODDataUnsignedShortNorm: |
| return static_cast<PVRTuint32>(sizeof(unsigned short)); |
| case EPODDataRGBA: |
| return static_cast<PVRTuint32>(sizeof(unsigned int)); |
| case EPODDataABGR: |
| return static_cast<PVRTuint32>(sizeof(unsigned int)); |
| case EPODDataARGB: |
| return static_cast<PVRTuint32>(sizeof(unsigned int)); |
| case EPODDataD3DCOLOR: |
| return static_cast<PVRTuint32>(sizeof(unsigned int)); |
| case EPODDataUBYTE4: |
| return static_cast<PVRTuint32>(sizeof(unsigned int)); |
| case EPODDataDEC3N: |
| return static_cast<PVRTuint32>(sizeof(unsigned int)); |
| case EPODDataFixed16_16: |
| return static_cast<PVRTuint32>(sizeof(unsigned int)); |
| case EPODDataUnsignedByte: |
| case EPODDataUnsignedByteNorm: |
| case EPODDataByte: |
| case EPODDataByteNorm: |
| return static_cast<PVRTuint32>(sizeof(unsigned char)); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODDataTypeComponentCount |
| @Input type Type to get the number of components from |
| @Return number of components in the data element |
| @Description Returns the number of components in a data element. |
| *****************************************************************************/ |
| PVRTuint32 PVRTModelPODDataTypeComponentCount(const EPVRTDataType type) |
| { |
| switch(type) |
| { |
| default: |
| _ASSERT(false); |
| return 0; |
| |
| case EPODDataFloat: |
| case EPODDataInt: |
| case EPODDataUnsignedInt: |
| case EPODDataShort: |
| case EPODDataShortNorm: |
| case EPODDataUnsignedShort: |
| case EPODDataUnsignedShortNorm: |
| case EPODDataFixed16_16: |
| case EPODDataByte: |
| case EPODDataByteNorm: |
| case EPODDataUnsignedByte: |
| case EPODDataUnsignedByteNorm: |
| return 1; |
| |
| case EPODDataDEC3N: |
| return 3; |
| |
| case EPODDataRGBA: |
| case EPODDataABGR: |
| case EPODDataARGB: |
| case EPODDataD3DCOLOR: |
| case EPODDataUBYTE4: |
| return 4; |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODDataStride |
| @Input data Data elements |
| @Return Size of the vector elements |
| @Description Returns the size of the vector of data elements. |
| *****************************************************************************/ |
| PVRTuint32 PVRTModelPODDataStride(const CPODData &data) |
| { |
| return PVRTModelPODDataTypeSize(data.eType) * data.n; |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODDataConvert |
| @Modified data Data elements to convert |
| @Input eNewType New type of elements |
| @Input nCnt Number of elements |
| @Description Convert the format of the array of vectors. |
| *****************************************************************************/ |
| void PVRTModelPODDataConvert(CPODData &data, const unsigned int nCnt, const EPVRTDataType eNewType) |
| { |
| PVRTVECTOR4f v; |
| unsigned int i; |
| CPODData old; |
| |
| if(!data.pData || data.eType == eNewType) |
| return; |
| |
| old = data; |
| |
| switch(eNewType) |
| { |
| case EPODDataFloat: |
| case EPODDataInt: |
| case EPODDataUnsignedInt: |
| case EPODDataUnsignedShort: |
| case EPODDataUnsignedShortNorm: |
| case EPODDataFixed16_16: |
| case EPODDataUnsignedByte: |
| case EPODDataUnsignedByteNorm: |
| case EPODDataShort: |
| case EPODDataShortNorm: |
| case EPODDataByte: |
| case EPODDataByteNorm: |
| data.n = (PVRTuint32) (old.n * PVRTModelPODDataTypeComponentCount(old.eType)); |
| break; |
| case EPODDataRGBA: |
| case EPODDataABGR: |
| case EPODDataARGB: |
| case EPODDataD3DCOLOR: |
| case EPODDataUBYTE4: |
| case EPODDataDEC3N: |
| data.n = 1; |
| break; |
| default: |
| _ASSERT(false); // unrecognised type |
| break; |
| } |
| |
| data.eType = eNewType; |
| data.nStride = (unsigned int)PVRTModelPODDataStride(data); |
| |
| // If the old & new strides are identical, we can convert it in place |
| if(old.nStride != data.nStride) |
| { |
| data.pData = (unsigned char*)malloc(data.nStride * nCnt); |
| } |
| |
| for(i = 0; i < nCnt; ++i) |
| { |
| PVRTVertexRead(&v, old.pData + i * old.nStride, old.eType, old.n); |
| PVRTVertexWrite(data.pData + i * data.nStride, eNewType, (int) (data.n * PVRTModelPODDataTypeComponentCount(data.eType)), &v); |
| } |
| |
| if(old.nStride != data.nStride) |
| { |
| FREE(old.pData); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODScaleAndConvertVtxData |
| @Modified mesh POD mesh to scale and convert the mesh data |
| @Input eNewType The data type to scale and convert the vertex data to |
| @Return PVR_SUCCESS on success and PVR_FAIL on failure. |
| @Description Scales the vertex data to fit within the range of the requested |
| data type and then converts the data to that type. This function |
| isn't currently compiled in for fixed point builds of the tools. |
| *****************************************************************************/ |
| #if !defined(PVRT_FIXED_POINT_ENABLE) |
| EPVRTError PVRTModelPODScaleAndConvertVtxData(SPODMesh &mesh, const EPVRTDataType eNewType) |
| { |
| // Initialise the matrix to identity |
| PVRTMatrixIdentity(mesh.mUnpackMatrix); |
| |
| // No vertices to process |
| if(!mesh.nNumVertex) |
| return PVR_SUCCESS; |
| |
| // This function expects the data to be floats and not interleaved |
| if(mesh.sVertex.eType != EPODDataFloat && mesh.pInterleaved != 0) |
| return PVR_FAIL; |
| |
| if(eNewType == EPODDataFloat) // Nothing to do |
| return PVR_FAIL; |
| |
| // A few variables |
| float fLower = 0.0f, fUpper = 0.0f; |
| PVRTBOUNDINGBOX BoundingBox; |
| PVRTMATRIX mOffset, mScale; |
| PVRTVECTOR4 v,o; |
| |
| // Set the w component of o as it is needed for later |
| o.w = 1.0f; |
| |
| // Calc bounding box |
| PVRTBoundingBoxComputeInterleaved(&BoundingBox, mesh.sVertex.pData, mesh.nNumVertex, 0, mesh.sVertex.nStride); |
| |
| // Get new type data range that we wish to scale the data to |
| |
| // Due to a hardware bug in early MBXs in some cases we clamp the data to the minimum possible value +1 |
| switch(eNewType) |
| { |
| case EPODDataInt: |
| fUpper = 1 << 30; |
| fLower = -fUpper; |
| break; |
| case EPODDataUnsignedInt: |
| fUpper = 1 << 30; |
| break; |
| case EPODDataShort: |
| case EPODDataFixed16_16: |
| fUpper = 32767.0f; |
| fLower = -fUpper; |
| break; |
| case EPODDataUnsignedShort: |
| fUpper = 0x0ffff; |
| break; |
| case EPODDataRGBA: |
| case EPODDataABGR: |
| case EPODDataARGB: |
| case EPODDataD3DCOLOR: |
| fUpper = 1.0f; |
| break; |
| case EPODDataUBYTE4: |
| case EPODDataUnsignedByte: |
| fUpper = 0x0ff; |
| break; |
| case EPODDataShortNorm: |
| case EPODDataUnsignedShortNorm: |
| case EPODDataByteNorm: |
| case EPODDataUnsignedByteNorm: |
| fUpper = 1.0f; |
| fLower = -fUpper; |
| break; |
| case EPODDataDEC3N: |
| fUpper = 511.0f; |
| fLower = -fUpper; |
| break; |
| case EPODDataByte: |
| fUpper = 127.0f; |
| fLower = -fUpper; |
| break; |
| default: |
| _ASSERT(false); |
| return PVR_FAIL; // Unsupported format specified |
| } |
| |
| PVRTVECTOR3f vScale, vOffset; |
| |
| float fRange = fUpper - fLower; |
| vScale.x = fRange / (BoundingBox.Point[7].x - BoundingBox.Point[0].x); |
| vScale.y = fRange / (BoundingBox.Point[7].y - BoundingBox.Point[0].y); |
| vScale.z = fRange / (BoundingBox.Point[7].z - BoundingBox.Point[0].z); |
| |
| vOffset.x = -BoundingBox.Point[0].x; |
| vOffset.y = -BoundingBox.Point[0].y; |
| vOffset.z = -BoundingBox.Point[0].z; |
| |
| PVRTMatrixTranslation(mOffset, -fLower, -fLower, -fLower); |
| PVRTMatrixScaling(mScale, 1.0f / vScale.x, 1.0f / vScale.y, 1.0f / vScale.z); |
| PVRTMatrixMultiply(mesh.mUnpackMatrix, mOffset, mScale); |
| |
| PVRTMatrixTranslation(mOffset, -vOffset.x, -vOffset.y, -vOffset.z); |
| PVRTMatrixMultiply(mesh.mUnpackMatrix, mesh.mUnpackMatrix, mOffset); |
| |
| // Transform vertex data |
| for(unsigned int i = 0; i < mesh.nNumVertex; ++i) |
| { |
| PVRTVertexRead(&v, mesh.sVertex.pData + i * mesh.sVertex.nStride, mesh.sVertex.eType, mesh.sVertex.n); |
| |
| o.x = (v.x + vOffset.x) * vScale.x + fLower; |
| o.y = (v.y + vOffset.y) * vScale.y + fLower; |
| o.z = (v.z + vOffset.z) * vScale.z + fLower; |
| |
| _ASSERT((o.x >= fLower && o.x <= fUpper) || fabs(1.0f - o.x / fLower) < 0.01f || fabs(1.0f - o.x / fUpper) < 0.01f); |
| _ASSERT((o.y >= fLower && o.y <= fUpper) || fabs(1.0f - o.y / fLower) < 0.01f || fabs(1.0f - o.y / fUpper) < 0.01f); |
| _ASSERT((o.z >= fLower && o.z <= fUpper) || fabs(1.0f - o.z / fLower) < 0.01f || fabs(1.0f - o.z / fUpper) < 0.01f); |
| |
| #if defined(_DEBUG) |
| PVRTVECTOR4 res; |
| PVRTTransform(&res, &o, &mesh.mUnpackMatrix); |
| |
| _ASSERT(fabs(res.x - v.x) <= 0.02); |
| _ASSERT(fabs(res.y - v.y) <= 0.02); |
| _ASSERT(fabs(res.z - v.z) <= 0.02); |
| _ASSERT(fabs(res.w - 1.0) <= 0.02); |
| #endif |
| |
| PVRTVertexWrite(mesh.sVertex.pData + i * mesh.sVertex.nStride, mesh.sVertex.eType, (int) (mesh.sVertex.n * PVRTModelPODDataTypeComponentCount(mesh.sVertex.eType)), &o); |
| } |
| |
| // Convert the data to the chosen format |
| PVRTModelPODDataConvert(mesh.sVertex, mesh.nNumVertex, eNewType); |
| |
| return PVR_SUCCESS; |
| } |
| #endif |
| /*!*************************************************************************** |
| @Function PVRTModelPODDataShred |
| @Modified data Data elements to modify |
| @Input nCnt Number of elements |
| @Input pChannels A list of the wanted channels, e.g. {'x', 'y', 0} |
| @Description Reduce the number of dimensions in 'data' using the requested |
| channel array. The array should have a maximum length of 4 |
| or be null terminated if less channels are wanted. It is also |
| possible to negate an element, e.g. {'x','y', -'z'}. |
| *****************************************************************************/ |
| void PVRTModelPODDataShred(CPODData &data, const unsigned int nCnt, const int * pChannels) |
| { |
| CPODData old; |
| PVRTVECTOR4f v,o; |
| float * const pv = &v.x; |
| float * const po = &o.x; |
| unsigned int i, nCh; |
| int i32Map[4]; |
| bool bNegate[4]; |
| |
| if(!data.pData || !pChannels) |
| return; |
| |
| old = data; |
| |
| // Count the number of output channels while setting up cMap and bNegate |
| for(data.n = 0; data.n < 4 && pChannels[data.n]; ++data.n) |
| { |
| i32Map[data.n] = abs(pChannels[data.n]) == 'w' ? 3 : abs(pChannels[data.n]) - 'x'; |
| bNegate[data.n] = pChannels[data.n] < 0; |
| } |
| |
| if(data.n > old.n) |
| data.n = old.n; |
| |
| // Allocate output memory |
| data.nStride = (unsigned int)PVRTModelPODDataStride(data); |
| |
| if(data.nStride == 0) |
| { |
| FREE(data.pData); |
| return; |
| } |
| |
| data.pData = (unsigned char*)malloc(data.nStride * nCnt); |
| |
| for(i = 0; i < nCnt; ++i) |
| { |
| // Read the vector |
| PVRTVertexRead(&v, old.pData + i * old.nStride, old.eType, old.n); |
| |
| // Shred the vector |
| for(nCh = 0; nCh < 4 && pChannels[nCh]; ++nCh) |
| po[nCh] = bNegate[nCh] ? -pv[i32Map[nCh]] : pv[i32Map[nCh]]; |
| |
| for(; nCh < 4; ++nCh) |
| po[nCh] = 0; |
| |
| // Write the vector |
| PVRTVertexWrite((char*)data.pData + i * data.nStride, data.eType, (int) (data.n * PVRTModelPODDataTypeComponentCount(data.eType)), &o); |
| } |
| |
| FREE(old.pData); |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODReorderFaces |
| @Modified mesh The mesh to re-order the faces of |
| @Input i32El1 The first index to be written out |
| @Input i32El2 The second index to be written out |
| @Input i32El3 The third index to be written out |
| @Description Reorders the face indices of a mesh. |
| *****************************************************************************/ |
| void PVRTModelPODReorderFaces(SPODMesh &mesh, const int i32El1, const int i32El2, const int i32El3) |
| { |
| if(!mesh.sFaces.pData) |
| return; |
| |
| unsigned int ui32V[3]; |
| |
| for(unsigned int i = 0; i < mesh.nNumFaces * 3; i += 3) |
| { |
| unsigned char *pData = mesh.sFaces.pData + i * mesh.sFaces.nStride; |
| |
| // Read |
| PVRTVertexRead(&ui32V[0], pData, mesh.sFaces.eType); |
| PVRTVertexRead(&ui32V[1], pData + mesh.sFaces.nStride, mesh.sFaces.eType); |
| PVRTVertexRead(&ui32V[2], pData + 2 * mesh.sFaces.nStride, mesh.sFaces.eType); |
| |
| // Write in place the new order |
| PVRTVertexWrite(pData, mesh.sFaces.eType, ui32V[i32El1]); |
| PVRTVertexWrite(pData + mesh.sFaces.nStride, mesh.sFaces.eType, ui32V[i32El2]); |
| PVRTVertexWrite(pData + 2 * mesh.sFaces.nStride, mesh.sFaces.eType, ui32V[i32El3]); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function InterleaveArray |
| @Modified pInterleaved |
| @Modified data |
| @Input nNumVertex |
| @Input nStride |
| @Input nPadding |
| @Input nOffset |
| @Description Interleaves the pod data |
| *****************************************************************************/ |
| static void InterleaveArray( |
| char * const pInterleaved, |
| CPODData &data, |
| const PVRTuint32 nNumVertex, |
| const PVRTuint32 nStride, |
| const PVRTuint32 nPadding, |
| PVRTuint32 &nOffset) |
| { |
| if(!data.nStride) |
| return; |
| |
| for(PVRTuint32 i = 0; i < nNumVertex; ++i) |
| memcpy(pInterleaved + i * nStride + nOffset, (char*)data.pData + i * data.nStride, data.nStride); |
| |
| FREE(data.pData); |
| data.pData = (unsigned char*)nOffset; |
| data.nStride = nStride; |
| nOffset += PVRTModelPODDataStride(data) + nPadding; |
| } |
| |
| /*!*************************************************************************** |
| @Function DeinterleaveArray |
| @Input data |
| @Input pInter |
| @Input nNumVertex |
| @Description DeInterleaves the pod data |
| *****************************************************************************/ |
| static void DeinterleaveArray( |
| CPODData &data, |
| const void * const pInter, |
| const PVRTuint32 nNumVertex, |
| const PVRTuint32 nAlignToNBytes) |
| { |
| const PVRTuint32 nSrcStride = data.nStride; |
| const PVRTuint32 nDestStride= PVRTModelPODDataStride(data); |
| const PVRTuint32 nAlignedStride = nDestStride + ((nAlignToNBytes - nDestStride % nAlignToNBytes) % nAlignToNBytes); |
| const char *pSrc = (char*)pInter + (size_t)data.pData; |
| |
| if(!nSrcStride) |
| return; |
| |
| data.pData = 0; |
| SafeAlloc(data.pData, nAlignedStride * nNumVertex); |
| data.nStride = nAlignedStride; |
| |
| for(PVRTuint32 i = 0; i < nNumVertex; ++i) |
| memcpy((char*)data.pData + i * nAlignedStride, pSrc + i * nSrcStride, nDestStride); |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODToggleInterleaved |
| @Modified mesh Mesh to modify |
| @Input ui32AlignToNBytes Align the interleaved data to this no. of bytes. |
| @Description Switches the supplied mesh to or from interleaved data format. |
| *****************************************************************************/ |
| void PVRTModelPODToggleInterleaved(SPODMesh &mesh, const PVRTuint32 ui32AlignToNBytes) |
| { |
| unsigned int i; |
| |
| if(!mesh.nNumVertex) |
| return; |
| |
| if(mesh.pInterleaved) |
| { |
| /* |
| De-interleave |
| */ |
| DeinterleaveArray(mesh.sVertex, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| DeinterleaveArray(mesh.sNormals, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| DeinterleaveArray(mesh.sTangents, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| DeinterleaveArray(mesh.sBinormals, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| |
| for(i = 0; i < mesh.nNumUVW; ++i) |
| DeinterleaveArray(mesh.psUVW[i], mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| |
| DeinterleaveArray(mesh.sVtxColours, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| DeinterleaveArray(mesh.sBoneIdx, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| DeinterleaveArray(mesh.sBoneWeight, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes); |
| FREE(mesh.pInterleaved); |
| } |
| else |
| { |
| PVRTuint32 nStride, nOffset, nBytes; |
| |
| #define NEEDED_PADDING(x) ((x && ui32AlignToNBytes) ? (ui32AlignToNBytes - x % ui32AlignToNBytes) % ui32AlignToNBytes : 0) |
| |
| // Interleave |
| |
| PVRTuint32 nVertexStride, nNormalStride, nTangentStride, nBinormalStride, nVtxColourStride, nBoneIdxStride, nBoneWeightStride; |
| PVRTuint32 nUVWStride[8]; |
| PVRTuint32 nVertexPadding, nNormalPadding, nTangentPadding, nBinormalPadding, nVtxColourPadding, nBoneIdxPadding, nBoneWeightPadding; |
| PVRTuint32 nUVWPadding[8]; |
| |
| _ASSERT(mesh.nNumUVW < 8); |
| |
| nStride = nVertexStride = PVRTModelPODDataStride(mesh.sVertex); |
| nStride += nVertexPadding = NEEDED_PADDING(nVertexStride); |
| |
| nStride += nNormalStride = PVRTModelPODDataStride(mesh.sNormals); |
| nStride += nNormalPadding = NEEDED_PADDING(nNormalStride); |
| |
| nStride += nTangentStride = PVRTModelPODDataStride(mesh.sTangents); |
| nStride += nTangentPadding = NEEDED_PADDING(nTangentStride); |
| |
| nStride += nBinormalStride = PVRTModelPODDataStride(mesh.sBinormals); |
| nStride += nBinormalPadding = NEEDED_PADDING(nBinormalStride); |
| |
| for(i = 0; i < mesh.nNumUVW; ++i) |
| { |
| nStride += nUVWStride[i] = PVRTModelPODDataStride(mesh.psUVW[i]); |
| nStride += nUVWPadding[i] = NEEDED_PADDING(nUVWStride[i]); |
| } |
| |
| nStride += nVtxColourStride = PVRTModelPODDataStride(mesh.sVtxColours); |
| nStride += nVtxColourPadding = NEEDED_PADDING(nVtxColourStride); |
| |
| nStride += nBoneIdxStride = PVRTModelPODDataStride(mesh.sBoneIdx); |
| nStride += nBoneIdxPadding = NEEDED_PADDING(nBoneIdxStride); |
| |
| nStride += nBoneWeightStride = PVRTModelPODDataStride(mesh.sBoneWeight); |
| nStride += nBoneWeightPadding = NEEDED_PADDING(nBoneWeightStride); |
| |
| #undef NEEDED_PADDING |
| // Allocate interleaved array |
| SafeAlloc(mesh.pInterleaved, mesh.nNumVertex * nStride); |
| |
| // Interleave the data |
| nOffset = 0; |
| |
| for(nBytes = 4; nBytes > 0; nBytes >>= 1) |
| { |
| if(PVRTModelPODDataTypeSize(mesh.sVertex.eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.sVertex, mesh.nNumVertex, nStride, nVertexPadding, nOffset); |
| |
| if(PVRTModelPODDataTypeSize(mesh.sNormals.eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.sNormals, mesh.nNumVertex, nStride, nNormalPadding, nOffset); |
| |
| if(PVRTModelPODDataTypeSize(mesh.sTangents.eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.sTangents, mesh.nNumVertex, nStride, nTangentPadding, nOffset); |
| |
| if(PVRTModelPODDataTypeSize(mesh.sBinormals.eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.sBinormals, mesh.nNumVertex, nStride, nBinormalPadding, nOffset); |
| |
| if(PVRTModelPODDataTypeSize(mesh.sVtxColours.eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.sVtxColours, mesh.nNumVertex, nStride, nVtxColourPadding, nOffset); |
| |
| for(i = 0; i < mesh.nNumUVW; ++i) |
| { |
| if(PVRTModelPODDataTypeSize(mesh.psUVW[i].eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.psUVW[i], mesh.nNumVertex, nStride, nUVWPadding[i], nOffset); |
| } |
| |
| if(PVRTModelPODDataTypeSize(mesh.sBoneIdx.eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.sBoneIdx, mesh.nNumVertex, nStride, nBoneIdxPadding, nOffset); |
| |
| if(PVRTModelPODDataTypeSize(mesh.sBoneWeight.eType) == nBytes) |
| InterleaveArray((char*)mesh.pInterleaved, mesh.sBoneWeight, mesh.nNumVertex, nStride, nBoneWeightPadding, nOffset); |
| } |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODDeIndex |
| @Modified mesh Mesh to modify |
| @Description De-indexes the supplied mesh. The mesh must be |
| Interleaved before calling this function. |
| *****************************************************************************/ |
| void PVRTModelPODDeIndex(SPODMesh &mesh) |
| { |
| unsigned char *pNew = 0; |
| |
| if(!mesh.pInterleaved || !mesh.nNumVertex) |
| return; |
| |
| _ASSERT(mesh.nNumVertex && mesh.nNumFaces); |
| |
| // Create a new vertex list |
| mesh.nNumVertex = PVRTModelPODCountIndices(mesh); |
| SafeAlloc(pNew, mesh.sVertex.nStride * mesh.nNumVertex); |
| |
| // Deindex the vertices |
| if(mesh.sFaces.eType == EPODDataUnsignedShort) |
| { |
| for(unsigned int i = 0; i < mesh.nNumVertex; ++i) |
| memcpy(pNew + i * mesh.sVertex.nStride, (char*)mesh.pInterleaved + ((unsigned short*)mesh.sFaces.pData)[i] * mesh.sVertex.nStride, mesh.sVertex.nStride); |
| } |
| else |
| { |
| _ASSERT(mesh.sFaces.eType == EPODDataUnsignedInt); |
| |
| for(unsigned int i = 0; i < mesh.nNumVertex; ++i) |
| memcpy(pNew + i * mesh.sVertex.nStride, (char*)mesh.pInterleaved + ((unsigned int*)mesh.sFaces.pData)[i] * mesh.sVertex.nStride, mesh.sVertex.nStride); |
| } |
| |
| // Replace the old vertex list |
| FREE(mesh.pInterleaved); |
| mesh.pInterleaved = pNew; |
| |
| // Get rid of the index list |
| FREE(mesh.sFaces.pData); |
| mesh.sFaces.n = 0; |
| mesh.sFaces.nStride = 0; |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODToggleStrips |
| @Modified mesh Mesh to modify |
| @Description Converts the supplied mesh to or from strips. |
| *****************************************************************************/ |
| void PVRTModelPODToggleStrips(SPODMesh &mesh) |
| { |
| CPODData old; |
| size_t nIdxSize, nTriStride; |
| |
| if(!mesh.nNumFaces) |
| return; |
| |
| _ASSERT(mesh.sFaces.n == 1); |
| nIdxSize = PVRTModelPODDataTypeSize(mesh.sFaces.eType); |
| nTriStride = PVRTModelPODDataStride(mesh.sFaces) * 3; |
| |
| old = mesh.sFaces; |
| mesh.sFaces.pData = 0; |
| SafeAlloc(mesh.sFaces.pData, nTriStride * mesh.nNumFaces); |
| |
| if(mesh.nNumStrips) |
| { |
| unsigned int nListIdxCnt, nStripIdxCnt; |
| |
| // Convert to list |
| nListIdxCnt = 0; |
| nStripIdxCnt = 0; |
| |
| for(unsigned int i = 0; i < mesh.nNumStrips; ++i) |
| { |
| for(unsigned int j = 0; j < mesh.pnStripLength[i]; ++j) |
| { |
| if(j) |
| { |
| _ASSERT(j == 1); // Because this will surely break with any other number |
| |
| memcpy( |
| (char*)mesh.sFaces.pData + nIdxSize * nListIdxCnt, |
| (char*)old.pData + nIdxSize * (nStripIdxCnt - 1), |
| nIdxSize); |
| nListIdxCnt += 1; |
| |
| memcpy( |
| (char*)mesh.sFaces.pData + nIdxSize * nListIdxCnt, |
| (char*)old.pData + nIdxSize * (nStripIdxCnt - 2), |
| nIdxSize); |
| nListIdxCnt += 1; |
| |
| memcpy( |
| (char*)mesh.sFaces.pData + nIdxSize * nListIdxCnt, |
| (char*)old.pData + nIdxSize * nStripIdxCnt, |
| nIdxSize); |
| nListIdxCnt += 1; |
| |
| nStripIdxCnt += 1; |
| } |
| else |
| { |
| memcpy( |
| (char*)mesh.sFaces.pData + nIdxSize * nListIdxCnt, |
| (char*)old.pData + nIdxSize * nStripIdxCnt, |
| nTriStride); |
| |
| nStripIdxCnt += 3; |
| nListIdxCnt += 3; |
| } |
| } |
| } |
| |
| _ASSERT(nListIdxCnt == mesh.nNumFaces*3); |
| FREE(mesh.pnStripLength); |
| mesh.nNumStrips = 0; |
| } |
| else |
| { |
| int nIdxCnt; |
| int nBatchCnt; |
| unsigned int n0, n1, n2; |
| unsigned int p0, p1, p2, nFaces; |
| unsigned char* pFaces; |
| |
| // Convert to strips |
| mesh.pnStripLength = (unsigned int*)calloc(mesh.nNumFaces, sizeof(*mesh.pnStripLength)); |
| mesh.nNumStrips = 0; |
| nIdxCnt = 0; |
| nBatchCnt = mesh.sBoneBatches.nBatchCnt ? mesh.sBoneBatches.nBatchCnt : 1; |
| |
| for(int h = 0; h < nBatchCnt; ++h) |
| { |
| n0 = 0; |
| n1 = 0; |
| n2 = 0; |
| |
| if(!mesh.sBoneBatches.nBatchCnt) |
| { |
| nFaces = mesh.nNumFaces; |
| pFaces = old.pData; |
| } |
| else |
| { |
| if(h + 1 < mesh.sBoneBatches.nBatchCnt) |
| nFaces = mesh.sBoneBatches.pnBatchOffset[h+1] - mesh.sBoneBatches.pnBatchOffset[h]; |
| else |
| nFaces = mesh.nNumFaces - mesh.sBoneBatches.pnBatchOffset[h]; |
| |
| pFaces = &old.pData[3 * mesh.sBoneBatches.pnBatchOffset[h] * old.nStride]; |
| } |
| |
| for(unsigned int i = 0; i < nFaces; ++i) |
| { |
| p0 = n0; |
| p1 = n1; |
| p2 = n2; |
| |
| PVRTVertexRead(&n0, (char*)pFaces + (3 * i + 0) * old.nStride, old.eType); |
| PVRTVertexRead(&n1, (char*)pFaces + (3 * i + 1) * old.nStride, old.eType); |
| PVRTVertexRead(&n2, (char*)pFaces + (3 * i + 2) * old.nStride, old.eType); |
| |
| if(mesh.pnStripLength[mesh.nNumStrips]) |
| { |
| if(mesh.pnStripLength[mesh.nNumStrips] & 0x01) |
| { |
| if(p1 == n1 && p2 == n0) |
| { |
| PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n2); |
| ++nIdxCnt; |
| mesh.pnStripLength[mesh.nNumStrips] += 1; |
| continue; |
| } |
| } |
| else |
| { |
| if(p2 == n1 && p0 == n0) |
| { |
| PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n2); |
| ++nIdxCnt; |
| mesh.pnStripLength[mesh.nNumStrips] += 1; |
| continue; |
| } |
| } |
| |
| ++mesh.nNumStrips; |
| } |
| |
| // Start of strip, copy entire triangle |
| PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n0); |
| ++nIdxCnt; |
| PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n1); |
| ++nIdxCnt; |
| PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n2); |
| ++nIdxCnt; |
| |
| mesh.pnStripLength[mesh.nNumStrips] += 1; |
| } |
| } |
| |
| if(mesh.pnStripLength[mesh.nNumStrips]) |
| ++mesh.nNumStrips; |
| |
| SafeRealloc(mesh.sFaces.pData, nIdxCnt * nIdxSize); |
| mesh.pnStripLength = (unsigned int*)realloc(mesh.pnStripLength, sizeof(*mesh.pnStripLength) * mesh.nNumStrips); |
| } |
| |
| FREE(old.pData); |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCountIndices |
| @Input mesh Mesh |
| @Return Number of indices used by mesh |
| @Description Counts the number of indices of a mesh |
| *****************************************************************************/ |
| unsigned int PVRTModelPODCountIndices(const SPODMesh &mesh) |
| { |
| return mesh.nNumStrips ? mesh.nNumFaces + (mesh.nNumStrips * 2) : mesh.nNumFaces * 3; |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCopyCPODData |
| @Input in |
| @Output out |
| @Input ui32No |
| @Input bInterleaved |
| @Description Used to copy a CPODData of a mesh |
| *****************************************************************************/ |
| void PVRTModelPODCopyCPODData(const CPODData &in, CPODData &out, unsigned int ui32No, bool bInterleaved) |
| { |
| FREE(out.pData); |
| |
| out.eType = in.eType; |
| out.n = in.n; |
| out.nStride = in.nStride; |
| |
| if(bInterleaved) |
| { |
| out.pData = in.pData; |
| } |
| else if(in.pData) |
| { |
| size_t ui32Size = PVRTModelPODDataStride(out) * ui32No; |
| |
| if(SafeAlloc(out.pData, ui32Size)) |
| memcpy(out.pData, in.pData, ui32Size); |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCopyNode |
| @Input in |
| @Output out |
| @Input nNumFrames |
| @Description Used to copy a pod node |
| *****************************************************************************/ |
| void PVRTModelPODCopyNode(const SPODNode &in, SPODNode &out, int nNumFrames) |
| { |
| out.nIdx = in.nIdx; |
| out.nIdxMaterial = in.nIdxMaterial; |
| out.nIdxParent = in.nIdxParent; |
| out.nAnimFlags = in.nAnimFlags; |
| out.pUserData = 0; |
| out.nUserDataSize = 0; |
| |
| if(in.pszName && SafeAlloc(out.pszName, strlen(in.pszName) + 1)) |
| memcpy(out.pszName, in.pszName, strlen(in.pszName) + 1); |
| |
| int i32Size; |
| |
| // Position |
| i32Size = in.nAnimFlags & ePODHasPositionAni ? PVRTModelPODGetAnimArraySize(in.pnAnimPositionIdx, nNumFrames, 3) : 3; |
| |
| if(in.pnAnimPositionIdx && SafeAlloc(out.pnAnimPositionIdx, nNumFrames)) |
| memcpy(out.pnAnimPositionIdx, in.pnAnimPositionIdx, sizeof(*out.pnAnimPositionIdx) * nNumFrames); |
| |
| if(in.pfAnimPosition && SafeAlloc(out.pfAnimPosition, i32Size)) |
| memcpy(out.pfAnimPosition, in.pfAnimPosition, sizeof(*out.pfAnimPosition) * i32Size); |
| |
| // Rotation |
| i32Size = in.nAnimFlags & ePODHasRotationAni ? PVRTModelPODGetAnimArraySize(in.pnAnimRotationIdx, nNumFrames, 4) : 4; |
| |
| if(in.pnAnimRotationIdx && SafeAlloc(out.pnAnimRotationIdx, nNumFrames)) |
| memcpy(out.pnAnimRotationIdx, in.pnAnimRotationIdx, sizeof(*out.pnAnimRotationIdx) * nNumFrames); |
| |
| if(in.pfAnimRotation && SafeAlloc(out.pfAnimRotation, i32Size)) |
| memcpy(out.pfAnimRotation, in.pfAnimRotation, sizeof(*out.pfAnimRotation) * i32Size); |
| |
| // Scale |
| i32Size = in.nAnimFlags & ePODHasScaleAni ? PVRTModelPODGetAnimArraySize(in.pnAnimScaleIdx, nNumFrames, 7) : 7; |
| |
| if(in.pnAnimScaleIdx && SafeAlloc(out.pnAnimScaleIdx, nNumFrames)) |
| memcpy(out.pnAnimScaleIdx, in.pnAnimScaleIdx, sizeof(*out.pnAnimScaleIdx) * nNumFrames); |
| |
| if(in.pfAnimScale && SafeAlloc(out.pfAnimScale, i32Size)) |
| memcpy(out.pfAnimScale, in.pfAnimScale, sizeof(*out.pfAnimScale) * i32Size); |
| |
| // Matrix |
| i32Size = in.nAnimFlags & ePODHasMatrixAni ? PVRTModelPODGetAnimArraySize(in.pnAnimMatrixIdx, nNumFrames, 16) : 16; |
| |
| if(in.pnAnimMatrixIdx && SafeAlloc(out.pnAnimMatrixIdx, nNumFrames)) |
| memcpy(out.pnAnimMatrixIdx, in.pnAnimMatrixIdx, sizeof(*out.pnAnimMatrixIdx) * nNumFrames); |
| |
| if(in.pfAnimMatrix && SafeAlloc(out.pfAnimMatrix, i32Size)) |
| memcpy(out.pfAnimMatrix, in.pfAnimMatrix, sizeof(*out.pfAnimMatrix) * i32Size); |
| |
| if(in.pUserData && SafeAlloc(out.pUserData, in.nUserDataSize)) |
| { |
| memcpy(out.pUserData, in.pUserData, in.nUserDataSize); |
| out.nUserDataSize = in.nUserDataSize; |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCopyMesh |
| @Input in |
| @Output out |
| @Description Used to copy a pod mesh |
| *****************************************************************************/ |
| void PVRTModelPODCopyMesh(const SPODMesh &in, SPODMesh &out) |
| { |
| unsigned int i; |
| bool bInterleaved = in.pInterleaved != 0; |
| out.nNumVertex = in.nNumVertex; |
| out.nNumFaces = in.nNumFaces; |
| |
| // Face data |
| PVRTModelPODCopyCPODData(in.sFaces , out.sFaces , out.nNumFaces * 3, false); |
| |
| // Vertex data |
| PVRTModelPODCopyCPODData(in.sVertex , out.sVertex , out.nNumVertex, bInterleaved); |
| PVRTModelPODCopyCPODData(in.sNormals , out.sNormals , out.nNumVertex, bInterleaved); |
| PVRTModelPODCopyCPODData(in.sTangents , out.sTangents , out.nNumVertex, bInterleaved); |
| PVRTModelPODCopyCPODData(in.sBinormals , out.sBinormals , out.nNumVertex, bInterleaved); |
| PVRTModelPODCopyCPODData(in.sVtxColours, out.sVtxColours, out.nNumVertex, bInterleaved); |
| PVRTModelPODCopyCPODData(in.sBoneIdx , out.sBoneIdx , out.nNumVertex, bInterleaved); |
| PVRTModelPODCopyCPODData(in.sBoneWeight, out.sBoneWeight, out.nNumVertex, bInterleaved); |
| |
| if(in.nNumUVW && SafeAlloc(out.psUVW, in.nNumUVW)) |
| { |
| out.nNumUVW = in.nNumUVW; |
| |
| for(i = 0; i < out.nNumUVW; ++i) |
| { |
| PVRTModelPODCopyCPODData(in.psUVW[i], out.psUVW[i], out.nNumVertex, bInterleaved); |
| } |
| } |
| |
| // Allocate and copy interleaved array |
| if(bInterleaved && SafeAlloc(out.pInterleaved, out.nNumVertex * in.sVertex.nStride)) |
| memcpy(out.pInterleaved, in.pInterleaved, out.nNumVertex * in.sVertex.nStride); |
| |
| if(in.pnStripLength && SafeAlloc(out.pnStripLength, out.nNumFaces)) |
| { |
| memcpy(out.pnStripLength, in.pnStripLength, sizeof(*out.pnStripLength) * out.nNumFaces); |
| out.nNumStrips = in.nNumStrips; |
| } |
| |
| if(in.sBoneBatches.nBatchCnt) |
| { |
| out.sBoneBatches.Release(); |
| |
| out.sBoneBatches.nBatchBoneMax = in.sBoneBatches.nBatchBoneMax; |
| out.sBoneBatches.nBatchCnt = in.sBoneBatches.nBatchCnt; |
| |
| if(in.sBoneBatches.pnBatches) |
| { |
| out.sBoneBatches.pnBatches = (int*) malloc(out.sBoneBatches.nBatchCnt * out.sBoneBatches.nBatchBoneMax * sizeof(*out.sBoneBatches.pnBatches)); |
| |
| if(out.sBoneBatches.pnBatches) |
| memcpy(out.sBoneBatches.pnBatches, in.sBoneBatches.pnBatches, out.sBoneBatches.nBatchCnt * out.sBoneBatches.nBatchBoneMax * sizeof(*out.sBoneBatches.pnBatches)); |
| } |
| |
| if(in.sBoneBatches.pnBatchBoneCnt) |
| { |
| out.sBoneBatches.pnBatchBoneCnt = (int*) malloc(out.sBoneBatches.nBatchCnt * sizeof(*out.sBoneBatches.pnBatchBoneCnt)); |
| |
| if(out.sBoneBatches.pnBatchBoneCnt) |
| memcpy(out.sBoneBatches.pnBatchBoneCnt, in.sBoneBatches.pnBatchBoneCnt, out.sBoneBatches.nBatchCnt * sizeof(*out.sBoneBatches.pnBatchBoneCnt)); |
| } |
| |
| if(in.sBoneBatches.pnBatchOffset) |
| { |
| out.sBoneBatches.pnBatchOffset = (int*) malloc(out.sBoneBatches.nBatchCnt * sizeof(out.sBoneBatches.pnBatchOffset)); |
| |
| if(out.sBoneBatches.pnBatchOffset) |
| memcpy(out.sBoneBatches.pnBatchOffset, in.sBoneBatches.pnBatchOffset, out.sBoneBatches.nBatchCnt * sizeof(*out.sBoneBatches.pnBatchOffset)); |
| } |
| } |
| |
| memcpy(out.mUnpackMatrix.f, in.mUnpackMatrix.f, sizeof(in.mUnpackMatrix.f[0]) * 16); |
| |
| out.ePrimitiveType = in.ePrimitiveType; |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCopyTexture |
| @Input in |
| @Output out |
| @Description Used to copy a pod texture |
| *****************************************************************************/ |
| void PVRTModelPODCopyTexture(const SPODTexture &in, SPODTexture &out) |
| { |
| if(in.pszName && SafeAlloc(out.pszName, strlen(in.pszName) + 1)) |
| memcpy(out.pszName, in.pszName, strlen(in.pszName) + 1); |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCopyMaterial |
| @Input in |
| @Output out |
| @Description Used to copy a pod material |
| *****************************************************************************/ |
| void PVRTModelPODCopyMaterial(const SPODMaterial &in, SPODMaterial &out) |
| { |
| memcpy(&out, &in, sizeof(SPODMaterial)); |
| |
| out.pszName = 0; |
| out.pszEffectFile = 0; |
| out.pszEffectName = 0; |
| out.pUserData = 0; |
| out.nUserDataSize = 0; |
| |
| if(in.pszName && SafeAlloc(out.pszName, strlen(in.pszName) + 1)) |
| memcpy(out.pszName, in.pszName, strlen(in.pszName) + 1); |
| |
| if(in.pszEffectFile && SafeAlloc(out.pszEffectFile, strlen(in.pszEffectFile) + 1)) |
| memcpy(out.pszEffectFile, in.pszEffectFile, strlen(in.pszEffectFile) + 1); |
| |
| if(in.pszEffectName && SafeAlloc(out.pszEffectName, strlen(in.pszEffectName) + 1)) |
| memcpy(out.pszEffectName, in.pszEffectName, strlen(in.pszEffectName) + 1); |
| |
| if(in.pUserData && SafeAlloc(out.pUserData, in.nUserDataSize)) |
| { |
| memcpy(out.pUserData, in.pUserData, in.nUserDataSize); |
| out.nUserDataSize = in.nUserDataSize; |
| } |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCopyCamera |
| @Input in |
| @Output out |
| @Input nNumFrames The number of animation frames |
| @Description Used to copy a pod camera |
| *****************************************************************************/ |
| void PVRTModelPODCopyCamera(const SPODCamera &in, SPODCamera &out, int nNumFrames) |
| { |
| memcpy(&out, &in, sizeof(SPODCamera)); |
| |
| out.pfAnimFOV = 0; |
| |
| if(in.pfAnimFOV && SafeAlloc(out.pfAnimFOV, nNumFrames)) |
| memcpy(out.pfAnimFOV, in.pfAnimFOV, sizeof(*out.pfAnimFOV) * nNumFrames); |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODCopyLight |
| @Input in |
| @Output out |
| @Description Used to copy a pod light |
| *****************************************************************************/ |
| void PVRTModelPODCopyLight(const SPODLight &in, SPODLight &out) |
| { |
| memcpy(&out, &in, sizeof(SPODLight)); |
| } |
| |
| /*!*************************************************************************** |
| @Function TransformCPODData |
| @Input in |
| @Output out |
| @Input idx Value to transform |
| @Input pPalette Palette of matrices to transform with |
| @Input pBoneIdx Array of indices into pPalette |
| @Input pBoneWeight Array of weights to weight the influence of the matrices of pPalette with |
| @Input i32BoneCnt Size of pBoneIdx and pBoneWeight |
| @Description Used to transform a particular value in a CPODData |
| *****************************************************************************/ |
| inline void TransformCPODData(CPODData &in, CPODData &out, int idx, PVRTMATRIX *pPalette, float *pBoneIdx, float *pBoneW, int i32BoneCnt, bool bNormalise) |
| { |
| PVRTVECTOR4f fResult, fOrig, fTmp; |
| |
| if(in.n) |
| { |
| |
| PVRTVertexRead(&fOrig, in.pData + (idx * in.nStride), in.eType, in.n); |
| |
| memset(&fResult.x, 0, sizeof(fResult)); |
| |
| if(i32BoneCnt) |
| { |
| for(int i = 0; i < i32BoneCnt; ++i) |
| { |
| int i32BoneIdx = (int) pBoneIdx[i]; |
| fTmp.x = vt2f(pPalette[i32BoneIdx].f[0]) * fOrig.x + vt2f(pPalette[i32BoneIdx].f[4]) * fOrig.y + vt2f(pPalette[i32BoneIdx].f[8]) * fOrig.z + vt2f(pPalette[i32BoneIdx].f[12]) * fOrig.w; |
| fTmp.y = vt2f(pPalette[i32BoneIdx].f[1]) * fOrig.x + vt2f(pPalette[i32BoneIdx].f[5]) * fOrig.y + vt2f(pPalette[i32BoneIdx].f[9]) * fOrig.z + vt2f(pPalette[i32BoneIdx].f[13]) * fOrig.w; |
| fTmp.z = vt2f(pPalette[i32BoneIdx].f[2]) * fOrig.x + vt2f(pPalette[i32BoneIdx].f[6]) * fOrig.y + vt2f(pPalette[i32BoneIdx].f[10])* fOrig.z + vt2f(pPalette[i32BoneIdx].f[14]) * fOrig.w; |
| fTmp.w = vt2f(pPalette[i32BoneIdx].f[3]) * fOrig.x + vt2f(pPalette[i32BoneIdx].f[7]) * fOrig.y + vt2f(pPalette[i32BoneIdx].f[11])* fOrig.z + vt2f(pPalette[i32BoneIdx].f[15]) * fOrig.w; |
| |
| fResult.x += fTmp.x * pBoneW[i]; |
| fResult.y += fTmp.y * pBoneW[i]; |
| fResult.z += fTmp.z * pBoneW[i]; |
| fResult.w += fTmp.w * pBoneW[i]; |
| } |
| } |
| else |
| { |
| fResult.x = vt2f(pPalette[0].f[0]) * fOrig.x + vt2f(pPalette[0].f[4]) * fOrig.y + vt2f(pPalette[0].f[8]) * fOrig.z + vt2f(pPalette[0].f[12]) * fOrig.w; |
| fResult.y = vt2f(pPalette[0].f[1]) * fOrig.x + vt2f(pPalette[0].f[5]) * fOrig.y + vt2f(pPalette[0].f[9]) * fOrig.z + vt2f(pPalette[0].f[13]) * fOrig.w; |
| fResult.z = vt2f(pPalette[0].f[2]) * fOrig.x + vt2f(pPalette[0].f[6]) * fOrig.y + vt2f(pPalette[0].f[10])* fOrig.z + vt2f(pPalette[0].f[14]) * fOrig.w; |
| fResult.w = vt2f(pPalette[0].f[3]) * fOrig.x + vt2f(pPalette[0].f[7]) * fOrig.y + vt2f(pPalette[0].f[11])* fOrig.z + vt2f(pPalette[0].f[15]) * fOrig.w; |
| } |
| |
| if(bNormalise) |
| { |
| double temp = (double)(fResult.x * fResult.x + fResult.y * fResult.y + fResult.z * fResult.z); |
| temp = 1.0 / sqrt(temp); |
| float f = (float)temp; |
| |
| fResult.x = fResult.x * f; |
| fResult.y = fResult.y * f; |
| fResult.z = fResult.z * f; |
| } |
| |
| PVRTVertexWrite(out.pData + (idx * out.nStride), out.eType, in.n, &fResult); |
| } |
| } |
| /*!*************************************************************************** |
| @Function PVRTModelPODFlattenToWorldSpace |
| @Input in - Source scene. All meshes must not be interleaved. |
| @Output out |
| @Description Used to flatten a pod scene to world space. All animation |
| and skinning information will be removed. The returned |
| position, normal, binormals and tangent data if present |
| will be returned as floats regardless of the input data |
| type. |
| *****************************************************************************/ |
| EPVRTError PVRTModelPODFlattenToWorldSpace(CPVRTModelPOD &in, CPVRTModelPOD &out) |
| { |
| unsigned int i, j, k, l; |
| PVRTMATRIX mWorld; |
| |
| // Destroy the out pod scene to make sure it is clean |
| out.Destroy(); |
| |
| // Init mesh and node arrays |
| SafeAlloc(out.pNode, in.nNumNode); |
| SafeAlloc(out.pMesh, in.nNumMeshNode); |
| |
| out.nNumNode = in.nNumNode; |
| out.nNumMesh = out.nNumMeshNode = in.nNumMeshNode; |
| |
| // Init scene values |
| out.nNumFrame = 0; |
| out.nFlags = in.nFlags; |
| out.fUnits = in.fUnits; |
| |
| for(i = 0; i < 3; ++i) |
| { |
| out.pfColourBackground[i] = in.pfColourBackground[i]; |
| out.pfColourAmbient[i] = in.pfColourAmbient[i]; |
| } |
| |
| // flatten meshes to world space |
| for(i = 0; i < in.nNumMeshNode; ++i) |
| { |
| |
| |
| SPODNode& inNode = in.pNode[i]; |
| SPODNode& outNode = out.pNode[i]; |
| |
| // Get the meshes |
| SPODMesh& inMesh = in.pMesh[inNode.nIdx]; |
| SPODMesh& outMesh = out.pMesh[i]; |
| |
| if(inMesh.pInterleaved != 0) // This function requires all the meshes to be de-interleaved |
| { |
| _ASSERT(inMesh.pInterleaved == 0); |
| out.Destroy(); // Destroy the out pod scene |
| return PVR_FAIL; |
| } |
| |
| // Copy the node |
| PVRTModelPODCopyNode(inNode, outNode, in.nNumFrame); |
| |
| // Strip out animation and parenting |
| outNode.nIdxParent = -1; |
| |
| outNode.nAnimFlags = 0; |
| FREE(outNode.pfAnimMatrix); |
| FREE(outNode.pfAnimPosition); |
| FREE(outNode.pfAnimRotation); |
| FREE(outNode.pfAnimScale); |
| |
| // Update the mesh ID. The rest of the IDs should remain correct |
| outNode.nIdx = i; |
| |
| // Copy the mesh |
| PVRTModelPODCopyMesh(inMesh, outMesh); |
| |
| // Strip out skinning information as that is no longer needed |
| outMesh.sBoneBatches.Release(); |
| outMesh.sBoneIdx.Reset(); |
| outMesh.sBoneWeight.Reset(); |
| |
| // Set the data type to float and resize the arrays as this function outputs transformed data as float only |
| if(inMesh.sVertex.n) |
| { |
| outMesh.sVertex.eType = EPODDataFloat; |
| outMesh.sVertex.pData = (unsigned char*) realloc(outMesh.sVertex.pData, PVRTModelPODDataStride(outMesh.sVertex) * inMesh.nNumVertex); |
| } |
| |
| if(inMesh.sNormals.n) |
| { |
| outMesh.sNormals.eType = EPODDataFloat; |
| outMesh.sNormals.pData = (unsigned char*) realloc(outMesh.sNormals.pData, PVRTModelPODDataStride(outMesh.sNormals) * inMesh.nNumVertex); |
| } |
| |
| if(inMesh.sTangents.n) |
| { |
| outMesh.sTangents.eType = EPODDataFloat; |
| outMesh.sTangents.pData = (unsigned char*) realloc(outMesh.sTangents.pData, PVRTModelPODDataStride(outMesh.sTangents) * inMesh.nNumVertex); |
| } |
| |
| if(inMesh.sBinormals.n) |
| { |
| outMesh.sBinormals.eType = EPODDataFloat; |
| outMesh.sBinormals.pData = (unsigned char*) realloc(outMesh.sBinormals.pData, PVRTModelPODDataStride(outMesh.sBinormals) * inMesh.nNumVertex); |
| } |
| |
| if(inMesh.sBoneBatches.nBatchCnt) |
| { |
| unsigned int ui32BatchPaletteSize = 0; |
| PVRTMATRIX *pPalette = 0; |
| PVRTMATRIX *pPaletteInvTrans = 0; |
| unsigned int ui32Offset = 0, ui32Strip = 0; |
| bool *pbTransformed = 0; |
| |
| SafeAlloc(pPalette, inMesh.sBoneBatches.nBatchBoneMax); |
| SafeAlloc(pPaletteInvTrans, inMesh.sBoneBatches.nBatchBoneMax); |
| SafeAlloc(pbTransformed, inMesh.nNumVertex); |
| |
| for(j = 0; j < (unsigned int) inMesh.sBoneBatches.nBatchCnt; ++j) |
| { |
| ui32BatchPaletteSize = (unsigned int) inMesh.sBoneBatches.pnBatchBoneCnt[j]; |
| |
| for(k = 0; k < ui32BatchPaletteSize; ++k) |
| { |
| // Get the Node of the bone |
| int i32NodeID = inMesh.sBoneBatches.pnBatches[j * inMesh.sBoneBatches.nBatchBoneMax + k]; |
| |
| // Get the World transformation matrix for this bone |
| in.GetBoneWorldMatrix(pPalette[k], inNode, in.pNode[i32NodeID]); |
| |
| // Get the inverse transpose of the 3x3 |
| if(inMesh.sNormals.n || inMesh.sTangents.n || inMesh.sBinormals.n) |
| { |
| pPaletteInvTrans[k] = pPalette[k]; |
| pPaletteInvTrans[k].f[3] = pPaletteInvTrans[k].f[7] = pPaletteInvTrans[k].f[11] = 0; |
| pPaletteInvTrans[k].f[12] = pPaletteInvTrans[k].f[13] = pPaletteInvTrans[k].f[14] = 0; |
| PVRTMatrixInverse(pPaletteInvTrans[k], pPaletteInvTrans[k]); |
| PVRTMatrixTranspose(pPaletteInvTrans[k], pPaletteInvTrans[k]); |
| } |
| } |
| // Calculate the number of triangles in the current batch |
| unsigned int ui32Tris; |
| |
| if(j + 1 < (unsigned int) inMesh.sBoneBatches.nBatchCnt) |
| ui32Tris = inMesh.sBoneBatches.pnBatchOffset[j + 1] - inMesh.sBoneBatches.pnBatchOffset[j]; |
| else |
| ui32Tris = inMesh.nNumFaces - inMesh.sBoneBatches.pnBatchOffset[j]; |
| |
| unsigned int idx; |
| float fBoneIdx[4], fBoneWeights[4]; |
| |
| if(inMesh.nNumStrips == 0) |
| { |
| ui32Offset = 3 * inMesh.sBoneBatches.pnBatchOffset[j]; |
| |
| for(l = ui32Offset; l < ui32Offset + (ui32Tris * 3); ++l) |
| { |
| if(inMesh.sFaces.pData) // Indexed Triangle Lists |
| PVRTVertexRead(&idx, inMesh.sFaces.pData + (l * inMesh.sFaces.nStride), inMesh.sFaces.eType); |
| else // Indexed Triangle Lists |
| idx = l; |
| |
| if(!pbTransformed[idx]) |
| { |
| PVRTVertexRead((PVRTVECTOR4f*) &fBoneIdx[0], inMesh.sBoneIdx.pData + (idx * inMesh.sBoneIdx.nStride), inMesh.sBoneIdx.eType, inMesh.sBoneIdx.n); |
| PVRTVertexRead((PVRTVECTOR4f*) &fBoneWeights[0], inMesh.sBoneWeight.pData + (idx * inMesh.sBoneWeight.nStride), inMesh.sBoneWeight.eType, inMesh.sBoneWeight.n); |
| |
| TransformCPODData(inMesh.sVertex, outMesh.sVertex, idx, pPalette, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, false); |
| TransformCPODData(inMesh.sNormals, outMesh.sNormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true); |
| TransformCPODData(inMesh.sTangents, outMesh.sTangents, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true); |
| TransformCPODData(inMesh.sBinormals, outMesh.sBinormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true); |
| pbTransformed[idx] = true; |
| } |
| } |
| } |
| else |
| { |
| unsigned int ui32TrisDrawn = 0; |
| |
| while(ui32TrisDrawn < ui32Tris) |
| { |
| for(l = ui32Offset; l < ui32Offset + (inMesh.pnStripLength[ui32Strip]+2); ++l) |
| { |
| if(inMesh.sFaces.pData) // Indexed Triangle Strips |
| PVRTVertexRead(&idx, inMesh.sFaces.pData + (l * inMesh.sFaces.nStride), inMesh.sFaces.eType); |
| else // Triangle Strips |
| idx = l; |
| |
| if(!pbTransformed[idx]) |
| { |
| PVRTVertexRead((PVRTVECTOR4f*) &fBoneIdx[0], inMesh.sBoneIdx.pData + (idx * inMesh.sBoneIdx.nStride), inMesh.sBoneIdx.eType, inMesh.sBoneIdx.n); |
| PVRTVertexRead((PVRTVECTOR4f*) &fBoneWeights[0], inMesh.sBoneWeight.pData + (idx * inMesh.sBoneWeight.nStride), inMesh.sBoneWeight.eType, inMesh.sBoneWeight.n); |
| |
| TransformCPODData(inMesh.sVertex, outMesh.sVertex, idx, pPalette, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, false); |
| TransformCPODData(inMesh.sNormals, outMesh.sNormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true); |
| TransformCPODData(inMesh.sTangents, outMesh.sTangents, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true); |
| TransformCPODData(inMesh.sBinormals, outMesh.sBinormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true); |
| pbTransformed[idx] = true; |
| } |
| } |
| |
| ui32Offset += inMesh.pnStripLength[ui32Strip] + 2; |
| ui32TrisDrawn += inMesh.pnStripLength[ui32Strip]; |
| |
| ++ui32Strip; |
| } |
| } |
| } |
| |
| FREE(pPalette); |
| FREE(pPaletteInvTrans); |
| FREE(pbTransformed); |
| } |
| else |
| { |
| // Get transformation matrix |
| in.GetWorldMatrix(mWorld, inNode); |
| PVRTMATRIX mWorldInvTrans; |
| |
| // Get the inverse transpose of the 3x3 |
| if(inMesh.sNormals.n || inMesh.sTangents.n || inMesh.sBinormals.n) |
| { |
| mWorldInvTrans = mWorld; |
| mWorldInvTrans.f[3] = mWorldInvTrans.f[7] = mWorldInvTrans.f[11] = 0; |
| mWorldInvTrans.f[12] = mWorldInvTrans.f[13] = mWorldInvTrans.f[14] = 0; |
| PVRTMatrixInverse(mWorldInvTrans, mWorldInvTrans); |
| PVRTMatrixTranspose(mWorldInvTrans, mWorldInvTrans); |
| } |
| |
| // Transform the vertices |
| for(j = 0; j < inMesh.nNumVertex; ++j) |
| { |
| TransformCPODData(inMesh.sVertex, outMesh.sVertex, j, &mWorld, 0, 0, 0, false); |
| TransformCPODData(inMesh.sNormals, outMesh.sNormals, j, &mWorldInvTrans, 0, 0, 0, true); |
| TransformCPODData(inMesh.sTangents, outMesh.sTangents, j, &mWorldInvTrans, 0, 0, 0, true); |
| TransformCPODData(inMesh.sBinormals, outMesh.sBinormals, j, &mWorldInvTrans, 0, 0, 0, true); |
| } |
| } |
| } |
| |
| // Copy the rest of the nodes |
| for(i = in.nNumMeshNode; i < in.nNumNode; ++i) |
| { |
| PVRTModelPODCopyNode(in.pNode[i], out.pNode[i], in.nNumFrame); |
| |
| // Strip out animation and parenting |
| out.pNode[i].nIdxParent = -1; |
| |
| out.pNode[i].nAnimFlags = 0; |
| FREE(out.pNode[i].pfAnimMatrix); |
| FREE(out.pNode[i].pnAnimMatrixIdx); |
| |
| FREE(out.pNode[i].pfAnimPosition); |
| FREE(out.pNode[i].pnAnimPositionIdx); |
| |
| FREE(out.pNode[i].pfAnimRotation); |
| FREE(out.pNode[i].pnAnimRotationIdx); |
| |
| FREE(out.pNode[i].pfAnimScale); |
| FREE(out.pNode[i].pnAnimScaleIdx); |
| |
| // Get world transformation matrix.... |
| in.GetWorldMatrix(mWorld, in.pNode[i]); |
| |
| // ...set the out node transformation matrix |
| if(SafeAlloc(out.pNode[i].pfAnimMatrix, 16)) |
| memcpy(out.pNode[i].pfAnimMatrix, mWorld.f, sizeof(PVRTMATRIX)); |
| } |
| |
| // Copy camera, lights |
| if(in.nNumCamera && SafeAlloc(out.pCamera, in.nNumCamera)) |
| { |
| out.nNumCamera = in.nNumCamera; |
| |
| for(i = 0; i < in.nNumCamera; ++i) |
| PVRTModelPODCopyCamera(in.pCamera[i], out.pCamera[i], in.nNumFrame); |
| } |
| |
| if(in.nNumLight && SafeAlloc(out.pLight, in.nNumLight)) |
| { |
| out.nNumLight = in.nNumLight; |
| |
| for(i = 0; i < out.nNumLight; ++i) |
| PVRTModelPODCopyLight(in.pLight[i], out.pLight[i]); |
| } |
| |
| // Copy textures |
| if(in.nNumTexture && SafeAlloc(out.pTexture, in.nNumTexture)) |
| { |
| out.nNumTexture = in.nNumTexture; |
| |
| for(i = 0; i < out.nNumTexture; ++i) |
| PVRTModelPODCopyTexture(in.pTexture[i], out.pTexture[i]); |
| } |
| |
| // Copy materials |
| if(in.nNumMaterial && SafeAlloc(out.pMaterial, in.nNumMaterial)) |
| { |
| out.nNumMaterial = in.nNumMaterial; |
| |
| for(i = 0; i < in.nNumMaterial; ++i) |
| PVRTModelPODCopyMaterial(in.pMaterial[i], out.pMaterial[i]); |
| } |
| |
| out.InitImpl(); |
| |
| return PVR_SUCCESS; |
| } |
| |
| static bool MergeTexture(const CPVRTModelPOD &src, CPVRTModelPOD &dst, const int &srcTexID, int &dstTexID) |
| { |
| if(srcTexID != -1 && srcTexID < (int) src.nNumTexture) |
| { |
| if(dstTexID == -1) |
| { |
| // Resize our texture array to add our texture |
| dst.pTexture = (SPODTexture*) realloc(dst.pTexture, (dst.nNumTexture + 1) * sizeof(SPODTexture)); |
| |
| if(!dst.pTexture) |
| return false; |
| |
| dstTexID = dst.nNumTexture; |
| ++dst.nNumTexture; |
| |
| dst.pTexture[dstTexID].pszName = (char*) malloc(strlen(src.pTexture[srcTexID].pszName) + 1); |
| strcpy(dst.pTexture[dstTexID].pszName, src.pTexture[srcTexID].pszName); |
| return true; |
| } |
| |
| // See if our texture names match |
| if(strcmp(src.pTexture[srcTexID].pszName, dst.pTexture[dstTexID].pszName) == 0) |
| return true; // Nothing to do |
| |
| // See if our texture filenames match |
| char * srcName = src.pTexture[srcTexID].pszName; |
| char * dstName = dst.pTexture[dstTexID].pszName; |
| bool bFoundPossibleEndOfFilename = false; |
| bool bStrMatch = true, bFilenameMatch = true; |
| |
| while(*srcName != '\0' && *dstName != '\0') |
| { |
| if(*srcName != *dstName) |
| { |
| if(!bFoundPossibleEndOfFilename) |
| return true; // They don't match |
| |
| bStrMatch = false; |
| } |
| |
| if(*srcName == '.') |
| { |
| if(!bStrMatch) |
| return true; // They don't match |
| |
| bFoundPossibleEndOfFilename = true; |
| bFilenameMatch = bStrMatch; |
| } |
| |
| ++srcName; |
| ++dstName; |
| } |
| |
| if(bFilenameMatch) |
| { |
| // Our filenames match but our extensions don't so merge our textures |
| FREE(dst.pTexture[dstTexID].pszName); |
| dst.pTexture[dstTexID].pszName = (char*) malloc(strlen(src.pTexture[srcTexID].pszName) + 1); |
| strcpy(dst.pTexture[dstTexID].pszName, src.pTexture[srcTexID].pszName); |
| return true; |
| } |
| |
| // Our texture names aren't the same so don't try and merge |
| } |
| |
| return true; |
| } |
| |
| /*!*************************************************************************** |
| @Function PVRTModelPODMergeMaterials |
| @Input src - Source scene |
| @Output dst - Destination scene |
| @Description This function takes two scenes and merges the textures, |
| PFX effects and blending parameters from the src materials |
| into the dst materials if they have the same material name. |
| *****************************************************************************/ |
| EPVRTError PVRTModelPODMergeMaterials(const CPVRTModelPOD &src, CPVRTModelPOD &dst) |
| { |
| if(!src.nNumMaterial || !dst.nNumMaterial) |
| return PVR_SUCCESS; |
| |
| bool *bMatched = (bool*) calloc(dst.nNumMaterial, sizeof(bool)); |
| |
| if(!bMatched) |
| return PVR_FAIL; |
| |
| for(unsigned int i = 0; i < src.nNumMaterial; ++i) |
| { |
| const SPODMaterial &srcMaterial = src.pMaterial[i]; |
| |
| // Match our current material with one in the dst |
| for(unsigned int j = 0; j < dst.nNumMaterial; ++j) |
| { |
| if(bMatched[j]) |
| continue; // We have already matched this material with another |
| |
| SPODMaterial &dstMaterial = dst.pMaterial[j]; |
| |
| // We've found a material with the same name |
| if(strcmp(srcMaterial.pszName, dstMaterial.pszName) == 0) |
| { |
| bMatched[j] = true; |
| |
| // Merge the textures |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexDiffuse, dstMaterial.nIdxTexDiffuse)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexAmbient, dstMaterial.nIdxTexAmbient)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexSpecularColour, dstMaterial.nIdxTexSpecularColour)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexSpecularLevel, dstMaterial.nIdxTexSpecularLevel)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexBump, dstMaterial.nIdxTexBump)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexEmissive, dstMaterial.nIdxTexEmissive)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexGlossiness, dstMaterial.nIdxTexGlossiness)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexOpacity, dstMaterial.nIdxTexOpacity)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexReflection, dstMaterial.nIdxTexReflection)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| if(!MergeTexture(src, dst, srcMaterial.nIdxTexRefraction, dstMaterial.nIdxTexRefraction)) |
| { |
| FREE(bMatched); |
| return PVR_FAIL; |
| } |
| |
| dstMaterial.eBlendSrcRGB = srcMaterial.eBlendSrcRGB; |
| dstMaterial.eBlendSrcA = srcMaterial.eBlendSrcA; |
| dstMaterial.eBlendDstRGB = srcMaterial.eBlendDstRGB; |
| dstMaterial.eBlendDstA = srcMaterial.eBlendDstA; |
| dstMaterial.eBlendOpRGB = srcMaterial.eBlendOpRGB; |
| dstMaterial.eBlendOpA = srcMaterial.eBlendOpA; |
| memcpy(dstMaterial.pfBlendColour, srcMaterial.pfBlendColour, 4 * sizeof(VERTTYPE)); |
| memcpy(dstMaterial.pfBlendFactor, srcMaterial.pfBlendFactor, 4 * sizeof(VERTTYPE)); |
| dstMaterial.nFlags = srcMaterial.nFlags; |
| |
| // Merge effect names |
| if(srcMaterial.pszEffectFile) |
| { |
| FREE(dstMaterial.pszEffectFile); |
| dstMaterial.pszEffectFile = (char*) malloc(strlen(srcMaterial.pszEffectFile) + 1); |
| strcpy(dstMaterial.pszEffectFile, srcMaterial.pszEffectFile); |
| } |
| |
| if(srcMaterial.pszEffectName) |
| { |
| FREE(dstMaterial.pszEffectName); |
| dstMaterial.pszEffectName = (char*) malloc(strlen(srcMaterial.pszEffectName) + 1); |
| strcpy(dstMaterial.pszEffectName, srcMaterial.pszEffectName); |
| } |
| |
| break; |
| } |
| } |
| } |
| |
| FREE(bMatched); |
| return PVR_SUCCESS; |
| } |
| |
| /***************************************************************************** |
| End of file (PVRTModelPOD.cpp) |
| *****************************************************************************/ |
| |