//
// Asura_Bicubic_Patch.cpp
//
// Created by Kevin Lea - 12/06/2001 11:04:39
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#ifdef ASURA_TOOLS

#include "Rendering/Asura_Bicubic_Patch.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_PatchSubdivision.h"



static inline void SubdivideColour(Asura_Bicubic_Patch_Vertex* v1, Asura_Bicubic_Patch_Vertex* v2, Asura_Bicubic_Patch_Vertex* vm)
{
	vm->Colour.R = (unsigned char)((v1->Colour.R+v2->Colour.R)>>1);
	vm->Colour.G = (unsigned char)((v1->Colour.G+v2->Colour.G)>>1);
	vm->Colour.B = (unsigned char)((v1->Colour.B+v2->Colour.B)>>1);
	vm->Colour.A = (unsigned char)((v1->Colour.A+v2->Colour.A)>>1);

}

static inline void SubdivideUVs( const Asura_Bicubic_Patch_Vertex* pxV1, const Asura_Bicubic_Patch_Vertex* pxV2, Asura_Bicubic_Patch_Vertex* pxOut )
{
	pxOut->u = (pxV1->u + pxV2->u) * 0.5f;
	pxOut->v = (pxV1->v + pxV2->v) * 0.5f;

	pxOut->u2 = (pxV1->u2 + pxV2->u2) * 0.5f;
	pxOut->v2 = (pxV1->v2 + pxV2->v2) * 0.5f;
}

static inline void SubdividePos( const Asura_Bicubic_Patch_Vertex* pxV1, const Asura_Bicubic_Patch_Vertex* pxV2, Asura_Bicubic_Patch_Vertex* pxOut )
{
    pxOut->du0dv0 = (pxV1->du0dv0 + pxV2->du0dv0) * 0.5f;
}

#if defined(ASURA_TARGET_SM3)
static inline void SubdivideTangentAndBinormal( const Asura_Bicubic_Patch_Vertex* pxV1, const Asura_Bicubic_Patch_Vertex* pxV2, Asura_Bicubic_Patch_Vertex* pxOut )
{
    pxOut->m_xTangent = pxV1->m_xTangent + pxV2->m_xTangent;
	pxOut->m_xTangent.Normalise();

    pxOut->m_xBinormal = pxV1->m_xBinormal + pxV2->m_xBinormal;
	pxOut->m_xBinormal.Normalise();

}
#endif


void Asura_Bicubic_Patch::Render(Asura_Surface* s, bool outputPolys)
{
	m_iMaterialID = s->m_iMaterialID;

	#if 0
	// frustum reject
	if( (BoundingBox.MaxZ>=0.01f)
	  &&(-BoundingBox.MaxX<=BoundingBox.MaxZ)
	  &&(BoundingBox.MinX<=BoundingBox.MaxZ)
	  &&(-BoundingBox.MaxY<=BoundingBox.MaxZ)
	  &&(BoundingBox.MinY<=BoundingBox.MaxZ))
	{

	}
	else
	{
		return;
	}
	
	// back face cull reject
	if (BackFaceCulled())
	{
		return;
	}
	#endif

	// initialise vertex data
	nextAvailableVertex = 0;
	#if 0
	{
	
		for (int i=0; i<ASURA_BICUBIC_PATCH_MAX_NO_OF_VERTICES; i++)
		{
			vIndex[i]=-1;
		}
	}
	#endif
	ThisFrame++;

	noOfIndices=0;

	// set maximum detail required
	maxOrder = MaxDetailLevel;

	if (maxOrder==0)
	{
		noOfEdgeVertices = 2;
	}
	else
	{
		noOfEdgeVertices = (2<<(maxOrder-1))+1;
	}

	// set corners
	int tl = 0;
	int tr = noOfEdgeVertices-1;
	int bl = noOfEdgeVertices*tr;
	int br = bl+tr;

	GetCorner(tl,s,0);
	GetCorner(tr,s,1);
	GetCorner(br,s,2);
	GetCorner(bl,s,3);

	//Flag all the corners as WantsSub so that the lines in between them are allowed to be subdivided
	//RAR 09/04/03
	GetVertex(tl)->WantsSub = true;
	GetVertex(bl)->WantsSub = true;
	GetVertex(tr)->WantsSub = true;
	GetVertex(br)->WantsSub = true;



//	EvaluateCorner(tl,0.0f,0.0f,0);
//	EvaluateCorner(tr,1.0f,0.0f,1);

//	EvaluateCorner(br,1.0f,1.0f,2);
//	EvaluateCorner(bl,0.0f,1.0f,3);


	IsEndCap = s->IsEndCap;
	IsCurved = s->IsCurved;

	// subdivide!
	#if 01
	if (s->IsCurved||s->IsEndCap)
	{
		if (s->NumberOfVertices==3)
		{
			SubdividePatchTri(tl, bl, tr, br, maxOrder, maxOrder);
		}
		else
		{
			SubdividePatch(tl, bl, tr, br, maxOrder, maxOrder);
		}
	}
	else
	#endif
	{
	#if 01
		if (s->NumberOfVertices==4)
		{
			SubdivideFlatPatch(tl, bl, tr, br, maxOrder, maxOrder);
		}
		else
		{
			SubdivideFlatTriPatch(tl, bl, tr, br, maxOrder, maxOrder);
		}
	#endif
	}

	// lighting
//  	CalculateNormals();

	// output polygons
//  	if (outputPolys) OutputTriangles();
	
}
#if 0
void Asura_Bicubic_Patch::SendToVB(int vbIndex)
{
	subdivideSize = 0;

	// initialise vertex data
	{
		nextAvailableVertex = 0;
		for (int i=0; i<Asura_Bicubic_Patch_MAX_NO_OF_VERTICES; i++)
		{
			vIndex[i]=-1;
		}
	}
	noOfIndices=0;
	noOfTriangles=0;

	// set maximum detail required
	maxOrder = MaxDetailLevel;

	if (maxOrder==0)
	{
		noOfEdgeVertices = 2;
	}
	else
	{
		noOfEdgeVertices = (2<<(maxOrder-1))+1;
	}

	// set corners
	int tl = 0;
	int tr = noOfEdgeVertices-1;
	int bl = noOfEdgeVertices*tr;
	int br = bl+tr;

	EvaluateCorner(tl,0.0f,0.0f);
	EvaluateCorner(tr,1.0f,0.0f);
	EvaluateCorner(bl,0.0f,1.0f);
	EvaluateCorner(br,1.0f,1.0f);

	// subdivide!	

	SubdividePatch(tl, bl, tr, br, maxOrder);


	// output polygons
  	OutputTrianglesToVB(vbIndex);
	
}
#endif
void Asura_Bicubic_Patch::EvaluateCorner(int index, float u, float v,int n)
{
#if 0
	NewVertex(index);
	Asura_Bicubic_Patch_Vertex *vertexPtr = GetVertex(index);

    vertexPtr->du0dv0 = EvaluateCurve(u,v);
	vertexPtr->du2dv0 = EvaluateCurve_d2u(u,v);
	vertexPtr->du0dv2 = EvaluateCurve_d2v(u,v);
	vertexPtr->du2dv2 = EvaluateCurve_d2ud2v(u,v);
	vertexPtr->u = U[n];
	vertexPtr->v = V[n];
	vertexPtr->Normal = Normals[n];
#endif
}
void Asura_Bicubic_Patch::GetCorner(int index, Asura_Surface* s, int n)
{
	NewVertex(index);
	Asura_Bicubic_Patch_Vertex *vertexPtr = GetVertex(index);

    vertexPtr->du0dv0 = s->du0dv0[n];
	vertexPtr->du2dv0 = s->du2dv0[n];
	vertexPtr->du0dv2 = s->du0dv2[n];
	
	vertexPtr->u = s->U[n];
	vertexPtr->v = s->V[n];
	vertexPtr->u2 = s->U2[n];
	vertexPtr->v2 = s->V2[n];
	vertexPtr->Diffuse = s->Colour[n];
	vertexPtr->Normal = s->Normal[n];

#if defined(ASURA_TARGET_SM3)
	vertexPtr->m_xLightTransportHarmonic = s->m_axLightTransportHarmonic[n];
	vertexPtr->m_xDiffuseSH = s->m_axDiffuseSH[n];

	// VGP 13Feb2006
	vertexPtr->m_xBrightestLightVector = s->m_axBrightestLightVector[n];

	vertexPtr->m_xTangent = s->m_axTangent[n];
	vertexPtr->m_xBinormal = s->m_axBinormal[n];
	vertexPtr->m_xTangent2 = s->m_axTangent2[n];
	vertexPtr->m_xBinormal2 = s->m_axBinormal2[n];
#endif
}


