#define		D3D_OVERLOADS
#include	<d3d.h>
#include	"volumetric.h"

#define		CreateFace( IndTable, i1, i2, i3 ) (*(IndTable)++ = (WORD)(i1),\
											    *(IndTable)++ = (WORD)(i2),\
											    *(IndTable)++ = (WORD)(i3))
//ConectivityData
//CreateVolume 
//CreateConectivityData
//CreateShadowVolume( vertex, ind, light, ShadowVolume );
//CreateVolumetricLight
//CreateLightVolume( 
//RenderVolumetricLight( VOLUMETRIC
//RenderLightVolume( LIGHTVOLUM
//CreateShiluette( CCDATA, OBJECT, LPWORD )
//RenderShiluette(

#define EdgeInFace( a, b, f ) ( ( (a) == (f)[0] && (b) == (f)[1] ) || ( (a) == (f)[1] && (b) == (f)[0] ) || \
							    ( (a) == (f)[1] && (b) == (f)[2] ) || ( (a) == (f)[2] && (b) == (f)[1] ) || \
							    ( (a) == (f)[2] && (b) == (f)[0] ) || ( (a) == (f)[0] && (b) == (f)[2] ) )
						

VOID CreateConectivityData( LPOBJECT3D object, LPWORD ccdata )
{	
	//ccdata = new WORD[ object->indNbr ];

	for( int i  = 0 ; i<object->indNbr/3 ; i++ )
	{		
		ccdata[i*3+0] = 0xffff;
		ccdata[i*3+1] = 0xffff;
		ccdata[i*3+2] = 0xffff;

		for( int j = 0 ; j<object->indNbr/3 ; j++ )
		{
			if( j == i )
				continue;		
						

			if( EdgeInFace( object->ind[3*i], object->ind[3*i+1], &object->ind[3*j] ) )
			{
				ccdata[i*3] = j;
			}

			if( EdgeInFace( object->ind[3*i+1], object->ind[3*i+2], &object->ind[3*j] ) )
				ccdata[i*3+1] = j;

			if( EdgeInFace( object->ind[3*i+2], object->ind[3*i], &object->ind[3*j] ) )
				ccdata[i*3+2] = j;
		}
	}
}

#define Visible( a, b, c ) ( ((b).x - (a).x)*((c).y - (a).y) > ((c).x - (a).x)*((b).y - (a).y ) )
#define CreateSide( vol, a, b, c, d ) \
	*((vol)++) = (a);\
	*((vol)++) = (b);\
	*((vol)++) = (c);\
	*((vol)++) = (b);\
	*((vol)++) = (d);\
	*((vol)++) = (c)

#define	VOLLENGTH	50.0f

