|  | /****************************************************************************** | 
|  |  | 
|  | @File         PVRTShadowVol.cpp | 
|  |  | 
|  | @Title        PVRTShadowVol | 
|  |  | 
|  | @Version | 
|  |  | 
|  | @Copyright    Copyright (c) Imagination Technologies Limited. | 
|  |  | 
|  | @Platform     ANSI compatible | 
|  |  | 
|  | @Description  Declarations of functions relating to shadow volume generation. | 
|  |  | 
|  | ******************************************************************************/ | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "PVRTGlobal.h" | 
|  | #include "PVRTContext.h" | 
|  | #include "PVRTFixedPoint.h" | 
|  | #include "PVRTMatrix.h" | 
|  | #include "PVRTTrans.h" | 
|  | #include "PVRTShadowVol.h" | 
|  | #include "PVRTError.h" | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Build options | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Defines | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Macros | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Structures | 
|  | ****************************************************************************/ | 
|  | struct SVertexShVol { | 
|  | float	x, y, z; | 
|  | unsigned int dwExtrude; | 
|  | #if defined(BUILD_OGLES) | 
|  | float fWeight; | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Constants | 
|  | ****************************************************************************/ | 
|  | const static unsigned short c_pwLinesHyperCube[64] = { | 
|  | // Cube0 | 
|  | 0, 1,  2, 3,  0, 2,  1, 3, | 
|  | 4, 5,  6, 7,  4, 6,  5, 7, | 
|  | 0, 4,  1, 5,  2, 6,  3, 7, | 
|  | // Cube1 | 
|  | 8, 9,  10, 11,  8, 10,  9, 11, | 
|  | 12, 13,  14, 15,  12, 14,  13, 15, | 
|  | 8, 12,  9, 13,  10, 14,  11, 15, | 
|  | // Hyper cube jn | 
|  | 0, 8,  1, 9,  2, 10,  3, 11, | 
|  | 4, 12,  5, 13,  6, 14,  7, 15 | 
|  | }; | 
|  | const static PVRTVECTOR3 c_pvRect[4] = { | 
|  | { -1, -1, 1 }, | 
|  | { -1,  1, 1 }, | 
|  | {  1, -1, 1 }, | 
|  | {  1,  1, 1 } | 
|  | }; | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Shared globals | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Globals | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Declarations | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Code | 
|  | ****************************************************************************/ | 
|  | /**************************************************************************** | 
|  | @Function		FindOrCreateVertex | 
|  | @Modified		psMesh				The mesh to check against/add to | 
|  | @Input			pV					The vertex to compare/add | 
|  | @Return			unsigned short		The array index of the vertex | 
|  | @Description	Searches through the mesh data to see if the vertex has | 
|  | already been used. If it has, the array index of the vertex | 
|  | is returned. If the mesh does not already use the vertex, | 
|  | it is appended to the vertex array and the array count is incremented. | 
|  | The index in the array of the new vertex is then returned. | 
|  | ****************************************************************************/ | 
|  | static unsigned short FindOrCreateVertex(PVRTShadowVolShadowMesh * const psMesh, const PVRTVECTOR3 * const pV) { | 
|  | unsigned short	wCurr; | 
|  |  | 
|  | /* | 
|  | First check whether we already have a vertex here | 
|  | */ | 
|  | for(wCurr = 0; wCurr < psMesh->nV; wCurr++) { | 
|  | if(memcmp(&psMesh->pV[wCurr], pV, sizeof(*pV)) == 0) { | 
|  | /* Don't do anything more if the vertex already exists */ | 
|  | return wCurr; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Add the vertex then! | 
|  | */ | 
|  | psMesh->pV[psMesh->nV] = *pV; | 
|  |  | 
|  | return (unsigned short) psMesh->nV++; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | @Function		FindOrCreateEdge | 
|  | @Modified		psMesh				The mesh to check against/add to | 
|  | @Input			pv0					The first point that defines the edge | 
|  | @Input			pv1					The second point that defines the edge | 
|  | @Return			PVRTShadowVolMEdge	The index of the found/created edge in the | 
|  | mesh's array | 
|  | @Description	Searches through the mesh data to see if the edge has | 
|  | already been used. If it has, the array index of the edge | 
|  | is returned. If the mesh does not already use the edge, | 
|  | it is appended to the edge array and the array cound is incremented. | 
|  | The index in the array of the new edge is then returned. | 
|  | ****************************************************************************/ | 
|  | static unsigned int FindOrCreateEdge(PVRTShadowVolShadowMesh * const psMesh, const PVRTVECTOR3 * const pv0, const PVRTVECTOR3 * const pv1) { | 
|  | unsigned int	nCurr; | 
|  | unsigned short			wV0, wV1; | 
|  |  | 
|  | wV0 = FindOrCreateVertex(psMesh, pv0); | 
|  | wV1 = FindOrCreateVertex(psMesh, pv1); | 
|  |  | 
|  |  | 
|  | /* | 
|  | First check whether we already have a edge here | 
|  | */ | 
|  | for(nCurr = 0; nCurr < psMesh->nE; nCurr++) { | 
|  | if( | 
|  | (psMesh->pE[nCurr].wV0 == wV0 && psMesh->pE[nCurr].wV1 == wV1) || | 
|  | (psMesh->pE[nCurr].wV0 == wV1 && psMesh->pE[nCurr].wV1 == wV0)) | 
|  | { | 
|  | /* Don't do anything more if the edge already exists */ | 
|  | return nCurr; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Add the edge then! | 
|  | */ | 
|  | psMesh->pE[psMesh->nE].wV0	= wV0; | 
|  | psMesh->pE[psMesh->nE].wV1	= wV1; | 
|  | psMesh->pE[psMesh->nE].nVis	= 0; | 
|  |  | 
|  | return psMesh->nE++; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | @Function		CrossProduct | 
|  | @Output			pvOut			The resultant vector | 
|  | @Input			pv0				Vector zero | 
|  | @Input			pv1				Vector one | 
|  | @Input			pv2				Vector two | 
|  | @Description	Finds the vector between vector zero and vector one, | 
|  | and the vector between vector zero and vector two. | 
|  | These two resultant vectors are then multiplied together | 
|  | and the result is assigned to the output vector. | 
|  | ****************************************************************************/ | 
|  | static void CrossProduct( | 
|  | PVRTVECTOR3 * const pvOut, | 
|  | const PVRTVECTOR3 * const pv0, | 
|  | const PVRTVECTOR3 * const pv1, | 
|  | const PVRTVECTOR3 * const pv2) | 
|  | { | 
|  | PVRTVECTOR3 v0, v1; | 
|  |  | 
|  | v0.x = pv1->x - pv0->x; | 
|  | v0.y = pv1->y - pv0->y; | 
|  | v0.z = pv1->z - pv0->z; | 
|  |  | 
|  | v1.x = pv2->x - pv0->x; | 
|  | v1.y = pv2->y - pv0->y; | 
|  | v1.z = pv2->z - pv0->z; | 
|  |  | 
|  | PVRTMatrixVec3CrossProduct(*pvOut, v0, v1); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | @Function		FindOrCreateTriangle | 
|  | @Modified		psMesh			The mesh to check against/add to | 
|  | @Input			pv0				Vertex zero | 
|  | @Input			pv1				Vertex one | 
|  | @Input			pv2				Vertex two | 
|  | @Description	Searches through the mesh data to see if the triangle has | 
|  | already been used. If it has, the function returns. | 
|  | If the mesh does not already use the triangle, | 
|  | it is appended to the triangle array and the array cound is incremented. | 
|  | ****************************************************************************/ | 
|  | static void FindOrCreateTriangle( | 
|  | PVRTShadowVolShadowMesh	* const psMesh, | 
|  | const PVRTVECTOR3	* const pv0, | 
|  | const PVRTVECTOR3	* const pv1, | 
|  | const PVRTVECTOR3	* const pv2) | 
|  | { | 
|  | unsigned int	nCurr; | 
|  | PVRTShadowVolMEdge	*psE0, *psE1, *psE2; | 
|  | unsigned int wE0, wE1, wE2; | 
|  |  | 
|  | wE0 = FindOrCreateEdge(psMesh, pv0, pv1); | 
|  | wE1 = FindOrCreateEdge(psMesh, pv1, pv2); | 
|  | wE2 = FindOrCreateEdge(psMesh, pv2, pv0); | 
|  |  | 
|  | if(wE0 == wE1 || wE1 == wE2 || wE2 == wE0) { | 
|  | /* Don't add degenerate triangles */ | 
|  | _RPT0(_CRT_WARN, "FindOrCreateTriangle() Degenerate triangle.\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | First check whether we already have a triangle here | 
|  | */ | 
|  | for(nCurr = 0; nCurr < psMesh->nT; nCurr++) { | 
|  | if( | 
|  | (psMesh->pT[nCurr].wE0 == wE0 || psMesh->pT[nCurr].wE0 == wE1 || psMesh->pT[nCurr].wE0 == wE2) && | 
|  | (psMesh->pT[nCurr].wE1 == wE0 || psMesh->pT[nCurr].wE1 == wE1 || psMesh->pT[nCurr].wE1 == wE2) && | 
|  | (psMesh->pT[nCurr].wE2 == wE0 || psMesh->pT[nCurr].wE2 == wE1 || psMesh->pT[nCurr].wE2 == wE2)) | 
|  | { | 
|  | /* Don't do anything more if the triangle already exists */ | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Add the triangle then! | 
|  | */ | 
|  | psMesh->pT[psMesh->nT].wE0 = wE0; | 
|  | psMesh->pT[psMesh->nT].wE1 = wE1; | 
|  | psMesh->pT[psMesh->nT].wE2 = wE2; | 
|  |  | 
|  | psE0 = &psMesh->pE[wE0]; | 
|  | psE1 = &psMesh->pE[wE1]; | 
|  | psE2 = &psMesh->pE[wE2]; | 
|  |  | 
|  | /* | 
|  | Store the triangle indices; these are indices into the shadow mesh, not the source model indices | 
|  | */ | 
|  | if(psE0->wV0 == psE1->wV0 || psE0->wV0 == psE1->wV1) | 
|  | psMesh->pT[psMesh->nT].w[0] = psE0->wV1; | 
|  | else | 
|  | psMesh->pT[psMesh->nT].w[0] = psE0->wV0; | 
|  |  | 
|  | if(psE1->wV0 == psE2->wV0 || psE1->wV0 == psE2->wV1) | 
|  | psMesh->pT[psMesh->nT].w[1] = psE1->wV1; | 
|  | else | 
|  | psMesh->pT[psMesh->nT].w[1] = psE1->wV0; | 
|  |  | 
|  | if(psE2->wV0 == psE0->wV0 || psE2->wV0 == psE0->wV1) | 
|  | psMesh->pT[psMesh->nT].w[2] = psE2->wV1; | 
|  | else | 
|  | psMesh->pT[psMesh->nT].w[2] = psE2->wV0; | 
|  |  | 
|  | /* Calculate the triangle normal */ | 
|  | CrossProduct(&psMesh->pT[psMesh->nT].vNormal, pv0, pv1, pv2); | 
|  |  | 
|  | /* Check which edges have the correct winding order for this triangle */ | 
|  | psMesh->pT[psMesh->nT].nWinding = 0; | 
|  | if(memcmp(&psMesh->pV[psE0->wV0], pv0, sizeof(*pv0)) == 0) psMesh->pT[psMesh->nT].nWinding |= 0x01; | 
|  | if(memcmp(&psMesh->pV[psE1->wV0], pv1, sizeof(*pv1)) == 0) psMesh->pT[psMesh->nT].nWinding |= 0x02; | 
|  | if(memcmp(&psMesh->pV[psE2->wV0], pv2, sizeof(*pv2)) == 0) psMesh->pT[psMesh->nT].nWinding |= 0x04; | 
|  |  | 
|  | psMesh->nT++; | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function	PVRTShadowVolMeshCreateMesh | 
|  | @Modified	psMesh		The shadow volume mesh to populate | 
|  | @Input		pVertex		A list of vertices | 
|  | @Input		nNumVertex	The number of vertices | 
|  | @Input		pFaces		A list of faces | 
|  | @Input		nNumFaces	The number of faces | 
|  | @Description	Creates a mesh format suitable for generating shadow volumes | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolMeshCreateMesh( | 
|  | PVRTShadowVolShadowMesh		* const psMesh, | 
|  | const float				* const pVertex, | 
|  | const unsigned int		nNumVertex, | 
|  | const unsigned short	* const pFaces, | 
|  | const unsigned int		nNumFaces) | 
|  | { | 
|  | unsigned int	nCurr; | 
|  |  | 
|  | /* | 
|  | Prep the structure to return | 
|  | */ | 
|  | memset(psMesh, 0, sizeof(*psMesh)); | 
|  |  | 
|  | /* | 
|  | Allocate some working space to find the unique vertices | 
|  | */ | 
|  | psMesh->pV = (PVRTVECTOR3*)malloc(nNumVertex * sizeof(*psMesh->pV)); | 
|  | psMesh->pE = (PVRTShadowVolMEdge*)malloc(nNumFaces * sizeof(*psMesh->pE) * 3); | 
|  | psMesh->pT = (PVRTShadowVolMTriangle*)malloc(nNumFaces * sizeof(*psMesh->pT)); | 
|  | _ASSERT(psMesh->pV); | 
|  | _ASSERT(psMesh->pE); | 
|  | _ASSERT(psMesh->pT); | 
|  |  | 
|  | for(nCurr = 0; nCurr < nNumFaces; nCurr++) { | 
|  | FindOrCreateTriangle(psMesh, | 
|  | (PVRTVECTOR3*)&pVertex[3 * pFaces[3 * nCurr + 0]], | 
|  | (PVRTVECTOR3*)&pVertex[3 * pFaces[3 * nCurr + 1]], | 
|  | (PVRTVECTOR3*)&pVertex[3 * pFaces[3 * nCurr + 2]]); | 
|  | } | 
|  |  | 
|  | _ASSERT(psMesh->nV <= nNumVertex); | 
|  | _ASSERT(psMesh->nE < nNumFaces * 3); | 
|  | _ASSERT(psMesh->nT == nNumFaces); | 
|  |  | 
|  | _RPT2(_CRT_WARN, "Unique vertices : %d (from %d)\n", psMesh->nV, nNumVertex); | 
|  | _RPT2(_CRT_WARN, "Unique edges    : %d (from %d)\n", psMesh->nE, nNumFaces * 3); | 
|  | _RPT2(_CRT_WARN, "Unique triangles: %d (from %d)\n", psMesh->nT, nNumFaces); | 
|  |  | 
|  | /* | 
|  | Create the real unique lists | 
|  | */ | 
|  | psMesh->pV = (PVRTVECTOR3*)realloc(psMesh->pV, psMesh->nV * sizeof(*psMesh->pV)); | 
|  | psMesh->pE = (PVRTShadowVolMEdge*)realloc(psMesh->pE, psMesh->nE * sizeof(*psMesh->pE)); | 
|  | psMesh->pT = (PVRTShadowVolMTriangle*)realloc(psMesh->pT, psMesh->nT * sizeof(*psMesh->pT)); | 
|  | _ASSERT(psMesh->pV); | 
|  | _ASSERT(psMesh->pE); | 
|  | _ASSERT(psMesh->pT); | 
|  |  | 
|  | #if defined(_DEBUG) && !defined(_UNICODE) && defined(_WIN32) | 
|  | /* | 
|  | Check we have sensible model data | 
|  | */ | 
|  | { | 
|  | unsigned int nTri, nEdge; | 
|  | PVRTERROR_OUTPUT_DEBUG("ShadowMeshCreate() Sanity check..."); | 
|  |  | 
|  | for(nEdge = 0; nEdge < psMesh->nE; nEdge++) { | 
|  | nCurr = 0; | 
|  |  | 
|  | for(nTri = 0; nTri < psMesh->nT; nTri++) { | 
|  | if(psMesh->pT[nTri].wE0 == nEdge) | 
|  | nCurr++; | 
|  |  | 
|  | if(psMesh->pT[nTri].wE1 == nEdge) | 
|  | nCurr++; | 
|  |  | 
|  | if(psMesh->pT[nTri].wE2 == nEdge) | 
|  | nCurr++; | 
|  | } | 
|  |  | 
|  | /* | 
|  | Every edge should be referenced exactly twice. | 
|  | If they aren't then the mesh isn't closed which will cause problems when rendering the shadows. | 
|  | */ | 
|  | _ASSERTE(nCurr == 2); | 
|  | } | 
|  |  | 
|  | PVRTERROR_OUTPUT_DEBUG("done.\n"); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolMeshInitMesh | 
|  | @Input			psMesh	The shadow volume mesh | 
|  | @Input			pContext	A struct for API specific data | 
|  | @Returns 		True on success | 
|  | @Description	Init the mesh | 
|  | *************************************************************************/ | 
|  | bool PVRTShadowVolMeshInitMesh( | 
|  | PVRTShadowVolShadowMesh		* const psMesh, | 
|  | const SPVRTContext		* const pContext) | 
|  | { | 
|  | unsigned int	nCurr; | 
|  | #if defined(BUILD_DX11) | 
|  | HRESULT			hRes; | 
|  | #endif | 
|  | SVertexShVol	*pvData; | 
|  |  | 
|  | #if defined(BUILD_OGL) | 
|  | _ASSERT(pContext && pContext->pglExt); | 
|  |  | 
|  | if(!pContext || !pContext->pglExt) | 
|  | return false; | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_OGLES2) || defined(BUILD_OGLES) || defined(BUILD_OGLES3) | 
|  | PVRT_UNREFERENCED_PARAMETER(pContext); | 
|  | #endif | 
|  | _ASSERT(psMesh); | 
|  | _ASSERT(psMesh->nV); | 
|  | _ASSERT(psMesh->nE); | 
|  | _ASSERT(psMesh->nT); | 
|  |  | 
|  | /* | 
|  | Allocate a vertex buffer for the shadow volumes | 
|  | */ | 
|  | _ASSERT(psMesh->pivb == NULL); | 
|  | _RPT3(_CRT_WARN, "ShadowMeshInitMesh() %5d byte VB (%3dv x 2 x size(%d))\n", psMesh->nV * 2 * sizeof(*pvData), psMesh->nV, sizeof(*pvData)); | 
|  |  | 
|  | #if defined(BUILD_DX11) | 
|  | D3D11_BUFFER_DESC sVBBufferDesc; | 
|  | sVBBufferDesc.ByteWidth		= psMesh->nV * 2 * 3 * sizeof(*pvData); | 
|  | sVBBufferDesc.Usage			= D3D11_USAGE_DYNAMIC; | 
|  | sVBBufferDesc.BindFlags		= D3D11_BIND_VERTEX_BUFFER; | 
|  | sVBBufferDesc.CPUAccessFlags= 0; | 
|  | sVBBufferDesc.MiscFlags		= 0; | 
|  |  | 
|  | hRes = pContext->pDev->CreateBuffer(&sVBBufferDesc, NULL, &psMesh->pivb) != S_OK; | 
|  |  | 
|  | if(FAILED(hRes)) | 
|  | { | 
|  | _ASSERT(false); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | D3D11_MAPPED_SUBRESOURCE data; | 
|  | ID3D11DeviceContext *pDeviceContext = 0; | 
|  | pContext->pDev->GetImmediateContext(&pDeviceContext); | 
|  | hRes = pDeviceContext->Map(psMesh->pivb, 0, D3D11_MAP_WRITE_DISCARD, NULL, &data); | 
|  |  | 
|  | if(FAILED(hRes)) | 
|  | { | 
|  | _ASSERT(false); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | pvData = (SVertexShVol*) data.pData; | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_OGL) | 
|  | _ASSERT(pContext && pContext->pglExt); | 
|  | if (!pContext || !pContext->pglExt) | 
|  | return false; | 
|  | pContext->pglExt->glGenBuffersARB(1, &psMesh->pivb); | 
|  | pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, psMesh->pivb); | 
|  | pContext->pglExt->glBufferDataARB(GL_ARRAY_BUFFER_ARB, psMesh->nV * 2 * sizeof(*pvData), NULL, GL_STREAM_DRAW_ARB); | 
|  | pvData = (SVertexShVol*)pContext->pglExt->glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) | 
|  | psMesh->pivb = malloc(psMesh->nV * 2 * sizeof(*pvData)); | 
|  | pvData = (SVertexShVol*)psMesh->pivb; | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | Fill the vertex buffer with two subtly different copies of the vertices | 
|  | */ | 
|  | for(nCurr = 0; nCurr < psMesh->nV; ++nCurr) | 
|  | { | 
|  | pvData[nCurr].x			= psMesh->pV[nCurr].x; | 
|  | pvData[nCurr].y			= psMesh->pV[nCurr].y; | 
|  | pvData[nCurr].z			= psMesh->pV[nCurr].z; | 
|  | pvData[nCurr].dwExtrude = 0; | 
|  |  | 
|  | #if defined(BUILD_OGLES) | 
|  | pvData[nCurr].fWeight = 1; | 
|  | pvData[nCurr + psMesh->nV].fWeight = 1; | 
|  | #endif | 
|  | pvData[nCurr + psMesh->nV]				= pvData[nCurr]; | 
|  | pvData[nCurr + psMesh->nV].dwExtrude	= 0x04030201;		// Order is wzyx | 
|  | } | 
|  |  | 
|  | #if defined(BUILD_OGL) | 
|  | pContext->pglExt->glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); | 
|  | pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_DX11) | 
|  | pDeviceContext->Unmap(psMesh->pivb, 0); | 
|  | #endif | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolMeshInitVol | 
|  | @Modified		psVol	The shadow volume struct | 
|  | @Input			psMesh	The shadow volume mesh | 
|  | @Input			pContext	A struct for API specific data | 
|  | @Returns		True on success | 
|  | @Description	Init the renderable shadow volume information. | 
|  | *************************************************************************/ | 
|  | bool PVRTShadowVolMeshInitVol( | 
|  | PVRTShadowVolShadowVol			* const psVol, | 
|  | const PVRTShadowVolShadowMesh	* const psMesh, | 
|  | const SPVRTContext		* const pContext) | 
|  | { | 
|  | #if defined(BUILD_DX11) | 
|  | HRESULT hRes; | 
|  | #endif | 
|  | #if defined(BUILD_OGLES2) || defined(BUILD_OGLES) || defined(BUILD_OGL) || defined(BUILD_OGLES3) | 
|  | PVRT_UNREFERENCED_PARAMETER(pContext); | 
|  | #endif | 
|  | _ASSERT(psVol); | 
|  | _ASSERT(psMesh); | 
|  | _ASSERT(psMesh->nV); | 
|  | _ASSERT(psMesh->nE); | 
|  | _ASSERT(psMesh->nT); | 
|  |  | 
|  | _RPT1(_CRT_WARN, "ShadowMeshInitVol() %5lu byte IB\n", psMesh->nT * 2 * 3 * sizeof(unsigned short)); | 
|  |  | 
|  | /* | 
|  | Allocate a index buffer for the shadow volumes | 
|  | */ | 
|  | #if defined(_DEBUG) | 
|  | psVol->nIdxCntMax = psMesh->nT * 2 * 3; | 
|  | #endif | 
|  | #if defined(BUILD_DX11) | 
|  | D3D11_BUFFER_DESC sIdxBuferDesc; | 
|  | sIdxBuferDesc.ByteWidth		= psMesh->nT * 2 * 3 * sizeof(unsigned short); | 
|  | sIdxBuferDesc.Usage			= D3D11_USAGE_DYNAMIC; | 
|  | sIdxBuferDesc.BindFlags		= D3D11_BIND_INDEX_BUFFER; | 
|  | sIdxBuferDesc.CPUAccessFlags= 0; | 
|  | sIdxBuferDesc.MiscFlags		= 0; | 
|  |  | 
|  | hRes = pContext->pDev->CreateBuffer(&sIdxBuferDesc, NULL, &psVol->piib) != S_OK; | 
|  |  | 
|  | if(FAILED(hRes)) { | 
|  | _ASSERT(false); | 
|  | return false; | 
|  | } | 
|  | #endif | 
|  | #if defined(BUILD_OGL) | 
|  | _ASSERT(pContext && pContext->pglExt); | 
|  | if (!pContext || !pContext->pglExt) | 
|  | return false; | 
|  | pContext->pglExt->glGenBuffersARB(1, &psVol->piib); | 
|  | pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, psVol->piib); | 
|  | pContext->pglExt->glBufferDataARB(GL_ARRAY_BUFFER_ARB, psMesh->nT * 2 * 3 * sizeof(unsigned short), NULL, GL_STREAM_DRAW_ARB); | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) | 
|  | psVol->piib = (unsigned short*)malloc(psMesh->nT * 2 * 3 * sizeof(unsigned short)); | 
|  | #endif | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolMeshDestroyMesh | 
|  | @Input			psMesh	The shadow volume mesh to destroy | 
|  | @Description	Destroys all shadow volume mesh data created by PVRTShadowVolMeshCreateMesh | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolMeshDestroyMesh( | 
|  | PVRTShadowVolShadowMesh		* const psMesh) | 
|  | { | 
|  | FREE(psMesh->pV); | 
|  | FREE(psMesh->pE); | 
|  | FREE(psMesh->pT); | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolMeshReleaseMesh | 
|  | @Input			psMesh	The shadow volume mesh to release | 
|  | @Description	Releases all shadow volume mesh data created by PVRTShadowVolMeshInitMesh | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolMeshReleaseMesh( | 
|  | PVRTShadowVolShadowMesh		* const psMesh, | 
|  | SPVRTContext				* const psContext) | 
|  | { | 
|  | #if defined(BUILD_OGL) | 
|  | _ASSERT(psContext && psContext->pglExt); | 
|  | if (!psContext || !psContext->pglExt) | 
|  | return; | 
|  | psContext->pglExt->glDeleteBuffersARB(1, &psMesh->pivb); | 
|  | #endif | 
|  | #if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) | 
|  | PVRT_UNREFERENCED_PARAMETER(psContext); | 
|  | FREE(psMesh->pivb); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolMeshReleaseVol | 
|  | @Input			psVol	The shadow volume information to release | 
|  | @Description	Releases all data create by PVRTShadowVolMeshInitVol | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolMeshReleaseVol( | 
|  | PVRTShadowVolShadowVol			* const psVol, | 
|  | SPVRTContext					* const psContext) | 
|  | { | 
|  | #if defined(BUILD_OGL) | 
|  | _ASSERT(psContext && psContext->pglExt); | 
|  | if (!psContext || !psContext->pglExt) | 
|  | return; | 
|  | psContext->pglExt->glDeleteBuffersARB(1, &psVol->piib); | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) | 
|  | PVRT_UNREFERENCED_PARAMETER(psContext); | 
|  | FREE(psVol->piib); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolSilhouetteProjectedBuild | 
|  | @Modified		psVol	The shadow volume information | 
|  | @Input			dwVisFlags	Shadow volume creation flags | 
|  | @Input			psMesh	The shadow volume mesh | 
|  | @Input			pvLightModel	The light position/direction | 
|  | @Input			bPointLight		Is the light a point light | 
|  | @Input			pContext	A struct for passing in API specific data | 
|  | @Description	Using the light set up the shadow volume so it can be extruded. | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolSilhouetteProjectedBuild( | 
|  | PVRTShadowVolShadowVol			* const psVol, | 
|  | const unsigned int		dwVisFlags, | 
|  | const PVRTShadowVolShadowMesh	* const psMesh, | 
|  | const PVRTVec3		* const pvLightModel, | 
|  | const bool				bPointLight, | 
|  | const SPVRTContext * const pContext) | 
|  | { | 
|  | PVRTShadowVolSilhouetteProjectedBuild(psVol, dwVisFlags,psMesh, (PVRTVECTOR3*) pvLightModel, bPointLight, pContext); | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolSilhouetteProjectedBuild | 
|  | @Modified		psVol	The shadow volume information | 
|  | @Input			dwVisFlags	Shadow volume creation flags | 
|  | @Input			psMesh	The shadow volume mesh | 
|  | @Input			pvLightModel	The light position/direction | 
|  | @Input			bPointLight		Is the light a point light | 
|  | @Input			pContext	A struct for passing in API specific data | 
|  | @Description	Using the light set up the shadow volume so it can be extruded. | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolSilhouetteProjectedBuild( | 
|  | PVRTShadowVolShadowVol			* const psVol, | 
|  | const unsigned int		dwVisFlags, | 
|  | const PVRTShadowVolShadowMesh	* const psMesh, | 
|  | const PVRTVECTOR3		* const pvLightModel, | 
|  | const bool				bPointLight, | 
|  | const SPVRTContext * const pContext) | 
|  | { | 
|  | PVRTVECTOR3		v; | 
|  | PVRTShadowVolMTriangle	*psTri; | 
|  | PVRTShadowVolMEdge		*psEdge; | 
|  | unsigned short	*pwIdx; | 
|  | #if defined(BUILD_DX11) | 
|  | HRESULT			hRes; | 
|  | #endif | 
|  | unsigned int	nCurr; | 
|  | float			f; | 
|  |  | 
|  | /* | 
|  | Lock the index buffer; this is where we create the shadow volume | 
|  | */ | 
|  | _ASSERT(psVol && psVol->piib); | 
|  | #if defined(BUILD_OGL) || defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) | 
|  | PVRT_UNREFERENCED_PARAMETER(pContext); | 
|  | #endif | 
|  | #if defined(BUILD_DX11) | 
|  | _ASSERT(pContext); | 
|  |  | 
|  | if(!pContext) | 
|  | return; | 
|  |  | 
|  | D3D11_MAPPED_SUBRESOURCE data; | 
|  | ID3D11DeviceContext *pDeviceContext = 0; | 
|  | pContext->pDev->GetImmediateContext(&pDeviceContext); | 
|  | hRes = pDeviceContext->Map(psVol->piib, 0, D3D11_MAP_WRITE_DISCARD, NULL, &data); | 
|  | pwIdx = (unsigned short*) data.pData; | 
|  |  | 
|  | _ASSERT(SUCCEEDED(hRes)); | 
|  | #endif | 
|  | #if defined(BUILD_OGL) | 
|  | _ASSERT(pContext && pContext->pglExt); | 
|  | if (!pContext || !pContext->pglExt) | 
|  | return; | 
|  |  | 
|  | pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, psVol->piib); | 
|  | pwIdx = (unsigned short*)pContext->pglExt->glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); | 
|  | #endif | 
|  | #if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3) | 
|  | pwIdx = psVol->piib; | 
|  | #endif | 
|  |  | 
|  | psVol->nIdxCnt = 0; | 
|  |  | 
|  | // Run through triangles, testing which face the From point | 
|  | for(nCurr = 0; nCurr < psMesh->nT; ++nCurr) | 
|  | { | 
|  | PVRTShadowVolMEdge *pE0, *pE1, *pE2; | 
|  | psTri = &psMesh->pT[nCurr]; | 
|  | pE0 = &psMesh->pE[psTri->wE0]; | 
|  | pE1 = &psMesh->pE[psTri->wE1]; | 
|  | pE2 = &psMesh->pE[psTri->wE2]; | 
|  |  | 
|  | if(bPointLight) { | 
|  | v.x = psMesh->pV[pE0->wV0].x - pvLightModel->x; | 
|  | v.y = psMesh->pV[pE0->wV0].y - pvLightModel->y; | 
|  | v.z = psMesh->pV[pE0->wV0].z - pvLightModel->z; | 
|  | f = PVRTMatrixVec3DotProduct(psTri->vNormal, v); | 
|  | } else { | 
|  | f = PVRTMatrixVec3DotProduct(psTri->vNormal, *pvLightModel); | 
|  | } | 
|  |  | 
|  | if(f >= 0) { | 
|  | /* Triangle is in the light */ | 
|  | pE0->nVis |= 0x01; | 
|  | pE1->nVis |= 0x01; | 
|  | pE2->nVis |= 0x01; | 
|  |  | 
|  | if(dwVisFlags & PVRTSHADOWVOLUME_NEED_CAP_FRONT) | 
|  | { | 
|  | // Add the triangle to the volume, unextruded. | 
|  | pwIdx[psVol->nIdxCnt+0] = psTri->w[0]; | 
|  | pwIdx[psVol->nIdxCnt+1] = psTri->w[1]; | 
|  | pwIdx[psVol->nIdxCnt+2] = psTri->w[2]; | 
|  | psVol->nIdxCnt += 3; | 
|  | } | 
|  | } else { | 
|  | /* Triangle is in shade; set Bit3 if the winding order needs reversed */ | 
|  | pE0->nVis |= 0x02 | (psTri->nWinding & 0x01) << 2; | 
|  | pE1->nVis |= 0x02 | (psTri->nWinding & 0x02) << 1; | 
|  | pE2->nVis |= 0x02 | (psTri->nWinding & 0x04); | 
|  |  | 
|  | if(dwVisFlags & PVRTSHADOWVOLUME_NEED_CAP_BACK) { | 
|  | // Add the triangle to the volume, extruded. | 
|  | // psMesh->nV is used as an offst so that the new index refers to the | 
|  | // corresponding position in the second array of vertices (which are extruded) | 
|  | pwIdx[psVol->nIdxCnt+0] = (unsigned short) psMesh->nV + psTri->w[0]; | 
|  | pwIdx[psVol->nIdxCnt+1] = (unsigned short) psMesh->nV + psTri->w[1]; | 
|  | pwIdx[psVol->nIdxCnt+2] = (unsigned short) psMesh->nV + psTri->w[2]; | 
|  | psVol->nIdxCnt += 3; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(_DEBUG) | 
|  | _ASSERT(psVol->nIdxCnt <= psVol->nIdxCntMax); | 
|  | for(nCurr = 0; nCurr < psVol->nIdxCnt; ++nCurr) { | 
|  | _ASSERT(pwIdx[nCurr] < psMesh->nV*2); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | Run through edges, testing which are silhouette edges | 
|  | */ | 
|  | for(nCurr = 0; nCurr < psMesh->nE; nCurr++) { | 
|  | psEdge = &psMesh->pE[nCurr]; | 
|  |  | 
|  | if((psEdge->nVis & 0x03) == 0x03) { | 
|  | /* | 
|  | Silhouette edge found! | 
|  | The edge is both visible and hidden, | 
|  | so it is along the silhouette of the model | 
|  | (See header notes for more info) | 
|  | */ | 
|  | if(psEdge->nVis & 0x04) { | 
|  | pwIdx[psVol->nIdxCnt+0] = psEdge->wV0; | 
|  | pwIdx[psVol->nIdxCnt+1] = psEdge->wV1; | 
|  | pwIdx[psVol->nIdxCnt+2] = psEdge->wV0 + (unsigned short) psMesh->nV; | 
|  |  | 
|  | pwIdx[psVol->nIdxCnt+3] = psEdge->wV0 + (unsigned short) psMesh->nV; | 
|  | pwIdx[psVol->nIdxCnt+4] = psEdge->wV1; | 
|  | pwIdx[psVol->nIdxCnt+5] = psEdge->wV1 + (unsigned short) psMesh->nV; | 
|  | } else { | 
|  | pwIdx[psVol->nIdxCnt+0] = psEdge->wV1; | 
|  | pwIdx[psVol->nIdxCnt+1] = psEdge->wV0; | 
|  | pwIdx[psVol->nIdxCnt+2] = psEdge->wV1 + (unsigned short) psMesh->nV; | 
|  |  | 
|  | pwIdx[psVol->nIdxCnt+3] = psEdge->wV1 + (unsigned short) psMesh->nV; | 
|  | pwIdx[psVol->nIdxCnt+4] = psEdge->wV0; | 
|  | pwIdx[psVol->nIdxCnt+5] = psEdge->wV0 + (unsigned short) psMesh->nV; | 
|  | } | 
|  |  | 
|  | psVol->nIdxCnt += 6; | 
|  | } | 
|  |  | 
|  | /* Zero for next render */ | 
|  | psEdge->nVis = 0; | 
|  | } | 
|  |  | 
|  | #if defined(_DEBUG) | 
|  | _ASSERT(psVol->nIdxCnt <= psVol->nIdxCntMax); | 
|  | for(nCurr = 0; nCurr < psVol->nIdxCnt; ++nCurr) { | 
|  | _ASSERT(pwIdx[nCurr] < psMesh->nV*2); | 
|  | } | 
|  | #endif | 
|  | #if defined(BUILD_OGL) | 
|  | pContext->pglExt->glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB); | 
|  | pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_DX11) | 
|  | pDeviceContext->Unmap(psVol->piib, 0); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		IsBoundingBoxVisibleEx | 
|  | @Input			pBoundingHyperCube	The hypercube to test against | 
|  | @Input			fCamZ				The camera's position along the z-axis | 
|  | @Return			bool				Returns true if the bounding box is visible | 
|  | @Description	This method tests the bounding box's position against | 
|  | the camera's position to determine if it is visible. | 
|  | If it is visible, the function returns true. | 
|  | *************************************************************************/ | 
|  | static bool IsBoundingBoxVisibleEx( | 
|  | const PVRTVECTOR4	* const pBoundingHyperCube, | 
|  | const float			fCamZ) | 
|  | { | 
|  | PVRTVECTOR3	v, vShift[16]; | 
|  | unsigned int		dwClipFlags; | 
|  | int			i, j; | 
|  | unsigned short		w0, w1; | 
|  |  | 
|  | dwClipFlags = 0;		// Assume all are off-screen | 
|  |  | 
|  | i = 8; | 
|  | while(i) | 
|  | { | 
|  | i--; | 
|  |  | 
|  | if(pBoundingHyperCube[i].x <  pBoundingHyperCube[i].w) | 
|  | dwClipFlags |= 1 << 0; | 
|  |  | 
|  | if(pBoundingHyperCube[i].x > -pBoundingHyperCube[i].w) | 
|  | dwClipFlags |= 1 << 1; | 
|  |  | 
|  | if(pBoundingHyperCube[i].y <  pBoundingHyperCube[i].w) | 
|  | dwClipFlags |= 1 << 2; | 
|  |  | 
|  | if(pBoundingHyperCube[i].y > -pBoundingHyperCube[i].w) | 
|  | dwClipFlags |= 1 << 3; | 
|  |  | 
|  | if(pBoundingHyperCube[i].z > 0) | 
|  | dwClipFlags |= 1 << 4; | 
|  | } | 
|  |  | 
|  | /* | 
|  | Volume is hidden if all the vertices are over a screen edge | 
|  | */ | 
|  | if(dwClipFlags != 0x1F) | 
|  | return false; | 
|  |  | 
|  | /* | 
|  | Well, according to the simple bounding box check, it might be | 
|  | visible. Let's now test the view frustrum against the bounding | 
|  | cube. (Basically the reverse of the previous test!) | 
|  |  | 
|  | This catches those cases where a diagonal cube passes near a | 
|  | screen edge. | 
|  | */ | 
|  |  | 
|  | // Subtract the camera position from the vertices. I.e. move the camera to 0,0,0 | 
|  | for(i = 0; i < 8; ++i) { | 
|  | vShift[i].x = pBoundingHyperCube[i].x; | 
|  | vShift[i].y = pBoundingHyperCube[i].y; | 
|  | vShift[i].z = pBoundingHyperCube[i].z - fCamZ; | 
|  | } | 
|  |  | 
|  | i = 12; | 
|  | while(i) { | 
|  | --i; | 
|  |  | 
|  | w0 = c_pwLinesHyperCube[2 * i + 0]; | 
|  | w1 = c_pwLinesHyperCube[2 * i + 1]; | 
|  |  | 
|  | PVRTMatrixVec3CrossProduct(v, vShift[w0], vShift[w1]); | 
|  | dwClipFlags = 0; | 
|  |  | 
|  | j = 4; | 
|  | while(j) { | 
|  | --j; | 
|  |  | 
|  | if(PVRTMatrixVec3DotProduct(c_pvRect[j], v) < 0) | 
|  | ++dwClipFlags; | 
|  | } | 
|  |  | 
|  | // dwClipFlagsA will be 0 or 4 if the screen edges are on the outside of | 
|  | // this bounding-box-silhouette-edge. | 
|  | if(dwClipFlags % 4) | 
|  | continue; | 
|  |  | 
|  | j = 8; | 
|  | while(j) { | 
|  | --j; | 
|  |  | 
|  | if((j != w0) & (j != w1) && (PVRTMatrixVec3DotProduct(vShift[j], v) > 0)) | 
|  | ++dwClipFlags; | 
|  | } | 
|  |  | 
|  | // dwClipFlagsA will be 0 or 18 if this is a silhouette edge of the bounding box | 
|  | if(dwClipFlags % 12) | 
|  | continue; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		IsHyperBoundingBoxVisibleEx | 
|  | @Input			pBoundingHyperCube	The hypercube to test against | 
|  | @Input			fCamZ				The camera's position along the z-axis | 
|  | @Return			bool				Returns true if the bounding box is visible | 
|  | @Description	This method tests the hypercube bounding box's position against | 
|  | the camera's position to determine if it is visible. | 
|  | If it is visible, the function returns true. | 
|  | *************************************************************************/ | 
|  | static bool IsHyperBoundingBoxVisibleEx( | 
|  | const PVRTVECTOR4	* const pBoundingHyperCube, | 
|  | const float			fCamZ) | 
|  | { | 
|  | const PVRTVECTOR4	*pv0; | 
|  | PVRTVECTOR3	v, vShift[16]; | 
|  | unsigned int		dwClipFlagsA, dwClipFlagsB; | 
|  | int			i, j; | 
|  | unsigned short		w0, w1; | 
|  |  | 
|  | pv0 = &pBoundingHyperCube[8]; | 
|  | dwClipFlagsA = 0;		// Assume all are off-screen | 
|  | dwClipFlagsB = 0; | 
|  |  | 
|  | i = 8; | 
|  | while(i) | 
|  | { | 
|  | i--; | 
|  |  | 
|  | // Far | 
|  | if(pv0[i].x <  pv0[i].w) | 
|  | dwClipFlagsA |= 1 << 0; | 
|  |  | 
|  | if(pv0[i].x > -pv0[i].w) | 
|  | dwClipFlagsA |= 1 << 1; | 
|  |  | 
|  | if(pv0[i].y <  pv0[i].w) | 
|  | dwClipFlagsA |= 1 << 2; | 
|  |  | 
|  | if(pv0[i].y > -pv0[i].w) | 
|  | dwClipFlagsA |= 1 << 3; | 
|  |  | 
|  | if(pv0[i].z >  0) | 
|  | dwClipFlagsA |= 1 << 4; | 
|  |  | 
|  | // Near | 
|  | if(pBoundingHyperCube[i].x <  pBoundingHyperCube[i].w) | 
|  | dwClipFlagsB |= 1 << 0; | 
|  |  | 
|  | if(pBoundingHyperCube[i].x > -pBoundingHyperCube[i].w) | 
|  | dwClipFlagsB |= 1 << 1; | 
|  |  | 
|  | if(pBoundingHyperCube[i].y <  pBoundingHyperCube[i].w) | 
|  | dwClipFlagsB |= 1 << 2; | 
|  |  | 
|  | if(pBoundingHyperCube[i].y > -pBoundingHyperCube[i].w) | 
|  | dwClipFlagsB |= 1 << 3; | 
|  |  | 
|  | if(pBoundingHyperCube[i].z > 0) | 
|  | dwClipFlagsB |= 1 << 4; | 
|  | } | 
|  |  | 
|  | /* | 
|  | Volume is hidden if all the vertices are over a screen edge | 
|  | */ | 
|  | if((dwClipFlagsA | dwClipFlagsB) != 0x1F) | 
|  | return false; | 
|  |  | 
|  | /* | 
|  | Well, according to the simple bounding box check, it might be | 
|  | visible. Let's now test the view frustrum against the bounding | 
|  | hyper cube. (Basically the reverse of the previous test!) | 
|  |  | 
|  | This catches those cases where a diagonal hyper cube passes near a | 
|  | screen edge. | 
|  | */ | 
|  |  | 
|  | // Subtract the camera position from the vertices. I.e. move the camera to 0,0,0 | 
|  | for(i = 0; i < 16; ++i) { | 
|  | vShift[i].x = pBoundingHyperCube[i].x; | 
|  | vShift[i].y = pBoundingHyperCube[i].y; | 
|  | vShift[i].z = pBoundingHyperCube[i].z - fCamZ; | 
|  | } | 
|  |  | 
|  | i = 32; | 
|  | while(i) { | 
|  | --i; | 
|  |  | 
|  | w0 = c_pwLinesHyperCube[2 * i + 0]; | 
|  | w1 = c_pwLinesHyperCube[2 * i + 1]; | 
|  |  | 
|  | PVRTMatrixVec3CrossProduct(v, vShift[w0], vShift[w1]); | 
|  | dwClipFlagsA = 0; | 
|  |  | 
|  | j = 4; | 
|  | while(j) { | 
|  | --j; | 
|  |  | 
|  | if(PVRTMatrixVec3DotProduct(c_pvRect[j], v) < 0) | 
|  | ++dwClipFlagsA; | 
|  | } | 
|  |  | 
|  | // dwClipFlagsA will be 0 or 4 if the screen edges are on the outside of | 
|  | // this bounding-box-silhouette-edge. | 
|  | if(dwClipFlagsA % 4) | 
|  | continue; | 
|  |  | 
|  | j = 16; | 
|  | while(j) { | 
|  | --j; | 
|  |  | 
|  | if((j != w0) & (j != w1) && (PVRTMatrixVec3DotProduct(vShift[j], v) > 0)) | 
|  | ++dwClipFlagsA; | 
|  | } | 
|  |  | 
|  | // dwClipFlagsA will be 0 or 18 if this is a silhouette edge of the bounding box | 
|  | if(dwClipFlagsA % 18) | 
|  | continue; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | /*!*********************************************************************** | 
|  | @Function		IsFrontClipInVolume | 
|  | @Input			pBoundingHyperCube	The hypercube to test against | 
|  | @Return			bool | 
|  | @Description	Returns true if the hypercube is within the view frustrum. | 
|  | *************************************************************************/ | 
|  | static bool IsFrontClipInVolume( | 
|  | const PVRTVECTOR4	* const pBoundingHyperCube) | 
|  | { | 
|  | const PVRTVECTOR4	*pv0, *pv1; | 
|  | unsigned int				dwClipFlags; | 
|  | int					i; | 
|  | float				fScale, x, y, w; | 
|  |  | 
|  | /* | 
|  | OK. The hyper-bounding-box is in the view frustrum. | 
|  |  | 
|  | Now decide if we can use Z-pass instead of Z-fail. | 
|  |  | 
|  | TODO: if we calculate the convex hull of the front-clip intersection | 
|  | points, we can use the connecting lines to do a more accurate on- | 
|  | screen check (currently it just uses the bounding box of the | 
|  | intersection points.) | 
|  | */ | 
|  | dwClipFlags = 0; | 
|  |  | 
|  | i = 32; | 
|  | while(i) { | 
|  | --i; | 
|  |  | 
|  | pv0 = &pBoundingHyperCube[c_pwLinesHyperCube[2 * i + 0]]; | 
|  | pv1 = &pBoundingHyperCube[c_pwLinesHyperCube[2 * i + 1]]; | 
|  |  | 
|  | // If both coords are negative, or both coords are positive, it doesn't cross the Z=0 plane | 
|  | if(pv0->z * pv1->z > 0) | 
|  | continue; | 
|  |  | 
|  | // TODO: if fScale > 0.5f, do the lerp in the other direction; this is | 
|  | // because we want fScale to be close to 0, not 1, to retain accuracy. | 
|  | fScale = (0 - pv0->z) / (pv1->z - pv0->z); | 
|  |  | 
|  | x = fScale * pv1->x + (1.0f - fScale) * pv0->x; | 
|  | y = fScale * pv1->y + (1.0f - fScale) * pv0->y; | 
|  | w = fScale * pv1->w + (1.0f - fScale) * pv0->w; | 
|  |  | 
|  | if(x > -w) | 
|  | dwClipFlags |= 1 << 0; | 
|  |  | 
|  | if(x < w) | 
|  | dwClipFlags |= 1 << 1; | 
|  |  | 
|  | if(y > -w) | 
|  | dwClipFlags |= 1 << 2; | 
|  |  | 
|  | if(y < w) | 
|  | dwClipFlags |= 1 << 3; | 
|  | } | 
|  |  | 
|  | if(dwClipFlags == 0x0F) | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolBoundingBoxExtrude | 
|  | @Modified		pvExtrudedCube	8 Vertices to represent the extruded box | 
|  | @Input			pBoundingBox	The bounding box to extrude | 
|  | @Input			pvLightMdl		The light position/direction | 
|  | @Input			bPointLight		Is the light a point light | 
|  | @Input			fVolLength		The length the volume has been extruded by | 
|  | @Description	Extrudes the bounding box of the volume | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolBoundingBoxExtrude( | 
|  | PVRTVECTOR3				* const pvExtrudedCube, | 
|  | const PVRTBOUNDINGBOX	* const pBoundingBox, | 
|  | const PVRTVECTOR3		* const pvLightMdl, | 
|  | const bool				bPointLight, | 
|  | const float				fVolLength) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(bPointLight) { | 
|  | i = 8; | 
|  | while(i) | 
|  | { | 
|  | i--; | 
|  |  | 
|  | pvExtrudedCube[i].x = pBoundingBox->Point[i].x + fVolLength * (pBoundingBox->Point[i].x - pvLightMdl->x); | 
|  | pvExtrudedCube[i].y = pBoundingBox->Point[i].y + fVolLength * (pBoundingBox->Point[i].y - pvLightMdl->y); | 
|  | pvExtrudedCube[i].z = pBoundingBox->Point[i].z + fVolLength * (pBoundingBox->Point[i].z - pvLightMdl->z); | 
|  | } | 
|  | } else { | 
|  | i = 8; | 
|  | while(i) | 
|  | { | 
|  | i--; | 
|  |  | 
|  | pvExtrudedCube[i].x = pBoundingBox->Point[i].x + fVolLength * pvLightMdl->x; | 
|  | pvExtrudedCube[i].y = pBoundingBox->Point[i].y + fVolLength * pvLightMdl->y; | 
|  | pvExtrudedCube[i].z = pBoundingBox->Point[i].z + fVolLength * pvLightMdl->z; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolBoundingBoxIsVisible | 
|  | @Modified		pdwVisFlags		Visibility flags | 
|  | @Input			bObVisible		Unused set to true | 
|  | @Input			bNeedsZClipping	Unused set to true | 
|  | @Input			pBoundingBox	The volumes bounding box | 
|  | @Input			pmTrans			The projection matrix | 
|  | @Input			pvLightMdl		The light position/direction | 
|  | @Input			bPointLight		Is the light a point light | 
|  | @Input			fCamZProj		The camera's z projection value | 
|  | @Input			fVolLength		The length the volume is extruded by | 
|  | @Description	Determines if the volume is visible and if it needs caps | 
|  | *************************************************************************/ | 
|  | void PVRTShadowVolBoundingBoxIsVisible( | 
|  | unsigned int			* const pdwVisFlags, | 
|  | const bool				bObVisible,				// Is the object visible? | 
|  | const bool				bNeedsZClipping,		// Does the object require Z clipping? | 
|  | const PVRTBOUNDINGBOX	* const pBoundingBox, | 
|  | const PVRTMATRIX		* const pmTrans, | 
|  | const PVRTVECTOR3		* const pvLightMdl, | 
|  | const bool				bPointLight, | 
|  | const float				fCamZProj, | 
|  | const float				fVolLength) | 
|  | { | 
|  | PVRTVECTOR3		pvExtrudedCube[8]; | 
|  | PVRTVECTOR4		BoundingHyperCubeT[16]; | 
|  | int				i; | 
|  | unsigned int	dwClipFlagsA, dwClipZCnt; | 
|  | float			fLightProjZ; | 
|  |  | 
|  | PVRT_UNREFERENCED_PARAMETER(bObVisible); | 
|  | PVRT_UNREFERENCED_PARAMETER(bNeedsZClipping); | 
|  |  | 
|  | _ASSERT((bObVisible && bNeedsZClipping) || !bNeedsZClipping); | 
|  |  | 
|  | /* | 
|  | Transform the eight bounding box points into projection space | 
|  | */ | 
|  | PVRTTransformVec3Array(&BoundingHyperCubeT[0], sizeof(*BoundingHyperCubeT), pBoundingBox->Point,	sizeof(*pBoundingBox->Point),	pmTrans, 8); | 
|  |  | 
|  | /* | 
|  | Get the light Z coordinate in projection space | 
|  | */ | 
|  | fLightProjZ = | 
|  | pmTrans->f[ 2] * pvLightMdl->x + | 
|  | pmTrans->f[ 6] * pvLightMdl->y + | 
|  | pmTrans->f[10] * pvLightMdl->z + | 
|  | pmTrans->f[14]; | 
|  |  | 
|  | /* | 
|  | Where is the object relative to the near clip plane and light? | 
|  | */ | 
|  | dwClipZCnt		= 0; | 
|  | dwClipFlagsA	= 0; | 
|  | i = 8; | 
|  | while(i) { | 
|  | --i; | 
|  |  | 
|  | if(BoundingHyperCubeT[i].z <= 0) | 
|  | ++dwClipZCnt; | 
|  |  | 
|  | if(BoundingHyperCubeT[i].z <= fLightProjZ) | 
|  | ++dwClipFlagsA; | 
|  | } | 
|  |  | 
|  | if(dwClipZCnt == 8 && dwClipFlagsA == 8) { | 
|  | // hidden | 
|  | *pdwVisFlags = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | Shadow the bounding box into pvExtrudedCube. | 
|  | */ | 
|  | PVRTShadowVolBoundingBoxExtrude(pvExtrudedCube, pBoundingBox, pvLightMdl, bPointLight, fVolLength); | 
|  |  | 
|  | /* | 
|  | Transform to projection space | 
|  | */ | 
|  | PVRTTransformVec3Array(&BoundingHyperCubeT[8], sizeof(*BoundingHyperCubeT), pvExtrudedCube, sizeof(*pvExtrudedCube), pmTrans, 8); | 
|  |  | 
|  | /* | 
|  | Check whether any part of the hyper bounding box is even visible | 
|  | */ | 
|  | if(!IsHyperBoundingBoxVisibleEx(BoundingHyperCubeT, fCamZProj)) { | 
|  | *pdwVisFlags = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | It's visible, so choose a render method | 
|  | */ | 
|  | if(dwClipZCnt == 8) { | 
|  | // 1 | 
|  | if(IsFrontClipInVolume(BoundingHyperCubeT)) { | 
|  | *pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE | PVRTSHADOWVOLUME_NEED_ZFAIL; | 
|  |  | 
|  | if(IsBoundingBoxVisibleEx(&BoundingHyperCubeT[8], fCamZProj)) | 
|  | { | 
|  | *pdwVisFlags |= PVRTSHADOWVOLUME_NEED_CAP_BACK; | 
|  | } | 
|  | } else { | 
|  | *pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE; | 
|  | } | 
|  | } else { | 
|  | if(!(dwClipZCnt | dwClipFlagsA)) { | 
|  | // 3 | 
|  | *pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE; | 
|  | } else { | 
|  | // 5 | 
|  | if(IsFrontClipInVolume(BoundingHyperCubeT)) { | 
|  | *pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE | PVRTSHADOWVOLUME_NEED_ZFAIL; | 
|  |  | 
|  | if(IsBoundingBoxVisibleEx(BoundingHyperCubeT, fCamZProj)) | 
|  | { | 
|  | *pdwVisFlags |= PVRTSHADOWVOLUME_NEED_CAP_FRONT; | 
|  | } | 
|  |  | 
|  | if(IsBoundingBoxVisibleEx(&BoundingHyperCubeT[8], fCamZProj)) | 
|  | { | 
|  | *pdwVisFlags |= PVRTSHADOWVOLUME_NEED_CAP_BACK; | 
|  | } | 
|  | } else { | 
|  | *pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		PVRTShadowVolSilhouetteProjectedRender | 
|  | @Input			psMesh		Shadow volume mesh | 
|  | @Input			psVol		Renderable shadow volume information | 
|  | @Input			pContext	A struct for passing in API specific data | 
|  | @Description	Draws the shadow volume | 
|  | *************************************************************************/ | 
|  | int PVRTShadowVolSilhouetteProjectedRender( | 
|  | const PVRTShadowVolShadowMesh	* const psMesh, | 
|  | const PVRTShadowVolShadowVol	* const psVol, | 
|  | const SPVRTContext				* const pContext) | 
|  | { | 
|  | #if defined(BUILD_DX11) | 
|  | return 0; // Not implemented yet | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_OGL) || defined(BUILD_OGLES2) || defined(BUILD_OGLES) || defined(BUILD_OGLES3) | 
|  | _ASSERT(psMesh->pivb); | 
|  |  | 
|  | #if defined(_DEBUG) // To fix error in Linux | 
|  | _ASSERT(psVol->nIdxCnt <= psVol->nIdxCntMax); | 
|  | _ASSERT(psVol->nIdxCnt % 3 == 0); | 
|  | _ASSERT(psVol->nIdxCnt / 3 <= 0xFFFF); | 
|  | #endif | 
|  |  | 
|  | #if defined(BUILD_OGL) | 
|  | _ASSERT(pContext && pContext->pglExt); | 
|  |  | 
|  | //Bind the buffers | 
|  | pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, psMesh->pivb); | 
|  | pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, psVol->piib); | 
|  |  | 
|  | pContext->pglExt->glEnableVertexAttribArrayARB(0); | 
|  | pContext->pglExt->glEnableVertexAttribArrayARB(1); | 
|  |  | 
|  | pContext->pglExt->glVertexAttribPointerARB(0, 3, GL_FLOAT, GL_FALSE, sizeof(SVertexShVol), (void*)0); | 
|  | pContext->pglExt->glVertexAttribPointerARB(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(SVertexShVol), (void*)12); | 
|  |  | 
|  | glDrawElements(GL_TRIANGLES, psVol->nIdxCnt, GL_UNSIGNED_SHORT, NULL); | 
|  |  | 
|  | pContext->pglExt->glDisableVertexAttribArrayARB(0); | 
|  | pContext->pglExt->glDisableVertexAttribArrayARB(1); | 
|  |  | 
|  | pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); | 
|  | pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); | 
|  |  | 
|  | return psVol->nIdxCnt / 3; | 
|  | #elif defined(BUILD_OGLES2) || defined(BUILD_OGLES3) | 
|  | PVRT_UNREFERENCED_PARAMETER(pContext); | 
|  | GLint i32CurrentProgram; | 
|  | glGetIntegerv(GL_CURRENT_PROGRAM, &i32CurrentProgram); | 
|  |  | 
|  | _ASSERT(i32CurrentProgram); //no program currently set | 
|  |  | 
|  | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].x); | 
|  | glEnableVertexAttribArray(0); | 
|  |  | 
|  | glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].dwExtrude); | 
|  | glEnableVertexAttribArray(1); | 
|  |  | 
|  | glDrawElements(GL_TRIANGLES, psVol->nIdxCnt, GL_UNSIGNED_SHORT, psVol->piib); | 
|  |  | 
|  | glDisableVertexAttribArray(0); | 
|  | glDisableVertexAttribArray(1); | 
|  |  | 
|  | return psVol->nIdxCnt / 3; | 
|  |  | 
|  | #elif defined(BUILD_OGLES) | 
|  | _ASSERT(pContext && pContext->pglesExt); | 
|  |  | 
|  | glEnableClientState(GL_VERTEX_ARRAY); | 
|  | glEnableClientState(GL_MATRIX_INDEX_ARRAY_OES); | 
|  | glEnableClientState(GL_WEIGHT_ARRAY_OES); | 
|  |  | 
|  | glVertexPointer(3, GL_FLOAT, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].x); | 
|  | pContext->pglesExt->glMatrixIndexPointerOES(1, GL_UNSIGNED_BYTE, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].dwExtrude); | 
|  | pContext->pglesExt->glWeightPointerOES(1, GL_FLOAT, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].fWeight); | 
|  |  | 
|  | glDrawElements(GL_TRIANGLES, psVol->nIdxCnt, GL_UNSIGNED_SHORT, psVol->piib); | 
|  |  | 
|  | glDisableClientState(GL_VERTEX_ARRAY); | 
|  | glDisableClientState(GL_MATRIX_INDEX_ARRAY_OES); | 
|  | glDisableClientState(GL_WEIGHT_ARRAY_OES); | 
|  |  | 
|  | return psVol->nIdxCnt / 3; | 
|  | #endif | 
|  |  | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /***************************************************************************** | 
|  | End of file (PVRTShadowVol.cpp) | 
|  | *****************************************************************************/ | 
|  |  |