#pragma warning(disable:4786) 

#include "SM_Engine3DPCH.h"
#include "SM_Mesh.h"
#include "MSystemFunctions.h"
#include "MVFS.h"

using namespace std;

// Generic render vertex
void      RenderVertex::Init()
{
  nx=ny=nz=x=y=z=u=v=s=t=0.0f;
  diffuse=specular=0;
}

// Simple mesh element (single material+vertices+indices)
RenderMeshElement::RenderMeshElement()
{
  Init();
}

RenderMeshElement::~RenderMeshElement()
{
  Shutdown();
}

int RenderMeshElement::Init()
{
  m_uVertices=m_uIndices=0;
  m_pRenderVertices=0;
  m_pusIndices=0;
  m_iMaterial=-1;

  return (0);
}

int RenderMeshElement::Shutdown()
{
  if (m_pRenderVertices) { delete[] m_pRenderVertices; m_pRenderVertices=0; }
  if (m_pusIndices)       { delete[] m_pusIndices    ; m_pusIndices=0; }
  m_uVertices=m_uIndices=0;

  return (0);
}

int RenderMeshElement::Load(MVFSFILE* f)
{
  return (LoadCompressed(f));
  Shutdown();

  if (MVFS::fread(&m_uVertices, sizeof(unsigned), 1, f)!=1) return -1;
  if (MVFS::fread(&m_uIndices, sizeof(unsigned), 1, f)!=1) return -1;

  if (!(m_pRenderVertices=new RenderVertex[m_uVertices])) return -1;
  if (!(m_pusIndices=new unsigned short[m_uIndices])) return -1;

  if (MVFS::fread(m_pRenderVertices, sizeof(RenderVertex)*m_uVertices, 1, f)!=1) return -1;
  if (MVFS::fread(m_pusIndices, sizeof(unsigned short)*m_uIndices, 1, f)!=1) return -1;
  if (MVFS::fread(m_pcMaterial, sizeof(m_pcMaterial), 1, f)!=1) return -1;


  if (strcmp(m_pcMaterial, "")==0)
  {    
    m_iMaterial=ShaderManager::LoadShader("NULL");
  }
  else
  {
    m_iMaterial=ShaderManager::LoadShader(m_pcMaterial);
    if (m_iMaterial == -1)
    {
      SM_Main::OutputError("Failed to load material %s", m_pcMaterial);
    }
  }


/*
  MVFSFILE* f2;
  
  f2=MVFS::fopen("COMPRESSED.GEO", "ab");
  SaveCompressed(f2);
  MVFS::fclose(f2);

  f2=MVFS::fopen("UNCOMPRESSED.GEO", "ab");
  Save(f2);
  MVFS::fclose(f2);
  */
  
  
   

  return 0;
}