void Asura_Bicubic_Patch::AddTriangles(int c00, int c01, int c10, int c11)
{
	if (c00!=c10 && c10!=c11 && c00!=c11)
	{
		indexOrder[noOfIndices++] = (unsigned short)vIndex[c00];
		indexOrder[noOfIndices++] = (unsigned short)vIndex[c10];
		indexOrder[noOfIndices++] = (unsigned short)vIndex[c11];
	}
	if (c00!=c01 && c01!=c11 && c00!=c11)
	{	
		indexOrder[noOfIndices++] = (unsigned short)vIndex[c11];
		indexOrder[noOfIndices++] = (unsigned short)vIndex[c01];
		indexOrder[noOfIndices++] = (unsigned short)vIndex[c00];
	}
	
}
void Asura_Bicubic_Patch::AddTriangle(int c0, int c1, int c2)
{
	indexOrder[noOfIndices++] = (unsigned short)vIndex[c0];
	indexOrder[noOfIndices++] = (unsigned short)vIndex[c2];
	indexOrder[noOfIndices++] = (unsigned short)vIndex[c1];
}
	

inline void Asura_Bicubic_Patch::CalcUVs(Asura_Bicubic_Patch_Vertex* r, Asura_Bicubic_Patch_Vertex* v0, Asura_Bicubic_Patch_Vertex* v1, Asura_Bicubic_Patch_Vertex* v2)
{
	float alpha, beta;


//return;
	if (!IsEndCap) return;
	Asura_Vector_3 R = (r->du0dv0-v0->du0dv0);
	Asura_Vector_3 V1 = (v1->du0dv0-v0->du0dv0);
	Asura_Vector_3 V2 = (v2->du0dv0-v0->du0dv0);
	
	if (v0->du0dv0==v1->du0dv0)
	{
		alpha = 0;	
		beta =  (R.Magnitude()/V2.Magnitude());
		//if (R*V2<0) beta=-beta;
	}
	else if (v0->du0dv0==v2->du0dv0)
	{
		
		alpha = (R.Magnitude()/V1.Magnitude());
		beta = 0;
		//zif (R*V1<0) alpha=-alpha;
	}
	else
	{
		
		Asura_Vector_3 n = V1^V2;
		float nm = n.Magnitude();
		if (nm==0.0f)
		{
			return;
		}
		
		{
			Asura_Vector_3 d = R^V2;

			alpha = d.Magnitude()/nm;
			if (d*n<0.0f) alpha = -alpha;
		}
		{
			Asura_Vector_3 d = R^V1;
			beta =  d.Magnitude()/nm;
			if (-d*n<0.0f) beta=-beta;
		}

	}
	

	r->u = v0->u + alpha*(v1->u-v0->u) + beta*(v2->u-v0->u);
	r->v = v0->v + alpha*(v1->v-v0->v) + beta*(v2->v-v0->v);

	r->u2 = v0->u2 + alpha*(v1->u2-v0->u2) + beta*(v2->u2-v0->u2);
	r->v2 = v0->v2 + alpha*(v1->v2-v0->v2) + beta*(v2->v2-v0->v2);
}
void Asura_Bicubic_Patch::SubdividePatch(int c00, int c01, int c10, int c11, int orderU, int orderV)
{

	
	if (!orderU && !orderV)
	{
		AddTriangles(c00,c01,c10,c11);
		return;
	}
	
	float deltaU,deltaV;
	{
		int d = 2<<(MaxDetailLevel-orderU);
		deltaU = 1.0f/((float)d);
		deltaU = deltaU*deltaU*0.5f;
	}
	{
		int d = 2<<(MaxDetailLevel-orderV);
		deltaV = 1.0f/((float)d);
		deltaV = deltaV*deltaV*0.5f;
	}

	Asura_Bicubic_Patch_Vertex* v00 = GetVertex(c00);
	Asura_Bicubic_Patch_Vertex* v10 = GetVertex(c10);
	Asura_Bicubic_Patch_Vertex* v11 = GetVertex(c11);									
	Asura_Bicubic_Patch_Vertex* v01 = GetVertex(c01);


	int um0Index = (c00+c10)/2;
	bool um0IsNew = NewVertex(um0Index);
	Asura_Bicubic_Patch_Vertex* um0 = GetVertex(um0Index);
	if (um0IsNew) 
	{
		Asura_Vector_3 flat;
		
		CD_CurveAndFlat(um0->du0dv0, flat, um0->du2dv0, deltaU, v00->du0dv0, v10->du0dv0, v00->du2dv0, v10->du2dv0);
#if 0
		if (orderU==maxOrder)		
		{
			um0->WantsSub = ShouldSubdivide_Length(v00->du0dv0,v10->du0dv0,flat);
		}
		else
#endif
		{
			um0->WantsSub = ShouldSubdivide(v00->du0dv0,v10->du0dv0,um0->du0dv0,flat);
		}
		if(!v00->WantsSub || !v10->WantsSub)
		{
			//If the vertex at either end of this side was marked as don't subdivide , then don't allow
			//subdivision at this point (or rather ,  make sure any subdivision happening here is subdivision
			//of a straight line).
			//Otherwise tears can occur with lines like this :
			//	-
			// - -
			//-	  -	  -
			//	   - -
			//		-
			//In this case the mid point will be marked as not needing subdivision , but if the polygon gets subdivided due
			//to the opposite side needing it , it will end up deciding to subdivide 1/4 and 3/4 away along the line.
			//This incomprehensible comment was brought to you by RAR 09/04/03
			um0->WantsSub = false;
		}
	
		if (!um0->WantsSub)
		{
			um0->du0dv0 = flat;
		}
		
	}

	int um1Index = (c01+c11)/2;
	bool um1IsNew = NewVertex(um1Index);
	Asura_Bicubic_Patch_Vertex* um1 = GetVertex(um1Index);
	if (um1IsNew) 
	{
		Asura_Vector_3 flat;
		
		CD_CurveAndFlat(um1->du0dv0, flat, um1->du2dv0, deltaU, v01->du0dv0, v11->du0dv0, v01->du2dv0, v11->du2dv0);
#if 0		
		if (orderU==maxOrder)		
		{
			um1->WantsSub = ShouldSubdivide_Length(v01->du0dv0,v11->du0dv0,flat);
		}
		else
#endif
		{
			um1->WantsSub = ShouldSubdivide(v01->du0dv0,v11->du0dv0,um1->du0dv0,flat);
		}
		if(!v01->WantsSub || !v11->WantsSub)
		{
			um1->WantsSub = false;
		}


		if (!um1->WantsSub)
		{
			um1->du0dv0 = flat;
		}


	}
		
	int vm0Index = (c00+c01)/2;
	bool vm0IsNew = NewVertex(vm0Index);
	Asura_Bicubic_Patch_Vertex* vm0 = GetVertex(vm0Index);				
	if (vm0IsNew) 
	{
		Asura_Vector_3 flat;
		

		CD_CurveAndFlat(vm0->du0dv0, flat, vm0->du0dv2, deltaV, v00->du0dv0, v01->du0dv0, v00->du0dv2, v01->du0dv2);
#if 0		
		if (orderV==maxOrder)		
		{
			vm0->WantsSub = ShouldSubdivide_Length(v00->du0dv0,v01->du0dv0,flat);
		}
		else
#endif
		{
			vm0->WantsSub = ShouldSubdivide(v00->du0dv0,v01->du0dv0,vm0->du0dv0,flat);
		}
		
		if(!v00->WantsSub || !v01->WantsSub)
		{
			vm0->WantsSub = false;
		}
				
		
		if (!vm0->WantsSub)
		{
			vm0->du0dv0 = flat;
		}
		
	
	}
		
	int vm1Index = (c10+c11)/2;
	bool vm1IsNew = NewVertex(vm1Index);
	Asura_Bicubic_Patch_Vertex* vm1 = GetVertex(vm1Index);
	if (vm1IsNew) 
	{
		Asura_Vector_3 flat;
		
		CD_CurveAndFlat(vm1->du0dv0, flat, vm1->du0dv2, deltaV, v10->du0dv0, v11->du0dv0, v10->du0dv2, v11->du0dv2);
#if 0		
		if (orderV==maxOrder)		
		{
			vm1->WantsSub = ShouldSubdivide_Length(v10->du0dv0,v11->du0dv0,flat);
		}
		else
#endif
		{
			vm1->WantsSub= ShouldSubdivide(v10->du0dv0,v11->du0dv0,vm1->du0dv0,flat);
		}
			
		if(!v10->WantsSub || !v11->WantsSub)
		{
			vm1->WantsSub = false;
		}

		if (!vm1->WantsSub)
		{
			vm1->du0dv0 = flat;
		}
		

	}
	//float delta2=delta;
	if ((um0->WantsSub||um1->WantsSub)&&(vm0->WantsSub||vm1->WantsSub)&&orderU&&orderV)//&&(v10->du0dv0!=v11->du0dv0))
	{
		if (um0IsNew)
		{
			//CentralDifference(um0->du0dv2, um0->du2dv2, delta2, v00->du0dv2, v10->du0dv2, v00->du2dv2, v10->du2dv2);
			um0->du0dv2 = (v00->du0dv2+v10->du0dv2)*0.5f;
			um0->u = (v10->u+v00->u)*0.5f;
			um0->v = (v10->v+v00->v)*0.5f;
			um0->u2 = (v10->u2+v00->u2)*0.5f;
			um0->v2 = (v10->v2+v00->v2)*0.5f;

			CalcUVs(um0,v00,v10,v01);
			SubdivideColour(v00,v10,um0);
		
			um0->Normal = (v00->Normal+v10->Normal);
			um0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v10,um0);
#endif
		}
		if (um1IsNew)
		{
			//CentralDifference(um1->du0dv2, um1->du2dv2, delta2, v01->du0dv2, v11->du0dv2, v01->du2dv2, v11->du2dv2);
			um1->du0dv2 = (v01->du0dv2+v11->du0dv2)*0.5f;
			um1->u = (v11->u+v01->u)*0.5f;
			um1->v = (v11->v+v01->v)*0.5f;
			um1->u2 = (v11->u2+v01->u2)*0.5f;
			um1->v2 = (v11->v2+v01->v2)*0.5f;

			CalcUVs(um1,v00,v10,v01);
			SubdivideColour(v01,v11,um1);
			
			um1->Normal = (v01->Normal+v11->Normal);
			um1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v01,v11,um1);
#endif
		}
		if (vm0IsNew)
		{
			//CentralDifference(vm0->du2dv0, vm0->du2dv2, delta2, v00->du2dv0, v01->du2dv0, v00->du2dv2, v01->du2dv2);
			vm0->du2dv0 = (v00->du2dv0+v01->du2dv0)*0.5f;
			vm0->u = (v01->u+v00->u)*0.5f;
			vm0->v = (v01->v+v00->v)*0.5f;
			vm0->u2 = (v01->u2+v00->u2)*0.5f;
			vm0->v2 = (v01->v2+v00->v2)*0.5f;
			
			CalcUVs(vm0,v00,v10,v01);
			SubdivideColour(v00,v01,vm0);

			vm0->Normal = (v01->Normal+v00->Normal);
			vm0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v01,vm0);
