|  | /****************************************************************************** | 
|  |  | 
|  | @File         PVRTVertex.cpp | 
|  |  | 
|  | @Title        PVRTVertex | 
|  |  | 
|  | @Version | 
|  |  | 
|  | @Copyright    Copyright (c) Imagination Technologies Limited. | 
|  |  | 
|  | @Platform     ANSI compatible | 
|  |  | 
|  | @Description  Utility functions which process vertices. | 
|  |  | 
|  | ******************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Includes | 
|  | ****************************************************************************/ | 
|  | #include "PVRTGlobal.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "PVRTFixedPoint.h" | 
|  | #include "PVRTMatrix.h" | 
|  | #include "PVRTVertex.h" | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Defines | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Macros | 
|  | ****************************************************************************/ | 
|  | #define MAX_VERTEX_OUT (3*nVtxNum) | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Structures | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Constants | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Local function definitions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /***************************************************************************** | 
|  | ** Functions | 
|  | *****************************************************************************/ | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			PVRTVertexRead | 
|  | @Output			pV | 
|  | @Input				pData | 
|  | @Input				eType | 
|  | @Input				nCnt | 
|  | @Description		Read a vector | 
|  | *****************************************************************************/ | 
|  | void PVRTVertexRead( | 
|  | PVRTVECTOR4f		* const pV, | 
|  | const void			* const pData, | 
|  | const EPVRTDataType	eType, | 
|  | const int			nCnt) | 
|  | { | 
|  | int		i; | 
|  | float	*pOut = (float*)pV; | 
|  |  | 
|  | pV->x = 0; | 
|  | pV->y = 0; | 
|  | pV->z = 0; | 
|  | pV->w = 1; | 
|  |  | 
|  | switch(eType) | 
|  | { | 
|  | default: | 
|  | _ASSERT(false); | 
|  | break; | 
|  |  | 
|  | case EPODDataFloat: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = ((float*)pData)[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataFixed16_16: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = ((int*)pData)[i] * 1.0f / (float)(1 << 16); | 
|  | break; | 
|  |  | 
|  | case EPODDataInt: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((int*)pData)[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedInt: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((unsigned int*)pData)[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataByte: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((char*)pData)[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataByteNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((char*)pData)[i] / (float)((1 << 7)-1); | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedByte: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((unsigned char*)pData)[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedByteNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((unsigned char*)pData)[i] / (float)((1 << 8)-1); | 
|  | break; | 
|  |  | 
|  | case EPODDataShort: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((short*)pData)[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataShortNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((short*)pData)[i] / (float)((1 << 15)-1); | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedShort: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((unsigned short*)pData)[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedShortNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | pOut[i] = (float)((unsigned short*)pData)[i] / (float)((1 << 16)-1); | 
|  | break; | 
|  |  | 
|  | case EPODDataRGBA: | 
|  | { | 
|  | unsigned int dwVal = *(unsigned int*)pData; | 
|  | unsigned char v[4]; | 
|  |  | 
|  | v[0] = (unsigned char) (dwVal >> 24); | 
|  | v[1] = (unsigned char) (dwVal >> 16); | 
|  | v[2] = (unsigned char) (dwVal >>  8); | 
|  | v[3] = (unsigned char) (dwVal >>  0); | 
|  |  | 
|  | for(i = 0; i < 4; ++i) | 
|  | pOut[i] = 1.0f / 255.0f * (float)v[i]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataABGR: | 
|  | { | 
|  | unsigned int dwVal = *(unsigned int*)pData; | 
|  | unsigned char v[4]; | 
|  |  | 
|  | v[0] = (unsigned char) (dwVal >> 0); | 
|  | v[1] = (unsigned char) (dwVal >> 8); | 
|  | v[2] = (unsigned char) (dwVal >> 16); | 
|  | v[3] = (unsigned char) (dwVal >> 24); | 
|  |  | 
|  | for(i = 0; i < 4; ++i) | 
|  | pOut[i] = 1.0f / 255.0f * (float)v[i]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataARGB: | 
|  | case EPODDataD3DCOLOR: | 
|  | { | 
|  | unsigned int dwVal = *(unsigned int*)pData; | 
|  | unsigned char v[4]; | 
|  |  | 
|  | v[0] = (unsigned char) (dwVal >> 16); | 
|  | v[1] = (unsigned char) (dwVal >>  8); | 
|  | v[2] = (unsigned char) (dwVal >>  0); | 
|  | v[3] = (unsigned char) (dwVal >> 24); | 
|  |  | 
|  | for(i = 0; i < 4; ++i) | 
|  | pOut[i] = 1.0f / 255.0f * (float)v[i]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataUBYTE4: | 
|  | { | 
|  | unsigned int dwVal = *(unsigned int*)pData; | 
|  | unsigned char v[4]; | 
|  |  | 
|  | v[0] = (unsigned char) (dwVal >>  0); | 
|  | v[1] = (unsigned char) (dwVal >>  8); | 
|  | v[2] = (unsigned char) (dwVal >> 16); | 
|  | v[3] = (unsigned char) (dwVal >> 24); | 
|  |  | 
|  | for(i = 0; i < 4; ++i) | 
|  | pOut[i] = v[i]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataDEC3N: | 
|  | { | 
|  | int dwVal = *(int*)pData; | 
|  | int v[4]; | 
|  |  | 
|  | v[0] = (dwVal << 22) >> 22; | 
|  | v[1] = (dwVal << 12) >> 22; | 
|  | v[2] = (dwVal <<  2) >> 22; | 
|  | v[3] = 0; | 
|  |  | 
|  | for(i = 0; i < 3; ++i) | 
|  | pOut[i] = (float)v[i] * (1.0f / 511.0f); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			PVRTVertexRead | 
|  | @Output			pV | 
|  | @Input				pData | 
|  | @Input				eType | 
|  | @Description		Read an int | 
|  | *****************************************************************************/ | 
|  | void PVRTVertexRead( | 
|  | unsigned int		* const pV, | 
|  | const void			* const pData, | 
|  | const EPVRTDataType	eType) | 
|  | { | 
|  | switch(eType) | 
|  | { | 
|  | default: | 
|  | _ASSERT(false); | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedShort: | 
|  | *pV = *(unsigned short*)pData; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedInt: | 
|  | *pV = *(unsigned int*)pData; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			PVRTVertexWrite | 
|  | @Output			pOut | 
|  | @Input				eType | 
|  | @Input				nCnt | 
|  | @Input				pV | 
|  | @Description		Write a vector | 
|  | *****************************************************************************/ | 
|  | void PVRTVertexWrite( | 
|  | void				* const pOut, | 
|  | const EPVRTDataType	eType, | 
|  | const int			nCnt, | 
|  | const PVRTVECTOR4f	* const pV) | 
|  | { | 
|  | int		i; | 
|  | float	*pData = (float*)pV; | 
|  |  | 
|  | switch(eType) | 
|  | { | 
|  | default: | 
|  | _ASSERT(false); | 
|  | break; | 
|  |  | 
|  | case EPODDataDEC3N: | 
|  | { | 
|  | int v[3]; | 
|  |  | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | { | 
|  | v[i] = (int)(pData[i] * 511.0f); | 
|  | v[i] = PVRT_CLAMP(v[i], -511, 511); | 
|  | v[i] &= 0x000003ff; | 
|  | } | 
|  |  | 
|  | for(; i < 3; ++i) | 
|  | { | 
|  | v[i] = 0; | 
|  | } | 
|  |  | 
|  | *(unsigned int*)pOut = (v[0] << 0) | (v[1] << 10) | (v[2] << 20); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataARGB: | 
|  | case EPODDataD3DCOLOR: | 
|  | { | 
|  | unsigned char v[4]; | 
|  |  | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | v[i] = (unsigned char)PVRT_CLAMP(pData[i] * 255.0f, 0.0f, 255.0f); | 
|  |  | 
|  | for(; i < 4; ++i) | 
|  | v[i] = 0; | 
|  |  | 
|  | *(unsigned int*)pOut = (v[3] << 24) | (v[0] << 16) | (v[1] << 8) | v[2]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataRGBA: | 
|  | { | 
|  | unsigned char v[4]; | 
|  |  | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | v[i] = (unsigned char)PVRT_CLAMP(pData[i] * 255.0f, 0.0f, 255.0f); | 
|  |  | 
|  | for(; i < 4; ++i) | 
|  | v[i] = 0; | 
|  |  | 
|  | *(unsigned int*)pOut = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataABGR: | 
|  | { | 
|  | unsigned char v[4]; | 
|  |  | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | v[i] = (unsigned char)PVRT_CLAMP(pData[i] * 255.0f, 0.0f, 255.0f); | 
|  |  | 
|  | for(; i < 4; ++i) | 
|  | v[i] = 0; | 
|  |  | 
|  | *(unsigned int*)pOut = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataUBYTE4: | 
|  | { | 
|  | unsigned char v[4]; | 
|  |  | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | v[i] = (unsigned char)PVRT_CLAMP(pData[i], 0.0f, 255.0f); | 
|  |  | 
|  | for(; i < 4; ++i) | 
|  | v[i] = 0; | 
|  |  | 
|  | *(unsigned int*)pOut = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case EPODDataFloat: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((float*)pOut)[i] = pData[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataFixed16_16: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((int*)pOut)[i] = (int)(pData[i] * (float)(1 << 16)); | 
|  | break; | 
|  |  | 
|  | case EPODDataInt: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((int*)pOut)[i] = (int)pData[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedInt: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((unsigned int*)pOut)[i] = (unsigned int)pData[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataByte: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((char*)pOut)[i] = (char)pData[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataByteNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((char*)pOut)[i] = (char)(pData[i] * (float)((1 << 7)-1)); | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedByte: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((unsigned char*)pOut)[i] = (unsigned char)pData[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedByteNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((char*)pOut)[i] = (unsigned char)(pData[i] * (float)((1 << 8)-1)); | 
|  | break; | 
|  |  | 
|  | case EPODDataShort: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((short*)pOut)[i] = (short)pData[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataShortNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((short*)pOut)[i] = (short)(pData[i] * (float)((1 << 15)-1)); | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedShort: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((unsigned short*)pOut)[i] = (unsigned short)pData[i]; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedShortNorm: | 
|  | for(i = 0; i < nCnt; ++i) | 
|  | ((unsigned short*)pOut)[i] = (unsigned short)(pData[i] * (float)((1 << 16)-1)); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			PVRTVertexWrite | 
|  | @Output			pOut | 
|  | @Input				eType | 
|  | @Input				V | 
|  | @Description		Write an int | 
|  | *****************************************************************************/ | 
|  | void PVRTVertexWrite( | 
|  | void				* const pOut, | 
|  | const EPVRTDataType	eType, | 
|  | const unsigned int	V) | 
|  | { | 
|  | switch(eType) | 
|  | { | 
|  | default: | 
|  | _ASSERT(false); | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedShort: | 
|  | *(unsigned short*)pOut = (unsigned short) V; | 
|  | break; | 
|  |  | 
|  | case EPODDataUnsignedInt: | 
|  | *(unsigned int*)pOut = V; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			PVRTVertexTangentBitangent | 
|  | @Output			pvTan | 
|  | @Output			pvBin | 
|  | @Input				pvNor | 
|  | @Input				pfPosA | 
|  | @Input				pfPosB | 
|  | @Input				pfPosC | 
|  | @Input				pfTexA | 
|  | @Input				pfTexB | 
|  | @Input				pfTexC | 
|  | @Description		Calculates the tangent and bitangent vectors for | 
|  | vertex 'A' of the triangle defined by the 3 supplied | 
|  | 3D position coordinates (pfPosA) and 2D texture | 
|  | coordinates (pfTexA). | 
|  | *****************************************************************************/ | 
|  | void PVRTVertexTangentBitangent( | 
|  | PVRTVECTOR3f		* const pvTan, | 
|  | PVRTVECTOR3f		* const pvBin, | 
|  | const PVRTVECTOR3f	* const pvNor, | 
|  | const float			* const pfPosA, | 
|  | const float			* const pfPosB, | 
|  | const float			* const pfPosC, | 
|  | const float			* const pfTexA, | 
|  | const float			* const pfTexB, | 
|  | const float			* const pfTexC) | 
|  | { | 
|  | PVRTVECTOR3f BaseVector1, BaseVector2, AlignedVector; | 
|  |  | 
|  | if(PVRTMatrixVec3DotProductF(*pvNor, *pvNor) == 0) | 
|  | { | 
|  | pvTan->x = 0; | 
|  | pvTan->y = 0; | 
|  | pvTan->z = 0; | 
|  | pvBin->x = 0; | 
|  | pvBin->y = 0; | 
|  | pvBin->z = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* BaseVectors are A-B and A-C. */ | 
|  | BaseVector1.x = pfPosB[0] - pfPosA[0]; | 
|  | BaseVector1.y = pfPosB[1] - pfPosA[1]; | 
|  | BaseVector1.z = pfPosB[2] - pfPosA[2]; | 
|  |  | 
|  | BaseVector2.x = pfPosC[0] - pfPosA[0]; | 
|  | BaseVector2.y = pfPosC[1] - pfPosA[1]; | 
|  | BaseVector2.z = pfPosC[2] - pfPosA[2]; | 
|  |  | 
|  | if (pfTexB[0]==pfTexA[0] && pfTexC[0]==pfTexA[0]) | 
|  | { | 
|  | // Degenerate tri | 
|  | //		_ASSERT(0); | 
|  | pvTan->x = 0; | 
|  | pvTan->y = 0; | 
|  | pvTan->z = 0; | 
|  | pvBin->x = 0; | 
|  | pvBin->y = 0; | 
|  | pvBin->z = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Calc the vector that follows the V direction (it is not the tangent vector)*/ | 
|  | if(pfTexB[0]==pfTexA[0]) { | 
|  | AlignedVector = BaseVector1; | 
|  | if((pfTexB[1] - pfTexA[1]) < 0) { | 
|  | AlignedVector.x = -AlignedVector.x; | 
|  | AlignedVector.y = -AlignedVector.y; | 
|  | AlignedVector.z = -AlignedVector.z; | 
|  | } | 
|  | } else if(pfTexC[0]==pfTexA[0]) { | 
|  | AlignedVector = BaseVector2; | 
|  | if((pfTexC[1] - pfTexA[1]) < 0) { | 
|  | AlignedVector.x = -AlignedVector.x; | 
|  | AlignedVector.y = -AlignedVector.y; | 
|  | AlignedVector.z = -AlignedVector.z; | 
|  | } | 
|  | } else { | 
|  | float fFac; | 
|  |  | 
|  | fFac = -(pfTexB[0] - pfTexA[0]) / (pfTexC[0] - pfTexA[0]); | 
|  |  | 
|  | /* This is the vector that follows the V direction (it is not the tangent vector)*/ | 
|  | AlignedVector.x = BaseVector1.x + BaseVector2.x * fFac; | 
|  | AlignedVector.y = BaseVector1.y + BaseVector2.y * fFac; | 
|  | AlignedVector.z = BaseVector1.z + BaseVector2.z * fFac; | 
|  |  | 
|  | if(((pfTexB[1] - pfTexA[1]) + (pfTexC[1] - pfTexA[1]) * fFac) < 0) { | 
|  | AlignedVector.x = -AlignedVector.x; | 
|  | AlignedVector.y = -AlignedVector.y; | 
|  | AlignedVector.z = -AlignedVector.z; | 
|  | } | 
|  | } | 
|  |  | 
|  | PVRTMatrixVec3NormalizeF(AlignedVector, AlignedVector); | 
|  |  | 
|  | /* The Tangent vector is perpendicular to the plane defined by vAlignedVector and the Normal. */ | 
|  | PVRTMatrixVec3CrossProductF(*pvTan, *pvNor, AlignedVector); | 
|  |  | 
|  | /* The Bitangent vector is the vector perpendicular to the Normal and Tangent (and | 
|  | that follows the vAlignedVector direction) */ | 
|  | PVRTMatrixVec3CrossProductF(*pvBin, *pvTan, *pvNor); | 
|  |  | 
|  | _ASSERT(PVRTMatrixVec3DotProductF(*pvBin, AlignedVector) > 0.0f); | 
|  |  | 
|  | // Worry about wrapping; this is esentially a 2D cross product on texture coords | 
|  | if((pfTexC[0]-pfTexA[0])*(pfTexB[1]-pfTexA[1]) < (pfTexC[1]-pfTexA[1])*(pfTexB[0]-pfTexA[0])) { | 
|  | pvTan->x = -pvTan->x; | 
|  | pvTan->y = -pvTan->y; | 
|  | pvTan->z = -pvTan->z; | 
|  | } | 
|  |  | 
|  | /* Normalize results */ | 
|  | PVRTMatrixVec3NormalizeF(*pvTan, *pvTan); | 
|  | PVRTMatrixVec3NormalizeF(*pvBin, *pvBin); | 
|  |  | 
|  | _ASSERT(PVRTMatrixVec3DotProductF(*pvNor, *pvNor) > 0.9f); | 
|  | _ASSERT(PVRTMatrixVec3DotProductF(*pvTan, *pvTan) > 0.9f); | 
|  | _ASSERT(PVRTMatrixVec3DotProductF(*pvBin, *pvBin) > 0.9f); | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			PVRTVertexGenerateTangentSpace | 
|  | @Output			pnVtxNumOut			Output vertex count | 
|  | @Output			pVtxOut				Output vertices (program must free() this) | 
|  | @Modified			pui32Idx			input AND output; index array for triangle list | 
|  | @Input				nVtxNum				Input vertex count | 
|  | @Input				pVtx				Input vertices | 
|  | @Input				nStride				Size of a vertex (in bytes) | 
|  | @Input				nOffsetPos			Offset in bytes to the vertex position | 
|  | @Input				eTypePos			Data type of the position | 
|  | @Input				nOffsetNor			Offset in bytes to the vertex normal | 
|  | @Input				eTypeNor			Data type of the normal | 
|  | @Input				nOffsetTex			Offset in bytes to the vertex texture coordinate to use | 
|  | @Input				eTypeTex			Data type of the texture coordinate | 
|  | @Input				nOffsetTan			Offset in bytes to the vertex tangent | 
|  | @Input				eTypeTan			Data type of the tangent | 
|  | @Input				nOffsetBin			Offset in bytes to the vertex bitangent | 
|  | @Input				eTypeBin			Data type of the bitangent | 
|  | @Input				nTriNum				Number of triangles | 
|  | @Input				fSplitDifference	Split a vertex if the DP3 of tangents/bitangents are below this (range -1..1) | 
|  | @Return			PVR_FAIL if there was a problem. | 
|  | @Description		Calculates the tangent space for all supplied vertices. | 
|  | Writes tangent and bitangent vectors to the output | 
|  | vertices, copies all other elements from input vertices. | 
|  | Will split vertices if necessary - i.e. if two triangles | 
|  | sharing a vertex want to assign it different | 
|  | tangent-space matrices. The decision whether to split | 
|  | uses fSplitDifference - of the DP3 of two desired | 
|  | tangents or two desired bitangents is higher than this, | 
|  | the vertex will be split. | 
|  | *****************************************************************************/ | 
|  | EPVRTError PVRTVertexGenerateTangentSpace( | 
|  | unsigned int	* const pnVtxNumOut, | 
|  | char			** const pVtxOut, | 
|  | unsigned int	* const pui32Idx, | 
|  | const unsigned int	nVtxNum, | 
|  | const char		* const pVtx, | 
|  | const unsigned int	nStride, | 
|  | const unsigned int	nOffsetPos, | 
|  | EPVRTDataType	eTypePos, | 
|  | const unsigned int	nOffsetNor, | 
|  | EPVRTDataType	eTypeNor, | 
|  | const unsigned int	nOffsetTex, | 
|  | EPVRTDataType	eTypeTex, | 
|  | const unsigned int	nOffsetTan, | 
|  | EPVRTDataType	eTypeTan, | 
|  | const unsigned int	nOffsetBin, | 
|  | EPVRTDataType	eTypeBin, | 
|  | const unsigned int	nTriNum, | 
|  | const float		fSplitDifference) | 
|  | { | 
|  | const int cnMaxSharedVtx = 32; | 
|  | struct SVtxData | 
|  | { | 
|  | int				n;							// Number of items in following arrays, AKA number of tris using this vtx | 
|  | PVRTVECTOR3f	pvTan[cnMaxSharedVtx];		// Tangent (one per triangle referencing this vtx) | 
|  | PVRTVECTOR3f	pvBin[cnMaxSharedVtx];		// Bitangent (one per triangle referencing this vtx) | 
|  | int				pnTri[cnMaxSharedVtx];		// Triangle index (one per triangle referencing this vtx) | 
|  | }; | 
|  | SVtxData		*psVtxData;		// Array of desired tangent spaces per vertex | 
|  | SVtxData		*psTSpass;		// Array of *different* tangent spaces desired for current vertex | 
|  | unsigned int	nTSpassLen; | 
|  | SVtxData		*psVtx, *psCmp; | 
|  | unsigned int	nVert, nCurr, i, j;	// Loop counters | 
|  | unsigned int	nIdx0, nIdx1, nIdx2; | 
|  | float			pfPos0[4], pfPos1[4], pfPos2[4]; | 
|  | float			pfTex0[4], pfTex1[4], pfTex2[4]; | 
|  | float			pfNor0[4], pfNor1[4], pfNor2[4]; | 
|  | unsigned int	*pui32IdxNew;		// New index array, this will be copied over the input array | 
|  |  | 
|  | // Initialise the outputs | 
|  | *pnVtxNumOut	= 0; | 
|  | *pVtxOut		= (char*)malloc(MAX_VERTEX_OUT * nStride); | 
|  | if(!*pVtxOut) | 
|  | { | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | // Allocate some work space | 
|  | pui32IdxNew		= (unsigned int*)calloc(nTriNum * 3, sizeof(*pui32IdxNew)); | 
|  | _ASSERT(pui32IdxNew); | 
|  | psVtxData		= (SVtxData*)calloc(nVtxNum, sizeof(*psVtxData)); | 
|  | _ASSERT(psVtxData); | 
|  | psTSpass		= (SVtxData*)calloc(cnMaxSharedVtx, sizeof(*psTSpass)); | 
|  | _ASSERT(psTSpass); | 
|  | if(!pui32IdxNew || !psVtxData || !psTSpass) | 
|  | { | 
|  | free(pui32IdxNew); | 
|  | free(psVtxData); | 
|  | free(psTSpass); | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | for(nCurr = 0; nCurr < nTriNum; ++nCurr) { | 
|  | nIdx0 = pui32Idx[3*nCurr+0]; | 
|  | nIdx1 = pui32Idx[3*nCurr+1]; | 
|  | nIdx2 = pui32Idx[3*nCurr+2]; | 
|  |  | 
|  | _ASSERT(nIdx0 < nVtxNum); | 
|  | _ASSERT(nIdx1 < nVtxNum); | 
|  | _ASSERT(nIdx2 < nVtxNum); | 
|  |  | 
|  | if(nIdx0 == nIdx1 || nIdx1 == nIdx2 || nIdx0 == nIdx2) { | 
|  | _RPT0(_CRT_WARN,"GenerateTangentSpace(): Degenerate triangle found.\n"); | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | if( | 
|  | psVtxData[nIdx0].n >= cnMaxSharedVtx || | 
|  | psVtxData[nIdx1].n >= cnMaxSharedVtx || | 
|  | psVtxData[nIdx2].n >= cnMaxSharedVtx) | 
|  | { | 
|  | _RPT0(_CRT_WARN,"GenerateTangentSpace(): Too many tris sharing a vtx.\n"); | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfPos0[0], (char*)&pVtx[nIdx0 * nStride] + nOffsetPos, eTypePos, 3); | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfPos1[0], (char*)&pVtx[nIdx1 * nStride] + nOffsetPos, eTypePos, 3); | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfPos2[0], (char*)&pVtx[nIdx2 * nStride] + nOffsetPos, eTypePos, 3); | 
|  |  | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfNor0[0], (char*)&pVtx[nIdx0 * nStride] + nOffsetNor, eTypeNor, 3); | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfNor1[0], (char*)&pVtx[nIdx1 * nStride] + nOffsetNor, eTypeNor, 3); | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfNor2[0], (char*)&pVtx[nIdx2 * nStride] + nOffsetNor, eTypeNor, 3); | 
|  |  | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfTex0[0], (char*)&pVtx[nIdx0 * nStride] + nOffsetTex, eTypeTex, 3); | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfTex1[0], (char*)&pVtx[nIdx1 * nStride] + nOffsetTex, eTypeTex, 3); | 
|  | PVRTVertexRead((PVRTVECTOR4f*) &pfTex2[0], (char*)&pVtx[nIdx2 * nStride] + nOffsetTex, eTypeTex, 3); | 
|  |  | 
|  | PVRTVertexTangentBitangent( | 
|  | &psVtxData[nIdx0].pvTan[psVtxData[nIdx0].n], | 
|  | &psVtxData[nIdx0].pvBin[psVtxData[nIdx0].n], | 
|  | (PVRTVECTOR3f*) &pfNor0[0], | 
|  | pfPos0, pfPos1, pfPos2, | 
|  | pfTex0, pfTex1, pfTex2); | 
|  |  | 
|  | PVRTVertexTangentBitangent( | 
|  | &psVtxData[nIdx1].pvTan[psVtxData[nIdx1].n], | 
|  | &psVtxData[nIdx1].pvBin[psVtxData[nIdx1].n], | 
|  | (PVRTVECTOR3f*) &pfNor1[0], | 
|  | pfPos1, pfPos2, pfPos0, | 
|  | pfTex1, pfTex2, pfTex0); | 
|  |  | 
|  | PVRTVertexTangentBitangent( | 
|  | &psVtxData[nIdx2].pvTan[psVtxData[nIdx2].n], | 
|  | &psVtxData[nIdx2].pvBin[psVtxData[nIdx2].n], | 
|  | (PVRTVECTOR3f*) &pfNor2[0], | 
|  | pfPos2, pfPos0, pfPos1, | 
|  | pfTex2, pfTex0, pfTex1); | 
|  |  | 
|  | psVtxData[nIdx0].pnTri[psVtxData[nIdx0].n] = nCurr; | 
|  | psVtxData[nIdx1].pnTri[psVtxData[nIdx1].n] = nCurr; | 
|  | psVtxData[nIdx2].pnTri[psVtxData[nIdx2].n] = nCurr; | 
|  |  | 
|  | ++psVtxData[nIdx0].n; | 
|  | ++psVtxData[nIdx1].n; | 
|  | ++psVtxData[nIdx2].n; | 
|  | } | 
|  |  | 
|  | // Now let's go through the vertices calculating avg tangent-spaces; create new vertices if necessary | 
|  | for(nVert = 0; nVert < nVtxNum; ++nVert) { | 
|  | psVtx = &psVtxData[nVert]; | 
|  |  | 
|  | // Start out with no output vertices required for this input vertex | 
|  | nTSpassLen = 0; | 
|  |  | 
|  | // Run through each desired tangent space for this vertex | 
|  | for(nCurr = 0; nCurr < (unsigned int) psVtx->n; ++nCurr) { | 
|  | // Run through the possible vertices we can share with to see if we match | 
|  | for(i = 0; i < nTSpassLen; ++i) { | 
|  | psCmp = &psTSpass[i]; | 
|  |  | 
|  | // Check all the shared vertices which match | 
|  | for(j = 0; j < (unsigned int) psCmp->n; ++j) { | 
|  | if(PVRTMatrixVec3DotProductF(psVtx->pvTan[nCurr], psCmp->pvTan[j]) < fSplitDifference) | 
|  | break; | 
|  | if(PVRTMatrixVec3DotProductF(psVtx->pvBin[nCurr], psCmp->pvBin[j]) < fSplitDifference) | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Did all the existing vertices match? | 
|  | if(j == (unsigned int) psCmp->n) { | 
|  | // Yes, so add to list | 
|  | _ASSERT(psCmp->n < cnMaxSharedVtx); | 
|  | psCmp->pvTan[psCmp->n] = psVtx->pvTan[nCurr]; | 
|  | psCmp->pvBin[psCmp->n] = psVtx->pvBin[nCurr]; | 
|  | psCmp->pnTri[psCmp->n] = psVtx->pnTri[nCurr]; | 
|  | ++psCmp->n; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(i == nTSpassLen) { | 
|  | // We never found another matching matrix, so let's add this as a different one | 
|  | _ASSERT(nTSpassLen < cnMaxSharedVtx); | 
|  | psTSpass[nTSpassLen].pvTan[0] = psVtx->pvTan[nCurr]; | 
|  | psTSpass[nTSpassLen].pvBin[0] = psVtx->pvBin[nCurr]; | 
|  | psTSpass[nTSpassLen].pnTri[0] = psVtx->pnTri[nCurr]; | 
|  | psTSpass[nTSpassLen].n = 1; | 
|  | ++nTSpassLen; | 
|  | } | 
|  | } | 
|  |  | 
|  | // OK, now we have 'nTSpassLen' different desired matrices, so we need to add that many to output | 
|  | _ASSERT(nTSpassLen >= 1); | 
|  | for(nCurr = 0; nCurr < nTSpassLen; ++nCurr) { | 
|  | psVtx = &psTSpass[nCurr]; | 
|  |  | 
|  | memset(&pfPos0, 0, sizeof(pfPos0)); | 
|  | memset(&pfPos1, 0, sizeof(pfPos1)); | 
|  |  | 
|  | for(i = 0; i < (unsigned int) psVtx->n; ++i) { | 
|  | // Sum the tangent & bitangents, so we can average them | 
|  | pfPos0[0] += psVtx->pvTan[i].x; | 
|  | pfPos0[1] += psVtx->pvTan[i].y; | 
|  | pfPos0[2] += psVtx->pvTan[i].z; | 
|  |  | 
|  | pfPos1[0] += psVtx->pvBin[i].x; | 
|  | pfPos1[1] += psVtx->pvBin[i].y; | 
|  | pfPos1[2] += psVtx->pvBin[i].z; | 
|  |  | 
|  | // Update triangle indices to use this vtx | 
|  | if(pui32Idx[3 * psVtx->pnTri[i] + 0] == nVert) { | 
|  | pui32IdxNew[3 * psVtx->pnTri[i] + 0] = *pnVtxNumOut; | 
|  |  | 
|  | } else if(pui32Idx[3 * psVtx->pnTri[i] + 1] == nVert) { | 
|  | pui32IdxNew[3 * psVtx->pnTri[i] + 1] = *pnVtxNumOut; | 
|  |  | 
|  | } else if(pui32Idx[3 * psVtx->pnTri[i] + 2] == nVert) { | 
|  | pui32IdxNew[3 * psVtx->pnTri[i] + 2] = *pnVtxNumOut; | 
|  |  | 
|  | } else { | 
|  | _ASSERT(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | PVRTMatrixVec3NormalizeF(*(PVRTVECTOR3f*) &pfPos0[0], *(PVRTVECTOR3f*) &pfPos0[0]); | 
|  | PVRTMatrixVec3NormalizeF(*(PVRTVECTOR3f*) &pfPos1[0], *(PVRTVECTOR3f*) &pfPos1[0]); | 
|  |  | 
|  | if(*pnVtxNumOut >= MAX_VERTEX_OUT) { | 
|  | _RPT0(_CRT_WARN,"PVRTVertexGenerateTangentSpace() ran out of working space! (Too many split vertices)\n"); | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | memcpy(&(*pVtxOut)[(*pnVtxNumOut) * nStride], &pVtx[nVert*nStride], nStride); | 
|  | PVRTVertexWrite((char*)&(*pVtxOut)[(*pnVtxNumOut) * nStride] + nOffsetTan, eTypeTan, 3, (PVRTVECTOR4f*) &pfPos0[0]); | 
|  | PVRTVertexWrite((char*)&(*pVtxOut)[(*pnVtxNumOut) * nStride] + nOffsetBin, eTypeBin, 3, (PVRTVECTOR4f*) &pfPos1[0]); | 
|  |  | 
|  | ++*pnVtxNumOut; | 
|  | } | 
|  | } | 
|  |  | 
|  | FREE(psTSpass); | 
|  | FREE(psVtxData); | 
|  |  | 
|  | *pVtxOut = (char*)realloc(*pVtxOut, *pnVtxNumOut * nStride); | 
|  | _ASSERT(*pVtxOut); | 
|  |  | 
|  | memcpy(pui32Idx, pui32IdxNew, nTriNum * 3 * sizeof(*pui32IdxNew)); | 
|  | FREE(pui32IdxNew); | 
|  |  | 
|  | _RPT3(_CRT_WARN, "GenerateTangentSpace(): %d tris, %d vtx in, %d vtx out\n", nTriNum, nVtxNum, *pnVtxNumOut); | 
|  | _ASSERT(*pnVtxNumOut >= nVtxNum); | 
|  |  | 
|  | return PVR_SUCCESS; | 
|  | } | 
|  |  | 
|  | /***************************************************************************** | 
|  | End of file (PVRTVertex.cpp) | 
|  | *****************************************************************************/ | 
|  |  |