int RenderMeshElement::LoadCompressed(MVFSFILE* f)
{
  Shutdown();
  
  // Number of verts/indices
  if (MVFS::fread(&m_uVertices, sizeof(unsigned), 1, f)!=1) return -1;  
  if (MVFS::fread(&m_uIndices, sizeof(unsigned), 1, f)!=1) return -1;

  // Allocate vertices
  if (!(m_pRenderVertices=new RenderVertex[m_uVertices])) return -1;
  if (!(m_pusIndices=new unsigned short[m_uIndices])) return -1;

  SM_Main::OutputConsole("Loading meshelement with %i vertices and %i faces\n", m_uVertices, m_uIndices/3);

  
  // Positions
  unsigned i;  
  unsigned short* pusPos=new unsigned short[3*m_uVertices];    
  Vector3D v3dMin;
  Vector3D v3dMax;
  Vector3D v3dDiff;

  if (MVFS::fread(&v3dMin, sizeof(v3dMin), 1, f)!=1) return -1;
  if (MVFS::fread(&v3dMax, sizeof(v3dMax), 1, f)!=1) return -1;
  if (MVFS::fread(pusPos,  sizeof(unsigned short)*m_uVertices*3, 1, f)!=1) return -1;
  
  v3dDiff=v3dMax-v3dMin;
  for (i=0 ; i<m_uVertices ; i++)
  {
    m_pRenderVertices[i].x=v3dMin.x+v3dDiff.x*float(pusPos[3*i+0])/65535.0f;
    m_pRenderVertices[i].y=v3dMin.y+v3dDiff.y*float(pusPos[3*i+1])/65535.0f;
    m_pRenderVertices[i].z=v3dMin.z+v3dDiff.z*float(pusPos[3*i+2])/65535.0f;    
    m_pRenderVertices[i].specular=0;
    m_pRenderVertices[i].s=0.0f;
    m_pRenderVertices[i].t=0.0f;
  }
  
  delete[] pusPos; pusPos=0;

  // Normals
  //unsigned char* pcNormals=new unsigned char[2*m_uVertices];  
  //if (MVFS::fread(pcNormals,  sizeof(unsigned char)*m_uVertices*2, 1, f)!=1) return -1;
  unsigned char* pcNormals=new unsigned char[3*m_uVertices];  
  if (MVFS::fread(pcNormals,  sizeof(unsigned char)*m_uVertices*3, 1, f)!=1) return -1;
  
  for (i=0 ; i<m_uVertices ; i++)
  {
    Vector3D Normal;

    //Normal.x=2.0f*float(pcNormals[i*2+0]/255.0f)-1.0f;
    //Normal.y=2.0f*float((pcNormals[i*2+1]&0x7F)/127.0f)-1.0f;
    Normal.x=2.0f*float(pcNormals [i*3+0]/255.0f)-1.0f;
    Normal.y=2.0f*float(pcNormals [i*3+1]/255.0f)-1.0f;
    Normal.z=2.0f*float(pcNormals [i*3+2]/255.0f)-1.0f;
    
    /*
    float fSum=Normal.x*Normal.x+Normal.y*Normal.y;
    if (fSum>=1.0f)
    {
      Normal.z=0.0f;
    }
    else
    {
      Normal.z=sqrtf(1.0f-Normal.x*Normal.x-Normal.y*Normal.y)*((pcNormals[i*2+1]&0x80)?-1.0f:1.0f);
    }

  */
    Normal.Normalize();    

    m_pRenderVertices[i].nx=Normal.x;
    m_pRenderVertices[i].ny=Normal.y;
    m_pRenderVertices[i].nz=Normal.z;    
  }
  delete[] pcNormals; pcNormals=0;

  // Colors
  unsigned* puColors=new unsigned[m_uVertices];  
  if (MVFS::fread(puColors,  sizeof(unsigned)*m_uVertices, 1, f)!=1) return -1;  
  for (i=0 ; i<m_uVertices ; i++)
  {
    m_pRenderVertices[i].diffuse=puColors[i];    
  }
  delete[] puColors; puColors=0;

  // Texture coords
  unsigned short* pusTexCoords=new unsigned short[2*m_uVertices];  
  
  if (MVFS::fread(&v3dMin.x, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fread(&v3dMin.y, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fread(&v3dMax.x, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fread(&v3dMax.y, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fread(pusTexCoords,  sizeof(unsigned short)*m_uVertices*2, 1, f)!=1) return -1;
  v3dDiff=(v3dMax-v3dMin);
  
  for (i=0 ; i<m_uVertices ; i++)
  {
    m_pRenderVertices[i].u=v3dMin.x+v3dDiff.x*float(pusTexCoords[2*i+0])/65535.0f;
    m_pRenderVertices[i].v=v3dMin.y+v3dDiff.y*float(pusTexCoords[2*i+1])/65535.0f;    
  }  
  delete[] pusTexCoords; pusTexCoords=0;


  // Material Length
  int iLen;
  if (MVFS::fread(&iLen, sizeof(unsigned), 1, f)!=1) return -1;
  if (iLen)
  {
    if (MVFS::fread(m_pcMaterial, iLen, 1, f)!=1) return -1;
  }
  m_pcMaterial[iLen]=0;

  // Indices
  if (m_uVertices>255)
  {
    if (MVFS::fread(m_pusIndices, sizeof(unsigned short)*m_uIndices, 1, f)!=1) return -1;
  }
  else
  {
    unsigned char* pcIndices=new unsigned char[m_uIndices];  
    if (MVFS::fread(pcIndices,  sizeof(unsigned char)*m_uIndices, 1, f)!=1) return -1;
    
    for (i=0 ; i<m_uIndices ; i++)
    {
      m_pusIndices[i]=pcIndices[i];      
    }
    delete[] pcIndices;    
  }

  if (strcmp(m_pcMaterial, "")==0)
  {    
    m_iMaterial=ShaderManager::LoadShader("NULL");
  }
  else
  {
    m_iMaterial=ShaderManager::LoadShader(m_pcMaterial);
  }
  
  return 0;
}


int RenderMeshElement::SaveCompressed(MVFSFILE* f)
{
  Vector3D v3dMin(FLT_MAX, FLT_MAX, FLT_MAX);
  Vector3D v3dMax(FLT_MIN, FLT_MIN, FLT_MIN);

  // Number of verts/indices

  if (MVFS::fwrite(&m_uVertices, sizeof(unsigned), 1, f)!=1) return -1;  
  if (MVFS::fwrite(&m_uIndices, sizeof(unsigned), 1, f)!=1) return -1;
  
  // Positions
  unsigned i;
  for (i=0 ; i<m_uVertices ; i++)
  {
    if (m_pRenderVertices[i].x<v3dMin.x) v3dMin.x=m_pRenderVertices[i].x;
    if (m_pRenderVertices[i].x>v3dMax.x) v3dMax.x=m_pRenderVertices[i].x;
    if (m_pRenderVertices[i].y<v3dMin.y) v3dMin.y=m_pRenderVertices[i].y;
    if (m_pRenderVertices[i].y>v3dMax.y) v3dMax.y=m_pRenderVertices[i].y;
    if (m_pRenderVertices[i].z<v3dMin.z) v3dMin.z=m_pRenderVertices[i].z;
    if (m_pRenderVertices[i].z>v3dMax.z) v3dMax.z=m_pRenderVertices[i].z;
  }
  Vector3D v3dDiff=(v3dMax-v3dMin);
  
  unsigned short* pusPos=new unsigned short[3*m_uVertices];  
  for (i=0 ; i<m_uVertices ; i++)
  {
    pusPos[3*i+0]=unsigned short(65535.0f*(m_pRenderVertices[i].x-v3dMin.x)/v3dDiff.x);
    pusPos[3*i+1]=unsigned short(65535.0f*(m_pRenderVertices[i].y-v3dMin.y)/v3dDiff.y);
    pusPos[3*i+2]=unsigned short(65535.0f*(m_pRenderVertices[i].z-v3dMin.z)/v3dDiff.z);
  }

  if (MVFS::fwrite(&v3dMin, sizeof(v3dMin), 1, f)!=1) return -1;
  if (MVFS::fwrite(&v3dMax, sizeof(v3dMax), 1, f)!=1) return -1;
  if (MVFS::fwrite(pusPos,  sizeof(unsigned short)*m_uVertices*3, 1, f)!=1) return -1;

  delete[] pusPos; pusPos=0;

  // Normals
  //unsigned char* pcNormals=new unsigned char[2*m_uVertices];  
  unsigned char* pcNormals=new unsigned char[3*m_uVertices];  
  for (i=0 ; i<m_uVertices ; i++)
  {    
    pcNormals[i*3+0]=unsigned char(0.5f*(1.0f+m_pRenderVertices[i].nx)*255.0f);
    pcNormals[i*3+1]=unsigned char(0.5f*(1.0f+m_pRenderVertices[i].ny)*255.0f);
    pcNormals[i*3+2]=unsigned char(0.5f*(1.0f+m_pRenderVertices[i].nz)*255.0f);
    //pcNormals[i*2+1]=unsigned char(0.5f*(1.0f+m_pRenderVertices[i].ny)*127.0f);      
    //pcNormals[i*2+1]|=(m_pRenderVertices[i].nz<0.0f?128:0);
  }
  //if (MVFS::fwrite(pcNormals,  sizeof(unsigned char)*m_uVertices*2, 1, f)!=1) return -1;
  if (MVFS::fwrite(pcNormals,  sizeof(unsigned char)*m_uVertices*3, 1, f)!=1) return -1;
  delete[] pcNormals; pcNormals=0;

  // Colors
  unsigned* puColors=new unsigned[m_uVertices];  
  for (i=0 ; i<m_uVertices ; i++)
  {
    puColors[i]=m_pRenderVertices[i].diffuse;    
  }
  if (MVFS::fwrite(puColors,  sizeof(unsigned)*m_uVertices, 1, f)!=1) return -1;
  delete[] puColors; puColors=0;

  // Texture coords
  for (i=0 ; i<m_uVertices ; i++)
  {
    if (m_pRenderVertices[i].u<v3dMin.x) v3dMin.x=m_pRenderVertices[i].u;
    if (m_pRenderVertices[i].u>v3dMax.x) v3dMax.x=m_pRenderVertices[i].u;
    if (m_pRenderVertices[i].v<v3dMin.y) v3dMin.y=m_pRenderVertices[i].v;
    if (m_pRenderVertices[i].v>v3dMax.y) v3dMax.y=m_pRenderVertices[i].v;    
  }
  v3dDiff=(v3dMax-v3dMin);
  
  unsigned short* pusTexCoords=new unsigned short[2*m_uVertices];  
  for (i=0 ; i<m_uVertices ; i++)
  {
    pusTexCoords[2*i+0]=unsigned short(65535.0f*(m_pRenderVertices[i].u-v3dMin.x)/v3dDiff.x);
    pusTexCoords[2*i+1]=unsigned short(65535.0f*(m_pRenderVertices[i].v-v3dMin.y)/v3dDiff.y);    
  }

  if (MVFS::fwrite(&v3dMin.x, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fwrite(&v3dMin.y, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fwrite(&v3dMax.x, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fwrite(&v3dMax.y, sizeof(float), 1, f)!=1) return -1;
  if (MVFS::fwrite(pusTexCoords,  sizeof(unsigned short)*m_uVertices*2, 1, f)!=1) return -1;

  delete[] pusTexCoords; pusTexCoords=0;


  // Material Length
  int iLen=strlen(m_pcMaterial);
  if (MVFS::fwrite(&iLen, sizeof(unsigned), 1, f)!=1) return -1;

  if (iLen)
  {
    if (MVFS::fwrite(m_pcMaterial, strlen(m_pcMaterial), 1, f)!=1) return -1;
  }

  // Indices
  if (m_uVertices>255)
  {
    if (MVFS::fwrite(m_pusIndices, sizeof(unsigned short)*m_uIndices, 1, f)!=1) return -1;
  }
  else
  {
    unsigned char* pcIndices=new unsigned char[m_uIndices];  
    for (i=0 ; i<m_uIndices ; i++)
    {
      pcIndices[i]=(unsigned char)m_pusIndices[i];      
    }
    if (MVFS::fwrite(pcIndices,  sizeof(unsigned char)*m_uIndices, 1, f)!=1) return -1;
    delete[] pcIndices; pcIndices=0;
  }
  
  return 0;
}

int RenderMeshElement::Save(MVFSFILE* f)
{  
  return (SaveCompressed(f));
  if (MVFS::fwrite(&m_uVertices, sizeof(unsigned), 1, f)!=1) return -1;
  if (MVFS::fwrite(&m_uIndices, sizeof(unsigned), 1, f)!=1) return -1;
  if (MVFS::fwrite(m_pRenderVertices, sizeof(RenderVertex)*m_uVertices, 1, f)!=1) return -1;
  if (MVFS::fwrite(m_pusIndices, sizeof(unsigned short)*m_uIndices, 1, f)!=1) return -1;
  if (MVFS::fwrite(m_pcMaterial, sizeof(m_pcMaterial), 1, f)!=1) return -1;

  return 0;
}


// Set of mesh elements
RenderMesh::RenderMesh()
{
  m_pElements=0;
  m_uElements=0;
  m_pcName   =0;
  
}

RenderMesh::~RenderMesh()
{
  Shutdown();
}


int RenderMesh::Init()
{
  Shutdown();
  return (0);
}

int RenderMesh::Shutdown()
{
  if (m_pElements) { delete[] m_pElements; m_pElements=0; }
  if (m_pcName) { delete[] m_pcName; m_pcName=0; }
  m_uElements=0;

  return (0);
}  


int RenderMesh::Load(MVFSFILE* f)
{
  Shutdown();


  int iStrlen;
  if (MVFS::fread(&iStrlen , sizeof(int), 1, f)!=1) return -1;

  m_pcName=new char[iStrlen+1];
  if (!m_pcName)
  {
    return -1;
  }

  if (MVFS::fread(m_pcName, iStrlen*sizeof(char), 1, f)!=1) return -1;
  m_pcName[iStrlen]=0;
  _strupr(m_pcName);


  if (MVFS::fread(&m_uElements, sizeof(unsigned), 1, f)!=1) return -1;


  m_pElements=new RenderMeshElement[m_uElements];
  if (!m_pElements)
  {
    return -1;
  }

  for (unsigned i=0 ; i<m_uElements ; i++)
  {
    if (m_pElements[i].Load(f)!=1) return -1;
  }
  
  return 0;
}

int RenderMesh::Save(MVFSFILE* f)
{
  int iStrlen=strlen(m_pcName);

  if (MVFS::fwrite(&iStrlen , sizeof(int), 1, f)!=1) return -1;
  if (MVFS::fwrite(m_pcName, iStrlen*sizeof(char), 1, f)!=1) return -1;

  if (MVFS::fwrite(&m_uElements, sizeof(unsigned), 1, f)!=1) return -1;

  unsigned i;

  for (i=0 ; i<m_uElements ; i++)
  {
    if (m_pElements[i].m_uVertices>65535)
    {
        SM_Main::OutputError("Malla %s tiene demasiados vertices",
            m_pcName, m_pElements[i].m_uVertices);
    }

    if (m_pElements[i].m_uIndices>60000)
    {
        SM_Main::OutputError("Malla %s tiene demasiadas caras (%i)",
            m_pcName, m_pElements[i].m_uIndices/3);
    }

    if (m_pElements[i].Save(f)!=1) return -1;
  }
  
  return 0;
}


int Mesh::Init()
{
  return (0);
}

int Mesh::Shutdown()
{
  return (0);
}

  

/*
*/

struct Vertex
{
  VertexPosition m_Position;
  VertexNormal   m_Normal;
  VertexTexCoord m_TexCoords;
  VertexColor    m_VertexColor;
  int            m_iSmoothingGroup;  
  int            m_iMaterial;    
};



bool operator<(const Vertex& a, const Vertex& b)
{
  return (memcmp(&a, &b, sizeof(Vertex))<0);
}

bool FaceMaterialCmp(const Face& a, const Face& b)
{
  return (a.m_iMaterial<b.m_iMaterial);
}


struct PositionSmooth
{  
  VertexPosition m_Position;
  int            m_iSmoothingGroup;  
};


bool operator<(const PositionSmooth& a, const PositionSmooth& b)
{
  return (memcmp(&a, &b, sizeof(PositionSmooth))<0);
}

int Mesh::GenerateRenderMesh(RenderMesh* pRenderMesh)
{
  unsigned i;
  unsigned j;
  vector<int>           Indices;
  map<Vertex, int>      UniqueVertices;
  vector<Vertex>        FinalVertices;
  int                   iVertices=0;
  int                   iBucket  =0;

    
  
  // Degenerate mesh?
  if (!m_Faces.size()) return (0);


  // Generate normals
  map<PositionSmooth, Vector3D> m_PositionSmooths;

  // Unique position-smoothing groups
  for (i=0 ; i<m_Faces.size() ; i++)
  {
    for (j=0 ; j<3 ; j++)
    {
      PositionSmooth ps;

      ps.m_Position=m_Positions[m_Faces[i].m_piPositions[j]];                             
      ps.m_iSmoothingGroup=m_Faces[i].m_iSmoothingGroup;      

      m_PositionSmooths.insert(std::pair<PositionSmooth, Vector3D>(ps, Vector3D(0.0f, 0.0f, 0.0f)));      
    }
  }

  // Generate normals for faces
  for (i=0 ; i<m_Faces.size() ; i++)
  {
    Vector3D vA=Vector3D(m_Positions[m_Faces[i].m_piPositions[0]].x,
                         m_Positions[m_Faces[i].m_piPositions[0]].y,
                         m_Positions[m_Faces[i].m_piPositions[0]].z);
    Vector3D vB=Vector3D(m_Positions[m_Faces[i].m_piPositions[1]].x,
                         m_Positions[m_Faces[i].m_piPositions[1]].y,
                         m_Positions[m_Faces[i].m_piPositions[1]].z);
    Vector3D vC=Vector3D(m_Positions[m_Faces[i].m_piPositions[2]].x,
                         m_Positions[m_Faces[i].m_piPositions[2]].y,
                         m_Positions[m_Faces[i].m_piPositions[2]].z);

    Vector3D vNormal=Vector3D::Cross((vC-vA),(vB-vA)).Normalize();

    for (j=0 ; j<3 ; j++)
    {
      PositionSmooth ps;

      ps.m_Position=m_Positions[m_Faces[i].m_piPositions[j]];
      ps.m_iSmoothingGroup=m_Faces[i].m_iSmoothingGroup;      
  
      m_PositionSmooths[ps]+=vNormal;
    }
  }

  // Normalize vertex normals
  map<PositionSmooth, Vector3D>::iterator Iter;
  for (Iter=m_PositionSmooths.begin() ; Iter!=m_PositionSmooths.end() ; Iter++)
  {
    Iter->second.Normalize();
  }

  int iNormals=0;
  for (i=0 ; i<m_Faces.size() ; i++)
  {
    for (j=0 ; j<3 ; j++)
    {
      PositionSmooth ps;

      ps.m_Position=m_Positions[m_Faces[i].m_piPositions[j]];                             
      ps.m_iSmoothingGroup=m_Faces[i].m_iSmoothingGroup;      

      Vector3D v3d=m_PositionSmooths[ps];

      VertexNormal Normal;
      Normal.x=v3d.x;
      Normal.y=v3d.y;
      Normal.z=v3d.z;

      m_VertexNormals.push_back(Normal);

      m_Faces[i].m_piNormals[j]=iNormals;
      iNormals++;
    }
  }



  // Sort faces by material
  sort(m_Faces.begin(), m_Faces.end(), FaceMaterialCmp);

  // We start with the material of the first face
  int iCurrentMaterial;
  
  
  iCurrentMaterial=m_Faces[0].m_iMaterial;

  // Count number of RenderMeshElement we'll need
  pRenderMesh->m_uElements=1;
  for (i=0 ; i<m_Faces.size() ; i++)
  {
    if (m_Faces[i].m_iMaterial!=iCurrentMaterial)
    {
      pRenderMesh->m_uElements++;
      iCurrentMaterial=m_Faces[i].m_iMaterial;
    }
  }

  // Allocate memory for RenderMeshElements
  pRenderMesh->m_pElements=new RenderMeshElement[pRenderMesh->m_uElements];
  if (!pRenderMesh->m_pElements)
  {
    goto HASERROR;
  }


  // Make unique vertices and material buckets
  iCurrentMaterial=m_Faces[0].m_iMaterial;
  for (i=0 ; i<m_Faces.size() ; )
  {
    Vertex v;

    for (j=0 ; j<3 ; j++)
    {
      // Generate vertex from face
      v.m_Position=m_Positions   [m_Faces[i].m_piPositions[j]];
      v.m_Normal=m_VertexNormals[m_Faces[i].m_piNormals[j]];
      
      if (m_iFlags & E_TEXCORDS)
      {
        v.m_TexCoords=m_TexCoords   [m_Faces[i].m_piTexCoords[j]];
      }
      else
      {
        v.m_TexCoords.u=v.m_TexCoords.v=0.0f;
      }

      if (m_iFlags & E_VERTEXCOLORS)
      {
        v.m_VertexColor=m_VertexColors[m_Faces[i].m_piColors   [j]];
      }
      else
      {
        v.m_VertexColor.r=v.m_VertexColor.g=v.m_VertexColor.b=1.0f;
      }
      
      v.m_iSmoothingGroup=(m_iFlags & E_SMOOTHINGGROUPS)?m_Faces[i].m_iSmoothingGroup:0;
      v.m_iMaterial      =m_Faces[i].m_iMaterial;
      
      if (UniqueVertices.find(v)==UniqueVertices.end())
      {
        // New unique vertex
        UniqueVertices.insert(std::pair<Vertex, int>(v, iVertices));
        FinalVertices.push_back(v);
        iVertices++;
      }

      // Store index 
      int iIndex=UniqueVertices[v];
      Indices.push_back(iIndex);
      assert(Indices[Indices.size()-1] < (int) UniqueVertices.size());
    }    
    
    // Next face
    i++;
    if (i==m_Faces.size() || m_Faces[i].m_iMaterial!=iCurrentMaterial)
    {     
      // Store mesh elements

      // Allocate Material
      pRenderMesh->m_pElements[iBucket].m_iMaterial=iCurrentMaterial;

      // Allocate indices
      pRenderMesh->m_pElements[iBucket].m_uIndices =Indices.size();

      if (Indices.size()>65535)
      {
        assert(!"Too many indices");
      }
      pRenderMesh->m_pElements[iBucket].m_pusIndices=new unsigned short[Indices.size()];
      if (!pRenderMesh->m_pElements[iBucket].m_pusIndices)
      {
        goto HASERROR;
      }

      // Copy indices
      unsigned j;
      for (j=0 ; j<pRenderMesh->m_pElements[iBucket].m_uIndices ; j++)
      {
        pRenderMesh->m_pElements[iBucket].m_pusIndices[j]=Indices[j];
      }

      #ifdef _DEBUG
      for (j=0 ; j<pRenderMesh->m_pElements[iBucket].m_uIndices ; j++)
      {
        assert(pRenderMesh->m_pElements[iBucket].m_pusIndices[j]<UniqueVertices.size());
      }
      #endif


      // Copy vertices
      pRenderMesh->m_pElements[iBucket].m_uVertices=UniqueVertices.size();
      pRenderMesh->m_pElements[iBucket].m_pRenderVertices=new RenderVertex[UniqueVertices.size()];
      if (!pRenderMesh->m_pElements[iBucket].m_pRenderVertices)
      {
        goto HASERROR;
      }
      for (j=0 ; j<pRenderMesh->m_pElements[iBucket].m_uVertices ; j++)
      {
        RenderVertex* pVertex=&(pRenderMesh->m_pElements[iBucket].m_pRenderVertices[j]);
        pVertex->Init();
        pVertex->x =FinalVertices[j].m_Position.x;
        pVertex->y =FinalVertices[j].m_Position.y;
        pVertex->z =FinalVertices[j].m_Position.z;
        pVertex->u =FinalVertices[j].m_TexCoords.u;
        pVertex->v =FinalVertices[j].m_TexCoords.v;
        pVertex->nx=FinalVertices[j].m_Normal.x;
        pVertex->ny=FinalVertices[j].m_Normal.y;
        pVertex->nz=FinalVertices[j].m_Normal.z;

        if (m_iFlags & E_VERTEXCOLORS)
        {
          pVertex->diffuse=0xFF000000 |
                           (int(255.0f*FinalVertices[j].m_VertexColor.r)<<16) |
                           (int(255.0f*FinalVertices[j].m_VertexColor.g)<<8) |
                           (int(255.0f*FinalVertices[j].m_VertexColor.b)) ;
        }
        else
        {
          pVertex->diffuse=0xFFFFFFFF;
        }
        
      }
      
      
      // Reset indices list
      Indices.clear();

      // Reset vertices map
      UniqueVertices.clear();

      // Clear vertices
      FinalVertices.clear();

      iVertices=0;

      // Next material
      if (i!=m_Faces.size())
      {
        iCurrentMaterial=m_Faces[i].m_iMaterial;
      }

      // Next Bucket
      iBucket++;
    }
  }

  return (0);

HASERROR:
  pRenderMesh->Shutdown();
  return (-1);
}

int Mesh::GenerateNormals()
{
  /*
  int i;

  for (i=0 ; i<m_Faces.size() ; i++)
  {
    Vector3D vA,vB,vC;

    vA=Vector3D(m_Faces[i].m_piPositions[0].x, m_Faces[i].m_piPositions[0].y, m_Faces[i].m_piPositions[0].z);

  }
  */

  return (0);
}

int Mesh::Import(MetaFileNode* pMetaFileNode)
{
  MetaFileNode* pNode;  
  unsigned i;

  if (!pMetaFileNode)
  {
    assert(!"NULL Pointer passed to Mesh::Import()");
    return (-1);
  }  

  // Reset flags
  m_iFlags=0;

  // Get MAXMESH node. This is the root of all the interesting data
  pNode=pMetaFileNode;

  MetaFileNode* pVertices;
  pVertices=pNode->GetSon("VERTICES"); if (!pVertices) return (-1);  
  for (i=0 ; i<pVertices->m_Sons.size() ; i+=3)
  {
    VertexPosition v;

    assert(
      pVertices->m_Sons[i+0]->m_Token.m_eType==MetaFileToken::E_FLOAT &&
      pVertices->m_Sons[i+1]->m_Token.m_eType==MetaFileToken::E_FLOAT &&
      pVertices->m_Sons[i+2]->m_Token.m_eType==MetaFileToken::E_FLOAT );

    v.x=pVertices->m_Sons[i+0]->m_Token.m_fValue;
    v.y=pVertices->m_Sons[i+1]->m_Token.m_fValue;
    v.z=pVertices->m_Sons[i+2]->m_Token.m_fValue;

    m_Positions.push_back(v);
  }


  // Tex coords
  MetaFileNode* pUVVertices;
  pUVVertices=pNode->GetSon("UVVERTICES"); if (!pUVVertices) return (-1);  

  // Mark flag if necessary
  if (pUVVertices->m_Sons.size()) m_iFlags |=E_TEXCORDS;

  // Read coords
  for (i=0 ; i<pUVVertices->m_Sons.size() ; i+=2)
  {
    VertexTexCoord v;

    assert(
      pUVVertices->m_Sons[i+0]->m_Token.m_eType==MetaFileToken::E_FLOAT &&
      pUVVertices->m_Sons[i+1]->m_Token.m_eType==MetaFileToken::E_FLOAT );

    v.u=pUVVertices->m_Sons[i+0]->m_Token.m_fValue;
    v.v=pUVVertices->m_Sons[i+1]->m_Token.m_fValue;    

    m_TexCoords.push_back(v);
  }


  //  Color vertices
  MetaFileNode* pColorVertices;
  pColorVertices=pNode->GetSon("COLORVERTICES"); if (!pColorVertices) return (-1);  

  // Mark flag if necessary
  if (pColorVertices->m_Sons.size()) m_iFlags |=E_VERTEXCOLORS;

  // Read color verices
  for (i=0 ; i<pColorVertices->m_Sons.size() ; i+=3)
  {
    VertexColor v;

    assert(
      pColorVertices->m_Sons[i+0]->m_Token.m_eType==MetaFileToken::E_FLOAT &&
      pColorVertices->m_Sons[i+1]->m_Token.m_eType==MetaFileToken::E_FLOAT &&
      pColorVertices->m_Sons[i+2]->m_Token.m_eType==MetaFileToken::E_FLOAT );

    v.r=pColorVertices->m_Sons[i+0]->m_Token.m_fValue;
    v.g=pColorVertices->m_Sons[i+1]->m_Token.m_fValue;
    v.b=pColorVertices->m_Sons[i+2]->m_Token.m_fValue;

    m_VertexColors.push_back(v);
  }



  MetaFileNode* pFaces           =0;
  MetaFileNode* pVerticesIndices =0;
  MetaFileNode* pTVerticesIndices=0;
  MetaFileNode* pCVerticesIndices=0;
  MetaFileNode* pMaterialIndices =0;
  MetaFileNode* pSmoothingGroups =0;
  pFaces=pNode->GetSon("FACES"); if (!pFaces) return (-1);  


  pVerticesIndices=pFaces->GetSon("VERTICESINDICES"); if (!pVerticesIndices) return (-1);
  for (i=0 ; i<pVerticesIndices->m_Sons.size() ; i+=3)
  {
    Face f;

    assert(
      pVerticesIndices->m_Sons[i+0]->m_Token.m_eType==MetaFileToken::E_INT &&
      pVerticesIndices->m_Sons[i+1]->m_Token.m_eType==MetaFileToken::E_INT &&
      pVerticesIndices->m_Sons[i+2]->m_Token.m_eType==MetaFileToken::E_INT );

    f.m_piPositions[0]=pVerticesIndices->m_Sons[i+0]->m_Token.m_iValue;
    f.m_piPositions[1]=pVerticesIndices->m_Sons[i+1]->m_Token.m_iValue;
    f.m_piPositions[2]=pVerticesIndices->m_Sons[i+2]->m_Token.m_iValue;

    m_Faces.push_back(f);
  }

  if (m_iFlags & E_TEXCORDS)
  {
    pTVerticesIndices=pFaces->GetSon("TVERTICESINDICES"); if (!pTVerticesIndices) return (-1);

    if (pTVerticesIndices->m_Sons.size()!=3*m_Faces.size())
    {
      assert(!"Wrong number of TVerticesIndices");
      return (-1);
    }

    for (i=0 ; i<pTVerticesIndices->m_Sons.size() ; i+=3)
    {
      assert(
        pTVerticesIndices->m_Sons[i+0]->m_Token.m_eType==MetaFileToken::E_INT &&
        pTVerticesIndices->m_Sons[i+1]->m_Token.m_eType==MetaFileToken::E_INT &&
        pTVerticesIndices->m_Sons[i+2]->m_Token.m_eType==MetaFileToken::E_INT );

      m_Faces[i/3].m_piTexCoords[0]=pTVerticesIndices->m_Sons[i+0]->m_Token.m_iValue;
      m_Faces[i/3].m_piTexCoords[1]=pTVerticesIndices->m_Sons[i+1]->m_Token.m_iValue;
      m_Faces[i/3].m_piTexCoords[2]=pTVerticesIndices->m_Sons[i+2]->m_Token.m_iValue;
    }
  }

  if (m_iFlags & E_VERTEXCOLORS)
  {
    pCVerticesIndices=pFaces->GetSon("CVERTICESINDICES"); if (!pCVerticesIndices) return (-1);

    if (pCVerticesIndices->m_Sons.size()!=3*m_Faces.size())
    {
      assert(!"Wrong number of CVerticesIndices");
      return (-1);
    }

    for (i=0 ; i<pCVerticesIndices->m_Sons.size() ; i+=3)
    {
      assert(
        pCVerticesIndices->m_Sons[i+0]->m_Token.m_eType==MetaFileToken::E_INT &&
        pCVerticesIndices->m_Sons[i+1]->m_Token.m_eType==MetaFileToken::E_INT &&
        pCVerticesIndices->m_Sons[i+2]->m_Token.m_eType==MetaFileToken::E_INT );

      m_Faces[i/3].m_piColors[0]=pCVerticesIndices->m_Sons[i+0]->m_Token.m_iValue;
      m_Faces[i/3].m_piColors[1]=pCVerticesIndices->m_Sons[i+1]->m_Token.m_iValue;
      m_Faces[i/3].m_piColors[2]=pCVerticesIndices->m_Sons[i+2]->m_Token.m_iValue;
    }
  }

  pSmoothingGroups=pFaces->GetSon("SMOOTHINGGROUP");
  if (pSmoothingGroups)
  {
    m_iFlags |= E_SMOOTHINGGROUPS;
  }

  if (m_iFlags & E_SMOOTHINGGROUPS)
  {
    pSmoothingGroups=pFaces->GetSon("SMOOTHINGGROUP"); if (!pSmoothingGroups) return (-1);

    if (pSmoothingGroups->m_Sons.size()!=m_Faces.size())
    {
      assert(!"Wrong number of Smoothing groups");
      return (-1);
    }

    for (i=0 ; i<pSmoothingGroups->m_Sons.size() ; i++)
    {
      assert(pSmoothingGroups->m_Sons[i]->m_Token.m_eType==MetaFileToken::E_INT);
      m_Faces[i].m_iSmoothingGroup=pSmoothingGroups->m_Sons[i]->m_Token.m_iValue;
    }
  }
  else
  {
    for (i=0 ; i<m_Faces.size() ; i++)
    {
      m_Faces[i].m_iSmoothingGroup=0;
    }
  }

  // Materials
  pMaterialIndices=pFaces->GetSon("MATERIALINDICES"); if (!pMaterialIndices) return (-1);
  for (i=0 ; i<m_Faces.size() ; i++)
  {
    if (pMaterialIndices->m_Sons.size()!=m_Faces.size())
    {
      assert(!"Wrong number of material indices");
      return (-1);
    }

    assert(pMaterialIndices->m_Sons[i]->m_Token.m_eType==MetaFileToken::E_INT);
    m_Faces[i].m_iMaterial=pMaterialIndices->m_Sons[i]->m_Token.m_iValue;
  }
  
  GenerateNormals();
  m_iFlags|=E_NORMALS;

  return (0);
}
