#include "stdafx.h"													// for precompiled headers (has to be in first place)
#include "error.h"													// Error, Message
#include "3dobject.h"												// C3DObject
#include <d3d8.h>														// IDirect3D8
#include "3DRenderer.h"											// POLYBUMPVERTEX
#include <assert.h>													// assert()
#include "ASCIIFile.h"											// CASCIIFile
#include "3DTools.h"												// CalcAngle
#include "RGBPackedBase.h"									// CRGBPackedBase
#include "TangentSpaceCalculation.h"				// CTangentSpaceCalculation


#include "NvTriStrip.h"
//#include "Cry_Math.h"


#include <map>															// STL map<>
#include <hash_map>													// STL hash_multimap<>
using namespace std;												// STL





#define SMALL_FLOAT 1e-12

#define LEFTCOMPARE(aa)							if(strncmp((const char *)p,aa,strlen(aa))==0)
#define VECTOR_TO_DWORD(src,dest)		{ D3DXVECTOR3 cpy=src;																			\
																		if(cpy.x<0)cpy.x=0;	if(cpy.y<0)cpy.y=0;	if(cpy.z<0)cpy.z=0;	\
																		if(cpy.x>1)cpy.x=1;	if(cpy.y>1)cpy.y=1;	if(cpy.z>1)cpy.z=1;	\
																		dest=((DWORD)(cpy.x*255.0f)<<16)|((DWORD)(cpy.y*255.0f)<<8)|(DWORD)(cpy.z*255.0f);}

// constructor
C3DObject::C3DObject( void )
{
	m_vMinBoundingBox=D3DXVECTOR3(FLT_MAX,FLT_MAX,FLT_MAX);
	m_vMaxBoundingBox=D3DXVECTOR3(-FLT_MAX,-FLT_MAX,-FLT_MAX);
	m_bObjectSpace=false;
}


// destructor
C3DObject::~C3DObject( void )
{
	FreeData();
}


void C3DObject::FreeData( void )
{
	vector<IDirect3DVertexBuffer8 *>::iterator it1;
	vector<IDirect3DIndexBuffer8 *>::iterator it2;

	for(it1=m_VertexBuffers.begin();it1!=m_VertexBuffers.end();++it1)
		(*it1)->Release();

	for(it2=m_IndexBuffers.begin();it2!=m_IndexBuffers.end();++it2)
		(*it2)->Release();

	m_MaterialManager.FreeData();
	m_VertexBuffers.clear();
	m_IndexBuffers.clear();
	m_TriangleBunch.clear();
}


bool IsWhitespace( const char c )
{
	return c==8 || c==32;
}

// check if there is more than just whitespace
bool IsEndOfLine( unsigned char * _p )
{
	unsigned char *p=_p;

	while(IsWhitespace(*p))
		p++;										// jump over whitespace

	return *p==0 || *p==10 || *p==13;
}

// read an integer from memory and increse the pointer
void GotoNextLine( unsigned char * &p )
{
	while(*p!=10 && *p!=13 && *p!=0)p++;					// jump over content

	while(*p==10 || *p==13)p++;										// jump over return
}

// jump over float/int
void GotoNextNumber( unsigned char * &p )
{
	while(IsWhitespace(*p))p++;										// jump over whitespace

	while((*p>='0' && *p<='9') 
		|| *p=='-' || *p=='.')p++;									// jump over float

	while(IsWhitespace(*p))p++;										// jump over whitespace
}


// inefficient?
void GetLineAsString( unsigned char * &p, string &outStr )
{
	outStr="";

	while(IsWhitespace(*p))p++;										// jump over whitespace

	while(*p!=10 && *p!=13 && *p!=0)
	{
		outStr+=*p++;
	}
}

//
DWORD GetDWORD( unsigned char * &p )
{
	DWORD ret=0;

	while(IsWhitespace(*p))p++;										// jump over whitespace

	while(*p>='0' && *p<='9')
	{
		ret = ret*10 + (*p-'0');p++;
	}
	
	return(ret);
}


//
int GetInt( unsigned char * &p )
{
	DWORD ret=0;
	bool bNeg=false;

	while(IsWhitespace(*p))p++;										// jump over whitespace

	if(*p=='-')
	{
		bNeg=true;p++;
	}

	while(*p>='0' && *p<='9')
	{
		ret = ret*10 + (*p-'0');p++;
	}

	if(bNeg)return(-(int)ret);
		return(ret);
}


// returns 0.0f if there is no float
float GetFloat( unsigned char * &p )
{
	float ret=0;
	bool bNeg=false;

	while(IsWhitespace(*p))p++;										// jump over whitespace

	if(*p=='-')
	{
		bNeg=true;p++;
	}

	DWORD V=0;

	while(*p>='0' && *p<='9')
	{
		V = V*10 + (*p-'0');p++;
	}

	if(*p=='.')
	{
		p++;
		DWORD N=0,Q=1;

		while(*p>='0' && *p<='9')
		{
			N = N*10 + (*p-'0');p++;
			Q = Q*10;
		}

		if(bNeg)return(-(float)( V + (float)N/(float)Q ));
				else return((float)( V + (float)N/(float)Q ));
	}

	if(bNeg)return(-(float)V);
			else return((float)V);
}



DWORD _CalcIndex( int indwValue, DWORD dwNum )
{
	if(indwValue<0)return((DWORD)(dwNum+indwValue));
		else return((DWORD)(indwValue-1));
}

//
bool C3DObject::_LoadOBJ( C3DRenderer &inRenderer, unsigned char *p_Start, CLoadHelper &outData )
{
	bool bRecreateNormals=true;

	assert(m_VertexBuffers.empty());
	assert(m_IndexBuffers.empty());
	assert(m_TriangleBunch.empty());

	C3DMaterial *pDefaultMaterial=new C3DMaterial("default");

	outData.m_bHasTex=false;
	outData.m_bHasNormals=false;

	DWORD dwFaces=0;	// to reserve the needed amount (for faster loading with less memory trashing)

	bool bBuildNormals=false;

	unsigned char *p=p_Start;
	bool bRet=false;																										// return value

	do																																	// goto replacement
	{
		DWORD	dwVertexMapSize=0;																					// size of VertexMap

		Message.Add("LoadOBJ: Enter Pass #1");

		// Pass #1: get the data count (for faster loading with less memory trashing)
		{
			DWORD NumVPos=0,NumVTex=0,NumVNorm=0;
			C3DMaterial *pCurrentMaterial=pDefaultMaterial;

			while(*p)
			{
				LEFTCOMPARE("v ")													// vertex position
				{
					NumVPos++;
//					m_dwVertices++;
				}
				else
				LEFTCOMPARE("vt ")												// vertex texture coordinates (optional)
				{
					NumVTex++;
				}
				else
				LEFTCOMPARE("vn ")												// vertex normals (optional)
				{
					NumVNorm++;
					bRecreateNormals=false;
				}
				else
				LEFTCOMPARE("usemtl ")										// convex polygon material
				{
					string sMatName;
					p+=7;

					GetLineAsString(p,sMatName);

					pCurrentMaterial=m_MaterialManager.GetMaterial(sMatName.c_str());
					if(!pCurrentMaterial)
					{
						// material was not in the mtl
						pCurrentMaterial=m_MaterialManager.AddMaterial(sMatName.c_str());
						outData.m_Materials.push_back(pCurrentMaterial);
					}
				}
				else
				LEFTCOMPARE("f ")													// convex polygon vertex No assignment
				{
					dwFaces++;
					p+=2;

					DWORD dwVertexCount=0;

					while(!IsEndOfLine(p))
					{
						// jump over one vertex
						for(;;)
						{			
							GetInt(p);
							if(*p=='/')
							{
								p++;
								continue;
							}
							break;
						} 

						dwVertexCount++;
					}

					pCurrentMaterial->AddTriangleCount(dwVertexCount-2);
				}

				GotoNextLine(p);
			}

			Message.Add("%d vertices, %d texture coordinates, %d normals, %d dwFaces", NumVPos,NumVTex,NumVNorm,dwFaces);

			// reserve the needed amount (for faster loading with less memory trashing)
			{
				outData.m_ObjVerticesPos.reserve(NumVPos);outData.m_ObjVerticesUV.reserve(NumVTex);outData.m_ObjVerticesNormal.reserve(NumVNorm);

				if(NumVTex)outData.m_bHasTex=true;
				if(NumVNorm)outData.m_bHasNormals=true;

				outData.m_TriIndices.reserve(dwFaces);
			}
		}		// Pass #1 end

		Message.Add("LoadOBJ: Enter Pass #2");

		// Pass #2: load data
		{
			DWORD NumVPos=0,NumVTex=0,NumVNorm=0;
			C3DMaterial *pCurrentMaterial=pDefaultMaterial;

			bool bTextureThere=false,bNormalsThere=false;										// what find out what is the meaning of %d %d/%d or %d/%d/%d
			bool bFaceMode=false;																						// facemode / vertex mode
			DWORD	dwCurrentSmoothingGroup=1;																// for creating the normals

			p=p_Start;
			for(;;)
			{
				LEFTCOMPARE("v ")										// vertex position
				{
					if(bFaceMode)											// next object (from facemode to vertexmode
					{ 
						bTextureThere=false;bNormalsThere=false;bFaceMode=false; 
					}

					p+=2;
					D3DVECTOR vec;

					vec.x=GetFloat(p);vec.y=GetFloat(p);vec.z=GetFloat(p);

					outData.m_ObjVerticesPos.push_back(vec);NumVPos++;
				}
				else
				LEFTCOMPARE("vt ")									// vertex texture coordinates (optional)
				{
					p+=3;
					D3DXVECTOR2 vec;

					vec.x=GetFloat(p);vec.y=1.0f-GetFloat(p);			// 1.0f- because texture coordinates in MAX have (left,bottom) as origin 
					
					bTextureThere=true;
					outData.m_ObjVerticesUV.push_back(vec);NumVTex++;
				}
				else
				LEFTCOMPARE("vn ")									// vertex normals (optional)
				{
					p+=3;
					D3DXVECTOR3 vec;
		
					vec.x=GetFloat(p);vec.y=GetFloat(p);vec.z=GetFloat(p);

//				  D3DXVec3Normalize(&vec,&vec);

					bNormalsThere=true;
					outData.m_ObjVerticesNormal.push_back(vec);NumVNorm++;
				}
				else
				LEFTCOMPARE("usemtl ")							// convex polygon material
				{
					string sMatName;
					p+=7;

					GetLineAsString(p,sMatName);

					pCurrentMaterial=m_MaterialManager.GetMaterial(sMatName.c_str());
					assert(pCurrentMaterial);
				}
				else
				LEFTCOMPARE("s ")										// smoothing groups
				{
					p+=2;

					dwCurrentSmoothingGroup=GetDWORD(p);
				}
				else
				LEFTCOMPARE("f ")										// convex polygon vertex No assignment
				{
					CVertexLoadHelper vertexindices[3];

					bFaceMode=true;
					p+=2;

					int i=0;

//					for(int i=0;i<3;i++)																						// 3 vertices per triangle
					while(!IsEndOfLine(p))																						// as many vertices are there
					{
						vertexindices[2].m_PosIndex=0xffffffff;														// unassiged
						vertexindices[2].m_TexIndex=0xffffffff;														// unassiged
						vertexindices[2].m_VertexSmoothingGroup=dwCurrentSmoothingGroup;	// vertex smoothing group
						vertexindices[2].m_NormIndex=0xffffffff;													// unassiged
						vertexindices[2].m_BaseIndex=0xffffffff;													// unassiged

						vertexindices[2].m_PosIndex=_CalcIndex(GetInt(p),NumVPos);

						if(bTextureThere)
						{
							assert(*p=='/');
							while(*p=='/')p++;
							vertexindices[2].m_TexIndex=_CalcIndex(GetInt(p),NumVTex);
						}

						if(bNormalsThere)
						{
							assert(*p=='/');
							while(*p=='/')p++;
							vertexindices[2].m_NormIndex=_CalcIndex(GetInt(p),NumVNorm);
						}

						if(vertexindices[2].m_PosIndex!=0xffffffff)
							if(vertexindices[2].m_PosIndex>=outData.m_ObjVerticesPos.size())		
							{ Error.Add("LoadOBJ bad index m_PosIndex>=outData.m_ObjVerticesPos.size [%d,%d]",vertexindices[2].m_PosIndex,outData.m_ObjVerticesPos.size()); }
						if(vertexindices[2].m_TexIndex!=0xffffffff)
							if(vertexindices[2].m_TexIndex>=outData.m_ObjVerticesUV.size())		
							{ Error.Add("LoadOBJ bad index m_TexIndex>=outData.m_ObjVerticesUV.size [%d,%d]",vertexindices[2].m_TexIndex,outData.m_ObjVerticesUV.size()); }
						if(vertexindices[2].m_NormIndex!=0xffffffff)
							if(vertexindices[2].m_NormIndex>=outData.m_ObjVerticesNormal.size())	
							{ Error.Add("LoadOBJ bad index m_NormIndex>=outData.m_ObjVerticesNormal.size [%d,%d]",vertexindices[2].m_NormIndex,outData.m_ObjVerticesNormal.size()); }

						if(i>=2)
						{
							// store a new triangle
							vector<CTriangleIndicesLoadHelper> &refMaterial = outData.m_TriIndices;

							refMaterial.push_back(CTriangleIndicesLoadHelper());
							CTriangleIndicesLoadHelper &refTriangle=refMaterial.back();

							refTriangle.m_SmoothingGroup=dwCurrentSmoothingGroup;
							refTriangle.m_pMaterial=pCurrentMaterial;

							refTriangle.m_LoadedVertex[0]=vertexindices[0];
							refTriangle.m_LoadedVertex[1]=vertexindices[1];
							refTriangle.m_LoadedVertex[2]=vertexindices[2];
						}

						// triangle fan
						if(i==0)
							vertexindices[0]=vertexindices[2];
						 else
							vertexindices[1]=vertexindices[2];

						i++;
					}		// for i

				}			// "f "

				GotoNextLine(p);

				if(*p==0)
				{
					bRet=true;
					break;
				}
			}
		}		// Pass #2 end

		if(!bRet)
		{
			Error.Add("LoadOBJ Error in Pass #2");
			break;																												// Error
		}

		// ************************************************************************************

		Message.Add("LoadOBJ normal recreation: %s",bRecreateNormals?"true":"false");
		
		// recreate normal information
		if(bRecreateNormals)
			_RecreateNormals(outData);

		bRet=true;

	} while(false);																										// goto replacement


	return(bRet);
}