DWORD CreateShadowVolume( LPOBJECT3D obj, LPOBJECT3D o, LPWORD ccdata, gem_Vector pos, LPD3DVERTEX vol )
{
	DWORD			k, kk, ind = 0;
	D3DVERTEX		a1, a2;	
	LPD3DVERTEX		tmp = vol;
	gem_Vector		dir1, dir2;


	for( DWORD i = 0, j = 0 ; i<obj->indNbr/3 ; i++, ind +=3 )
	{
		if( !Visible( o->vert[o->ind[ind]], o->vert[o->ind[ind+1]], o->vert[o->ind[ind+2]] ) )
			continue;

		k  = ccdata[ind]; 
		kk = 3*k; 

		if( k == 0xffff || !Visible( o->vert[o->ind[kk]], o->vert[o->ind[kk+1]], o->vert[o->ind[kk+2]] ) )
		{
			a1 = obj->vert[obj->ind[ind]];
			a2 = obj->vert[obj->ind[ind+1]];

			dir1 = VOLLENGTH*Normalize( gem_Vector( a1.x - pos.x, a1.y - pos.y, a1.z - pos.z ) );
			dir2 = VOLLENGTH*Normalize( gem_Vector( a2.x - pos.x, a2.y - pos.y, a2.z - pos.z ) );

			a1.x += dir1.x; a1.y += dir1.y; a1.z += dir1.z;
			a2.x += dir2.x; a2.y += dir2.y; a2.z += dir2.z;
			
			CreateSide( tmp, obj->vert[obj->ind[ind]], obj->vert[obj->ind[ind+1]], a1, a2 );
			j+=6;
		}

		k  = ccdata[ind+1]; 
		kk = 3*k; 

		if( k == 0xffff || !Visible( o->vert[o->ind[kk]], o->vert[o->ind[kk+1]], o->vert[o->ind[kk+2]] ) )
		{
			a1 = obj->vert[obj->ind[ind+1]];
			a2 = obj->vert[obj->ind[ind+2]];

			dir1 = VOLLENGTH*Normalize( gem_Vector( a1.x - pos.x, a1.y - pos.y, a1.z - pos.z ) );
			dir2 = VOLLENGTH*Normalize( gem_Vector( a2.x - pos.x, a2.y - pos.y, a2.z - pos.z ) );

			a1.x += dir1.x; a1.y += dir1.y; a1.z += dir1.z;
			a2.x += dir2.x; a2.y += dir2.y; a2.z += dir2.z;			
			
			CreateSide( tmp, obj->vert[obj->ind[ind+1]], obj->vert[obj->ind[ind+2]], a1, a2 );			
			j+=6;
		}

		k  = ccdata[ind+2]; 
		kk = 3*k; 

		if( k == 0xffff || !Visible( o->vert[o->ind[kk]], o->vert[o->ind[kk+1]], o->vert[o->ind[kk+2]] ) )
		{
			a1 = obj->vert[obj->ind[ind+2]];
			a2 = obj->vert[obj->ind[ind]];

			dir1 = VOLLENGTH*Normalize( gem_Vector( a1.x - pos.x, a1.y - pos.y, a1.z - pos.z ) );
			dir2 = VOLLENGTH*Normalize( gem_Vector( a2.x - pos.x, a2.y - pos.y, a2.z - pos.z ) );

			a1.x += dir1.x; a1.y += dir1.y; a1.z += dir1.z;
			a2.x += dir2.x; a2.y += dir2.y; a2.z += dir2.z;
			
			CreateSide( tmp, obj->vert[obj->ind[ind+2]], obj->vert[obj->ind[ind]], a1, a2 );			
			j+=6;
		}
	}

	return j;
}


VOID RenderShadowVolume( WORD volnbr, LPD3DVERTEX vol, LPDIRECT3DDEVICE7 pd3dDevice )
{
    // Turn depth buffer off, and stencil buffer on
    pd3dDevice->SetRenderState( D3DRENDERSTATE_ZWRITEENABLE,  FALSE );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILENABLE, TRUE );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, FALSE );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_SHADEMODE, D3DSHADE_FLAT );  

    // Set up stencil compare fuction, reference value, and masks
    // Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILFUNC,     D3DCMP_ALWAYS );

    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILZFAIL, D3DSTENCILOP_KEEP );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILFAIL,  D3DSTENCILOP_KEEP );

       
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILREF,      0x1 );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILMASK,     0xffffffff );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILWRITEMASK,0xffffffff );
    

	D3DMATRIX	mat = (D3DMATRIX)IdentMtx();
	pd3dDevice->SetTransform( D3DTRANSFORMSTATE_WORLD, &mat );

    // Since destcolor=SRCBLEND * SRC_COLOR + DESTBLEND * DEST_COLOR,
    // this should result in the tri color being completely dropped
	
    pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, TRUE );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND,  D3DBLEND_ZERO );
	//pd3dDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND,  D3DBLEND_ONE );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE );

	pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_CCW );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILPASS, D3DSTENCILOP_INCRSAT );
    
	pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_VERTEX, vol, volnbr, NULL );
    	
    
    pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE,   D3DCULL_CW );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILPASS, D3DSTENCILOP_DECRSAT );

    pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_VERTEX, vol, volnbr, NULL );

    // Restore render states
    pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_CCW );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_ZWRITEENABLE,     TRUE );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILENABLE,    FALSE );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, FALSE );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, TRUE );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD );
    
}

