#ifndef SHADOW_CPP
#define SHADOW_CPP

#include "object.h"
#include "add_math.h"

ShadowVolume::ShadowVolume()
{
//	m_pVertices = new D3DXVECTOR3[32000];
	m_pVertices = NULL;
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT ShadowVolume::Render(  )
{
	unsigned int k;
	glColor4f( 1, 1, 1, 0.5f);
	glBegin(GL_TRIANGLES);

	for(k=0;k<m_dwNumVertices;k++)
	{
		glVertex3f( m_pVertices[k].x, m_pVertices[k].y, m_pVertices[k].z );
	}
	glEnd();

	return S_OK;
}

struct ShadowFace
{
	D3DXVECTOR3 p0,p1,p2;
	D3DXVECTOR3 normal;
	ALONG nboor0, nboor1, nboor2;
	ALONG culled;
};

//-----------------------------------------------------------------------------
// Name: BuildFromMesh()
// Desc: Takes a mesh as input, and uses it to build a shadowvolume.
//-----------------------------------------------------------------------------
HRESULT ShadowVolume::BuildFromMesh( class object *pMesh, D3DXVECTOR3 vLight )
{
	ALONG i,j;
	m_dwNumVertices = 0;
    // Lock the geometry buffers
	ALONG dwNumFaces = 0;
    dwNumFaces = pMesh->nbf;

    // Allocate a temporary edge list
    ALONG dwNumEdges = 0;
	unsigned short *rel = pMesh->rel;

	for(i=0;i<pMesh->nbf;i++)
	{
		long ngone = *rel++;
		{
			unsigned short base = *rel;
			for(j=1;j<ngone-1;j++) dwNumFaces++;
		}
	}

    struct ShadowFace *pEdges = new struct ShadowFace[dwNumFaces];
	float *pEdges2 = new float[dwNumFaces*6*3];


	rel = pMesh->rel;

	dwNumFaces = 0;
	for(i=0;i<pMesh->nbf;i++)
	{
		long ngone = *rel++;
		unsigned short base = *rel;
		for(j=1;j<ngone-1;j++)
		{
			pEdges[dwNumFaces].p0.x = pMesh->shape3D[base];
			pEdges[dwNumFaces].p0.y = pMesh->shape3D[base +1];
			pEdges[dwNumFaces].p0.z = pMesh->shape3D[base +2];

			pEdges[dwNumFaces].p1.x = pMesh->shape3D[rel[j] ];
			pEdges[dwNumFaces].p1.y = pMesh->shape3D[rel[j] +1];
			pEdges[dwNumFaces].p1.z = pMesh->shape3D[rel[j] +2];

			pEdges[dwNumFaces].p2.x = pMesh->shape3D[rel[j+1] ];
			pEdges[dwNumFaces].p2.y = pMesh->shape3D[rel[j+1] +1];
			pEdges[dwNumFaces].p2.z = pMesh->shape3D[rel[j+1] +2];

			pEdges[dwNumFaces].normal.x = pMesh->fNormals[i*4];
			pEdges[dwNumFaces].normal.y = pMesh->fNormals[i*4 +1];
			pEdges[dwNumFaces].normal.z = pMesh->fNormals[i*4 +2];

			pEdges[dwNumFaces].nboor0 = -1;
			pEdges[dwNumFaces].nboor1 = -1;
			pEdges[dwNumFaces++].nboor2 = -1;
		}
		rel += ngone;
	}

	for( i=0;i<dwNumFaces;i++)
	{
		if( pEdges[i].nboor0 == -1 )
		{
			for( j=0;j<dwNumFaces;j++)
			{
				if( j!=i)
				{
/*
					if( (pEdges[i].p0 == pEdges[j].p0) &&
						(pEdges[i].p1 == pEdges[j].p1)) 
						pEdges[i].nboor0 = j;

					if( (pEdges[i].p0 == pEdges[j].p1) &&
						(pEdges[i].p1 == pEdges[j].p2)) 
						pEdges[i].nboor0 = j;

					if( (pEdges[i].p0 == pEdges[j].p2) &&
						(pEdges[i].p1 == pEdges[j].p0)) 
						pEdges[i].nboor0 = j;
*/
					long nID = 0;
					if( pEdges[i].p0 == pEdges[j].p0 ) nID++;
					if( pEdges[i].p0 == pEdges[j].p1 ) nID++;
					if( pEdges[i].p0 == pEdges[j].p2 ) nID++;

					if( pEdges[i].p1 == pEdges[j].p0 ) nID++;
					if( pEdges[i].p1 == pEdges[j].p1 ) nID++;
					if( pEdges[i].p1 == pEdges[j].p2 ) nID++;
					if( nID >= 2 ) pEdges[i].nboor0 = j;

				}
			}
		}

		if( pEdges[i].nboor1 == -1 )
		{
			for( j=0;j<dwNumFaces;j++)
			{
				if( j!=i)
				{
/*
					if( (pEdges[i].p1 == pEdges[j].p0) &&
						(pEdges[i].p2 == pEdges[j].p1)) 
						pEdges[i].nboor1 = j;

					if( (pEdges[i].p1 == pEdges[j].p1) &&
						(pEdges[i].p2 == pEdges[j].p2)) 
						pEdges[i].nboor1 = j;

					if( (pEdges[i].p1 == pEdges[j].p2) &&
						(pEdges[i].p2 == pEdges[j].p0)) 
						pEdges[i].nboor1 = j;
*/
					long nID = 0;
					if( pEdges[i].p1 == pEdges[j].p0 ) nID++;
					if( pEdges[i].p1 == pEdges[j].p1 ) nID++;
					if( pEdges[i].p1 == pEdges[j].p2 ) nID++;

					if( pEdges[i].p2 == pEdges[j].p0 ) nID++;
					if( pEdges[i].p2 == pEdges[j].p1 ) nID++;
					if( pEdges[i].p2 == pEdges[j].p2 ) nID++;
					if( nID >= 2 ) pEdges[i].nboor1 = j;

				}
			}
		}

		if( pEdges[i].nboor2 == -1 )
		{
			for( j=0;j<dwNumFaces;j++)
			{
				if( j!=i)
				{
/*
					if( (pEdges[i].p2 == pEdges[j].p0) &&
						(pEdges[i].p0 == pEdges[j].p1)) 
						pEdges[i].nboor2 = j;

					if( (pEdges[i].p2 == pEdges[j].p1) &&
						(pEdges[i].p0 == pEdges[j].p2)) 
						pEdges[i].nboor2 = j;

					if( (pEdges[i].p2 == pEdges[j].p2) &&
						(pEdges[i].p0 == pEdges[j].p0)) 
						pEdges[i].nboor2 = j;
*/
					long nID = 0;
					if( pEdges[i].p2 == pEdges[j].p0 ) nID++;
					if( pEdges[i].p2 == pEdges[j].p1 ) nID++;
					if( pEdges[i].p2 == pEdges[j].p2 ) nID++;

					if( pEdges[i].p0 == pEdges[j].p0 ) nID++;
					if( pEdges[i].p0 == pEdges[j].p1 ) nID++;
					if( pEdges[i].p0 == pEdges[j].p2 ) nID++;
					if( nID >= 2 ) pEdges[i].nboor2 = j;

				}
			}
		}
	}

	for( i=0;i<dwNumFaces;i++)
	{
		float val = (pEdges[i].normal.x * vLight.x) +
					(pEdges[i].normal.y * vLight.y) +
					(pEdges[i].normal.z * vLight.z);

		if( val >= 0 ) pEdges[i].culled = true;
		else pEdges[i].culled = false;
	}

	for( i=0;i<dwNumFaces;i++)
	{
		if( pEdges[i].culled == false )
		{
			if( (pEdges[i].nboor0 == -1) || pEdges[ pEdges[i].nboor0].culled )
			{
				pEdges2[dwNumEdges*6   ] = pEdges[i].p0.x;
				pEdges2[dwNumEdges*6 +1] = pEdges[i].p0.y;
				pEdges2[dwNumEdges*6 +2] = pEdges[i].p0.z;

				pEdges2[dwNumEdges*6 +3] = pEdges[i].p1.x;
				pEdges2[dwNumEdges*6 +4] = pEdges[i].p1.y;
				pEdges2[dwNumEdges*6 +5] = pEdges[i].p1.z;
				dwNumEdges++;
			}

			if( (pEdges[i].nboor1 == -1) || pEdges[ pEdges[i].nboor1].culled )
			{
				pEdges2[dwNumEdges*6   ] = pEdges[i].p1.x;
				pEdges2[dwNumEdges*6 +1] = pEdges[i].p1.y;
				pEdges2[dwNumEdges*6 +2] = pEdges[i].p1.z;

				pEdges2[dwNumEdges*6 +3] = pEdges[i].p2.x;
				pEdges2[dwNumEdges*6 +4] = pEdges[i].p2.y;
				pEdges2[dwNumEdges*6 +5] = pEdges[i].p2.z;
				dwNumEdges++;
			}

			if( (pEdges[i].nboor2 == -1) || pEdges[ pEdges[i].nboor2].culled )
			{
				pEdges2[dwNumEdges*6   ] = pEdges[i].p2.x;
				pEdges2[dwNumEdges*6 +1] = pEdges[i].p2.y;
				pEdges2[dwNumEdges*6 +2] = pEdges[i].p2.z;

				pEdges2[dwNumEdges*6 +3] = pEdges[i].p0.x;
				pEdges2[dwNumEdges*6 +4] = pEdges[i].p0.y;
				pEdges2[dwNumEdges*6 +5] = pEdges[i].p0.z;
				dwNumEdges++;
			}
		}
	}

    for( i=0; i<dwNumEdges; i++ )
    {
		D3DXVECTOR3 v1;
		v1.x = pEdges2[6*i+0];
		v1.y = pEdges2[6*i+1];
		v1.z = pEdges2[6*i+2];

		D3DXVECTOR3 v2;
		v2.x = pEdges2[6*i+3];
		v2.y = pEdges2[6*i+4];
		v2.z = pEdges2[6*i+5];

		D3DXVECTOR3 v3 = v1 - vLight*10;
		D3DXVECTOR3 v4 = v2 - vLight*10;

        // Add a quad (two triangles) to the vertex list
		m_pVertices[m_dwNumVertices++] = v1;
		m_pVertices[m_dwNumVertices++] = v2;
		m_pVertices[m_dwNumVertices++] = v3;

		m_pVertices[m_dwNumVertices++] = v2;
		m_pVertices[m_dwNumVertices++] = v4;
		m_pVertices[m_dwNumVertices++] = v3;
	}

	// Delete the temporary edge list
	delete pEdges;
	delete pEdges2;

    return S_OK;
}

/*
//-----------------------------------------------------------------------------
// Name: RenderShadow()
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::RenderShadow()
{
    // Disable z-buffer writes (note: z-testing still occurs), and enable the
    // stencil-buffer
    m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE,  FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE, TRUE );

    // Dont bother with interpolating color
    m_pd3dDevice->SetRenderState( D3DRS_SHADEMODE,     D3DSHADE_FLAT );

    // Set up stencil compare fuction, reference value, and masks.
    // Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
    // Note: since we set up the stencil-test to always pass, the STENCILFAIL
    // renderstate is really not needed.
    m_pd3dDevice->SetRenderState( D3DRS_STENCILFUNC,  D3DCMP_ALWAYS );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILFAIL,  D3DSTENCILOP_KEEP );

    // If ztest passes, inc/decrement stencil buffer value
    m_pd3dDevice->SetRenderState( D3DRS_STENCILREF,       0x1 );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILMASK,      0xffffffff );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILWRITEMASK, 0xffffffff );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILPASS,      D3DSTENCILOP_INCR );

    // Make sure that no pixels get drawn to the frame buffer
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_ZERO );
    m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

    // Draw front-side of shadow volume in stencil/z only
    m_pd3dDevice->SetTransform( D3DTS_WORLD, &m_matObjectMatrix );
    m_pShadowVolume->Render( m_pd3dDevice );

    // Now reverse cull order so back sides of shadow volume are written.
    m_pd3dDevice->SetRenderState( D3DRS_CULLMODE,   D3DCULL_CW );

    // Decrement stencil buffer value
    m_pd3dDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_DECR );

    // Draw back-side of shadow volume in stencil/z only
    m_pd3dDevice->SetTransform( D3DTS_WORLD, &m_matObjectMatrix );
    m_pShadowVolume->Render( m_pd3dDevice );

    // Restore render states
    m_pd3dDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
    m_pd3dDevice->SetRenderState( D3DRS_CULLMODE,  D3DCULL_CCW );
    m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE,     TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE,    FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DrawShadow()
// Desc: Draws a big gray polygon over scene according to the mask in the
//       stencil buffer. (Any pixel with stencil==1 is in the shadow.)
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::DrawShadow()
{
    // Set renderstates (disable z-buffering, enable stencil, disable fog, and
    // turn on alphablending)
    m_pd3dDevice->SetRenderState( D3DRS_ZENABLE,          FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE,    TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,        FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

    // Only write where stencil val >= 1 (count indicates # of shadows that
    // overlap that pixel)
    m_pd3dDevice->SetRenderState( D3DRS_STENCILREF,  0x1 );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );

    // Draw a big, gray square
    m_pd3dDevice->SetVertexShader( D3DFVF_SHADOWVERTEX );
    m_pd3dDevice->SetStreamSource( 0, m_pBigSquareVB, sizeof(SHADOWVERTEX) );
    m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

    // Restore render states
    m_pd3dDevice->SetRenderState( D3DRS_ZENABLE,          TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE,    FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,        TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

    return S_OK;
}

*/

#endif