#endif
		}
		if (vm1IsNew)
		{
			//CentralDifference(vm1->du2dv0, vm1->du2dv2, delta2, v10->du2dv0, v11->du2dv0, v10->du2dv2, v11->du2dv2);
			vm1->du2dv0 = (v10->du2dv0+v11->du2dv0)*0.5f;
			vm1->u = (v11->u+v10->u)*0.5f;
			vm1->v = (v11->v+v10->v)*0.5f;
			vm1->u2 = (v11->u2+v10->u2)*0.5f;
			vm1->v2 = (v11->v2+v10->v2)*0.5f;
			
			CalcUVs(vm1,v00,v10,v01);
			SubdivideColour(v10,v11,vm1);

			vm1->Normal = (v10->Normal+v11->Normal);
			vm1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v10,v11,vm1);
#endif
		}

		int cenIndex = (c00+c11)/2;
		NewVertex(cenIndex);
		Asura_Bicubic_Patch_Vertex* cen = GetVertex(cenIndex);
		
		CentralDifference(cen->du0dv0, cen->du2dv0, deltaU, vm0->du0dv0, vm1->du0dv0, vm0->du2dv0, vm1->du2dv0);
		cen->du0dv2 = (vm0->du0dv2+vm1->du0dv2)*0.5f;
		cen->u = (um0->u+um1->u)*0.5f;
		cen->v = (vm0->v+vm1->v)*0.5f;
		cen->u2 = (um0->u2+um1->u2)*0.5f;
		cen->v2 = (vm0->v2+vm1->v2)*0.5f;

		cen->WantsSub = true;
		SubdivideColour(vm0,vm1,cen);

		CalcUVs(cen,v00,v10,v01);

		cen->Normal = um0->Normal+um1->Normal+vm0->Normal+vm1->Normal;
		cen->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
		SubdivideTangentAndBinormal(vm0,vm1,cen);
#endif

		orderU--;
		orderV--;
		
		SubdividePatch(c00,vm0Index,um0Index,cenIndex,orderU,orderV);
		SubdividePatch(um0Index,cenIndex,c10,vm1Index,orderU,orderV);
		SubdividePatch(vm0Index,c01,cenIndex,um1Index,orderU,orderV);
		SubdividePatch(cenIndex,um1Index,vm1Index,c11,orderU,orderV);
	} 
	else if ((vm0->WantsSub||vm1->WantsSub)&&orderV)
	{
		//if (um1IsNew) DestroyVertex(um1Index);
		//if (um0IsNew) DestroyVertex(um0Index);
		if (um0IsNew)
		{
			//CentralDifference(um0->du0dv2, um0->du2dv2, delta2, v00->du0dv2, v10->du0dv2, v00->du2dv2, v10->du2dv2);
			um0->du0dv2 = (v00->du0dv2+v10->du0dv2)*0.5f;
			um0->u = (v10->u+v00->u)*0.5f;
			um0->v = (v10->v+v00->v)*0.5f;
			um0->u2 = (v10->u2+v00->u2)*0.5f;
			um0->v2 = (v10->v2+v00->v2)*0.5f;
			
			CalcUVs(um0,v00,v10,v01);
			SubdivideColour(v00,v10,um0);

			um0->Normal = (v00->Normal+v10->Normal);
			um0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v10,um0);
#endif
		}
		if (um1IsNew)
		{
			//CentralDifference(um1->du0dv2, um1->du2dv2, delta2, v01->du0dv2, v11->du0dv2, v01->du2dv2, v11->du2dv2);
			um1->du0dv2 = (v01->du0dv2+v11->du0dv2)*0.5f;
			um1->u = (v11->u+v01->u)*0.5f;
			um1->v = (v11->v+v01->v)*0.5f;
			um1->u2 = (v11->u2+v01->u2)*0.5f;
			um1->v2 = (v11->v2+v01->v2)*0.5f;
			
			CalcUVs(um1,v00,v10,v01);
			SubdivideColour(v01,v11,um1);
			
			um1->Normal = (v01->Normal+v11->Normal);
			um1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
		SubdivideTangentAndBinormal(v01,v11,um1);
#endif
		}
		if (vm0IsNew)
		{
			//CentralDifference(vm0->du2dv0, vm0->du2dv2, delta2, v00->du2dv0, v01->du2dv0, v00->du2dv2, v01->du2dv2);
			vm0->du2dv0 = (v00->du2dv0+v01->du2dv0)*0.5f;
			vm0->u = (v01->u+v00->u)*0.5f;
			vm0->v = (v01->v+v00->v)*0.5f;
			vm0->u2 = (v01->u2+v00->u2)*0.5f;
			vm0->v2 = (v01->v2+v00->v2)*0.5f;
			
			CalcUVs(vm0,v00,v10,v01);
			SubdivideColour(v00,v01,vm0);
			
			vm0->Normal = (v01->Normal+v00->Normal);
			vm0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v01,vm0);