// load the .OBJ file format (Alias Wavefront)
bool C3DObject::LoadOBJ( C3DRenderer &inRenderer, const char *inszFileName, bool inbObjectSpace )
{
	if(*inszFileName==0)return(false);
	m_bObjectSpace=inbObjectSpace;

	bool			bRet=false;																// return value

	Message.Add("LoadOBJ: start loading '%s'",inszFileName);

	do																									// goto replacement
	{
		// open the file ----------------------------------------
		CASCIIFile file;

		if(!file.IO_LoadASCIIFile(inszFileName))
		{
			Error.Add("IO_LoadASCIIFile '%s' failed",inszFileName);break;	
		}

		unsigned char *p=(unsigned char *)file.GetDataPtr();

		// read the data -----------------------------------------

		// material libray
		// for each object
		// {
		//		vertex position
		//		vertex texture coordinates (optional)
		//		vertex normals (optional)
		//		face material and vertex No assignment
		// }

		CLoadHelper data;

		// load .MTL file
		{
			string sMTLFilename=inszFileName;
			int len=sMTLFilename.length();

			if(len>=4 && sMTLFilename[len-4]=='.')
			{
				sMTLFilename[len-3]='M';sMTLFilename[len-2]='T';sMTLFilename[len-1]='L';
				bRet=_LoadMTL(inRenderer,sMTLFilename.c_str(),data);
			}
			else
			{
				bRet=false;
				Message.Add("Can't load material file for '%s'",sMTLFilename.c_str());
			}
		}
		// load .OBJ (and recreate normals)
		if(bRet)bRet=_LoadOBJ(inRenderer,p,data);

		// recreate tangent base
		if(m_bObjectSpace)
			_RecreateObjectBase(data);
		else
			_RecreateTangentBase(data);

		CRenderVertices RenderVertices;

		if(bRet)_BuildRenderVertices(data,RenderVertices);

		if(!m_bObjectSpace)_RecreateCloneBase(data,RenderVertices);

		assert(!data.m_TriIndices.empty());

		// store data in own format
		if(bRet)bRet=_InsertSurfaceData(inRenderer,data,RenderVertices);


		// Baustelle

//		MessageBox(0,"","Export test",MB_OK);
//		_ExportSurfaceData(data,RenderVertices,"mirrorsphere","c:\\_Vertex.cpp", "c:\\_Index.cpp");


//		Debug();

	} while(false);																			// goto replacement


	// close the file ---------------------------------------- 
	if(!bRet)FreeData();

	if(bRet)Message.Add("LoadOBJ: '%s' successfully loaded",inszFileName);

	return(bRet);
}




void C3DObject::_BuildRenderVertices( CLoadHelper &inData, CRenderVertices &outData )
{
	outData.m_OBJVertToVertexNo.clear();
	outData.m_Vertices.clear();

	vector<CTriangleIndicesLoadHelper>::iterator it;

	// for every face
	for(it=inData.m_TriIndices.begin();it!=inData.m_TriIndices.end();++it)
	{
		CRenderTriangle newRenderTri;

		for(int i=0;i<3;i++)																						// 3 vertices per triangle
		{
			CVertexLoadHelper &vertexindices=(*it).m_LoadedVertex[i];

			// find the same vertex in this material (if not there, add it to my vertices)
			DWORD dwFoundIndex=0xffffffff;
			{
				map<CVertexLoadHelper,DWORD,CVertexLoadOrder>::const_iterator found;

				if(vertexindices.m_VertexSmoothingGroup==0)
					found = outData.m_OBJVertToVertexNo.end();
				else
					found = outData.m_OBJVertToVertexNo.find(vertexindices);


				if(found!=outData.m_OBJVertToVertexNo.end())										// found
				{
					dwFoundIndex=(*found).second;
				}
				else																														// not found
				{
					dwFoundIndex=outData.m_Vertices.size();

					POLYBUMPVERTEX vert;

					vert.pos=inData.m_ObjVerticesPos[vertexindices.m_PosIndex];
					if(vertexindices.m_TexIndex!=0xffffffff)
					{
						vert.tu=inData.m_ObjVerticesUV[vertexindices.m_TexIndex].x;
						vert.tv=inData.m_ObjVerticesUV[vertexindices.m_TexIndex].y;
					}
					else
					{
						vert.tu=0.0f;vert.tv=0.0f;																	// no texture uv
					}

					if(vertexindices.m_NormIndex!=0xffffffff)
						vert.normal=inData.m_ObjVerticesNormal[vertexindices.m_NormIndex];
					else
						vert.normal=D3DXVECTOR3(0,0,0);

					if(vertexindices.m_BaseIndex!=0xffffffff)
					{
						assert(vertexindices.m_BaseIndex<inData.m_ObjVerticesBase.size());
						CBase3 &refBase=inData.m_ObjVerticesBase[vertexindices.m_BaseIndex];

						vert.binormal=refBase.m_BaseVectors[0];
						vert.tangent=refBase.m_BaseVectors[1];
						vert.tnormal=refBase.m_BaseVectors[2];
					}
					else
					{
						vert.binormal=D3DXVECTOR3(0,0,0);
						vert.tangent=D3DXVECTOR3(0,0,0);
						vert.tnormal=D3DXVECTOR3(0,0,0);
					}

					outData.m_Vertices.push_back(vert);
				}
					//								Message.Add("v[%d]=%d %d %d",dwFoundIndex,vertexindices.m_PosIndex+1,vertexindices.m_TexIndex+1,vertexindices.m_NormIndex+1);

				outData.m_OBJVertToVertexNo[vertexindices]=dwFoundIndex;
				newRenderTri.m_NewVertexIndex[i]=dwFoundIndex;
			}
		}		// for i

		outData.m_NewTriIndices.push_back(newRenderTri);
	}		// for it
}


// render the tangent vector of the object
void C3DObject::RenderDebugHelper( C3DRenderer &inRenderer, float infScale )
{
	vector<C3DTriangleBunch>::iterator it;

	for(it=m_TriangleBunch.begin();it!=m_TriangleBunch.end();++it)
	{
		C3DTriangleBunch &ref=(*it);

		ref.RenderDebugHelper(inRenderer,infScale);
	}
}


