#include "SM_Engine3DPCH.h"
#include "SM_GeomMorpher.h"
#include "SM_ParticleSystem.h"
#include "SM_KeyFrameSequence.h"
#include "SM_MathPch.h"
#include "MFastFPU.h"



#define FIGURES 7

bool SM_GeomMorpher::m_bIndices = false;
bool SM_GeomMorpher::m_bGeometries = false;
Vector3D SM_GeomMorpher::m_v3dPositions[NVERTICES];
Vector3D SM_GeomMorpher::m_v3dNormals[NVERTICES];
Vector3D m_v3dDataBuffers[FIGURES][NVERTICES];



  
__forceinline void Evaluate1(float u, float v, Vector3D* pOut)
{
  float r = 0;
  float theta = 2*PI*u;
  float phi = v*PI;

   static float m[8] = 
   {
      1.0, 3.0f, 5.0f, 6.0f, 5.0f, 3.0f, 5.0f, 1.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);

  /*
  pOut->x = u*1.0f;
  pOut->y = v*1.0f;
  pOut->z = 5.0f;
  */
  

  /*
  pOut->x = sinf(u) * (1 + cosf(v)) ;
  pOut->y = sinf(u + 2  / 3) *(1 + cosf(v + 2  / 3)) ;
  pOut->z = sinf(u + 4  / 3) *(1 + cosf(v + 4  / 3)) ;
  */

  /*
  pOut->x = sinf(u)*cosf(v);
  pOut->y = cosf(u)*cosf(v);
  pOut->z = sinf(v);                
  */
}

__forceinline void Evaluate2(float u, float v, Vector3D* pOut)
{
  float r = 0;
  float theta = 2*PI*u;
  float phi = v*PI;

   static float m[8] = 
   {
      2.0, 2.0f, 2.0f, 3.0f, 2.0f, 6.0f, 4.0f, 3.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);

}

__forceinline void Evaluate3(float u, float v, Vector3D* pOut)
{
  float r = 0;
  float theta = 2*PI*u;
  float phi = v*PI;

  static float m[8] = 
 {
    6.0f, 6.0f, 4.0f, 0.0f, 2.0f, 4.0f, 0.0f, 1.0f
 };

 r += powf(sinf(m[0]*phi),(double)m[1]);
 r += powf(cosf(m[2]*phi),(double)m[3]);
 r += powf(sinf(m[4]*theta),(double)m[5]);
 r += powf(cosf(m[6]*theta),(double)m[7]);

 pOut->x = r * sinf(phi) * cosf(theta);
 pOut->y = r * cosf(phi);
 pOut->z = r * sinf(phi) * sinf(theta);   
}  

__forceinline void Evaluate4(float u, float v, Vector3D* pOut)
{
  float r = 0;
  float theta = 2*PI*u;
  float phi = v*PI;

  static float m[8] = 
   {
      5.0, 2.0f, 2.0f, 3.0f, 2.0f, 8.0f, 4.0f, 3.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);   
}

__forceinline void Evaluate5(float u, float v, Vector3D* pOut)
{
   float r = 0;
  float theta = 2*PI*u;
  float phi = v*PI;

   static float m[8] = 
   {
      4.0f, 3.0f, 2.0f, 4.0f, 6.0f, 2.0f, 6.0f, 1.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);
}


__forceinline void Evaluate6(float u, float v, Vector3D* pOut)
{
  float r = 0;
  float theta = 2*PI*u;
  float phi = v*PI;

   static float m[8] = 
   {
      5.0f, 0.0f, 5.0f, 1.0f, 3.0f, 3.0f, 3.0f, 1.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);
  
}


__forceinline void Evaluate7(float u, float v, Vector3D* pOut)
{
  float r = 0;
  float theta = 2*PI*u;
  float phi = v*PI;

   static float m[8] = 
   {
      1.0f, 2.0f, 3.0f, 4.0f, 2.0f, 5.0f, 6.0f, 1.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);
}


__forceinline void Evaluate8(float u, float v, Vector3D* pOut)
{
   float r = 0;
   float theta = 2*PI*u;
   float phi = v*PI;

   static float m[8] = 
   {
      1.0, 10.0f, 4.0f, 29.0f, 1.0f, 11.0f, 1.0f, 13.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);
}