VOID RenderShadow( LPDIRECT3DDEVICE7 pd3dDevice )
{
	D3DTLVERTEX		ShadPlane[4];

    ShadPlane[0] = D3DTLVERTEX( D3DVECTOR( 0, 480, 0.5f ), 0.5f, 0x80000000, 0, 0.0f, 1.0f );
    ShadPlane[1] = D3DTLVERTEX( D3DVECTOR( 0, 0, 0.5f), 0.5f, 0x80000000, 0, 0.0f, 0.0f );
    ShadPlane[2] = D3DTLVERTEX( D3DVECTOR( 640, 480, 0.5f ) , 0.5f, 0x80000000, 0, 1.0f, 1.0f );
    ShadPlane[3] = D3DTLVERTEX( D3DVECTOR( 640, 0, 0.5f ), 0.5f, 0x80000000, 0, 1.0f, 0.0f );

	pd3dDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, FALSE );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_SHADEMODE, D3DSHADE_FLAT );  
	pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILENABLE, TRUE );
	
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILFUNC, D3DCMP_LESSEQUAL );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILREF,  0x1 );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILMASK,     0xffffffff );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILWRITEMASK,0xffffffff );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILPASS,  D3DSTENCILOP_KEEP );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILZFAIL, D3DSTENCILOP_KEEP );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILFAIL,  D3DSTENCILOP_KEEP );

	pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, TRUE );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND,  D3DBLEND_SRCALPHA );
    pd3dDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_NONE );
	pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );
	pd3dDevice->SetTexture( 0, NULL );

	pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, D3DFVF_TLVERTEX, ShadPlane, 4, NULL );

	pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );

	pd3dDevice->SetRenderState( D3DRENDERSTATE_STENCILENABLE, FALSE );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, TRUE );
	pd3dDevice->SetRenderState( D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD );  
	pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, FALSE );    
	pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_CCW );
}


// delete volume object
VOID DeleteVolume( LPVOLUME volume )
{
	if( volume->volumeHull ) delete volume->volumeHull;
	if( volume->volumeHullInd ) delete volume->volumeHullInd;
	if( volume->volumeSpot ) delete volume->volumeSpot;
}