// render the object (every material)
void C3DObject::Render( C3DRenderer &inRenderer, C3DScene &inScene )
{
	HRESULT hRes;
	vector<C3DTriangleBunch>::iterator it;

//	OutputDebugString("\nRender object\n");

	IDirect3DDevice8 *pDev=inRenderer.GetDirectXDevice();				assert(pDev);
	IDirect3DVertexBuffer8 *pCurrentVertexBuffer=0;
	IDirect3DIndexBuffer8 *pCurrentIndexBuffer=0;
//	C3DMaterial *pCurrentMaterial=0;

	for(it=m_TriangleBunch.begin();it!=m_TriangleBunch.end();++it)
	{
		C3DTriangleBunch &ref=(*it);

		// set vertex buffer
		if(ref.m_IVertexBuffer!=pCurrentVertexBuffer)
		{
			pCurrentVertexBuffer=ref.m_IVertexBuffer;

			hRes=pDev->SetStreamSource( 0, pCurrentVertexBuffer, sizeof(POLYBUMPVERTEX) );
			if(FAILED(hRes))
				Error.AddDirectX("CreateVertexBuffer failed: ",hRes);
		}

		// set index buffer
		if(ref.m_IIndexBuffer!=pCurrentIndexBuffer)
		{
			pCurrentIndexBuffer=ref.m_IIndexBuffer;

			hRes=pDev->SetIndices(pCurrentIndexBuffer,0);	// Base Vertex Index=0
			if(FAILED(hRes))
				Error.AddDirectX("SetIndices failed: ",hRes);
		}

		ref.m_pMaterial->SetMaterialAndRender(inRenderer,ref,inScene);
	}
}





IDirect3DVertexBuffer8 *C3DObject::CreateNewVertexBuffer( IDirect3DDevice8 *inpDevice, DWORD indwVertexCout )
{
	assert(indwVertexCout<=SIZE_VERTEXBUFFER);

	IDirect3DVertexBuffer8 *pVB;
	HRESULT hRes;

	hRes=inpDevice->CreateVertexBuffer(indwVertexCout*sizeof(POLYBUMPVERTEX),/*D3DUSAGE_WRITEONLY*/0,0,D3DPOOL_MANAGED,&pVB);
	if(FAILED(hRes))
	{
		Error.AddDirectX("CreateVertexBuffer failed: ",hRes);
		return(0);
	}

	m_VertexBuffers.push_back(pVB);

	return(pVB);
}




IDirect3DIndexBuffer8 *C3DObject::CreateNewIndexBuffer( IDirect3DDevice8 *inpDevice, DWORD indwIndexCout )
{
	assert(indwIndexCout<=SIZE_INDEXBUFFER);

	IDirect3DIndexBuffer8 *pIB;
	HRESULT hRes;

	hRes=inpDevice->CreateIndexBuffer(indwIndexCout*sizeof(WORD),/*D3DUSAGE_WRITEONLY*/0,
																		D3DFMT_INDEX16,D3DPOOL_MANAGED,&pIB);
	if(FAILED(hRes))
	{
		Error.AddDirectX("CreateIndexBuffer failed: ",hRes);
		return(0);
	}

	m_IndexBuffers.push_back(pIB);

	return(pIB);
}






#ifdef USE_NEW_CLEAN_TANGENTSPACE_CALCULATION

class CTriangleInputProxy
{

	// helper to get order for CVertexLoadHelper
	struct NormalCompare: public std::binary_function< D3DXVECTOR3, D3DXVECTOR3, bool>
	{
		bool operator() ( const D3DXVECTOR3 &a, const D3DXVECTOR3 &b ) const
		{
			// first sort by x
			if(a.x<b.x)return(true);
			if(a.x>b.x)return(false);

			// then by y
			if(a.y<b.y)return(true);
			if(a.y>b.y)return(false);

			// then by z
			if(a.z<b.z)return(true);
			if(a.z>b.z)return(false);

			return(false);
		}
	};

public:



	//! constructor
	//! /param inpMesh must not be 0
	CTriangleInputProxy( C3DObject::CLoadHelper &inData )
	{
		m_pData=&inData;

		// remap the normals (weld them)

		DWORD dwFaceCount=GetTriangleCount();

		m_NormIndx.reserve(dwFaceCount);

		map<D3DXVECTOR3,DWORD,NormalCompare>	mapNormalsToNumber;
		DWORD dwmapSize=0;

		// for every triangle
		for(DWORD i=0;i<dwFaceCount;i++)
		{
			CTriNormIndex idx;

			// for every vertex of the triangle
			for(DWORD e=0;e<3;e++)
			{
				int iNorm=m_pData->m_TriIndices[i].m_LoadedVertex[e].m_NormIndex;

				D3DXVECTOR3 &vNorm=m_pData->m_ObjVerticesNormal[iNorm];

				map<D3DXVECTOR3,DWORD,NormalCompare>::iterator iFind=mapNormalsToNumber.find(vNorm);

				if(iFind==mapNormalsToNumber.end())				// not found
				{
					idx.p[e]=dwmapSize;
					mapNormalsToNumber[vNorm]=dwmapSize;
					dwmapSize++;
				}
				else idx.p[e]=(*iFind).second;
			}

			m_NormIndx.push_back(idx);
		}
	}

	//! /return 0..
	DWORD GetTriangleCount( void ) const
	{
		return(m_pData->m_TriIndices.size());
	}

	//! /param indwTriNo 0..
	//! /param outdwPos
	//! /param outdwNorm
	//! /param outdwUV
	void GetTriangleIndices( const DWORD indwTriNo, DWORD outdwPos[3], DWORD outdwNorm[3], DWORD outdwUV[3] ) const
	{
		C3DObject::CTriangleIndicesLoadHelper &ref=m_pData->m_TriIndices[indwTriNo];
		const CTriNormIndex &norm=m_NormIndx[indwTriNo];

		for(int i=0;i<3;i++)
		{
			outdwPos[i]=ref.m_LoadedVertex[i].m_PosIndex;
			outdwUV[i]=ref.m_LoadedVertex[i].m_TexIndex;
			outdwNorm[i]=norm.p[i];
		}
	}

	//! /param indwPos 0..
	//! /param outfPos
	void GetPos( const DWORD indwPos, float outfPos[3] ) const
	{
		assert(indwPos<m_pData->m_ObjVerticesPos.size());
		D3DXVECTOR3 &ref=m_pData->m_ObjVerticesPos[indwPos];
		outfPos[0]=ref.x;
		outfPos[1]=ref.y;
		outfPos[2]=ref.z;
	}

	//! /param indwPos 0..
	//! /param outfUV 
	void GetUV( const DWORD indwPos, float outfUV[2] ) const
	{
		assert(indwPos<m_pData->m_ObjVerticesUV.size());
		D3DXVECTOR2 &ref=m_pData->m_ObjVerticesUV[indwPos];
		outfUV[0]=ref.x;
		outfUV[1]=ref.y;
	}

private:

	class CTriNormIndex
	{
	public:
		DWORD p[3];							//!< index in m_BaseVectors
	};

	vector<CTriNormIndex>								m_NormIndx;			//!< normal indices for each triangle
	C3DObject::CLoadHelper *						m_pData;				//!< must not be 0
};


// make sure the normals are calculated before calling this
void C3DObject::_RecreateTangentBase( CLoadHelper &inoutData )
{
	if(!inoutData.m_bHasTex)return;			// no uv assignment, no base vectors

	CTangentSpaceCalculation<CTriangleInputProxy> tangents;
	CTriangleInputProxy Input(inoutData);

	// calculate the base matrices
	tangents.CalculateTangentSpace(Input);

	DWORD dwCnt=tangents.GetBaseCount();

	// for every triangle
	vector<CTriangleIndicesLoadHelper>::iterator itT;
	DWORD dwTriNo=0;

	for(itT=inoutData.m_TriIndices.begin();itT!=inoutData.m_TriIndices.end();++itT,dwTriNo++)
	{
		DWORD dwBaseIndx[3];

		tangents.GetTriangleBaseIndices(dwTriNo,dwBaseIndx);

		assert(dwBaseIndx[0]<dwCnt);
		assert(dwBaseIndx[1]<dwCnt);
		assert(dwBaseIndx[2]<dwCnt);

		// for every vertex of the triangle
		for(int i=0;i<3;i++)
			(*itT).m_LoadedVertex[i].m_BaseIndex=dwBaseIndx[i];		// set the base vector
	}


	//
	for(DWORD i=0;i<dwCnt;i++)
	{
		CBase3 base;

		tangents.GetBase(i,base.m_BaseVectors[0],base.m_BaseVectors[1],base.m_BaseVectors[2]);

		inoutData.m_ObjVerticesBase.push_back(base);
	}
}
#endif // ifdef USE_NEW_CLEAN_TANGENTSPACE_CALCULATION








#define EPS  0.00001f

// from nvidia kitchen 
//! /param v0 input [0..2] position vertex 1
//! /param v1 input [0..2] position vertex 2
//! /param v2 input [0..2] position vertex 3
//! /param t0 input [0..1] texture coordinate vertex 1
//! /param t1 input [0..1] texture coordinate vertex 2
//! /param t2 input [0..1] texture coordinate vertex 3
//! /param tangent output vector 1
//! /param binormal output vector 2
void compute_tangent( const float * v0, const float * v1, const float * v2, 
                      const float * t0, const float * t1, const float * t2, 
                      D3DXVECTOR3 &tangent, D3DXVECTOR3 &binormal)
{
  D3DXVECTOR3 bi, cp, e0, e1;

  tangent  = D3DXVECTOR3(0,0,1);
  binormal = D3DXVECTOR3(0,0,1);

  // x
  e0[0] = v1[0] - v0[0];
  e0[1] = t1[0] - t0[0];
  e0[2] = t1[1] - t0[1];

  e1[0] = v2[0] - v0[0];
  e1[1] = t2[0] - t0[0];
  e1[2] = t2[1] - t0[1];

  D3DXVec3Cross(&cp,&e0,&e1);

  if( fabs(cp[0]) > EPS )
  {
    tangent[0] = -cp[1] / cp[0];
    binormal[0] = -cp[2] / cp[0];
  }

  // y
  e0[0] = v1[1] - v0[1];
  e0[1] = t1[0] - t0[0];
  e0[2] = t1[1] - t0[1];

  e1[0] = v2[1] - v0[1];
  e1[1] = t2[0] - t0[0];
  e1[2] = t2[1] - t0[1];

  D3DXVec3Cross(&cp,&e0,&e1);

  if( fabs(cp[0]) > EPS )
  {
    tangent[1] = -cp[1] / cp[0];
    binormal[1] = -cp[2] / cp[0];
  }

  // z
  e0[0] = v1[2] - v0[2];
  e0[1] = t1[0] - t0[0];
  e0[2] = t1[1] - t0[1];

  e1[0] = v2[2] - v0[2];
  e1[1] = t2[0] - t0[0];
  e1[2] = t2[1] - t0[1];

  D3DXVec3Cross(&cp,&e0,&e1);

  if( fabs(cp[0]) > EPS )
  {
    tangent[2] = -cp[1] / cp[0];
    binormal[2] = -cp[2] / cp[0];
  }

  D3DXVec3Normalize(&tangent,&tangent);
  D3DXVec3Normalize(&binormal,&binormal);
}