#endif
		}
		if (vm1IsNew)
		{
			//CentralDifference(vm1->du2dv0, vm1->du2dv2, delta2, v10->du2dv0, v11->du2dv0, v10->du2dv2, v11->du2dv2);
			vm1->du2dv0 = (v10->du2dv0+v11->du2dv0)*0.5f;
			vm1->u = (v11->u+v10->u)*0.5f;
			vm1->v = (v11->v+v10->v)*0.5f;
			vm1->u2 = (v11->u2+v10->u2)*0.5f;
			vm1->v2 = (v11->v2+v10->v2)*0.5f;
			
			CalcUVs(vm1,v00,v10,v01);
			SubdivideColour(v10,v11,vm1);
			
			vm1->Normal = (v10->Normal+v11->Normal);
			vm1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v10,v11,vm1);
#endif
		}
		orderV--;
		SubdividePatch(c00,vm0Index,c10,vm1Index,orderU,orderV);
		SubdividePatch(vm0Index,c01,vm1Index,c11,orderU,orderV);
	} 
	else if ((um0->WantsSub||um1->WantsSub)&&orderU)
	{
		if (vm1IsNew) DestroyVertex(vm1Index);
		if (vm0IsNew) DestroyVertex(vm0Index);
		if (um0IsNew)
		{
			//CentralDifference(um0->du0dv2, um0->du2dv2, delta2, v00->du0dv2, v10->du0dv2, v00->du2dv2, v10->du2dv2);
			um0->du0dv2 = (v00->du0dv2+v10->du0dv2)*0.5f;
			um0->u = (v10->u+v00->u)*0.5f;
			um0->v = (v10->v+v00->v)*0.5f;
			um0->u2 = (v10->u2+v00->u2)*0.5f;
			um0->v2 = (v10->v2+v00->v2)*0.5f;
			
			CalcUVs(um0,v00,v10,v01);
			SubdivideColour(v00,v10,um0);
			
			um0->Normal = (v00->Normal+v10->Normal);
			um0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v10,um0);
#endif
		}
		if (um1IsNew)
		{
			//CentralDifference(um1->du0dv2, um1->du2dv2, delta2, v01->du0dv2, v11->du0dv2, v01->du2dv2, v11->du2dv2);
			um1->du0dv2 = (v01->du0dv2+v11->du0dv2)*0.5f;
			um1->u = (v11->u+v01->u)*0.5f;
			um1->v = (v11->v+v01->v)*0.5f;
			um1->u2 = (v11->u2+v01->u2)*0.5f;
			um1->v2 = (v11->v2+v01->v2)*0.5f;
			
			CalcUVs(um1,v00,v10,v01);
			SubdivideColour(v01,v11,um1);
			
			um1->Normal = (v01->Normal+v11->Normal);
			um1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v01,v11,um1);
#endif
		}
		orderU--;	
		SubdividePatch(c00,c01,um0Index,um1Index,orderU,orderV);
		SubdividePatch(um0Index,um1Index,c10,c11,orderU,orderV);
	} 
	else // just render
	{

		if (vm1IsNew) DestroyVertex(vm1Index);
		if (vm0IsNew) DestroyVertex(vm0Index);
		if (um1IsNew) DestroyVertex(um1Index);
		if (um0IsNew) DestroyVertex(um0Index);
		
		AddTriangles(c00,c01,c10,c11);
	}

}
void Asura_Bicubic_Patch::SubdividePatchTri(int c00, int c01, int c10, int c11, int orderU, int orderV)
{

	
	if (!orderU && !orderV)
	{
	//	AddTriangles(c00,c01,c10,c11);
		AddTriangle(c00,c01,c10);
		return;
	}
	
	float deltaU,deltaV;
	{
		int d = 2<<(MaxDetailLevel-orderU);
		deltaU = 1.0f/((float)d);
		deltaU = deltaU*deltaU*0.5f;
	}
	{
		int d = 2<<(MaxDetailLevel-orderV);
		deltaV = 1.0f/((float)d);
		deltaV = deltaV*deltaV*0.5f;
	}

	Asura_Bicubic_Patch_Vertex* v00 = GetVertex(c00);
	Asura_Bicubic_Patch_Vertex* v10 = GetVertex(c10);
	Asura_Bicubic_Patch_Vertex* v11 = GetVertex(c11);									
	Asura_Bicubic_Patch_Vertex* v01 = GetVertex(c01);


	int um0Index = (c00+c10)/2;
	bool um0IsNew = NewVertex(um0Index);
	Asura_Bicubic_Patch_Vertex* um0 = GetVertex(um0Index);
	if (um0IsNew) 
	{
		Asura_Vector_3 flat;
		
		CD_CurveAndFlat(um0->du0dv0, flat, um0->du2dv0, deltaU, v00->du0dv0, v10->du0dv0, v00->du2dv0, v10->du2dv0);
#if 0
		if (orderU==maxOrder)		
		{
			um0->WantsSub = ShouldSubdivide_Length(v00->du0dv0,v10->du0dv0,flat);
		}
		else
#endif
		{
			um0->WantsSub = ShouldSubdivide(v00->du0dv0,v10->du0dv0,um0->du0dv0,flat);
		}
	
		if(!v00->WantsSub || !v10->WantsSub)
		{
			um0->WantsSub = false;
		}

		if (!um0->WantsSub)
		{
			um0->du0dv0 = flat;
		}
		
	}

	int um1Index = (c01+c11)/2;
	bool um1IsNew = NewVertex(um1Index);
	Asura_Bicubic_Patch_Vertex* um1 = GetVertex(um1Index);
	if (um1IsNew) 
	{
		Asura_Vector_3 flat;
		
		CD_CurveAndFlat(um1->du0dv0, flat, um1->du2dv0, deltaU, v01->du0dv0, v11->du0dv0, v01->du2dv0, v11->du2dv0);
#if 0
		if (orderU==maxOrder)		
		{
			um1->WantsSub = ShouldSubdivide_Length(v01->du0dv0,v11->du0dv0,flat);
		}
		else
#endif
		{
			um1->WantsSub = ShouldSubdivide(v01->du0dv0,v11->du0dv0,um1->du0dv0,flat);
		}			

		if(!v01->WantsSub || !v11->WantsSub)
		{
			um1->WantsSub = false;
		}
		if (!um1->WantsSub)
		{
			um1->du0dv0 = flat;
		}


	}
		
	int vm0Index = (c00+c01)/2;
	bool vm0IsNew = NewVertex(vm0Index);
	Asura_Bicubic_Patch_Vertex* vm0 = GetVertex(vm0Index);				
	if (vm0IsNew) 
	{
		Asura_Vector_3 flat;
		

		CD_CurveAndFlat(vm0->du0dv0, flat, vm0->du0dv2, deltaV, v00->du0dv0, v01->du0dv0, v00->du0dv2, v01->du0dv2);
#if 0		
		if (orderV==maxOrder)		
		{
			vm0->WantsSub = ShouldSubdivide_Length(v00->du0dv0,v01->du0dv0,flat);
		}
		else
#endif
		{
			vm0->WantsSub = ShouldSubdivide(v00->du0dv0,v01->du0dv0,vm0->du0dv0,flat);
		}		
		
		if(!v00->WantsSub || !v01->WantsSub)
		{
			vm0->WantsSub = false;
		}
		if (!vm0->WantsSub)
		{
			vm0->du0dv0 = flat;
		}
		
	
	}
		
	
	if ((um0->WantsSub||um1->WantsSub)&&(orderU))
	{
		if (vm0IsNew) DestroyVertex(vm0Index);
		if (um0IsNew)
		{
			//CentralDifference(um0->du0dv2, um0->du2dv2, delta2, v00->du0dv2, v10->du0dv2, v00->du2dv2, v10->du2dv2);
			um0->du0dv2 = (v00->du0dv2+v10->du0dv2)*0.5f;
			um0->u = (v10->u+v00->u)*0.5f;
			um0->v = (v10->v+v00->v)*0.5f;
			um0->u2 = (v10->u2+v00->u2)*0.5f;
			um0->v2 = (v10->v2+v00->v2)*0.5f;
			
			CalcUVs(um0,v00,v10,v01);
			SubdivideColour(v00,v10,um0);
			
			um0->Normal = (v00->Normal+v10->Normal);
			um0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v10,um0);
#endif
		}
		if (um1IsNew)
		{
			//CentralDifference(um1->du0dv2, um1->du2dv2, delta2, v01->du0dv2, v11->du0dv2, v01->du2dv2, v11->du2dv2);
			um1->du0dv2 = (v01->du0dv2+v11->du0dv2)*0.5f;
			um1->u = (v11->u+v01->u)*0.5f;
			um1->v = (v11->v+v01->v)*0.5f;
			um1->u2 = (v11->u2+v01->u2)*0.5f;
			um1->v2 = (v11->v2+v01->v2)*0.5f;
			
			CalcUVs(um1,v00,v10,v01);
			SubdivideColour(v01,v10,um1);//v11 colour not set
			
			um1->Normal = (v01->Normal+v11->Normal);
			um1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v01,v10,um1);
#endif
		}
		orderU--;	
		
		SubdividePatch(c00,c01,um0Index,um1Index,orderU,orderV);
		SubdividePatchTri(um0Index,um1Index,c10,c11,orderU,orderV);
	} 
	else if ((vm0->WantsSub)&&(orderV))
	{
	
		//if (um1IsNew) DestroyVertex(um1Index);
		//if (um0IsNew) DestroyVertex(um0Index);
		if (um0IsNew)
		{
			//CentralDifference(um0->du0dv2, um0->du2dv2, delta2, v00->du0dv2, v10->du0dv2, v00->du2dv2, v10->du2dv2);
			um0->du0dv2 = (v00->du0dv2+v10->du0dv2)*0.5f;
			um0->u = (v10->u+v00->u)*0.5f;
			um0->v = (v10->v+v00->v)*0.5f;
			um0->u2 = (v10->u2+v00->u2)*0.5f;
			um0->v2 = (v10->v2+v00->v2)*0.5f;
			
			CalcUVs(um0,v00,v10,v01);
			SubdivideColour(v00,v10,um0);

			um0->Normal = (v00->Normal+v10->Normal);
			um0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v10,um0);
#endif
		}
		if (um1IsNew)
		{
			//CentralDifference(um1->du0dv2, um1->du2dv2, delta2, v01->du0dv2, v11->du0dv2, v01->du2dv2, v11->du2dv2);
			um1->du0dv2 = (v01->du0dv2+v11->du0dv2)*0.5f;
			um1->u = (v11->u+v01->u)*0.5f;
			um1->v = (v11->v+v01->v)*0.5f;
			um1->u2 = (v11->u2+v01->u2)*0.5f;
			um1->v2 = (v11->v2+v01->v2)*0.5f;
			
			CalcUVs(um1,v00,v10,v01);
			SubdivideColour(v01,v10,um1);//v11 colour not set
			
			um1->Normal = (v01->Normal+v11->Normal);
			um1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v01,v10,um1);