// create light volume from given info
VOID CreateVolume( LPVOLUMEINFO info, LPVOLUME volume, FLOAT len, DWORD color )
{
	volume->volumeHull		= new D3DLVERTEX[2*info->vNbr];
	volume->volumeSpot		= new D3DLVERTEX[info->vNbr*100];

	volume->volumeHullInd	= new WORD[6*info->vNbr];

	volume->vNbrHull		= info->vNbr*2;
	volume->iNbrHull		= info->vNbr*6;
	volume->vNbrSpot		= info->vNbr;

	gem_Vector				a;
	gem_Vector*				dir = new gem_Vector[info->vNbr];
	LONG					i;


	// creating hull of volume
	for( i = 0 ; i<info->vNbr ; i++ )
	{
		a = info->chull[i];		
		volume->volumeHull[i].x = a.x;
		volume->volumeHull[i].y = a.y;
		volume->volumeHull[i].z = a.z;

		volume->volumeHull[i].tv = 1.0f;
		volume->volumeHull[i].tu = (FLOAT)i/info->vNbr;

		volume->volumeHull[i].dcColor = 0x80000000|color;		
		
		dir[i] = Normalize(info->chull[i] - info->emiterPos);
	}

	// creating inner spots
	LONG index = info->vNbr;
	for( i = 0 ; i<info->vNbr ; i++, index++ )
	{
		a = len*dir[i] + info->chull[i];

		volume->volumeHull[index].x = a.x;
		volume->volumeHull[index].y = a.y;
		volume->volumeHull[index].z = a.z;


		volume->volumeHull[index].tv = 0.0f;
		volume->volumeHull[index].tu = (FLOAT)i/info->vNbr;

		volume->volumeHull[index].dcColor = color;			
				
	}

	// first spot, it has alha set to 0xff to create a emiter of volume 
	for( LONG j = 0 ; j<info->vNbr ; j++ )
	{
			a = info->chull[j];
			volume->volumeSpot[j].x = a.x;
			volume->volumeSpot[j].y = a.y;
			volume->volumeSpot[j].z = a.z;

			volume->volumeSpot[j].tv = 1.0f;
			volume->volumeSpot[j].tu = (FLOAT)j/info->vNbr;

			volume->volumeSpot[j].dcColor = 0xff000000|color;	
	}

	LONG			k = info->vNbr;		
	
	// ceating faces of volume
	for( LONG l = 1 ; l<100 ; l++ )
	{	
		for( j = 0 ; j<info->vNbr ; j++, k++ )
		{
			a = len*0.01f*(FLOAT)l*dir[j] + info->chull[j];				
			volume->volumeSpot[k].x = a.x;
			volume->volumeSpot[k].y = a.y;
			volume->volumeSpot[k].z = a.z;

			volume->volumeSpot[k].tv = (FLOAT)(100 - l)*0.01f;
			volume->volumeSpot[k].tu = (FLOAT)j/info->vNbr;

			volume->volumeSpot[k].dcColor = 0x05000000|color;			
		}
	}
	
	delete dir;

	LPWORD					idx = volume->volumeHullInd;

	for( i = 0 ; i<info->vNbr-1 ; i++ )
	{
		CreateFace( idx, i, i+1, info->vNbr + i );
		CreateFace( idx, i+1, info->vNbr + i + 1, info->vNbr + i );
	}


	CreateFace( idx, info->vNbr - 1, 0 , info->vNbr + info->vNbr - 1 );
	CreateFace( idx, 0, info->vNbr, info->vNbr + info->vNbr - 1 );

}

// setting a color of volume 
VOID SetVolumeColor( LPVOLUME volume, DWORD color )
{
	LONG			i;

	for( i = 0 ; i<volume->vNbrHull ; i++ )
	{
		volume->volumeHull[i].dcColor&=0xFF000000;
		volume->volumeHull[i].dcColor|=color;
	}


	for( i = 0 ; i<100 ; i++ )
		for( LONG j = 0 ; j<volume->vNbrSpot ; j++ )
		{
			volume->volumeSpot[j].dcColor&=0xFF000000;
			volume->volumeSpot[j].dcColor|=color;
		}
}

// render volumetric light
// chnging same rendring and texture states so be careful
// also chnging current texture
VOID RenderVolume( LPVOLUME volume, LPDIRECTDRAWSURFACE7 volMap, LPDIRECT3DDEVICE7 pd3dDevice )
{	
	if( volMap != NULL )
		pd3dDevice->SetTexture( 0, volMap );

	pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_NONE );
	pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );

	pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, D3DFVF_LVERTEX, 
									  volume->volumeHull, volume->vNbrHull, 
									  volume->volumeHullInd, volume->iNbrHull, NULL );	

	pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_CCW );
	pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );

	pd3dDevice->SetTexture( 0, NULL );
	
	for( int i = 0 ; i<100 ; i++ )
	{		
		
		pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, D3DFVF_LVERTEX,
								   &volume->volumeSpot[i*volume->vNbrSpot], 
								   volume->vNbrSpot, NULL );
	}
}