// orthogonalize the base vectors
//! /param v0 input [0..2] position vertex 1
//! /param v1 input [0..2] position vertex 2
//! /param v2 input [0..2] position vertex 3
//! /param t0 input [0..1] texture coordinate vertex 1
//! /param t1 input [0..1] texture coordinate vertex 2
//! /param t2 input [0..1] texture coordinate vertex 3
//! /param tangent output vector 1
//! /param binormal output vector 2
//! /param tnormal output vector 3
void compute_tangent_base( const float *v0, const float *v1, const float *v2, 
                      const float *t0, const float *t1, const float *t2, 
                      D3DXVECTOR3 &tangent, D3DXVECTOR3 &binormal, D3DXVECTOR3 &tnormal )
{
	float fA[2]={ t1[0]-t0[0], t1[1]-t0[1] },fB[2]={ t2[0]-t0[0], t2[1]-t0[1] };

	float fOrientation = fA[0]*fB[1]-fA[1]*fB[0];

	compute_tangent(v0,v1,v2,t0,t1,t2,tangent,binormal);

	// make sure they are orthogonal
	D3DXVec3Cross(&tnormal,&tangent,&binormal);		D3DXVec3Normalize(&tnormal,&tnormal);
	D3DXVec3Cross(&binormal,&tnormal,&tangent);		D3DXVec3Normalize(&binormal,&binormal);
	if(fOrientation<0)tnormal=-tnormal;
}




// used for tangent base calculation
class CVertexOrderHelper
{
public:

	DWORD					m_PosIndex;								//!< 0xffffffff means unassigned
	DWORD					m_TexIndex;								//!< 0xffffffff means unassigned
	DWORD					m_ShaderIndex;						//!< 

	DWORD					m_SmoothingGroup;					//!< 0,1,2,4,8,16,...,128*256,256*256 a value with only one 1 bit

	//! constructor
	CVertexOrderHelper( DWORD indwPosIndex, DWORD indwTexIndex, DWORD indwShaderIndex, DWORD indwSmoothingGroup )
	{
		m_PosIndex=indwPosIndex;
		m_TexIndex=indwTexIndex;
		m_ShaderIndex=indwShaderIndex;
		m_SmoothingGroup=indwSmoothingGroup;
	}
};





class CVertexTangentBase
{
public:
	D3DXVECTOR3					m_vBinormal;							//!<
	D3DXVECTOR3					m_vTangent;								//!<
	D3DXVECTOR3					m_vTNormal;								//!<
};


// helper to get order for CVertexLoadHelper
struct CVertexTangentOrder: public std::binary_function< CVertexOrderHelper, CVertexOrderHelper, bool>
{
	bool operator() ( const CVertexOrderHelper &a, const CVertexOrderHelper &b ) const
	{
		// first sort by position
		if(a.m_PosIndex<b.m_PosIndex)return(true);
		if(a.m_PosIndex>b.m_PosIndex)return(false);

		// then by texture
		if(a.m_TexIndex<b.m_TexIndex)return(true);
		if(a.m_TexIndex>b.m_TexIndex)return(false);

		// then by material
		if(a.m_ShaderIndex<b.m_ShaderIndex)return(true);
		if(a.m_ShaderIndex>b.m_ShaderIndex)return(false);

		// last sort criteria (used for searching for all items with same values except smoothing group)
		// then by smoothing group
		if(a.m_SmoothingGroup<b.m_SmoothingGroup)return(true);
		if(a.m_SmoothingGroup>b.m_SmoothingGroup)return(false);

		return(false);
	}
};



