Home | History | Annotate | Download | only in Tools
      1 /******************************************************************************
      2 
      3  @File         PVRTModelPOD.cpp
      4 
      5  @Title        PVRTModelPOD
      6 
      7  @Version
      8 
      9  @Copyright    Copyright (c) Imagination Technologies Limited.
     10 
     11  @Platform     ANSI compatible
     12 
     13  @Description  Code to load POD files - models exported from MAX.
     14 
     15 ******************************************************************************/
     16 #include <math.h>
     17 #include <stdlib.h>
     18 #include <stdio.h>
     19 #include <string.h>
     20 
     21 #include "PVRTGlobal.h"
     22 #if defined(BUILD_DX11)
     23 #include "PVRTContext.h"
     24 #endif
     25 #include "PVRTFixedPoint.h"
     26 #include "PVRTMatrix.h"
     27 #include "PVRTQuaternion.h"
     28 #include "PVRTVertex.h"
     29 #include "PVRTBoneBatch.h"
     30 #include "PVRTModelPOD.h"
     31 #include "PVRTMisc.h"
     32 #include "PVRTResourceFile.h"
     33 #include "PVRTTrans.h"
     34 
     35 /****************************************************************************
     36 ** Defines
     37 ****************************************************************************/
     38 #define PVRTMODELPOD_TAG_MASK			(0x80000000)
     39 #define PVRTMODELPOD_TAG_START			(0x00000000)
     40 #define PVRTMODELPOD_TAG_END			(0x80000000)
     41 
     42 #define CFAH		(1024)
     43 
     44 /****************************************************************************
     45 ** Enumerations
     46 ****************************************************************************/
     47 /*!****************************************************************************
     48  @Struct      EPODFileName
     49  @Brief       Enum for the binary pod blocks
     50 ******************************************************************************/
     51 enum EPODFileName
     52 {
     53 	ePODFileVersion				= 1000,
     54 	ePODFileScene,
     55 	ePODFileExpOpt,
     56 	ePODFileHistory,
     57 	ePODFileEndiannessMisMatch  = -402456576,
     58 
     59 	ePODFileColourBackground	= 2000,
     60 	ePODFileColourAmbient,
     61 	ePODFileNumCamera,
     62 	ePODFileNumLight,
     63 	ePODFileNumMesh,
     64 	ePODFileNumNode,
     65 	ePODFileNumMeshNode,
     66 	ePODFileNumTexture,
     67 	ePODFileNumMaterial,
     68 	ePODFileNumFrame,
     69 	ePODFileCamera,		// Will come multiple times
     70 	ePODFileLight,		// Will come multiple times
     71 	ePODFileMesh,		// Will come multiple times
     72 	ePODFileNode,		// Will come multiple times
     73 	ePODFileTexture,	// Will come multiple times
     74 	ePODFileMaterial,	// Will come multiple times
     75 	ePODFileFlags,
     76 	ePODFileFPS,
     77 	ePODFileUserData,
     78 	ePODFileUnits,
     79 
     80 	ePODFileMatName				= 3000,
     81 	ePODFileMatIdxTexDiffuse,
     82 	ePODFileMatOpacity,
     83 	ePODFileMatAmbient,
     84 	ePODFileMatDiffuse,
     85 	ePODFileMatSpecular,
     86 	ePODFileMatShininess,
     87 	ePODFileMatEffectFile,
     88 	ePODFileMatEffectName,
     89 	ePODFileMatIdxTexAmbient,
     90 	ePODFileMatIdxTexSpecularColour,
     91 	ePODFileMatIdxTexSpecularLevel,
     92 	ePODFileMatIdxTexBump,
     93 	ePODFileMatIdxTexEmissive,
     94 	ePODFileMatIdxTexGlossiness,
     95 	ePODFileMatIdxTexOpacity,
     96 	ePODFileMatIdxTexReflection,
     97 	ePODFileMatIdxTexRefraction,
     98 	ePODFileMatBlendSrcRGB,
     99 	ePODFileMatBlendSrcA,
    100 	ePODFileMatBlendDstRGB,
    101 	ePODFileMatBlendDstA,
    102 	ePODFileMatBlendOpRGB,
    103 	ePODFileMatBlendOpA,
    104 	ePODFileMatBlendColour,
    105 	ePODFileMatBlendFactor,
    106 	ePODFileMatFlags,
    107 	ePODFileMatUserData,
    108 
    109 	ePODFileTexName				= 4000,
    110 
    111 	ePODFileNodeIdx				= 5000,
    112 	ePODFileNodeName,
    113 	ePODFileNodeIdxMat,
    114 	ePODFileNodeIdxParent,
    115 	ePODFileNodePos,
    116 	ePODFileNodeRot,
    117 	ePODFileNodeScale,
    118 	ePODFileNodeAnimPos,
    119 	ePODFileNodeAnimRot,
    120 	ePODFileNodeAnimScale,
    121 	ePODFileNodeMatrix,
    122 	ePODFileNodeAnimMatrix,
    123 	ePODFileNodeAnimFlags,
    124 	ePODFileNodeAnimPosIdx,
    125 	ePODFileNodeAnimRotIdx,
    126 	ePODFileNodeAnimScaleIdx,
    127 	ePODFileNodeAnimMatrixIdx,
    128 	ePODFileNodeUserData,
    129 
    130 	ePODFileMeshNumVtx			= 6000,
    131 	ePODFileMeshNumFaces,
    132 	ePODFileMeshNumUVW,
    133 	ePODFileMeshFaces,
    134 	ePODFileMeshStripLength,
    135 	ePODFileMeshNumStrips,
    136 	ePODFileMeshVtx,
    137 	ePODFileMeshNor,
    138 	ePODFileMeshTan,
    139 	ePODFileMeshBin,
    140 	ePODFileMeshUVW,			// Will come multiple times
    141 	ePODFileMeshVtxCol,
    142 	ePODFileMeshBoneIdx,
    143 	ePODFileMeshBoneWeight,
    144 	ePODFileMeshInterleaved,
    145 	ePODFileMeshBoneBatches,
    146 	ePODFileMeshBoneBatchBoneCnts,
    147 	ePODFileMeshBoneBatchOffsets,
    148 	ePODFileMeshBoneBatchBoneMax,
    149 	ePODFileMeshBoneBatchCnt,
    150 	ePODFileMeshUnpackMatrix,
    151 
    152 	ePODFileLightIdxTgt			= 7000,
    153 	ePODFileLightColour,
    154 	ePODFileLightType,
    155 	ePODFileLightConstantAttenuation,
    156 	ePODFileLightLinearAttenuation,
    157 	ePODFileLightQuadraticAttenuation,
    158 	ePODFileLightFalloffAngle,
    159 	ePODFileLightFalloffExponent,
    160 
    161 	ePODFileCamIdxTgt			= 8000,
    162 	ePODFileCamFOV,
    163 	ePODFileCamFar,
    164 	ePODFileCamNear,
    165 	ePODFileCamAnimFOV,
    166 
    167 	ePODFileDataType			= 9000,
    168 	ePODFileN,
    169 	ePODFileStride,
    170 	ePODFileData
    171 };
    172 
    173 /****************************************************************************
    174 ** Structures
    175 ****************************************************************************/
    176 struct SPVRTPODImpl
    177 {
    178 	VERTTYPE	fFrame;		/*!< Frame number */
    179 	VERTTYPE	fBlend;		/*!< Frame blend	(AKA fractional part of animation frame number) */
    180 	int			nFrame;		/*!< Frame number (AKA integer part of animation frame number) */
    181 
    182 	VERTTYPE	*pfCache;		/*!< Cache indicating the frames at which the matrix cache was filled */
    183 	PVRTMATRIX	*pWmCache;		/*!< Cache of world matrices */
    184 	PVRTMATRIX	*pWmZeroCache;	/*!< Pre-calculated frame 0 matrices */
    185 
    186 	bool		bFromMemory;	/*!< Was the mesh data loaded from memory? */
    187 
    188 #ifdef _DEBUG
    189 	PVRTint64 nWmTotal, nWmCacheHit, nWmZeroCacheHit;
    190 	float	fHitPerc, fHitPercZero;
    191 #endif
    192 };
    193 
    194 /****************************************************************************
    195 ** Local code: Memory allocation
    196 ****************************************************************************/
    197 
    198 /*!***************************************************************************
    199  @Function			SafeAlloc
    200  @Input				cnt
    201  @Output			ptr
    202  @Return			false if memory allocation failed
    203  @Description		Allocates a block of memory.
    204 *****************************************************************************/
    205 template <typename T>
    206 bool SafeAlloc(T* &ptr, size_t cnt)
    207 {
    208 	_ASSERT(!ptr);
    209 	if(cnt)
    210 	{
    211 		ptr = (T*)calloc(cnt, sizeof(T));
    212 		_ASSERT(ptr);
    213 		if(!ptr)
    214 			return false;
    215 	}
    216 	return true;
    217 }
    218 
    219 /*!***************************************************************************
    220  @Function			SafeRealloc
    221  @Modified			ptr
    222  @Input				cnt
    223  @Description		Changes the size of a memory allocation.
    224 *****************************************************************************/
    225 template <typename T>
    226 void SafeRealloc(T* &ptr, size_t cnt)
    227 {
    228 	ptr = (T*)realloc(ptr, cnt * sizeof(T));
    229 	_ASSERT(ptr);
    230 }
    231 
    232 /****************************************************************************
    233 ** Class: CPODData
    234 ****************************************************************************/
    235 /*!***************************************************************************
    236 @Function			Reset
    237 @Description		Resets the POD Data to NULL
    238 *****************************************************************************/
    239 void CPODData::Reset()
    240 {
    241 	eType = EPODDataFloat;
    242 	n = 0;
    243 	nStride = 0;
    244 	FREE(pData);
    245 }
    246 
    247 // check32BitType and check16BitType are structs where only the specialisations have a standard declaration (complete type)
    248 // if this struct is instantiated with a different type then the compiler will choke on it
    249 // Place a line like: " 		check32BitType<channelType>();	" in a template function
    250 // to ensure it won't be called using a type of the wrong size.
    251 template<class T> struct check32BitType;
    252 template<> struct check32BitType<unsigned int> {};
    253 template<> struct check32BitType<int> {};
    254 template<> struct check32BitType<float> {};
    255 template<class T> struct check16BitType;
    256 template<> struct check16BitType<unsigned short> {};
    257 template<> struct check16BitType<short> {};
    258 
    259 /*!***************************************************************************
    260  Class: CSource
    261 *****************************************************************************/
    262 class CSource
    263 {
    264 public:
    265 	/*!***************************************************************************
    266 	@Function			~CSource
    267 	@Description		Destructor
    268 	*****************************************************************************/
    269 	virtual ~CSource() {};
    270 	virtual bool Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead) = 0;
    271 	virtual bool Skip(const unsigned int nBytes) = 0;
    272 
    273 	template <typename T>
    274 	bool Read(T &n)
    275 	{
    276 		return Read(&n, sizeof(T));
    277 	}
    278 
    279 	template <typename T>
    280 	bool Read32(T &n)
    281 	{
    282 		unsigned char ub[4];
    283 
    284 		if(Read(&ub, 4))
    285 		{
    286 			unsigned int *pn = (unsigned int*) &n;
    287 			*pn = (unsigned int) ((ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0]);
    288 			return true;
    289 		}
    290 
    291 		return false;
    292 	}
    293 
    294 	template <typename T>
    295 	bool Read16(T &n)
    296 	{
    297 		unsigned char ub[2];
    298 
    299 		if(Read(&ub, 2))
    300 		{
    301 			unsigned short *pn = (unsigned short*) &n;
    302 			*pn = (unsigned short) ((ub[1] << 8) | ub[0]);
    303 			return true;
    304 		}
    305 
    306 		return false;
    307 	}
    308 
    309 	bool ReadMarker(unsigned int &nName, unsigned int &nLen);
    310 
    311 	template <typename T>
    312 	bool ReadAfterAlloc(T* &lpBuffer, const unsigned int dwNumberOfBytesToRead)
    313 	{
    314 		if(!SafeAlloc(lpBuffer, dwNumberOfBytesToRead))
    315 			return false;
    316 		return Read(lpBuffer, dwNumberOfBytesToRead);
    317 	}
    318 
    319 	template <typename T>
    320 	bool ReadAfterAlloc32(T* &lpBuffer, const unsigned int dwNumberOfBytesToRead)
    321 	{
    322 		check32BitType<T>();
    323 		if(!SafeAlloc(lpBuffer, dwNumberOfBytesToRead/4))
    324 			return false;
    325 		return ReadArray32((unsigned int*) lpBuffer, dwNumberOfBytesToRead / 4);
    326 	}
    327 
    328 	template <typename T>
    329 	bool ReadArray32(T* pn, const unsigned int i32Size)
    330 	{
    331 		check32BitType<T>();
    332 		bool bRet = true;
    333 
    334 		for(unsigned int i = 0; i < i32Size; ++i)
    335 			bRet &= Read32(pn[i]);
    336 
    337 		return bRet;
    338 	}
    339 
    340 	template <typename T>
    341 	bool ReadAfterAlloc16(T* &lpBuffer, const unsigned int dwNumberOfBytesToRead)
    342 	{
    343 		check16BitType<T>();
    344 		if(!SafeAlloc(lpBuffer, dwNumberOfBytesToRead/2 ))
    345 			return false;
    346 		return ReadArray16((unsigned short*) lpBuffer, dwNumberOfBytesToRead / 2);
    347 	}
    348 
    349 	bool ReadArray16(unsigned short* pn, unsigned int i32Size)
    350 	{
    351 		bool bRet = true;
    352 
    353 		for(unsigned int i = 0; i < i32Size; ++i)
    354 			bRet &= Read16(pn[i]);
    355 
    356 		return bRet;
    357 	}
    358 };
    359 
    360 bool CSource::ReadMarker(unsigned int &nName, unsigned int &nLen)
    361 {
    362 	if(!Read32(nName))
    363 		return false;
    364 	if(!Read32(nLen))
    365 		return false;
    366 	return true;
    367 }
    368 
    369 /*!***************************************************************************
    370  Class: CSourceStream
    371 *****************************************************************************/
    372 class CSourceStream : public CSource
    373 {
    374 protected:
    375 	CPVRTResourceFile* m_pFile;
    376 	size_t m_BytesReadCount;
    377 
    378 public:
    379 	/*!***************************************************************************
    380 	@Function			CSourceStream
    381 	@Description		Constructor
    382 	*****************************************************************************/
    383 	CSourceStream() : m_pFile(0), m_BytesReadCount(0) {}
    384 
    385 	/*!***************************************************************************
    386 	@Function			~CSourceStream
    387 	@Description		Destructor
    388 	*****************************************************************************/
    389 	virtual ~CSourceStream();
    390 
    391 	bool Init(const char * const pszFileName);
    392 	bool Init(const char * const pData, const size_t i32Size);
    393 
    394 	virtual bool Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead);
    395 	virtual bool Skip(const unsigned int nBytes);
    396 };
    397 
    398 /*!***************************************************************************
    399 @Function			~CSourceStream
    400 @Description		Destructor
    401 *****************************************************************************/
    402 CSourceStream::~CSourceStream()
    403 {
    404 	delete m_pFile;
    405 }
    406 
    407 /*!***************************************************************************
    408 @Function			Init
    409 @Input				pszFileName		Source file
    410 @Description		Initialises the source stream with a file at the specified
    411 					directory.
    412 *****************************************************************************/
    413 bool CSourceStream::Init(const char * const pszFileName)
    414 {
    415 	m_BytesReadCount = 0;
    416 	if (m_pFile)
    417 	{
    418 		delete m_pFile;
    419 		m_pFile = 0;
    420 	}
    421 
    422 	if(!pszFileName)
    423 		return false;
    424 
    425 	m_pFile = new CPVRTResourceFile(pszFileName);
    426 	if (!m_pFile->IsOpen())
    427 	{
    428 		delete m_pFile;
    429 		m_pFile = 0;
    430 		return false;
    431 	}
    432 	return true;
    433 }
    434 
    435 /*!***************************************************************************
    436 @Function			Init
    437 @Input				pData			Address of the source data
    438 @Input				i32Size			Size of the data (in bytes)
    439 @Description		Initialises the source stream with the data at the specified
    440 					directory.
    441 *****************************************************************************/
    442 bool CSourceStream::Init(const char * pData, size_t i32Size)
    443 {
    444 	m_BytesReadCount = 0;
    445 	if (m_pFile) delete m_pFile;
    446 
    447 	m_pFile = new CPVRTResourceFile(pData, i32Size);
    448 	if (!m_pFile->IsOpen())
    449 	{
    450 		delete m_pFile;
    451 		m_pFile = 0;
    452 		return false;
    453 	}
    454 	return true;
    455 }
    456 
    457 /*!***************************************************************************
    458 @Function			Read
    459 @Modified			lpBuffer				Buffer to write the data into
    460 @Input				dwNumberOfBytesToRead	Number of bytes to read
    461 @Description		Reads specified number of bytes from the source stream
    462 					into the output buffer.
    463 *****************************************************************************/
    464 bool CSourceStream::Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead)
    465 {
    466 	_ASSERT(lpBuffer);
    467 	_ASSERT(m_pFile);
    468 
    469 	if (m_BytesReadCount + dwNumberOfBytesToRead > m_pFile->Size()) return false;
    470 
    471 	memcpy(lpBuffer, &((char*) m_pFile->DataPtr())[m_BytesReadCount], dwNumberOfBytesToRead);
    472 
    473 	m_BytesReadCount += dwNumberOfBytesToRead;
    474 	return true;
    475 }
    476 
    477 /*!***************************************************************************
    478 @Function			Skip
    479 @Input				nBytes			The number of bytes to skip
    480 @Description		Skips the specified number of bytes of the source stream.
    481 *****************************************************************************/
    482 bool CSourceStream::Skip(const unsigned int nBytes)
    483 {
    484 	if (m_BytesReadCount + nBytes > m_pFile->Size()) return false;
    485 	m_BytesReadCount += nBytes;
    486 	return true;
    487 }
    488 
    489 #if defined(_WIN32)
    490 /*!***************************************************************************
    491  Class: CSourceResource
    492 *****************************************************************************/
    493 class CSourceResource : public CSource
    494 {
    495 protected:
    496 	const unsigned char	*m_pData;
    497 	unsigned int		m_nSize, m_nReadPos;
    498 
    499 public:
    500 	bool Init(const TCHAR * const pszName);
    501 	virtual bool Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead);
    502 	virtual bool Skip(const unsigned int nBytes);
    503 };
    504 
    505 /*!***************************************************************************
    506 @Function			Init
    507 @Input				pszName			The file extension of the resource file
    508 @Description		Initialises the source resource from the data at the
    509 					specified file extension.
    510 *****************************************************************************/
    511 bool CSourceResource::Init(const TCHAR * const pszName)
    512 {
    513 	HRSRC	hR;
    514 	HGLOBAL	hG;
    515 
    516 	// Find the resource
    517 	hR = FindResource(GetModuleHandle(NULL), pszName, RT_RCDATA);
    518 	if(!hR)
    519 		return false;
    520 
    521 	// How big is the resource?
    522 	m_nSize = SizeofResource(NULL, hR);
    523 	if(!m_nSize)
    524 		return false;
    525 
    526 	// Get a pointer to the resource data
    527 	hG = LoadResource(NULL, hR);
    528 	if(!hG)
    529 		return false;
    530 
    531 	m_pData = (unsigned char*)LockResource(hG);
    532 	if(!m_pData)
    533 		return false;
    534 
    535 	m_nReadPos = 0;
    536 	return true;
    537 }
    538 
    539 /*!***************************************************************************
    540 @Function			Read
    541 @Modified			lpBuffer				The buffer to write to
    542 @Input				dwNumberOfBytesToRead	The number of bytes to read
    543 @Description		Reads data from the resource to the specified output buffer.
    544 *****************************************************************************/
    545 bool CSourceResource::Read(void* lpBuffer, const unsigned int dwNumberOfBytesToRead)
    546 {
    547 	if(m_nReadPos + dwNumberOfBytesToRead > m_nSize)
    548 		return false;
    549 
    550 	_ASSERT(lpBuffer);
    551 	memcpy(lpBuffer, &m_pData[m_nReadPos], dwNumberOfBytesToRead);
    552 	m_nReadPos += dwNumberOfBytesToRead;
    553 	return true;
    554 }
    555 
    556 bool CSourceResource::Skip(const unsigned int nBytes)
    557 {
    558 	if(m_nReadPos + nBytes > m_nSize)
    559 		return false;
    560 
    561 	m_nReadPos += nBytes;
    562 	return true;
    563 }
    564 
    565 #endif /* _WIN32 */
    566 
    567 /****************************************************************************
    568 ** Local code: File writing
    569 ****************************************************************************/
    570 
    571 /*!***************************************************************************
    572  @Function			WriteFileSafe
    573  @Input				pFile
    574  @Input				lpBuffer
    575  @Input				nNumberOfBytesToWrite
    576  @Return			true if successful
    577  @Description		Writes data to a file, checking return codes.
    578 *****************************************************************************/
    579 static bool WriteFileSafe(FILE *pFile, const void * const lpBuffer, const unsigned int nNumberOfBytesToWrite)
    580 {
    581 	if(nNumberOfBytesToWrite)
    582 	{
    583 		size_t count = fwrite(lpBuffer, nNumberOfBytesToWrite, 1, pFile);
    584 		return count == 1;
    585 	}
    586 	return true;
    587 }
    588 
    589 static bool WriteFileSafe16(FILE *pFile, const unsigned short * const lpBuffer, const unsigned int nSize)
    590 {
    591 	if(nSize)
    592 	{
    593 		unsigned char ub[2];
    594 		bool bRet = true;
    595 
    596 		for(unsigned int i = 0; i < nSize; ++i)
    597 		{
    598 			ub[0] = (unsigned char) lpBuffer[i];
    599 			ub[1] = lpBuffer[i] >> 8;
    600 
    601 			bRet &= (fwrite(ub, 2, 1, pFile) == 1);
    602 		}
    603 
    604 		return bRet;
    605 	}
    606 	return true;
    607 }
    608 
    609 static bool WriteFileSafe32(FILE *pFile, const unsigned int * const lpBuffer, const unsigned int nSize)
    610 {
    611 	if(nSize)
    612 	{
    613 		unsigned char ub[4];
    614 		bool bRet = true;
    615 
    616 		for(unsigned int i = 0; i < nSize; ++i)
    617 		{
    618 			ub[0] = (unsigned char) (lpBuffer[i]);
    619 			ub[1] = (unsigned char) (lpBuffer[i] >> 8);
    620 			ub[2] = (unsigned char) (lpBuffer[i] >> 16);
    621 			ub[3] = (unsigned char) (lpBuffer[i] >> 24);
    622 
    623 			bRet &= (fwrite(ub, 4, 1, pFile) == 1);
    624 		}
    625 
    626 		return bRet;
    627 	}
    628 	return true;
    629 }
    630 /*!***************************************************************************
    631  @Function			WriteMarker
    632  @Input				pFile
    633  @Input				nName
    634  @Input				bEnd
    635  @Input				nLen
    636  Return				true if successful
    637  @Description		Write a marker to a POD file. If bEnd if false, it's a
    638 					beginning marker, otherwise it's an end marker.
    639 *****************************************************************************/
    640 static bool WriteMarker(
    641 	FILE				* const pFile,
    642 	const unsigned int	nName,
    643 	const bool			bEnd,
    644 	const unsigned int	nLen = 0)
    645 {
    646 	unsigned int nMarker;
    647 	bool bRet;
    648 
    649 	_ASSERT((nName & ~PVRTMODELPOD_TAG_MASK) == nName);
    650 	nMarker = nName | (bEnd ? PVRTMODELPOD_TAG_END : PVRTMODELPOD_TAG_START);
    651 
    652 	bRet  = WriteFileSafe32(pFile, &nMarker, 1);
    653 	bRet &= WriteFileSafe32(pFile, &nLen, 1);
    654 
    655 	return bRet;
    656 }
    657 
    658 /*!***************************************************************************
    659  @Function			WriteData
    660  @Input				pFile
    661  @Input				nName
    662  @Input				pData
    663  @Input				nLen
    664  @Return			true if successful
    665  @Description		Write nLen bytes of data from pData, bracketed by an nName
    666 					begin/end markers.
    667 *****************************************************************************/
    668 static bool WriteData(
    669 	FILE				* const pFile,
    670 	const unsigned int	nName,
    671 	const void			* const pData,
    672 	const unsigned int	nLen)
    673 {
    674 	if(pData)
    675 	{
    676 		_ASSERT(nLen);
    677 		if(!WriteMarker(pFile, nName, false, nLen)) return false;
    678 		if(!WriteFileSafe(pFile, pData, nLen)) return false;
    679 		if(!WriteMarker(pFile, nName, true)) return false;
    680 	}
    681 	return true;
    682 }
    683 
    684 /*!***************************************************************************
    685  @Function			WriteData16
    686  @Input				pFile
    687  @Input				nName
    688  @Input				pData
    689  @Input				i32Size
    690  @Return			true if successful
    691  @Description		Write i32Size no. of unsigned shorts from pData, bracketed by
    692 					an nName begin/end markers.
    693 *****************************************************************************/
    694 template <typename T>
    695 static bool WriteData16(
    696 	FILE				* const pFile,
    697 	const unsigned int	nName,
    698 	const T	* const pData,
    699 	int i32Size = 1)
    700 {
    701 	if(pData)
    702 	{
    703 		if(!WriteMarker(pFile, nName, false, 2 * i32Size)) return false;
    704 		if(!WriteFileSafe16(pFile, (unsigned short*) pData, i32Size)) return false;
    705 		if(!WriteMarker(pFile, nName, true)) return false;
    706 	}
    707 	return true;
    708 }
    709 
    710 /*!***************************************************************************
    711  @Function			WriteData32
    712  @Input				pFile
    713  @Input				nName
    714  @Input				pData
    715  @Input				i32Size
    716  @Return			true if successful
    717  @Description		Write i32Size no. of unsigned ints from pData, bracketed by
    718 					an nName begin/end markers.
    719 *****************************************************************************/
    720 template <typename T>
    721 static bool WriteData32(
    722 	FILE				* const pFile,
    723 	const unsigned int	nName,
    724 	const T	* const pData,
    725 	int i32Size = 1)
    726 {
    727 	if(pData)
    728 	{
    729 		if(!WriteMarker(pFile, nName, false, 4 * i32Size)) return false;
    730 		if(!WriteFileSafe32(pFile, (unsigned int*) pData, i32Size)) return false;
    731 		if(!WriteMarker(pFile, nName, true)) return false;
    732 	}
    733 	return true;
    734 }
    735 
    736 /*!***************************************************************************
    737  @Function			WriteData
    738  @Input				pFile
    739  @Input				nName
    740  @Input				n
    741  @Return			true if successful
    742  @Description		Write the value n, bracketed by an nName begin/end markers.
    743 *****************************************************************************/
    744 template <typename T>
    745 static bool WriteData(
    746 	FILE				* const pFile,
    747 	const unsigned int	nName,
    748 	const T				&n)
    749 {
    750 	unsigned int nSize = sizeof(T);
    751 
    752 	bool bRet = WriteData(pFile, nName, (void*)&n, nSize);
    753 
    754 	return bRet;
    755 }
    756 
    757 /*!***************************************************************************
    758  @Function			WriteCPODData
    759  @Input				pFile
    760  @Input				nName
    761  @Input				n
    762  @Input				nEntries
    763  @Input				bValidData
    764  @Return			true if successful
    765  @Description		Write the value n, bracketed by an nName begin/end markers.
    766 *****************************************************************************/
    767 static bool WriteCPODData(
    768 	FILE				* const pFile,
    769 	const unsigned int	nName,
    770 	const CPODData		&n,
    771 	const unsigned int	nEntries,
    772 	const bool			bValidData)
    773 {
    774 	if(!WriteMarker(pFile, nName, false)) return false;
    775 	if(!WriteData32(pFile, ePODFileDataType, &n.eType)) return false;
    776 	if(!WriteData32(pFile, ePODFileN, &n.n)) return false;
    777 	if(!WriteData32(pFile, ePODFileStride, &n.nStride)) return false;
    778 	if(bValidData)
    779 	{
    780 		switch(PVRTModelPODDataTypeSize(n.eType))
    781 		{
    782 			case 1: if(!WriteData(pFile, ePODFileData, n.pData, nEntries * n.nStride)) return false; break;
    783 			case 2: if(!WriteData16(pFile, ePODFileData, n.pData, nEntries * (n.nStride / 2))) return false; break;
    784 			case 4: if(!WriteData32(pFile, ePODFileData, n.pData, nEntries * (n.nStride / 4))) return false; break;
    785 			default: { _ASSERT(false); }
    786 		};
    787 	}
    788 	else
    789 	{
    790 		unsigned int offset = (unsigned int) (size_t) n.pData;
    791 		if(!WriteData32(pFile, ePODFileData, &offset)) return false;
    792 	}
    793 	if(!WriteMarker(pFile, nName, true)) return false;
    794 	return true;
    795 }
    796 
    797 /*!***************************************************************************
    798  @Function			WriteInterleaved
    799  @Input				pFile
    800  @Input				mesh
    801  @Return			true if successful
    802  @Description		Write out the interleaved data to file.
    803 *****************************************************************************/
    804 static bool WriteInterleaved(FILE * const pFile, SPODMesh &mesh)
    805 {
    806 	if(!mesh.pInterleaved)
    807 		return true;
    808 
    809 	unsigned int i;
    810 	unsigned int ui32CPODDataSize = 0;
    811 	CPODData **pCPODData = new CPODData*[7 + mesh.nNumUVW];
    812 
    813 	if(mesh.sVertex.n)		pCPODData[ui32CPODDataSize++] = &mesh.sVertex;
    814 	if(mesh.sNormals.n)		pCPODData[ui32CPODDataSize++] = &mesh.sNormals;
    815 	if(mesh.sTangents.n)	pCPODData[ui32CPODDataSize++] = &mesh.sTangents;
    816 	if(mesh.sBinormals.n)	pCPODData[ui32CPODDataSize++] = &mesh.sBinormals;
    817 	if(mesh.sVtxColours.n)	pCPODData[ui32CPODDataSize++] = &mesh.sVtxColours;
    818 	if(mesh.sBoneIdx.n)		pCPODData[ui32CPODDataSize++] = &mesh.sBoneIdx;
    819 	if(mesh.sBoneWeight.n)	pCPODData[ui32CPODDataSize++] = &mesh.sBoneWeight;
    820 
    821 	for(i = 0; i < mesh.nNumUVW; ++i)
    822 		if(mesh.psUVW[i].n) pCPODData[ui32CPODDataSize++] = &mesh.psUVW[i];
    823 
    824 	// Bubble sort pCPODData based on the vertex element offsets
    825 	bool bSwap = true;
    826 	unsigned int ui32Size = ui32CPODDataSize;
    827 
    828 	while(bSwap)
    829 	{
    830 		bSwap = false;
    831 
    832 		for(i = 0; i < ui32Size - 1; ++i)
    833 		{
    834 			if(pCPODData[i]->pData > pCPODData[i + 1]->pData)
    835 			{
    836 				PVRTswap(pCPODData[i], pCPODData[i + 1]);
    837 				bSwap = true;
    838 			}
    839 		}
    840 
    841 		--ui32Size;
    842 	}
    843 
    844 	// Write out the data
    845 	if(!WriteMarker(pFile, ePODFileMeshInterleaved, false, mesh.nNumVertex * mesh.sVertex.nStride)) return false;
    846 
    847 	for(i = 0; i < mesh.nNumVertex; ++i)
    848 	{
    849 		unsigned char* pVtxStart = mesh.pInterleaved + (i * mesh.sVertex.nStride);
    850 
    851 		for(unsigned int j = 0; j < ui32CPODDataSize; ++j)
    852 		{
    853 			unsigned char* pData = pVtxStart + (size_t) pCPODData[j]->pData;
    854 
    855 			switch(PVRTModelPODDataTypeSize(pCPODData[j]->eType))
    856 			{
    857 				case 1: if(!WriteFileSafe(pFile, pData, pCPODData[j]->n)) return false; break;
    858 				case 2: if(!WriteFileSafe16(pFile, (unsigned short*) pData, pCPODData[j]->n)) return false; break;
    859 				case 4: if(!WriteFileSafe32(pFile, (unsigned int*) pData, pCPODData[j]->n)) return false; break;
    860 				default: { _ASSERT(false); }
    861 			};
    862 
    863 			// Write out the padding
    864 			size_t padding;
    865 
    866 			if(j != ui32CPODDataSize - 1)
    867 				padding = ((size_t)pCPODData[j + 1]->pData - (size_t)pCPODData[j]->pData) - PVRTModelPODDataStride(*pCPODData[j]);
    868 			else
    869 				padding = (pCPODData[j]->nStride - (size_t)pCPODData[j]->pData) - PVRTModelPODDataStride(*pCPODData[j]);
    870 
    871 			fwrite("\0\0\0\0", padding, 1, pFile);
    872 		}
    873 	}
    874 
    875 	if(!WriteMarker(pFile, ePODFileMeshInterleaved, true)) return false;
    876 
    877 	// Delete our CPOD data array
    878 	delete[] pCPODData;
    879 
    880 	return true;
    881 }
    882 
    883 /*!***************************************************************************
    884  @Function			PVRTModelPODGetAnimArraySize
    885  @Input				pAnimDataIdx
    886  @Input				ui32Frames
    887  @Input				ui32Components
    888  @Return			Size of the animation array
    889  @Description		Calculates the size of an animation array
    890 *****************************************************************************/
    891 PVRTuint32 PVRTModelPODGetAnimArraySize(PVRTuint32 *pAnimDataIdx, PVRTuint32 ui32Frames, PVRTuint32 ui32Components)
    892 {
    893 	if(pAnimDataIdx)
    894 	{
    895 		// Find the largest index value
    896 		PVRTuint32 ui32Max = 0;
    897 		for(unsigned int i = 0; i < ui32Frames; ++i)
    898 		{
    899 			if(ui32Max < pAnimDataIdx[i])
    900 				ui32Max = pAnimDataIdx[i];
    901 		}
    902 
    903 		return ui32Max + ui32Components;
    904 	}
    905 
    906 	return ui32Frames * ui32Components;
    907 }
    908 
    909 /*!***************************************************************************
    910  @Function			WritePOD
    911  @Output			The file referenced by pFile
    912  @Input				s The POD Scene to write
    913  @Input				pszExpOpt Exporter options
    914  @Return			true if successful
    915  @Description		Write a POD file
    916 *****************************************************************************/
    917 static bool WritePOD(
    918 	FILE			* const pFile,
    919 	const char		* const pszExpOpt,
    920 	const char		* const pszHistory,
    921 	const SPODScene	&s)
    922 {
    923 	unsigned int i, j;
    924 
    925 	// Save: file version
    926 	{
    927 		char *pszVersion = (char*)PVRTMODELPOD_VERSION;
    928 
    929 		if(!WriteData(pFile, ePODFileVersion, pszVersion, (unsigned int)strlen(pszVersion) + 1)) return false;
    930 	}
    931 
    932 	// Save: exporter options
    933 	if(pszExpOpt && *pszExpOpt)
    934 	{
    935 		if(!WriteData(pFile, ePODFileExpOpt, pszExpOpt, (unsigned int)strlen(pszExpOpt) + 1)) return false;
    936 	}
    937 
    938 	// Save: .pod file history
    939 	if(pszHistory && *pszHistory)
    940 	{
    941 		if(!WriteData(pFile, ePODFileHistory, pszHistory, (unsigned int)strlen(pszHistory) + 1)) return false;
    942 	}
    943 
    944 	// Save: scene descriptor
    945 	if(!WriteMarker(pFile, ePODFileScene, false)) return false;
    946 
    947 	{
    948 		if(!WriteData32(pFile, ePODFileUnits, &s.fUnits)) return false;
    949 		if(!WriteData32(pFile, ePODFileColourBackground,	s.pfColourBackground, sizeof(s.pfColourBackground) / sizeof(*s.pfColourBackground))) return false;
    950 		if(!WriteData32(pFile, ePODFileColourAmbient,		s.pfColourAmbient, sizeof(s.pfColourAmbient) / sizeof(*s.pfColourAmbient))) return false;
    951 		if(!WriteData32(pFile, ePODFileNumCamera, &s.nNumCamera)) return false;
    952 		if(!WriteData32(pFile, ePODFileNumLight, &s.nNumLight)) return false;
    953 		if(!WriteData32(pFile, ePODFileNumMesh,	&s.nNumMesh)) return false;
    954 		if(!WriteData32(pFile, ePODFileNumNode,	&s.nNumNode)) return false;
    955 		if(!WriteData32(pFile, ePODFileNumMeshNode,	&s.nNumMeshNode)) return false;
    956 		if(!WriteData32(pFile, ePODFileNumTexture, &s.nNumTexture)) return false;
    957 		if(!WriteData32(pFile, ePODFileNumMaterial,	&s.nNumMaterial)) return false;
    958 		if(!WriteData32(pFile, ePODFileNumFrame, &s.nNumFrame)) return false;
    959 
    960 		if(s.nNumFrame)
    961 		{
    962 			if(!WriteData32(pFile, ePODFileFPS, &s.nFPS)) return false;
    963 		}
    964 
    965 		if(!WriteData32(pFile, ePODFileFlags, &s.nFlags)) return false;
    966 		if(!WriteData(pFile, ePODFileUserData, s.pUserData, s.nUserDataSize)) return false;
    967 
    968 		// Save: cameras
    969 		for(i = 0; i < s.nNumCamera; ++i)
    970 		{
    971 			if(!WriteMarker(pFile, ePODFileCamera, false)) return false;
    972 			if(!WriteData32(pFile, ePODFileCamIdxTgt, &s.pCamera[i].nIdxTarget)) return false;
    973 			if(!WriteData32(pFile, ePODFileCamFOV,	  &s.pCamera[i].fFOV)) return false;
    974 			if(!WriteData32(pFile, ePODFileCamFar,	  &s.pCamera[i].fFar)) return false;
    975 			if(!WriteData32(pFile, ePODFileCamNear,	  &s.pCamera[i].fNear)) return false;
    976 			if(!WriteData32(pFile, ePODFileCamAnimFOV,	s.pCamera[i].pfAnimFOV, s.nNumFrame)) return false;
    977 			if(!WriteMarker(pFile, ePODFileCamera, true)) return false;
    978 		}
    979 		// Save: lights
    980 		for(i = 0; i < s.nNumLight; ++i)
    981 		{
    982 			if(!WriteMarker(pFile, ePODFileLight, false)) return false;
    983 			if(!WriteData32(pFile, ePODFileLightIdxTgt,	&s.pLight[i].nIdxTarget)) return false;
    984 			if(!WriteData32(pFile, ePODFileLightColour,	s.pLight[i].pfColour, sizeof(s.pLight[i].pfColour) / sizeof(*s.pLight[i].pfColour))) return false;
    985 			if(!WriteData32(pFile, ePODFileLightType,	&s.pLight[i].eType)) return false;
    986 
    987 			if(s.pLight[i].eType != ePODDirectional)
    988 			{
    989 				if(!WriteData32(pFile, ePODFileLightConstantAttenuation,	&s.pLight[i].fConstantAttenuation))  return false;
    990 				if(!WriteData32(pFile, ePODFileLightLinearAttenuation,		&s.pLight[i].fLinearAttenuation))	  return false;
    991 				if(!WriteData32(pFile, ePODFileLightQuadraticAttenuation,	&s.pLight[i].fQuadraticAttenuation)) return false;
    992 			}
    993 
    994 			if(s.pLight[i].eType == ePODSpot)
    995 			{
    996 				if(!WriteData32(pFile, ePODFileLightFalloffAngle,			&s.pLight[i].fFalloffAngle))		  return false;
    997 				if(!WriteData32(pFile, ePODFileLightFalloffExponent,		&s.pLight[i].fFalloffExponent))	  return false;
    998 			}
    999 
   1000 			if(!WriteMarker(pFile, ePODFileLight, true)) return false;
   1001 		}
   1002 
   1003 		// Save: materials
   1004 		for(i = 0; i < s.nNumMaterial; ++i)
   1005 		{
   1006 			if(!WriteMarker(pFile, ePODFileMaterial, false)) return false;
   1007 
   1008 			if(!WriteData32(pFile, ePODFileMatFlags,  &s.pMaterial[i].nFlags)) return false;
   1009 			if(!WriteData(pFile,   ePODFileMatName,			s.pMaterial[i].pszName, (unsigned int)strlen(s.pMaterial[i].pszName)+1)) return false;
   1010 			if(!WriteData32(pFile, ePODFileMatIdxTexDiffuse,	&s.pMaterial[i].nIdxTexDiffuse)) return false;
   1011 			if(!WriteData32(pFile, ePODFileMatIdxTexAmbient,	&s.pMaterial[i].nIdxTexAmbient)) return false;
   1012 			if(!WriteData32(pFile, ePODFileMatIdxTexSpecularColour,	&s.pMaterial[i].nIdxTexSpecularColour)) return false;
   1013 			if(!WriteData32(pFile, ePODFileMatIdxTexSpecularLevel,	&s.pMaterial[i].nIdxTexSpecularLevel)) return false;
   1014 			if(!WriteData32(pFile, ePODFileMatIdxTexBump,	&s.pMaterial[i].nIdxTexBump)) return false;
   1015 			if(!WriteData32(pFile, ePODFileMatIdxTexEmissive,	&s.pMaterial[i].nIdxTexEmissive)) return false;
   1016 			if(!WriteData32(pFile, ePODFileMatIdxTexGlossiness,	&s.pMaterial[i].nIdxTexGlossiness)) return false;
   1017 			if(!WriteData32(pFile, ePODFileMatIdxTexOpacity,	&s.pMaterial[i].nIdxTexOpacity)) return false;
   1018 			if(!WriteData32(pFile, ePODFileMatIdxTexReflection,	&s.pMaterial[i].nIdxTexReflection)) return false;
   1019 			if(!WriteData32(pFile, ePODFileMatIdxTexRefraction,	&s.pMaterial[i].nIdxTexRefraction)) return false;
   1020 			if(!WriteData32(pFile, ePODFileMatOpacity,	&s.pMaterial[i].fMatOpacity)) return false;
   1021 			if(!WriteData32(pFile, ePODFileMatAmbient,		s.pMaterial[i].pfMatAmbient, sizeof(s.pMaterial[i].pfMatAmbient) / sizeof(*s.pMaterial[i].pfMatAmbient))) return false;
   1022 			if(!WriteData32(pFile, ePODFileMatDiffuse,		s.pMaterial[i].pfMatDiffuse, sizeof(s.pMaterial[i].pfMatDiffuse) / sizeof(*s.pMaterial[i].pfMatDiffuse))) return false;
   1023 			if(!WriteData32(pFile, ePODFileMatSpecular,		s.pMaterial[i].pfMatSpecular, sizeof(s.pMaterial[i].pfMatSpecular) / sizeof(*s.pMaterial[i].pfMatSpecular))) return false;
   1024 			if(!WriteData32(pFile, ePODFileMatShininess, &s.pMaterial[i].fMatShininess)) return false;
   1025 			if(!WriteData(pFile, ePODFileMatEffectFile,		s.pMaterial[i].pszEffectFile, s.pMaterial[i].pszEffectFile ? ((unsigned int)strlen(s.pMaterial[i].pszEffectFile)+1) : 0)) return false;
   1026 			if(!WriteData(pFile, ePODFileMatEffectName,		s.pMaterial[i].pszEffectName, s.pMaterial[i].pszEffectName ? ((unsigned int)strlen(s.pMaterial[i].pszEffectName)+1) : 0)) return false;
   1027 			if(!WriteData32(pFile, ePODFileMatBlendSrcRGB,  &s.pMaterial[i].eBlendSrcRGB))return false;
   1028 			if(!WriteData32(pFile, ePODFileMatBlendSrcA,	&s.pMaterial[i].eBlendSrcA))	return false;
   1029 			if(!WriteData32(pFile, ePODFileMatBlendDstRGB,  &s.pMaterial[i].eBlendDstRGB))return false;
   1030 			if(!WriteData32(pFile, ePODFileMatBlendDstA,	&s.pMaterial[i].eBlendDstA))	return false;
   1031 			if(!WriteData32(pFile, ePODFileMatBlendOpRGB,	&s.pMaterial[i].eBlendOpRGB)) return false;
   1032 			if(!WriteData32(pFile, ePODFileMatBlendOpA,		&s.pMaterial[i].eBlendOpA))	return false;
   1033 			if(!WriteData32(pFile, ePODFileMatBlendColour, s.pMaterial[i].pfBlendColour, sizeof(s.pMaterial[i].pfBlendColour) / sizeof(*s.pMaterial[i].pfBlendColour))) return false;
   1034 			if(!WriteData32(pFile, ePODFileMatBlendFactor, s.pMaterial[i].pfBlendFactor, sizeof(s.pMaterial[i].pfBlendFactor) / sizeof(*s.pMaterial[i].pfBlendFactor))) return false;
   1035 			if(!WriteData(pFile,   ePODFileMatUserData, s.pMaterial[i].pUserData, s.pMaterial[i].nUserDataSize)) return false;
   1036 
   1037 			if(!WriteMarker(pFile, ePODFileMaterial, true)) return false;
   1038 		}
   1039 
   1040 		// Save: meshes
   1041 		for(i = 0; i < s.nNumMesh; ++i)
   1042 		{
   1043 			if(!WriteMarker(pFile, ePODFileMesh, false)) return false;
   1044 
   1045 			if(!WriteData32(pFile, ePODFileMeshNumVtx,			&s.pMesh[i].nNumVertex)) return false;
   1046 			if(!WriteData32(pFile, ePODFileMeshNumFaces,		&s.pMesh[i].nNumFaces)) return false;
   1047 			if(!WriteData32(pFile, ePODFileMeshNumUVW,			&s.pMesh[i].nNumUVW)) return false;
   1048 			if(!WriteData32(pFile, ePODFileMeshStripLength,		s.pMesh[i].pnStripLength, s.pMesh[i].nNumStrips)) return false;
   1049 			if(!WriteData32(pFile, ePODFileMeshNumStrips,		&s.pMesh[i].nNumStrips)) return false;
   1050 			if(!WriteInterleaved(pFile, s.pMesh[i])) return false;
   1051 			if(!WriteData32(pFile, ePODFileMeshBoneBatchBoneMax,&s.pMesh[i].sBoneBatches.nBatchBoneMax)) return false;
   1052 			if(!WriteData32(pFile, ePODFileMeshBoneBatchCnt,	&s.pMesh[i].sBoneBatches.nBatchCnt)) return false;
   1053 			if(!WriteData32(pFile, ePODFileMeshBoneBatches,		s.pMesh[i].sBoneBatches.pnBatches, s.pMesh[i].sBoneBatches.nBatchBoneMax * s.pMesh[i].sBoneBatches.nBatchCnt)) return false;
   1054 			if(!WriteData32(pFile, ePODFileMeshBoneBatchBoneCnts,	s.pMesh[i].sBoneBatches.pnBatchBoneCnt, s.pMesh[i].sBoneBatches.nBatchCnt)) return false;
   1055 			if(!WriteData32(pFile, ePODFileMeshBoneBatchOffsets,	s.pMesh[i].sBoneBatches.pnBatchOffset,s.pMesh[i].sBoneBatches.nBatchCnt)) return false;
   1056 			if(!WriteData32(pFile, ePODFileMeshUnpackMatrix,	s.pMesh[i].mUnpackMatrix.f, 16))	return false;
   1057 
   1058 			if(!WriteCPODData(pFile, ePODFileMeshFaces,			s.pMesh[i].sFaces,		PVRTModelPODCountIndices(s.pMesh[i]), true)) return false;
   1059 			if(!WriteCPODData(pFile, ePODFileMeshVtx,			s.pMesh[i].sVertex,		s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1060 			if(!WriteCPODData(pFile, ePODFileMeshNor,			s.pMesh[i].sNormals,	s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1061 			if(!WriteCPODData(pFile, ePODFileMeshTan,			s.pMesh[i].sTangents,	s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1062 			if(!WriteCPODData(pFile, ePODFileMeshBin,			 s.pMesh[i].sBinormals,	s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1063 
   1064 			for(j = 0; j < s.pMesh[i].nNumUVW; ++j)
   1065 				if(!WriteCPODData(pFile, ePODFileMeshUVW,		s.pMesh[i].psUVW[j],	s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1066 
   1067 			if(!WriteCPODData(pFile, ePODFileMeshVtxCol,		s.pMesh[i].sVtxColours, s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1068 			if(!WriteCPODData(pFile, ePODFileMeshBoneIdx,		s.pMesh[i].sBoneIdx,	s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1069 			if(!WriteCPODData(pFile, ePODFileMeshBoneWeight,	s.pMesh[i].sBoneWeight,	s.pMesh[i].nNumVertex, s.pMesh[i].pInterleaved == 0)) return false;
   1070 
   1071 			if(!WriteMarker(pFile, ePODFileMesh, true)) return false;
   1072 		}
   1073 
   1074 		int iTransformationNo;
   1075 		// Save: node
   1076 		for(i = 0; i < s.nNumNode; ++i)
   1077 		{
   1078 			if(!WriteMarker(pFile, ePODFileNode, false)) return false;
   1079 
   1080 			{
   1081 				if(!WriteData32(pFile, ePODFileNodeIdx,		&s.pNode[i].nIdx)) return false;
   1082 				if(!WriteData(pFile, ePODFileNodeName,		s.pNode[i].pszName, (unsigned int)strlen(s.pNode[i].pszName)+1)) return false;
   1083 				if(!WriteData32(pFile, ePODFileNodeIdxMat,	&s.pNode[i].nIdxMaterial)) return false;
   1084 				if(!WriteData32(pFile, ePODFileNodeIdxParent, &s.pNode[i].nIdxParent)) return false;
   1085 				if(!WriteData32(pFile, ePODFileNodeAnimFlags, &s.pNode[i].nAnimFlags)) return false;
   1086 
   1087 				if(s.pNode[i].pnAnimPositionIdx)
   1088 				{
   1089 					if(!WriteData32(pFile, ePODFileNodeAnimPosIdx,	s.pNode[i].pnAnimPositionIdx,	s.nNumFrame)) return false;
   1090 				}
   1091 
   1092 				iTransformationNo = s.pNode[i].nAnimFlags & ePODHasPositionAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimPositionIdx, s.nNumFrame, 3) : 3;
   1093 				if(!WriteData32(pFile, ePODFileNodeAnimPos,	s.pNode[i].pfAnimPosition,	iTransformationNo)) return false;
   1094 
   1095 				if(s.pNode[i].pnAnimRotationIdx)
   1096 				{
   1097 					if(!WriteData32(pFile, ePODFileNodeAnimRotIdx,	s.pNode[i].pnAnimRotationIdx,	s.nNumFrame)) return false;
   1098 				}
   1099 
   1100 				iTransformationNo = s.pNode[i].nAnimFlags & ePODHasRotationAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimRotationIdx, s.nNumFrame, 4) : 4;
   1101 				if(!WriteData32(pFile, ePODFileNodeAnimRot,	s.pNode[i].pfAnimRotation,	iTransformationNo)) return false;
   1102 
   1103 				if(s.pNode[i].pnAnimScaleIdx)
   1104 				{
   1105 					if(!WriteData32(pFile, ePODFileNodeAnimScaleIdx,	s.pNode[i].pnAnimScaleIdx,	s.nNumFrame)) return false;
   1106 				}
   1107 
   1108 				iTransformationNo = s.pNode[i].nAnimFlags & ePODHasScaleAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimScaleIdx, s.nNumFrame, 7) : 7;
   1109 				if(!WriteData32(pFile, ePODFileNodeAnimScale,	s.pNode[i].pfAnimScale,		iTransformationNo))    return false;
   1110 
   1111 				if(s.pNode[i].pnAnimMatrixIdx)
   1112 				{
   1113 					if(!WriteData32(pFile, ePODFileNodeAnimMatrixIdx,	s.pNode[i].pnAnimMatrixIdx,	s.nNumFrame)) return false;
   1114 				}
   1115 
   1116 				iTransformationNo = s.pNode[i].nAnimFlags & ePODHasMatrixAni ? PVRTModelPODGetAnimArraySize(s.pNode[i].pnAnimMatrixIdx, s.nNumFrame, 16) : 16;
   1117 				if(!WriteData32(pFile, ePODFileNodeAnimMatrix,s.pNode[i].pfAnimMatrix,	iTransformationNo))   return false;
   1118 
   1119 				if(!WriteData(pFile, ePODFileNodeUserData, s.pNode[i].pUserData, s.pNode[i].nUserDataSize)) return false;
   1120 			}
   1121 
   1122 			if(!WriteMarker(pFile, ePODFileNode, true)) return false;
   1123 		}
   1124 
   1125 		// Save: texture
   1126 		for(i = 0; i < s.nNumTexture; ++i)
   1127 		{
   1128 			if(!WriteMarker(pFile, ePODFileTexture, false)) return false;
   1129 			if(!WriteData(pFile, ePODFileTexName, s.pTexture[i].pszName, (unsigned int)strlen(s.pTexture[i].pszName)+1)) return false;
   1130 			if(!WriteMarker(pFile, ePODFileTexture, true)) return false;
   1131 		}
   1132 	}
   1133 	if(!WriteMarker(pFile, ePODFileScene, true)) return false;
   1134 
   1135 	return true;
   1136 }
   1137 
   1138 /****************************************************************************
   1139 ** Local code: File reading
   1140 ****************************************************************************/
   1141 /*!***************************************************************************
   1142  @Function			ReadCPODData
   1143  @Modified			s The CPODData to read into
   1144  @Input				src CSource object to read data from.
   1145  @Input				nSpec
   1146  @Input				bValidData
   1147  @Return			true if successful
   1148  @Description		Read a CPODData block in  from a pod file
   1149 *****************************************************************************/
   1150 static bool ReadCPODData(
   1151 	CPODData			&s,
   1152 	CSource				&src,
   1153 	const unsigned int	nSpec,
   1154 	const bool			bValidData)
   1155 {
   1156 	unsigned int nName, nLen, nBuff;
   1157 
   1158 	while(src.ReadMarker(nName, nLen))
   1159 	{
   1160 		if(nName == (nSpec | PVRTMODELPOD_TAG_END))
   1161 			return true;
   1162 
   1163 		switch(nName)
   1164 		{
   1165 		case ePODFileDataType:	if(!src.Read32(s.eType)) return false;					break;
   1166 		case ePODFileN:			if(!src.Read32(s.n)) return false;						break;
   1167 		case ePODFileStride:	if(!src.Read32(s.nStride)) return false;					break;
   1168 		case ePODFileData:
   1169 			if(bValidData)
   1170 			{
   1171 				switch(PVRTModelPODDataTypeSize(s.eType))
   1172 				{
   1173 					case 1: if(!src.ReadAfterAlloc(s.pData, nLen)) return false; break;
   1174 					case 2:
   1175 						{ // reading 16bit data but have 8bit pointer
   1176 							PVRTuint16 *p16Pointer=NULL;
   1177 							if(!src.ReadAfterAlloc16(p16Pointer, nLen)) return false;
   1178 							s.pData = (unsigned char*)p16Pointer;
   1179 							break;
   1180 						}
   1181 					case 4:
   1182 						{ // reading 32bit data but have 8bit pointer
   1183 							PVRTuint32 *p32Pointer=NULL;
   1184 							if(!src.ReadAfterAlloc32(p32Pointer, nLen)) return false;
   1185 							s.pData = (unsigned char*)p32Pointer;
   1186 							break;
   1187 						}
   1188 					default:
   1189 						{ _ASSERT(false);}
   1190 				}
   1191 			}
   1192 			else
   1193 			{
   1194 				if(src.Read32(nBuff))
   1195 				{
   1196 					s.pData = (unsigned char*) (size_t) nBuff;
   1197 				}
   1198 				else
   1199 				{
   1200 					return false;
   1201 				}
   1202 			}
   1203 		 break;
   1204 
   1205 		default:
   1206 			if(!src.Skip(nLen)) return false;
   1207 		}
   1208 	}
   1209 	return false;
   1210 }
   1211 
   1212 /*!***************************************************************************
   1213  @Function			ReadCamera
   1214  @Modified			s The SPODCamera to read into
   1215  @Input				src	CSource object to read data from.
   1216  @Return			true if successful
   1217  @Description		Read a camera block in from a pod file
   1218 *****************************************************************************/
   1219 static bool ReadCamera(
   1220 	SPODCamera	&s,
   1221 	CSource		&src)
   1222 {
   1223 	unsigned int nName, nLen;
   1224 	s.pfAnimFOV = 0;
   1225 
   1226 	while(src.ReadMarker(nName, nLen))
   1227 	{
   1228 		switch(nName)
   1229 		{
   1230 		case ePODFileCamera | PVRTMODELPOD_TAG_END:			return true;
   1231 
   1232 		case ePODFileCamIdxTgt:		if(!src.Read32(s.nIdxTarget)) return false;					break;
   1233 		case ePODFileCamFOV:		if(!src.Read32(s.fFOV)) return false;							break;
   1234 		case ePODFileCamFar:		if(!src.Read32(s.fFar)) return false;							break;
   1235 		case ePODFileCamNear:		if(!src.Read32(s.fNear)) return false;						break;
   1236 		case ePODFileCamAnimFOV:	if(!src.ReadAfterAlloc32(s.pfAnimFOV, nLen)) return false;	break;
   1237 
   1238 		default:
   1239 			if(!src.Skip(nLen)) return false;
   1240 		}
   1241 	}
   1242 	return false;
   1243 }
   1244 
   1245 /*!***************************************************************************
   1246  @Function			ReadLight
   1247  @Modified			s The SPODLight to read into
   1248  @Input				src	CSource object to read data from.
   1249  @Return			true if successful
   1250  @Description		Read a light block in from a pod file
   1251 *****************************************************************************/
   1252 static bool ReadLight(
   1253 	SPODLight	&s,
   1254 	CSource		&src)
   1255 {
   1256 	unsigned int nName, nLen;
   1257 
   1258 	while(src.ReadMarker(nName, nLen))
   1259 	{
   1260 		switch(nName)
   1261 		{
   1262 		case ePODFileLight | PVRTMODELPOD_TAG_END:			return true;
   1263 
   1264 		case ePODFileLightIdxTgt:	if(!src.Read32(s.nIdxTarget)) return false;	break;
   1265 		case ePODFileLightColour:	if(!src.ReadArray32(s.pfColour, 3)) return false;		break;
   1266 		case ePODFileLightType:		if(!src.Read32(s.eType)) return false;		break;
   1267 		case ePODFileLightConstantAttenuation: 		if(!src.Read32(s.fConstantAttenuation))	return false;	break;
   1268 		case ePODFileLightLinearAttenuation:		if(!src.Read32(s.fLinearAttenuation))		return false;	break;
   1269 		case ePODFileLightQuadraticAttenuation:		if(!src.Read32(s.fQuadraticAttenuation))	return false;	break;
   1270 		case ePODFileLightFalloffAngle:				if(!src.Read32(s.fFalloffAngle))			return false;	break;
   1271 		case ePODFileLightFalloffExponent:			if(!src.Read32(s.fFalloffExponent))		return false;	break;
   1272 		default:
   1273 			if(!src.Skip(nLen)) return false;
   1274 		}
   1275 	}
   1276 	return false;
   1277 }
   1278 
   1279 /*!***************************************************************************
   1280  @Function			ReadMaterial
   1281  @Modified			s The SPODMaterial to read into
   1282  @Input				src	CSource object to read data from.
   1283  @Return			true if successful
   1284  @Description		Read a material block in from a pod file
   1285 *****************************************************************************/
   1286 static bool ReadMaterial(
   1287 	SPODMaterial	&s,
   1288 	CSource			&src)
   1289 {
   1290 	unsigned int nName, nLen;
   1291 
   1292 	// Set texture IDs to -1
   1293 	s.nIdxTexDiffuse = -1;
   1294 	s.nIdxTexAmbient = -1;
   1295 	s.nIdxTexSpecularColour = -1;
   1296 	s.nIdxTexSpecularLevel = -1;
   1297 	s.nIdxTexBump = -1;
   1298 	s.nIdxTexEmissive = -1;
   1299 	s.nIdxTexGlossiness = -1;
   1300 	s.nIdxTexOpacity = -1;
   1301 	s.nIdxTexReflection = -1;
   1302 	s.nIdxTexRefraction = -1;
   1303 
   1304 	// Set defaults for blend modes
   1305 	s.eBlendSrcRGB = s.eBlendSrcA = ePODBlendFunc_ONE;
   1306 	s.eBlendDstRGB = s.eBlendDstA = ePODBlendFunc_ZERO;
   1307 	s.eBlendOpRGB  = s.eBlendOpA  = ePODBlendOp_ADD;
   1308 
   1309 	memset(s.pfBlendColour, 0, sizeof(s.pfBlendColour));
   1310 	memset(s.pfBlendFactor, 0, sizeof(s.pfBlendFactor));
   1311 
   1312 	// Set default for material flags
   1313 	s.nFlags = 0;
   1314 
   1315 	// Set default for user data
   1316 	s.pUserData = 0;
   1317 	s.nUserDataSize = 0;
   1318 
   1319 	while(src.ReadMarker(nName, nLen))
   1320 	{
   1321 		switch(nName)
   1322 		{
   1323 		case ePODFileMaterial | PVRTMODELPOD_TAG_END:			return true;
   1324 
   1325 		case ePODFileMatFlags:					if(!src.Read32(s.nFlags)) return false;				break;
   1326 		case ePODFileMatName:					if(!src.ReadAfterAlloc(s.pszName, nLen)) return false;		break;
   1327 		case ePODFileMatIdxTexDiffuse:			if(!src.Read32(s.nIdxTexDiffuse)) return false;				break;
   1328 		case ePODFileMatIdxTexAmbient:			if(!src.Read32(s.nIdxTexAmbient)) return false;				break;
   1329 		case ePODFileMatIdxTexSpecularColour:	if(!src.Read32(s.nIdxTexSpecularColour)) return false;		break;
   1330 		case ePODFileMatIdxTexSpecularLevel:	if(!src.Read32(s.nIdxTexSpecularLevel)) return false;			break;
   1331 		case ePODFileMatIdxTexBump:				if(!src.Read32(s.nIdxTexBump)) return false;					break;
   1332 		case ePODFileMatIdxTexEmissive:			if(!src.Read32(s.nIdxTexEmissive)) return false;				break;
   1333 		case ePODFileMatIdxTexGlossiness:		if(!src.Read32(s.nIdxTexGlossiness)) return false;			break;
   1334 		case ePODFileMatIdxTexOpacity:			if(!src.Read32(s.nIdxTexOpacity)) return false;				break;
   1335 		case ePODFileMatIdxTexReflection:		if(!src.Read32(s.nIdxTexReflection)) return false;			break;
   1336 		case ePODFileMatIdxTexRefraction:		if(!src.Read32(s.nIdxTexRefraction)) return false;			break;
   1337 		case ePODFileMatOpacity:		if(!src.Read32(s.fMatOpacity)) return false;						break;
   1338 		case ePODFileMatAmbient:		if(!src.ReadArray32(s.pfMatAmbient,  sizeof(s.pfMatAmbient) / sizeof(*s.pfMatAmbient))) return false;		break;
   1339 		case ePODFileMatDiffuse:		if(!src.ReadArray32(s.pfMatDiffuse,  sizeof(s.pfMatDiffuse) / sizeof(*s.pfMatDiffuse))) return false;		break;
   1340 		case ePODFileMatSpecular:		if(!src.ReadArray32(s.pfMatSpecular, sizeof(s.pfMatSpecular) / sizeof(*s.pfMatSpecular))) return false;		break;
   1341 		case ePODFileMatShininess:		if(!src.Read32(s.fMatShininess)) return false;					break;
   1342 		case ePODFileMatEffectFile:		if(!src.ReadAfterAlloc(s.pszEffectFile, nLen)) return false;	break;
   1343 		case ePODFileMatEffectName:		if(!src.ReadAfterAlloc(s.pszEffectName, nLen)) return false;	break;
   1344 		case ePODFileMatBlendSrcRGB:	if(!src.Read32(s.eBlendSrcRGB))	return false;	break;
   1345 		case ePODFileMatBlendSrcA:		if(!src.Read32(s.eBlendSrcA))		return false;	break;
   1346 		case ePODFileMatBlendDstRGB:	if(!src.Read32(s.eBlendDstRGB))	return false;	break;
   1347 		case ePODFileMatBlendDstA:		if(!src.Read32(s.eBlendDstA))		return false;	break;
   1348 		case ePODFileMatBlendOpRGB:		if(!src.Read32(s.eBlendOpRGB))	return false;	break;
   1349 		case ePODFileMatBlendOpA:		if(!src.Read32(s.eBlendOpA))		return false;	break;
   1350 		case ePODFileMatBlendColour:	if(!src.ReadArray32(s.pfBlendColour, sizeof(s.pfBlendColour) / sizeof(*s.pfBlendColour)))	return false;	break;
   1351 		case ePODFileMatBlendFactor:	if(!src.ReadArray32(s.pfBlendFactor, sizeof(s.pfBlendFactor) / sizeof(*s.pfBlendFactor)))	return false;	break;
   1352 
   1353 		case ePODFileMatUserData:
   1354 			if(!src.ReadAfterAlloc(s.pUserData, nLen))
   1355 				return false;
   1356 			else
   1357 			{
   1358 				s.nUserDataSize = nLen;
   1359 				break;
   1360 			}
   1361 
   1362 		default:
   1363 			if(!src.Skip(nLen)) return false;
   1364 		}
   1365 	}
   1366 	return false;
   1367 }
   1368 
   1369 /*!***************************************************************************
   1370  @Function			PVRTFixInterleavedEndiannessUsingCPODData
   1371  @Modified			pInterleaved - The interleaved data
   1372  @Input				data - The CPODData.
   1373  @Return			ui32Size - Number of elements in pInterleaved
   1374  @Description		Called multiple times and goes through the interleaved data
   1375 					correcting the endianness.
   1376 *****************************************************************************/
   1377 static void PVRTFixInterleavedEndiannessUsingCPODData(unsigned char* pInterleaved, CPODData &data, unsigned int ui32Size)
   1378 {
   1379 	if(!data.n)
   1380 		return;
   1381 
   1382 	size_t ui32TypeSize = PVRTModelPODDataTypeSize(data.eType);
   1383 
   1384 	unsigned char ub[4];
   1385 	unsigned char *pData = pInterleaved + (size_t) data.pData;
   1386 
   1387 	switch(ui32TypeSize)
   1388 	{
   1389 		case 1: return;
   1390 		case 2:
   1391 			{
   1392 				for(unsigned int i = 0; i < ui32Size; ++i)
   1393 				{
   1394 					for(unsigned int j = 0; j < data.n; ++j)
   1395 					{
   1396 						ub[0] = pData[ui32TypeSize * j + 0];
   1397 						ub[1] = pData[ui32TypeSize * j + 1];
   1398 
   1399 						((unsigned short*) pData)[j] = (unsigned short) ((ub[1] << 8) | ub[0]);
   1400 					}
   1401 
   1402 					pData += data.nStride;
   1403 				}
   1404 			}
   1405 			break;
   1406 		case 4:
   1407 			{
   1408 				for(unsigned int i = 0; i < ui32Size; ++i)
   1409 				{
   1410 					for(unsigned int j = 0; j < data.n; ++j)
   1411 					{
   1412 						ub[0] = pData[ui32TypeSize * j + 0];
   1413 						ub[1] = pData[ui32TypeSize * j + 1];
   1414 						ub[2] = pData[ui32TypeSize * j + 2];
   1415 						ub[3] = pData[ui32TypeSize * j + 3];
   1416 
   1417 						((unsigned int*) pData)[j] = (unsigned int) ((ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0]);
   1418 					}
   1419 
   1420 					pData += data.nStride;
   1421 				}
   1422 			}
   1423 			break;
   1424 		default: { _ASSERT(false); }
   1425 	};
   1426 }
   1427 
   1428 static void PVRTFixInterleavedEndianness(SPODMesh &s)
   1429 {
   1430 	if(!s.pInterleaved || PVRTIsLittleEndian())
   1431 		return;
   1432 
   1433 	PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sVertex, s.nNumVertex);
   1434 	PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sNormals, s.nNumVertex);
   1435 	PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sTangents, s.nNumVertex);
   1436 	PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sBinormals, s.nNumVertex);
   1437 
   1438 	for(unsigned int i = 0; i < s.nNumUVW; ++i)
   1439 		PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.psUVW[i], s.nNumVertex);
   1440 
   1441 	PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sVtxColours, s.nNumVertex);
   1442 	PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sBoneIdx, s.nNumVertex);
   1443 	PVRTFixInterleavedEndiannessUsingCPODData(s.pInterleaved, s.sBoneWeight, s.nNumVertex);
   1444 }
   1445 
   1446 /*!***************************************************************************
   1447  @Function			ReadMesh
   1448  @Modified			s The SPODMesh to read into
   1449  @Input				src	CSource object to read data from.
   1450  @Return			true if successful
   1451  @Description		Read a mesh block in from a pod file
   1452 *****************************************************************************/
   1453 static bool ReadMesh(
   1454 	SPODMesh	&s,
   1455 	CSource		&src)
   1456 {
   1457 	unsigned int	nName, nLen;
   1458 	unsigned int	nUVWs=0;
   1459 
   1460 	PVRTMatrixIdentity(s.mUnpackMatrix);
   1461 
   1462 	while(src.ReadMarker(nName, nLen))
   1463 	{
   1464 		switch(nName)
   1465 		{
   1466 		case ePODFileMesh | PVRTMODELPOD_TAG_END:
   1467 			if(nUVWs != s.nNumUVW)
   1468 				return false;
   1469 			PVRTFixInterleavedEndianness(s);
   1470 			return true;
   1471 
   1472 		case ePODFileMeshNumVtx:			if(!src.Read32(s.nNumVertex)) return false;													break;
   1473 		case ePODFileMeshNumFaces:			if(!src.Read32(s.nNumFaces)) return false;													break;
   1474 		case ePODFileMeshNumUVW:			if(!src.Read32(s.nNumUVW)) return false;	if(!SafeAlloc(s.psUVW, s.nNumUVW)) return false;	break;
   1475 		case ePODFileMeshStripLength:		if(!src.ReadAfterAlloc32(s.pnStripLength, nLen)) return false;								break;
   1476 		case ePODFileMeshNumStrips:			if(!src.Read32(s.nNumStrips)) return false;													break;
   1477 		case ePODFileMeshInterleaved:		if(!src.ReadAfterAlloc(s.pInterleaved, nLen)) return false;									break;
   1478 		case ePODFileMeshBoneBatches:		if(!src.ReadAfterAlloc32(s.sBoneBatches.pnBatches, nLen)) return false;						break;
   1479 		case ePODFileMeshBoneBatchBoneCnts:	if(!src.ReadAfterAlloc32(s.sBoneBatches.pnBatchBoneCnt, nLen)) return false;					break;
   1480 		case ePODFileMeshBoneBatchOffsets:	if(!src.ReadAfterAlloc32(s.sBoneBatches.pnBatchOffset, nLen)) return false;					break;
   1481 		case ePODFileMeshBoneBatchBoneMax:	if(!src.Read32(s.sBoneBatches.nBatchBoneMax)) return false;									break;
   1482 		case ePODFileMeshBoneBatchCnt:		if(!src.Read32(s.sBoneBatches.nBatchCnt)) return false;										break;
   1483 		case ePODFileMeshUnpackMatrix:		if(!src.ReadArray32(&s.mUnpackMatrix.f[0], 16)) return false;										break;
   1484 
   1485 		case ePODFileMeshFaces:			if(!ReadCPODData(s.sFaces, src, ePODFileMeshFaces, true)) return false;							break;
   1486 		case ePODFileMeshVtx:			if(!ReadCPODData(s.sVertex, src, ePODFileMeshVtx, s.pInterleaved == 0)) return false;			break;
   1487 		case ePODFileMeshNor:			if(!ReadCPODData(s.sNormals, src, ePODFileMeshNor, s.pInterleaved == 0)) return false;			break;
   1488 		case ePODFileMeshTan:			if(!ReadCPODData(s.sTangents, src, ePODFileMeshTan, s.pInterleaved == 0)) return false;			break;
   1489 		case ePODFileMeshBin:			if(!ReadCPODData(s.sBinormals, src, ePODFileMeshBin, s.pInterleaved == 0)) return false;			break;
   1490 		case ePODFileMeshUVW:			if(!ReadCPODData(s.psUVW[nUVWs++], src, ePODFileMeshUVW, s.pInterleaved == 0)) return false;		break;
   1491 		case ePODFileMeshVtxCol:		if(!ReadCPODData(s.sVtxColours, src, ePODFileMeshVtxCol, s.pInterleaved == 0)) return false;		break;
   1492 		case ePODFileMeshBoneIdx:		if(!ReadCPODData(s.sBoneIdx, src, ePODFileMeshBoneIdx, s.pInterleaved == 0)) return false;		break;
   1493 		case ePODFileMeshBoneWeight:	if(!ReadCPODData(s.sBoneWeight, src, ePODFileMeshBoneWeight, s.pInterleaved == 0)) return false;	break;
   1494 
   1495 		default:
   1496 			if(!src.Skip(nLen)) return false;
   1497 		}
   1498 	}
   1499 	return false;
   1500 }
   1501 
   1502 /*!***************************************************************************
   1503  @Function			ReadNode
   1504  @Modified			s The SPODNode to read into
   1505  @Input				src	CSource object to read data from.
   1506  @Return			true if successful
   1507  @Description		Read a node block in from a pod file
   1508 *****************************************************************************/
   1509 static bool ReadNode(
   1510 	SPODNode	&s,
   1511 	CSource		&src)
   1512 {
   1513 	unsigned int nName, nLen;
   1514 	bool bOldNodeFormat = false;
   1515 	VERTTYPE fPos[3]   = {0,0,0};
   1516 	VERTTYPE fQuat[4]  = {0,0,0,f2vt(1)};
   1517 	VERTTYPE fScale[7] = {f2vt(1),f2vt(1),f2vt(1),0,0,0,0};
   1518 
   1519 	// Set default for user data
   1520 	s.pUserData = 0;
   1521 	s.nUserDataSize = 0;
   1522 
   1523 	while(src.ReadMarker(nName, nLen))
   1524 	{
   1525 		switch(nName)
   1526 		{
   1527 		case ePODFileNode | PVRTMODELPOD_TAG_END:
   1528 			if(bOldNodeFormat)
   1529 			{
   1530 				if(s.pfAnimPosition)
   1531 					s.nAnimFlags |= ePODHasPositionAni;
   1532 				else
   1533 				{
   1534 					s.pfAnimPosition = (VERTTYPE*) malloc(sizeof(fPos));
   1535 					memcpy(s.pfAnimPosition, fPos, sizeof(fPos));
   1536 				}
   1537 
   1538 				if(s.pfAnimRotation)
   1539 					s.nAnimFlags |= ePODHasRotationAni;
   1540 				else
   1541 				{
   1542 					s.pfAnimRotation = (VERTTYPE*) malloc(sizeof(fQuat));
   1543 					memcpy(s.pfAnimRotation, fQuat, sizeof(fQuat));
   1544 				}
   1545 
   1546 				if(s.pfAnimScale)
   1547 					s.nAnimFlags |= ePODHasScaleAni;
   1548 				else
   1549 				{
   1550 					s.pfAnimScale = (VERTTYPE*) malloc(sizeof(fScale));
   1551 					memcpy(s.pfAnimScale, fScale, sizeof(fScale));
   1552 				}
   1553 			}
   1554 			return true;
   1555 
   1556 		case ePODFileNodeIdx:		if(!src.Read32(s.nIdx)) return false;								break;
   1557 		case ePODFileNodeName:		if(!src.ReadAfterAlloc(s.pszName, nLen)) return false;			break;
   1558 		case ePODFileNodeIdxMat:	if(!src.Read32(s.nIdxMaterial)) return false;						break;
   1559 		case ePODFileNodeIdxParent:	if(!src.Read32(s.nIdxParent)) return false;						break;
   1560 		case ePODFileNodeAnimFlags:if(!src.Read32(s.nAnimFlags))return false;							break;
   1561 
   1562 		case ePODFileNodeAnimPosIdx:	if(!src.ReadAfterAlloc32(s.pnAnimPositionIdx, nLen)) return false;	break;
   1563 		case ePODFileNodeAnimPos:	if(!src.ReadAfterAlloc32(s.pfAnimPosition, nLen)) return false;	break;
   1564 
   1565 		case ePODFileNodeAnimRotIdx:	if(!src.ReadAfterAlloc32(s.pnAnimRotationIdx, nLen)) return false;	break;
   1566 		case ePODFileNodeAnimRot:	if(!src.ReadAfterAlloc32(s.pfAnimRotation, nLen)) return false;	break;
   1567 
   1568 		case ePODFileNodeAnimScaleIdx:	if(!src.ReadAfterAlloc32(s.pnAnimScaleIdx, nLen)) return false;	break;
   1569 		case ePODFileNodeAnimScale:	if(!src.ReadAfterAlloc32(s.pfAnimScale, nLen)) return false;		break;
   1570 
   1571 		case ePODFileNodeAnimMatrixIdx:	if(!src.ReadAfterAlloc32(s.pnAnimMatrixIdx, nLen)) return false;	break;
   1572 		case ePODFileNodeAnimMatrix:if(!src.ReadAfterAlloc32(s.pfAnimMatrix, nLen)) return false;	break;
   1573 
   1574 		case ePODFileNodeUserData:
   1575 			if(!src.ReadAfterAlloc(s.pUserData, nLen))
   1576 				return false;
   1577 			else
   1578 			{
   1579 				s.nUserDataSize = nLen;
   1580 				break;
   1581 			}
   1582 
   1583 		// Parameters from the older pod format
   1584 		case ePODFileNodePos:		if(!src.ReadArray32(&fPos[0], 3))   return false;		bOldNodeFormat = true;		break;
   1585 		case ePODFileNodeRot:		if(!src.ReadArray32(&fQuat[0], 4))  return false;		bOldNodeFormat = true;		break;
   1586 		case ePODFileNodeScale:		if(!src.ReadArray32(&fScale[0], 3)) return false;		bOldNodeFormat = true;		break;
   1587 
   1588 		default:
   1589 			if(!src.Skip(nLen)) return false;
   1590 		}
   1591 	}
   1592 
   1593 	return false;
   1594 }
   1595 
   1596 /*!***************************************************************************
   1597  @Function			ReadTexture
   1598  @Modified			s The SPODTexture to read into
   1599  @Input				src	CSource object to read data from.
   1600  @Return			true if successful
   1601  @Description		Read a texture block in from a pod file
   1602 *****************************************************************************/
   1603 static bool ReadTexture(
   1604 	SPODTexture	&s,
   1605 	CSource		&src)
   1606 {
   1607 	unsigned int nName, nLen;
   1608 
   1609 	while(src.ReadMarker(nName, nLen))
   1610 	{
   1611 		switch(nName)
   1612 		{
   1613 		case ePODFileTexture | PVRTMODELPOD_TAG_END:			return true;
   1614 
   1615 		case ePODFileTexName:		if(!src.ReadAfterAlloc(s.pszName, nLen)) return false;			break;
   1616 
   1617 		default:
   1618 			if(!src.Skip(nLen)) return false;
   1619 		}
   1620 	}
   1621 	return false;
   1622 }
   1623 
   1624 /*!***************************************************************************
   1625  @Function			ReadScene
   1626  @Modified			s The SPODScene to read into
   1627  @Input				src	CSource object to read data from.
   1628  @Return			true if successful
   1629  @Description		Read a scene block in from a pod file
   1630 *****************************************************************************/
   1631 static bool ReadScene(
   1632 	SPODScene	&s,
   1633 	CSource		&src)
   1634 {
   1635 	unsigned int nName, nLen;
   1636 	unsigned int nCameras=0, nLights=0, nMaterials=0, nMeshes=0, nTextures=0, nNodes=0;
   1637 	s.nFPS = 30;
   1638 	s.fUnits = 1.0f;
   1639 
   1640 	// Set default for user data
   1641 	s.pUserData = 0;
   1642 	s.nUserDataSize = 0;
   1643 
   1644 	while(src.ReadMarker(nName, nLen))
   1645 	{
   1646 		switch(nName)
   1647 		{
   1648 		case ePODFileScene | PVRTMODELPOD_TAG_END:
   1649 			if(nCameras		!= s.nNumCamera) return false;
   1650 			if(nLights		!= s.nNumLight) return false;
   1651 			if(nMaterials	!= s.nNumMaterial) return false;
   1652 			if(nMeshes		!= s.nNumMesh) return false;
   1653 			if(nTextures	!= s.nNumTexture) return false;
   1654 			if(nNodes		!= s.nNumNode) return false;
   1655 			return true;
   1656 
   1657 		case ePODFileUnits:				if(!src.Read32(s.fUnits))	return false;				break;
   1658 		case ePODFileColourBackground:	if(!src.ReadArray32(&s.pfColourBackground[0], sizeof(s.pfColourBackground) / sizeof(*s.pfColourBackground))) return false;	break;
   1659 		case ePODFileColourAmbient:		if(!src.ReadArray32(&s.pfColourAmbient[0], sizeof(s.pfColourAmbient) / sizeof(*s.pfColourAmbient))) return false;		break;
   1660 		case ePODFileNumCamera:			if(!src.Read32(s.nNumCamera)) return false;			if(!SafeAlloc(s.pCamera, s.nNumCamera)) return false;		break;
   1661 		case ePODFileNumLight:			if(!src.Read32(s.nNumLight)) return false;			if(!SafeAlloc(s.pLight, s.nNumLight)) return false;			break;
   1662 		case ePODFileNumMesh:			if(!src.Read32(s.nNumMesh)) return false;				if(!SafeAlloc(s.pMesh, s.nNumMesh)) return false;			break;
   1663 		case ePODFileNumNode:			if(!src.Read32(s.nNumNode)) return false;				if(!SafeAlloc(s.pNode, s.nNumNode)) return false;			break;
   1664 		case ePODFileNumMeshNode:		if(!src.Read32(s.nNumMeshNode)) return false;			break;
   1665 		case ePODFileNumTexture:		if(!src.Read32(s.nNumTexture)) return false;			if(!SafeAlloc(s.pTexture, s.nNumTexture)) return false;		break;
   1666 		case ePODFileNumMaterial:		if(!src.Read32(s.nNumMaterial)) return false;			if(!SafeAlloc(s.pMaterial, s.nNumMaterial)) return false;	break;
   1667 		case ePODFileNumFrame:			if(!src.Read32(s.nNumFrame)) return false;			break;
   1668 		case ePODFileFPS:				if(!src.Read32(s.nFPS))	return false;				break;
   1669 		case ePODFileFlags:				if(!src.Read32(s.nFlags)) return false;				break;
   1670 
   1671 		case ePODFileCamera:	if(!ReadCamera(s.pCamera[nCameras++], src)) return false;		break;
   1672 		case ePODFileLight:		if(!ReadLight(s.pLight[nLights++], src)) return false;			break;
   1673 		case ePODFileMaterial:	if(!ReadMaterial(s.pMaterial[nMaterials++], src)) return false;	break;
   1674 		case ePODFileMesh:		if(!ReadMesh(s.pMesh[nMeshes++], src)) return false;			break;
   1675 		case ePODFileNode:		if(!ReadNode(s.pNode[nNodes++], src)) return false;				break;
   1676 		case ePODFileTexture:	if(!ReadTexture(s.pTexture[nTextures++], src)) return false;	break;
   1677 
   1678 		case ePODFileUserData:
   1679 			if(!src.ReadAfterAlloc(s.pUserData, nLen))
   1680 				return false;
   1681 			else
   1682 			{
   1683 				s.nUserDataSize = nLen;
   1684 				break;
   1685 			}
   1686 
   1687 		default:
   1688 			if(!src.Skip(nLen)) return false;
   1689 		}
   1690 	}
   1691 	return false;
   1692 }
   1693 
   1694 /*!***************************************************************************
   1695  @Function			Read
   1696  @Output			pS				SPODScene data. May be NULL.
   1697  @Input				src				CSource object to read data from.
   1698  @Output			pszExpOpt		Export options.
   1699  @Input				count			Data size.
   1700  @Output			pszHistory		Export history.
   1701  @Input				historyCount	History data size.
   1702  @Description		Loads the specified ".POD" file; returns the scene in
   1703 					pScene. This structure must later be destroyed with
   1704 					PVRTModelPODDestroy() to prevent memory leaks.
   1705 					".POD" files are exported from 3D Studio MAX using a
   1706 					PowerVR plugin. pS may be NULL if only the export options
   1707 					are required.
   1708 *****************************************************************************/
   1709 static bool Read(
   1710 	SPODScene		* const pS,
   1711 	CSource			&src,
   1712 	char			* const pszExpOpt,
   1713 	const size_t	count,
   1714 	char			* const pszHistory,
   1715 	const size_t	historyCount)
   1716 {
   1717 	unsigned int	nName, nLen;
   1718 	bool			bVersionOK = false, bDone = false;
   1719 	bool			bNeedOptions = pszExpOpt != 0;
   1720 	bool			bNeedHistory = pszHistory != 0;
   1721 	bool			bLoadingOptionsOrHistory = bNeedOptions || bNeedHistory;
   1722 
   1723 	while(src.ReadMarker(nName, nLen))
   1724 	{
   1725 		switch(nName)
   1726 		{
   1727 		case ePODFileVersion:
   1728 			{
   1729 				char *pszVersion = NULL;
   1730 				if(nLen != strlen(PVRTMODELPOD_VERSION)+1) return false;
   1731 				if(!SafeAlloc(pszVersion, nLen)) return false;
   1732 				if(!src.Read(pszVersion, nLen)) return false;
   1733 				if(strcmp(pszVersion, PVRTMODELPOD_VERSION) != 0) return false;
   1734 				bVersionOK = true;
   1735 				FREE(pszVersion);
   1736 			}
   1737 			continue;
   1738 
   1739 		case ePODFileScene:
   1740 			if(pS)
   1741 			{
   1742 				if(!ReadScene(*pS, src))
   1743 					return false;
   1744 				bDone = true;
   1745 			}
   1746 			continue;
   1747 
   1748 		case ePODFileExpOpt:
   1749 			if(bNeedOptions)
   1750 			{
   1751 				if(!src.Read(pszExpOpt, PVRT_MIN(nLen, (unsigned int) count)))
   1752 					return false;
   1753 
   1754 				bNeedOptions = false;
   1755 
   1756 				if(count < nLen)
   1757 					nLen -= (unsigned int) count ; // Adjust nLen as the read has moved our position
   1758 				else
   1759 					nLen = 0;
   1760 			}
   1761 			break;
   1762 
   1763 		case ePODFileHistory:
   1764 			if(bNeedHistory)
   1765 			{
   1766 				if(!src.Read(pszHistory, PVRT_MIN(nLen, (unsigned int) historyCount)))
   1767 					return false;
   1768 
   1769 				bNeedHistory = false;
   1770 
   1771 				if(count < nLen)
   1772 					nLen -= (unsigned int) historyCount; // Adjust nLen as the read has moved our position
   1773 				else
   1774 					nLen = 0;
   1775 			}
   1776 			break;
   1777 
   1778 		case ePODFileScene | PVRTMODELPOD_TAG_END:
   1779 			return bVersionOK == true && bDone == true;
   1780 
   1781 		case (unsigned int) ePODFileEndiannessMisMatch:
   1782 			PVRTErrorOutputDebug("Error: Endianness mismatch between the .pod file and the platform.\n");
   1783 			return false;
   1784 
   1785 		}
   1786 
   1787 		if(bLoadingOptionsOrHistory && !bNeedOptions && !bNeedHistory)
   1788 			return true; // The options and/or history has been loaded
   1789 
   1790 		// Unhandled data, skip it
   1791 		if(!src.Skip(nLen))
   1792 			return false;
   1793 	}
   1794 
   1795 	if(bLoadingOptionsOrHistory)
   1796 		return true;
   1797 
   1798 	if(!pS)
   1799 		return false;
   1800 
   1801 	/*
   1802 		Convert data to fixed or float point as this build desires
   1803 	*/
   1804 #ifdef PVRT_FIXED_POINT_ENABLE
   1805 	if(!(pS->nFlags & PVRTMODELPODSF_FIXED))
   1806 	{
   1807 		PVRTErrorOutputDebug("Error: The tools have been compiled with fixed point enabled but the POD file isn't in fixed point format.\n");
   1808 #else
   1809 	if(pS->nFlags & PVRTMODELPODSF_FIXED)
   1810 	{
   1811 		PVRTErrorOutputDebug("Error: The POD file is in fixed point format but the tools haven't been compiled with fixed point enabled.\n");
   1812 #endif
   1813 		return false;
   1814 	}
   1815 
   1816 
   1817 	return bVersionOK == true && bDone == true;
   1818 }
   1819 
   1820 /*!***************************************************************************
   1821  @Function			ReadFromSourceStream
   1822  @Output			pS				CPVRTModelPOD data. May not be NULL.
   1823  @Input				src				CSource object to read data from.
   1824  @Output			pszExpOpt		Export options.
   1825  @Input				count			Data size.
   1826  @Output			pszHistory		Export history.
   1827  @Input				historyCount	History data size.
   1828  @Description		Loads the ".POD" data from the source stream; returns the scene
   1829 					in pS.
   1830 *****************************************************************************/
   1831 static EPVRTError ReadFromSourceStream(
   1832 	CPVRTModelPOD	* const pS,
   1833 	CSourceStream &src,
   1834 	char			* const pszExpOpt,
   1835 	const size_t	count,
   1836 	char			* const pszHistory,
   1837 	const size_t	historyCount)
   1838 {
   1839 	memset(pS, 0, sizeof(*pS));
   1840 	if(!Read(pszExpOpt || pszHistory ? NULL : pS, src, pszExpOpt, count, pszHistory, historyCount))
   1841 		return PVR_FAIL;
   1842 
   1843 	if(pS->InitImpl() != PVR_SUCCESS)
   1844 		return PVR_FAIL;
   1845 
   1846 	return PVR_SUCCESS;
   1847 }
   1848 
   1849 /****************************************************************************
   1850 ** Class: CPVRTModelPOD
   1851 ****************************************************************************/
   1852 
   1853 /*!***************************************************************************
   1854  @Function			ReadFromFile
   1855  @Input				pszFileName		Filename to load
   1856  @Output			pszExpOpt		String in which to place exporter options
   1857  @Input				count			Maximum number of characters to store.
   1858  @Output			pszHistory		String in which to place the pod file history
   1859  @Input				historyCount	Maximum number of characters to store.
   1860  @Return			PVR_SUCCESS if successful, PVR_FAIL if not
   1861  @Description		Loads the specified ".POD" file; returns the scene in
   1862 					pScene. This structure must later be destroyed with
   1863 					PVRTModelPODDestroy() to prevent memory leaks.
   1864 					".POD" files are exported using the PVRGeoPOD exporters.
   1865 					If pszExpOpt is NULL, the scene is loaded; otherwise the
   1866 					scene is not loaded and pszExpOpt is filled in. The same
   1867 					is true for pszHistory.
   1868 *****************************************************************************/
   1869 EPVRTError CPVRTModelPOD::ReadFromFile(
   1870 	const char		* const pszFileName,
   1871 	char			* const pszExpOpt,
   1872 	const size_t	count,
   1873 	char			* const pszHistory,
   1874 	const size_t	historyCount)
   1875 {
   1876 	CSourceStream src;
   1877 
   1878 	if(!src.Init(pszFileName))
   1879 		return PVR_FAIL;
   1880 
   1881 	return ReadFromSourceStream(this, src, pszExpOpt, count, pszHistory, historyCount);
   1882 }
   1883 
   1884 /*!***************************************************************************
   1885  @Function			ReadFromMemory
   1886  @Input				pData			Data to load
   1887  @Input				i32Size			Size of data
   1888  @Output			pszExpOpt		String in which to place exporter options
   1889  @Input				count			Maximum number of characters to store.
   1890  @Output			pszHistory		String in which to place the pod file history
   1891  @Input				historyCount	Maximum number of characters to store.
   1892  @Return			PVR_SUCCESS if successful, PVR_FAIL if not
   1893  @Description		Loads the supplied pod data. This data can be exported
   1894 					directly to a header using one of the pod exporters.
   1895 					If pszExpOpt is NULL, the scene is loaded; otherwise the
   1896 					scene is not loaded and pszExpOpt is filled in. The same
   1897 					is true for pszHistory.
   1898 *****************************************************************************/
   1899 EPVRTError CPVRTModelPOD::ReadFromMemory(
   1900 	const char		* pData,
   1901 	const size_t	i32Size,
   1902 	char			* const pszExpOpt,
   1903 	const size_t	count,
   1904 	char			* const pszHistory,
   1905 	const size_t	historyCount)
   1906 {
   1907 	CSourceStream src;
   1908 
   1909 	if(!src.Init(pData, i32Size))
   1910 		return PVR_FAIL;
   1911 
   1912 	return ReadFromSourceStream(this, src, pszExpOpt, count, pszHistory, historyCount);
   1913 }
   1914 
   1915 /*!***************************************************************************
   1916  @Function			ReadFromMemory
   1917  @Input				scene			Scene data from the header file
   1918  @Return			PVR_SUCCESS if successful, PVR_FAIL if not
   1919  @Description		Sets the scene data from the supplied data structure. Use
   1920 					when loading from .H files.
   1921 *****************************************************************************/
   1922 EPVRTError CPVRTModelPOD::ReadFromMemory(
   1923 	const SPODScene &scene)
   1924 {
   1925 	Destroy();
   1926 
   1927 	memset(this, 0, sizeof(*this));
   1928 
   1929 	*(SPODScene*)this = scene;
   1930 
   1931 	if(InitImpl() != PVR_SUCCESS)
   1932 		return PVR_FAIL;
   1933 
   1934 	m_pImpl->bFromMemory = true;
   1935 
   1936 	return PVR_SUCCESS;
   1937 }
   1938 
   1939 /*!***************************************************************************
   1940  @Function			CopyFromMemory
   1941  @Input				scene			Scene data
   1942  @Return			PVR_SUCCESS if successful, PVR_FAIL if not
   1943  @Description		Sets the scene data from the supplied data structure.
   1944 *****************************************************************************/
   1945 EPVRTError CPVRTModelPOD::CopyFromMemory(const SPODScene &scene)
   1946 {
   1947 	Destroy();
   1948 
   1949 	unsigned int i;
   1950 
   1951 	// SPODScene
   1952 	nNumFrame	= scene.nNumFrame;
   1953 	nFPS		= scene.nFPS;
   1954 	nFlags		= scene.nFlags;
   1955 	fUnits		= scene.fUnits;
   1956 
   1957 	for(i = 0; i < 3; ++i)
   1958 	{
   1959 		pfColourBackground[i] = scene.pfColourBackground[i];
   1960 		pfColourAmbient[i]	  = scene.pfColourAmbient[i];
   1961 	}
   1962 
   1963 	// Nodes
   1964 	if(scene.nNumNode && SafeAlloc(pNode, scene.nNumNode))
   1965 	{
   1966 		nNumNode     = scene.nNumNode;
   1967 		nNumMeshNode = scene.nNumMeshNode;
   1968 
   1969 		for(i = 0; i < nNumNode; ++i)
   1970 			PVRTModelPODCopyNode(scene.pNode[i], pNode[i], scene.nNumFrame);
   1971 	}
   1972 
   1973 	// Meshes
   1974 	if(scene.nNumMesh && SafeAlloc(pMesh, scene.nNumMesh))
   1975 	{
   1976 		nNumMesh = scene.nNumMesh;
   1977 
   1978 		for(i = 0; i < nNumMesh; ++i)
   1979 			PVRTModelPODCopyMesh(scene.pMesh[i], pMesh[i]);
   1980 	}
   1981 
   1982 	// Cameras
   1983 	if(scene.nNumCamera && SafeAlloc(pCamera, scene.nNumCamera))
   1984 	{
   1985 		nNumCamera = scene.nNumCamera;
   1986 
   1987 		for(i = 0; i < nNumCamera; ++i)
   1988 			PVRTModelPODCopyCamera(scene.pCamera[i], pCamera[i], scene.nNumFrame);
   1989 	}
   1990 
   1991 	// Lights
   1992 	if(scene.nNumLight && SafeAlloc(pLight, scene.nNumLight))
   1993 	{
   1994 		nNumLight = scene.nNumLight;
   1995 
   1996 		for(i = 0; i < nNumLight; ++i)
   1997 			PVRTModelPODCopyLight(scene.pLight[i], pLight[i]);
   1998 	}
   1999 
   2000 	// Textures
   2001 	if(scene.nNumTexture && SafeAlloc(pTexture, scene.nNumTexture))
   2002 	{
   2003 		nNumTexture = scene.nNumTexture;
   2004 
   2005 		for(i = 0; i < nNumTexture; ++i)
   2006 			PVRTModelPODCopyTexture(scene.pTexture[i], pTexture[i]);
   2007 	}
   2008 
   2009 	// Materials
   2010 	if(scene.nNumMaterial && SafeAlloc(pMaterial, scene.nNumMaterial))
   2011 	{
   2012 		nNumMaterial = scene.nNumMaterial;
   2013 
   2014 		for(i = 0; i < nNumMaterial; ++i)
   2015 			PVRTModelPODCopyMaterial(scene.pMaterial[i], pMaterial[i]);
   2016 	}
   2017 
   2018 	if(scene.pUserData && SafeAlloc(pUserData, scene.nUserDataSize))
   2019 	{
   2020 		memcpy(pUserData, scene.pUserData, nUserDataSize);
   2021 		nUserDataSize = scene.nUserDataSize;
   2022 	}
   2023 
   2024 	if(InitImpl() != PVR_SUCCESS)
   2025 		return PVR_FAIL;
   2026 
   2027 	return PVR_SUCCESS;
   2028 }
   2029 
   2030 #if defined(_WIN32)
   2031 /*!***************************************************************************
   2032  @Function			ReadFromResource
   2033  @Input				pszName			Name of the resource to load from
   2034  @Return			PVR_SUCCESS if successful, PVR_FAIL if not
   2035  @Description		Loads the specified ".POD" file; returns the scene in
   2036 					pScene. This structure must later be destroyed with
   2037 					PVRTModelPODDestroy() to prevent memory leaks.
   2038 					".POD" files are exported from 3D Studio MAX using a
   2039 					PowerVR plugin.
   2040 *****************************************************************************/
   2041 EPVRTError CPVRTModelPOD::ReadFromResource(
   2042 	const TCHAR * const pszName)
   2043 {
   2044 	CSourceResource src;
   2045 
   2046 	if(!src.Init(pszName))
   2047 		return PVR_FAIL;
   2048 
   2049 	memset(this, 0, sizeof(*this));
   2050 	if(!Read(this, src, NULL, 0, NULL, 0))
   2051 		return PVR_FAIL;
   2052 	if(InitImpl() != PVR_SUCCESS)
   2053 		return PVR_FAIL;
   2054 	return PVR_SUCCESS;
   2055 }
   2056 #endif /* WIN32 */
   2057 
   2058 /*!***********************************************************************
   2059  @Function		InitImpl
   2060  @Description	Used by the Read*() fns to initialise implementation
   2061 				details. Should also be called by applications which
   2062 				manually build data in the POD structures for rendering;
   2063 				in this case call it after the data has been created.
   2064 				Otherwise, do not call this function.
   2065 *************************************************************************/
   2066 EPVRTError CPVRTModelPOD::InitImpl()
   2067 {
   2068 	// Allocate space for implementation data
   2069 	delete m_pImpl;
   2070 	m_pImpl = new SPVRTPODImpl;
   2071 	if(!m_pImpl)
   2072 		return PVR_FAIL;
   2073 
   2074 	// Zero implementation data
   2075 	memset(m_pImpl, 0, sizeof(*m_pImpl));
   2076 
   2077 #ifdef _DEBUG
   2078 	m_pImpl->nWmTotal = 0;
   2079 #endif
   2080 
   2081 	// Allocate world-matrix cache
   2082 	m_pImpl->pfCache		= new VERTTYPE[nNumNode];
   2083 	m_pImpl->pWmCache		= new PVRTMATRIX[nNumNode];
   2084 	m_pImpl->pWmZeroCache	= new PVRTMATRIX[nNumNode];
   2085 	FlushCache();
   2086 
   2087 	return PVR_SUCCESS;
   2088 }
   2089 
   2090 /*!***********************************************************************
   2091  @Function		DestroyImpl
   2092  @Description	Used to free memory allocated by the implementation.
   2093 *************************************************************************/
   2094 void CPVRTModelPOD::DestroyImpl()
   2095 {
   2096 	if(m_pImpl)
   2097 	{
   2098 		if(m_pImpl->pfCache)		delete [] m_pImpl->pfCache;
   2099 		if(m_pImpl->pWmCache)		delete [] m_pImpl->pWmCache;
   2100 		if(m_pImpl->pWmZeroCache)	delete [] m_pImpl->pWmZeroCache;
   2101 
   2102 		delete m_pImpl;
   2103 		m_pImpl = 0;
   2104 	}
   2105 }
   2106 
   2107 /*!***********************************************************************
   2108  @Function		FlushCache
   2109  @Description	Clears the matrix cache; use this if necessary when you
   2110 				edit the position or animation of a node.
   2111 *************************************************************************/
   2112 void CPVRTModelPOD::FlushCache()
   2113 {
   2114 	// Pre-calc frame zero matrices
   2115 	SetFrame(0);
   2116 	for(unsigned int i = 0; i < nNumNode; ++i)
   2117 		GetWorldMatrixNoCache(m_pImpl->pWmZeroCache[i], pNode[i]);
   2118 
   2119 	// Load cache with frame-zero data
   2120 	memcpy(m_pImpl->pWmCache, m_pImpl->pWmZeroCache, nNumNode * sizeof(*m_pImpl->pWmCache));
   2121 	memset(m_pImpl->pfCache, 0, nNumNode * sizeof(*m_pImpl->pfCache));
   2122 }
   2123 
   2124 /*!***********************************************************************
   2125  @Function		IsLoaded
   2126  @Description	Boolean to check whether a POD file has been loaded.
   2127 *************************************************************************/
   2128 bool CPVRTModelPOD::IsLoaded()
   2129 {
   2130 	return (m_pImpl!=NULL);
   2131 }
   2132 
   2133 /*!***************************************************************************
   2134  @Function			Constructor
   2135  @Description		Initializes the pointer to scene data to NULL
   2136 *****************************************************************************/
   2137 CPVRTModelPOD::CPVRTModelPOD() : m_pImpl(NULL)
   2138 {}
   2139 
   2140 /*!***************************************************************************
   2141  @Function			Destructor
   2142  @Description		Frees the memory allocated to store the scene in pScene.
   2143 *****************************************************************************/
   2144 CPVRTModelPOD::~CPVRTModelPOD()
   2145 {
   2146 	Destroy();
   2147 }
   2148 
   2149 /*!***************************************************************************
   2150  @Function			Destroy
   2151  @Description		Frees the memory allocated to store the scene in pScene.
   2152 *****************************************************************************/
   2153 void CPVRTModelPOD::Destroy()
   2154 {
   2155 	unsigned int	i;
   2156 
   2157 	if(m_pImpl != NULL)
   2158 	{
   2159 		/*
   2160 			Only attempt to free this memory if it was actually allocated at
   2161 			run-time, as opposed to compiled into the app.
   2162 		*/
   2163 		if(!m_pImpl->bFromMemory)
   2164 		{
   2165 
   2166 			for(i = 0; i < nNumCamera; ++i)
   2167 				FREE(pCamera[i].pfAnimFOV);
   2168 			FREE(pCamera);
   2169 
   2170 			FREE(pLight);
   2171 
   2172 			for(i = 0; i < nNumMaterial; ++i)
   2173 			{
   2174 				FREE(pMaterial[i].pszName);
   2175 				FREE(pMaterial[i].pszEffectFile);
   2176 				FREE(pMaterial[i].pszEffectName);
   2177 				FREE(pMaterial[i].pUserData);
   2178 			}
   2179 			FREE(pMaterial);
   2180 
   2181 			for(i = 0; i < nNumMesh; ++i) {
   2182 				FREE(pMesh[i].sFaces.pData);
   2183 				FREE(pMesh[i].pnStripLength);
   2184 				if(pMesh[i].pInterleaved)
   2185 				{
   2186 					FREE(pMesh[i].pInterleaved);
   2187 				}
   2188 				else
   2189 				{
   2190 					FREE(pMesh[i].sVertex.pData);
   2191 					FREE(pMesh[i].sNormals.pData);
   2192 					FREE(pMesh[i].sTangents.pData);
   2193 					FREE(pMesh[i].sBinormals.pData);
   2194 					for(unsigned int j = 0; j < pMesh[i].nNumUVW; ++j)
   2195 						FREE(pMesh[i].psUVW[j].pData);
   2196 					FREE(pMesh[i].sVtxColours.pData);
   2197 					FREE(pMesh[i].sBoneIdx.pData);
   2198 					FREE(pMesh[i].sBoneWeight.pData);
   2199 				}
   2200 				FREE(pMesh[i].psUVW);
   2201 				pMesh[i].sBoneBatches.Release();
   2202 			}
   2203 			FREE(pMesh);
   2204 
   2205 			for(i = 0; i < nNumNode; ++i) {
   2206 				FREE(pNode[i].pszName);
   2207 				FREE(pNode[i].pfAnimPosition);
   2208 				FREE(pNode[i].pnAnimPositionIdx);
   2209 				FREE(pNode[i].pfAnimRotation);
   2210 				FREE(pNode[i].pnAnimRotationIdx);
   2211 				FREE(pNode[i].pfAnimScale);
   2212 				FREE(pNode[i].pnAnimScaleIdx);
   2213 				FREE(pNode[i].pfAnimMatrix);
   2214 				FREE(pNode[i].pnAnimMatrixIdx);
   2215 				FREE(pNode[i].pUserData);
   2216 				pNode[i].nAnimFlags = 0;
   2217 			}
   2218 
   2219 			FREE(pNode);
   2220 
   2221 			for(i = 0; i < nNumTexture; ++i)
   2222 				FREE(pTexture[i].pszName);
   2223 			FREE(pTexture);
   2224 
   2225 			FREE(pUserData);
   2226 		}
   2227 
   2228 		// Free the working space used by the implementation
   2229 		DestroyImpl();
   2230 	}
   2231 
   2232 	memset(this, 0, sizeof(*this));
   2233 }
   2234 
   2235 /*!***************************************************************************
   2236  @Function			SetFrame
   2237  @Input				fFrame			Frame number
   2238  @Description		Set the animation frame for which subsequent Get*() calls
   2239 					should return data.
   2240 *****************************************************************************/
   2241 void CPVRTModelPOD::SetFrame(const VERTTYPE fFrame)
   2242 {
   2243 	if(nNumFrame) {
   2244 		/*
   2245 			Limit animation frames.
   2246 
   2247 			Example: If there are 100 frames of animation, the highest frame
   2248 			number allowed is 98, since that will blend between frames 98 and
   2249 			99. (99 being of course the 100th frame.)
   2250 		*/
   2251 		_ASSERT(fFrame <= f2vt((float)(nNumFrame-1)));
   2252 		m_pImpl->nFrame = (int)vt2f(fFrame);
   2253 		m_pImpl->fBlend = fFrame - f2vt(m_pImpl->nFrame);
   2254 	}
   2255 	else
   2256 	{
   2257 		m_pImpl->fBlend = 0;
   2258 		m_pImpl->nFrame = 0;
   2259 	}
   2260 
   2261 	m_pImpl->fFrame = fFrame;
   2262 }
   2263 
   2264 /*!***************************************************************************
   2265  @Function			GetRotationMatrix
   2266  @Output			mOut			Rotation matrix
   2267  @Input				node			Node to get the rotation matrix from
   2268  @Description		Generates the world matrix for the given Mesh Instance;
   2269 					applies the parent's transform too. Uses animation data.
   2270 *****************************************************************************/
   2271 void CPVRTModelPOD::GetRotationMatrix(
   2272 	PVRTMATRIX		&mOut,
   2273 	const SPODNode	&node) const
   2274 {
   2275 	PVRTQUATERNION	q;
   2276 
   2277 	if(node.pfAnimRotation)
   2278 	{
   2279 		if(node.nAnimFlags & ePODHasRotationAni)
   2280 		{
   2281 			if(node.pnAnimRotationIdx)
   2282 			{
   2283 				PVRTMatrixQuaternionSlerp(
   2284 					q,
   2285 					(PVRTQUATERNION&)node.pfAnimRotation[node.pnAnimRotationIdx[m_pImpl->nFrame]],
   2286 					(PVRTQUATERNION&)node.pfAnimRotation[node.pnAnimRotationIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend);
   2287 			}
   2288 			else
   2289 			{
   2290 				PVRTMatrixQuaternionSlerp(
   2291 					q,
   2292 					(PVRTQUATERNION&)node.pfAnimRotation[4*m_pImpl->nFrame],
   2293 					(PVRTQUATERNION&)node.pfAnimRotation[4*(m_pImpl->nFrame+1)], m_pImpl->fBlend);
   2294 			}
   2295 
   2296 			PVRTMatrixRotationQuaternion(mOut, q);
   2297 		}
   2298 		else
   2299 		{
   2300 			PVRTMatrixRotationQuaternion(mOut, *(PVRTQUATERNION*)node.pfAnimRotation);
   2301 		}
   2302 	}
   2303 	else
   2304 	{
   2305 		PVRTMatrixIdentity(mOut);
   2306 	}
   2307 }
   2308 
   2309 /*!***************************************************************************
   2310  @Function		GetRotationMatrix
   2311  @Input			node			Node to get the rotation matrix from
   2312  @Returns		Rotation matrix
   2313  @Description	Generates the world matrix for the given Mesh Instance;
   2314 				applies the parent's transform too. Uses animation data.
   2315 *****************************************************************************/
   2316 PVRTMat4 CPVRTModelPOD::GetRotationMatrix(const SPODNode &node) const
   2317 {
   2318 	PVRTMat4 mOut;
   2319 	GetRotationMatrix(mOut,node);
   2320 	return mOut;
   2321 }
   2322 
   2323 /*!***************************************************************************
   2324  @Function			GetScalingMatrix
   2325  @Output			mOut			Scaling matrix
   2326  @Input				node			Node to get the rotation matrix from
   2327  @Description		Generates the world matrix for the given Mesh Instance;
   2328 					applies the parent's transform too. Uses animation data.
   2329 *****************************************************************************/
   2330 void CPVRTModelPOD::GetScalingMatrix(
   2331 	PVRTMATRIX		&mOut,
   2332 	const SPODNode	&node) const
   2333 {
   2334 	PVRTVECTOR3 v;
   2335 
   2336 	if(node.pfAnimScale)
   2337 	{
   2338 		if(node.nAnimFlags & ePODHasScaleAni)
   2339 		{
   2340 			if(node.pnAnimScaleIdx)
   2341 			{
   2342 				PVRTMatrixVec3Lerp(
   2343 					v,
   2344 					(PVRTVECTOR3&)node.pfAnimScale[node.pnAnimScaleIdx[m_pImpl->nFrame+0]],
   2345 					(PVRTVECTOR3&)node.pfAnimScale[node.pnAnimScaleIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend);
   2346 			}
   2347 			else
   2348 			{
   2349 				PVRTMatrixVec3Lerp(
   2350 					v,
   2351 					(PVRTVECTOR3&)node.pfAnimScale[7*(m_pImpl->nFrame+0)],
   2352 					(PVRTVECTOR3&)node.pfAnimScale[7*(m_pImpl->nFrame+1)], m_pImpl->fBlend);
   2353 			}
   2354 
   2355 			PVRTMatrixScaling(mOut, v.x, v.y, v.z);
   2356 		}
   2357 		else
   2358 		{
   2359 			PVRTMatrixScaling(mOut, node.pfAnimScale[0], node.pfAnimScale[1], node.pfAnimScale[2]);
   2360 		}
   2361 	}
   2362 	else
   2363 	{
   2364 		PVRTMatrixIdentity(mOut);
   2365 	}
   2366 }
   2367 
   2368 /*!***************************************************************************
   2369  @Function		GetScalingMatrix
   2370  @Input			node			Node to get the rotation matrix from
   2371  @Returns		Scaling matrix
   2372  @Description	Generates the world matrix for the given Mesh Instance;
   2373 				applies the parent's transform too. Uses animation data.
   2374 *****************************************************************************/
   2375 PVRTMat4 CPVRTModelPOD::GetScalingMatrix(const SPODNode &node) const
   2376 {
   2377 	PVRTMat4 mOut;
   2378 	GetScalingMatrix(mOut, node);
   2379 	return mOut;
   2380 }
   2381 
   2382 /*!***************************************************************************
   2383  @Function			GetTranslation
   2384  @Output			V				Translation vector
   2385  @Input				node			Node to get the translation vector from
   2386  @Description		Generates the translation vector for the given Mesh
   2387 					Instance. Uses animation data.
   2388 *****************************************************************************/
   2389 void CPVRTModelPOD::GetTranslation(
   2390 	PVRTVECTOR3		&V,
   2391 	const SPODNode	&node) const
   2392 {
   2393 	if(node.pfAnimPosition)
   2394 	{
   2395 		if(node.nAnimFlags & ePODHasPositionAni)
   2396 		{
   2397 			if(node.pnAnimPositionIdx)
   2398 			{
   2399 				PVRTMatrixVec3Lerp(V,
   2400 					(PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+0]],
   2401 					(PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend);
   2402 			}
   2403 			else
   2404 			{
   2405 				PVRTMatrixVec3Lerp(V,
   2406 					(PVRTVECTOR3&)node.pfAnimPosition[3 * (m_pImpl->nFrame+0)],
   2407 					(PVRTVECTOR3&)node.pfAnimPosition[3 * (m_pImpl->nFrame+1)], m_pImpl->fBlend);
   2408 			}
   2409 		}
   2410 		else
   2411 		{
   2412 			V = *(PVRTVECTOR3*) node.pfAnimPosition;
   2413 		}
   2414 	}
   2415 	else
   2416 	{
   2417 		_ASSERT(false);
   2418 	}
   2419 }
   2420 
   2421 /*!***************************************************************************
   2422  @Function		GetTranslation
   2423  @Input			node			Node to get the translation vector from
   2424  @Returns		Translation vector
   2425  @Description	Generates the translation vector for the given Mesh
   2426 				Instance. Uses animation data.
   2427 *****************************************************************************/
   2428 PVRTVec3 CPVRTModelPOD::GetTranslation(const SPODNode &node) const
   2429 {
   2430 	PVRTVec3 vOut;
   2431 	GetTranslation(vOut, node);
   2432 	return vOut;
   2433 }
   2434 
   2435 /*!***************************************************************************
   2436  @Function			GetTranslationMatrix
   2437  @Output			mOut			Translation matrix
   2438  @Input				node			Node to get the translation matrix from
   2439  @Description		Generates the world matrix for the given Mesh Instance;
   2440 					applies the parent's transform too. Uses animation data.
   2441 *****************************************************************************/
   2442 void CPVRTModelPOD::GetTranslationMatrix(
   2443 	PVRTMATRIX		&mOut,
   2444 	const SPODNode	&node) const
   2445 {
   2446 	PVRTVECTOR3 v;
   2447 
   2448 	if(node.pfAnimPosition)
   2449 	{
   2450 		if(node.nAnimFlags & ePODHasPositionAni)
   2451 		{
   2452 			if(node.pnAnimPositionIdx)
   2453 			{
   2454 				PVRTMatrixVec3Lerp(v,
   2455 					(PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+0]],
   2456 					(PVRTVECTOR3&)node.pfAnimPosition[node.pnAnimPositionIdx[m_pImpl->nFrame+1]], m_pImpl->fBlend);
   2457 			}
   2458 			else
   2459 			{
   2460 				PVRTMatrixVec3Lerp(v,
   2461 					(PVRTVECTOR3&)node.pfAnimPosition[3*(m_pImpl->nFrame+0)],
   2462 					(PVRTVECTOR3&)node.pfAnimPosition[3*(m_pImpl->nFrame+1)], m_pImpl->fBlend);
   2463 			}
   2464 
   2465 			PVRTMatrixTranslation(mOut, v.x, v.y, v.z);
   2466 		}
   2467 		else
   2468 		{
   2469 			PVRTMatrixTranslation(mOut, node.pfAnimPosition[0], node.pfAnimPosition[1], node.pfAnimPosition[2]);
   2470 		}
   2471 	}
   2472 	else
   2473 	{
   2474 		PVRTMatrixIdentity(mOut);
   2475 	}
   2476 }
   2477 
   2478 /*!***************************************************************************
   2479  @Function		GetTranslationMatrix
   2480  @Input			node			Node to get the translation matrix from
   2481  @Returns		Translation matrix
   2482  @Description	Generates the world matrix for the given Mesh Instance;
   2483 				applies the parent's transform too. Uses animation data.
   2484 *****************************************************************************/
   2485 PVRTMat4 CPVRTModelPOD::GetTranslationMatrix(const SPODNode &node) const
   2486 {
   2487 	PVRTMat4 mOut;
   2488 	GetTranslationMatrix(mOut, node);
   2489 	return mOut;
   2490 }
   2491 
   2492 /*!***************************************************************************
   2493  @Function		GetTransformationMatrix
   2494  @Output		mOut			Transformation matrix
   2495  @Input			node			Node to get the transformation matrix from
   2496  @Description	Generates the world matrix for the given Mesh Instance;
   2497 				applies the parent's transform too. Uses animation data.
   2498 *****************************************************************************/
   2499 void CPVRTModelPOD::GetTransformationMatrix(PVRTMATRIX &mOut, const SPODNode &node) const
   2500 {
   2501 	if(node.pfAnimMatrix)
   2502 	{
   2503 		if(node.nAnimFlags & ePODHasMatrixAni)
   2504 		{
   2505 			if(node.pnAnimMatrixIdx)
   2506 				mOut = *((PVRTMATRIX*) &node.pfAnimMatrix[node.pnAnimMatrixIdx[m_pImpl->nFrame]]);
   2507 			else
   2508 				mOut = *((PVRTMATRIX*) &node.pfAnimMatrix[16*m_pImpl->nFrame]);
   2509 		}
   2510 		else
   2511 		{
   2512 			mOut = *((PVRTMATRIX*) node.pfAnimMatrix);
   2513 		}
   2514 	}
   2515 	else
   2516 	{
   2517 		PVRTMatrixIdentity(mOut);
   2518 	}
   2519 }
   2520 /*!***************************************************************************
   2521  @Function			GetWorldMatrixNoCache
   2522  @Output			mOut			World matrix
   2523  @Input				node			Node to get the world matrix from
   2524  @Description		Generates the world matrix for the given Mesh Instance;
   2525 					applies the parent's transform too. Uses animation data.
   2526 *****************************************************************************/
   2527 void CPVRTModelPOD::GetWorldMatrixNoCache(
   2528 	PVRTMATRIX		&mOut,
   2529 	const SPODNode	&node) const
   2530 {
   2531 	PVRTMATRIX mTmp;
   2532 
   2533     if(node.pfAnimMatrix) // The transformations are stored as matrices
   2534 		GetTransformationMatrix(mOut, node);
   2535 	else
   2536 	{
   2537 		// Scale
   2538 		GetScalingMatrix(mOut, node);
   2539 
   2540 		// Rotation
   2541 		GetRotationMatrix(mTmp, node);
   2542 		PVRTMatrixMultiply(mOut, mOut, mTmp);
   2543 
   2544 		// Translation
   2545 		GetTranslationMatrix(mTmp, node);
   2546 		PVRTMatrixMultiply(mOut, mOut, mTmp);
   2547 	}
   2548 
   2549  	// Do we have to worry about a parent?
   2550 	if(node.nIdxParent < 0)
   2551 		return;
   2552 
   2553 	// Apply parent's transform too.
   2554 	GetWorldMatrixNoCache(mTmp, pNode[node.nIdxParent]);
   2555 	PVRTMatrixMultiply(mOut, mOut, mTmp);
   2556 }
   2557 
   2558 /*!***************************************************************************
   2559  @Function		GetWorldMatrixNoCache
   2560  @Input			node			Node to get the world matrix from
   2561  @Returns		World matrix
   2562  @Description	Generates the world matrix for the given Mesh Instance;
   2563 				applies the parent's transform too. Uses animation data.
   2564 *****************************************************************************/
   2565 PVRTMat4 CPVRTModelPOD::GetWorldMatrixNoCache(const SPODNode& node) const
   2566 {
   2567 	PVRTMat4 mWorld;
   2568 	GetWorldMatrixNoCache(mWorld,node);
   2569 	return mWorld;
   2570 }
   2571 
   2572 /*!***************************************************************************
   2573  @Function			GetWorldMatrix
   2574  @Output			mOut			World matrix
   2575  @Input				node			Node to get the world matrix from
   2576  @Description		Generates the world matrix for the given Mesh Instance;
   2577 					applies the parent's transform too. Uses animation data.
   2578 *****************************************************************************/
   2579 void CPVRTModelPOD::GetWorldMatrix(
   2580 	PVRTMATRIX		&mOut,
   2581 	const SPODNode	&node) const
   2582 {
   2583 	unsigned int nIdx;
   2584 
   2585 #ifdef _DEBUG
   2586 	++m_pImpl->nWmTotal;
   2587 	m_pImpl->fHitPerc = (float)m_pImpl->nWmCacheHit / (float)m_pImpl->nWmTotal;
   2588 	m_pImpl->fHitPercZero = (float)m_pImpl->nWmZeroCacheHit / (float)m_pImpl->nWmTotal;
   2589 #endif
   2590 
   2591 	// Calculate a node index
   2592 	nIdx = (unsigned int)(&node - pNode);
   2593 
   2594 	// There is a dedicated cache for frame 0 data
   2595 	if(m_pImpl->fFrame == 0)
   2596 	{
   2597 		mOut = m_pImpl->pWmZeroCache[nIdx];
   2598 #ifdef _DEBUG
   2599 		++m_pImpl->nWmZeroCacheHit;
   2600 #endif
   2601 		return;
   2602 	}
   2603 
   2604 	// Has this matrix been calculated & cached?
   2605 	if(m_pImpl->fFrame == m_pImpl->pfCache[nIdx])
   2606 	{
   2607 		mOut = m_pImpl->pWmCache[nIdx];
   2608 #ifdef _DEBUG
   2609 		++m_pImpl->nWmCacheHit;
   2610 #endif
   2611 		return;
   2612 	}
   2613 
   2614 	GetWorldMatrixNoCache(mOut, node);
   2615 
   2616 	// Cache the matrix
   2617 	m_pImpl->pfCache[nIdx]	= m_pImpl->fFrame;
   2618 	m_pImpl->pWmCache[nIdx]	= mOut;
   2619 }
   2620 
   2621 /*!***************************************************************************
   2622  @Function		GetWorldMatrix
   2623  @Input			node			Node to get the world matrix from
   2624  @Returns		World matrix
   2625  @Description	Generates the world matrix for the given Mesh Instance;
   2626 				applies the parent's transform too. Uses animation data.
   2627 *****************************************************************************/
   2628 PVRTMat4 CPVRTModelPOD::GetWorldMatrix(const SPODNode& node) const
   2629 {
   2630 	PVRTMat4 mWorld;
   2631 	GetWorldMatrix(mWorld,node);
   2632 	return mWorld;
   2633 }
   2634 
   2635 /*!***************************************************************************
   2636  @Function			GetBoneWorldMatrix
   2637  @Output			mOut			Bone world matrix
   2638  @Input				NodeMesh		Mesh to take the bone matrix from
   2639  @Input				NodeBone		Bone to take the matrix from
   2640  @Description		Generates the world matrix for the given bone.
   2641 *****************************************************************************/
   2642 void CPVRTModelPOD::GetBoneWorldMatrix(
   2643 	PVRTMATRIX		&mOut,
   2644 	const SPODNode	&NodeMesh,
   2645 	const SPODNode	&NodeBone)
   2646 {
   2647 	PVRTMATRIX	mTmp;
   2648 	VERTTYPE	fFrame;
   2649 
   2650 	fFrame = m_pImpl->fFrame;
   2651 
   2652 	SetFrame(0);
   2653 
   2654 	// Transform by object matrix
   2655 	GetWorldMatrix(mOut, NodeMesh);
   2656 
   2657 	// Back transform bone from frame 0 position
   2658 	GetWorldMatrix(mTmp, NodeBone);
   2659 	PVRTMatrixInverse(mTmp, mTmp);
   2660 	PVRTMatrixMultiply(mOut, mOut, mTmp);
   2661 
   2662 	// The bone origin should now be at the origin
   2663 
   2664 	SetFrame(fFrame);
   2665 
   2666 	// Transform bone into frame fFrame position
   2667 	GetWorldMatrix(mTmp, NodeBone);
   2668 	PVRTMatrixMultiply(mOut, mOut, mTmp);
   2669 }
   2670 
   2671 /*!***************************************************************************
   2672  @Function		GetBoneWorldMatrix
   2673  @Input			NodeMesh		Mesh to take the bone matrix from
   2674  @Input			NodeBone		Bone to take the matrix from
   2675  @Returns		Bone world matrix
   2676  @Description	Generates the world matrix for the given bone.
   2677 *****************************************************************************/
   2678 PVRTMat4 CPVRTModelPOD::GetBoneWorldMatrix(
   2679 	const SPODNode	&NodeMesh,
   2680 	const SPODNode	&NodeBone)
   2681 {
   2682 	PVRTMat4 mOut;
   2683 	GetBoneWorldMatrix(mOut,NodeMesh,NodeBone);
   2684 	return mOut;
   2685 }
   2686 
   2687 /*!***************************************************************************
   2688  @Function			GetCamera
   2689  @Output			vFrom			Position of the camera
   2690  @Output			vTo				Target of the camera
   2691  @Output			vUp				Up direction of the camera
   2692  @Input				nIdx			Camera number
   2693  @Return			Camera horizontal FOV
   2694  @Description		Calculate the From, To and Up vectors for the given
   2695 					camera. Uses animation data.
   2696 					Note that even if the camera has a target, *pvTo is not
   2697 					the position of that target. *pvTo is a position in the
   2698 					correct direction of the target, one unit away from the
   2699 					camera.
   2700 *****************************************************************************/
   2701 VERTTYPE CPVRTModelPOD::GetCamera(
   2702 	PVRTVECTOR3			&vFrom,
   2703 	PVRTVECTOR3			&vTo,
   2704 	PVRTVECTOR3			&vUp,
   2705 	const unsigned int	nIdx) const
   2706 {
   2707 	PVRTMATRIX		mTmp;
   2708 	VERTTYPE		*pfData;
   2709 	SPODCamera		*pCam;
   2710 	const SPODNode	*pNd;
   2711 
   2712 	_ASSERT(nIdx < nNumCamera);
   2713 
   2714 	// Camera nodes are after the mesh and light nodes in the array
   2715 	pNd = &pNode[nNumMeshNode + nNumLight + nIdx];
   2716 
   2717 	pCam = &pCamera[pNd->nIdx];
   2718 
   2719 	GetWorldMatrix(mTmp, *pNd);
   2720 
   2721 	// View position is 0,0,0,1 transformed by world matrix
   2722 	vFrom.x = mTmp.f[12];
   2723 	vFrom.y = mTmp.f[13];
   2724 	vFrom.z = mTmp.f[14];
   2725 
   2726 	// View direction is 0,-1,0,1 transformed by world matrix
   2727 	vTo.x = -mTmp.f[4] + mTmp.f[12];
   2728 	vTo.y = -mTmp.f[5] + mTmp.f[13];
   2729 	vTo.z = -mTmp.f[6] + mTmp.f[14];
   2730 
   2731 #if defined(BUILD_DX11)
   2732 	/*
   2733 		When you rotate the camera from "straight forward" to "straight down", in
   2734 		D3D the UP vector will be [0, 0, 1]
   2735 	*/
   2736 	vUp.x = mTmp.f[ 8];
   2737 	vUp.y = mTmp.f[ 9];
   2738 	vUp.z = mTmp.f[10];
   2739 #endif
   2740 
   2741 #if defined(BUILD_OGL) || defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
   2742 	/*
   2743 		When you rotate the camera from "straight forward" to "straight down", in
   2744 		OpenGL the UP vector will be [0, 0, -1]
   2745 	*/
   2746 	vUp.x = -mTmp.f[ 8];
   2747 	vUp.y = -mTmp.f[ 9];
   2748 	vUp.z = -mTmp.f[10];
   2749 #endif
   2750 
   2751 	/*
   2752 		Find & calculate FOV value
   2753 	*/
   2754 	if(pCam->pfAnimFOV) {
   2755 		pfData = &pCam->pfAnimFOV[m_pImpl->nFrame];
   2756 
   2757 		return pfData[0] + m_pImpl->fBlend * (pfData[1] - pfData[0]);
   2758 	} else {
   2759 		return pCam->fFOV;
   2760 	}
   2761 }
   2762 
   2763 /*!***************************************************************************
   2764  @Function			GetCameraPos
   2765  @Output			vFrom			Position of the camera
   2766  @Output			vTo				Target of the camera
   2767  @Input				nIdx			Camera number
   2768  @Return			Camera horizontal FOV
   2769  @Description		Calculate the position of the camera and its target. Uses
   2770 					animation data.
   2771 					If the queried camera does not have a target, *pvTo is
   2772 					not changed.
   2773 *****************************************************************************/
   2774 VERTTYPE CPVRTModelPOD::GetCameraPos(
   2775 	PVRTVECTOR3			&vFrom,
   2776 	PVRTVECTOR3			&vTo,
   2777 	const unsigned int	nIdx) const
   2778 {
   2779 	PVRTMATRIX		mTmp;
   2780 	VERTTYPE		*pfData;
   2781 	SPODCamera		*pCam;
   2782 	const SPODNode	*pNd;
   2783 
   2784 	_ASSERT(nIdx < nNumCamera);
   2785 
   2786 	// Camera nodes are after the mesh and light nodes in the array
   2787 	pNd = &pNode[nNumMeshNode + nNumLight + nIdx];
   2788 
   2789 	// View position is 0,0,0,1 transformed by world matrix
   2790 	GetWorldMatrix(mTmp, *pNd);
   2791 	vFrom.x = mTmp.f[12];
   2792 	vFrom.y = mTmp.f[13];
   2793 	vFrom.z = mTmp.f[14];
   2794 
   2795 	pCam = &pCamera[pNd->nIdx];
   2796 	if(pCam->nIdxTarget >= 0)
   2797 	{
   2798 		// View position is 0,0,0,1 transformed by world matrix
   2799 		GetWorldMatrix(mTmp, pNode[pCam->nIdxTarget]);
   2800 		vTo.x = mTmp.f[12];
   2801 		vTo.y = mTmp.f[13];
   2802 		vTo.z = mTmp.f[14];
   2803 	}
   2804 
   2805 	/*
   2806 		Find & calculate FOV value
   2807 	*/
   2808 	if(pCam->pfAnimFOV) {
   2809 		pfData = &pCam->pfAnimFOV[m_pImpl->nFrame];
   2810 
   2811 		return pfData[0] + m_pImpl->fBlend * (pfData[1] - pfData[0]);
   2812 	} else {
   2813 		return pCam->fFOV;
   2814 	}
   2815 }
   2816 
   2817 /*!***************************************************************************
   2818  @Function			GetLight
   2819  @Output			vPos			Position of the light
   2820  @Output			vDir			Direction of the light
   2821  @Input				nIdx			Light number
   2822  @Description		Calculate the position and direction of the given Light.
   2823 					Uses animation data.
   2824 *****************************************************************************/
   2825 void CPVRTModelPOD::GetLight(
   2826 	PVRTVECTOR3			&vPos,
   2827 	PVRTVECTOR3			&vDir,
   2828 	const unsigned int	nIdx) const
   2829 {
   2830 	PVRTMATRIX		mTmp;
   2831 	const SPODNode	*pNd;
   2832 
   2833 	_ASSERT(nIdx < nNumLight);
   2834 
   2835 	// Light nodes are after the mesh nodes in the array
   2836 	pNd = &pNode[nNumMeshNode + nIdx];
   2837 
   2838 	GetWorldMatrix(mTmp, *pNd);
   2839 
   2840 	// View position is 0,0,0,1 transformed by world matrix
   2841 	vPos.x = mTmp.f[12];
   2842 	vPos.y = mTmp.f[13];
   2843 	vPos.z = mTmp.f[14];
   2844 
   2845 	// View direction is 0,-1,0,0 transformed by world matrix
   2846 	vDir.x = -mTmp.f[4];
   2847 	vDir.y = -mTmp.f[5];
   2848 	vDir.z = -mTmp.f[6];
   2849 }
   2850 
   2851 /*!***************************************************************************
   2852  @Function		GetLightPositon
   2853  @Input			u32Idx			Light number
   2854  @Return		PVRTVec4 position of light with w set correctly
   2855  @Description	Calculates the position of the given light. Uses animation data
   2856 *****************************************************************************/
   2857 PVRTVec4 CPVRTModelPOD::GetLightPosition(const unsigned int u32Idx) const
   2858 {	// TODO: make this a real function instead of just wrapping GetLight()
   2859 	PVRTVec3 vPos, vDir;
   2860 	GetLight(vPos,vDir,u32Idx);
   2861 
   2862 	_ASSERT(u32Idx < nNumLight);
   2863 	_ASSERT(pLight[u32Idx].eType!=ePODDirectional);
   2864 	return PVRTVec4(vPos,1);
   2865 }
   2866 
   2867 /*!***************************************************************************
   2868  @Function		GetLightDirection
   2869  @Input			u32Idx			Light number
   2870  @Return		PVRTVec4 direction of light with w set correctly
   2871  @Description	Calculate the direction of the given Light. Uses animation data.
   2872 *****************************************************************************/
   2873 PVRTVec4 CPVRTModelPOD::GetLightDirection(const unsigned int u32Idx) const
   2874 {	// TODO: make this a real function instead of just wrapping GetLight()
   2875 	PVRTVec3 vPos, vDir;
   2876 	GetLight(vPos,vDir,u32Idx);
   2877 
   2878 	_ASSERT(u32Idx < nNumLight);
   2879 	_ASSERT(pLight[u32Idx].eType!=ePODPoint);
   2880 	return PVRTVec4(vDir,0);
   2881 }
   2882 
   2883 /*!***************************************************************************
   2884  @Function			CreateSkinIdxWeight
   2885  @Output			pIdx				Four bytes containing matrix indices for vertex (0..255) (D3D: use UBYTE4)
   2886  @Output			pWeight				Four bytes containing blend weights for vertex (0.0 .. 1.0) (D3D: use D3DCOLOR)
   2887  @Input				nVertexBones		Number of bones this vertex uses
   2888  @Input				pnBoneIdx			Pointer to 'nVertexBones' indices
   2889  @Input				pfBoneWeight		Pointer to 'nVertexBones' blend weights
   2890  @Description		Creates the matrix indices and blend weights for a boned
   2891 					vertex. Call once per vertex of a boned mesh.
   2892 *****************************************************************************/
   2893 EPVRTError CPVRTModelPOD::CreateSkinIdxWeight(
   2894 	char			* const pIdx,			// Four bytes containing matrix indices for vertex (0..255) (D3D: use UBYTE4)
   2895 	char			* const pWeight,		// Four bytes containing blend weights for vertex (0.0 .. 1.0) (D3D: use D3DCOLOR)
   2896 	const int		nVertexBones,			// Number of bones this vertex uses
   2897 	const int		* const pnBoneIdx,		// Pointer to 'nVertexBones' indices
   2898 	const VERTTYPE	* const pfBoneWeight)	// Pointer to 'nVertexBones' blend weights
   2899 {
   2900 	int i, nSum;
   2901 	int nIdx[4];
   2902 	int nWeight[4];
   2903 
   2904 	for(i = 0; i < nVertexBones; ++i)
   2905 	{
   2906 		nIdx[i]		= pnBoneIdx[i];
   2907 		nWeight[i]	= (int)vt2f((VERTTYPEMUL(f2vt(255.0f), pfBoneWeight[i])));
   2908 
   2909 		if(nIdx[i] > 255)
   2910 		{
   2911 			PVRTErrorOutputDebug("Too many bones (highest index is 255).\n");
   2912 			return PVR_FAIL;
   2913 		}
   2914 
   2915 		nWeight[i]	= PVRT_MAX(nWeight[i], 0);
   2916 		nWeight[i]	= PVRT_MIN(nWeight[i], 255);
   2917 	}
   2918 
   2919 	for(; i < 4; ++i)
   2920 	{
   2921 		nIdx[i]		= 0;
   2922 		nWeight[i]	= 0;
   2923 	}
   2924 
   2925 	if(nVertexBones)
   2926 	{
   2927 		// It's important the weights sum to 1
   2928 		nSum = 0;
   2929 		for(i = 0; i < 4; ++i)
   2930 			nSum += nWeight[i];
   2931 
   2932 		if(!nSum)
   2933 			return PVR_FAIL;
   2934 
   2935 		_ASSERT(nSum <= 255);
   2936 
   2937 		i = 0;
   2938 		while(nSum < 255)
   2939 		{
   2940 			if(nWeight[i]) {
   2941 				++nWeight[i];
   2942 				++nSum;
   2943 			}
   2944 
   2945 			if(++i > 3)
   2946 				i = 0;
   2947 		}
   2948 
   2949 		_ASSERT(nSum == 255);
   2950 	}
   2951 
   2952 #if defined(BUILD_DX11)
   2953 	*(unsigned int*)pIdx = ((unsigned int)(((nIdx[3]&0xff)<<24)|((nIdx[2]&0xff)<<16)|((nIdx[1]&0xff)<<8)|(nIdx[0]&0xff)));					// UBYTE4 is WZYX
   2954 	*(unsigned int*)pWeight = ((unsigned int)(((nWeight[3]&0xff)<<24)|((nWeight[0]&0xff)<<16)|((nWeight[1]&0xff)<<8)|(nWeight[2]&0xff)));	// D3DCOLORs are WXYZ
   2955 #endif
   2956 
   2957 #if defined(BUILD_OGL) || defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
   2958 	// Return indices and weights as bytes
   2959 	for(i = 0; i < 4; ++i)
   2960 	{
   2961 		pIdx[i]		= (char) nIdx[i];
   2962 		pWeight[i]	= (char) nWeight[i];
   2963 	}
   2964 #endif
   2965 
   2966 	return PVR_SUCCESS;
   2967 }
   2968 
   2969 /*!***************************************************************************
   2970  @Function			SavePOD
   2971  @Input				pszFilename		Filename to save to
   2972  @Input				pszExpOpt		A string containing the options used by the exporter
   2973  @Description		Save a binary POD file (.POD).
   2974 *****************************************************************************/
   2975 EPVRTError CPVRTModelPOD::SavePOD(const char * const pszFilename, const char * const pszExpOpt, const char * const pszHistory)
   2976 {
   2977 	FILE	*pFile;
   2978 	bool	bRet;
   2979 
   2980 	pFile = fopen(pszFilename, "wb+");
   2981 	if(!pFile)
   2982 		return PVR_FAIL;
   2983 
   2984 	bRet = WritePOD(pFile, pszExpOpt, pszHistory, *this);
   2985 
   2986 	// Done
   2987 	fclose(pFile);
   2988 	return bRet ? PVR_SUCCESS : PVR_FAIL;
   2989 }
   2990 
   2991 
   2992 /*!***************************************************************************
   2993  @Function			PVRTModelPODDataTypeSize
   2994  @Input				type		Type to get the size of
   2995  @Return			Size of the data element
   2996  @Description		Returns the size of each data element.
   2997 *****************************************************************************/
   2998 PVRTuint32 PVRTModelPODDataTypeSize(const EPVRTDataType type)
   2999 {
   3000 	switch(type)
   3001 	{
   3002 	default:
   3003 		_ASSERT(false);
   3004 		return 0;
   3005 	case EPODDataFloat:
   3006 		return static_cast<PVRTuint32>(sizeof(float));
   3007 	case EPODDataInt:
   3008 	case EPODDataUnsignedInt:
   3009 		return static_cast<PVRTuint32>(sizeof(int));
   3010 	case EPODDataShort:
   3011 	case EPODDataShortNorm:
   3012 	case EPODDataUnsignedShort:
   3013 	case EPODDataUnsignedShortNorm:
   3014 		return static_cast<PVRTuint32>(sizeof(unsigned short));
   3015 	case EPODDataRGBA:
   3016 		return static_cast<PVRTuint32>(sizeof(unsigned int));
   3017 	case EPODDataABGR:
   3018 		return static_cast<PVRTuint32>(sizeof(unsigned int));
   3019 	case EPODDataARGB:
   3020 		return static_cast<PVRTuint32>(sizeof(unsigned int));
   3021 	case EPODDataD3DCOLOR:
   3022 		return static_cast<PVRTuint32>(sizeof(unsigned int));
   3023 	case EPODDataUBYTE4:
   3024 		return static_cast<PVRTuint32>(sizeof(unsigned int));
   3025 	case EPODDataDEC3N:
   3026 		return static_cast<PVRTuint32>(sizeof(unsigned int));
   3027 	case EPODDataFixed16_16:
   3028 		return static_cast<PVRTuint32>(sizeof(unsigned int));
   3029 	case EPODDataUnsignedByte:
   3030 	case EPODDataUnsignedByteNorm:
   3031 	case EPODDataByte:
   3032 	case EPODDataByteNorm:
   3033 		return static_cast<PVRTuint32>(sizeof(unsigned char));
   3034 	}
   3035 }
   3036 
   3037 /*!***************************************************************************
   3038 @Function			PVRTModelPODDataTypeComponentCount
   3039 @Input				type		Type to get the number of components from
   3040 @Return				number of components in the data element
   3041 @Description		Returns the number of components in a data element.
   3042 *****************************************************************************/
   3043 PVRTuint32 PVRTModelPODDataTypeComponentCount(const EPVRTDataType type)
   3044 {
   3045 	switch(type)
   3046 	{
   3047 	default:
   3048 		_ASSERT(false);
   3049 		return 0;
   3050 
   3051 	case EPODDataFloat:
   3052 	case EPODDataInt:
   3053 	case EPODDataUnsignedInt:
   3054 	case EPODDataShort:
   3055 	case EPODDataShortNorm:
   3056 	case EPODDataUnsignedShort:
   3057 	case EPODDataUnsignedShortNorm:
   3058 	case EPODDataFixed16_16:
   3059 	case EPODDataByte:
   3060 	case EPODDataByteNorm:
   3061 	case EPODDataUnsignedByte:
   3062 	case EPODDataUnsignedByteNorm:
   3063 		return 1;
   3064 
   3065 	case EPODDataDEC3N:
   3066 		return 3;
   3067 
   3068 	case EPODDataRGBA:
   3069 	case EPODDataABGR:
   3070 	case EPODDataARGB:
   3071 	case EPODDataD3DCOLOR:
   3072 	case EPODDataUBYTE4:
   3073 		return 4;
   3074 	}
   3075 }
   3076 
   3077 /*!***************************************************************************
   3078  @Function			PVRTModelPODDataStride
   3079  @Input				data		Data elements
   3080  @Return			Size of the vector elements
   3081  @Description		Returns the size of the vector of data elements.
   3082 *****************************************************************************/
   3083 PVRTuint32 PVRTModelPODDataStride(const CPODData &data)
   3084 {
   3085 	return PVRTModelPODDataTypeSize(data.eType) * data.n;
   3086 }
   3087 
   3088 /*!***************************************************************************
   3089  @Function			PVRTModelPODDataConvert
   3090  @Modified			data		Data elements to convert
   3091  @Input				eNewType	New type of elements
   3092  @Input				nCnt		Number of elements
   3093  @Description		Convert the format of the array of vectors.
   3094 *****************************************************************************/
   3095 void PVRTModelPODDataConvert(CPODData &data, const unsigned int nCnt, const EPVRTDataType eNewType)
   3096 {
   3097 	PVRTVECTOR4f	v;
   3098 	unsigned int	i;
   3099 	CPODData		old;
   3100 
   3101 	if(!data.pData || data.eType == eNewType)
   3102 		return;
   3103 
   3104 	old = data;
   3105 
   3106 	switch(eNewType)
   3107 	{
   3108 	case EPODDataFloat:
   3109 	case EPODDataInt:
   3110 	case EPODDataUnsignedInt:
   3111 	case EPODDataUnsignedShort:
   3112 	case EPODDataUnsignedShortNorm:
   3113 	case EPODDataFixed16_16:
   3114 	case EPODDataUnsignedByte:
   3115 	case EPODDataUnsignedByteNorm:
   3116 	case EPODDataShort:
   3117 	case EPODDataShortNorm:
   3118 	case EPODDataByte:
   3119 	case EPODDataByteNorm:
   3120 		data.n = (PVRTuint32) (old.n * PVRTModelPODDataTypeComponentCount(old.eType));
   3121 		break;
   3122 	case EPODDataRGBA:
   3123 	case EPODDataABGR:
   3124 	case EPODDataARGB:
   3125 	case EPODDataD3DCOLOR:
   3126 	case EPODDataUBYTE4:
   3127 	case EPODDataDEC3N:
   3128 		data.n = 1;
   3129 		break;
   3130 	default:
   3131 		_ASSERT(false); // unrecognised type
   3132 		break;
   3133 	}
   3134 
   3135 	data.eType = eNewType;
   3136 	data.nStride = (unsigned int)PVRTModelPODDataStride(data);
   3137 
   3138 	// If the old & new strides are identical, we can convert it in place
   3139 	if(old.nStride != data.nStride)
   3140 	{
   3141 		data.pData = (unsigned char*)malloc(data.nStride * nCnt);
   3142 	}
   3143 
   3144 	for(i = 0; i < nCnt; ++i)
   3145 	{
   3146 		PVRTVertexRead(&v, old.pData + i * old.nStride, old.eType, old.n);
   3147 		PVRTVertexWrite(data.pData + i * data.nStride, eNewType, (int) (data.n * PVRTModelPODDataTypeComponentCount(data.eType)), &v);
   3148 	}
   3149 
   3150 	if(old.nStride != data.nStride)
   3151 	{
   3152 		FREE(old.pData);
   3153 	}
   3154 }
   3155 
   3156 /*!***************************************************************************
   3157  @Function		PVRTModelPODScaleAndConvertVtxData
   3158  @Modified		mesh		POD mesh to scale and convert the mesh data
   3159  @Input			eNewType	The data type to scale and convert the vertex data to
   3160  @Return		PVR_SUCCESS on success and PVR_FAIL on failure.
   3161  @Description	Scales the vertex data to fit within the range of the requested
   3162 				data type and then converts the data to that type. This function
   3163 				isn't currently compiled in for fixed point builds of the tools.
   3164 *****************************************************************************/
   3165 #if !defined(PVRT_FIXED_POINT_ENABLE)
   3166 EPVRTError PVRTModelPODScaleAndConvertVtxData(SPODMesh &mesh, const EPVRTDataType eNewType)
   3167 {
   3168 	// Initialise the matrix to identity
   3169 	PVRTMatrixIdentity(mesh.mUnpackMatrix);
   3170 
   3171 	// No vertices to process
   3172 	if(!mesh.nNumVertex)
   3173 		return PVR_SUCCESS;
   3174 
   3175 	// This function expects the data to be floats and not interleaved
   3176 	if(mesh.sVertex.eType != EPODDataFloat && mesh.pInterleaved != 0)
   3177 		return PVR_FAIL;
   3178 
   3179 	if(eNewType == EPODDataFloat) // Nothing to do
   3180 		return PVR_FAIL;
   3181 
   3182 	// A few variables
   3183 	float fLower = 0.0f, fUpper = 0.0f;
   3184 	PVRTBOUNDINGBOX BoundingBox;
   3185 	PVRTMATRIX	mOffset, mScale;
   3186 	PVRTVECTOR4 v,o;
   3187 
   3188 	// Set the w component of o as it is needed for later
   3189 	o.w = 1.0f;
   3190 
   3191 	// Calc bounding box
   3192 	PVRTBoundingBoxComputeInterleaved(&BoundingBox, mesh.sVertex.pData,  mesh.nNumVertex, 0,  mesh.sVertex.nStride);
   3193 
   3194 	// Get new type data range that we wish to scale the data to
   3195 
   3196 	// Due to a hardware bug in early MBXs in some cases we clamp the data to the minimum possible value +1
   3197 	switch(eNewType)
   3198 	{
   3199 	case EPODDataInt:
   3200 		fUpper = 1 << 30;
   3201 		fLower = -fUpper;
   3202 	break;
   3203 	case EPODDataUnsignedInt:
   3204 		fUpper = 1 << 30;
   3205 	break;
   3206 	case EPODDataShort:
   3207 	case EPODDataFixed16_16:
   3208 		fUpper =  32767.0f;
   3209 		fLower = -fUpper;
   3210 	break;
   3211 	case EPODDataUnsignedShort:
   3212 		fUpper = 0x0ffff;
   3213 	break;
   3214 	case EPODDataRGBA:
   3215 	case EPODDataABGR:
   3216 	case EPODDataARGB:
   3217 	case EPODDataD3DCOLOR:
   3218 		fUpper = 1.0f;
   3219 	break;
   3220 	case EPODDataUBYTE4:
   3221 	case EPODDataUnsignedByte:
   3222 		fUpper = 0x0ff;
   3223 	break;
   3224 	case EPODDataShortNorm:
   3225 	case EPODDataUnsignedShortNorm:
   3226 	case EPODDataByteNorm:
   3227 	case EPODDataUnsignedByteNorm:
   3228 		fUpper =  1.0f;
   3229 		fLower = -fUpper;
   3230 	break;
   3231 	case EPODDataDEC3N:
   3232 		fUpper =  511.0f;
   3233 		fLower = -fUpper;
   3234 	break;
   3235 	case EPODDataByte:
   3236 		fUpper =  127.0f;
   3237 		fLower = -fUpper;
   3238 	break;
   3239 	default:
   3240 		_ASSERT(false);
   3241 		return PVR_FAIL; // Unsupported format specified
   3242 	}
   3243 
   3244 	PVRTVECTOR3f vScale, vOffset;
   3245 
   3246 	float fRange = fUpper - fLower;
   3247 	vScale.x = fRange / (BoundingBox.Point[7].x - BoundingBox.Point[0].x);
   3248 	vScale.y = fRange / (BoundingBox.Point[7].y - BoundingBox.Point[0].y);
   3249 	vScale.z = fRange / (BoundingBox.Point[7].z - BoundingBox.Point[0].z);
   3250 
   3251 	vOffset.x = -BoundingBox.Point[0].x;
   3252 	vOffset.y = -BoundingBox.Point[0].y;
   3253 	vOffset.z = -BoundingBox.Point[0].z;
   3254 
   3255 	PVRTMatrixTranslation(mOffset, -fLower, -fLower, -fLower);
   3256 	PVRTMatrixScaling(mScale, 1.0f / vScale.x, 1.0f / vScale.y, 1.0f / vScale.z);
   3257 	PVRTMatrixMultiply(mesh.mUnpackMatrix, mOffset, mScale);
   3258 
   3259 	PVRTMatrixTranslation(mOffset, -vOffset.x, -vOffset.y, -vOffset.z);
   3260 	PVRTMatrixMultiply(mesh.mUnpackMatrix, mesh.mUnpackMatrix, mOffset);
   3261 
   3262 	// Transform vertex data
   3263 	for(unsigned int i = 0; i < mesh.nNumVertex; ++i)
   3264 	{
   3265 		PVRTVertexRead(&v,  mesh.sVertex.pData + i *  mesh.sVertex.nStride,  mesh.sVertex.eType,  mesh.sVertex.n);
   3266 
   3267 		o.x = (v.x + vOffset.x) * vScale.x + fLower;
   3268 		o.y = (v.y + vOffset.y) * vScale.y + fLower;
   3269 		o.z = (v.z + vOffset.z) * vScale.z + fLower;
   3270 
   3271 		_ASSERT((o.x >= fLower && o.x <= fUpper) || fabs(1.0f - o.x / fLower) < 0.01f || fabs(1.0f - o.x / fUpper) < 0.01f);
   3272 		_ASSERT((o.y >= fLower && o.y <= fUpper) || fabs(1.0f - o.y / fLower) < 0.01f || fabs(1.0f - o.y / fUpper) < 0.01f);
   3273 		_ASSERT((o.z >= fLower && o.z <= fUpper) || fabs(1.0f - o.z / fLower) < 0.01f || fabs(1.0f - o.z / fUpper) < 0.01f);
   3274 
   3275 #if defined(_DEBUG)
   3276 		PVRTVECTOR4 res;
   3277 		PVRTTransform(&res, &o, &mesh.mUnpackMatrix);
   3278 
   3279 		_ASSERT(fabs(res.x - v.x) <= 0.02);
   3280 		_ASSERT(fabs(res.y - v.y) <= 0.02);
   3281 		_ASSERT(fabs(res.z - v.z) <= 0.02);
   3282 		_ASSERT(fabs(res.w - 1.0) <= 0.02);
   3283 #endif
   3284 
   3285 		PVRTVertexWrite(mesh.sVertex.pData + i * mesh.sVertex.nStride, mesh.sVertex.eType, (int) (mesh.sVertex.n * PVRTModelPODDataTypeComponentCount(mesh.sVertex.eType)), &o);
   3286 	}
   3287 
   3288 	// Convert the data to the chosen format
   3289 	PVRTModelPODDataConvert(mesh.sVertex, mesh.nNumVertex, eNewType);
   3290 
   3291 	return PVR_SUCCESS;
   3292 }
   3293 #endif
   3294 /*!***************************************************************************
   3295  @Function			PVRTModelPODDataShred
   3296  @Modified			data		Data elements to modify
   3297  @Input				nCnt		Number of elements
   3298  @Input				pChannels	A list of the wanted channels, e.g. {'x', 'y', 0}
   3299  @Description		Reduce the number of dimensions in 'data' using the requested
   3300 					channel array. The array should have a maximum length of 4
   3301 					or be null terminated if less channels are wanted. It is also
   3302 					possible to negate an element, e.g. {'x','y', -'z'}.
   3303 *****************************************************************************/
   3304 void PVRTModelPODDataShred(CPODData &data, const unsigned int nCnt, const int * pChannels)
   3305 {
   3306 	CPODData		old;
   3307 	PVRTVECTOR4f	v,o;
   3308 	float * const pv = &v.x;
   3309 	float * const po = &o.x;
   3310 	unsigned int	i, nCh;
   3311 	int  i32Map[4];
   3312 	bool bNegate[4];
   3313 
   3314 	if(!data.pData || !pChannels)
   3315 		return;
   3316 
   3317 	old = data;
   3318 
   3319 	// Count the number of output channels while setting up cMap and bNegate
   3320 	for(data.n = 0; data.n < 4 && pChannels[data.n]; ++data.n)
   3321 	{
   3322 		i32Map[data.n]	= abs(pChannels[data.n]) == 'w' ? 3 : abs(pChannels[data.n]) - 'x';
   3323 		bNegate[data.n] = pChannels[data.n] < 0;
   3324 	}
   3325 
   3326 	if(data.n > old.n)
   3327 		data.n = old.n;
   3328 
   3329 	// Allocate output memory
   3330 	data.nStride = (unsigned int)PVRTModelPODDataStride(data);
   3331 
   3332 	if(data.nStride == 0)
   3333 	{
   3334 		FREE(data.pData);
   3335 		return;
   3336 	}
   3337 
   3338 	data.pData = (unsigned char*)malloc(data.nStride * nCnt);
   3339 
   3340 	for(i = 0; i < nCnt; ++i)
   3341 	{
   3342 		// Read the vector
   3343 		PVRTVertexRead(&v, old.pData + i * old.nStride, old.eType, old.n);
   3344 
   3345 		// Shred the vector
   3346 		for(nCh = 0; nCh < 4 && pChannels[nCh]; ++nCh)
   3347 			po[nCh] = bNegate[nCh] ? -pv[i32Map[nCh]] : pv[i32Map[nCh]];
   3348 
   3349 		for(; nCh < 4; ++nCh)
   3350 			po[nCh] = 0;
   3351 
   3352 		// Write the vector
   3353 		PVRTVertexWrite((char*)data.pData + i * data.nStride, data.eType, (int) (data.n * PVRTModelPODDataTypeComponentCount(data.eType)), &o);
   3354 	}
   3355 
   3356 	FREE(old.pData);
   3357 }
   3358 
   3359 /*!***************************************************************************
   3360  @Function			PVRTModelPODReorderFaces
   3361  @Modified			mesh		The mesh to re-order the faces of
   3362  @Input				i32El1		The first index to be written out
   3363  @Input				i32El2		The second index to be written out
   3364  @Input				i32El3		The third index to be written out
   3365  @Description		Reorders the face indices of a mesh.
   3366 *****************************************************************************/
   3367 void PVRTModelPODReorderFaces(SPODMesh &mesh, const int i32El1, const int i32El2, const int i32El3)
   3368 {
   3369 	if(!mesh.sFaces.pData)
   3370 		return;
   3371 
   3372 	unsigned int ui32V[3];
   3373 
   3374 	for(unsigned int i = 0; i < mesh.nNumFaces * 3; i += 3)
   3375 	{
   3376 		unsigned char *pData = mesh.sFaces.pData + i * mesh.sFaces.nStride;
   3377 
   3378 		// Read
   3379 		PVRTVertexRead(&ui32V[0], pData, mesh.sFaces.eType);
   3380 		PVRTVertexRead(&ui32V[1], pData + mesh.sFaces.nStride, mesh.sFaces.eType);
   3381 		PVRTVertexRead(&ui32V[2], pData + 2 * mesh.sFaces.nStride, mesh.sFaces.eType);
   3382 
   3383 		// Write in place the new order
   3384 		PVRTVertexWrite(pData, mesh.sFaces.eType, ui32V[i32El1]);
   3385 		PVRTVertexWrite(pData + mesh.sFaces.nStride, mesh.sFaces.eType, ui32V[i32El2]);
   3386 		PVRTVertexWrite(pData + 2 * mesh.sFaces.nStride, mesh.sFaces.eType, ui32V[i32El3]);
   3387 	}
   3388 }
   3389 
   3390 /*!***************************************************************************
   3391  @Function			InterleaveArray
   3392  @Modified			pInterleaved
   3393  @Modified			data
   3394  @Input				nNumVertex
   3395  @Input				nStride
   3396  @Input				nPadding
   3397  @Input				nOffset
   3398  @Description		Interleaves the pod data
   3399 *****************************************************************************/
   3400 static void InterleaveArray(
   3401 	char			* const pInterleaved,
   3402 	CPODData		&data,
   3403 	const PVRTuint32 nNumVertex,
   3404 	const PVRTuint32 nStride,
   3405 	const PVRTuint32 nPadding,
   3406 	PVRTuint32		&nOffset)
   3407 {
   3408 	if(!data.nStride)
   3409 		return;
   3410 
   3411 	for(PVRTuint32 i = 0; i < nNumVertex; ++i)
   3412 		memcpy(pInterleaved + i * nStride + nOffset, (char*)data.pData + i * data.nStride, data.nStride);
   3413 
   3414 	FREE(data.pData);
   3415 	data.pData		= (unsigned char*)nOffset;
   3416 	data.nStride	= nStride;
   3417 	nOffset			+= PVRTModelPODDataStride(data) + nPadding;
   3418 }
   3419 
   3420 /*!***************************************************************************
   3421  @Function			DeinterleaveArray
   3422  @Input				data
   3423  @Input				pInter
   3424  @Input				nNumVertex
   3425  @Description		DeInterleaves the pod data
   3426 *****************************************************************************/
   3427 static void DeinterleaveArray(
   3428 	CPODData			&data,
   3429 	const void			* const pInter,
   3430 	const PVRTuint32	nNumVertex,
   3431 	const PVRTuint32	nAlignToNBytes)
   3432 {
   3433 	const PVRTuint32 nSrcStride	= data.nStride;
   3434 	const PVRTuint32 nDestStride= PVRTModelPODDataStride(data);
   3435 	const PVRTuint32 nAlignedStride = nDestStride + ((nAlignToNBytes - nDestStride % nAlignToNBytes) % nAlignToNBytes);
   3436 	const char		*pSrc		= (char*)pInter + (size_t)data.pData;
   3437 
   3438 	if(!nSrcStride)
   3439 		return;
   3440 
   3441 	data.pData = 0;
   3442 	SafeAlloc(data.pData, nAlignedStride * nNumVertex);
   3443 	data.nStride = nAlignedStride;
   3444 
   3445 	for(PVRTuint32 i = 0; i < nNumVertex; ++i)
   3446 		memcpy((char*)data.pData + i * nAlignedStride, pSrc + i * nSrcStride, nDestStride);
   3447 }
   3448 
   3449 /*!***************************************************************************
   3450  @Function		PVRTModelPODToggleInterleaved
   3451  @Modified		mesh		Mesh to modify
   3452  @Input			ui32AlignToNBytes Align the interleaved data to this no. of bytes.
   3453  @Description	Switches the supplied mesh to or from interleaved data format.
   3454 *****************************************************************************/
   3455 void PVRTModelPODToggleInterleaved(SPODMesh &mesh, const PVRTuint32 ui32AlignToNBytes)
   3456 {
   3457 	unsigned int i;
   3458 
   3459 	if(!mesh.nNumVertex)
   3460 		return;
   3461 
   3462 	if(mesh.pInterleaved)
   3463 	{
   3464 		/*
   3465 			De-interleave
   3466 		*/
   3467 		DeinterleaveArray(mesh.sVertex, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3468 		DeinterleaveArray(mesh.sNormals, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3469 		DeinterleaveArray(mesh.sTangents, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3470 		DeinterleaveArray(mesh.sBinormals, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3471 
   3472 		for(i = 0; i < mesh.nNumUVW; ++i)
   3473 			DeinterleaveArray(mesh.psUVW[i], mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3474 
   3475 		DeinterleaveArray(mesh.sVtxColours, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3476 		DeinterleaveArray(mesh.sBoneIdx, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3477 		DeinterleaveArray(mesh.sBoneWeight, mesh.pInterleaved, mesh.nNumVertex, ui32AlignToNBytes);
   3478 		FREE(mesh.pInterleaved);
   3479 	}
   3480 	else
   3481 	{
   3482 		PVRTuint32 nStride, nOffset, nBytes;
   3483 
   3484 #define NEEDED_PADDING(x) ((x && ui32AlignToNBytes) ? (ui32AlignToNBytes - x % ui32AlignToNBytes) % ui32AlignToNBytes : 0)
   3485 
   3486 		// Interleave
   3487 
   3488 		PVRTuint32 nVertexStride, nNormalStride, nTangentStride, nBinormalStride, nVtxColourStride, nBoneIdxStride, nBoneWeightStride;
   3489 		PVRTuint32 nUVWStride[8];
   3490 		PVRTuint32 nVertexPadding, nNormalPadding, nTangentPadding, nBinormalPadding, nVtxColourPadding, nBoneIdxPadding, nBoneWeightPadding;
   3491 		PVRTuint32 nUVWPadding[8];
   3492 
   3493 		_ASSERT(mesh.nNumUVW < 8);
   3494 
   3495 		nStride  = nVertexStride = PVRTModelPODDataStride(mesh.sVertex);
   3496 		nStride += nVertexPadding = NEEDED_PADDING(nVertexStride);
   3497 
   3498 		nStride += nNormalStride = PVRTModelPODDataStride(mesh.sNormals);
   3499 		nStride += nNormalPadding = NEEDED_PADDING(nNormalStride);
   3500 
   3501 		nStride += nTangentStride = PVRTModelPODDataStride(mesh.sTangents);
   3502 		nStride += nTangentPadding = NEEDED_PADDING(nTangentStride);
   3503 
   3504 		nStride += nBinormalStride = PVRTModelPODDataStride(mesh.sBinormals);
   3505 		nStride += nBinormalPadding = NEEDED_PADDING(nBinormalStride);
   3506 
   3507 		for(i = 0; i < mesh.nNumUVW; ++i)
   3508 		{
   3509 			nStride += nUVWStride[i] = PVRTModelPODDataStride(mesh.psUVW[i]);
   3510 			nStride += nUVWPadding[i] = NEEDED_PADDING(nUVWStride[i]);
   3511 		}
   3512 
   3513 		nStride += nVtxColourStride = PVRTModelPODDataStride(mesh.sVtxColours);
   3514 		nStride += nVtxColourPadding = NEEDED_PADDING(nVtxColourStride);
   3515 
   3516 		nStride += nBoneIdxStride = PVRTModelPODDataStride(mesh.sBoneIdx);
   3517 		nStride += nBoneIdxPadding = NEEDED_PADDING(nBoneIdxStride);
   3518 
   3519 		nStride += nBoneWeightStride = PVRTModelPODDataStride(mesh.sBoneWeight);
   3520 		nStride += nBoneWeightPadding = NEEDED_PADDING(nBoneWeightStride);
   3521 
   3522 #undef NEEDED_PADDING
   3523 		// Allocate interleaved array
   3524 		SafeAlloc(mesh.pInterleaved, mesh.nNumVertex * nStride);
   3525 
   3526 		// Interleave the data
   3527 		nOffset = 0;
   3528 
   3529 		for(nBytes = 4; nBytes > 0; nBytes >>= 1)
   3530 		{
   3531 			if(PVRTModelPODDataTypeSize(mesh.sVertex.eType) == nBytes)
   3532 				InterleaveArray((char*)mesh.pInterleaved, mesh.sVertex, mesh.nNumVertex, nStride, nVertexPadding, nOffset);
   3533 
   3534 			if(PVRTModelPODDataTypeSize(mesh.sNormals.eType) == nBytes)
   3535 				InterleaveArray((char*)mesh.pInterleaved, mesh.sNormals, mesh.nNumVertex, nStride, nNormalPadding, nOffset);
   3536 
   3537 			if(PVRTModelPODDataTypeSize(mesh.sTangents.eType) == nBytes)
   3538 				InterleaveArray((char*)mesh.pInterleaved, mesh.sTangents, mesh.nNumVertex, nStride, nTangentPadding, nOffset);
   3539 
   3540 			if(PVRTModelPODDataTypeSize(mesh.sBinormals.eType) == nBytes)
   3541 				InterleaveArray((char*)mesh.pInterleaved, mesh.sBinormals, mesh.nNumVertex, nStride, nBinormalPadding, nOffset);
   3542 
   3543 			if(PVRTModelPODDataTypeSize(mesh.sVtxColours.eType) == nBytes)
   3544 				InterleaveArray((char*)mesh.pInterleaved, mesh.sVtxColours, mesh.nNumVertex, nStride, nVtxColourPadding, nOffset);
   3545 
   3546 			for(i = 0; i < mesh.nNumUVW; ++i)
   3547 			{
   3548 				if(PVRTModelPODDataTypeSize(mesh.psUVW[i].eType) == nBytes)
   3549 					InterleaveArray((char*)mesh.pInterleaved, mesh.psUVW[i], mesh.nNumVertex, nStride, nUVWPadding[i], nOffset);
   3550 			}
   3551 
   3552 			if(PVRTModelPODDataTypeSize(mesh.sBoneIdx.eType) == nBytes)
   3553 				InterleaveArray((char*)mesh.pInterleaved, mesh.sBoneIdx, mesh.nNumVertex, nStride, nBoneIdxPadding, nOffset);
   3554 
   3555 			if(PVRTModelPODDataTypeSize(mesh.sBoneWeight.eType) == nBytes)
   3556 				InterleaveArray((char*)mesh.pInterleaved, mesh.sBoneWeight, mesh.nNumVertex, nStride, nBoneWeightPadding, nOffset);
   3557 		}
   3558 	}
   3559 }
   3560 
   3561 /*!***************************************************************************
   3562  @Function			PVRTModelPODDeIndex
   3563  @Modified			mesh		Mesh to modify
   3564  @Description		De-indexes the supplied mesh. The mesh must be
   3565 					Interleaved before calling this function.
   3566 *****************************************************************************/
   3567 void PVRTModelPODDeIndex(SPODMesh &mesh)
   3568 {
   3569 	unsigned char *pNew = 0;
   3570 
   3571 	if(!mesh.pInterleaved || !mesh.nNumVertex)
   3572 		return;
   3573 
   3574 	_ASSERT(mesh.nNumVertex && mesh.nNumFaces);
   3575 
   3576 	// Create a new vertex list
   3577 	mesh.nNumVertex = PVRTModelPODCountIndices(mesh);
   3578 	SafeAlloc(pNew, mesh.sVertex.nStride * mesh.nNumVertex);
   3579 
   3580 	// Deindex the vertices
   3581 	if(mesh.sFaces.eType == EPODDataUnsignedShort)
   3582 	{
   3583 		for(unsigned int i = 0; i < mesh.nNumVertex; ++i)
   3584 			memcpy(pNew + i * mesh.sVertex.nStride, (char*)mesh.pInterleaved + ((unsigned short*)mesh.sFaces.pData)[i] * mesh.sVertex.nStride, mesh.sVertex.nStride);
   3585 	}
   3586 	else
   3587 	{
   3588 		_ASSERT(mesh.sFaces.eType == EPODDataUnsignedInt);
   3589 
   3590 		for(unsigned int i = 0; i < mesh.nNumVertex; ++i)
   3591 			memcpy(pNew + i * mesh.sVertex.nStride, (char*)mesh.pInterleaved + ((unsigned int*)mesh.sFaces.pData)[i] * mesh.sVertex.nStride, mesh.sVertex.nStride);
   3592 	}
   3593 
   3594 	// Replace the old vertex list
   3595 	FREE(mesh.pInterleaved);
   3596 	mesh.pInterleaved = pNew;
   3597 
   3598 	// Get rid of the index list
   3599 	FREE(mesh.sFaces.pData);
   3600 	mesh.sFaces.n		= 0;
   3601 	mesh.sFaces.nStride	= 0;
   3602 }
   3603 
   3604 /*!***************************************************************************
   3605  @Function			PVRTModelPODToggleStrips
   3606  @Modified			mesh		Mesh to modify
   3607  @Description		Converts the supplied mesh to or from strips.
   3608 *****************************************************************************/
   3609 void PVRTModelPODToggleStrips(SPODMesh &mesh)
   3610 {
   3611 	CPODData	old;
   3612 	size_t	nIdxSize, nTriStride;
   3613 
   3614 	if(!mesh.nNumFaces)
   3615 		return;
   3616 
   3617 	_ASSERT(mesh.sFaces.n == 1);
   3618 	nIdxSize	= PVRTModelPODDataTypeSize(mesh.sFaces.eType);
   3619 	nTriStride	= PVRTModelPODDataStride(mesh.sFaces) * 3;
   3620 
   3621 	old					= mesh.sFaces;
   3622 	mesh.sFaces.pData	= 0;
   3623 	SafeAlloc(mesh.sFaces.pData, nTriStride * mesh.nNumFaces);
   3624 
   3625 	if(mesh.nNumStrips)
   3626 	{
   3627 		unsigned int nListIdxCnt, nStripIdxCnt;
   3628 
   3629 		//	Convert to list
   3630 		nListIdxCnt		= 0;
   3631 		nStripIdxCnt	= 0;
   3632 
   3633 		for(unsigned int i = 0; i < mesh.nNumStrips; ++i)
   3634 		{
   3635 			for(unsigned int j = 0; j < mesh.pnStripLength[i]; ++j)
   3636 			{
   3637 				if(j)
   3638 				{
   3639 					_ASSERT(j == 1); // Because this will surely break with any other number
   3640 
   3641 					memcpy(
   3642 						(char*)mesh.sFaces.pData	+ nIdxSize * nListIdxCnt,
   3643 						(char*)old.pData			+ nIdxSize * (nStripIdxCnt - 1),
   3644 						nIdxSize);
   3645 					nListIdxCnt += 1;
   3646 
   3647 					memcpy(
   3648 						(char*)mesh.sFaces.pData	+ nIdxSize * nListIdxCnt,
   3649 						(char*)old.pData			+ nIdxSize * (nStripIdxCnt - 2),
   3650 						nIdxSize);
   3651 					nListIdxCnt += 1;
   3652 
   3653 					memcpy(
   3654 						(char*)mesh.sFaces.pData	+ nIdxSize * nListIdxCnt,
   3655 						(char*)old.pData			+ nIdxSize * nStripIdxCnt,
   3656 						nIdxSize);
   3657 					nListIdxCnt += 1;
   3658 
   3659 					nStripIdxCnt += 1;
   3660 				}
   3661 				else
   3662 				{
   3663 					memcpy(
   3664 						(char*)mesh.sFaces.pData	+ nIdxSize * nListIdxCnt,
   3665 						(char*)old.pData			+ nIdxSize * nStripIdxCnt,
   3666 						nTriStride);
   3667 
   3668 					nStripIdxCnt += 3;
   3669 					nListIdxCnt += 3;
   3670 				}
   3671 			}
   3672 		}
   3673 
   3674 		_ASSERT(nListIdxCnt == mesh.nNumFaces*3);
   3675 		FREE(mesh.pnStripLength);
   3676 		mesh.nNumStrips = 0;
   3677 	}
   3678 	else
   3679 	{
   3680 		int		nIdxCnt;
   3681 		int		nBatchCnt;
   3682 		unsigned int n0, n1, n2;
   3683 		unsigned int p0, p1, p2, nFaces;
   3684 		unsigned char* pFaces;
   3685 
   3686 		//	Convert to strips
   3687 		mesh.pnStripLength	= (unsigned int*)calloc(mesh.nNumFaces, sizeof(*mesh.pnStripLength));
   3688 		mesh.nNumStrips		= 0;
   3689 		nIdxCnt				= 0;
   3690 		nBatchCnt			= mesh.sBoneBatches.nBatchCnt ? mesh.sBoneBatches.nBatchCnt : 1;
   3691 
   3692 		for(int h = 0; h < nBatchCnt; ++h)
   3693 		{
   3694 			n0 = 0;
   3695 			n1 = 0;
   3696 			n2 = 0;
   3697 
   3698 			if(!mesh.sBoneBatches.nBatchCnt)
   3699 			{
   3700 				nFaces = mesh.nNumFaces;
   3701 				pFaces = old.pData;
   3702 			}
   3703 			else
   3704 			{
   3705 				if(h + 1 < mesh.sBoneBatches.nBatchCnt)
   3706 					nFaces = mesh.sBoneBatches.pnBatchOffset[h+1] - mesh.sBoneBatches.pnBatchOffset[h];
   3707 				else
   3708 					nFaces = mesh.nNumFaces - mesh.sBoneBatches.pnBatchOffset[h];
   3709 
   3710 				pFaces = &old.pData[3 * mesh.sBoneBatches.pnBatchOffset[h] * old.nStride];
   3711 			}
   3712 
   3713 			for(unsigned int i = 0; i < nFaces; ++i)
   3714 			{
   3715 				p0 = n0;
   3716 				p1 = n1;
   3717 				p2 = n2;
   3718 
   3719 				PVRTVertexRead(&n0, (char*)pFaces + (3 * i + 0) * old.nStride, old.eType);
   3720 				PVRTVertexRead(&n1, (char*)pFaces + (3 * i + 1) * old.nStride, old.eType);
   3721 				PVRTVertexRead(&n2, (char*)pFaces + (3 * i + 2) * old.nStride, old.eType);
   3722 
   3723 				if(mesh.pnStripLength[mesh.nNumStrips])
   3724 				{
   3725 					if(mesh.pnStripLength[mesh.nNumStrips] & 0x01)
   3726 					{
   3727 						if(p1 == n1 && p2 == n0)
   3728 						{
   3729 							PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n2);
   3730 							++nIdxCnt;
   3731 							mesh.pnStripLength[mesh.nNumStrips] += 1;
   3732 							continue;
   3733 						}
   3734 					}
   3735 					else
   3736 					{
   3737 						if(p2 == n1 && p0 == n0)
   3738 						{
   3739 							PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n2);
   3740 							++nIdxCnt;
   3741 							mesh.pnStripLength[mesh.nNumStrips] += 1;
   3742 							continue;
   3743 						}
   3744 					}
   3745 
   3746 					++mesh.nNumStrips;
   3747 				}
   3748 
   3749 				//	Start of strip, copy entire triangle
   3750 				PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n0);
   3751 				++nIdxCnt;
   3752 				PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n1);
   3753 				++nIdxCnt;
   3754 				PVRTVertexWrite((char*)mesh.sFaces.pData + nIdxCnt * mesh.sFaces.nStride, mesh.sFaces.eType, n2);
   3755 				++nIdxCnt;
   3756 
   3757 				mesh.pnStripLength[mesh.nNumStrips] += 1;
   3758 			}
   3759 		}
   3760 
   3761 		if(mesh.pnStripLength[mesh.nNumStrips])
   3762 			++mesh.nNumStrips;
   3763 
   3764 		SafeRealloc(mesh.sFaces.pData, nIdxCnt * nIdxSize);
   3765 		mesh.pnStripLength	= (unsigned int*)realloc(mesh.pnStripLength, sizeof(*mesh.pnStripLength) * mesh.nNumStrips);
   3766 	}
   3767 
   3768 	FREE(old.pData);
   3769 }
   3770 
   3771 /*!***************************************************************************
   3772  @Function		PVRTModelPODCountIndices
   3773  @Input			mesh		Mesh
   3774  @Return		Number of indices used by mesh
   3775  @Description	Counts the number of indices of a mesh
   3776 *****************************************************************************/
   3777 unsigned int PVRTModelPODCountIndices(const SPODMesh &mesh)
   3778 {
   3779 	return mesh.nNumStrips ? mesh.nNumFaces + (mesh.nNumStrips * 2) : mesh.nNumFaces * 3;
   3780 }
   3781 
   3782 /*!***************************************************************************
   3783  @Function			PVRTModelPODCopyCPODData
   3784  @Input				in
   3785  @Output			out
   3786  @Input				ui32No
   3787  @Input				bInterleaved
   3788  @Description		Used to copy a CPODData of a mesh
   3789 *****************************************************************************/
   3790 void PVRTModelPODCopyCPODData(const CPODData &in, CPODData &out, unsigned int ui32No, bool bInterleaved)
   3791 {
   3792 	FREE(out.pData);
   3793 
   3794 	out.eType	= in.eType;
   3795 	out.n		= in.n;
   3796 	out.nStride = in.nStride;
   3797 
   3798 	if(bInterleaved)
   3799 	{
   3800 		out.pData = in.pData;
   3801 	}
   3802 	else if(in.pData)
   3803 	{
   3804 		size_t ui32Size = PVRTModelPODDataStride(out) * ui32No;
   3805 
   3806 		if(SafeAlloc(out.pData, ui32Size))
   3807 			memcpy(out.pData, in.pData, ui32Size);
   3808 	}
   3809 }
   3810 
   3811 /*!***************************************************************************
   3812  @Function			PVRTModelPODCopyNode
   3813  @Input				in
   3814  @Output			out
   3815  @Input				nNumFrames
   3816  @Description		Used to copy a pod node
   3817 *****************************************************************************/
   3818 void PVRTModelPODCopyNode(const SPODNode &in, SPODNode &out, int nNumFrames)
   3819 {
   3820 	out.nIdx = in.nIdx;
   3821 	out.nIdxMaterial = in.nIdxMaterial;
   3822 	out.nIdxParent = in.nIdxParent;
   3823 	out.nAnimFlags = in.nAnimFlags;
   3824 	out.pUserData = 0;
   3825 	out.nUserDataSize = 0;
   3826 
   3827 	if(in.pszName && SafeAlloc(out.pszName, strlen(in.pszName) + 1))
   3828 		memcpy(out.pszName, in.pszName, strlen(in.pszName) + 1);
   3829 
   3830 	int i32Size;
   3831 
   3832 	// Position
   3833 	i32Size = in.nAnimFlags & ePODHasPositionAni ? PVRTModelPODGetAnimArraySize(in.pnAnimPositionIdx, nNumFrames, 3) : 3;
   3834 
   3835 	if(in.pnAnimPositionIdx && SafeAlloc(out.pnAnimPositionIdx, nNumFrames))
   3836 		memcpy(out.pnAnimPositionIdx, in.pnAnimPositionIdx, sizeof(*out.pnAnimPositionIdx) * nNumFrames);
   3837 
   3838 	if(in.pfAnimPosition && SafeAlloc(out.pfAnimPosition, i32Size))
   3839 		memcpy(out.pfAnimPosition, in.pfAnimPosition, sizeof(*out.pfAnimPosition) * i32Size);
   3840 
   3841 	// Rotation
   3842 	i32Size = in.nAnimFlags & ePODHasRotationAni ? PVRTModelPODGetAnimArraySize(in.pnAnimRotationIdx, nNumFrames, 4) : 4;
   3843 
   3844 	if(in.pnAnimRotationIdx && SafeAlloc(out.pnAnimRotationIdx, nNumFrames))
   3845 		memcpy(out.pnAnimRotationIdx, in.pnAnimRotationIdx, sizeof(*out.pnAnimRotationIdx) * nNumFrames);
   3846 
   3847 	if(in.pfAnimRotation && SafeAlloc(out.pfAnimRotation, i32Size))
   3848 		memcpy(out.pfAnimRotation, in.pfAnimRotation, sizeof(*out.pfAnimRotation) * i32Size);
   3849 
   3850 	// Scale
   3851 	i32Size = in.nAnimFlags & ePODHasScaleAni ? PVRTModelPODGetAnimArraySize(in.pnAnimScaleIdx, nNumFrames, 7) : 7;
   3852 
   3853 	if(in.pnAnimScaleIdx && SafeAlloc(out.pnAnimScaleIdx, nNumFrames))
   3854 		memcpy(out.pnAnimScaleIdx, in.pnAnimScaleIdx, sizeof(*out.pnAnimScaleIdx) * nNumFrames);
   3855 
   3856 	if(in.pfAnimScale && SafeAlloc(out.pfAnimScale, i32Size))
   3857 		memcpy(out.pfAnimScale, in.pfAnimScale, sizeof(*out.pfAnimScale) * i32Size);
   3858 
   3859 	// Matrix
   3860 	i32Size = in.nAnimFlags & ePODHasMatrixAni ? PVRTModelPODGetAnimArraySize(in.pnAnimMatrixIdx, nNumFrames, 16) : 16;
   3861 
   3862 	if(in.pnAnimMatrixIdx && SafeAlloc(out.pnAnimMatrixIdx, nNumFrames))
   3863 		memcpy(out.pnAnimMatrixIdx, in.pnAnimMatrixIdx, sizeof(*out.pnAnimMatrixIdx) * nNumFrames);
   3864 
   3865 	if(in.pfAnimMatrix && SafeAlloc(out.pfAnimMatrix, i32Size))
   3866 		memcpy(out.pfAnimMatrix, in.pfAnimMatrix, sizeof(*out.pfAnimMatrix) * i32Size);
   3867 
   3868 	if(in.pUserData && SafeAlloc(out.pUserData, in.nUserDataSize))
   3869 	{
   3870 		memcpy(out.pUserData, in.pUserData, in.nUserDataSize);
   3871 		out.nUserDataSize = in.nUserDataSize;
   3872 	}
   3873 }
   3874 
   3875 /*!***************************************************************************
   3876  @Function			PVRTModelPODCopyMesh
   3877  @Input				in
   3878  @Output			out
   3879  @Description		Used to copy a pod mesh
   3880 *****************************************************************************/
   3881 void PVRTModelPODCopyMesh(const SPODMesh &in, SPODMesh &out)
   3882 {
   3883 	unsigned int i;
   3884 	bool bInterleaved = in.pInterleaved != 0;
   3885 	out.nNumVertex = in.nNumVertex;
   3886 	out.nNumFaces  = in.nNumFaces;
   3887 
   3888 	// Face data
   3889 	PVRTModelPODCopyCPODData(in.sFaces	 , out.sFaces	 , out.nNumFaces * 3, false);
   3890 
   3891 	// Vertex data
   3892 	PVRTModelPODCopyCPODData(in.sVertex	 , out.sVertex	 , out.nNumVertex, bInterleaved);
   3893 	PVRTModelPODCopyCPODData(in.sNormals	 , out.sNormals	 , out.nNumVertex, bInterleaved);
   3894 	PVRTModelPODCopyCPODData(in.sTangents	 , out.sTangents	 , out.nNumVertex, bInterleaved);
   3895 	PVRTModelPODCopyCPODData(in.sBinormals , out.sBinormals , out.nNumVertex, bInterleaved);
   3896 	PVRTModelPODCopyCPODData(in.sVtxColours, out.sVtxColours, out.nNumVertex, bInterleaved);
   3897 	PVRTModelPODCopyCPODData(in.sBoneIdx	 , out.sBoneIdx	 , out.nNumVertex, bInterleaved);
   3898 	PVRTModelPODCopyCPODData(in.sBoneWeight, out.sBoneWeight, out.nNumVertex, bInterleaved);
   3899 
   3900 	if(in.nNumUVW && SafeAlloc(out.psUVW, in.nNumUVW))
   3901 	{
   3902 		out.nNumUVW = in.nNumUVW;
   3903 
   3904 		for(i = 0; i < out.nNumUVW; ++i)
   3905 		{
   3906 			PVRTModelPODCopyCPODData(in.psUVW[i], out.psUVW[i], out.nNumVertex, bInterleaved);
   3907 		}
   3908 	}
   3909 
   3910 	// Allocate and copy interleaved array
   3911 	if(bInterleaved && SafeAlloc(out.pInterleaved, out.nNumVertex * in.sVertex.nStride))
   3912 		memcpy(out.pInterleaved, in.pInterleaved, out.nNumVertex * in.sVertex.nStride);
   3913 
   3914 	if(in.pnStripLength && SafeAlloc(out.pnStripLength, out.nNumFaces))
   3915 	{
   3916 		memcpy(out.pnStripLength, in.pnStripLength, sizeof(*out.pnStripLength) * out.nNumFaces);
   3917 		out.nNumStrips = in.nNumStrips;
   3918 	}
   3919 
   3920 	if(in.sBoneBatches.nBatchCnt)
   3921 	{
   3922 		out.sBoneBatches.Release();
   3923 
   3924 		out.sBoneBatches.nBatchBoneMax = in.sBoneBatches.nBatchBoneMax;
   3925 		out.sBoneBatches.nBatchCnt     = in.sBoneBatches.nBatchCnt;
   3926 
   3927 		if(in.sBoneBatches.pnBatches)
   3928 		{
   3929 			out.sBoneBatches.pnBatches = (int*) malloc(out.sBoneBatches.nBatchCnt * out.sBoneBatches.nBatchBoneMax * sizeof(*out.sBoneBatches.pnBatches));
   3930 
   3931 			if(out.sBoneBatches.pnBatches)
   3932 				memcpy(out.sBoneBatches.pnBatches, in.sBoneBatches.pnBatches, out.sBoneBatches.nBatchCnt * out.sBoneBatches.nBatchBoneMax * sizeof(*out.sBoneBatches.pnBatches));
   3933 		}
   3934 
   3935 		if(in.sBoneBatches.pnBatchBoneCnt)
   3936 		{
   3937 			out.sBoneBatches.pnBatchBoneCnt = (int*) malloc(out.sBoneBatches.nBatchCnt * sizeof(*out.sBoneBatches.pnBatchBoneCnt));
   3938 
   3939 			if(out.sBoneBatches.pnBatchBoneCnt)
   3940 				memcpy(out.sBoneBatches.pnBatchBoneCnt, in.sBoneBatches.pnBatchBoneCnt, out.sBoneBatches.nBatchCnt * sizeof(*out.sBoneBatches.pnBatchBoneCnt));
   3941 		}
   3942 
   3943 		if(in.sBoneBatches.pnBatchOffset)
   3944 		{
   3945 			out.sBoneBatches.pnBatchOffset = (int*) malloc(out.sBoneBatches.nBatchCnt * sizeof(out.sBoneBatches.pnBatchOffset));
   3946 
   3947 			if(out.sBoneBatches.pnBatchOffset)
   3948 				memcpy(out.sBoneBatches.pnBatchOffset, in.sBoneBatches.pnBatchOffset, out.sBoneBatches.nBatchCnt * sizeof(*out.sBoneBatches.pnBatchOffset));
   3949 		}
   3950 	}
   3951 
   3952 	memcpy(out.mUnpackMatrix.f, in.mUnpackMatrix.f, sizeof(in.mUnpackMatrix.f[0]) * 16);
   3953 
   3954 	out.ePrimitiveType = in.ePrimitiveType;
   3955 }
   3956 
   3957 /*!***************************************************************************
   3958  @Function			PVRTModelPODCopyTexture
   3959  @Input				in
   3960  @Output			out
   3961  @Description		Used to copy a pod texture
   3962 *****************************************************************************/
   3963 void PVRTModelPODCopyTexture(const SPODTexture &in, SPODTexture &out)
   3964 {
   3965 	if(in.pszName && SafeAlloc(out.pszName, strlen(in.pszName) + 1))
   3966 		memcpy(out.pszName, in.pszName, strlen(in.pszName) + 1);
   3967 }
   3968 
   3969 /*!***************************************************************************
   3970  @Function			PVRTModelPODCopyMaterial
   3971  @Input				in
   3972  @Output			out
   3973  @Description		Used to copy a pod material
   3974 *****************************************************************************/
   3975 void PVRTModelPODCopyMaterial(const SPODMaterial &in, SPODMaterial &out)
   3976 {
   3977 	memcpy(&out, &in, sizeof(SPODMaterial));
   3978 
   3979 	out.pszName = 0;
   3980 	out.pszEffectFile = 0;
   3981 	out.pszEffectName = 0;
   3982 	out.pUserData = 0;
   3983 	out.nUserDataSize = 0;
   3984 
   3985 	if(in.pszName && SafeAlloc(out.pszName, strlen(in.pszName) + 1))
   3986 		memcpy(out.pszName, in.pszName, strlen(in.pszName) + 1);
   3987 
   3988 	if(in.pszEffectFile && SafeAlloc(out.pszEffectFile, strlen(in.pszEffectFile) + 1))
   3989 		memcpy(out.pszEffectFile, in.pszEffectFile, strlen(in.pszEffectFile) + 1);
   3990 
   3991 	if(in.pszEffectName && SafeAlloc(out.pszEffectName, strlen(in.pszEffectName) + 1))
   3992 		memcpy(out.pszEffectName, in.pszEffectName, strlen(in.pszEffectName) + 1);
   3993 
   3994 	if(in.pUserData && SafeAlloc(out.pUserData, in.nUserDataSize))
   3995 	{
   3996 		memcpy(out.pUserData, in.pUserData, in.nUserDataSize);
   3997 		out.nUserDataSize = in.nUserDataSize;
   3998 	}
   3999 }
   4000 
   4001 /*!***************************************************************************
   4002  @Function			PVRTModelPODCopyCamera
   4003  @Input				in
   4004  @Output			out
   4005  @Input				nNumFrames The number of animation frames
   4006  @Description		Used to copy a pod camera
   4007 *****************************************************************************/
   4008 void PVRTModelPODCopyCamera(const SPODCamera &in, SPODCamera &out, int nNumFrames)
   4009 {
   4010 	memcpy(&out, &in, sizeof(SPODCamera));
   4011 
   4012 	out.pfAnimFOV = 0;
   4013 
   4014 	if(in.pfAnimFOV && SafeAlloc(out.pfAnimFOV, nNumFrames))
   4015 		memcpy(out.pfAnimFOV, in.pfAnimFOV, sizeof(*out.pfAnimFOV) * nNumFrames);
   4016 }
   4017 
   4018 /*!***************************************************************************
   4019  @Function			PVRTModelPODCopyLight
   4020  @Input				in
   4021  @Output			out
   4022  @Description		Used to copy a pod light
   4023 *****************************************************************************/
   4024 void PVRTModelPODCopyLight(const SPODLight &in, SPODLight &out)
   4025 {
   4026 	memcpy(&out, &in, sizeof(SPODLight));
   4027 }
   4028 
   4029 /*!***************************************************************************
   4030  @Function			TransformCPODData
   4031  @Input				in
   4032  @Output			out
   4033  @Input				idx Value to transform
   4034  @Input				pPalette Palette of matrices to transform with
   4035  @Input				pBoneIdx Array of indices into pPalette
   4036  @Input				pBoneWeight Array of weights to weight the influence of the matrices of pPalette with
   4037  @Input				i32BoneCnt Size of pBoneIdx and pBoneWeight
   4038  @Description		Used to transform a particular value in a CPODData
   4039 *****************************************************************************/
   4040 inline void TransformCPODData(CPODData &in, CPODData &out, int idx, PVRTMATRIX *pPalette, float *pBoneIdx, float *pBoneW, int i32BoneCnt, bool bNormalise)
   4041 {
   4042 	PVRTVECTOR4f fResult, fOrig, fTmp;
   4043 
   4044 	if(in.n)
   4045 	{
   4046 
   4047 		PVRTVertexRead(&fOrig, in.pData + (idx * in.nStride), in.eType, in.n);
   4048 
   4049 		memset(&fResult.x, 0, sizeof(fResult));
   4050 
   4051 		if(i32BoneCnt)
   4052 		{
   4053 			for(int i = 0; i < i32BoneCnt; ++i)
   4054 			{
   4055 				int i32BoneIdx = (int) pBoneIdx[i];
   4056 				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;
   4057 				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;
   4058 				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;
   4059 				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;
   4060 
   4061 				fResult.x += fTmp.x * pBoneW[i];
   4062 				fResult.y += fTmp.y * pBoneW[i];
   4063 				fResult.z += fTmp.z * pBoneW[i];
   4064 				fResult.w += fTmp.w * pBoneW[i];
   4065 			}
   4066 		}
   4067 		else
   4068 		{
   4069 			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;
   4070 			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;
   4071 			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;
   4072 			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;
   4073 		}
   4074 
   4075 		if(bNormalise)
   4076 		{
   4077 			double temp = (double)(fResult.x * fResult.x + fResult.y * fResult.y + fResult.z * fResult.z);
   4078 			temp = 1.0 / sqrt(temp);
   4079 			float f = (float)temp;
   4080 
   4081 			fResult.x = fResult.x * f;
   4082 			fResult.y = fResult.y * f;
   4083 			fResult.z = fResult.z * f;
   4084 		}
   4085 
   4086 		PVRTVertexWrite(out.pData + (idx * out.nStride), out.eType, in.n, &fResult);
   4087 	}
   4088 }
   4089 /*!***************************************************************************
   4090  @Function			PVRTModelPODFlattenToWorldSpace
   4091  @Input				in - Source scene. All meshes must not be interleaved.
   4092  @Output			out
   4093  @Description		Used to flatten a pod scene to world space. All animation
   4094 					and skinning information will be removed. The returned
   4095 					position, normal, binormals and tangent data if present
   4096 					will be returned as floats regardless of the input data
   4097 					type.
   4098 *****************************************************************************/
   4099 EPVRTError PVRTModelPODFlattenToWorldSpace(CPVRTModelPOD &in, CPVRTModelPOD &out)
   4100 {
   4101 	unsigned int i, j, k, l;
   4102 	PVRTMATRIX mWorld;
   4103 
   4104 	// Destroy the out pod scene to make sure it is clean
   4105 	out.Destroy();
   4106 
   4107 	// Init mesh and node arrays
   4108 	SafeAlloc(out.pNode, in.nNumNode);
   4109 	SafeAlloc(out.pMesh, in.nNumMeshNode);
   4110 
   4111 	out.nNumNode = in.nNumNode;
   4112 	out.nNumMesh = out.nNumMeshNode = in.nNumMeshNode;
   4113 
   4114 	// Init scene values
   4115 	out.nNumFrame = 0;
   4116 	out.nFlags = in.nFlags;
   4117 	out.fUnits = in.fUnits;
   4118 
   4119 	for(i = 0; i < 3; ++i)
   4120 	{
   4121 		out.pfColourBackground[i] = in.pfColourBackground[i];
   4122 		out.pfColourAmbient[i]	  = in.pfColourAmbient[i];
   4123 	}
   4124 
   4125 	// flatten meshes to world space
   4126 	for(i = 0; i < in.nNumMeshNode; ++i)
   4127 	{
   4128 
   4129 
   4130 		SPODNode& inNode  = in.pNode[i];
   4131 		SPODNode& outNode = out.pNode[i];
   4132 
   4133 		// Get the meshes
   4134 		SPODMesh& inMesh  = in.pMesh[inNode.nIdx];
   4135 		SPODMesh& outMesh = out.pMesh[i];
   4136 
   4137 		if(inMesh.pInterleaved != 0) // This function requires all the meshes to be de-interleaved
   4138 		{
   4139 			_ASSERT(inMesh.pInterleaved == 0);
   4140 			out.Destroy(); // Destroy the out pod scene
   4141 			return PVR_FAIL;
   4142 		}
   4143 
   4144 		// Copy the node
   4145 		PVRTModelPODCopyNode(inNode, outNode, in.nNumFrame);
   4146 
   4147 		// Strip out animation and parenting
   4148 		outNode.nIdxParent = -1;
   4149 
   4150 		outNode.nAnimFlags = 0;
   4151 		FREE(outNode.pfAnimMatrix);
   4152 		FREE(outNode.pfAnimPosition);
   4153 		FREE(outNode.pfAnimRotation);
   4154 		FREE(outNode.pfAnimScale);
   4155 
   4156 		// Update the mesh ID. The rest of the IDs should remain correct
   4157 		outNode.nIdx = i;
   4158 
   4159 		// Copy the mesh
   4160 		PVRTModelPODCopyMesh(inMesh, outMesh);
   4161 
   4162 		// Strip out skinning information as that is no longer needed
   4163 		outMesh.sBoneBatches.Release();
   4164 		outMesh.sBoneIdx.Reset();
   4165 		outMesh.sBoneWeight.Reset();
   4166 
   4167 		// Set the data type to float and resize the arrays as this function outputs transformed data as float only
   4168 		if(inMesh.sVertex.n)
   4169 		{
   4170 			outMesh.sVertex.eType = EPODDataFloat;
   4171 			outMesh.sVertex.pData = (unsigned char*) realloc(outMesh.sVertex.pData, PVRTModelPODDataStride(outMesh.sVertex) * inMesh.nNumVertex);
   4172 		}
   4173 
   4174 		if(inMesh.sNormals.n)
   4175 		{
   4176 			outMesh.sNormals.eType = EPODDataFloat;
   4177 			outMesh.sNormals.pData = (unsigned char*) realloc(outMesh.sNormals.pData, PVRTModelPODDataStride(outMesh.sNormals) * inMesh.nNumVertex);
   4178 		}
   4179 
   4180 		if(inMesh.sTangents.n)
   4181 		{
   4182 			outMesh.sTangents.eType = EPODDataFloat;
   4183 			outMesh.sTangents.pData = (unsigned char*) realloc(outMesh.sTangents.pData, PVRTModelPODDataStride(outMesh.sTangents) * inMesh.nNumVertex);
   4184 		}
   4185 
   4186 		if(inMesh.sBinormals.n)
   4187 		{
   4188 			outMesh.sBinormals.eType = EPODDataFloat;
   4189 			outMesh.sBinormals.pData = (unsigned char*) realloc(outMesh.sBinormals.pData, PVRTModelPODDataStride(outMesh.sBinormals) * inMesh.nNumVertex);
   4190 		}
   4191 
   4192 		if(inMesh.sBoneBatches.nBatchCnt)
   4193 		{
   4194 			unsigned int ui32BatchPaletteSize   = 0;
   4195 			PVRTMATRIX *pPalette = 0;
   4196 			PVRTMATRIX *pPaletteInvTrans = 0;
   4197 			unsigned int ui32Offset = 0, ui32Strip = 0;
   4198 			bool *pbTransformed = 0;
   4199 
   4200 			SafeAlloc(pPalette, inMesh.sBoneBatches.nBatchBoneMax);
   4201 			SafeAlloc(pPaletteInvTrans, inMesh.sBoneBatches.nBatchBoneMax);
   4202 			SafeAlloc(pbTransformed, inMesh.nNumVertex);
   4203 
   4204 			for(j = 0; j < (unsigned int) inMesh.sBoneBatches.nBatchCnt; ++j)
   4205 			{
   4206 				ui32BatchPaletteSize = (unsigned int) inMesh.sBoneBatches.pnBatchBoneCnt[j];
   4207 
   4208 				for(k = 0; k < ui32BatchPaletteSize; ++k)
   4209 				{
   4210 					// Get the Node of the bone
   4211 					int i32NodeID = inMesh.sBoneBatches.pnBatches[j * inMesh.sBoneBatches.nBatchBoneMax + k];
   4212 
   4213 					// Get the World transformation matrix for this bone
   4214 					in.GetBoneWorldMatrix(pPalette[k], inNode, in.pNode[i32NodeID]);
   4215 
   4216 					// Get the inverse transpose of the 3x3
   4217 					if(inMesh.sNormals.n || inMesh.sTangents.n || inMesh.sBinormals.n)
   4218 					{
   4219 						pPaletteInvTrans[k] = pPalette[k];
   4220 						pPaletteInvTrans[k].f[3]  = pPaletteInvTrans[k].f[7]  = pPaletteInvTrans[k].f[11] = 0;
   4221 						pPaletteInvTrans[k].f[12] = pPaletteInvTrans[k].f[13] = pPaletteInvTrans[k].f[14] = 0;
   4222 						PVRTMatrixInverse(pPaletteInvTrans[k], pPaletteInvTrans[k]);
   4223 						PVRTMatrixTranspose(pPaletteInvTrans[k], pPaletteInvTrans[k]);
   4224 					}
   4225 				}
   4226 				// Calculate the number of triangles in the current batch
   4227 				unsigned int ui32Tris;
   4228 
   4229 				if(j + 1 < (unsigned int) inMesh.sBoneBatches.nBatchCnt)
   4230 					ui32Tris = inMesh.sBoneBatches.pnBatchOffset[j + 1] - inMesh.sBoneBatches.pnBatchOffset[j];
   4231 				else
   4232 					ui32Tris = inMesh.nNumFaces - inMesh.sBoneBatches.pnBatchOffset[j];
   4233 
   4234 				unsigned int idx;
   4235 				float fBoneIdx[4], fBoneWeights[4];
   4236 
   4237 				if(inMesh.nNumStrips == 0)
   4238 				{
   4239 					ui32Offset = 3 * inMesh.sBoneBatches.pnBatchOffset[j];
   4240 
   4241 					for(l = ui32Offset; l < ui32Offset + (ui32Tris * 3); ++l)
   4242 					{
   4243 						if(inMesh.sFaces.pData) // Indexed Triangle Lists
   4244 							PVRTVertexRead(&idx, inMesh.sFaces.pData + (l * inMesh.sFaces.nStride), inMesh.sFaces.eType);
   4245 						else // Indexed Triangle Lists
   4246 							idx = l;
   4247 
   4248 						if(!pbTransformed[idx])
   4249 						{
   4250 							PVRTVertexRead((PVRTVECTOR4f*) &fBoneIdx[0], inMesh.sBoneIdx.pData + (idx * inMesh.sBoneIdx.nStride), inMesh.sBoneIdx.eType, inMesh.sBoneIdx.n);
   4251 							PVRTVertexRead((PVRTVECTOR4f*) &fBoneWeights[0], inMesh.sBoneWeight.pData + (idx * inMesh.sBoneWeight.nStride), inMesh.sBoneWeight.eType, inMesh.sBoneWeight.n);
   4252 
   4253 							TransformCPODData(inMesh.sVertex, outMesh.sVertex, idx, pPalette, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, false);
   4254 							TransformCPODData(inMesh.sNormals, outMesh.sNormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true);
   4255 							TransformCPODData(inMesh.sTangents, outMesh.sTangents, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true);
   4256 							TransformCPODData(inMesh.sBinormals, outMesh.sBinormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true);
   4257 							pbTransformed[idx] = true;
   4258 						}
   4259 					}
   4260 				}
   4261 				else
   4262 				{
   4263 					unsigned int ui32TrisDrawn = 0;
   4264 
   4265 					while(ui32TrisDrawn < ui32Tris)
   4266 					{
   4267 						for(l = ui32Offset; l < ui32Offset + (inMesh.pnStripLength[ui32Strip]+2); ++l)
   4268 						{
   4269 							if(inMesh.sFaces.pData) // Indexed Triangle Strips
   4270 								PVRTVertexRead(&idx, inMesh.sFaces.pData + (l * inMesh.sFaces.nStride), inMesh.sFaces.eType);
   4271 							else // Triangle Strips
   4272 								idx = l;
   4273 
   4274 							if(!pbTransformed[idx])
   4275 							{
   4276 								PVRTVertexRead((PVRTVECTOR4f*) &fBoneIdx[0], inMesh.sBoneIdx.pData + (idx * inMesh.sBoneIdx.nStride), inMesh.sBoneIdx.eType, inMesh.sBoneIdx.n);
   4277 								PVRTVertexRead((PVRTVECTOR4f*) &fBoneWeights[0], inMesh.sBoneWeight.pData + (idx * inMesh.sBoneWeight.nStride), inMesh.sBoneWeight.eType, inMesh.sBoneWeight.n);
   4278 
   4279 								TransformCPODData(inMesh.sVertex, outMesh.sVertex, idx, pPalette, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, false);
   4280 								TransformCPODData(inMesh.sNormals, outMesh.sNormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true);
   4281 								TransformCPODData(inMesh.sTangents, outMesh.sTangents, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true);
   4282 								TransformCPODData(inMesh.sBinormals, outMesh.sBinormals, idx, pPaletteInvTrans, &fBoneIdx[0], &fBoneWeights[0], inMesh.sBoneIdx.n, true);
   4283 								pbTransformed[idx] = true;
   4284 							}
   4285 						}
   4286 
   4287 						ui32Offset	  += inMesh.pnStripLength[ui32Strip] + 2;
   4288 						ui32TrisDrawn += inMesh.pnStripLength[ui32Strip];
   4289 
   4290 						++ui32Strip;
   4291 					}
   4292 				}
   4293 			}
   4294 
   4295 			FREE(pPalette);
   4296 			FREE(pPaletteInvTrans);
   4297 			FREE(pbTransformed);
   4298 		}
   4299 		else
   4300 		{
   4301 			// Get transformation matrix
   4302 			in.GetWorldMatrix(mWorld, inNode);
   4303 			PVRTMATRIX mWorldInvTrans;
   4304 
   4305 			// Get the inverse transpose of the 3x3
   4306 			if(inMesh.sNormals.n || inMesh.sTangents.n || inMesh.sBinormals.n)
   4307 			{
   4308 				mWorldInvTrans = mWorld;
   4309 				mWorldInvTrans.f[3]  = mWorldInvTrans.f[7]  = mWorldInvTrans.f[11] = 0;
   4310 				mWorldInvTrans.f[12] = mWorldInvTrans.f[13] = mWorldInvTrans.f[14] = 0;
   4311 				PVRTMatrixInverse(mWorldInvTrans, mWorldInvTrans);
   4312 				PVRTMatrixTranspose(mWorldInvTrans, mWorldInvTrans);
   4313 			}
   4314 
   4315 			// Transform the vertices
   4316 			for(j = 0; j < inMesh.nNumVertex; ++j)
   4317 			{
   4318 				TransformCPODData(inMesh.sVertex, outMesh.sVertex, j, &mWorld, 0, 0, 0, false);
   4319 				TransformCPODData(inMesh.sNormals, outMesh.sNormals, j, &mWorldInvTrans, 0, 0, 0, true);
   4320 				TransformCPODData(inMesh.sTangents, outMesh.sTangents, j, &mWorldInvTrans, 0, 0, 0, true);
   4321 				TransformCPODData(inMesh.sBinormals, outMesh.sBinormals, j, &mWorldInvTrans, 0, 0, 0, true);
   4322 			}
   4323 		}
   4324 	}
   4325 
   4326 	// Copy the rest of the nodes
   4327 	for(i = in.nNumMeshNode; i < in.nNumNode; ++i)
   4328 	{
   4329 		PVRTModelPODCopyNode(in.pNode[i], out.pNode[i], in.nNumFrame);
   4330 
   4331 		// Strip out animation and parenting
   4332 		out.pNode[i].nIdxParent = -1;
   4333 
   4334 		out.pNode[i].nAnimFlags = 0;
   4335 		FREE(out.pNode[i].pfAnimMatrix);
   4336 		FREE(out.pNode[i].pnAnimMatrixIdx);
   4337 
   4338 		FREE(out.pNode[i].pfAnimPosition);
   4339 		FREE(out.pNode[i].pnAnimPositionIdx);
   4340 
   4341 		FREE(out.pNode[i].pfAnimRotation);
   4342 		FREE(out.pNode[i].pnAnimRotationIdx);
   4343 
   4344 		FREE(out.pNode[i].pfAnimScale);
   4345 		FREE(out.pNode[i].pnAnimScaleIdx);
   4346 
   4347 		// Get world transformation matrix....
   4348 		in.GetWorldMatrix(mWorld, in.pNode[i]);
   4349 
   4350 		// ...set the out node transformation matrix
   4351 		if(SafeAlloc(out.pNode[i].pfAnimMatrix, 16))
   4352 			memcpy(out.pNode[i].pfAnimMatrix, mWorld.f, sizeof(PVRTMATRIX));
   4353 	}
   4354 
   4355 	// Copy camera, lights
   4356 	if(in.nNumCamera && SafeAlloc(out.pCamera, in.nNumCamera))
   4357 	{
   4358 		out.nNumCamera = in.nNumCamera;
   4359 
   4360 		for(i = 0; i < in.nNumCamera; ++i)
   4361 			PVRTModelPODCopyCamera(in.pCamera[i], out.pCamera[i], in.nNumFrame);
   4362 	}
   4363 
   4364 	if(in.nNumLight && SafeAlloc(out.pLight, in.nNumLight))
   4365 	{
   4366 		out.nNumLight = in.nNumLight;
   4367 
   4368 		for(i = 0; i < out.nNumLight; ++i)
   4369 			PVRTModelPODCopyLight(in.pLight[i], out.pLight[i]);
   4370 	}
   4371 
   4372 	// Copy textures
   4373 	if(in.nNumTexture && SafeAlloc(out.pTexture, in.nNumTexture))
   4374 	{
   4375 		out.nNumTexture = in.nNumTexture;
   4376 
   4377 		for(i = 0; i < out.nNumTexture; ++i)
   4378 			PVRTModelPODCopyTexture(in.pTexture[i], out.pTexture[i]);
   4379 	}
   4380 
   4381 	// Copy materials
   4382 	if(in.nNumMaterial && SafeAlloc(out.pMaterial, in.nNumMaterial))
   4383 	{
   4384 		out.nNumMaterial = in.nNumMaterial;
   4385 
   4386 		for(i = 0; i < in.nNumMaterial; ++i)
   4387 			PVRTModelPODCopyMaterial(in.pMaterial[i], out.pMaterial[i]);
   4388 	}
   4389 
   4390 	out.InitImpl();
   4391 
   4392 	return PVR_SUCCESS;
   4393 }
   4394 
   4395 static bool MergeTexture(const CPVRTModelPOD &src, CPVRTModelPOD &dst, const int &srcTexID, int &dstTexID)
   4396 {
   4397 	if(srcTexID != -1 && srcTexID < (int) src.nNumTexture)
   4398 	{
   4399 		if(dstTexID == -1)
   4400 		{
   4401 			// Resize our texture array to add our texture
   4402 			dst.pTexture = (SPODTexture*) realloc(dst.pTexture, (dst.nNumTexture + 1) * sizeof(SPODTexture));
   4403 
   4404 			if(!dst.pTexture)
   4405 				return false;
   4406 
   4407 			dstTexID = dst.nNumTexture;
   4408 			++dst.nNumTexture;
   4409 
   4410 			dst.pTexture[dstTexID].pszName = (char*) malloc(strlen(src.pTexture[srcTexID].pszName) + 1);
   4411 			strcpy(dst.pTexture[dstTexID].pszName, src.pTexture[srcTexID].pszName);
   4412 			return true;
   4413 		}
   4414 
   4415 		// See if our texture names match
   4416 		if(strcmp(src.pTexture[srcTexID].pszName, dst.pTexture[dstTexID].pszName) == 0)
   4417 			return true; // Nothing to do
   4418 
   4419 		// See if our texture filenames match
   4420 		char * srcName = src.pTexture[srcTexID].pszName;
   4421 		char * dstName = dst.pTexture[dstTexID].pszName;
   4422 		bool bFoundPossibleEndOfFilename = false;
   4423 		bool bStrMatch = true, bFilenameMatch = true;
   4424 
   4425 		while(*srcName != '\0' && *dstName != '\0')
   4426 		{
   4427 			if(*srcName != *dstName)
   4428 			{
   4429 				if(!bFoundPossibleEndOfFilename)
   4430 					return true; // They don't match
   4431 
   4432 				bStrMatch = false;
   4433 			}
   4434 
   4435 			if(*srcName == '.')
   4436 			{
   4437 				if(!bStrMatch)
   4438 					return true; // They don't match
   4439 
   4440 				bFoundPossibleEndOfFilename = true;
   4441 				bFilenameMatch = bStrMatch;
   4442 			}
   4443 
   4444 			++srcName;
   4445 			++dstName;
   4446 		}
   4447 
   4448 		if(bFilenameMatch)
   4449 		{
   4450 			// Our filenames match but our extensions don't so merge our textures
   4451 			FREE(dst.pTexture[dstTexID].pszName);
   4452 			dst.pTexture[dstTexID].pszName = (char*) malloc(strlen(src.pTexture[srcTexID].pszName) + 1);
   4453 			strcpy(dst.pTexture[dstTexID].pszName, src.pTexture[srcTexID].pszName);
   4454 			return true;
   4455 		}
   4456 
   4457 		// Our texture names aren't the same so don't try and merge
   4458 	}
   4459 
   4460 	return true;
   4461 }
   4462 
   4463 /*!***************************************************************************
   4464  @Function			PVRTModelPODMergeMaterials
   4465  @Input				src - Source scene
   4466  @Output			dst - Destination scene
   4467  @Description		This function takes two scenes and merges the textures,
   4468 					PFX effects and blending parameters from the src materials
   4469 					into the dst materials if they have the same material name.
   4470 *****************************************************************************/
   4471 EPVRTError PVRTModelPODMergeMaterials(const CPVRTModelPOD &src, CPVRTModelPOD &dst)
   4472 {
   4473 	if(!src.nNumMaterial || !dst.nNumMaterial)
   4474 		return PVR_SUCCESS;
   4475 
   4476 	bool *bMatched = (bool*) calloc(dst.nNumMaterial, sizeof(bool));
   4477 
   4478 	if(!bMatched)
   4479 		return PVR_FAIL;
   4480 
   4481 	for(unsigned int i = 0; i < src.nNumMaterial; ++i)
   4482 	{
   4483 		const SPODMaterial &srcMaterial = src.pMaterial[i];
   4484 
   4485 		// Match our current material with one in the dst
   4486 		for(unsigned int j = 0; j < dst.nNumMaterial; ++j)
   4487 		{
   4488 			if(bMatched[j])
   4489 				continue; // We have already matched this material with another
   4490 
   4491 			SPODMaterial &dstMaterial = dst.pMaterial[j];
   4492 
   4493 			// We've found a material with the same name
   4494 			if(strcmp(srcMaterial.pszName, dstMaterial.pszName) == 0)
   4495 			{
   4496 				bMatched[j] = true;
   4497 
   4498 				// Merge the textures
   4499 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexDiffuse, dstMaterial.nIdxTexDiffuse))
   4500 				{
   4501 					FREE(bMatched);
   4502 					return PVR_FAIL;
   4503 				}
   4504 
   4505 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexAmbient, dstMaterial.nIdxTexAmbient))
   4506 				{
   4507 					FREE(bMatched);
   4508 					return PVR_FAIL;
   4509 				}
   4510 
   4511 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexSpecularColour, dstMaterial.nIdxTexSpecularColour))
   4512 				{
   4513 					FREE(bMatched);
   4514 					return PVR_FAIL;
   4515 				}
   4516 
   4517 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexSpecularLevel, dstMaterial.nIdxTexSpecularLevel))
   4518 				{
   4519 					FREE(bMatched);
   4520 					return PVR_FAIL;
   4521 				}
   4522 
   4523 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexBump, dstMaterial.nIdxTexBump))
   4524 				{
   4525 					FREE(bMatched);
   4526 					return PVR_FAIL;
   4527 				}
   4528 
   4529 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexEmissive, dstMaterial.nIdxTexEmissive))
   4530 				{
   4531 					FREE(bMatched);
   4532 					return PVR_FAIL;
   4533 				}
   4534 
   4535 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexGlossiness, dstMaterial.nIdxTexGlossiness))
   4536 				{
   4537 					FREE(bMatched);
   4538 					return PVR_FAIL;
   4539 				}
   4540 
   4541 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexOpacity, dstMaterial.nIdxTexOpacity))
   4542 				{
   4543 					FREE(bMatched);
   4544 					return PVR_FAIL;
   4545 				}
   4546 
   4547 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexReflection, dstMaterial.nIdxTexReflection))
   4548 				{
   4549 					FREE(bMatched);
   4550 					return PVR_FAIL;
   4551 				}
   4552 
   4553 				if(!MergeTexture(src, dst, srcMaterial.nIdxTexRefraction, dstMaterial.nIdxTexRefraction))
   4554 				{
   4555 					FREE(bMatched);
   4556 					return PVR_FAIL;
   4557 				}
   4558 
   4559 				dstMaterial.eBlendSrcRGB = srcMaterial.eBlendSrcRGB;
   4560 				dstMaterial.eBlendSrcA = srcMaterial.eBlendSrcA;
   4561 				dstMaterial.eBlendDstRGB = srcMaterial.eBlendDstRGB;
   4562 				dstMaterial.eBlendDstA = srcMaterial.eBlendDstA;
   4563 				dstMaterial.eBlendOpRGB = srcMaterial.eBlendOpRGB;
   4564 				dstMaterial.eBlendOpA = srcMaterial.eBlendOpA;
   4565 				memcpy(dstMaterial.pfBlendColour, srcMaterial.pfBlendColour, 4 * sizeof(VERTTYPE));
   4566 				memcpy(dstMaterial.pfBlendFactor, srcMaterial.pfBlendFactor, 4 * sizeof(VERTTYPE));
   4567 				dstMaterial.nFlags = srcMaterial.nFlags;
   4568 
   4569 				// Merge effect names
   4570 				if(srcMaterial.pszEffectFile)
   4571 				{
   4572 					FREE(dstMaterial.pszEffectFile);
   4573 					dstMaterial.pszEffectFile = (char*) malloc(strlen(srcMaterial.pszEffectFile) + 1);
   4574 					strcpy(dstMaterial.pszEffectFile, srcMaterial.pszEffectFile);
   4575 				}
   4576 
   4577 				if(srcMaterial.pszEffectName)
   4578 				{
   4579 					FREE(dstMaterial.pszEffectName);
   4580 					dstMaterial.pszEffectName = (char*) malloc(strlen(srcMaterial.pszEffectName) + 1);
   4581 					strcpy(dstMaterial.pszEffectName, srcMaterial.pszEffectName);
   4582 				}
   4583 
   4584 				break;
   4585 			}
   4586 		}
   4587 	}
   4588 
   4589 	FREE(bMatched);
   4590 	return PVR_SUCCESS;
   4591 }
   4592 
   4593 /*****************************************************************************
   4594  End of file (PVRTModelPOD.cpp)
   4595 *****************************************************************************/
   4596 
   4597