BOOL InitPlaneBlend(IDirect3DDevice8* pdevice)
{
//////////////////////// Local variables ////////////////////////
	BOOL		res = TRUE ;
	VertexB2	locPlaneData[((MAX_PLANE_TESS_LEVEL_HORZ*2)+1) * ((MAX_PLANE_TESS_LEVEL_VERT*2)+1)] ;
	Face		locFaceData[((MAX_PLANE_TESS_LEVEL_HORZ*2)*(MAX_PLANE_TESS_LEVEL_VERT*2))*4] ;
	DWORD		i, j ;
	float		fHorzPosShift, fVertPosShift ;
	float		fHorzTextureCoordShift ;
	float		fVertTextureCoordShift ;
	float		tu, tv ;
	DWORD		iIndex ;
	D3DVECTOR	Position = { 0.0f } ;
	D3DVECTOR	Normal = { 0.0f, 0.0f, -1.0f } ;
	DWORD		dwNumIndices, dwNumVertices ;
	DWORD		a,b,c, k ;
	DWORD		dwCount1, dwCount2 ;

	float		fWeightStartHorz, fWeightStartVert, fWeightShiftHorz, fWeightShiftVert ;
////////////////////////////////////////////////////////////


	LogMsg("\n In InitPlaneBlend \n") ;

/*	The following code sets up the plane data. This has been explained --
	-- in detail on the web page.
	It's just plain linear math. Shouldn't be too much of a problem.
	Hope my code is correct.

	What I am doing here is linearly interpolate Position, texture co-ordinates,
	and blending weights based on the tesselation level and physical parameters of the plane.
*/

	ZeroMemory(locPlaneData, sizeof(locPlaneData)) ;
	ZeroMemory(locFaceData, sizeof(locFaceData)) ;

	fHorzTextureCoordShift = 0.5f / ((((float)MAX_PLANE_TESS_LEVEL_HORZ)+0.0f)) ;
	fVertTextureCoordShift = 0.5f / ((((float)MAX_PLANE_TESS_LEVEL_VERT)+0.0f)) ;

	fHorzPosShift = (((FLOAT)PLANE_WIDTH) * 0.5f) / ((float)MAX_PLANE_TESS_LEVEL_HORZ) ;
	fVertPosShift = (((FLOAT)PLANE_HEIGHT) * 0.5f) / ((float)MAX_PLANE_TESS_LEVEL_VERT) ;

	fWeightShiftHorz = (0.5f) / ((float)MAX_PLANE_TESS_LEVEL_HORZ) ;
	fWeightShiftVert = (0.0f) ;

	tu = 0.0f ;
	tv = 0.0f ;
	iIndex = 0 ;
	Position.x = -((((FLOAT)PLANE_WIDTH) * 0.5f)) ;
	Position.y = ((((FLOAT)PLANE_HEIGHT) * 0.5f)) ;
	Position.z = 0.0f ;

	g_dwpbNumVertices = dwNumVertices = ((MAX_PLANE_TESS_LEVEL_HORZ*2)+1) * ((MAX_PLANE_TESS_LEVEL_VERT*2)+1) ;
	dwCount1 = ((MAX_PLANE_TESS_LEVEL_VERT*2)+1) ;
	dwCount2 = ((MAX_PLANE_TESS_LEVEL_HORZ*2)+1) ;
	for(i=0;i<dwCount1;i++)
	{
		fWeightStartHorz = 1.0f ;
		fWeightStartVert = 0.0f ;
		for(j=0;j<dwCount2;j++,iIndex++)
		{
			locPlaneData[iIndex].Position = Position ;
			locPlaneData[iIndex].Normal = Normal ;

			locPlaneData[iIndex].tu = tu + (((float)j)*fHorzTextureCoordShift) ;
			locPlaneData[iIndex].tv = tv + (((float)i)*fVertTextureCoordShift) ;

			if(fWeightStartHorz >= 0.5f)
			{
				locPlaneData[iIndex].Weights[0] = fWeightStartHorz ;
				locPlaneData[iIndex].Weights[1] = 1.0f-fWeightStartHorz ;

				locPlaneData[iIndex].Indices[0] = (BYTE)(0) ;
				locPlaneData[iIndex].Indices[1] = (BYTE)(1) ;
			}
			else
			{
				locPlaneData[iIndex].Weights[1] = fWeightStartHorz ;
				locPlaneData[iIndex].Weights[0] = 1.0f-fWeightStartHorz ;

				locPlaneData[iIndex].Indices[1] = (BYTE)(0) ;
				locPlaneData[iIndex].Indices[0] = (BYTE)(1) ;
			}

			fWeightStartHorz -= fWeightShiftHorz ;

			Position.x += (fHorzPosShift) ;
		}
		Position.x = -((((FLOAT)PLANE_WIDTH) * 0.5f)) ;
		Position.y += -(fVertPosShift) ;
	}

	g_dwpbNumIndices = dwNumIndices = ((MAX_PLANE_TESS_LEVEL_HORZ*2)*(MAX_PLANE_TESS_LEVEL_VERT*2))*2 ;
	dwCount1 = (MAX_PLANE_TESS_LEVEL_VERT*2) ;
	dwCount2 = (MAX_PLANE_TESS_LEVEL_HORZ*2)*2 ;
	for(i=0;i<dwCount1;i++)
	{
		k = 0 ;
		for(j=0;j<dwCount2;j+=2,k++)
		{
			a = (k) + ((i) * ((MAX_PLANE_TESS_LEVEL_HORZ*2)+1)) ;
			b = (k+1) + ((i) * ((MAX_PLANE_TESS_LEVEL_HORZ*2)+1)) ;
			c = (k) + ((i+1) * ((MAX_PLANE_TESS_LEVEL_HORZ*2)+1)) ;

			iIndex = (i*dwCount2) + j ;

			locFaceData[iIndex].v[0] = (WORD)a ;
			locFaceData[iIndex].v[1] = (WORD)b ;
			locFaceData[iIndex].v[2] = (WORD)c ;

			locFaceData[iIndex+1].v[0] = (WORD)b ;
			locFaceData[iIndex+1].v[1] = (WORD)(c+1) ;
			locFaceData[iIndex+1].v[2] = (WORD)(c) ;
		}
	}

	/////////// Print plane Debug info ///////////////
	LogMsg("\n Plane Info: \n") ;
	LogMsg("\n ------- Vertex Info -------\n") ;
	LogMsg("\n Num Vertices = %d\n", g_dwpbNumVertices) ;
	for(i=0;i<g_dwpbNumVertices;i++)
	{
		LogMsg("\n Vertex [%03d] : %-5.3f, %-5.3f, %-5.3f", i, locPlaneData[i].Position.x, locPlaneData[i].Position.y, locPlaneData[i].Position.z) ;
		LogMsg("  \t%-4.3f, %-4.3f, Indices = %02d, %02d,   Tex = %-4.3f, %-4.3f", locPlaneData[i].Weights[0], locPlaneData[i].Weights[1], 
			locPlaneData[i].Indices[0], locPlaneData[i].Indices[1], 
			locPlaneData[i].tu, locPlaneData[i].tv) ;

		if(i && (i%((MAX_PLANE_TESS_LEVEL_HORZ*2)))==0)
		{
			LogMsg("\n--- row ---\n") ;
		}
	}

	LogMsg("\n--- End of vertex info ---\n") ;

	LogMsg("\n ----- Face Info ----- \n") ;
	LogMsg("\n Num Faces = %d\n", g_dwpbNumIndices) ;
	for(i=0;i<g_dwpbNumIndices;i++)
	{
		LogMsg("\n Face [%03d] : %d, %d, %d", i, locFaceData[i].v[0], locFaceData[i].v[1], locFaceData[i].v[2]) ;
	}
	/////////////////////////////////////////////


	if(!InitVertexBuffer(&g_pbVB, pdevice, locPlaneData, sizeof(locPlaneData),
		D3DFVF_PB_VERTEXFORMAT, 0, D3DPOOL_DEFAULT))
	{
		LogMsg("\n Failed to Create Vertex Buffer\n") ;
		e_xit ;
	}

	if(!InitIndexBuffer(&g_pbIB, pdevice, locFaceData, sizeof(locFaceData),
		D3DFMT_INDEX16, 0, D3DPOOL_DEFAULT))
	{
		LogMsg("\n Failed to Create Index Buffer\n") ;
		e_xit ;
	}

	if(!LoadTexture(&g_pbTexture, pdevice, _T("Plane.tga"), FALSE, TF_32BITRGB, TRUE))
	{
		LogMsg("\n Failed to Create texture Buffer\n") ;
		e_xit ;
	}

	if(!CreateShadersForPlaneBlend(pdevice))
	{
		LogMsg("\n CreateShadersForPlaneBlend FAILED\n") ;
		e_xit ;
	}

	D3DXMatrixIdentity(&g_rotMatPlane1) ;
	D3DXMatrixIdentity(&g_rotMatPlane2) ;

	// set default shader
	SETVERTEXSHADER(pdevice, g_pbVS[BASIC_VS]) ;

	LogMsg("\n Out of InitPlaneBlend successfully \n") ;

xit :
	return res ;
}



