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