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******************************************************************************/
51enum 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****************************************************************************/
176struct 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*****************************************************************************/
205template <typename T>
206bool 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*****************************************************************************/
225template <typename T>
226void 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*****************************************************************************/
239void 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.
251template<class T> struct check32BitType;
252template<> struct check32BitType<unsigned int> {};
253template<> struct check32BitType<int> {};
254template<> struct check32BitType<float> {};
255template<class T> struct check16BitType;
256template<> struct check16BitType<unsigned short> {};
257template<> struct check16BitType<short> {};
258
259/*!***************************************************************************
260 Class: CSource
261*****************************************************************************/
262class CSource
263{
264public:
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
360bool 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*****************************************************************************/
372class CSourceStream : public CSource
373{
374protected:
375	CPVRTResourceFile* m_pFile;
376	size_t m_BytesReadCount;
377
378public:
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*****************************************************************************/
402CSourceStream::~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*****************************************************************************/
413bool 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*****************************************************************************/
442bool 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*****************************************************************************/
464bool 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*****************************************************************************/
482bool 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*****************************************************************************/
493class CSourceResource : public CSource
494{
495protected:
496	const unsigned char	*m_pData;
497	unsigned int		m_nSize, m_nReadPos;
498
499public:
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*****************************************************************************/
511bool 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*****************************************************************************/
545bool 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
556bool 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*****************************************************************************/
579static 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
589static 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
609static 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*****************************************************************************/
640static 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*****************************************************************************/
668static 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*****************************************************************************/
694template <typename T>
695static 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*****************************************************************************/
720template <typename T>
721static 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*****************************************************************************/
744template <typename T>
745static 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*****************************************************************************/
767static 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*****************************************************************************/
804static 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*****************************************************************************/
891PVRTuint32 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*****************************************************************************/
917static 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*****************************************************************************/
1150static 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*****************************************************************************/
1219static 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*****************************************************************************/
1252static 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*****************************************************************************/
1286static 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*****************************************************************************/
1377static 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
1428static 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*****************************************************************************/
1453static 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*****************************************************************************/
1509static 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*****************************************************************************/
1603static 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*****************************************************************************/
1631static 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*****************************************************************************/
1709static 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*****************************************************************************/
1831static 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*****************************************************************************/
1869EPVRTError 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*****************************************************************************/
1899EPVRTError 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*****************************************************************************/
1922EPVRTError 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*****************************************************************************/
1945EPVRTError 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*****************************************************************************/
2041EPVRTError 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*************************************************************************/
2066EPVRTError 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*************************************************************************/
2094void 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*************************************************************************/
2112void 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*************************************************************************/
2128bool 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*****************************************************************************/
2137CPVRTModelPOD::CPVRTModelPOD() : m_pImpl(NULL)
2138{}
2139
2140/*!***************************************************************************
2141 @Function			Destructor
2142 @Description		Frees the memory allocated to store the scene in pScene.
2143*****************************************************************************/
2144CPVRTModelPOD::~CPVRTModelPOD()
2145{
2146	Destroy();
2147}
2148
2149/*!***************************************************************************
2150 @Function			Destroy
2151 @Description		Frees the memory allocated to store the scene in pScene.
2152*****************************************************************************/
2153void 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*****************************************************************************/
2241void 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*****************************************************************************/
2271void 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*****************************************************************************/
2316PVRTMat4 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*****************************************************************************/
2330void 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*****************************************************************************/
2375PVRTMat4 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*****************************************************************************/
2389void 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*****************************************************************************/
2428PVRTVec3 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*****************************************************************************/
2442void 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*****************************************************************************/
2485PVRTMat4 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*****************************************************************************/
2499void 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*****************************************************************************/
2527void 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*****************************************************************************/
2565PVRTMat4 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*****************************************************************************/
2579void 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*****************************************************************************/
2628PVRTMat4 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*****************************************************************************/
2642void 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*****************************************************************************/
2678PVRTMat4 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*****************************************************************************/
2701VERTTYPE 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*****************************************************************************/
2774VERTTYPE 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*****************************************************************************/
2825void 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*****************************************************************************/
2857PVRTVec4 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*****************************************************************************/
2873PVRTVec4 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*****************************************************************************/
2893EPVRTError 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*****************************************************************************/
2975EPVRTError 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*****************************************************************************/
2998PVRTuint32 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*****************************************************************************/
3043PVRTuint32 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*****************************************************************************/
3083PVRTuint32 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*****************************************************************************/
3095void 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)
3166EPVRTError 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*****************************************************************************/
3304void 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*****************************************************************************/
3367void 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*****************************************************************************/
3400static 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*****************************************************************************/
3427static 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*****************************************************************************/
3455void 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*****************************************************************************/
3567void 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*****************************************************************************/
3609void 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*****************************************************************************/
3777unsigned 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*****************************************************************************/
3790void 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*****************************************************************************/
3818void 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*****************************************************************************/
3881void 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*****************************************************************************/
3963void 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*****************************************************************************/
3975void 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*****************************************************************************/
4008void 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*****************************************************************************/
4024void 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*****************************************************************************/
4040inline 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*****************************************************************************/
4099EPVRTError 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
4395static 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*****************************************************************************/
4471EPVRTError 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