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