/*    

	Wavefront OBJ model reader


	This reader is based on:

	  -----------------------------------------------------------------
      Wavefront OBJ model file format reader/writer/manipulator.

      glm.c
      Nate Robins, 1997, 2000
      nate@pobox.com, http://www.pobox.com/~nate
 	  -----------------------------------------------------------------

	Modifications by Carsten Dachsbacher, 2003:

	- clean ups, optimizations
	- Direct3D 9 Rendering
	- Reads now non-100%-standard OBJs

*/

// this is required for OBJ generated with Crossroads. Those OBJ files are not
// correct as specified...
#define RESTART_INDEX_PER_GROUP

#include <windows.h>
#include <d3dx9.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "vertex3dop.h"
#include "obj.h"


#define T(x) ( this->pTriangleList[ (x) ] )

OBJmodel::OBJmodel()
{
    this->mtllibname		= NULL;
    this->nVertices			= 0;
    this->pVertexList		= NULL;
    this->nNormals			= 0;
    this->pNormalList		= NULL;
    this->nTexCoords		= 0;
    this->pTexCoordList		= NULL;
    this->nFaceNormals		= 0;
    this->pFaceNormalList	= NULL;
    this->nTriangles		= 0;
    this->pTriangleList		= NULL;
    this->nMaterials		= 0;
    this->pMaterialList		= NULL;
    this->nGroups			= 0;
    this->pGroupList		= NULL;

	this->pMeshVB			= NULL;
};


//
// find a model group by name
//
OBJgroup	*OBJmodel::objFindGroup( char *name )
{
    OBJgroup *group;
    
    group = pGroupList;
    
	while ( group ) 
	{
        if ( !strcmp( name, group->name ) )
            break;
        group = group->next;
    }
    
    return group;
}

//
// add a group to the model, if it does not exist yet
//
OBJgroup	*OBJmodel::objAddGroup( char *name )
{
    OBJgroup *group;
    
    group = objFindGroup( name );
    
	if ( !group ) 
	{
        group = new OBJgroup;
        
		group->name			 = strdup( name );
        group->material		 = 0;
        group->nTriangles	 = 0;
        group->pTriangleList = NULL;
        group->next			 = this->pGroupList;

        pGroupList = group;
        
		nGroups++;
    }
    
    return group;
}

//
// find a material by name
//
DWORD	OBJmodel::objFindMaterial( char *name )
{
    for ( DWORD i = 0; i < nMaterials; i++ )
        if ( !strcmp( pMaterialList[ i ].name, name ) )
            return i;
    
	return 0;
}


//
// extract directory from path string
//
static char *objDirName( char *path )
{
    char *dir;
    char *s;
    
    dir = strdup( path);
    
    s = strrchr( dir, '/' );
    if ( s )
        s[ 1 ] = '\0'; else
        dir[ 0 ] = '\0';
    
    return dir;
}