// recreate the tangent base information (with smoothing groups)
// make sure the normals are calculated before calling this
void C3DObject::_RecreateCloneBase( CLoadHelper &inLoadData, CRenderVertices &inRenderData )
{
	if(!inLoadData.m_bHasTex)return;			// no uv assignment, no base vectors

	assert(inLoadData.m_bHasNormals);		// call _RecreateNormals before

	map<CVertexOrderHelper,CVertexTangentBase,CVertexTangentOrder> TangentBases;			// key=dwOldVertexNo value=pair<dwSmoothingGroup,dwNewVertexNo>
	vector<C3DMaterial *>::iterator it;

	// for every material
	DWORD dwMaterialNo=0;

#ifdef USE_MASSIVEDEBUGOUTPUT
	Message.Add("_RecreateTangentBase");
#endif

	for(it=inLoadData.m_Materials.begin();it!=inLoadData.m_Materials.end();++it,dwMaterialNo++)
	{
		C3DMaterial *pMaterial=(*it);

		if(!pMaterial->GetCloneMapRef().IsAvailable())continue;

#ifdef USE_MASSIVEDEBUGOUTPUT
		Message.Add("  per Material");
#endif

		vector<CRenderTriangle> &refI = inRenderData.m_NewTriIndices;
		vector<CRenderTriangle>::iterator it2;
		vector<CTriangleIndicesLoadHelper>::iterator it1;

		// for every triangle
		for(it1=inLoadData.m_TriIndices.begin(),it2=refI.begin(); it2!=refI.end(); ++it2,++it1)
		{
			CTriangleIndicesLoadHelper &refTI=(*it1);
			CRenderTriangle &refT=(*it2);

			if(refTI.m_pMaterial!=pMaterial)continue;

			POLYBUMPVERTEX *pV[3]={ &inRenderData.m_Vertices[refT.m_NewVertexIndex[0]],
				&inRenderData.m_Vertices[refT.m_NewVertexIndex[1]],
				&inRenderData.m_Vertices[refT.m_NewVertexIndex[2]] };

			DWORD dwTriSmoothingGroup=refTI.m_SmoothingGroup;

			D3DXVECTOR3 _v0=pV[0]->pos,_v1=pV[1]->pos,_v2=pV[2]->pos;

			float v0[3] = { _v0.x, _v0.y, _v0.z };			// vertex position
			float v1[3] = { _v1.x, _v1.y, _v1.z };
			float v2[3] = { _v2.x, _v2.y, _v2.z };

			float t0[2] = { pV[0]->tu, 1.0f-pV[0]->tv };			// texture uv
			float t1[2] = { pV[1]->tu, 1.0f-pV[1]->tv };
			float t2[2] = { pV[2]->tu, 1.0f-pV[2]->tv };

#ifdef USE_MASSIVEDEBUGOUTPUT
			Message.Add("Triangle Pos (%.2f %.2f %.2f) (%.2f %.2f %.2f) (%.2f %.2f %.2f) UV: (%.2f %.2f) (%.2f %.2f) (%.2f %.2f)",
				_v0.x,_v0.y,_v0.z,_v1.x,_v1.y,_v1.z,_v2.x,_v2.y,_v2.z,t0[0],t0[1],t1[0],t1[1],t2[0],t2[1]);
#endif

			D3DXVECTOR3 vTangent,vBinormal,vTNormal;

			compute_tangent_base(v0,v1,v2, t0,t1,t2, vTangent,vBinormal,vTNormal);

#ifdef USE_MASSIVEDEBUGOUTPUT
			Message.Add("compute_tangent_base T(%.2f %.2f %.2f) B(%.2f %.2f %.2f) TN(%.2f %.2f %.2f)",
				vTangent.x,vTangent.y,vTangent.z,vBinormal.x,vBinormal.y,vBinormal.z,vTNormal.x,vTNormal.y,vTNormal.z);
#endif
		
			D3DXVECTOR3 vOrigTangent,vOrigBinormal,vOrigTNormal;

			float fU[3]={ pV[0]->tu, pV[1]->tu, pV[2]->tu };
			float fV[3]={ pV[0]->tv, pV[1]->tv, pV[2]->tv };

			if(pMaterial->GetCloneMapRef().Get(fU,fV,vOrigTangent,vOrigBinormal,vOrigTNormal))
			{
#ifdef USE_MASSIVEDEBUGOUTPUT
				Message.Add("GetCloneMapRef T(%.2f %.2f %.2f) B(%.2f %.2f %.2f) TN(%.2f %.2f %.2f)",
					vOrigTangent.x,vOrigTangent.y,vOrigTangent.z,vOrigBinormal.x,vOrigBinormal.y,vOrigBinormal.z,vOrigTNormal.x,vOrigTNormal.y,vOrigTNormal.z);
#endif

				// Given:     mOrig * mTransform = mClone
				// Searched:  mTransform
				D3DXMATRIX mOrig,mClone,mTransform;

				mOrig=D3DXMATRIX( vOrigTangent[0],vOrigBinormal[0],vOrigTNormal[0],0.0f,
					vOrigTangent[1],vOrigBinormal[1],vOrigTNormal[1],0.0f,
					vOrigTangent[2],vOrigBinormal[2],vOrigTNormal[2],0.0f,
					0.0f,						0.0f,						 0.0f,					 1.0f);

				mClone=D3DXMATRIX(vTangent[0],		vBinormal[0],		 vTNormal[0],		 0.0f,
					vTangent[1],		vBinormal[1],		 vTNormal[1],		 0.0f,
					vTangent[2],		vBinormal[2],		 vTNormal[2],		 0.0f,
					0.0f,						0.0f,						 0.0f,					 1.0f);

				D3DXMatrixTranspose(&mClone,&mClone);
				D3DXMatrixMultiply(&mTransform,&mOrig,&mClone);

#ifdef USE_MASSIVEDEBUGOUTPUT
				Message.Add("mTransform (%.2f %.2f %.2f %.2f) (%.2f %.2f %.2f %.2f) (%.2f %.2f %.2f %.2f)",
					mTransform[0],mTransform[1],mTransform[2],mTransform[3],
					mTransform[4],mTransform[5],mTransform[6],mTransform[7],
					mTransform[8],mTransform[9],mTransform[10],mTransform[11],
					mTransform[12],mTransform[13],mTransform[14],mTransform[15]);
#endif
				D3DXVECTOR3 vA,vB,vC;

				GetCloneSpaceVectors(vA,vB,vC);

				D3DXVec3TransformNormal(&vTangent,&vA,&mTransform);
				D3DXVec3TransformNormal(&vBinormal,&vB,&mTransform);
				D3DXVec3TransformNormal(&vTNormal,&vC,&mTransform);
			}
			else
			{
				DWORD dwWidth=pMaterial->GetCloneMapRef().GetWidth();
				DWORD dwHeight=pMaterial->GetCloneMapRef().GetHeight();
				int x=(int)((fU[0]+fU[1]+fU[2])/3.0f*dwWidth),y=(int)((fV[0]+fV[1]+fV[2])/3.0f*dwHeight);

				Error.Add("CPBCloneMapDest::Get(%d,%d) failed (adjust mapping)",x,y);
			}


			for(int j=0;j<3;j++)
			{
				CVertexOrderHelper VertexIdx(refTI.m_LoadedVertex[j].m_PosIndex,refTI.m_LoadedVertex[j].m_TexIndex,dwMaterialNo,dwTriSmoothingGroup);

				map<CVertexOrderHelper,CVertexTangentBase,CVertexTangentOrder>::iterator iFind = TangentBases.find(VertexIdx);

				float fAngle=CalcAngle(pV[(j+2)%3]->pos-pV[j]->pos, pV[(j+1)%3]->pos-pV[j]->pos);

				if(fAngle<0)fAngle=0.0f;		// get get 0 percentace for NAN
				/*
				char str[256];

				sprintf(str," %d (%.2f %.2f %.2f)(%.2f %.2f %.2f) SM:%d Angle=%.2f %c\n",j,
				vTangent[0],vTangent[1],vTangent[2],
				vBinormal[0],vBinormal[1],vBinormal[2],
				dwTriSmoothingGroup,fAngle*180.0f/3.14159265f,(iFind==TangentBases.end())?'X':'-');
				OutputDebugString(str);
				*/
				if(iFind==TangentBases.end())																// new Index, set value
				{
					CVertexTangentBase Base;

					Base.m_vTangent=vTangent*fAngle;
					Base.m_vBinormal=vBinormal*fAngle;
					Base.m_vTNormal=vTNormal*fAngle;

					TangentBases[VertexIdx]=Base;
				}
				else																												// old Index, add Base
				{
					CVertexTangentBase &rBase = (*iFind).second;

					rBase.m_vTangent+=vTangent*fAngle;
					rBase.m_vBinormal+=vBinormal*fAngle;
					rBase.m_vTNormal+=vTNormal*fAngle;
				}

				if(dwTriSmoothingGroup==0)
				{
					pV[j]->tangent=vTangent;									// init
					pV[j]->binormal=vBinormal;
					pV[j]->tnormal=vTNormal;
				}
				else
				{
					pV[j]->tangent=D3DXVECTOR3(0,0,0);				// clear
					pV[j]->binormal=D3DXVECTOR3(0,0,0);
					pV[j]->tnormal=D3DXVECTOR3(0,0,0);
				}

			} //j

		}		// for every triangle



		// for every triangle
		for(it1=inLoadData.m_TriIndices.begin(),it2=refI.begin(); it2!=refI.end(); ++it2,++it1)
		{
			CTriangleIndicesLoadHelper &refTI=(*it1);
			CRenderTriangle &refT=(*it2);

			if(refTI.m_pMaterial!=pMaterial)continue;

			POLYBUMPVERTEX *pV[3]={ &inRenderData.m_Vertices[refT.m_NewVertexIndex[0]],
				&inRenderData.m_Vertices[refT.m_NewVertexIndex[1]],
				&inRenderData.m_Vertices[refT.m_NewVertexIndex[2]] };

			map<CVertexOrderHelper,CVertexTangentBase,CVertexTangentOrder>::iterator iFind;

			DWORD dwTriSmoothingGroup=refTI.m_SmoothingGroup;

			for(int j=0;j<3;j++)
			{
				CVertexOrderHelper VertexIdx(refTI.m_LoadedVertex[j].m_PosIndex,refTI.m_LoadedVertex[j].m_TexIndex,dwMaterialNo,dwTriSmoothingGroup);
				iFind=TangentBases.find(VertexIdx);

				// all smoothing groups for this index
				while(iFind!=TangentBases.end())
				{
					if((*iFind).first.m_SmoothingGroup & dwTriSmoothingGroup)
					{
						CVertexTangentBase &refBase=TangentBases[VertexIdx];
						/*
						char str[256];

						sprintf(str,"     (%.2f %.2f %.2f)(%.2f %.2f %.2f)\n",
						refBase.m_vTangent[0],refBase.m_vTangent[1],refBase.m_vTangent[2],
						refBase.m_vBinormal[0],refBase.m_vBinormal[1],refBase.m_vBinormal[2]);
						OutputDebugString(str);
						*/
						pV[j]->tangent+=refBase.m_vTangent;
						pV[j]->binormal+=refBase.m_vBinormal;
						pV[j]->tnormal+=refBase.m_vTNormal;
					}

					++iFind;
					if((*iFind).first.m_PosIndex!=VertexIdx.m_PosIndex)break;
					if((*iFind).first.m_TexIndex!=VertexIdx.m_TexIndex)break;
					if((*iFind).first.m_ShaderIndex!=VertexIdx.m_ShaderIndex)break;
				}
			}		// for every triangle vertex

		}			// for every triangle


		// normalize tangent and binormal result, calculate tangent x binormal, orthonormalize, store result in vertices
		//


		// for every triangle
		for(it1=inLoadData.m_TriIndices.begin(),it2=refI.begin(); it2!=refI.end(); ++it2,++it1)
		{
			CTriangleIndicesLoadHelper &refTI=(*it1);
			CRenderTriangle &refT=(*it2);

			if(refTI.m_pMaterial!=pMaterial)continue;

			D3DXVECTOR3 face_normal,vVecA,vVecB;

			POLYBUMPVERTEX *pV[3]={ &inRenderData.m_Vertices[refT.m_NewVertexIndex[0]],
				&inRenderData.m_Vertices[refT.m_NewVertexIndex[1]],
				&inRenderData.m_Vertices[refT.m_NewVertexIndex[2]] };

			// calculate face normal
			D3DXVec3Subtract(&vVecA,&pV[1]->pos,&pV[0]->pos);
			D3DXVec3Subtract(&vVecB,&pV[2]->pos,&pV[0]->pos);
			D3DXVec3Cross(&face_normal,&vVecA,&vVecB);

			for(int j=0;j<3;j++)
			{
				D3DXVECTOR3 tangent=pV[j]->tangent,binormal=pV[j]->binormal;

				D3DXVec3Normalize(&tangent,&tangent);
				D3DXVec3Normalize(&binormal,&binormal);

				D3DXVECTOR3 tnormal;

				D3DXVec3Cross(&tnormal,&tangent,&binormal);
				D3DXVec3Normalize(&tnormal,&tnormal);

				// Gram-Schmidt orthogonalization process for B
				// compute the cross product B=NxT to obtain 
				// an orthogonal basis
				D3DXVec3Cross(&binormal,&tnormal,&tangent);

				if(D3DXVec3Dot(&tnormal,&pV[j]->tnormal)< 0)		// CloneSpace needs this, but not TangentSpace
					tnormal = -tnormal;

				pV[j]->tnormal=tnormal;
				pV[j]->tangent=tangent;
				pV[j]->binormal=binormal;			
			}
		}			// for every triangle
	}				// for every material
}