#endif
		}
		if (vm0IsNew)
		{
			//CentralDifference(vm0->du2dv0, vm0->du2dv2, delta2, v00->du2dv0, v01->du2dv0, v00->du2dv2, v01->du2dv2);
			vm0->du2dv0 = (v00->du2dv0+v01->du2dv0)*0.5f;
			vm0->u = (v01->u+v00->u)*0.5f;
			vm0->v = (v01->v+v00->v)*0.5f;
			vm0->u2 = (v01->u2+v00->u2)*0.5f;
			vm0->v2 = (v01->v2+v00->v2)*0.5f;
			
			CalcUVs(vm0,v00,v10,v01);
			SubdivideColour(v00,v01,vm0);
			
			vm0->Normal = (v01->Normal+v00->Normal);
			vm0->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00,v01,vm0);
#endif
		}
		
		int vm1Index = (c10+c11)/2;
		bool vm1IsNew = NewVertex(vm1Index);
		Asura_Bicubic_Patch_Vertex* vm1 = GetVertex(vm1Index);
		if (vm1IsNew) 
		{
			Asura_Vector_3 flat;
			
			CD_CurveAndFlat(vm1->du0dv0, flat, vm1->du0dv2, deltaV, v10->du0dv0, v11->du0dv0, v10->du0dv2, v11->du0dv2);
#if 0			
			if (orderV==maxOrder)		
			{
				vm1->WantsSub = ShouldSubdivide_Length(v10->du0dv0,v11->du0dv0,flat);
			}
			else
#endif
			{
				vm1->WantsSub= ShouldSubdivide(v10->du0dv0,v11->du0dv0,vm1->du0dv0,flat);
			}
				
			if(!v10->WantsSub || !v11->WantsSub)
			{
				vm1->WantsSub = false;
			}

			if (!vm1->WantsSub)
			{
				vm1->du0dv0 = flat;
			}
		}
		if (vm1IsNew)
		{
			//CentralDifference(vm1->du2dv0, vm1->du2dv2, delta2, v10->du2dv0, v11->du2dv0, v10->du2dv2, v11->du2dv2);
			vm1->du2dv0 = (v10->du2dv0+v11->du2dv0)*0.5f;
			vm1->u = (v11->u+v10->u)*0.5f;
			vm1->v = (v11->v+v10->v)*0.5f;
			vm1->u2 = (v11->u2+v10->u2)*0.5f;
			vm1->v2 = (v11->v2+v10->v2)*0.5f;
			
			CalcUVs(vm1,v00,v10,v01);
			SubdivideColour(v10,v11,vm1);
			
			vm1->Normal = (v10->Normal+v11->Normal);
			vm1->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v10,v11,vm1);