//
// read a wavefront material library file
//
void	OBJmodel::objReadMTL( char *name )
{
    FILE	*file;
    char	*dir, *filename, buf[ 128 ];
    
	DWORD	nMaterials, i;
    
    dir = objDirName( pathname );
    filename = new char[ strlen(dir) + strlen(name) + 1 ];
    strcpy( filename, dir );
    strcat( filename, name );
    delete dir;
    
    file = fopen( filename, "r" );
    if ( !file ) exit(1);
    
	delete filename;

    // count materials
    nMaterials = 1;
    while( fscanf( file, "%s", buf ) != EOF ) 
	{
        switch( buf[ 0 ] )
		{
        case '#': // comment
            fgets(buf, sizeof(buf), file);
            break;
        case 'n': // new material
            fgets(buf, sizeof(buf), file);
            nMaterials++;
            sscanf(buf, "%s %s", buf, buf);
            break;
        default:
            fgets(buf, sizeof(buf), file);
            break;
        }
    }
    
    rewind( file );
    
    pMaterialList = new OBJMaterial[ nMaterials ];
    nMaterials    = nMaterials;
    

    // default material
	OBJMaterial defaultMaterial = {
		NULL, 
		0.8f, 0.8f, 0.8f, 1.0f, 
		0.2f, 0.2f, 0.2f, 1.0f,
		0.0f, 0.0f, 0.0f, 1.0f,
		0.0f, 0.0f, 0.0f, 0.0f, 65.0f
	};

    for ( i = 0; i < nMaterials; i++ )
        pMaterialList[ i ] = defaultMaterial;
    pMaterialList[ 0 ].name = strdup("default");
    
    // read material data
    nMaterials = 0;
    while( fscanf( file, "%s", buf ) != EOF )
	{
        switch( buf[ 0 ] )
		{
        case '#': // comment
            fgets(buf, sizeof(buf), file);
            break;
        case 'n': // new material
            fgets(buf, sizeof(buf), file);
            sscanf(buf, "%s %s", buf, buf);
            nMaterials++;
            pMaterialList[ nMaterials ].name = strdup(buf);
            break;
        case 'N': // shininess
            fscanf(file, "%f", &pMaterialList[nMaterials].data.Power);
            pMaterialList[nMaterials].data.Power /= 1000.0;
            pMaterialList[nMaterials].data.Power *= 128.0;
            break;
        case 'K': // color diffuse/specular/ambient
            switch(buf[1]) {
            case 'd':
                fscanf(file, "%f %f %f",
                    &pMaterialList[nMaterials].data.Diffuse.r,
                    &pMaterialList[nMaterials].data.Diffuse.g,
                    &pMaterialList[nMaterials].data.Diffuse.b );
                break;
            case 's':
                fscanf(file, "%f %f %f",
                    &pMaterialList[nMaterials].data.Specular.r,
                    &pMaterialList[nMaterials].data.Specular.g,
                    &pMaterialList[nMaterials].data.Specular.b );
                break;
            case 'a':
                fscanf(file, "%f %f %f",
                    &pMaterialList[nMaterials].data.Ambient.r,
                    &pMaterialList[nMaterials].data.Ambient.g,
                    &pMaterialList[nMaterials].data.Ambient.b );
                break;
            default:
                fgets(buf, sizeof(buf), file);
                break;
            }
            break;
            default:
                fgets(buf, sizeof(buf), file);
                break;
        }
    }
}


#define SKIPTRASH { \
	int c = -1; \
	do { c = fgetc( file ); } while ( c != ' ' && c != '\n' && c != 0 ); }

#ifdef RESTART_INDEX_PER_GROUP

#define ADJUSTGROUPINDEX { \
v += currentVertexListOffset - 1; \
n += currentVertexListOffset - 1; \
t += currentVertexListOffset - 1; }

#else

#define ADJUSTGROUPINDEX { \
		v --; n --; t --; }

#endif

//
// first read pass: get #vertices, #faces etc.
//
void	OBJmodel::objFirstPass(  FILE *file ) 
{
    unsigned    v, n, t;
    char        buf[128];
    
    OBJgroup *group = objAddGroup( "default" );
    
    nVertices = nNormals = nTexCoords = nTriangles = 0;

    while( fscanf( file, "%s", buf ) != EOF )
	{
        switch( buf[ 0 ] )
		{
        case '#': // comment
            fgets(buf, sizeof(buf), file);
            break;
        case 'v': // vertex data
            switch(buf[1]) {
            case '\0': // position
                fgets(buf, sizeof(buf), file);
                nVertices++;
                break;
            case 'x': // tangent space
                fgets(buf, sizeof(buf), file);
                nSpaces++;
                break;
            case 'n': // normal
                fgets(buf, sizeof(buf), file);
                nNormals++;
                break;
            case 't': // texture coordinate
                fgets(buf, sizeof(buf), file);
                nTexCoords++;
                break;
            default:
				// unknown token
                exit(1);
                break;
            }
            break;
            case 'm': // material
                fgets(buf, sizeof(buf), file);
                sscanf(buf, "%s %s", buf, buf);
                this->mtllibname = strdup(buf);
                objReadMTL( buf);
                break;
            case 'u':
                fgets(buf, sizeof(buf), file);
                break;
            case 'g': // group
                fgets(buf, sizeof(buf), file);
#if SINGLE_STRING_GROUP_NAMES
                sscanf(buf, "%s", buf);
#else
                buf[strlen(buf)-1] = '\0';
#endif
                group = objAddGroup( buf);
                break;
            case 'f': // polygon
                v = n = t = 0;
                fscanf(file, "%s", buf);
                // can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d
                if (strstr(buf, "//")) 
				{
                    // v//n 
                    sscanf(buf, "%d//%d", &v, &n);
                    SKIPTRASH fscanf(file, "%d//%d", &v, &n);
                    SKIPTRASH fscanf(file, "%d//%d", &v, &n);
                    nTriangles++;
                    group->nTriangles++;
                    SKIPTRASH 
					while(fscanf(file, "%d//%d", &v, &n) > 0) {
                        nTriangles++;
                        group->nTriangles++;
						SKIPTRASH 
                    }
                } else 
				if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) 
				{
                    // v/t/n 
                    SKIPTRASH fscanf(file, "%d/%d/%d", &v, &t, &n);
                    SKIPTRASH fscanf(file, "%d/%d/%d", &v, &t, &n);
                    nTriangles++;
                    group->nTriangles++;
					SKIPTRASH 
					while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
                        nTriangles++;
                        group->nTriangles++;
						SKIPTRASH 
                    }
                } else 
				if (sscanf(buf, "%d/%d", &v, &t) == 2) 
				{
                    // v/t
					SKIPTRASH fscanf(file, "%d/%d", &v, &t); 
                    SKIPTRASH fscanf(file, "%d/%d", &v, &t); 
                    nTriangles++;
                    group->nTriangles++;
					SKIPTRASH 
                    while(fscanf(file, "%d/%d", &v, &t) > 0) {
                        nTriangles++;
                        group->nTriangles++;
						SKIPTRASH 
                    }
                } else {
                    // v
                    SKIPTRASH fscanf(file, "%d", &v);
                    SKIPTRASH fscanf(file, "%d", &v);
                    nTriangles++;
                    group->nTriangles++;
					SKIPTRASH 
                    while(fscanf(file, "%d", &v) > 0) {
                        nTriangles++;
                        group->nTriangles++;
						SKIPTRASH 
                    }
                }
                break;
                
            default:
                fgets(buf, sizeof(buf), file);
                break;
        }
  }
  
  group = pGroupList;
  while ( group ) 
  {
      group->pTriangleList = new DWORD[ group->nTriangles ];
      group->nTriangles    = 0;
      group = group->next;
  }
}