// recreate the normal information (with smoothing groups)
void C3DObject::_RecreateNormals( CLoadHelper &inData )
{
	multimap<DWORD,pair<DWORD,DWORD> > mmSharedMap;			// key=dwVertexNo value=pair<dwSmoothingGroup,dwNormalNo>

	inData.m_ObjVerticesNormal.clear();

	// for every triangle
	vector<CTriangleIndicesLoadHelper>::iterator it2;

	for(it2=inData.m_TriIndices.begin();it2!=inData.m_TriIndices.end();++it2)
	{
		DWORD dwTriSmoothingGroup=(*it2).m_SmoothingGroup;

		// for every vertex of the triangle
		for(int i=0;i<3;i++)
		{
			DWORD dwNormalIndex=0xffffffff;
			DWORD dwVertexIndex=(*it2).m_LoadedVertex[i].m_PosIndex;
			multimap<DWORD,pair<DWORD,DWORD> >::iterator itFind=mmSharedMap.find(dwVertexIndex);	

			// merge vertices (is destroying clone space calculations - fuck)
			// for every vertex with the same smoothing group and vertex number in my map
			while(itFind!=mmSharedMap.end())
			{
				if((*itFind).second.first==dwTriSmoothingGroup)
				{
					dwNormalIndex=(*itFind).second.second;break;
				}

				++itFind;if((*itFind).first!=dwVertexIndex)break;
			}

			if(dwNormalIndex==0xffffffff)																				// create the normal 
			{
				dwNormalIndex=inData.m_ObjVerticesNormal.size();
			
				inData.m_ObjVerticesNormal.push_back(D3DXVECTOR3(0,0,0));					// born as clean normal

				mmSharedMap.insert(pair<DWORD,pair<DWORD,DWORD> >(dwVertexIndex,
							pair<DWORD,DWORD>(dwTriSmoothingGroup,dwNormalIndex)));
			}

			(*it2).m_LoadedVertex[i].m_NormIndex=dwNormalIndex;
		}
	}	// for every triangle

	// add the normals **************************************

	// for every triangle
	for(it2=inData.m_TriIndices.begin();it2!=inData.m_TriIndices.end();++it2)
	{
		DWORD dwTriSmoothingGroup=(*it2).m_SmoothingGroup;
		D3DXVECTOR3 vTriNormal;
		DWORD posindex[3]={ (*it2).m_LoadedVertex[0].m_PosIndex, (*it2).m_LoadedVertex[1].m_PosIndex, (*it2).m_LoadedVertex[2].m_PosIndex };

		// build triangle normal
		{
			D3DXVECTOR3 a=inData.m_ObjVerticesPos[posindex[1]]-inData.m_ObjVerticesPos[posindex[0]];
			D3DXVECTOR3 b=inData.m_ObjVerticesPos[posindex[2]]-inData.m_ObjVerticesPos[posindex[0]];

			D3DXVec3Cross(&vTriNormal,&a,&b);
			D3DXVec3Normalize(&vTriNormal,&vTriNormal);
		}

		// for every vertex of the triangle
		for(int i=0;i<3;i++)
		{
			DWORD dwNormIdx=(*it2).m_LoadedVertex[i].m_NormIndex;

			if(dwTriSmoothingGroup==0)
				{ inData.m_ObjVerticesNormal[dwNormIdx]=vTriNormal;continue; }

			DWORD dwVertexPosIdx=(*it2).m_LoadedVertex[i].m_PosIndex;
			multimap<DWORD,pair<DWORD,DWORD> >::iterator itFind;
			
			itFind=mmSharedMap.find( dwVertexPosIdx );

			while(itFind!=mmSharedMap.end())
			{
				DWORD dwVertexSmoothingGroup=(*itFind).second.first;
				D3DXVECTOR3 &vert=inData.m_ObjVerticesNormal[(*itFind).second.second];

//				char str[80];

//					sprintf(str,"%d %d (%.2f %.2f %.2f) %c\n",dwVertexSmoothingGroup,dwTriSmoothingGroup,
//						vTriNormal[0],vTriNormal[1],vTriNormal[2],(dwVertexSmoothingGroup & dwTriSmoothingGroup)?'*':'.');
//					OutputDebugString(str);

				if((dwVertexSmoothingGroup & dwTriSmoothingGroup) !=0)				// only vertices with the same smoothing group
				{
					D3DXVECTOR3 a=inData.m_ObjVerticesPos[posindex[1]]-inData.m_ObjVerticesPos[posindex[0]];
					D3DXVECTOR3 b=inData.m_ObjVerticesPos[posindex[2]]-inData.m_ObjVerticesPos[posindex[0]];

					float fAngle=CalcAngle( inData.m_ObjVerticesPos[posindex[(i+2)%3]]-inData.m_ObjVerticesPos[posindex[i]],
																	inData.m_ObjVerticesPos[posindex[(i+1)%3]]-inData.m_ObjVerticesPos[posindex[i]]);

					vert+=vTriNormal*fAngle;

//						sprintf(str,"    (%.2f %.2f %.2f)\n",vert[0],vert[1],vert[2]);
//						OutputDebugString(str);
				}

				++itFind;
				if((*itFind).first!=dwVertexPosIdx)break;
			}
		}
	}	// for every triangle


	// normalize normals **************************************

	// for every normal
	vector<D3DXVECTOR3>::iterator itV;

	for(itV=inData.m_ObjVerticesNormal.begin();itV!=inData.m_ObjVerticesNormal.end();++itV)
	{
		D3DXVECTOR3 &vVertex=(*itV);

		D3DXVec3Normalize(&vVertex,&vVertex);
	}		

	inData.m_bHasNormals=true;
}








// recreate the normal information (with smoothing groups)
void C3DObject::_RecreateObjectBase( CLoadHelper &inoutData )
{
	// for every triangle
	vector<CTriangleIndicesLoadHelper>::iterator itT;

	for(itT=inoutData.m_TriIndices.begin();itT!=inoutData.m_TriIndices.end();++itT)
	{
		// for every vertex of the triangle
		for(int i=0;i<3;i++)
			(*itT).m_LoadedVertex[i].m_BaseIndex=0;		// set the base vector
	}

	// only one base is neccessary

	CBase3 base;

	GetObjectSpaceVectors(base.m_BaseVectors[0],base.m_BaseVectors[1],base.m_BaseVectors[2]);

	inoutData.m_ObjVerticesBase.push_back(base);
}



IDirect3DIndexBuffer8 *C3DObject::PushToIndexBuffer( C3DRenderer &inRenderer, vector<WORD> &invIndices )
{
	HRESULT hRes;
	IDirect3DDevice8 *pDev=inRenderer.GetDirectXDevice();				assert(pDev);

	IDirect3DIndexBuffer8 *IIndexBuffer=CreateNewIndexBuffer(pDev,invIndices.size());

	if(!IIndexBuffer)
	{
		Error.Add("CreateNewIndexBuffer failed");
		return(0);
	}

	WORD *IndexBufferPtr=0;

	hRes=IIndexBuffer->Lock(0,invIndices.size()*sizeof(WORD),(BYTE **)(&IndexBufferPtr),0);
	if(FAILED(hRes))
	{
		IIndexBuffer->Release();
		Error.AddDirectX("Lock IndexBuffer failed",hRes);
		return(0);
	}

	vector<WORD>::iterator it;
	
	for(it=invIndices.begin();it!=invIndices.end();++it)
		*IndexBufferPtr++=*it;

	invIndices.clear();

	IIndexBuffer->Unlock();
	
	return(IIndexBuffer);
}



IDirect3DVertexBuffer8 *C3DObject::PushToVertexBuffer( C3DRenderer &inRenderer, vector<DWORD> &invIndices, vector<POLYBUMPVERTEX> &invVertices )
{
	HRESULT hRes;
	IDirect3DDevice8 *pDev=inRenderer.GetDirectXDevice();				assert(pDev);

	assert(!invIndices.empty());		if(invIndices.empty())return(0);

	// get VertexBuffer memory
	// unlock last VertexBuffer
	IDirect3DVertexBuffer8 *IVertexBuffer=0;

	IVertexBuffer=CreateNewVertexBuffer(pDev,invIndices.size());

	if(!IVertexBuffer)
	{
		Error.Add("CreateNewVertexBuffer failed");
		return(0);
	}

	POLYBUMPVERTEX *VertexBufferPtr=0;

	hRes=IVertexBuffer->Lock(0,invIndices.size()*sizeof(POLYBUMPVERTEX),(BYTE **)(&VertexBufferPtr),0);
	if(FAILED(hRes))
	{
		IVertexBuffer->Release();
		Error.AddDirectX("Lock VertexBuffer failed",hRes);
		return(0);
	}

	vector<DWORD>::iterator it;
	
	for(it=invIndices.begin();it!=invIndices.end();++it)
		*VertexBufferPtr++=invVertices[*it];

	invIndices.clear();

	assert(IVertexBuffer);
	IVertexBuffer->Unlock();

	return(IVertexBuffer);
}



//
bool C3DObject::_InsertSurfaceData( C3DRenderer &inRenderer, CLoadHelper &inData, CRenderVertices &inDataR  )
{
	IDirect3DDevice8 *pDev=inRenderer.GetDirectXDevice();				assert(pDev);

	m_vMinBoundingBox=D3DXVECTOR3(FLT_MAX,FLT_MAX,FLT_MAX);
	m_vMaxBoundingBox=D3DXVECTOR3(-FLT_MAX,-FLT_MAX,-FLT_MAX);

	// now I have loaded the whole data
	// I have a container with a pointer to every material (MaterialMap), with a container with
	// every triangle in it (vector<DWORD>, 3 indices per triangle)
	// The indices are in the VertexMap

	Message.Add("LoadOBJ: Enter Pass #3");

	// Pass #3: sort the data
	{ 
		// VertexBuffer
		map<DWORD,DWORD> VertexBufferMap;		// to get unique vertices, first is in CLoadHelper::m_Vertices, second is index in VertexBufferPtr

		vector<C3DMaterial *>::iterator it;

		vector<DWORD> vVertexBufferTemp;			vVertexBufferTemp.reserve(2048);		// for less memory allocations
		vector<WORD> vIndexBufferTemp;				vIndexBufferTemp.reserve(2048);			// for less memory allocations

		// for every material
		for(it=inData.m_Materials.begin();it!=inData.m_Materials.end();++it)
		{
			C3DMaterial *pMat = (*it);

			C3DTriangleBunch TriBunch;

			vector<CTriangleIndicesLoadHelper> &refI = inData.m_TriIndices;

			Message.Add("_InsertSurfaceData Material:'%s' Triangles:%d",pMat->GetName(),refI.size());

			vector<CTriangleIndicesLoadHelper>::iterator it2;

			// for every triangle
			for(it2=refI.begin();it2!=refI.end();++it2)
			{
				if((*it2).m_pMaterial!=pMat)continue;		// not very efficient but acceptable

				CTriangleIndicesLoadHelper &refT=*it2;

				DWORD found[3];

				// if buffer overflow, flush triangle bunch
				if(vVertexBufferTemp.size()>=SIZE_VERTEXBUFFER-3		// -3 to make sure all 3 vertices of the triangle fit
				|| vIndexBufferTemp.size()>=SIZE_INDEXBUFFER-3)			// -3 to make sure all 3 vertices of the triangle fit
				{
					// flush index buffer
					{
						TriBunch.m_IIndexBuffer=PushToIndexBuffer(inRenderer,vIndexBufferTemp);
						
						if(!TriBunch.m_IIndexBuffer)
							return(false);
					}

					// flush vertex buffer
					{
						TriBunch.m_IVertexBuffer=PushToVertexBuffer(inRenderer,vVertexBufferTemp,inDataR.m_Vertices);
	
						if(!TriBunch.m_IVertexBuffer)
							return(false);

						VertexBufferMap.clear();
					}

					// flush triangle bunch
					{
						TriBunch.m_pMaterial=pMat;

						m_TriangleBunch.push_back(TriBunch);

						TriBunch=C3DTriangleBunch();
					}
				}


				TriBunch.m_PrimitiveCount++;

				// 
				for(int i=0;i<3;i++)
				{
					DWORD searched=inDataR.m_OBJVertToVertexNo[refT.m_LoadedVertex[i]];

					if(VertexBufferMap.count(searched))										// vertex already in buffer
					{
						found[i]=VertexBufferMap[searched];
					}
					else
					{
						POLYBUMPVERTEX &vertex=inDataR.m_Vertices[searched];

						// correct Bounding Box
						{
							D3DXVECTOR3 vPos=vertex.pos;

							if(vPos.x<m_vMinBoundingBox.x) m_vMinBoundingBox.x=vPos.x;
							if(vPos.y<m_vMinBoundingBox.y) m_vMinBoundingBox.y=vPos.y;
							if(vPos.z<m_vMinBoundingBox.z) m_vMinBoundingBox.z=vPos.z;
							if(vPos.x>m_vMaxBoundingBox.x) m_vMaxBoundingBox.x=vPos.x;
							if(vPos.y>m_vMaxBoundingBox.y) m_vMaxBoundingBox.y=vPos.y;
							if(vPos.z>m_vMaxBoundingBox.z) m_vMaxBoundingBox.z=vPos.z;
						}

						VertexBufferMap[searched]=vVertexBufferTemp.size();
						found[i]=vVertexBufferTemp.size();

						vVertexBufferTemp.push_back(searched);

//ffuu						TriBunch.m_maxlocalVertex++;
					}

					assert(found[i]<=256*256);
					vIndexBufferTemp.push_back((WORD)found[i]);

//					if(TriBunch.m_minlocalVertex>found[i])
//ffuu						TriBunch.m_minlocalVertex=found[i];

					//ffuu
					if(TriBunch.m_maxlocalVertex<found[i])
						TriBunch.m_maxlocalVertex=found[i];

				}	// for the 3 vertices of a triangle
			}	// for every triangle

			// unpreprocessed triangles left?
			if(vVertexBufferTemp.size())
			{
				// flush index buffer
				{
					TriBunch.m_IIndexBuffer=PushToIndexBuffer(inRenderer,vIndexBufferTemp);
					
					if(!TriBunch.m_IIndexBuffer)
						return(false);
				}

				// flush vertex buffer
				{
					TriBunch.m_IVertexBuffer=PushToVertexBuffer(inRenderer,vVertexBufferTemp,inDataR.m_Vertices);

					if(!TriBunch.m_IVertexBuffer)
						return(false);
				}

				// flush triangle bunch
				{
					TriBunch.m_pMaterial=pMat;

					m_TriangleBunch.push_back(TriBunch);

					TriBunch=C3DTriangleBunch();			
				}
			}

			assert(vIndexBufferTemp.empty());
			assert(vVertexBufferTemp.empty());

		}	// for every material

	} // Passs #3 end

	return(true);
}


