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