//
// second read pass: get actual data
//
void	OBJmodel::objSecondPass( FILE *file ) 
{
    DWORD	material;
    DWORD	v, n, t;
    char	buf[128];

	int		currentVertexListOffset = 0;
	int		inVertexList = 0;
    
    OBJgroup *group = pGroupList;
    
    nVertices = nNormals = nSpaces = nTexCoords = nTriangles = material = 0;

    while( fscanf( file, "%s", buf ) != EOF )
	{
        switch( buf[ 0 ] )
		{
        case '#': // comment
            fgets(buf, sizeof(buf), file);
            break;
        case 'v': // vertex data
			if ( inVertexList == 0 )
			{
				currentVertexListOffset = nVertices;
				inVertexList = 1;
			}
            switch( buf[ 1  ] )
			{
            case '\0': // position
                fscanf( file, "%f %f %f", 
                    &pVertexList[ nVertices ].x, 
                    &pVertexList[ nVertices ].y, 
                    &pVertexList[ nVertices ].z );
                nVertices ++;
                break;
            case 'n': // normal
                fscanf( file, "%f %f %f", 
                    &pNormalList[ nNormals ].x,
                    &pNormalList[ nNormals ].y, 
                    &pNormalList[ nNormals ].z );
                nNormals ++;
                break;
            case 'x': // tangent space
                fscanf( file, "%f %f %f %f %f %f %f %f %f", 
                    &pNormalList[ nSpaces ].x,
                    &pNormalList[ nSpaces ].y, 
                    &pNormalList[ nSpaces ].z,
                    &pTangentList[ nSpaces ].x,
                    &pTangentList[ nSpaces ].y, 
                    &pTangentList[ nSpaces ].z,
                    &pBinormalList[ nSpaces ].x,
                    &pBinormalList[ nSpaces ].y, 
                    &pBinormalList[ nSpaces ].z );
                nSpaces ++;
                break;
            case 't': // texture coordinates
                fscanf( file, "%f %f", 
                    &pTexCoordList[ 2 * nTexCoords + 0 ],
                    &pTexCoordList[ 2 * nTexCoords + 1 ] );
                nTexCoords ++;
                break;
            }
            break;
            case 'u': // material
                fgets(buf, sizeof(buf), file);
                sscanf(buf, "%s %s", buf, buf);
                group->material = material = objFindMaterial( buf);
                break;
            case 'g': // group
                fgets(buf, sizeof(buf), file);
#if SINGLE_STRING_GROUP_NAMES
                sscanf(buf, "%s", buf);
#else
                buf[strlen(buf)-1] = '\0';
#endif
                group = objFindGroup( buf);
                group->material = material;

                break;
            case 'f': // polygon

				inVertexList = 0;
                v = n = t = 0;
                fscanf(file, "%s", buf);
                // can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d 
                if (strstr(buf, "//")) {
                    // v//n 
                    sscanf(buf, "%d//%d", &v, &n); ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[0] = v;
                    T(nTriangles).idxN[0] = n;
					SKIPTRASH 
                    fscanf(file, "%d//%d", &v, &n); ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[1] = v;
                    T(nTriangles).idxN[1] = n;
					SKIPTRASH 
                    fscanf(file, "%d//%d", &v, &n); ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[2] = v;
                    T(nTriangles).idxN[2] = n;
                    group->pTriangleList[group->nTriangles++] = nTriangles;
                    nTriangles++;
					SKIPTRASH 
                    while(fscanf(file, "%d//%d", &v, &n) > 0) {
						ADJUSTGROUPINDEX 
                        T(nTriangles).idxV[0] = T(nTriangles-1).idxV[0];
                        T(nTriangles).idxN[0] = T(nTriangles-1).idxN[0];
                        T(nTriangles).idxV[1] = T(nTriangles-1).idxV[2];
                        T(nTriangles).idxN[1] = T(nTriangles-1).idxN[2];
                        T(nTriangles).idxV[2] = v;
                        T(nTriangles).idxN[2] = n;
                        group->pTriangleList[group->nTriangles++] = nTriangles;
                        nTriangles++;
						SKIPTRASH 
                    }
                } else 
				if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
					ADJUSTGROUPINDEX 
                    // v/t/n 
                    T(nTriangles).idxV[0] = v;
                    T(nTriangles).idxT[0] = t;
                    T(nTriangles).idxN[0] = n;
                    SKIPTRASH fscanf(file, "%d/%d/%d", &v, &t, &n);
					ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[1] = v;
                    T(nTriangles).idxT[1] = t;
                    T(nTriangles).idxN[1] = n;
                    SKIPTRASH fscanf(file, "%d/%d/%d", &v, &t, &n);
					ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[2] = v;
                    T(nTriangles).idxT[2] = t;
                    T(nTriangles).idxN[2] = n;
                    group->pTriangleList[group->nTriangles++] = nTriangles;
                    nTriangles++;
					SKIPTRASH 
                    while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
						ADJUSTGROUPINDEX 
                        T(nTriangles).idxV[0] = T(nTriangles-1).idxV[0];
                        T(nTriangles).idxT[0] = T(nTriangles-1).idxT[0];
                        T(nTriangles).idxN[0] = T(nTriangles-1).idxN[0];
                        T(nTriangles).idxV[1] = T(nTriangles-1).idxV[2];
                        T(nTriangles).idxT[1] = T(nTriangles-1).idxT[2];
                        T(nTriangles).idxN[1] = T(nTriangles-1).idxN[2];
                        T(nTriangles).idxV[2] = v;
                        T(nTriangles).idxT[2] = t;
                        T(nTriangles).idxN[2] = n;
                        group->pTriangleList[group->nTriangles++] = nTriangles;
                        nTriangles++;
						SKIPTRASH 
                    }
                } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
                    // v/t 
					ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[0] = v;
                    T(nTriangles).idxT[0] = t;
                    SKIPTRASH fscanf(file, "%d/%d", &v, &t);
					
					ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[1] = v;
                    T(nTriangles).idxT[1] = t;
                    SKIPTRASH fscanf(file, "%d/%d", &v, &t);

					ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[2] = v;
                    T(nTriangles).idxT[2] = t;
                    group->pTriangleList[group->nTriangles++] = nTriangles;
                    nTriangles++;
					SKIPTRASH 
                    while(fscanf(file, "%d/%d", &v, &t) > 0) {
						ADJUSTGROUPINDEX 
                        T(nTriangles).idxV[0] = T(nTriangles-1).idxV[0];
                        T(nTriangles).idxT[0] = T(nTriangles-1).idxT[0];
                        T(nTriangles).idxV[1] = T(nTriangles-1).idxV[2];
                        T(nTriangles).idxT[1] = T(nTriangles-1).idxT[2];
                        T(nTriangles).idxV[2] = v;
                        T(nTriangles).idxT[2] = t;
                        group->pTriangleList[group->nTriangles++] = nTriangles;
                        nTriangles++; SKIPTRASH 
                    }
                } else {
                    // v 
                    sscanf(buf, "%d", &v); ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[0] = v;
                    SKIPTRASH fscanf(file, "%d", &v); ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[1] = v;
                    SKIPTRASH fscanf(file, "%d", &v); ADJUSTGROUPINDEX 
                    T(nTriangles).idxV[2] = v;
                    group->pTriangleList[group->nTriangles++] = nTriangles;
                    nTriangles++;
					SKIPTRASH 
                    while(fscanf(file, "%d", &v) > 0) {
						ADJUSTGROUPINDEX 
                        T(nTriangles).idxV[0] = T(nTriangles-1).idxV[0];
                        T(nTriangles).idxV[1] = T(nTriangles-1).idxV[2];
                        T(nTriangles).idxV[2] = v;
                        group->pTriangleList[group->nTriangles++] = nTriangles;
                        nTriangles++; SKIPTRASH 
                    }
                }
                break;
                
            default:
                fgets(buf, sizeof(buf), file);
                break;
    }
  }
}