/*
float Gen_V[] = 
{
      0.733600020409f,     -0.707199990749f,      1.409600019455f,    0.950831055641f,-0.056374162436f, 0.304535865784f,     0.053485687822f, 0.998409390450f, 0.017825936899f,     0.305056452751f, 0.000661143451f,-0.952333986759f,        0.970000028610f, 0.569999992847f,    //0000
      0.773199975491f,     -0.662400007248f,      1.172799944878f,    0.998268067837f,-0.058769766241f, 0.002636074787f,     0.058566089720f, 0.997041404247f, 0.049782674760f,     0.005553991999f, 0.049542073160f,-0.998756527901f,        0.980000019073f, 0.660000026226f,    //0001
};

unsigned short Generator_I[] = {

	0x0000,0x0001,0x0002,  //0x0000
		0x0003,0x0004,0x0005,  //0x0001
};


*/

typedef signed char         int8;
typedef signed short        int16;
typedef signed long         int32;
typedef signed __int64			int64;
typedef unsigned char				uint8;
typedef unsigned short			uint16;
typedef unsigned long				uint32;
typedef unsigned __int64		uint64;

_inline int16 tPackF2B(float f)
{
	return (int16)(f * 32767.0f);
}
_inline float tPackB2F(int16 i)
{
	return (float)((float)i / 32767.0f);
}

/*
bool C3DObject::_ExportSurfaceData(	CLoadHelper &inData, CRenderVertices &inDataR, const char *inszObjName, const char *inszVerticesFilePath, const char *inszIndicesFilePath )
{

	//--------------------------------------------------------------------------------
	//-- dump all vertices
	//--------------------------------------------------------------------------------
	uint32 numExtVertices = inDataR.m_Vertices.size();

	FILE* vstream = fopen( "c:\\VBuffer.txt", "w+b" );
	for (uint32 v=0; v<numExtVertices; v++)
	{
		Vec3 p;
		p.x = inDataR.m_Vertices[v].pos.x/256.0f;
		p.y = inDataR.m_Vertices[v].pos.y/256.0f;
		p.z = inDataR.m_Vertices[v].pos.z/256.0f;
		fprintf(vstream, " {%15.10ff,%15.10ff,%15.10ff,  ",p.x,p.y,p.z);  

		Vec3 b;
		b.x = inDataR.m_Vertices[v].tangent.x;
		b.y = inDataR.m_Vertices[v].tangent.y;
		b.z = inDataR.m_Vertices[v].tangent.z;

		Vec3 t;
		t.x = inDataR.m_Vertices[v].binormal.x;
		t.y = inDataR.m_Vertices[v].binormal.y;
		t.z = inDataR.m_Vertices[v].binormal.z;

		Vec3 n;
		n.x = inDataR.m_Vertices[v].normal.x;
		n.y = inDataR.m_Vertices[v].normal.y;
		n.z = inDataR.m_Vertices[v].normal.z;

		Vec3 testnormal = (t%b).GetNormalized();
		f32 dot=n|testnormal; 

		uint16 flip = tPackF2B(1);
		if (dot<0) flip = tPackF2B(-1);

		int16 tx = tPackF2B(t.x);
		int16 ty = tPackF2B(t.y);
		int16 tz = tPackF2B(t.z);
		int16 tw = flip;

		int16 bx = tPackF2B(b.x);
		int16 by = tPackF2B(b.y);
		int16 bz = tPackF2B(b.z);
		int16 bw = flip;

		if (tx<0)	fprintf(vstream, "-0x%04x,",-tx);
		else      fprintf(vstream, "+0x%04x,",+tx);
		if (ty<0)	fprintf(vstream, "-0x%04x,",-ty);
		else      fprintf(vstream, "+0x%04x,",+ty);
		if (tz<0)	fprintf(vstream, "-0x%04x,",-tz);
		else      fprintf(vstream, "+0x%04x,",+tz);
		if (tw<0)	fprintf(vstream, "-0x%04x,  ",-tw);
		else      fprintf(vstream, "+0x%04x,  ",+tw);

		if (bx<0)	fprintf(vstream, "-0x%04x,",-bx);
		else      fprintf(vstream, "+0x%04x,",+bx);
		if (by<0)	fprintf(vstream, "-0x%04x,",-by);
		else      fprintf(vstream, "+0x%04x,",+by);
		if (bz<0)	fprintf(vstream, "-0x%04x,",-bz);
		else      fprintf(vstream, "+0x%04x,",+bz);
		if (bw<0)	fprintf(vstream, "-0x%04x,  ",-bw);
		else      fprintf(vstream, "+0x%04x,  ",+bw);

//		f32 tu = inDataR.m_Vertices[v].tu;
//		f32 tv = inDataR.m_Vertices[v].tv;
//		fprintf(vstream, "%15.10ff,%15.10ff }, //%04x",tu,tv,	v); 

		f32 fu = inDataR.m_Vertices[v].tu;
		f32 fv = inDataR.m_Vertices[v].tv;
		if (fu>+1) fu=+1;
		if (fu<-1) fu=-1;
		if (fv>+1) fv=+1;
		if (fv<-1) fv=-1;
		int16 iu = tPackF2B(fu);
		int16 iv = tPackF2B(fv);
		if (iu<0)	fprintf(vstream, "-0x%04x,",-iu);
		else      fprintf(vstream, "+0x%04x,",+iu);
		if (iv<0)	fprintf(vstream, "-0x%04x ",-iv);
		else      fprintf(vstream, "+0x%04x ",+iv);
		fprintf(vstream, " }, //%04x",	v); 


		fprintf(vstream, "\r\n");
	}
	fprintf(vstream, "\r\n\r\n");
	fclose( vstream );

	//--------------------------------------------------------------------------------
	//--   dump all indices
	//--------------------------------------------------------------------------------
	uint32 numExtFaces = inDataR.m_NewTriIndices.size();
	FILE* istream = fopen( "c:\\IBuffer.txt", "w+b" );
	for (uint32 f=0; f<numExtFaces; f++)
	{
		uint32 i0 = inDataR.m_NewTriIndices[f].m_NewVertexIndex[0];
		uint32 i1 = inDataR.m_NewTriIndices[f].m_NewVertexIndex[1];
		uint32 i2 = inDataR.m_NewTriIndices[f].m_NewVertexIndex[2];
		fprintf(istream, "0x%04x,0x%04x,0x%04x, //0x%08x",i0,i1,i2,f);  
		fprintf(vstream, "\r\n");
	}
	fprintf(istream, "\r\n\r\n");
	fclose(istream);

	return(true);
}
*/

