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