//
// calculate face normals
//
void	OBJmodel::calcFaceNormals()
{
    DWORD    i;
	VERTEX3D u, v;
    
    if ( pFaceNormalList )
        delete pFaceNormalList;
    
    nFaceNormals    = nTriangles;
    pFaceNormalList = new VERTEX3D[ nFaceNormals ];
    
    for ( i = 0; i < nTriangles; i++ )
	{
        pTriangleList[ i ].idxFN = i;
        
		u = pVertexList[ T(i).idxV[ 1 ] ] - pVertexList[ T(i).idxV[ 0 ] ];
		v = pVertexList[ T(i).idxV[ 2 ] ] - pVertexList[ T(i).idxV[ 0 ] ];
        
		pFaceNormalList[ i ] = u ^ v;

		pTriangleList[ i ].area = pFaceNormalList[ i ] * pFaceNormalList[ i ];

		~pFaceNormalList[ i ];
    }
}

//
// create per vertex normals
//
void	OBJmodel::calcVertexNormals()
{
    if ( pNormalList )
        delete pNormalList;
    
    nNormals = nVertices;
    pNormalList = new VERTEX3D[ nNormals ];
	
	VERTEX3D *pTempList = new VERTEX3D[ this->nVertices ];

	for ( DWORD i = 0; i < nVertices; i++ )
		pNormalList[ i ].x =
		pNormalList[ i ].y =
		pNormalList[ i ].z = 0.0f;

	for ( i = 0; i < nTriangles; i++ )
	{
		VERTEX3D *faceNrml = &pFaceNormalList[ pTriangleList[ i ].idxFN ];

		pNormalList[ pTriangleList[ i ].idxV[ 0 ] ] += *faceNrml;
		pNormalList[ pTriangleList[ i ].idxV[ 1 ] ] += *faceNrml;
		pNormalList[ pTriangleList[ i ].idxV[ 2 ] ] += *faceNrml;
	}

	for ( i = 0; i < nVertices; i++ )
	{
		~pNormalList[ i ];
	}

	for ( i = 0; i < nTriangles; i++ )
	{
		OBJtriangle *tri = &pTriangleList[ i ];

		tri->idxN[ 0 ] = tri->idxV[ 0 ];
		tri->idxN[ 1 ] = tri->idxV[ 1 ];
		tri->idxN[ 2 ] = tri->idxV[ 2 ];
	}
}