#endif
		}
		orderV--;
		SubdividePatch(c00,vm0Index,c10,vm1Index,orderU,orderV);
		SubdividePatch(vm0Index,c01,vm1Index,c11,orderU,orderV);
	} 
	else // just render
	{

//		if (vm1IsNew) DestroyVertex(vm1Index);
		if (vm0IsNew) DestroyVertex(vm0Index);
		if (um1IsNew) DestroyVertex(um1Index);
		if (um0IsNew) DestroyVertex(um0Index);
		
		AddTriangles(c00,c01,c10,c11);
	}

}
void Asura_Bicubic_Patch::SubdivideFlatPatch(int c00, int c01, int c10, int c11, int orderU, int orderV)
{
	if ((!orderU && !orderV) || !Asura_PatchSubdivision::GetSubdivideFlat())
	{
		AddTriangles(c00,c01,c10,c11);
		return;
	}
	#if 1
	float deltaU,deltaV;
	{
		int d = 2<<(MaxDetailLevel-orderU);
		deltaU = 1.0f/((float)d);
		deltaU = deltaU*deltaU*0.5f;
	}
	{
		int d = 2<<(MaxDetailLevel-orderV);
		deltaV = 1.0f/((float)d);
		deltaV = deltaV*deltaV*0.5f;
	}

	Asura_Bicubic_Patch_Vertex* v00 = GetVertex(c00);
	Asura_Bicubic_Patch_Vertex* v10 = GetVertex(c10);
	Asura_Bicubic_Patch_Vertex* v11 = GetVertex(c11);									
	Asura_Bicubic_Patch_Vertex* v01 = GetVertex(c01);
	
	int um0Index = 0;
	int um1Index = 0;
	int vm0Index = 0;
	int vm1Index = 0;
	Asura_Bicubic_Patch_Vertex* um0 = 0;
	Asura_Bicubic_Patch_Vertex* um1 = 0;
	Asura_Bicubic_Patch_Vertex* vm0 = 0;
	Asura_Bicubic_Patch_Vertex* vm1 = 0;
	int cenIndex = 0;
	bool um0IsNew=false;
	bool um1IsNew=false;
	bool vm0IsNew=false;
	bool vm1IsNew=false;
	int noOfUEdges = 0;
	int noOfVEdges = 0;

	if (orderU)
	{
		um0Index = (c00+c10)/2;
		um0IsNew = NewVertex(um0Index);
		um0 = GetVertex(um0Index);
		if (um0IsNew) 
		{
            SubdivideUVs( v10, v00, um0 );
			SubdivideColour( v10, v00,um0 );
            SubdividePos( v10, v00, um0 );
#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v10, v00,um0);
#endif
			um0->WantsSub = ShouldSubdivideFlat( v00, v10, um0 );
		}

		um1Index = (c01+c11)/2;
		um1IsNew = NewVertex(um1Index);
		um1 = GetVertex(um1Index);
		if (um1IsNew) 
		{
            SubdivideUVs( v01, v11, um1 );
			SubdivideColour( v01, v11, um1 );
            SubdividePos( v01, v11, um1 );
#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal( v01, v11, um1 );
#endif
			um1->WantsSub = ShouldSubdivideFlat( v01, v11, um1 );
		}

		if (um0->WantsSub) noOfUEdges++;
		if (um1->WantsSub) noOfUEdges++;
	}

	if (orderV)
	{
		vm0Index = (c00+c01)/2;
		vm0IsNew = NewVertex(vm0Index);
		vm0 = GetVertex(vm0Index);				
		if (vm0IsNew) 
		{
            SubdivideUVs( v01, v00, vm0 );
			SubdivideColour( v01, v00, vm0 );
            SubdividePos( v01, v00, vm0 );
#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v01, v00, vm0);
#endif
			vm0->WantsSub = ShouldSubdivideFlat( v01, v00, vm0 );
		}
			
		vm1Index = (c10+c11)/2;
		vm1IsNew = NewVertex(vm1Index);
		vm1 = GetVertex(vm1Index);
		if (vm1IsNew) 
		{
            SubdivideUVs( v11, v10, vm1 );
			SubdivideColour( v11, v10, vm1 );
            SubdividePos( v11, v10, vm1 );
#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v11, v10, vm1);
#endif
			vm1->WantsSub = ShouldSubdivideFlat( v11, v10, vm1 );
		}

		if (vm0->WantsSub) noOfVEdges++;
		if (vm1->WantsSub) noOfVEdges++;
	}



	bool needCentre=(noOfUEdges&&noOfVEdges);
	
	
	if (um0IsNew||needCentre)
	{
		um0->Normal = (v00->Normal+v10->Normal);
		um0->Normal.Normalise();
	}
	if (um1IsNew||needCentre)
	{
		um1->Normal = (v01->Normal+v11->Normal);
		um1->Normal.Normalise();
	}
	if (vm0IsNew||needCentre)
	{
		vm0->Normal = (v01->Normal+v00->Normal);
		vm0->Normal.Normalise();
	}
	if (vm1IsNew||needCentre)
	{
		vm1->Normal = (v10->Normal+v11->Normal);
		vm1->Normal.Normalise();
	}
	
	if (!orderV)
	{
		if (noOfUEdges==2)
		{	
			orderU--;	
			SubdivideFlatPatch(c00,c01,um0Index,um1Index,orderU,orderV);
			SubdivideFlatPatch(um0Index,um1Index,c10,c11,orderU,orderV);
		}
		else if (um0->WantsSub)
		{
			AddTriangle(c00,c01,um0Index);
			AddTriangle(c01,c11,um0Index);
			AddTriangle(um0Index,c11,c10);
		}
		else if (um1->WantsSub)
		{
			AddTriangle(c00,c01,um1Index);
			AddTriangle(c00,um1Index,c10);
			AddTriangle(c10,um1Index,c11);
		}
		else
		{
			AddTriangles(c00,c01,c10,c11);
		}
		return;
	}
	if (!orderU)
	{
		if (noOfVEdges==2)
		{
			orderV--;
			SubdivideFlatPatch(c00,vm0Index,c10,vm1Index,orderU,orderV);
			SubdivideFlatPatch(vm0Index,c01,vm1Index,c11,orderU,orderV);
		}	
		else if (vm0->WantsSub)
		{
			AddTriangle(c00,vm0Index,c10);
			AddTriangle(c10,vm0Index,c11);
			AddTriangle(c11,vm0Index,c01);
		}
		else if (vm1->WantsSub)
		{
			AddTriangle(c00,vm1Index,c10);
			AddTriangle(c01,vm1Index,c00);
			AddTriangle(c01,c11,vm1Index);
		}
		else
		{
			AddTriangles(c00,c01,c10,c11);
		}
		return;
	}

	if (needCentre)
	{
		cenIndex = (c00+c11)/2;
		NewVertex(cenIndex);
		Asura_Bicubic_Patch_Vertex* cen = GetVertex(cenIndex);
		
		cen->du0dv0 = (vm0->du0dv0+vm1->du0dv0)*0.5f;
		cen->u = (um0->u+um1->u)*0.5f;
		cen->v = (vm0->v+vm1->v)*0.5f;
		cen->u2 = (um0->u2+um1->u2)*0.5f;
		cen->v2 = (vm0->v2+vm1->v2)*0.5f;

		//SubdivideColour(um0,um1,cen);
		cen->Colour.R = (unsigned char)((v00->Colour.R+v01->Colour.R+v11->Colour.R+v10->Colour.R)>>2);
		cen->Colour.G = (unsigned char)((v00->Colour.G+v01->Colour.G+v11->Colour.G+v10->Colour.G)>>2);
		cen->Colour.B = (unsigned char)((v00->Colour.B+v01->Colour.B+v11->Colour.B+v10->Colour.B)>>2);
		cen->Colour.A = (unsigned char)((v00->Colour.A+v01->Colour.A+v11->Colour.A+v10->Colour.A)>>2);

		cen->Normal = um0->Normal+um1->Normal+vm0->Normal+vm1->Normal;
		cen->Normal.Normalise();

#if defined(ASURA_TARGET_SM3)
		cen->m_xTangent =	um0->m_xTangent +
										um1->m_xTangent +
										vm0->m_xTangent +
										vm1->m_xTangent;
		cen->m_xTangent.Normalise();

		cen->m_xBinormal =	um0->m_xBinormal +
										um1->m_xBinormal +
										vm0->m_xBinormal +
										vm1->m_xBinormal;
		cen->m_xBinormal.Normalise();
#endif

	}

	if (noOfUEdges==2&&noOfVEdges==2) // subdivide into four quads
	{
		orderU--;
		orderV--;
		
		SubdivideFlatPatch(c00,vm0Index,um0Index,cenIndex,orderU,orderV);
		SubdivideFlatPatch(um0Index,cenIndex,c10,vm1Index,orderU,orderV);
		SubdivideFlatPatch(vm0Index,c01,cenIndex,um1Index,orderU,orderV);
		SubdivideFlatPatch(cenIndex,um1Index,vm1Index,c11,orderU,orderV);
	} 
	else if (noOfVEdges==2)
	{
		orderV--;
	
		SubdivideFlatPatch(c00,vm0Index,c10,vm1Index,orderU,orderV);
		SubdivideFlatPatch(vm0Index,c01,vm1Index,c11,orderU,orderV);
	} 
	else if (noOfUEdges==2)
	{
		orderU--;	
		
		SubdivideFlatPatch(c00,c01,um0Index,um1Index,orderU,orderV);
		SubdivideFlatPatch(um0Index,um1Index,c10,c11,orderU,orderV);
	} 
	else
	{
		if (um0->WantsSub)
		{
			if (vm0->WantsSub)
			{
				orderU--;
				orderV--;
		
				SubdivideFlatPatch(c00,vm0Index,um0Index,cenIndex,orderU,orderV);
				AddTriangle(um0Index,cenIndex,c10);
				AddTriangle(cenIndex,c11,c10);
				AddTriangle(c01,c11,cenIndex);
				AddTriangle(vm0Index,c01,cenIndex);

			}
			else if (vm1->WantsSub)
			{
				orderU--;
				orderV--;
		
				SubdivideFlatPatch(um0Index,cenIndex,c10,vm1Index,orderU,orderV);
				AddTriangle(c00,cenIndex,um0Index);
				AddTriangle(c01,cenIndex,c00);
				AddTriangle(c01,c11,cenIndex);
				AddTriangle(cenIndex,c11,vm1Index);
			}
			else // just render
			{
				AddTriangle(c00,c01,um0Index);
				AddTriangle(c01,c11,um0Index);
				AddTriangle(um0Index,c11,c10);
			}
		}
		else if (um1->WantsSub)
		{
		
			if (vm0->WantsSub)
			{
				orderU--;
				orderV--;
		
				SubdivideFlatPatch(vm0Index,c01,cenIndex,um1Index,orderU,orderV);
				AddTriangle(c00,vm0Index,cenIndex);
				AddTriangle(c00,cenIndex,c10);
				AddTriangle(c10,cenIndex,c11);
				AddTriangle(um1Index,c11,cenIndex);
			}
			else if (vm1->WantsSub)
			{
				orderU--;
				orderV--;
		
				SubdivideFlatPatch(cenIndex,um1Index,vm1Index,c11,orderU,orderV);
				AddTriangle(c01,um1Index,cenIndex);
				AddTriangle(c00,c01,cenIndex);
				AddTriangle(c00,cenIndex,c10);
				AddTriangle(c10,cenIndex,vm1Index);
			}
			else // just render
			{
				AddTriangle(c00,c01,um1Index);
				AddTriangle(c00,um1Index,c10);
				AddTriangle(c10,um1Index,c11);
			}
		}
		else if (vm0->WantsSub)
		{
			AddTriangle(c00,vm0Index,c10);
			AddTriangle(c10,vm0Index,c11);
			AddTriangle(c11,vm0Index,c01);
		}
		else if (vm1->WantsSub)
		{
			AddTriangle(c00,vm1Index,c10);
			AddTriangle(c01,vm1Index,c00);
			AddTriangle(c01,c11,vm1Index);
		}
		else // just render
		{
			AddTriangles(c00,c01,c10,c11);
		}
	}
	#endif	
}