__forceinline void Evaluate9(float u, float v, Vector3D* pOut)
{
   float r = 0;
   float theta = 2*PI*u;
   float phi = v*PI;

   static float m[8] = 
   {
      3.0, 10.0f, 2.0f, 6.0f, 7.0f, 11.0f, 4.0f, 13.0f
   };

   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);
}

__forceinline void Evaluate10(float u, float v, Vector3D* pOut)
{
   float r = 0;
   float theta = 2*PI*u;
   float phi = v*PI;

   static float m[8] = 
   {
      //5.0f, 0.0f, 5.0f, 1.0f, 3.0f, 3.0f, 3.0f
     0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
   };

   /*
   r += powf(sinf(m[0]*phi),(double)m[1]);
   r += powf(cosf(m[2]*phi),(double)m[3]);
   r += powf(sinf(m[4]*theta),(double)m[5]);
   r += powf(cosf(m[6]*theta),(double)m[7]);
   */

   r = 1.0f;

   pOut->x = r * sinf(phi) * cosf(theta);
   pOut->y = r * cosf(phi);
   pOut->z = r * sinf(phi) * sinf(theta);
}


struct WeirdGeometry
{
virtual void Evaluate(float u, float v, Vector3D* pOut) = 0;
virtual void GenerateData(Vector3D* pv3d, unsigned uStart, unsigned uRange)
{
  unsigned i,j;

  float v  = 0;
  float du = (1.0f)/(COLUMNS);
  float dv = (1.0f)/(ROWS);

  Vector3D* pOut = pv3d;
  
  float fMax = 0.0f;
  for (j = 0 ; j <= ROWS ; j++)
  {
    float u  = 0;
  
    for (i = 0 ; i <= COLUMNS ; i++)
    {        
      Evaluate(u, v, pOut);
      fMax = max(fMax, pOut->Length());

      pOut++;


      u += du;
    }

    v += dv;
  }



  // hacky normalization
  fMax = 30.0f/fMax;
  for (i = 0 ; i < (ROWS+1)*(COLUMNS+1) ; i++)
  {
    pv3d[i]*=fMax;
  }
  
}
};

/*
struct WeirdGeometry_1 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate1 (u, v, pOut); }  };
struct WeirdGeometry_2 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate2 (u, v, pOut); }  };
struct WeirdGeometry_3 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate3 (u, v, pOut); }  };
struct WeirdGeometry_4 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate4 (u, v, pOut); }  };
struct WeirdGeometry_5 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate5 (u, v, pOut); }  };
struct WeirdGeometry_6 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate6 (u, v, pOut); }  };
struct WeirdGeometry_7 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate7 (u, v, pOut); }  };
struct WeirdGeometry_8 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate8 (u, v, pOut); }  };
struct WeirdGeometry_9 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate9 (u, v, pOut); }  };
struct WeirdGeometry_10: public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate10(u, v, pOut); }  };
*/
struct WeirdGeometry_1 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate1 (u, v, pOut); }  };
struct WeirdGeometry_2 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate2 (u, v, pOut); }  };
struct WeirdGeometry_3 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate3 (u, v, pOut); }  };
struct WeirdGeometry_4 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate4 (u, v, pOut); }  };
struct WeirdGeometry_5 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate5 (u, v, pOut); }  };
struct WeirdGeometry_6 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate6 (u, v, pOut); }  };
struct WeirdGeometry_7 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate7 (u, v, pOut); }  };
struct WeirdGeometry_8 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate7 (u, v, pOut); }  };
struct WeirdGeometry_9 : public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate7 (u, v, pOut); }  };
struct WeirdGeometry_10: public WeirdGeometry { void Evaluate(float u, float v, Vector3D* pOut) { Evaluate7 (u, v, pOut); }  };




SM_GeomMorpher::SM_GeomMorpher      ()
{
}

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


int  SM_GeomMorpher::Init(int iShader, IKFAnimable* pAnimable)    
{
  m_iShader = iShader;
  m_pAnimable = pAnimable;

  return 0;
}

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

int  SM_GeomMorpher::Reset              ()
{
  return 0;
}