// extrude edge
VOID ExtrudeEdge( LPD3DLVERTEX side, D3DVERTEX& a1, D3DVERTEX& a2, FLOAT len, gem_Vector& src )
{
	gem_Vector	dir1, dir2;

	dir1.x = a1.x - src.x;
	dir1.y = a1.y - src.y;
	dir1.z = a1.z - src.z;

	dir2.x = a2.x - src.x;
	dir2.y = a2.y - src.y;
	dir2.z = a2.z - src.z;

	dir1 = len*Normalize( dir1 );
	dir2 = len*Normalize( dir2 );
	
	side[0].x = a2.x;
	side[0].y = a2.y;
	side[0].z = a2.z;
	side[0].dcColor = 0x80ffffff;

	side[1].x = a1.x;
	side[1].y = a1.y;
	side[1].z = a1.z;
	side[1].dcColor = 0x80ffffff;

	side[2].x = dir2.x + a2.x;
	side[2].y = dir2.y + a2.y;
	side[2].z = dir2.z + a2.z;
	side[2].dcColor = 0x00ffffff;

	side[3].x = a1.x;
	side[3].y = a1.y;
	side[3].z = a1.z;
	side[3].dcColor = 0x80ffffff;

	side[4].x = dir1.x + a1.x;
	side[4].y = dir1.y + a1.y;
	side[4].z = dir1.z + a1.z;
	side[4].dcColor = 0x00ffffff;

	side[5].x = dir2.x + a2.x;
	side[5].y = dir2.y + a2.y;
	side[5].z = dir2.z + a2.z;
	side[5].dcColor = 0x00ffffff;	
}

#define Equal( a, b ) ( (a).x == (b).x && (a).y == (b).y && (a).z == (b).z )

// check if edge a1, a2 is present in verts
LONG FindEdge( LPD3DLVERTEX verts, DWORD index, D3DVERTEX& a1, D3DVERTEX& a2, DWORD step = 6 )
{
	LONG		foundIndex = -1;

	for( DWORD i = 0 ; i<index ; i+=step )
	{
		if( ( Equal( a1, verts[i] ) && Equal( a2, verts[i+1] ) ) ||
			( Equal( a2, verts[i] ) && Equal( a1, verts[i+1] ) ) )
		{
			foundIndex = i;
			break;
		}
	}

	return foundIndex;
}

// create a object shilluete, cull mode determine if culling (CCW) is preformed for each face
DWORD CreateShiluette( LPOBJECT3D obj, LPD3DLVERTEX vert, LONG start, LONG end, DWORD cull, DWORD color )
{	
	if( end < 0 )
		end = obj->indNbr/3;

	if( start<0 || end*3>obj->indNbr )
		return 0;

	D3DVERTEX		a1, a2;
	LONG			index;

	for( DWORD i = start, j = 0 ; i<(DWORD)end ; i++ )
	{		
		a1 = obj->vert[obj->ind[i*3]];
		a2 = obj->vert[obj->ind[i*3+1]];

		if( ( index = FindEdge( vert, j, a1, a2, 2 ) ) > 0 )
		{
			memcpy( &vert[index], &vert[index+2], (j - index - 2)*sizeof(D3DLVERTEX) );
			j -= 2;
		}
		else
		{
			vert[j].x = a1.x;
			vert[j].y = a1.y;
			vert[j].z = a1.z;			
			vert[j].dcColor = color;
			j++;
			vert[j].x = a2.x;
			vert[j].y = a2.y;
			vert[j].z = a2.z;			
			vert[j].dcColor = color;
			j++;
		}

		a1 = obj->vert[obj->ind[i*3+1]];
		a2 = obj->vert[obj->ind[i*3+2]];

		if( ( index = FindEdge( vert, j, a1, a2, 2 ) ) > 0 )
		{
			memcpy( &vert[index], &vert[index+2], (j - index - 2)*sizeof(D3DLVERTEX) );
			j -= 2;
		}
		else
		{
			vert[j].x = a1.x;
			vert[j].y = a1.y;
			vert[j].z = a1.z;			
			vert[j].dcColor = color;
			j++;
			vert[j].x = a2.x;
			vert[j].y = a2.y;
			vert[j].z = a2.z;			
			vert[j].dcColor = color;
			j++;
		}

		a1 = obj->vert[obj->ind[i*3+2]];
		a2 = obj->vert[obj->ind[i*3+0]];

		if( ( index = FindEdge( vert, j, a1, a2, 2 ) ) > 0 )
		{
			memcpy( &vert[index], &vert[index+2], (j - index - 2)*sizeof(D3DLVERTEX) );
			j -= 2;
		}
		else
		{
			vert[j].x = a1.x;
			vert[j].y = a1.y;
			vert[j].z = a1.z;			
			vert[j].dcColor = color;
			j++;
			vert[j].x = a2.x;
			vert[j].y = a2.y;
			vert[j].z = a2.z;			
			vert[j].dcColor = color;
			j++;
		}
	}

	return j;
}
			