void Asura_Bicubic_Patch::SubdivideFlatTriPatch(int c00, int c01, int c10, int c11, int orderU, int orderV)
{
	if ((!orderU && !orderV) || !Asura_PatchSubdivision::GetSubdivideFlat())
	{
		AddTriangle(c00,c01,c10);
		return;

	}
	#if 1
	//if (!orderU || !orderV) return;
	
//	float deltaU,deltaV;
//	{
//		int d = 2<<(MaxDetailLevel-orderU);
//		deltaU = 1.0f/((float)d);
//		deltaU = deltaU*deltaU*0.5f;
//	}
//	{
//		int d = 2<<(MaxDetailLevel-orderV);
//		deltaV = 1.0f/((float)d);
//		deltaV = deltaV*deltaV*0.5f;
//	}

	Asura_Bicubic_Patch_Vertex* v00 = GetVertex(c00);
	Asura_Bicubic_Patch_Vertex* v10 = GetVertex(c10);
	Asura_Bicubic_Patch_Vertex* v11 = GetVertex(c11);									
	Asura_Bicubic_Patch_Vertex* v01 = GetVertex(c01);
	
	int um0Index = 0;
	int um1Index = 0;
	int vm0Index = 0;
	Asura_Bicubic_Patch_Vertex* um0 = 0;
	Asura_Bicubic_Patch_Vertex* um1 = 0;
	Asura_Bicubic_Patch_Vertex* vm0 = 0;

	bool um0IsNew;
	bool um1IsNew;
	bool vm0IsNew;
	int noOfUEdges = 0;
//	int noOfVEdges = 0;

	if (orderU)
	{
		um0Index = (c00+c10)/2;
		um0IsNew = NewVertex(um0Index);
		um0 = GetVertex(um0Index);
		if (um0IsNew) 
		{
            SubdivideUVs( v00, v10, um0 );
			SubdivideColour( v00, v10, um0 );
            SubdividePos( v00, v10, um0 );
#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal(v00, v10, um0);
#endif
			um0->WantsSub = ShouldSubdivideFlat( v00, v10, um0 );
		}

		um1Index = (c01+c11)/2;
		um1IsNew = NewVertex(um1Index);
		um1 = GetVertex(um1Index);
		if (um1IsNew) 
		{
            SubdivideUVs( v01, v11, um1 );
			SubdivideColour( v01, v10, um1 ); //v11 colour not set
            SubdividePos( v01, v11, um1 );
#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal( v01, v11, um1 );
#endif
			um1->WantsSub = ShouldSubdivideFlat( v01, v11, um1 );
		}

		if (um0->WantsSub) noOfUEdges++;
		if (um1->WantsSub) noOfUEdges++;
	
		if (um0IsNew)
		{
			um0->Normal = (v00->Normal+v10->Normal);
			um0->Normal.Normalise();
		}
		if (um1IsNew)
		{
			um1->Normal = (v01->Normal+v11->Normal);
			um1->Normal.Normalise();
		}
	}

	if (orderV)
	{
		vm0Index = (c00+c01)/2;
		vm0IsNew = NewVertex(vm0Index);
		vm0 = GetVertex(vm0Index);				
		if (vm0IsNew) 
		{
            SubdivideUVs( v00, v01, vm0 );
			SubdivideColour( v00, v01, vm0 ); //v11 colour not set
            SubdividePos( v00, v01, vm0 );
#if defined(ASURA_TARGET_SM3)
			SubdivideTangentAndBinormal( v00, v01, vm0 );
#endif
			vm0->WantsSub = ShouldSubdivideFlat( v00, v01, vm0 );
		}
		
		if (vm0IsNew)
		{
			vm0->Normal = (v01->Normal+v00->Normal);
			vm0->Normal.Normalise();
		}
	}
	
	if (noOfUEdges==2)
	{
		orderU--;	
		SubdivideFlatPatch(c00,c01,um0Index,um1Index,orderU,orderV);
		if (Asura_PatchSubdivision::ShouldAvoidTriangleSlivers() && (orderV>1)) orderV--; // to stop pointiness
		SubdivideFlatTriPatch(um0Index,um1Index,c10,c11,orderU,orderV);
	} 
	else
	{
		if (orderV&&vm0->WantsSub)
		{	
			if (orderU&&um0->WantsSub)
			{
				AddTriangle(c00,vm0Index,um0Index);
				AddTriangle(vm0Index,c01,um0Index);
				AddTriangle(um0Index,c01,c11);
			}
			else if (orderU&&um1->WantsSub)
			{
				AddTriangle(c00,vm0Index,um1Index);
				AddTriangle(vm0Index,c01,um1Index);
				AddTriangle(c00,um1Index,c11);
			

			}
			else 
			{
				AddTriangle(c00,vm0Index,c11);
				AddTriangle(vm0Index,c01,c11);
			}
		}
		else if (orderU&&um0->WantsSub)
		{
			AddTriangle(c00,c01,um0Index);
			AddTriangle(c01,c11,um0Index);
		}
		else if (orderU&&um1->WantsSub)
		{
			AddTriangle(c00,c01,um1Index);
			AddTriangle(c00,um1Index,c11);
		}
		else // just render
		{
			AddTriangle(c00,c01,c11);
		}
	}
	
	#endif	
}
inline void Asura_Bicubic_Patch::CD_CurveAndFlat(Asura_Vector_3& result_curve, Asura_Vector_3& result_flat, Asura_Vector_3& result_d2, float delta, Asura_Vector_3& curve_u1, Asura_Vector_3& curve_u2, Asura_Vector_3& curve_d2_u1, Asura_Vector_3& curve_d2_u2)
{
    result_flat = (curve_u1+curve_u2)*0.5f;
	result_d2 = (curve_d2_u1+curve_d2_u2)*0.5f;
	result_curve = result_flat- result_d2*delta;
}

inline void Asura_Bicubic_Patch::CentralDifference(Asura_Vector_3& result, Asura_Vector_3& result_d2, float delta, Asura_Vector_3& curve_u1, Asura_Vector_3& curve_u2, Asura_Vector_3& curve_d2_u1, Asura_Vector_3& curve_d2_u2)
{
	result = (curve_u1+curve_u2)*0.5f;

	result_d2 = (curve_d2_u1+curve_d2_u2)*0.5f;

	result -= result_d2*delta;
	
}