//
// destructor
//
OBJmodel::~OBJmodel()
{
	if ( pMeshVB != NULL )
		pMeshVB->Release();
    
    if ( pathname )
		delete pathname;
    if ( mtllibname )
		delete mtllibname;
    if ( pVertexList )
		delete pVertexList;
    if ( pNormalList )  
		delete pNormalList;
    if ( pTexCoordList )  
		delete pTexCoordList;
    if ( pFaceNormalList ) 
		delete pFaceNormalList;
    if ( pTriangleList )  
		delete pTriangleList;

    if ( pMaterialList )
	{
        for ( DWORD i = 0; i < nMaterials; i++ )
            delete pMaterialList[ i ].name;

		delete pMaterialList;
    }

    while ( pGroupList ) 
	{
        OBJgroup *group = pGroupList;
        
		pGroupList = pGroupList->next;

        delete group->name;
        delete group->pTriangleList;
        delete group;
    }
}

// 
// read a Wavefront OBJ file
//
int	OBJmodel::readOBJ( char *filename )
{
    FILE*   file;
    
    file = fopen(filename, "rt" );
    
	if ( !file ) 
        return -1;
    
    this->pathname			= strdup( filename );
    this->mtllibname		= NULL;
    this->nVertices			= 0;
    this->pVertexList		= NULL;
    this->nNormals			= 0;
    this->pNormalList		= NULL;
    this->nSpaces			= 0;
    this->pTangentList		= NULL;
    this->pBinormalList		= NULL;
    this->nTexCoords		= 0;
    this->pTexCoordList		= NULL;
    this->nFaceNormals		= 0;
    this->pFaceNormalList   = NULL;
    this->nTriangles		= 0;
    this->pTriangleList     = NULL;
    this->nMaterials		= 0;
    this->pMaterialList     = NULL;
    this->nGroups			= 0;
    this->pGroupList		= NULL;

	// first pass: get #vertices etc.
    objFirstPass( file );
    
    // allocate memory

    pVertexList   = new VERTEX3D[ nVertices ];
    pTriangleList = new OBJtriangle[ nTriangles ];
    
	if ( nNormals > 0 )
        pNormalList = new VERTEX3D[ nNormals ];

	if ( nSpaces > 0 )
	{
        pNormalList = new VERTEX3D[ nSpaces ];
        pTangentList = new VERTEX3D[ nSpaces ];
        pBinormalList = new VERTEX3D[ nSpaces ];
	}

    if ( nTexCoords > 0 )
        pTexCoordList = new float[ 2 * this->nTexCoords ];
    
	// second pass: get data !
    rewind( file );
    objSecondPass( file );

	if ( pFaceNormalList == NULL )
		calcFaceNormals();

	if ( pNormalList == NULL )
		calcVertexNormals();

    fclose( file );

	return 0;
}