void SM_GeomMorpher::Render             (RenderContext* pRenderContext, int iOutcode, float fTime)
{
  static FVF_PosNormalDiffuseTex1 pVertices[(ROWSPERDP+1)*(COLUMNS+1)];    
  static unsigned short pusIndices[ROWSPERDP*COLUMNS*6];
  static bool bIndices = false;
  static bool bGeometries = false;

  // Fill out index list
  if (!bIndices)
  {
    unsigned i,j;
    unsigned short* pI = pusIndices;
    unsigned uCurrent = 0;
    for (j = 0 ; j<ROWS ; j++, uCurrent+=COLUMNS+1)
    {
      for (i = 0 ; i < COLUMNS ; i++)
      {
        /*
        unsigned uV = uCurrent+i;
        pI[0] = uV;
        pI[1] = uV+1;
        pI[2] = uV+COLUMNS+1;
        pI[3] = uV+COLUMNS+1;
        pI[4] = uV+1;
        pI[5] = uV+COLUMNS+2;
        pI+=6;
        */
        unsigned uV = uCurrent+i;


        
          pI[0] = uCurrent+i;
          pI[2] = uCurrent+((i+1)%COLUMNS);
          pI[1] = uCurrent+i+COLUMNS+1;
          pI[3] = uCurrent+i+COLUMNS+1;
          pI[5] = uCurrent+((i+1)%COLUMNS);
          pI[4] = uCurrent+((i+1)%COLUMNS)+COLUMNS+1;
          pI+=6;
        
        
      }
    }

    bIndices = true;
  }

  if (!bGeometries)
  {
    WeirdGeometry_1  Geo1;
    WeirdGeometry_2  Geo2;
    WeirdGeometry_3  Geo3;
    WeirdGeometry_4  Geo4;
    WeirdGeometry_5  Geo5;
    WeirdGeometry_6  Geo6;
    WeirdGeometry_7  Geo7;
    /*
    WeirdGeometry_8  Geo8;
    WeirdGeometry_9  Geo9;
    WeirdGeometry_10 Geo10;
    */


    Geo1.GenerateData(m_v3dDataBuffers[0], 0, 0);
    Geo2.GenerateData(m_v3dDataBuffers[1], 0, 0);
    Geo3.GenerateData(m_v3dDataBuffers[2], 0, 0);
    Geo4.GenerateData(m_v3dDataBuffers[3], 0, 0);
    Geo5.GenerateData(m_v3dDataBuffers[4], 0, 0);
    Geo6.GenerateData(m_v3dDataBuffers[5], 0, 0);
    Geo7.GenerateData(m_v3dDataBuffers[6], 0, 0);
    /*
    Geo8.GenerateData(m_v3dDataBuffers[7], 0, 0);
    Geo9.GenerateData(m_v3dDataBuffers[8], 0, 0);
    Geo10.GenerateData(m_v3dDataBuffers[9], 0, 0);
    */
    

    bGeometries =true;
  }

  assert(ROWSPERDP == ROWS);

  
  // Fill out positions/normals
  unsigned i;


  
  float fOffset = Timer::GetTime()*1.0f;

  float fFigure = fOffset*0.5f;

  int iA = int(fFigure)%FIGURES;
  int iB = (1+int(fFigure ))%FIGURES;

  float fLerp = fmodf(fOffset, 2.0f);
  if (fLerp <= 1.0f)
  {
    fLerp = 0.0f;
  }
  else
  {
    fLerp -= 1.0f;
    fLerp = sinf(fLerp*PI*0.5f);
  }
  


  for (i = 0 ; i < NVERTICES ; i++)
  {
    Vector3D* pA = m_v3dDataBuffers[iA];
    Vector3D* pB = m_v3dDataBuffers[iB];

    m_v3dPositions[i] = (pA[i]+fLerp*(pB[i]-pA[i]));
  }

  Vector3D* pvNormal=new Vector3D[NPRIMITIVES*3];
  int iTri;
  for (i=0, iTri=0 ; i<NPRIMITIVES*3 ; i+=3, iTri++)
  {
    pvNormal[iTri]=
      Vector3D::Cross(
        m_v3dPositions[pusIndices[i+1]]-m_v3dPositions[pusIndices[i]],
        m_v3dPositions[pusIndices[i+2]]-m_v3dPositions[pusIndices[i]]
        );
  }

  Vector3D* pvVertexNormal=new Vector3D[NVERTICES];
  memset(pvVertexNormal, 0, sizeof(Vector3D)*NVERTICES);
  for (i=0, iTri=0 ; i<NPRIMITIVES*3 ; i+=3, iTri++)
  {
    pvVertexNormal[pusIndices[i]]  +=pvNormal[iTri];
    pvVertexNormal[pusIndices[i+1]]+=pvNormal[iTri];
    pvVertexNormal[pusIndices[i+2]]+=pvNormal[iTri];
  }

  for (i=0 ; i<NVERTICES ; i++)
  {
    m_v3dNormals[i] = pvVertexNormal[i].Normalize();
  }

  delete[] pvNormal;
  delete[] pvVertexNormal;

  MeshElement me;
      
  me.m_iShader            =m_iShader;
  me.m_iVB                =-1;
  me.m_iIB                =-1;
  me.m_pIndices           =pusIndices;
  me.m_pVertices          =pVertices;
  me.m_uStartVertex       =0;
  me.m_uVertices          =NVERTICES;
  me.m_uStartIndex        =0;
  me.m_uPrimitives        =NPRIMITIVES;
  me.m_iActiveLightMask   =-1;  
  me.m_fDepth             =0.0f;


  Vector3D Pos;
  Quaternion q;


  if (m_pAnimable->m_pKeyFrameSequence)
  {
    m_pAnimable->m_pKeyFrameSequence->GetKeyFrame(fTime, 
                                                  m_pAnimable->m_fKeyFrameSequenceStartTime,
                                                  &Pos,
                                                  &q);
  }
  else
  {
    Pos = m_v3dPosition;
    q = m_qRotation;
  }


  
  ToTransform(&me.m_WorldTransform, &Pos, &q);
  
  Vector3D* pV = m_v3dPositions;
  Vector3D* pN = m_v3dNormals;

  FVF_PosNormalDiffuseTex1* pDVBVertices;
  unsigned short*           pDIBIndices;
  int                       iVertexOffset, iIndexOffset;        
  
  // Allocate VB and IB
  if ((iVertexOffset=ResourceManager::GiveVBChunk(ResourceManager::m_iPosNormalDiffuseTex1Stream, NVERTICES*sizeof(FVF_PosNormalDiffuseTex1), (void**)&pDVBVertices))==-1)
  {
    assert(0);
  }

  if ((iIndexOffset=ResourceManager::GiveIBChunk(ResourceManager::m_iIndexStream, NPRIMITIVES*3*sizeof(unsigned short), (void**)&pDIBIndices))==-1)
  {
    assert(0);
  }

  FVF_PosNormalDiffuseTex1* pG = pDVBVertices;
  for (i=0 ; i<NVERTICES ; i++)
  {      
    pG ->x=pV->x; pG ->y=pV->y; pG ->z=pV->z; 
    pV++;
    pG ->nx=pN->x; pG ->ny=pN->y; pG ->nz=pN->z;
    pN++;
    pG ->diffuse=0xFFFFFFFF;
    pG++;                     
  }    

  memcpy(pDIBIndices, pusIndices, sizeof(unsigned short)*NPRIMITIVES*3);

  ResourceManager::DoneVBChunk(ResourceManager::m_iPosNormalDiffuseTex1Stream);
  ResourceManager::DoneIBChunk(ResourceManager::m_iIndexStream);

  unsigned uStartIndex =iIndexOffset/sizeof(unsigned short);
  unsigned uStartVertex=iVertexOffset/sizeof(FVF_PosNormalDiffuseTex1);

  me.m_iVB          =ResourceManager::m_iPosNormalDiffuseTex1Stream;
  me.m_iIB          =ResourceManager::m_iIndexStream;
  me.m_pIndices     =0;
  me.m_pVertices    =0;    
  me.m_uStartVertex = uStartVertex;
  me.m_uStartIndex  = uStartIndex;

  RenderPipeline::Render(&me);
  RenderPipeline::Flush();   
}
