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