|  | /****************************************************************************** | 
|  |  | 
|  | @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) | 
|  | *****************************************************************************/ | 
|  |  |