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