void	OBJmodel::scaleIsotropic()
{
	// calculate bounding box of mesh
	float	xminv = 1e37f, xmaxv = -1e37f;
	float	yminv = 1e37f, ymaxv = -1e37f;
	float	zminv = 1e37f, zmaxv = -1e37f;

	for ( DWORD i = 0; i < nVertices; i++ )
	{

		xminv = min( xminv, pVertexList[ i ].x );
		yminv = min( yminv, pVertexList[ i ].y );
		zminv = min( zminv, pVertexList[ i ].z );
		xmaxv = max( xmaxv, pVertexList[ i ].x );
		ymaxv = max( ymaxv, pVertexList[ i ].y );
		zmaxv = max( zmaxv, pVertexList[ i ].z );
	}

	float	xmove = -(float)0.5 * ( xminv + xmaxv );
	float	ymove = -(float)0.5 * ( yminv + ymaxv );
	float	zmove = -(float)0.5 * ( zminv + zmaxv );

	float	maxSize = (float)max( (float)fabs( xminv - xmaxv ), max( (float)fabs( yminv - ymaxv ), (float)fabs( zminv - zmaxv ) ) );

	float	scale = 10.0f / (float)maxSize;

	// translate and scale vertices
	for ( i = 0; i < nVertices; i++ )
	{
		pVertexList[ i ].x += xmove;
		pVertexList[ i ].y += ymove;
		pVertexList[ i ].z += zmove;
		pVertexList[ i ].x *= scale;
		pVertexList[ i ].y *= scale;
		pVertexList[ i ].z *= scale;
	}
}

void	OBJmodel::drawModel( LPDIRECT3DDEVICE9 pD3DDevice )
{
	pD3DDevice->SetStreamSource( 0, pMeshVB, 0, sizePerVertex );
	pD3DDevice->SetFVF( fvfMeshVertex );

	if ( currentMode & RENDER_MATERIAL )
	{
		OBJgroup	*pGroup = this->pGroupList;
		DWORD		currTriangle = 0;

		for ( DWORD i = 0; i < nGroups; i++ )
		{
			D3DMATERIAL9 *pMat = &pMaterialList[ pGroup->material ].data;

			pD3DDevice->SetMaterial( pMat );

			pD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, currTriangle * 3, pGroup->nTriangles );

			currTriangle += pGroup->nTriangles;

			pGroup = pGroup->next;
		}
	} else
	{
		/*int batch = nTriangles / 8;
		for ( int i = 0; i < 8; i++ )
		{
			pD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, i*batch*3, batch );
		}*/
		HRESULT hr = pD3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, nTriangles );
	}
}