// for testing (e.g. to produce PS2 input data)
/*bool C3DObject::_ExportSurfaceData(	CLoadHelper &inData, CRenderVertices &inDataR, const char *inszObjName, const char *inszVerticesFilePath, const char *inszIndicesFilePath )
{
	int index_i=0;
	int index_v=0;

	FILE *out_i=fopen(inszIndicesFilePath,"wb");

	if(!out_i)return(false);

	FILE *out_v=fopen(inszVerticesFilePath,"wb");

	if(!out_v){ fclose(out_i);return(false); }

	fprintf(out_i,"short %s_I[] =\n{\n",inszObjName);
	fprintf(out_v,"\n// pos.xyz u v binormal.xyz tangent.xyz normal.xyz\n\n");
	fprintf(out_v,"float %s_V[] =\n{\n",inszObjName);

	// now I have loaded the whole data
	// I have a container with a pointer to every material (MaterialMap), with a container with
	// every triangle in it (vector<DWORD>, 3 indices per triangle)
	// The indices are in the VertexMap

	vector<DWORD> vVertexBufferTemp;			vVertexBufferTemp.reserve(2048);		// for less memory allocations
	vector<WORD> vIndexBufferTemp;				vIndexBufferTemp.reserve(2048);			// for less memory allocations

	// Pass #3: sort the data
	{ 
		// VertexBuffer
		map<DWORD,DWORD> VertexBufferMap;		// to get unique vertices, first is in CLoadHelper::m_Vertices, second is index in VertexBufferPtr

		vector<C3DMaterial *>::iterator it;

		// for every material
		for(it=inData.m_Materials.begin();it!=inData.m_Materials.end();++it)
		{
			C3DMaterial *pMat = (*it);

			vector<CTriangleIndicesLoadHelper> &refI = inData.m_TriIndices;

			vector<CTriangleIndicesLoadHelper>::iterator it2;

			// for every triangle
			for(it2=refI.begin();it2!=refI.end();++it2)
			{
				if((*it2).m_pMaterial!=pMat)continue;		// not very efficient but acceptable

				CTriangleIndicesLoadHelper &refT=*it2;

				DWORD found[3];

				// 
				for(int i=0;i<3;i++)
				{
					DWORD searched=inDataR.m_OBJVertToVertexNo[refT.m_LoadedVertex[i]];

					if(VertexBufferMap.count(searched))										// vertex already in buffer
					{
						found[i]=VertexBufferMap[searched];
					}
					else
					{
						POLYBUMPVERTEX &vertex=inDataR.m_Vertices[searched];

						VertexBufferMap[searched]=vVertexBufferTemp.size();
						found[i]=vVertexBufferTemp.size();

						vVertexBufferTemp.push_back(searched);
					}

					assert(found[i]<=256*256);
					vIndexBufferTemp.push_back((WORD)found[i]);

				}	// for the 3 vertices of a triangle
			}	// for every triangle

		}	// for every material

	} // Passs #3 end


	{
		// flush index buffer
		{

			// No degenerates for PS2, 51 vertices
			::SetStitchStrips(false);
			::SetCacheSize(51);

			// Generate the strips
			::PrimitiveGroup *pStrips = NULL;
			unsigned short iNumGroups = 0;
			::GenerateStrips((const unsigned short *) &vIndexBufferTemp[0], vIndexBufferTemp.size(), &pStrips, &iNumGroups);

			UINT iCurStrip, iCurIdx;
			UINT iTotalNumIndices = 0;
			for (iCurStrip=0; iCurStrip<iNumGroups; iCurStrip++)
			{
				assert(pStrips[iCurStrip].type == PT_STRIP);

				iTotalNumIndices += pStrips[iCurStrip].numIndices;

				for (iCurIdx=0; iCurIdx<pStrips[iCurStrip].numIndices; iCurIdx++)
				{
					//fprintf(out_i, "  %d, ", (DWORD) pStrips[iCurStrip].indices[iCurIdx]);
				}

				fprintf(out_i, " -1,\n");
			}

			if (pStrips)
			{
				delete [] pStrips;
				pStrips = NULL;
			}
		

			
			for(vector<WORD>::iterator it=vIndexBufferTemp.begin();it!=vIndexBufferTemp.end();)
			{
				fprintf(out_i,"0x%04x,",(DWORD)(*it++));
				fprintf(out_i,"0x%04x,",(DWORD)(*it++));
				fprintf(out_i,"0x%04x,",(DWORD)(*it++));
				fprintf(out_i," // 0x%04x\n",index_i++);
			}
		

			 // int _iBreak = 0;
		}
		


		// flush vertex buffer
		{
			for(vector<DWORD>::iterator it=vVertexBufferTemp.begin();it!=vVertexBufferTemp.end();++it)
			{
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].pos.x/256.0f);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].pos.y/256.0f);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].pos.z/256.0f);


				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].binormal.x);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].binormal.y);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].binormal.z);

				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tangent.x);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tangent.y);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tangent.z);

				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tnormal.x);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tnormal.y);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tnormal.z);

				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tu);
				fprintf(out_v,"%15.10ff, ",inDataR.m_Vertices[*it].tv);

				fprintf(out_v," // %04x\n",index_v++);

			}
		}
	}
			
	fprintf(out_i,"};");
	fprintf(out_v,"};");

	fclose(out_i);
	fclose(out_v);

	return(true);
}*/




// only used for debugging
void C3DObject::Debug( void )
{
	Message.Add("Debug TriangleBunch:");
	Message.Add("{");

	vector<C3DTriangleBunch>::iterator it;

	for(it=m_TriangleBunch.begin();it!=m_TriangleBunch.end();++it)
	{
		C3DTriangleBunch &ref=(*it);

		Message.Add("  V:%p I:%p Mat:%s FI:%.6d Cnt:%.6d minV:%.6d maxV:%.6d",ref.m_IVertexBuffer,ref.m_IIndexBuffer,ref.m_pMaterial->GetName(),
			ref.m_localIndex,ref.m_PrimitiveCount,ref.m_minlocalVertex,ref.m_maxlocalVertex);
	}	

	Message.Add("}");
}




bool C3DObject::_LoadMTL( C3DRenderer &inRenderer, const char *inszFileName, CLoadHelper &outData )
{
	CASCIIFile file;

	if(!file.IO_LoadASCIIFile(inszFileName))
	{
//		Error.Add("IO_LoadASCIIFile '%s' failed",inszFileName);
		return(true);		// no file, nothing to load
	}

	bool bLoadClonemap=!m_bObjectSpace;					// load .cln only for Objectspace texture
	char drive[_MAX_DRIVE];
  char dir[_MAX_DIR];
  char fname[_MAX_FNAME];
  char ext[_MAX_EXT];

	_splitpath(inszFileName,drive,dir,fname,ext);

	unsigned char *p=(unsigned char *)file.GetDataPtr();
	C3DMaterial *pCurrentMaterial=0;
	D3DXVECTOR3 vec;
	DWORD col;

	while(*p)
	{
		while(*p>0 && *p<=' ')p++;		// ignore whitspace

		LEFTCOMPARE("newmtl ")		// material name
		{
			p+=7;
			string sMatName;
			GetLineAsString(p,sMatName);
			pCurrentMaterial=m_MaterialManager.GetMaterial(sMatName.c_str());
			if(!pCurrentMaterial)
			{
				pCurrentMaterial=m_MaterialManager.AddMaterial(sMatName.c_str());
				outData.m_Materials.push_back(pCurrentMaterial);
			}
		}
		else
		if(pCurrentMaterial)			// proceed only with a material pointer
		LEFTCOMPARE("Ka ")				// ambient color
		{
			p+=3;
			vec.x=GetFloat(p);vec.y=GetFloat(p);vec.z=GetFloat(p);
			VECTOR_TO_DWORD(vec,col);
			pCurrentMaterial->SetAmbientColor(col);
		}
		else
		LEFTCOMPARE("Kd ")				// diffuse color
		{
			p+=3;
			vec.x=GetFloat(p);vec.y=GetFloat(p);vec.z=GetFloat(p);
			VECTOR_TO_DWORD(vec,col);
			pCurrentMaterial->SetDiffuseColor(col);
		}
		else
		LEFTCOMPARE("Ks ")				// specular color
		{
			p+=3;
			vec.x=GetFloat(p);vec.y=GetFloat(p);vec.z=GetFloat(p);
			VECTOR_TO_DWORD(vec,col);
			pCurrentMaterial->SetSpecularColor(col);
		}
		else
		LEFTCOMPARE("Kss ")				// subsurface color (non OBJ standard)
		{
			p+=4;
			vec.x=GetFloat(p);vec.y=GetFloat(p);vec.z=GetFloat(p);
			VECTOR_TO_DWORD(vec,col);
			pCurrentMaterial->SetSubsurfaceColor(col);
		}
		else
		LEFTCOMPARE("d ")					//
		{
		}
		else
		LEFTCOMPARE("Ns ")				//  specular highlight
		{
		}
		else
		LEFTCOMPARE("illum ")			//
		{
		}
		else
		LEFTCOMPARE("map_Kd ")		//
		{
			p+=7;
			string sTexName;
			GetLineAsString(p,sTexName);

			char path_buffer[_MAX_PATH];
			_makepath(path_buffer,drive,dir,sTexName.c_str(),0);

			if(!pCurrentMaterial->SetDiffuseTexture(inRenderer,path_buffer))
			{
				Error.Add("SetDiffuseTexture '%s' failed (file not found?)",sTexName.c_str());
			}
		}
		else
		LEFTCOMPARE("bump ")			//
		{
			p+=5;
			string sTexName;
			GetLineAsString(p,sTexName);

			char path_buffer[_MAX_PATH];
			_makepath(path_buffer,drive,dir,sTexName.c_str(),0);

			if(!pCurrentMaterial->SetPolyBumpTexture(inRenderer,path_buffer,bLoadClonemap))
			{
				Error.Add("SetPolyBumpTexture '%s' failed (file not found?)",sTexName.c_str());
			}
		}
		else
		LEFTCOMPARE("occdir ")			//
		{
			p+=7;
			string sTexName;
			GetLineAsString(p,sTexName);

			char path_buffer[_MAX_PATH];
			_makepath(path_buffer,drive,dir,sTexName.c_str(),0);

			if(!pCurrentMaterial->SetOcclusionDirectionTexture(inRenderer,path_buffer))
			{
				Error.Add("SetOcclusionDirectionTexture '%s' failed (file not found?)",sTexName.c_str());
			}
		}

		GotoNextLine(p);
	}

	return(true);
}



// get the bounding box O(1) (could be used to find the midpoint)
void C3DObject::GetBoundingBox( D3DXVECTOR3 &outvMin, D3DXVECTOR3 &outvMax ) const
{
	outvMin=m_vMinBoundingBox;
	outvMax=m_vMaxBoundingBox;
}



void C3DObject::GetStats( DWORD &outdwFaces, DWORD &outdwVertices )
{
	outdwFaces=0;
	outdwVertices=0;

	vector<IDirect3DVertexBuffer8 *>::iterator it1;
	vector<IDirect3DIndexBuffer8 *>::iterator it2;

	for(it1=m_VertexBuffers.begin();it1!=m_VertexBuffers.end();++it1)
	{
		D3DVERTEXBUFFER_DESC desc;

		(*it1)->GetDesc(&desc);

		outdwVertices+=desc.Size/sizeof(POLYBUMPVERTEX);
	}

	for(it2=m_IndexBuffers.begin();it2!=m_IndexBuffers.end();++it2)
	{
		D3DINDEXBUFFER_DESC desc;

		(*it2)->GetDesc(&desc);

		outdwFaces+=desc.Size/6;		// 3 vertices * 2 byes(16 bit index)
	}
}

void C3DObject::GetObjectSpaceVectors( D3DXVECTOR3 &outvA, D3DXVECTOR3 &outvB, D3DXVECTOR3 &outvC )
{
	outvA =	D3DXVECTOR3(1,0,0);			// identity
	outvB =	D3DXVECTOR3(0,1,0);
	outvC =	D3DXVECTOR3(0,0,1);				
}


void C3DObject::GetCloneSpaceVectors( D3DXVECTOR3 &outvA, D3DXVECTOR3 &outvB, D3DXVECTOR3 &outvC )
{
	outvA =	D3DXVECTOR3(0,1,0);
	outvB =	D3DXVECTOR3(1,0,0);
	outvC =	D3DXVECTOR3(0,0,1);	
}