// create a volume from given mesh			
VOID ExtrudeToVolume( LPOBJECT3D obj, LPVOLUME vol, WORD start, WORD end, FLOAT len, gem_Vector& src )
{
	gem_Vector		a[3], b;
	DWORD			i, j, ind, k;	

	if( start*3>= obj->indNbr || start>end )
		return;

	vol->vNbrHull = ( end - start + 1 )*18;
	vol->vNbrSpot = ( end - start + 1 )*120;	

	vol->volumeHull = new D3DLVERTEX[vol->vNbrHull];
	vol->volumeSpot = new D3DLVERTEX[vol->vNbrSpot];

	LPWORD			idx = vol->volumeHullInd;	
	D3DVERTEX		a1, a2;
	LONG			index;
	
	for( i = start, j = 0, ind = start*3 ; i<=end ; i++, ind+=3 )
	{
		a1 = obj->vert[obj->ind[ind]];
		a2 = obj->vert[obj->ind[ind+1]];

		if( (index = FindEdge( vol->volumeHull, j, a1, a2 )) >= 0 )
		{
			memcpy( &vol->volumeHull[index], &vol->volumeHull[index+6], (j - index - 6)*sizeof(D3DLVERTEX) );
			j -= 6;
		}
		else
		{
			ExtrudeEdge( &vol->volumeHull[j], a1, a2, len, src );
			j+=6;
		}

		a1 = obj->vert[obj->ind[ind+1]];
		a2 = obj->vert[obj->ind[ind+2]];

		if( (index = FindEdge( vol->volumeHull, j, a1, a2 )) >= 0 )
		{
			memcpy( &vol->volumeHull[index], &vol->volumeHull[index+6], (j - index - 6)*sizeof(D3DLVERTEX) );
			j -= 6;
		}
		else
		{
			ExtrudeEdge( &vol->volumeHull[j], a1, a2, len, src );
			j+=6;
		}

		a1 = obj->vert[obj->ind[ind+2]];
		a2 = obj->vert[obj->ind[ind]];

		if( (index = FindEdge( vol->volumeHull, j, a1, a2 )) >= 0 )
		{
			memcpy( &vol->volumeHull[index], &vol->volumeHull[index+6], (j - index - 6)*sizeof(D3DLVERTEX) );
			j -= 6;
		}
		else
		{
			ExtrudeEdge( &vol->volumeHull[j], a1, a2, len, src );			
			j+=6;
		}		
	}

	vol->vNbrHull = (WORD)j;
	
	DWORD				objInd;	
	FLOAT				coeff;
	static gem_Vector	dir[500];

	for( i = 0, j = start, objInd = start*3; j<=end ; j++, objInd+=3, i+=3 )
	{
		b.x = obj->vert[obj->ind[objInd]].x - src.x;
		b.y = obj->vert[obj->ind[objInd]].y - src.y;
		b.z = obj->vert[obj->ind[objInd]].z - src.z;
		dir[i] = Normalize( b );

		b.x = obj->vert[obj->ind[objInd+1]].x - src.x;
		b.y = obj->vert[obj->ind[objInd+1]].y - src.y;
		b.z = obj->vert[obj->ind[objInd+1]].z - src.z;
		dir[i+1] = Normalize( b );

		b.x = obj->vert[obj->ind[objInd+2]].x - src.x;
		b.y = obj->vert[obj->ind[objInd+2]].y - src.y;
		b.z = obj->vert[obj->ind[objInd+2]].z - src.z;
		dir[i+2] = Normalize( b );
	}


	for( i = 0, ind = 0 ; i<40 ; i++ )
		for( k = 0, j = start, objInd = start*3; j<=end ; j++, ind+=3, objInd+=3, k+=3 )
		{	
			coeff = 0.025f*(FLOAT)i*len;

			vol->volumeSpot[ind].x = dir[k].x*coeff + obj->vert[obj->ind[objInd]].x;
			vol->volumeSpot[ind].y = dir[k].y*coeff + obj->vert[obj->ind[objInd]].y;
			vol->volumeSpot[ind].z = dir[k].z*coeff + obj->vert[obj->ind[objInd]].z;
			vol->volumeSpot[ind].dcColor = 0x02ffffff;

			vol->volumeSpot[ind+1].x = dir[k+1].x*coeff + obj->vert[obj->ind[objInd+1]].x;
			vol->volumeSpot[ind+1].y = dir[k+1].y*coeff + obj->vert[obj->ind[objInd+1]].y;
			vol->volumeSpot[ind+1].z = dir[k+1].z*coeff + obj->vert[obj->ind[objInd+1]].z;
			vol->volumeSpot[ind+1].dcColor = 0x02ffffff;

			vol->volumeSpot[ind+2].x = dir[k+2].x*coeff + obj->vert[obj->ind[objInd+2]].x;
			vol->volumeSpot[ind+2].y = dir[k+2].y*coeff + obj->vert[obj->ind[objInd+2]].y;
			vol->volumeSpot[ind+2].z = dir[k+2].z*coeff + obj->vert[obj->ind[objInd+2]].z;
			vol->volumeSpot[ind+2].dcColor = 0x02ffffff;			
		}


	for( j = 0; j< (DWORD)(end - start + 1)*3 ; j++ )
		vol->volumeSpot[j].dcColor = 0xffffffff;
}