/*
	This procedure creates shaders for the different effects.
	Note that the declaration remains the same for all of them.
*/
static BOOL CreateShadersForPlaneBlend(IDirect3DDevice8* pdevice)
{
	BOOL		res = TRUE ;
	LPCTSTR	lpszShaders[] = {	_T("BasicTr.vso") , 
					_T("BasicTrWithLight.vso") ,
					_T("BlendVS.vso") ,
					_T("BlendNoVS.vso") ,
					_T("BlendWithLight.vso") , } ;
	LPCTSTR	lpszStr ;
	DWORD		declBasicTr[] = 	{
			D3DVSD_STREAM(0),
			D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3 ),		// position
			D3DVSD_REG(D3DVSDE_BLENDWEIGHT, D3DVSDT_FLOAT2 ),	// 2 blending weights
			D3DVSD_REG(D3DVSDE_BLENDINDICES, D3DVSDT_UBYTE4 ),	// 4 indices, (we need only 2). you can use this or use  -- 													// D3DVSDT_SHORT2 and change the data type in the VertexB2 structure
			D3DVSD_REG(D3DVSDE_NORMAL, D3DVSDT_FLOAT3 ),	// normal for light calculation
			D3DVSD_REG(D3DVSDE_TEXCOORD0,D3DVSDT_FLOAT2 ),	// texture co-ordinates
			D3DVSD_END() 	} ;
	DWORD		i ;

	for(i=0;i<sizeof(lpszShaders)/sizeof(lpszShaders[0]);i++)
	{
		lpszStr = lpszShaders[i] ;
		if(!LoadAndCreateShader(pdevice, lpszStr, declBasicTr, 0, SHADERTYPE_VERTEX,
			&g_pbVS[i]))
		{
			LogMsg("\n\n LoadAndCreateShader with [%s] FAILED \n", lpszStr) ;
			e_xit ;
		}
	}

xit :
	return res ;
}