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