#include "Camera/Asura_Camera.h"
#if 0
inline bool Asura_Bicubic_Patch::ShouldSubdivide_Length(Asura_Vector_3& v1, Asura_Vector_3& v2, Asura_Vector_3& v4)
{
	//return true;
	float d;

  	{
		
		//return true;
		Asura_Vector_3 l = (v1-v2);
		
		Asura_Vector_3 f = (v4-*Asura_Camera::GetPosition());
		d = (l*l)*2.0f;

		if (d<(Asura_PatchSubdivision::s_fFlatSubdivideSize)*(f*f))
		{
			return false;
		}
		else
		{
			return true;
		}
	}
}
#endif
#include "Light/Asura_Light.h"
inline bool Asura_Bicubic_Patch::ShouldSubdivide( const Asura_Vector_3& v1, const Asura_Vector_3& v2, const Asura_Vector_3& v3, const Asura_Vector_3& v4 )
{
	//return true;
	float d;
	
	if (Asura_PatchSubdivision::ShouldSubdivideEvenly())
	{
		//return false;
		//if ((v1-v3).Magnitude()>0.6f) return true;
		if ((v4-v3).Magnitude()>0.01f) return true;
		return false;
	}

  	{
		//return true;
		Asura_Vector_3 l = (v1-v2);
		Asura_Vector_3 c = (v3-v4);
		float fDist = Asura_PatchSubdivision::GetDistanceSquared(v4,m_iMaterialID);
		fDist = powf(fDist, Asura_PatchSubdivision::GetDistancePower());
//		float s = 1.0f;
		#if 0
		{
			for (int i=0; i<Asura_Lighting_System::NumberOfLocalLights; i++)
			{
				Asura_Light* lightPtr = Asura_Lighting_System::LocalLights[i];

				float r = (v1-lightPtr->Position).Magnitude();

				if (r<lightPtr->Range)
				{
					s = 1.0f;
					break;
				}
			}
		}
		#endif
		//d = (l*l)*s+(c*c)*256.0f;//*32.0f;
		if(Asura_PatchSubdivision::GetSubdivideFlat())
		{
			d = l*l;
		}
		else
		{
			d = c*c*256.0f;
		}

		if (d<(Asura_PatchSubdivision::s_fCurveSubdivideSize)*(fDist))
		{
			return false;
		}
		else
		{
			return true;
		}
	}
}

#include "Core/Asura_Timers.h"
#include "math.h"
#ifdef ASURA_TOOLS
#include "MeshProcessing/Tools_MeshSubdivision.h"
#endif

inline bool Asura_Bicubic_Patch::ShouldSubdivideFlat( const Asura_Bicubic_Patch_Vertex* pxV1, const Asura_Bicubic_Patch_Vertex* pxV2, const Asura_Bicubic_Patch_Vertex* pxMidpoint )
{
	if (Asura_PatchSubdivision::ShouldSubdivideEvenly())
	{
		return false;
		//if ((v1-v2).Magnitude()<0.6f) return false;
		//return true;
	}

    // Subdivide due to proximity to waypoint /cutscene camera track / etc. ?
    {
	    Asura_Vector_3 l = (pxV1->du0dv0 - pxV2->du0dv0);
	    float fDist = Asura_PatchSubdivision::GetDistanceSquared(pxMidpoint->du0dv0,m_iMaterialID);
		fDist = powf(fDist, Asura_PatchSubdivision::GetDistancePower());
	    float s = 1.0f;

	    float d = (l*l)*s;
	    if( d >= (Asura_PatchSubdivision::s_fFlatSubdivideSize)*(fDist) )
	    {
		    return true;
	    }
    }

#ifdef ASURA_TOOLS
    // Subdivide for some tools-specific reason?
    if( Tools_MeshSubdivision::ShouldForceFlatSubdivision(this, pxV1, pxV2, pxMidpoint) )
    {
        return true;
    }
#endif

    return false;
}





Asura_Vector_3 Asura_Bicubic_Patch::EvaluateCurve(float u, float v)
{
	Asura_Vector_3 f(0,0,0);
	
	for(int i=0; i<4; i++)
	{
		for(int j=0; j<4; j++)
		{
			f += ControlPoint[i][j]*Bernstein(i,u)*Bernstein(j,v);
		}
	}
		
	return f;
}
Asura_Vector_3 Asura_Bicubic_Patch::EvaluateCurve_d2u(float u, float v)
{
	Asura_Vector_3 f(0,0,0);
	
	for(int i=0; i<4; i++)
	{
		for(int j=0; j<4; j++)
		{
			f += ControlPoint[i][j]*Bernstein_SecondDif(i,u)*Bernstein(j,v);
		}
	}
		
	return f;
}
Asura_Vector_3 Asura_Bicubic_Patch::EvaluateCurve_d2v(float u, float v)
{
	Asura_Vector_3 f(0,0,0);
	
	for(int i=0; i<4; i++)
	{
		for(int j=0; j<4; j++)
		{
			f += ControlPoint[i][j]*Bernstein(i,u)*Bernstein_SecondDif(j,v);
		}
	}
		
	return f;
}
Asura_Vector_3 Asura_Bicubic_Patch::EvaluateCurve_Normal(float u, float v)
{
	Asura_Vector_3 uNormal, vNormal;

	uNormal.Zero();
	vNormal.Zero();
	
	for(int i=0; i<4; i++)
	{
		for(int j=0; j<4; j++)
		{
			uNormal += ControlPoint[i][j]*Bernstein_FirstDif(i,u)*Bernstein(j,v);
			vNormal += ControlPoint[i][j]*Bernstein(i,u)*Bernstein_FirstDif(j,v);
		}
	}

	Asura_Vector_3 normal = uNormal^vNormal;
	normal.Normalise();

	return normal;

}



float Asura_Bicubic_Patch::Bernstein(int i, float u)
{
#if 1
	if (u==0)
	{
		switch(i)
		{
			default:
			case 3:
			case 2:
			case 1:
			{
				return 0;
			}
			case 0:
			{
				return 1.0f;
			}
		}
	}
	else
	{
		switch(i)
		{
			default:
			case 3:
			{
				return 1.0f;
			}
			case 2:
			case 1:
			case 0:
			{
				return 0;
			}
		}
	}
#else
	switch(i)
	{
		default:
		case 3:
		{
			return u*u*u;
		}
		case 2:
		{
			return 3.0f*u*u*(1.0f-u);
		}
		case 1:
		{
			return 3.0f*u*(1.0f-u)*(1.0f-u);
		}
		case 0:
		{
			return (1.0f-u)*(1.0f-u)*(1.0f-u);
		}
	}
#endif
}

float Asura_Bicubic_Patch::Bernstein_FirstDif(int i, float u)
{
	if (u==0)
	{
		switch(i)
		{
			default:
			case 2:
			{
				return 3.0f;		
			}
			case 3:
			case 1:
			{
				return 0;
			}
			case 0:
			{
				return -3.0f;
			}
		}
	}
	else
	{
		switch(i)
		{
			default:
			case 3:
			{
				return 3.0f;
			}
			case 1:
			{
				return -3.0f;
			}
			case 2:
			case 0:
			{
				return 0;
			}
		}
	}
}

float Asura_Bicubic_Patch::Bernstein_SecondDif(int i, float u)
{
#if 1
	if (u==0)
	{
		switch(i)
		{
			default:
			case 3:
			{
				return 0;
			}
			case 2:
			{
				return 6.0f;
			}
			case 1:
			{
				return -12.0f;
			}
			case 0:
			{
				return 6.0f;
			}
		}
	}
	else
	{
		switch(i)
		{
			default:
			case 3:
			{
				return 6.0f;
			}
			case 2:
			{
				return -12.0f;
			}
			case 1:
			{
				return 6.0f;
			}
			case 0:
			{
				return 0;
			}
		}
	}
	
#else
	switch(i)
	{
		default:
		case 3:
		{
			return 6.0f*u;
		}
		case 2:
		{
			return 6.0f - 18.0f*u;
		}
		case 1:
		{
			return -12.0f + 18.0f*u;
		}
		case 0:
		{
			return 6.0f - 6.0f*u;
		}
	}
#endif
}



void Asura_Bicubic_Patch::OutputTriangles_GraphicNovel(void)
{
}

#endif // #ifdef ASURA_TOOLS