void	OBJmodel::buildVertexBuffer( LPDIRECT3DDEVICE9 pD3DDevice, int mode )
{
	if ( pMeshVB )
		delete pMeshVB;

	currentMode   = mode;
	fvfMeshVertex = D3DFVF_XYZ;
	sizePerVertex = 3 * sizeof( float );

	if ( mode & RENDER_DIFFUSE_COLOR )
	{
		fvfMeshVertex |= D3DFVF_DIFFUSE;
		sizePerVertex += sizeof( DWORD );
	}

	if ( mode & RENDER_FLAT_SHADED || mode & RENDER_SMOOTH_SHADED )
	{
		fvfMeshVertex |= D3DFVF_NORMAL;
		sizePerVertex += 3 * sizeof( float );
	}

	if ( mode & RENDER_TEXTURE_COORDS )
	{
		if ( this->pTexCoordList )
		{
			fvfMeshVertex |= D3DFVF_TEX1;
			sizePerVertex += 2 * sizeof( float );
		} else
			mode &= ~RENDER_TEXTURE_COORDS;
	}

	if ( mode == RENDER_TANGENT_SPACE_UV )
	{
		fvfMeshVertex = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX3 |
			D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEXCOORDSIZE3(1) | D3DFVF_TEXCOORDSIZE2(2);
		sizePerVertex = ( 3 + 3 + 3 + 3 + 2 ) * sizeof( float );
	}
	
	HRESULT hr = pD3DDevice->CreateVertexBuffer( this->nTriangles * 3 * sizePerVertex, 
										 D3DUSAGE_DONOTCLIP|D3DUSAGE_WRITEONLY, 
										 fvfMeshVertex, D3DPOOL_MANAGED, &pMeshVB, NULL );

	float *pData = NULL;

	hr = pMeshVB->Lock( 0, 0, (void**)&pData, 0 );

    OBJgroup	*pGroup = this->pGroupList;

	for ( DWORD i = 0; i < nGroups; i++ )
	{
        D3DMATERIAL9 *pMat = &pMaterialList[ pGroup->material ].data;

		for ( DWORD j = 0; j < pGroup->nTriangles; j++ )
		{
			OBJtriangle *triangle = &T( pGroup->pTriangleList[ j ] );

			for ( DWORD k = 0; k < 3; k++ )
			{
				if ( mode == RENDER_TANGENT_SPACE_UV )
				{
					memcpy( pData, &pVertexList[ triangle->idxV[ k ] ], 3 * sizeof( float ) );
					pData += 3;

					memcpy( pData, &pNormalList[ triangle->idxN[ k ] ], 3 * sizeof( float ) );
					pData += 3;

					memcpy( pData, &pTangentList[ triangle->idxN[ k ] ], 3 * sizeof( float ) );
					pData += 3;
					
					memcpy( pData, &pBinormalList[ triangle->idxN[ k ] ], 3 * sizeof( float ) );
					pData += 3;

					memcpy( pData, &pTexCoordList[ 2 * triangle->idxT[ k ] ], 2 * sizeof( FLOAT ) );
					pData += 2;
				} else
				{
					memcpy( pData, &pVertexList[ triangle->idxV[ k ] ], 3 * sizeof( float ) );
					pData += 3;
					
					if ( mode & RENDER_FLAT_SHADED )
					{
						memcpy( pData, &pFaceNormalList[ triangle->idxFN ], 3 * sizeof( float ) );
						pData += 3;
					} else
					if ( mode & RENDER_SMOOTH_SHADED )
					{
						memcpy( pData, &pNormalList[ triangle->idxN[ k ] ], 3 * sizeof( float ) );
						pData += 3;
					}

					if ( mode & RENDER_DIFFUSE_COLOR )
					{
						DWORD rgba = D3DCOLOR_COLORVALUE( pMat->Diffuse.r, pMat->Diffuse.g, pMat->Diffuse.b, pMat->Diffuse.a );
						memcpy( pData, &rgba, sizeof( DWORD ) );
						pData += 1;
					}
            
					if ( mode & RENDER_TEXTURE_COORDS )
					{
						memcpy( pData, &pTexCoordList[ 2 * triangle->idxT[ k ] ], 2 * sizeof( FLOAT ) );
						pData += 2;
					}
				}
			}
		}

		pGroup = pGroup->next;
	}
	pMeshVB->Unlock();

}

