1/******************************************************************************
2
3 @File         PVRTShadowVol.cpp
4
5 @Title        PVRTShadowVol
6
7 @Version
8
9 @Copyright    Copyright (c) Imagination Technologies Limited.
10
11 @Platform     ANSI compatible
12
13 @Description  Declarations of functions relating to shadow volume generation.
14
15******************************************************************************/
16#include <stdlib.h>
17#include <string.h>
18
19#include "PVRTGlobal.h"
20#include "PVRTContext.h"
21#include "PVRTFixedPoint.h"
22#include "PVRTMatrix.h"
23#include "PVRTTrans.h"
24#include "PVRTShadowVol.h"
25#include "PVRTError.h"
26
27/****************************************************************************
28** Build options
29****************************************************************************/
30
31/****************************************************************************
32** Defines
33****************************************************************************/
34
35/****************************************************************************
36** Macros
37****************************************************************************/
38
39/****************************************************************************
40** Structures
41****************************************************************************/
42struct SVertexShVol {
43	float	x, y, z;
44	unsigned int dwExtrude;
45#if defined(BUILD_OGLES)
46	float fWeight;
47#endif
48};
49
50/****************************************************************************
51** Constants
52****************************************************************************/
53const static unsigned short c_pwLinesHyperCube[64] = {
54	// Cube0
55	0, 1,  2, 3,  0, 2,  1, 3,
56	4, 5,  6, 7,  4, 6,  5, 7,
57	0, 4,  1, 5,  2, 6,  3, 7,
58	// Cube1
59	8, 9,  10, 11,  8, 10,  9, 11,
60	12, 13,  14, 15,  12, 14,  13, 15,
61	8, 12,  9, 13,  10, 14,  11, 15,
62	// Hyper cube jn
63	0, 8,  1, 9,  2, 10,  3, 11,
64	4, 12,  5, 13,  6, 14,  7, 15
65};
66const static PVRTVECTOR3 c_pvRect[4] = {
67	{ -1, -1, 1 },
68	{ -1,  1, 1 },
69	{  1, -1, 1 },
70	{  1,  1, 1 }
71};
72
73/****************************************************************************
74** Shared globals
75****************************************************************************/
76
77/****************************************************************************
78** Globals
79****************************************************************************/
80
81/****************************************************************************
82** Declarations
83****************************************************************************/
84
85/****************************************************************************
86** Code
87****************************************************************************/
88/****************************************************************************
89@Function		FindOrCreateVertex
90@Modified		psMesh				The mesh to check against/add to
91@Input			pV					The vertex to compare/add
92@Return			unsigned short		The array index of the vertex
93@Description	Searches through the mesh data to see if the vertex has
94				already been used. If it has, the array index of the vertex
95				is returned. If the mesh does not already use the vertex,
96				it is appended to the vertex array and the array count is incremented.
97				The index in the array of the new vertex is then returned.
98****************************************************************************/
99static unsigned short FindOrCreateVertex(PVRTShadowVolShadowMesh * const psMesh, const PVRTVECTOR3 * const pV) {
100	unsigned short	wCurr;
101
102	/*
103		First check whether we already have a vertex here
104	*/
105	for(wCurr = 0; wCurr < psMesh->nV; wCurr++) {
106		if(memcmp(&psMesh->pV[wCurr], pV, sizeof(*pV)) == 0) {
107			/* Don't do anything more if the vertex already exists */
108			return wCurr;
109		}
110	}
111
112	/*
113		Add the vertex then!
114	*/
115	psMesh->pV[psMesh->nV] = *pV;
116
117	return (unsigned short) psMesh->nV++;
118}
119
120/****************************************************************************
121@Function		FindOrCreateEdge
122@Modified		psMesh				The mesh to check against/add to
123@Input			pv0					The first point that defines the edge
124@Input			pv1					The second point that defines the edge
125@Return			PVRTShadowVolMEdge	The index of the found/created edge in the
126									mesh's array
127@Description	Searches through the mesh data to see if the edge has
128				already been used. If it has, the array index of the edge
129				is returned. If the mesh does not already use the edge,
130				it is appended to the edge array and the array cound is incremented.
131				The index in the array of the new edge is then returned.
132****************************************************************************/
133static unsigned int FindOrCreateEdge(PVRTShadowVolShadowMesh * const psMesh, const PVRTVECTOR3 * const pv0, const PVRTVECTOR3 * const pv1) {
134	unsigned int	nCurr;
135	unsigned short			wV0, wV1;
136
137	wV0 = FindOrCreateVertex(psMesh, pv0);
138	wV1 = FindOrCreateVertex(psMesh, pv1);
139
140
141	/*
142		First check whether we already have a edge here
143	*/
144	for(nCurr = 0; nCurr < psMesh->nE; nCurr++) {
145		if(
146			(psMesh->pE[nCurr].wV0 == wV0 && psMesh->pE[nCurr].wV1 == wV1) ||
147			(psMesh->pE[nCurr].wV0 == wV1 && psMesh->pE[nCurr].wV1 == wV0))
148		{
149			/* Don't do anything more if the edge already exists */
150			return nCurr;
151		}
152	}
153
154	/*
155		Add the edge then!
156	*/
157	psMesh->pE[psMesh->nE].wV0	= wV0;
158	psMesh->pE[psMesh->nE].wV1	= wV1;
159	psMesh->pE[psMesh->nE].nVis	= 0;
160
161	return psMesh->nE++;
162}
163
164/****************************************************************************
165@Function		CrossProduct
166@Output			pvOut			The resultant vector
167@Input			pv0				Vector zero
168@Input			pv1				Vector one
169@Input			pv2				Vector two
170@Description	Finds the vector between vector zero and vector one,
171				and the vector between vector zero and vector two.
172				These two resultant vectors are then multiplied together
173				and the result is assigned to the output vector.
174****************************************************************************/
175static void CrossProduct(
176	PVRTVECTOR3 * const pvOut,
177	const PVRTVECTOR3 * const pv0,
178	const PVRTVECTOR3 * const pv1,
179	const PVRTVECTOR3 * const pv2)
180{
181	PVRTVECTOR3 v0, v1;
182
183	v0.x = pv1->x - pv0->x;
184	v0.y = pv1->y - pv0->y;
185	v0.z = pv1->z - pv0->z;
186
187	v1.x = pv2->x - pv0->x;
188	v1.y = pv2->y - pv0->y;
189	v1.z = pv2->z - pv0->z;
190
191	PVRTMatrixVec3CrossProduct(*pvOut, v0, v1);
192}
193
194/****************************************************************************
195@Function		FindOrCreateTriangle
196@Modified		psMesh			The mesh to check against/add to
197@Input			pv0				Vertex zero
198@Input			pv1				Vertex one
199@Input			pv2				Vertex two
200@Description	Searches through the mesh data to see if the triangle has
201				already been used. If it has, the function returns.
202				If the mesh does not already use the triangle,
203				it is appended to the triangle array and the array cound is incremented.
204****************************************************************************/
205static void FindOrCreateTriangle(
206	PVRTShadowVolShadowMesh	* const psMesh,
207	const PVRTVECTOR3	* const pv0,
208	const PVRTVECTOR3	* const pv1,
209	const PVRTVECTOR3	* const pv2)
210{
211	unsigned int	nCurr;
212	PVRTShadowVolMEdge	*psE0, *psE1, *psE2;
213	unsigned int wE0, wE1, wE2;
214
215	wE0 = FindOrCreateEdge(psMesh, pv0, pv1);
216	wE1 = FindOrCreateEdge(psMesh, pv1, pv2);
217	wE2 = FindOrCreateEdge(psMesh, pv2, pv0);
218
219	if(wE0 == wE1 || wE1 == wE2 || wE2 == wE0) {
220		/* Don't add degenerate triangles */
221		_RPT0(_CRT_WARN, "FindOrCreateTriangle() Degenerate triangle.\n");
222		return;
223	}
224
225	/*
226		First check whether we already have a triangle here
227	*/
228	for(nCurr = 0; nCurr < psMesh->nT; nCurr++) {
229		if(
230			(psMesh->pT[nCurr].wE0 == wE0 || psMesh->pT[nCurr].wE0 == wE1 || psMesh->pT[nCurr].wE0 == wE2) &&
231			(psMesh->pT[nCurr].wE1 == wE0 || psMesh->pT[nCurr].wE1 == wE1 || psMesh->pT[nCurr].wE1 == wE2) &&
232			(psMesh->pT[nCurr].wE2 == wE0 || psMesh->pT[nCurr].wE2 == wE1 || psMesh->pT[nCurr].wE2 == wE2))
233		{
234			/* Don't do anything more if the triangle already exists */
235			return;
236		}
237	}
238
239	/*
240		Add the triangle then!
241	*/
242	psMesh->pT[psMesh->nT].wE0 = wE0;
243	psMesh->pT[psMesh->nT].wE1 = wE1;
244	psMesh->pT[psMesh->nT].wE2 = wE2;
245
246	psE0 = &psMesh->pE[wE0];
247	psE1 = &psMesh->pE[wE1];
248	psE2 = &psMesh->pE[wE2];
249
250	/*
251		Store the triangle indices; these are indices into the shadow mesh, not the source model indices
252	*/
253	if(psE0->wV0 == psE1->wV0 || psE0->wV0 == psE1->wV1)
254		psMesh->pT[psMesh->nT].w[0] = psE0->wV1;
255	else
256		psMesh->pT[psMesh->nT].w[0] = psE0->wV0;
257
258	if(psE1->wV0 == psE2->wV0 || psE1->wV0 == psE2->wV1)
259		psMesh->pT[psMesh->nT].w[1] = psE1->wV1;
260	else
261		psMesh->pT[psMesh->nT].w[1] = psE1->wV0;
262
263	if(psE2->wV0 == psE0->wV0 || psE2->wV0 == psE0->wV1)
264		psMesh->pT[psMesh->nT].w[2] = psE2->wV1;
265	else
266		psMesh->pT[psMesh->nT].w[2] = psE2->wV0;
267
268	/* Calculate the triangle normal */
269	CrossProduct(&psMesh->pT[psMesh->nT].vNormal, pv0, pv1, pv2);
270
271	/* Check which edges have the correct winding order for this triangle */
272	psMesh->pT[psMesh->nT].nWinding = 0;
273	if(memcmp(&psMesh->pV[psE0->wV0], pv0, sizeof(*pv0)) == 0) psMesh->pT[psMesh->nT].nWinding |= 0x01;
274	if(memcmp(&psMesh->pV[psE1->wV0], pv1, sizeof(*pv1)) == 0) psMesh->pT[psMesh->nT].nWinding |= 0x02;
275	if(memcmp(&psMesh->pV[psE2->wV0], pv2, sizeof(*pv2)) == 0) psMesh->pT[psMesh->nT].nWinding |= 0x04;
276
277	psMesh->nT++;
278}
279
280/*!***********************************************************************
281@Function	PVRTShadowVolMeshCreateMesh
282@Modified	psMesh		The shadow volume mesh to populate
283@Input		pVertex		A list of vertices
284@Input		nNumVertex	The number of vertices
285@Input		pFaces		A list of faces
286@Input		nNumFaces	The number of faces
287@Description	Creates a mesh format suitable for generating shadow volumes
288*************************************************************************/
289void PVRTShadowVolMeshCreateMesh(
290	PVRTShadowVolShadowMesh		* const psMesh,
291	const float				* const pVertex,
292	const unsigned int		nNumVertex,
293	const unsigned short	* const pFaces,
294	const unsigned int		nNumFaces)
295{
296	unsigned int	nCurr;
297
298	/*
299		Prep the structure to return
300	*/
301	memset(psMesh, 0, sizeof(*psMesh));
302
303	/*
304		Allocate some working space to find the unique vertices
305	*/
306	psMesh->pV = (PVRTVECTOR3*)malloc(nNumVertex * sizeof(*psMesh->pV));
307	psMesh->pE = (PVRTShadowVolMEdge*)malloc(nNumFaces * sizeof(*psMesh->pE) * 3);
308	psMesh->pT = (PVRTShadowVolMTriangle*)malloc(nNumFaces * sizeof(*psMesh->pT));
309	_ASSERT(psMesh->pV);
310	_ASSERT(psMesh->pE);
311	_ASSERT(psMesh->pT);
312
313	for(nCurr = 0; nCurr < nNumFaces; nCurr++) {
314		FindOrCreateTriangle(psMesh,
315			(PVRTVECTOR3*)&pVertex[3 * pFaces[3 * nCurr + 0]],
316			(PVRTVECTOR3*)&pVertex[3 * pFaces[3 * nCurr + 1]],
317			(PVRTVECTOR3*)&pVertex[3 * pFaces[3 * nCurr + 2]]);
318	}
319
320	_ASSERT(psMesh->nV <= nNumVertex);
321	_ASSERT(psMesh->nE < nNumFaces * 3);
322	_ASSERT(psMesh->nT == nNumFaces);
323
324	_RPT2(_CRT_WARN, "Unique vertices : %d (from %d)\n", psMesh->nV, nNumVertex);
325	_RPT2(_CRT_WARN, "Unique edges    : %d (from %d)\n", psMesh->nE, nNumFaces * 3);
326	_RPT2(_CRT_WARN, "Unique triangles: %d (from %d)\n", psMesh->nT, nNumFaces);
327
328	/*
329		Create the real unique lists
330	*/
331	psMesh->pV = (PVRTVECTOR3*)realloc(psMesh->pV, psMesh->nV * sizeof(*psMesh->pV));
332	psMesh->pE = (PVRTShadowVolMEdge*)realloc(psMesh->pE, psMesh->nE * sizeof(*psMesh->pE));
333	psMesh->pT = (PVRTShadowVolMTriangle*)realloc(psMesh->pT, psMesh->nT * sizeof(*psMesh->pT));
334	_ASSERT(psMesh->pV);
335	_ASSERT(psMesh->pE);
336	_ASSERT(psMesh->pT);
337
338#if defined(_DEBUG) && !defined(_UNICODE) && defined(_WIN32)
339	/*
340		Check we have sensible model data
341	*/
342	{
343		unsigned int nTri, nEdge;
344		PVRTERROR_OUTPUT_DEBUG("ShadowMeshCreate() Sanity check...");
345
346		for(nEdge = 0; nEdge < psMesh->nE; nEdge++) {
347			nCurr = 0;
348
349			for(nTri = 0; nTri < psMesh->nT; nTri++) {
350				if(psMesh->pT[nTri].wE0 == nEdge)
351					nCurr++;
352
353				if(psMesh->pT[nTri].wE1 == nEdge)
354					nCurr++;
355
356				if(psMesh->pT[nTri].wE2 == nEdge)
357					nCurr++;
358			}
359
360			/*
361				Every edge should be referenced exactly twice.
362				If they aren't then the mesh isn't closed which will cause problems when rendering the shadows.
363			*/
364			_ASSERTE(nCurr == 2);
365		}
366
367		PVRTERROR_OUTPUT_DEBUG("done.\n");
368	}
369#endif
370}
371
372/*!***********************************************************************
373@Function		PVRTShadowVolMeshInitMesh
374@Input			psMesh	The shadow volume mesh
375@Input			pContext	A struct for API specific data
376@Returns 		True on success
377@Description	Init the mesh
378*************************************************************************/
379bool PVRTShadowVolMeshInitMesh(
380	PVRTShadowVolShadowMesh		* const psMesh,
381	const SPVRTContext		* const pContext)
382{
383	unsigned int	nCurr;
384#if defined(BUILD_DX11)
385	HRESULT			hRes;
386#endif
387	SVertexShVol	*pvData;
388
389#if defined(BUILD_OGL)
390	_ASSERT(pContext && pContext->pglExt);
391
392	if(!pContext || !pContext->pglExt)
393		return false;
394#endif
395
396#if defined(BUILD_OGLES2) || defined(BUILD_OGLES) || defined(BUILD_OGLES3)
397	PVRT_UNREFERENCED_PARAMETER(pContext);
398#endif
399	_ASSERT(psMesh);
400	_ASSERT(psMesh->nV);
401	_ASSERT(psMesh->nE);
402	_ASSERT(psMesh->nT);
403
404	/*
405		Allocate a vertex buffer for the shadow volumes
406	*/
407	_ASSERT(psMesh->pivb == NULL);
408	_RPT3(_CRT_WARN, "ShadowMeshInitMesh() %5d byte VB (%3dv x 2 x size(%d))\n", psMesh->nV * 2 * sizeof(*pvData), psMesh->nV, sizeof(*pvData));
409
410#if defined(BUILD_DX11)
411	D3D11_BUFFER_DESC sVBBufferDesc;
412	sVBBufferDesc.ByteWidth		= psMesh->nV * 2 * 3 * sizeof(*pvData);
413	sVBBufferDesc.Usage			= D3D11_USAGE_DYNAMIC;
414	sVBBufferDesc.BindFlags		= D3D11_BIND_VERTEX_BUFFER;
415	sVBBufferDesc.CPUAccessFlags= 0;
416	sVBBufferDesc.MiscFlags		= 0;
417
418	hRes = pContext->pDev->CreateBuffer(&sVBBufferDesc, NULL, &psMesh->pivb) != S_OK;
419
420	if(FAILED(hRes))
421	{
422		_ASSERT(false);
423		return false;
424	}
425
426	D3D11_MAPPED_SUBRESOURCE data;
427	ID3D11DeviceContext *pDeviceContext = 0;
428	pContext->pDev->GetImmediateContext(&pDeviceContext);
429	hRes = pDeviceContext->Map(psMesh->pivb, 0, D3D11_MAP_WRITE_DISCARD, NULL, &data);
430
431	if(FAILED(hRes))
432	{
433		_ASSERT(false);
434		return false;
435	}
436
437	pvData = (SVertexShVol*) data.pData;
438#endif
439
440#if defined(BUILD_OGL)
441	_ASSERT(pContext && pContext->pglExt);
442	if (!pContext || !pContext->pglExt)
443		return false;
444	pContext->pglExt->glGenBuffersARB(1, &psMesh->pivb);
445	pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, psMesh->pivb);
446	pContext->pglExt->glBufferDataARB(GL_ARRAY_BUFFER_ARB, psMesh->nV * 2 * sizeof(*pvData), NULL, GL_STREAM_DRAW_ARB);
447	pvData = (SVertexShVol*)pContext->pglExt->glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
448#endif
449
450#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
451	psMesh->pivb = malloc(psMesh->nV * 2 * sizeof(*pvData));
452	pvData = (SVertexShVol*)psMesh->pivb;
453#endif
454
455	/*
456		Fill the vertex buffer with two subtly different copies of the vertices
457	*/
458	for(nCurr = 0; nCurr < psMesh->nV; ++nCurr)
459	{
460		pvData[nCurr].x			= psMesh->pV[nCurr].x;
461		pvData[nCurr].y			= psMesh->pV[nCurr].y;
462		pvData[nCurr].z			= psMesh->pV[nCurr].z;
463		pvData[nCurr].dwExtrude = 0;
464
465#if defined(BUILD_OGLES)
466		pvData[nCurr].fWeight = 1;
467		pvData[nCurr + psMesh->nV].fWeight = 1;
468#endif
469		pvData[nCurr + psMesh->nV]				= pvData[nCurr];
470		pvData[nCurr + psMesh->nV].dwExtrude	= 0x04030201;		// Order is wzyx
471	}
472
473#if defined(BUILD_OGL)
474	pContext->pglExt->glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
475	pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
476#endif
477
478#if defined(BUILD_DX11)
479	pDeviceContext->Unmap(psMesh->pivb, 0);
480#endif
481	return true;
482}
483
484/*!***********************************************************************
485@Function		PVRTShadowVolMeshInitVol
486@Modified		psVol	The shadow volume struct
487@Input			psMesh	The shadow volume mesh
488@Input			pContext	A struct for API specific data
489@Returns		True on success
490@Description	Init the renderable shadow volume information.
491*************************************************************************/
492bool PVRTShadowVolMeshInitVol(
493	PVRTShadowVolShadowVol			* const psVol,
494	const PVRTShadowVolShadowMesh	* const psMesh,
495	const SPVRTContext		* const pContext)
496{
497#if defined(BUILD_DX11)
498	HRESULT hRes;
499#endif
500#if defined(BUILD_OGLES2) || defined(BUILD_OGLES) || defined(BUILD_OGL) || defined(BUILD_OGLES3)
501	PVRT_UNREFERENCED_PARAMETER(pContext);
502#endif
503	_ASSERT(psVol);
504	_ASSERT(psMesh);
505	_ASSERT(psMesh->nV);
506	_ASSERT(psMesh->nE);
507	_ASSERT(psMesh->nT);
508
509	_RPT1(_CRT_WARN, "ShadowMeshInitVol() %5lu byte IB\n", psMesh->nT * 2 * 3 * sizeof(unsigned short));
510
511	/*
512		Allocate a index buffer for the shadow volumes
513	*/
514#if defined(_DEBUG)
515	psVol->nIdxCntMax = psMesh->nT * 2 * 3;
516#endif
517#if defined(BUILD_DX11)
518	D3D11_BUFFER_DESC sIdxBuferDesc;
519	sIdxBuferDesc.ByteWidth		= psMesh->nT * 2 * 3 * sizeof(unsigned short);
520	sIdxBuferDesc.Usage			= D3D11_USAGE_DYNAMIC;
521	sIdxBuferDesc.BindFlags		= D3D11_BIND_INDEX_BUFFER;
522	sIdxBuferDesc.CPUAccessFlags= 0;
523	sIdxBuferDesc.MiscFlags		= 0;
524
525	hRes = pContext->pDev->CreateBuffer(&sIdxBuferDesc, NULL, &psVol->piib) != S_OK;
526
527	if(FAILED(hRes)) {
528		_ASSERT(false);
529		return false;
530	}
531#endif
532#if defined(BUILD_OGL)
533	_ASSERT(pContext && pContext->pglExt);
534	if (!pContext || !pContext->pglExt)
535		return false;
536	pContext->pglExt->glGenBuffersARB(1, &psVol->piib);
537	pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, psVol->piib);
538	pContext->pglExt->glBufferDataARB(GL_ARRAY_BUFFER_ARB, psMesh->nT * 2 * 3 * sizeof(unsigned short), NULL, GL_STREAM_DRAW_ARB);
539#endif
540
541#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
542	psVol->piib = (unsigned short*)malloc(psMesh->nT * 2 * 3 * sizeof(unsigned short));
543#endif
544
545	return true;
546}
547
548/*!***********************************************************************
549@Function		PVRTShadowVolMeshDestroyMesh
550@Input			psMesh	The shadow volume mesh to destroy
551@Description	Destroys all shadow volume mesh data created by PVRTShadowVolMeshCreateMesh
552*************************************************************************/
553void PVRTShadowVolMeshDestroyMesh(
554	PVRTShadowVolShadowMesh		* const psMesh)
555{
556	FREE(psMesh->pV);
557	FREE(psMesh->pE);
558	FREE(psMesh->pT);
559}
560
561/*!***********************************************************************
562@Function		PVRTShadowVolMeshReleaseMesh
563@Input			psMesh	The shadow volume mesh to release
564@Description	Releases all shadow volume mesh data created by PVRTShadowVolMeshInitMesh
565*************************************************************************/
566void PVRTShadowVolMeshReleaseMesh(
567	PVRTShadowVolShadowMesh		* const psMesh,
568	SPVRTContext				* const psContext)
569{
570#if defined(BUILD_OGL)
571	_ASSERT(psContext && psContext->pglExt);
572	if (!psContext || !psContext->pglExt)
573		return;
574	psContext->pglExt->glDeleteBuffersARB(1, &psMesh->pivb);
575#endif
576#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
577	PVRT_UNREFERENCED_PARAMETER(psContext);
578	FREE(psMesh->pivb);
579#endif
580}
581
582/*!***********************************************************************
583@Function		PVRTShadowVolMeshReleaseVol
584@Input			psVol	The shadow volume information to release
585@Description	Releases all data create by PVRTShadowVolMeshInitVol
586*************************************************************************/
587void PVRTShadowVolMeshReleaseVol(
588	PVRTShadowVolShadowVol			* const psVol,
589	SPVRTContext					* const psContext)
590{
591#if defined(BUILD_OGL)
592	_ASSERT(psContext && psContext->pglExt);
593	if (!psContext || !psContext->pglExt)
594		return;
595	psContext->pglExt->glDeleteBuffersARB(1, &psVol->piib);
596#endif
597
598#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
599	PVRT_UNREFERENCED_PARAMETER(psContext);
600	FREE(psVol->piib);
601#endif
602}
603
604/*!***********************************************************************
605@Function		PVRTShadowVolSilhouetteProjectedBuild
606@Modified		psVol	The shadow volume information
607@Input			dwVisFlags	Shadow volume creation flags
608@Input			psMesh	The shadow volume mesh
609@Input			pvLightModel	The light position/direction
610@Input			bPointLight		Is the light a point light
611@Input			pContext	A struct for passing in API specific data
612@Description	Using the light set up the shadow volume so it can be extruded.
613*************************************************************************/
614void PVRTShadowVolSilhouetteProjectedBuild(
615	PVRTShadowVolShadowVol			* const psVol,
616	const unsigned int		dwVisFlags,
617	const PVRTShadowVolShadowMesh	* const psMesh,
618	const PVRTVec3		* const pvLightModel,
619	const bool				bPointLight,
620	const SPVRTContext * const pContext)
621{
622	PVRTShadowVolSilhouetteProjectedBuild(psVol, dwVisFlags,psMesh, (PVRTVECTOR3*) pvLightModel, bPointLight, pContext);
623}
624
625/*!***********************************************************************
626@Function		PVRTShadowVolSilhouetteProjectedBuild
627@Modified		psVol	The shadow volume information
628@Input			dwVisFlags	Shadow volume creation flags
629@Input			psMesh	The shadow volume mesh
630@Input			pvLightModel	The light position/direction
631@Input			bPointLight		Is the light a point light
632@Input			pContext	A struct for passing in API specific data
633@Description	Using the light set up the shadow volume so it can be extruded.
634*************************************************************************/
635void PVRTShadowVolSilhouetteProjectedBuild(
636	PVRTShadowVolShadowVol			* const psVol,
637	const unsigned int		dwVisFlags,
638	const PVRTShadowVolShadowMesh	* const psMesh,
639	const PVRTVECTOR3		* const pvLightModel,
640	const bool				bPointLight,
641	const SPVRTContext * const pContext)
642{
643	PVRTVECTOR3		v;
644	PVRTShadowVolMTriangle	*psTri;
645	PVRTShadowVolMEdge		*psEdge;
646	unsigned short	*pwIdx;
647#if defined(BUILD_DX11)
648	HRESULT			hRes;
649#endif
650	unsigned int	nCurr;
651	float			f;
652
653	/*
654		Lock the index buffer; this is where we create the shadow volume
655	*/
656	_ASSERT(psVol && psVol->piib);
657#if defined(BUILD_OGL) || defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
658	PVRT_UNREFERENCED_PARAMETER(pContext);
659#endif
660#if defined(BUILD_DX11)
661	_ASSERT(pContext);
662
663	if(!pContext)
664		return;
665
666	D3D11_MAPPED_SUBRESOURCE data;
667	ID3D11DeviceContext *pDeviceContext = 0;
668	pContext->pDev->GetImmediateContext(&pDeviceContext);
669	hRes = pDeviceContext->Map(psVol->piib, 0, D3D11_MAP_WRITE_DISCARD, NULL, &data);
670	pwIdx = (unsigned short*) data.pData;
671
672	_ASSERT(SUCCEEDED(hRes));
673#endif
674#if defined(BUILD_OGL)
675	_ASSERT(pContext && pContext->pglExt);
676	if (!pContext || !pContext->pglExt)
677		return;
678
679	pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, psVol->piib);
680	pwIdx = (unsigned short*)pContext->pglExt->glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
681#endif
682#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
683	pwIdx = psVol->piib;
684#endif
685
686	psVol->nIdxCnt = 0;
687
688	// Run through triangles, testing which face the From point
689	for(nCurr = 0; nCurr < psMesh->nT; ++nCurr)
690	{
691		PVRTShadowVolMEdge *pE0, *pE1, *pE2;
692		psTri = &psMesh->pT[nCurr];
693		pE0 = &psMesh->pE[psTri->wE0];
694		pE1 = &psMesh->pE[psTri->wE1];
695		pE2 = &psMesh->pE[psTri->wE2];
696
697		if(bPointLight) {
698			v.x = psMesh->pV[pE0->wV0].x - pvLightModel->x;
699			v.y = psMesh->pV[pE0->wV0].y - pvLightModel->y;
700			v.z = psMesh->pV[pE0->wV0].z - pvLightModel->z;
701			f = PVRTMatrixVec3DotProduct(psTri->vNormal, v);
702		} else {
703			f = PVRTMatrixVec3DotProduct(psTri->vNormal, *pvLightModel);
704		}
705
706		if(f >= 0) {
707			/* Triangle is in the light */
708			pE0->nVis |= 0x01;
709			pE1->nVis |= 0x01;
710			pE2->nVis |= 0x01;
711
712			if(dwVisFlags & PVRTSHADOWVOLUME_NEED_CAP_FRONT)
713			{
714				// Add the triangle to the volume, unextruded.
715				pwIdx[psVol->nIdxCnt+0] = psTri->w[0];
716				pwIdx[psVol->nIdxCnt+1] = psTri->w[1];
717				pwIdx[psVol->nIdxCnt+2] = psTri->w[2];
718				psVol->nIdxCnt += 3;
719			}
720		} else {
721			/* Triangle is in shade; set Bit3 if the winding order needs reversed */
722			pE0->nVis |= 0x02 | (psTri->nWinding & 0x01) << 2;
723			pE1->nVis |= 0x02 | (psTri->nWinding & 0x02) << 1;
724			pE2->nVis |= 0x02 | (psTri->nWinding & 0x04);
725
726			if(dwVisFlags & PVRTSHADOWVOLUME_NEED_CAP_BACK) {
727				// Add the triangle to the volume, extruded.
728				// psMesh->nV is used as an offst so that the new index refers to the
729				// corresponding position in the second array of vertices (which are extruded)
730				pwIdx[psVol->nIdxCnt+0] = (unsigned short) psMesh->nV + psTri->w[0];
731				pwIdx[psVol->nIdxCnt+1] = (unsigned short) psMesh->nV + psTri->w[1];
732				pwIdx[psVol->nIdxCnt+2] = (unsigned short) psMesh->nV + psTri->w[2];
733				psVol->nIdxCnt += 3;
734			}
735		}
736	}
737
738#if defined(_DEBUG)
739	_ASSERT(psVol->nIdxCnt <= psVol->nIdxCntMax);
740	for(nCurr = 0; nCurr < psVol->nIdxCnt; ++nCurr) {
741		_ASSERT(pwIdx[nCurr] < psMesh->nV*2);
742	}
743#endif
744
745	/*
746		Run through edges, testing which are silhouette edges
747	*/
748	for(nCurr = 0; nCurr < psMesh->nE; nCurr++) {
749		psEdge = &psMesh->pE[nCurr];
750
751		if((psEdge->nVis & 0x03) == 0x03) {
752			/*
753				Silhouette edge found!
754				The edge is both visible and hidden,
755				so it is along the silhouette of the model
756				(See header notes for more info)
757			*/
758			if(psEdge->nVis & 0x04) {
759				pwIdx[psVol->nIdxCnt+0] = psEdge->wV0;
760				pwIdx[psVol->nIdxCnt+1] = psEdge->wV1;
761				pwIdx[psVol->nIdxCnt+2] = psEdge->wV0 + (unsigned short) psMesh->nV;
762
763				pwIdx[psVol->nIdxCnt+3] = psEdge->wV0 + (unsigned short) psMesh->nV;
764				pwIdx[psVol->nIdxCnt+4] = psEdge->wV1;
765				pwIdx[psVol->nIdxCnt+5] = psEdge->wV1 + (unsigned short) psMesh->nV;
766			} else {
767				pwIdx[psVol->nIdxCnt+0] = psEdge->wV1;
768				pwIdx[psVol->nIdxCnt+1] = psEdge->wV0;
769				pwIdx[psVol->nIdxCnt+2] = psEdge->wV1 + (unsigned short) psMesh->nV;
770
771				pwIdx[psVol->nIdxCnt+3] = psEdge->wV1 + (unsigned short) psMesh->nV;
772				pwIdx[psVol->nIdxCnt+4] = psEdge->wV0;
773				pwIdx[psVol->nIdxCnt+5] = psEdge->wV0 + (unsigned short) psMesh->nV;
774			}
775
776			psVol->nIdxCnt += 6;
777		}
778
779		/* Zero for next render */
780		psEdge->nVis = 0;
781	}
782
783#if defined(_DEBUG)
784	_ASSERT(psVol->nIdxCnt <= psVol->nIdxCntMax);
785	for(nCurr = 0; nCurr < psVol->nIdxCnt; ++nCurr) {
786		_ASSERT(pwIdx[nCurr] < psMesh->nV*2);
787	}
788#endif
789#if defined(BUILD_OGL)
790	pContext->pglExt->glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
791	pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
792#endif
793
794#if defined(BUILD_DX11)
795	pDeviceContext->Unmap(psVol->piib, 0);
796#endif
797}
798
799/*!***********************************************************************
800@Function		IsBoundingBoxVisibleEx
801@Input			pBoundingHyperCube	The hypercube to test against
802@Input			fCamZ				The camera's position along the z-axis
803@Return			bool				Returns true if the bounding box is visible
804@Description	This method tests the bounding box's position against
805				the camera's position to determine if it is visible.
806				If it is visible, the function returns true.
807*************************************************************************/
808static bool IsBoundingBoxVisibleEx(
809	const PVRTVECTOR4	* const pBoundingHyperCube,
810	const float			fCamZ)
811{
812	PVRTVECTOR3	v, vShift[16];
813	unsigned int		dwClipFlags;
814	int			i, j;
815	unsigned short		w0, w1;
816
817	dwClipFlags = 0;		// Assume all are off-screen
818
819	i = 8;
820	while(i)
821	{
822		i--;
823
824		if(pBoundingHyperCube[i].x <  pBoundingHyperCube[i].w)
825			dwClipFlags |= 1 << 0;
826
827		if(pBoundingHyperCube[i].x > -pBoundingHyperCube[i].w)
828			dwClipFlags |= 1 << 1;
829
830		if(pBoundingHyperCube[i].y <  pBoundingHyperCube[i].w)
831			dwClipFlags |= 1 << 2;
832
833		if(pBoundingHyperCube[i].y > -pBoundingHyperCube[i].w)
834			dwClipFlags |= 1 << 3;
835
836		if(pBoundingHyperCube[i].z > 0)
837			dwClipFlags |= 1 << 4;
838	}
839
840	/*
841		Volume is hidden if all the vertices are over a screen edge
842	*/
843	if(dwClipFlags != 0x1F)
844		return false;
845
846	/*
847		Well, according to the simple bounding box check, it might be
848		visible. Let's now test the view frustrum against the bounding
849		cube. (Basically the reverse of the previous test!)
850
851		This catches those cases where a diagonal cube passes near a
852		screen edge.
853	*/
854
855	// Subtract the camera position from the vertices. I.e. move the camera to 0,0,0
856	for(i = 0; i < 8; ++i) {
857		vShift[i].x = pBoundingHyperCube[i].x;
858		vShift[i].y = pBoundingHyperCube[i].y;
859		vShift[i].z = pBoundingHyperCube[i].z - fCamZ;
860	}
861
862	i = 12;
863	while(i) {
864		--i;
865
866		w0 = c_pwLinesHyperCube[2 * i + 0];
867		w1 = c_pwLinesHyperCube[2 * i + 1];
868
869		PVRTMatrixVec3CrossProduct(v, vShift[w0], vShift[w1]);
870		dwClipFlags = 0;
871
872		j = 4;
873		while(j) {
874			--j;
875
876			if(PVRTMatrixVec3DotProduct(c_pvRect[j], v) < 0)
877				++dwClipFlags;
878		}
879
880		// dwClipFlagsA will be 0 or 4 if the screen edges are on the outside of
881		// this bounding-box-silhouette-edge.
882		if(dwClipFlags % 4)
883			continue;
884
885		j = 8;
886		while(j) {
887			--j;
888
889			if((j != w0) & (j != w1) && (PVRTMatrixVec3DotProduct(vShift[j], v) > 0))
890				++dwClipFlags;
891		}
892
893		// dwClipFlagsA will be 0 or 18 if this is a silhouette edge of the bounding box
894		if(dwClipFlags % 12)
895			continue;
896
897		return false;
898	}
899
900	return true;
901}
902
903/*!***********************************************************************
904@Function		IsHyperBoundingBoxVisibleEx
905@Input			pBoundingHyperCube	The hypercube to test against
906@Input			fCamZ				The camera's position along the z-axis
907@Return			bool				Returns true if the bounding box is visible
908@Description	This method tests the hypercube bounding box's position against
909				the camera's position to determine if it is visible.
910				If it is visible, the function returns true.
911*************************************************************************/
912static bool IsHyperBoundingBoxVisibleEx(
913	const PVRTVECTOR4	* const pBoundingHyperCube,
914	const float			fCamZ)
915{
916	const PVRTVECTOR4	*pv0;
917	PVRTVECTOR3	v, vShift[16];
918	unsigned int		dwClipFlagsA, dwClipFlagsB;
919	int			i, j;
920	unsigned short		w0, w1;
921
922	pv0 = &pBoundingHyperCube[8];
923	dwClipFlagsA = 0;		// Assume all are off-screen
924	dwClipFlagsB = 0;
925
926	i = 8;
927	while(i)
928	{
929		i--;
930
931		// Far
932		if(pv0[i].x <  pv0[i].w)
933			dwClipFlagsA |= 1 << 0;
934
935		if(pv0[i].x > -pv0[i].w)
936			dwClipFlagsA |= 1 << 1;
937
938		if(pv0[i].y <  pv0[i].w)
939			dwClipFlagsA |= 1 << 2;
940
941		if(pv0[i].y > -pv0[i].w)
942			dwClipFlagsA |= 1 << 3;
943
944		if(pv0[i].z >  0)
945			dwClipFlagsA |= 1 << 4;
946
947		// Near
948		if(pBoundingHyperCube[i].x <  pBoundingHyperCube[i].w)
949			dwClipFlagsB |= 1 << 0;
950
951		if(pBoundingHyperCube[i].x > -pBoundingHyperCube[i].w)
952			dwClipFlagsB |= 1 << 1;
953
954		if(pBoundingHyperCube[i].y <  pBoundingHyperCube[i].w)
955			dwClipFlagsB |= 1 << 2;
956
957		if(pBoundingHyperCube[i].y > -pBoundingHyperCube[i].w)
958			dwClipFlagsB |= 1 << 3;
959
960		if(pBoundingHyperCube[i].z > 0)
961			dwClipFlagsB |= 1 << 4;
962	}
963
964	/*
965		Volume is hidden if all the vertices are over a screen edge
966	*/
967	if((dwClipFlagsA | dwClipFlagsB) != 0x1F)
968		return false;
969
970	/*
971		Well, according to the simple bounding box check, it might be
972		visible. Let's now test the view frustrum against the bounding
973		hyper cube. (Basically the reverse of the previous test!)
974
975		This catches those cases where a diagonal hyper cube passes near a
976		screen edge.
977	*/
978
979	// Subtract the camera position from the vertices. I.e. move the camera to 0,0,0
980	for(i = 0; i < 16; ++i) {
981		vShift[i].x = pBoundingHyperCube[i].x;
982		vShift[i].y = pBoundingHyperCube[i].y;
983		vShift[i].z = pBoundingHyperCube[i].z - fCamZ;
984	}
985
986	i = 32;
987	while(i) {
988		--i;
989
990		w0 = c_pwLinesHyperCube[2 * i + 0];
991		w1 = c_pwLinesHyperCube[2 * i + 1];
992
993		PVRTMatrixVec3CrossProduct(v, vShift[w0], vShift[w1]);
994		dwClipFlagsA = 0;
995
996		j = 4;
997		while(j) {
998			--j;
999
1000			if(PVRTMatrixVec3DotProduct(c_pvRect[j], v) < 0)
1001				++dwClipFlagsA;
1002		}
1003
1004		// dwClipFlagsA will be 0 or 4 if the screen edges are on the outside of
1005		// this bounding-box-silhouette-edge.
1006		if(dwClipFlagsA % 4)
1007			continue;
1008
1009		j = 16;
1010		while(j) {
1011			--j;
1012
1013			if((j != w0) & (j != w1) && (PVRTMatrixVec3DotProduct(vShift[j], v) > 0))
1014				++dwClipFlagsA;
1015		}
1016
1017		// dwClipFlagsA will be 0 or 18 if this is a silhouette edge of the bounding box
1018		if(dwClipFlagsA % 18)
1019			continue;
1020
1021		return false;
1022	}
1023
1024	return true;
1025}
1026/*!***********************************************************************
1027@Function		IsFrontClipInVolume
1028@Input			pBoundingHyperCube	The hypercube to test against
1029@Return			bool
1030@Description	Returns true if the hypercube is within the view frustrum.
1031*************************************************************************/
1032static bool IsFrontClipInVolume(
1033	const PVRTVECTOR4	* const pBoundingHyperCube)
1034{
1035	const PVRTVECTOR4	*pv0, *pv1;
1036	unsigned int				dwClipFlags;
1037	int					i;
1038	float				fScale, x, y, w;
1039
1040	/*
1041		OK. The hyper-bounding-box is in the view frustrum.
1042
1043		Now decide if we can use Z-pass instead of Z-fail.
1044
1045		TODO: if we calculate the convex hull of the front-clip intersection
1046		points, we can use the connecting lines to do a more accurate on-
1047		screen check (currently it just uses the bounding box of the
1048		intersection points.)
1049	*/
1050	dwClipFlags = 0;
1051
1052	i = 32;
1053	while(i) {
1054		--i;
1055
1056		pv0 = &pBoundingHyperCube[c_pwLinesHyperCube[2 * i + 0]];
1057		pv1 = &pBoundingHyperCube[c_pwLinesHyperCube[2 * i + 1]];
1058
1059		// If both coords are negative, or both coords are positive, it doesn't cross the Z=0 plane
1060		if(pv0->z * pv1->z > 0)
1061			continue;
1062
1063		// TODO: if fScale > 0.5f, do the lerp in the other direction; this is
1064		// because we want fScale to be close to 0, not 1, to retain accuracy.
1065		fScale = (0 - pv0->z) / (pv1->z - pv0->z);
1066
1067		x = fScale * pv1->x + (1.0f - fScale) * pv0->x;
1068		y = fScale * pv1->y + (1.0f - fScale) * pv0->y;
1069		w = fScale * pv1->w + (1.0f - fScale) * pv0->w;
1070
1071		if(x > -w)
1072			dwClipFlags |= 1 << 0;
1073
1074		if(x < w)
1075			dwClipFlags |= 1 << 1;
1076
1077		if(y > -w)
1078			dwClipFlags |= 1 << 2;
1079
1080		if(y < w)
1081			dwClipFlags |= 1 << 3;
1082	}
1083
1084	if(dwClipFlags == 0x0F)
1085		return true;
1086
1087	return false;
1088}
1089
1090/*!***********************************************************************
1091@Function		PVRTShadowVolBoundingBoxExtrude
1092@Modified		pvExtrudedCube	8 Vertices to represent the extruded box
1093@Input			pBoundingBox	The bounding box to extrude
1094@Input			pvLightMdl		The light position/direction
1095@Input			bPointLight		Is the light a point light
1096@Input			fVolLength		The length the volume has been extruded by
1097@Description	Extrudes the bounding box of the volume
1098*************************************************************************/
1099void PVRTShadowVolBoundingBoxExtrude(
1100	PVRTVECTOR3				* const pvExtrudedCube,
1101	const PVRTBOUNDINGBOX	* const pBoundingBox,
1102	const PVRTVECTOR3		* const pvLightMdl,
1103	const bool				bPointLight,
1104	const float				fVolLength)
1105{
1106	int i;
1107
1108	if(bPointLight) {
1109		i = 8;
1110		while(i)
1111		{
1112			i--;
1113
1114			pvExtrudedCube[i].x = pBoundingBox->Point[i].x + fVolLength * (pBoundingBox->Point[i].x - pvLightMdl->x);
1115			pvExtrudedCube[i].y = pBoundingBox->Point[i].y + fVolLength * (pBoundingBox->Point[i].y - pvLightMdl->y);
1116			pvExtrudedCube[i].z = pBoundingBox->Point[i].z + fVolLength * (pBoundingBox->Point[i].z - pvLightMdl->z);
1117		}
1118	} else {
1119		i = 8;
1120		while(i)
1121		{
1122			i--;
1123
1124			pvExtrudedCube[i].x = pBoundingBox->Point[i].x + fVolLength * pvLightMdl->x;
1125			pvExtrudedCube[i].y = pBoundingBox->Point[i].y + fVolLength * pvLightMdl->y;
1126			pvExtrudedCube[i].z = pBoundingBox->Point[i].z + fVolLength * pvLightMdl->z;
1127		}
1128	}
1129}
1130
1131/*!***********************************************************************
1132@Function		PVRTShadowVolBoundingBoxIsVisible
1133@Modified		pdwVisFlags		Visibility flags
1134@Input			bObVisible		Unused set to true
1135@Input			bNeedsZClipping	Unused set to true
1136@Input			pBoundingBox	The volumes bounding box
1137@Input			pmTrans			The projection matrix
1138@Input			pvLightMdl		The light position/direction
1139@Input			bPointLight		Is the light a point light
1140@Input			fCamZProj		The camera's z projection value
1141@Input			fVolLength		The length the volume is extruded by
1142@Description	Determines if the volume is visible and if it needs caps
1143*************************************************************************/
1144void PVRTShadowVolBoundingBoxIsVisible(
1145	unsigned int			* const pdwVisFlags,
1146	const bool				bObVisible,				// Is the object visible?
1147	const bool				bNeedsZClipping,		// Does the object require Z clipping?
1148	const PVRTBOUNDINGBOX	* const pBoundingBox,
1149	const PVRTMATRIX		* const pmTrans,
1150	const PVRTVECTOR3		* const pvLightMdl,
1151	const bool				bPointLight,
1152	const float				fCamZProj,
1153	const float				fVolLength)
1154{
1155	PVRTVECTOR3		pvExtrudedCube[8];
1156	PVRTVECTOR4		BoundingHyperCubeT[16];
1157	int				i;
1158	unsigned int	dwClipFlagsA, dwClipZCnt;
1159	float			fLightProjZ;
1160
1161	PVRT_UNREFERENCED_PARAMETER(bObVisible);
1162	PVRT_UNREFERENCED_PARAMETER(bNeedsZClipping);
1163
1164	_ASSERT((bObVisible && bNeedsZClipping) || !bNeedsZClipping);
1165
1166	/*
1167		Transform the eight bounding box points into projection space
1168	*/
1169	PVRTTransformVec3Array(&BoundingHyperCubeT[0], sizeof(*BoundingHyperCubeT), pBoundingBox->Point,	sizeof(*pBoundingBox->Point),	pmTrans, 8);
1170
1171	/*
1172		Get the light Z coordinate in projection space
1173	*/
1174	fLightProjZ =
1175		pmTrans->f[ 2] * pvLightMdl->x +
1176		pmTrans->f[ 6] * pvLightMdl->y +
1177		pmTrans->f[10] * pvLightMdl->z +
1178		pmTrans->f[14];
1179
1180	/*
1181		Where is the object relative to the near clip plane and light?
1182	*/
1183	dwClipZCnt		= 0;
1184	dwClipFlagsA	= 0;
1185	i = 8;
1186	while(i) {
1187		--i;
1188
1189		if(BoundingHyperCubeT[i].z <= 0)
1190			++dwClipZCnt;
1191
1192		if(BoundingHyperCubeT[i].z <= fLightProjZ)
1193			++dwClipFlagsA;
1194	}
1195
1196	if(dwClipZCnt == 8 && dwClipFlagsA == 8) {
1197		// hidden
1198		*pdwVisFlags = 0;
1199		return;
1200	}
1201
1202	/*
1203		Shadow the bounding box into pvExtrudedCube.
1204	*/
1205	PVRTShadowVolBoundingBoxExtrude(pvExtrudedCube, pBoundingBox, pvLightMdl, bPointLight, fVolLength);
1206
1207	/*
1208		Transform to projection space
1209	*/
1210	PVRTTransformVec3Array(&BoundingHyperCubeT[8], sizeof(*BoundingHyperCubeT), pvExtrudedCube, sizeof(*pvExtrudedCube), pmTrans, 8);
1211
1212	/*
1213		Check whether any part of the hyper bounding box is even visible
1214	*/
1215	if(!IsHyperBoundingBoxVisibleEx(BoundingHyperCubeT, fCamZProj)) {
1216		*pdwVisFlags = 0;
1217		return;
1218	}
1219
1220	/*
1221		It's visible, so choose a render method
1222	*/
1223	if(dwClipZCnt == 8) {
1224		// 1
1225		if(IsFrontClipInVolume(BoundingHyperCubeT)) {
1226			*pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE | PVRTSHADOWVOLUME_NEED_ZFAIL;
1227
1228			if(IsBoundingBoxVisibleEx(&BoundingHyperCubeT[8], fCamZProj))
1229			{
1230				*pdwVisFlags |= PVRTSHADOWVOLUME_NEED_CAP_BACK;
1231			}
1232		} else {
1233			*pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE;
1234		}
1235	} else {
1236		if(!(dwClipZCnt | dwClipFlagsA)) {
1237			// 3
1238			*pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE;
1239		} else {
1240			// 5
1241			if(IsFrontClipInVolume(BoundingHyperCubeT)) {
1242				*pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE | PVRTSHADOWVOLUME_NEED_ZFAIL;
1243
1244				if(IsBoundingBoxVisibleEx(BoundingHyperCubeT, fCamZProj))
1245				{
1246					*pdwVisFlags |= PVRTSHADOWVOLUME_NEED_CAP_FRONT;
1247				}
1248
1249				if(IsBoundingBoxVisibleEx(&BoundingHyperCubeT[8], fCamZProj))
1250				{
1251					*pdwVisFlags |= PVRTSHADOWVOLUME_NEED_CAP_BACK;
1252				}
1253			} else {
1254				*pdwVisFlags = PVRTSHADOWVOLUME_VISIBLE;
1255			}
1256		}
1257	}
1258}
1259
1260/*!***********************************************************************
1261@Function		PVRTShadowVolSilhouetteProjectedRender
1262@Input			psMesh		Shadow volume mesh
1263@Input			psVol		Renderable shadow volume information
1264@Input			pContext	A struct for passing in API specific data
1265@Description	Draws the shadow volume
1266*************************************************************************/
1267int PVRTShadowVolSilhouetteProjectedRender(
1268	const PVRTShadowVolShadowMesh	* const psMesh,
1269	const PVRTShadowVolShadowVol	* const psVol,
1270	const SPVRTContext				* const pContext)
1271{
1272#if defined(BUILD_DX11)
1273	return 0; // Not implemented yet
1274#endif
1275
1276#if defined(BUILD_OGL) || defined(BUILD_OGLES2) || defined(BUILD_OGLES) || defined(BUILD_OGLES3)
1277	_ASSERT(psMesh->pivb);
1278
1279#if defined(_DEBUG) // To fix error in Linux
1280	_ASSERT(psVol->nIdxCnt <= psVol->nIdxCntMax);
1281	_ASSERT(psVol->nIdxCnt % 3 == 0);
1282	_ASSERT(psVol->nIdxCnt / 3 <= 0xFFFF);
1283#endif
1284
1285#if defined(BUILD_OGL)
1286	_ASSERT(pContext && pContext->pglExt);
1287
1288	//Bind the buffers
1289	pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, psMesh->pivb);
1290	pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, psVol->piib);
1291
1292	pContext->pglExt->glEnableVertexAttribArrayARB(0);
1293	pContext->pglExt->glEnableVertexAttribArrayARB(1);
1294
1295	pContext->pglExt->glVertexAttribPointerARB(0, 3, GL_FLOAT, GL_FALSE, sizeof(SVertexShVol), (void*)0);
1296	pContext->pglExt->glVertexAttribPointerARB(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(SVertexShVol), (void*)12);
1297
1298	glDrawElements(GL_TRIANGLES, psVol->nIdxCnt, GL_UNSIGNED_SHORT, NULL);
1299
1300	pContext->pglExt->glDisableVertexAttribArrayARB(0);
1301	pContext->pglExt->glDisableVertexAttribArrayARB(1);
1302
1303	pContext->pglExt->glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
1304	pContext->pglExt->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
1305
1306	return psVol->nIdxCnt / 3;
1307#elif defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
1308	PVRT_UNREFERENCED_PARAMETER(pContext);
1309	GLint i32CurrentProgram;
1310	glGetIntegerv(GL_CURRENT_PROGRAM, &i32CurrentProgram);
1311
1312	_ASSERT(i32CurrentProgram); //no program currently set
1313
1314	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].x);
1315	glEnableVertexAttribArray(0);
1316
1317	glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].dwExtrude);
1318	glEnableVertexAttribArray(1);
1319
1320	glDrawElements(GL_TRIANGLES, psVol->nIdxCnt, GL_UNSIGNED_SHORT, psVol->piib);
1321
1322	glDisableVertexAttribArray(0);
1323	glDisableVertexAttribArray(1);
1324
1325	return psVol->nIdxCnt / 3;
1326
1327#elif defined(BUILD_OGLES)
1328	_ASSERT(pContext && pContext->pglesExt);
1329
1330	glEnableClientState(GL_VERTEX_ARRAY);
1331	glEnableClientState(GL_MATRIX_INDEX_ARRAY_OES);
1332	glEnableClientState(GL_WEIGHT_ARRAY_OES);
1333
1334	glVertexPointer(3, GL_FLOAT, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].x);
1335	pContext->pglesExt->glMatrixIndexPointerOES(1, GL_UNSIGNED_BYTE, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].dwExtrude);
1336	pContext->pglesExt->glWeightPointerOES(1, GL_FLOAT, sizeof(SVertexShVol), &((SVertexShVol*)psMesh->pivb)[0].fWeight);
1337
1338	glDrawElements(GL_TRIANGLES, psVol->nIdxCnt, GL_UNSIGNED_SHORT, psVol->piib);
1339
1340	glDisableClientState(GL_VERTEX_ARRAY);
1341	glDisableClientState(GL_MATRIX_INDEX_ARRAY_OES);
1342	glDisableClientState(GL_WEIGHT_ARRAY_OES);
1343
1344	return psVol->nIdxCnt / 3;
1345#endif
1346
1347#endif
1348}
1349
1350/*****************************************************************************
1351 End of file (PVRTShadowVol.cpp)
1352*****************************************************************************/
1353
1354