| /****************************************************************************** | |
| @File PVRTMisc.cpp | |
| @Title PVRTMisc | |
| @Version | |
| @Copyright Copyright (c) Imagination Technologies Limited. | |
| @Platform ANSI compatible | |
| @Description Miscellaneous functions used in 3D rendering. | |
| ******************************************************************************/ | |
| #include <string.h> | |
| #include <stdlib.h> | |
| #include <ctype.h> | |
| #include <limits.h> | |
| #include <math.h> | |
| #include "PVRTGlobal.h" | |
| #include "PVRTContext.h" | |
| #include "PVRTFixedPoint.h" | |
| #include "PVRTMatrix.h" | |
| #include "PVRTMisc.h" | |
| /*!*************************************************************************** | |
| @Function PVRTMiscCalculateIntersectionLinePlane | |
| @Input pfPlane Length 4 [A,B,C,D], values for plane | |
| equation | |
| @Input pv0 A point on the line | |
| @Input pv1 Another point on the line | |
| @Output pvIntersection The point of intersection | |
| @Description Calculates coords of the intersection of a line and an | |
| infinite plane | |
| *****************************************************************************/ | |
| void PVRTMiscCalculateIntersectionLinePlane( | |
| PVRTVECTOR3 * const pvIntersection, | |
| const VERTTYPE pfPlane[4], | |
| const PVRTVECTOR3 * const pv0, | |
| const PVRTVECTOR3 * const pv1) | |
| { | |
| PVRTVECTOR3 vD; | |
| VERTTYPE fN, fD, fT; | |
| /* Calculate vector from point0 to point1 */ | |
| vD.x = pv1->x - pv0->x; | |
| vD.y = pv1->y - pv0->y; | |
| vD.z = pv1->z - pv0->z; | |
| /* Denominator */ | |
| fD = | |
| VERTTYPEMUL(pfPlane[0], vD.x) + | |
| VERTTYPEMUL(pfPlane[1], vD.y) + | |
| VERTTYPEMUL(pfPlane[2], vD.z); | |
| /* Numerator */ | |
| fN = | |
| VERTTYPEMUL(pfPlane[0], pv0->x) + | |
| VERTTYPEMUL(pfPlane[1], pv0->y) + | |
| VERTTYPEMUL(pfPlane[2], pv0->z) + | |
| pfPlane[3]; | |
| fT = VERTTYPEDIV(-fN, fD); | |
| /* And for a finale, calculate the intersection coordinate */ | |
| pvIntersection->x = pv0->x + VERTTYPEMUL(fT, vD.x); | |
| pvIntersection->y = pv0->y + VERTTYPEMUL(fT, vD.y); | |
| pvIntersection->z = pv0->z + VERTTYPEMUL(fT, vD.z); | |
| } | |
| /*!*************************************************************************** | |
| @Function PVRTMiscCalculateInfinitePlane | |
| @Input nStride Size of each vertex structure containing pfVtx | |
| @Input pvPlane Length 4 [A,B,C,D], values for plane equation | |
| @Input pmViewProjInv The inverse of the View Projection matrix | |
| @Input pFrom Position of the camera | |
| @Input fFar Far clipping distance | |
| @Output pfVtx Position of the first of 3 floats to receive | |
| the position of vertex 0; up to 5 vertex positions | |
| will be written (5 is the maximum number of vertices | |
| required to draw an infinite polygon clipped to screen | |
| and far clip plane). | |
| @Returns Number of vertices in the polygon fan (Can be 0, 3, 4 or 5) | |
| @Description Calculates world-space coords of a screen-filling | |
| representation of an infinite plane The resulting vertices run | |
| counter-clockwise around the screen, and can be simply drawn using | |
| non-indexed TRIANGLEFAN | |
| *****************************************************************************/ | |
| int PVRTMiscCalculateInfinitePlane( | |
| VERTTYPE * const pfVtx, | |
| const int nStride, | |
| const PVRTVECTOR4 * const pvPlane, | |
| const PVRTMATRIX * const pmViewProjInv, | |
| const PVRTVECTOR3 * const pFrom, | |
| const VERTTYPE fFar) | |
| { | |
| PVRTVECTOR3 pvWorld[5]; | |
| PVRTVECTOR3 *pvPolyPtr; | |
| unsigned int dwCount; | |
| bool bClip; | |
| int nVert; | |
| VERTTYPE fDotProduct; | |
| /* | |
| Check whether the plane faces the camera | |
| */ | |
| fDotProduct = | |
| VERTTYPEMUL((pFrom->x + VERTTYPEMUL(pvPlane->x, pvPlane->w)), pvPlane->x) + | |
| VERTTYPEMUL((pFrom->y + VERTTYPEMUL(pvPlane->y, pvPlane->w)), pvPlane->y) + | |
| VERTTYPEMUL((pFrom->z + VERTTYPEMUL(pvPlane->z, pvPlane->w)), pvPlane->z); | |
| if(fDotProduct < 0) { | |
| /* Camera is behind plane, hence it's not visible */ | |
| return 0; | |
| } | |
| /* | |
| Back transform front clipping plane into world space, | |
| to give us a point on the line through each corner of the screen | |
| (from the camera). | |
| */ | |
| /* x = -1.0f; y = -1.0f; z = 1.0f; w = 1.0f */ | |
| pvWorld[0].x = VERTTYPEMUL((-pmViewProjInv->f[ 0] - pmViewProjInv->f[ 4] + pmViewProjInv->f[ 8] + pmViewProjInv->f[12]), fFar); | |
| pvWorld[0].y = VERTTYPEMUL((-pmViewProjInv->f[ 1] - pmViewProjInv->f[ 5] + pmViewProjInv->f[ 9] + pmViewProjInv->f[13]), fFar); | |
| pvWorld[0].z = VERTTYPEMUL((-pmViewProjInv->f[ 2] - pmViewProjInv->f[ 6] + pmViewProjInv->f[10] + pmViewProjInv->f[14]), fFar); | |
| /* x = 1.0f, y = -1.0f, z = 1.0f; w = 1.0f */ | |
| pvWorld[1].x = VERTTYPEMUL(( pmViewProjInv->f[ 0] - pmViewProjInv->f[ 4] + pmViewProjInv->f[ 8] + pmViewProjInv->f[12]), fFar); | |
| pvWorld[1].y = VERTTYPEMUL(( pmViewProjInv->f[ 1] - pmViewProjInv->f[ 5] + pmViewProjInv->f[ 9] + pmViewProjInv->f[13]), fFar); | |
| pvWorld[1].z = VERTTYPEMUL(( pmViewProjInv->f[ 2] - pmViewProjInv->f[ 6] + pmViewProjInv->f[10] + pmViewProjInv->f[14]), fFar); | |
| /* x = 1.0f, y = 1.0f, z = 1.0f; w = 1.0f */ | |
| pvWorld[2].x = VERTTYPEMUL(( pmViewProjInv->f[ 0] + pmViewProjInv->f[ 4] + pmViewProjInv->f[ 8] + pmViewProjInv->f[12]), fFar); | |
| pvWorld[2].y = VERTTYPEMUL(( pmViewProjInv->f[ 1] + pmViewProjInv->f[ 5] + pmViewProjInv->f[ 9] + pmViewProjInv->f[13]), fFar); | |
| pvWorld[2].z = VERTTYPEMUL(( pmViewProjInv->f[ 2] + pmViewProjInv->f[ 6] + pmViewProjInv->f[10] + pmViewProjInv->f[14]), fFar); | |
| /* x = -1.0f, y = 1.0f, z = 1.0f; w = 1.0f */ | |
| pvWorld[3].x = VERTTYPEMUL((-pmViewProjInv->f[ 0] + pmViewProjInv->f[ 4] + pmViewProjInv->f[ 8] + pmViewProjInv->f[12]), fFar); | |
| pvWorld[3].y = VERTTYPEMUL((-pmViewProjInv->f[ 1] + pmViewProjInv->f[ 5] + pmViewProjInv->f[ 9] + pmViewProjInv->f[13]), fFar); | |
| pvWorld[3].z = VERTTYPEMUL((-pmViewProjInv->f[ 2] + pmViewProjInv->f[ 6] + pmViewProjInv->f[10] + pmViewProjInv->f[14]), fFar); | |
| /* We need to do a closed loop of the screen vertices, so copy the first vertex into the last */ | |
| pvWorld[4] = pvWorld[0]; | |
| /* | |
| Now build a pre-clipped polygon | |
| */ | |
| /* Lets get ready to loop */ | |
| dwCount = 0; | |
| bClip = false; | |
| pvPolyPtr = (PVRTVECTOR3*)pfVtx; | |
| nVert = 5; | |
| while(nVert) | |
| { | |
| nVert--; | |
| /* | |
| Check which side of the Plane this corner of the far clipping | |
| plane is on. [A,B,C] of plane equation is the plane normal, D is | |
| distance from origin; hence [pvPlane->x * -pvPlane->w, | |
| pvPlane->y * -pvPlane->w, | |
| pvPlane->z * -pvPlane->w] | |
| is a point on the plane | |
| */ | |
| fDotProduct = | |
| VERTTYPEMUL((pvWorld[nVert].x + VERTTYPEMUL(pvPlane->x, pvPlane->w)), pvPlane->x) + | |
| VERTTYPEMUL((pvWorld[nVert].y + VERTTYPEMUL(pvPlane->y, pvPlane->w)), pvPlane->y) + | |
| VERTTYPEMUL((pvWorld[nVert].z + VERTTYPEMUL(pvPlane->z, pvPlane->w)), pvPlane->z); | |
| if(fDotProduct < 0) | |
| { | |
| /* | |
| Behind plane; Vertex does NOT need clipping | |
| */ | |
| if(bClip == true) | |
| { | |
| /* Clipping finished */ | |
| bClip = false; | |
| /* | |
| We've been clipping, so we need to add an additional | |
| point on the line to this point, where clipping was | |
| stopped. | |
| */ | |
| PVRTMiscCalculateIntersectionLinePlane(pvPolyPtr, &pvPlane->x, &pvWorld[nVert+1], &pvWorld[nVert]); | |
| pvPolyPtr = (PVRTVECTOR3*)((char*)pvPolyPtr + nStride); | |
| dwCount++; | |
| } | |
| if(!nVert) | |
| { | |
| /* Abort, abort: we've closed the loop with the clipped point */ | |
| break; | |
| } | |
| /* Add the current point */ | |
| PVRTMiscCalculateIntersectionLinePlane(pvPolyPtr, &pvPlane->x, pFrom, &pvWorld[nVert]); | |
| pvPolyPtr = (PVRTVECTOR3*)((char*)pvPolyPtr + nStride); | |
| dwCount++; | |
| } | |
| else | |
| { | |
| /* | |
| Before plane; Vertex DOES need clipping | |
| */ | |
| if(bClip == true) | |
| { | |
| /* Already in clipping, skip point */ | |
| continue; | |
| } | |
| /* Clipping initiated */ | |
| bClip = true; | |
| /* Don't bother with entry point on first vertex; will take care of it on last vertex (which is a repeat of first vertex) */ | |
| if(nVert != 4) | |
| { | |
| /* We need to add an additional point on the line to this point, where clipping was started */ | |
| PVRTMiscCalculateIntersectionLinePlane(pvPolyPtr, &pvPlane->x, &pvWorld[nVert+1], &pvWorld[nVert]); | |
| pvPolyPtr = (PVRTVECTOR3*)((char*)pvPolyPtr + nStride); | |
| dwCount++; | |
| } | |
| } | |
| } | |
| /* Valid vertex counts are 0, 3, 4, 5 */ | |
| _ASSERT(dwCount <= 5); | |
| _ASSERT(dwCount != 1); | |
| _ASSERT(dwCount != 2); | |
| return dwCount; | |
| } | |
| /*!*************************************************************************** | |
| @Function SetVertex | |
| @Modified Vertices | |
| @Input index | |
| @Input x | |
| @Input y | |
| @Input z | |
| @Description Writes a vertex in a vertex array | |
| *****************************************************************************/ | |
| static void SetVertex(VERTTYPE** Vertices, int index, VERTTYPE x, VERTTYPE y, VERTTYPE z) | |
| { | |
| (*Vertices)[index*3+0] = x; | |
| (*Vertices)[index*3+1] = y; | |
| (*Vertices)[index*3+2] = z; | |
| } | |
| /*!*************************************************************************** | |
| @Function SetUV | |
| @Modified UVs | |
| @Input index | |
| @Input u | |
| @Input v | |
| @Description Writes a texture coordinate in a texture coordinate array | |
| *****************************************************************************/ | |
| static void SetUV(VERTTYPE** UVs, int index, VERTTYPE u, VERTTYPE v) | |
| { | |
| (*UVs)[index*2+0] = u; | |
| (*UVs)[index*2+1] = v; | |
| } | |
| /*!*************************************************************************** | |
| @Function PVRTCreateSkybox | |
| @Input scale Scale the skybox | |
| @Input adjustUV Adjust or not UVs for PVRT compression | |
| @Input textureSize Texture size in pixels | |
| @Output Vertices Array of vertices | |
| @Output UVs Array of UVs | |
| @Description Creates the vertices and texture coordinates for a skybox | |
| *****************************************************************************/ | |
| void PVRTCreateSkybox(float scale, bool adjustUV, int textureSize, VERTTYPE** Vertices, VERTTYPE** UVs) | |
| { | |
| *Vertices = new VERTTYPE[24*3]; | |
| *UVs = new VERTTYPE[24*2]; | |
| VERTTYPE unit = f2vt(1); | |
| VERTTYPE a0 = 0, a1 = unit; | |
| if (adjustUV) | |
| { | |
| VERTTYPE oneover = f2vt(1.0f / textureSize); | |
| a0 = VERTTYPEMUL(f2vt(4.0f), oneover); | |
| a1 = unit - a0; | |
| } | |
| // Front | |
| SetVertex(Vertices, 0, -unit, +unit, -unit); | |
| SetVertex(Vertices, 1, +unit, +unit, -unit); | |
| SetVertex(Vertices, 2, -unit, -unit, -unit); | |
| SetVertex(Vertices, 3, +unit, -unit, -unit); | |
| SetUV(UVs, 0, a0, a1); | |
| SetUV(UVs, 1, a1, a1); | |
| SetUV(UVs, 2, a0, a0); | |
| SetUV(UVs, 3, a1, a0); | |
| // Right | |
| SetVertex(Vertices, 4, +unit, +unit, -unit); | |
| SetVertex(Vertices, 5, +unit, +unit, +unit); | |
| SetVertex(Vertices, 6, +unit, -unit, -unit); | |
| SetVertex(Vertices, 7, +unit, -unit, +unit); | |
| SetUV(UVs, 4, a0, a1); | |
| SetUV(UVs, 5, a1, a1); | |
| SetUV(UVs, 6, a0, a0); | |
| SetUV(UVs, 7, a1, a0); | |
| // Back | |
| SetVertex(Vertices, 8 , +unit, +unit, +unit); | |
| SetVertex(Vertices, 9 , -unit, +unit, +unit); | |
| SetVertex(Vertices, 10, +unit, -unit, +unit); | |
| SetVertex(Vertices, 11, -unit, -unit, +unit); | |
| SetUV(UVs, 8 , a0, a1); | |
| SetUV(UVs, 9 , a1, a1); | |
| SetUV(UVs, 10, a0, a0); | |
| SetUV(UVs, 11, a1, a0); | |
| // Left | |
| SetVertex(Vertices, 12, -unit, +unit, +unit); | |
| SetVertex(Vertices, 13, -unit, +unit, -unit); | |
| SetVertex(Vertices, 14, -unit, -unit, +unit); | |
| SetVertex(Vertices, 15, -unit, -unit, -unit); | |
| SetUV(UVs, 12, a0, a1); | |
| SetUV(UVs, 13, a1, a1); | |
| SetUV(UVs, 14, a0, a0); | |
| SetUV(UVs, 15, a1, a0); | |
| // Top | |
| SetVertex(Vertices, 16, -unit, +unit, +unit); | |
| SetVertex(Vertices, 17, +unit, +unit, +unit); | |
| SetVertex(Vertices, 18, -unit, +unit, -unit); | |
| SetVertex(Vertices, 19, +unit, +unit, -unit); | |
| SetUV(UVs, 16, a0, a1); | |
| SetUV(UVs, 17, a1, a1); | |
| SetUV(UVs, 18, a0, a0); | |
| SetUV(UVs, 19, a1, a0); | |
| // Bottom | |
| SetVertex(Vertices, 20, -unit, -unit, -unit); | |
| SetVertex(Vertices, 21, +unit, -unit, -unit); | |
| SetVertex(Vertices, 22, -unit, -unit, +unit); | |
| SetVertex(Vertices, 23, +unit, -unit, +unit); | |
| SetUV(UVs, 20, a0, a1); | |
| SetUV(UVs, 21, a1, a1); | |
| SetUV(UVs, 22, a0, a0); | |
| SetUV(UVs, 23, a1, a0); | |
| for (int i=0; i<24*3; i++) (*Vertices)[i] = VERTTYPEMUL((*Vertices)[i], f2vt(scale)); | |
| } | |
| /*!*************************************************************************** | |
| @Function PVRTDestroySkybox | |
| @Input Vertices Vertices array to destroy | |
| @Input UVs UVs array to destroy | |
| @Description Destroy the memory allocated for a skybox | |
| *****************************************************************************/ | |
| void PVRTDestroySkybox(VERTTYPE* Vertices, VERTTYPE* UVs) | |
| { | |
| delete [] Vertices; | |
| delete [] UVs; | |
| } | |
| /*!*************************************************************************** | |
| @Function PVRTGetPOTHigher | |
| @Input uiOriginalValue Base value | |
| @Input iTimesHigher Multiplier | |
| @Description When iTimesHigher is one, this function will return the closest | |
| power-of-two value above the base value. | |
| For every increment beyond one for the iTimesHigher value, | |
| the next highest power-of-two value will be calculated. | |
| *****************************************************************************/ | |
| unsigned int PVRTGetPOTHigher(unsigned int uiOriginalValue, int iTimesHigher) | |
| { | |
| if(uiOriginalValue == 0 || iTimesHigher < 0) | |
| { | |
| return 0; | |
| } | |
| unsigned int uiSize = 1; | |
| while (uiSize < uiOriginalValue) uiSize *= 2; | |
| // Keep increasing the POT value until the iTimesHigher value has been met | |
| for(int i = 1 ; i < iTimesHigher; ++i) | |
| { | |
| uiSize *= 2; | |
| } | |
| return uiSize; | |
| } | |
| /*!*************************************************************************** | |
| @Function PVRTGetPOTLower | |
| @Input uiOriginalValue Base value | |
| @Input iTimesLower Multiplier | |
| @Description When iTimesLower is one, this function will return the closest | |
| power-of-two value below the base value. | |
| For every increment beyond one for the iTimesLower value, | |
| the next lowest power-of-two value will be calculated. The lowest | |
| value that can be reached is 1. | |
| *****************************************************************************/ | |
| // NOTE: This function should be optimised | |
| unsigned int PVRTGetPOTLower(unsigned int uiOriginalValue, int iTimesLower) | |
| { | |
| if(uiOriginalValue == 0 || iTimesLower < 0) | |
| { | |
| return 0; | |
| } | |
| unsigned int uiSize = PVRTGetPOTHigher(uiOriginalValue,1); | |
| uiSize >>= 1;//uiSize /=2; | |
| for(int i = 1; i < iTimesLower; ++i) | |
| { | |
| uiSize >>= 1;//uiSize /=2; | |
| if(uiSize == 1) | |
| { | |
| // Lowest possible value has been reached, so break | |
| break; | |
| } | |
| } | |
| return uiSize; | |
| } | |
| /***************************************************************************** | |
| End of file (PVRTMisc.cpp) | |
| *****************************************************************************/ | |