VOID AddToExtruded( LPOBJECT3D obj, LPVOLUME vol, WORD start, WORD end, FLOAT len, gem_Vector& src )
{
	gem_Vector		a[3], b;	
	DWORD			i, j, ind, k;

	if( start*3>= obj->indNbr || start>end )
		return;	
	
	D3DVERTEX		a1, a2;
	LONG			index;
	
	for( i = start, j = vol->vNbrHull, ind = start*3 ; i<=end ; i++, ind+=3 )
	{
		a1 = obj->vert[obj->ind[ind]];
		a2 = obj->vert[obj->ind[ind+1]];

		if( (index = FindEdge( vol->volumeHull, j, a1, a2 )) >= 0 )
		{
			memcpy( &vol->volumeHull[index], &vol->volumeHull[index+6], (j - index - 6)*sizeof(D3DLVERTEX) );
			j -= 6;
		}
		else
		{
			ExtrudeEdge( &vol->volumeHull[j], a1, a2, len, src );
			j+=6;
		}

		a1 = obj->vert[obj->ind[ind+1]];
		a2 = obj->vert[obj->ind[ind+2]];

		if( (index = FindEdge( vol->volumeHull, j, a1, a2 )) >= 0 )
		{
			memcpy( &vol->volumeHull[index], &vol->volumeHull[index+6], (j - index - 6)*sizeof(D3DLVERTEX) );
			j -= 6;
		}
		else
		{
			ExtrudeEdge( &vol->volumeHull[j], a1, a2, len, src );
			j+=6;
		}

		a1 = obj->vert[obj->ind[ind+2]];
		a2 = obj->vert[obj->ind[ind]];

		if( (index = FindEdge( vol->volumeHull, j, a1, a2 )) >= 0 )
		{
			memcpy( &vol->volumeHull[index], &vol->volumeHull[index+6], (j - index - 6)*sizeof(D3DLVERTEX) );
			j -= 6;
		}
		else
		{
			ExtrudeEdge( &vol->volumeHull[j], a1, a2, len, src );			
			j+=6;
		}		
	}

	vol->vNbrHull = (WORD)j;	


	DWORD				objInd;
	FLOAT				coeff;
	static gem_Vector	dir[500];

	for( i = 0, j = start, objInd = start*3; j<=end ; j++, objInd+=3, i+=3 )
	{
		b.x = obj->vert[obj->ind[objInd]].x - src.x;
		b.y = obj->vert[obj->ind[objInd]].y - src.y;
		b.z = obj->vert[obj->ind[objInd]].z - src.z;
		dir[i] = Normalize( b );

		b.x = obj->vert[obj->ind[objInd+1]].x - src.x;
		b.y = obj->vert[obj->ind[objInd+1]].y - src.y;
		b.z = obj->vert[obj->ind[objInd+1]].z - src.z;
		dir[i+1] = Normalize( b );

		b.x = obj->vert[obj->ind[objInd+2]].x - src.x;
		b.y = obj->vert[obj->ind[objInd+2]].y - src.y;
		b.z = obj->vert[obj->ind[objInd+2]].z - src.z;
		dir[i+2] = Normalize( b );
	}


	for( i = 0, ind = 0 ; i<40 ; i++ )
		for( k = 0, j = start, objInd = start*3; j<=end ; j++, ind+=3, objInd+=3, k+=3 )
		{	
			coeff = 0.025f*(FLOAT)i*len;

			vol->volumeSpot[ind].x = dir[k].x*coeff + obj->vert[obj->ind[objInd]].x;
			vol->volumeSpot[ind].y = dir[k].y*coeff + obj->vert[obj->ind[objInd]].y;
			vol->volumeSpot[ind].z = dir[k].z*coeff + obj->vert[obj->ind[objInd]].z;
			vol->volumeSpot[ind].dcColor = 0x02ffffff;

			vol->volumeSpot[ind+1].x = dir[k+1].x*coeff + obj->vert[obj->ind[objInd+1]].x;
			vol->volumeSpot[ind+1].y = dir[k+1].y*coeff + obj->vert[obj->ind[objInd+1]].y;
			vol->volumeSpot[ind+1].z = dir[k+1].z*coeff + obj->vert[obj->ind[objInd+1]].z;
			vol->volumeSpot[ind+1].dcColor = 0x02ffffff;

			vol->volumeSpot[ind+2].x = dir[k+2].x*coeff + obj->vert[obj->ind[objInd+2]].x;
			vol->volumeSpot[ind+2].y = dir[k+2].y*coeff + obj->vert[obj->ind[objInd+2]].y;
			vol->volumeSpot[ind+2].z = dir[k+2].z*coeff + obj->vert[obj->ind[objInd+2]].z;
			vol->volumeSpot[ind+2].dcColor = 0x02ffffff;			
		}


	for( j = 0; j< (DWORD)(end - start + 1)*3 ; j++ )
		vol->volumeSpot[j].dcColor = 0xffffffff;

	vol->vNbrSpot += (end-start+1)*120;
}


VOID RenderExtruded( LPVOLUME volume, LPDIRECT3DDEVICE7 pd3dDevice )
{
	pd3dDevice->SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_NONE );

	pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_LVERTEX, 								
							   volume->volumeHull, volume->vNbrHull, NULL );	

	pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_LVERTEX, 								
							   volume->volumeSpot, volume->vNbrSpot, NULL );	
}

VOID SetExtrudedColor( LPVOLUME volume, DWORD color )
{
	LONG			i;

	for( i = 0 ; i<volume->vNbrHull ; i++ )
	{
		volume->volumeHull[i].dcColor&=0xFF000000;
		volume->volumeHull[i].dcColor|=color;
	}


	for( LONG j = 0 ; j<volume->vNbrSpot ; j++ )
	{
		volume->volumeSpot[j].dcColor&=0xFF000000;
		volume->volumeSpot[j].dcColor|=color;
	}
}


