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