#include "SM_CommonFXPCH.h"
#include "SM_MathPch.h"
#include "SM_Engine3DPCH.h"
#include "SM_MathAux.h"



class PlasmaFX : public SM_DemoEffect
{
public:              
  static unsigned ColorFromVector(float x, float y, float z)
  {
    unsigned r = int((x*0.5f+0.5f)*255.0f)<<16;
    unsigned g = int((y*0.5f+0.5f)*255.0f)<<8;
    unsigned b = int((z*0.5f+0.5f)*255.0f)<<0;

    return 0xFF000000|r|g|b;
  }

  int      m_iShader;  

  PlasmaFX(const char * pcName) : SM_DemoEffect(pcName)
  {
  }

  virtual          ~PlasmaFX()
  {
  }

  int      Init(const char* pcCommand)
  {
    InitMathAux();

    char* pcCopy =0;
    char* pcToken;
    int   iReturn=-1;
    
    pcCopy=new char[strlen(pcCommand)+1];
    strcpy(pcCopy, pcCommand);

    pcToken=strtok(pcCopy, " \t");

    if (pcToken)
    if (strcmp(pcToken, "FILL THIS UP")==0)
    {
    }

    iReturn  = 0;
    
//FAILED:
    if (pcCopy) { delete[] pcCopy; pcCopy=0; }

    m_iShader = ShaderManager::LoadShader("plasma");


    
    return iReturn;        
  }

  int      Shutdown()
  {
    return (0);
  }

  int      Start(float fTime)
  {
    return (0);
  }

  int      Stop()
  {
    return (0);
  }

  int      Reset()
  {
    return 0;
  }

  #define SIZE      64
  
  FVF_PosDiffuseSpecularTex1 pVertices[SIZE*SIZE];
  unsigned short pusIndices[6*(SIZE-1)*(SIZE-1)];

  int      Run(float fTime)
  {    
    fTime*=2.0f;
    // Paint a poly example!!! 
    RenderContext RC;   

    static bool bInit = false;
    if (!bInit)
    {
      bInit = true;

      unsigned i,j;
      unsigned short* pI = pusIndices;
      unsigned uCurrent = 0;
      for (j = 0 ; j  <SIZE-1 ; j++, uCurrent+=SIZE)
      {
        for (i = 0 ; i < SIZE-1 ; i++)
        {
          pI[0] = uCurrent+i;
          pI[1] = uCurrent+i+SIZE;
          pI[2] = uCurrent+(i+1);          
          pI[3] = uCurrent+i+SIZE;
          pI[4] = uCurrent+(i+1)+SIZE;
          pI[5] = uCurrent+(i+1);
          
          pI+=6;                   
        }
      }
    }

    RC.Set(
      //Vector3D(0.0f, 0.0f, 4.5f+sinf(fTime*2.0f)),
      Vector3D(0.0f, 0.0f, 4.5f),
      Vector3D(0.0f, 0.0f, 0.0f),
      Vector3D(sinf(-fTime), cosf(fTime+1.0f*sinf(fTime*0.3f)), 0.0f).Normalize(),
      //Vector3D(0.0f, 1.0f, 0.0f),
      90,
      0.75f,
      1.0f,
      200.0f);

    RC.SetViewport(0, 0, 640.0f, 480.0f);  
  
    RC.SyncRasterizer();
    RC.UpdateFrustum();

    ShaderManager::SetRenderContext(&RC);

    
    int i,j;

    float x,y,z;

    #define SCALE 0.35f
    #define DY 2.0f*SCALE

    x = 0.0f;
    y = DY*float(-SIZE/2);
    z = 0.0f;

    

    float fOffset = fmodf(fTime+1.0f+0.5f*SINF(fTime), 1.0f);

    fTime*=0.002f;

    float f11, f12,
          f21, f22;

    f11=-float(cos(fTime));
    f21=-float(sin(fTime));
    f12=float(sin(fTime));
    f22=-float(cos(fTime));

    float a_dudx[SIZE];
    float a_dvdx[SIZE];
    float a_dudy[SIZE];
    float a_dvdy[SIZE];

    
    float baseu=2.0f*(cosf(fTime)*96.0f+sinf(fTime*2.0f)*30.0f);
    float basev=2.0f*(sinf(fTime*1.37f)*64.0f+cosf(fTime*4.0f)*40.0f);
    
    
    for (i=0 ; i<SIZE ; i++)
      {
        float a,b,c,d;
        
        a=0.5f+sinf(fTime*2.0f+float(i)/3.1416f)/3.0f;
        b=0.5f+cosf(fTime*1.0f+float(i)/4.1416f)/2.0f;
        c=0.25f+cosf(fTime*2.0f+float(i)/4.1416f)/3.0f;
        d=-(0.5f+sinf(fTime*1.0f+float(i)/5.1416f)/3.0f);      




        a_dudx[i]=0.1f*(a*f11+b*f21);
        a_dvdx[i]=0.1f*(a*f12+b*f22);
        a_dudy[i]=0.1f*(c*f11+d*f21);
        a_dvdy[i]=0.1f*(c*f12+d*f22);
      }


    for (j = 0 ; j < SIZE ; j++)
    {
        
      FVF_PosDiffuseSpecularTex1* pV = pVertices+j*SIZE;    

      float tu = baseu;
      float tv = basev;
      for (i = 0 ; i < SIZE ; i++)
      {
       
        float u = float(i)/float(SIZE-1);
        float v = float(j)/float(SIZE-1);

        #define WIDTH 12.0f
        pV[i].x = u*WIDTH-WIDTH/2.0f;
        pV[i].y = v*WIDTH-WIDTH/2.0f;
        pV[i].z = 0.5+0.7f*sinf(fTime*10.0f+i*0.03f+j*0.02);        
        pV[i].u = tu;
        pV[i].v = tv;
        pV[i].diffuse = 0xFFFFFFFF;         
        
        tu += a_dudx[i];
        tv += a_dvdx[i];
        
      }
      
      baseu += a_dudy[j];
      basev += a_dvdy[j];
    }

    int iTri;
    Vector3D pvNormal[(SIZE-1)*(SIZE-1)*2];
    Vector3D pvUGradient[(SIZE-1)*(SIZE-1)*2];


    for (i=0, iTri=0 ; i<(SIZE-1)*(SIZE-1)*6 ; i+=3, iTri++)
    {
      FVF_PosDiffuseSpecularTex1* pV1 =  &pVertices[pusIndices[i]];
      FVF_PosDiffuseSpecularTex1* pV2 =  &pVertices[pusIndices[i+1]];
      FVF_PosDiffuseSpecularTex1* pV3 =  &pVertices[pusIndices[i+2]];

      Vector3D v1(pV1->x, pV1->y, pV1->z);
      Vector3D v2(pV2->x, pV2->y, pV2->z);
      Vector3D v3(pV3->x, pV3->y, pV3->z);

      pvNormal[iTri]=
        Vector3D::Cross(
          v3-v2,
          v2-v1).Normalize();

      if (iTri & 1)
      {
        pvUGradient[iTri]=(v2-v1).Normalize();
      }
      else
      {
        pvUGradient[iTri]=(v3-v1).Normalize();
      }
    }
    


    Vector3D pvVertexNormal[SIZE*SIZE];
    Vector3D pvUVertexGradient[SIZE*SIZE];
    Vector3D pvVVertexGradient[SIZE*SIZE];

    memset(pvVertexNormal, 0, sizeof(Vector3D)*SIZE*SIZE);
    memset(pvUVertexGradient, 0, sizeof(Vector3D)*SIZE*SIZE);
    
    for (i=0, iTri=0 ; i<(SIZE-1)*(SIZE-1)*6 ; i+=3, iTri++)
    {
      pvVertexNormal[pusIndices[i]]  +=pvNormal[iTri];
      pvVertexNormal[pusIndices[i+1]]+=pvNormal[iTri];
      pvVertexNormal[pusIndices[i+2]]+=pvNormal[iTri];

      pvUVertexGradient[pusIndices[i]]  +=pvUGradient[iTri];
      pvUVertexGradient[pusIndices[i+1]]+=pvUGradient[iTri];
      pvUVertexGradient[pusIndices[i+2]]+=pvUGradient[iTri];
      
    }

    for (i=0 ; i<SIZE*SIZE ; i++)
    {
      pvVertexNormal[i].Normalize();
      //pVertices[i].nx = pvVertexNormal[i].x;
      //pVertices[i].ny = pvVertexNormal[i].y;
      //pVertices[i].nz = pvVertexNormal[i].z;
    }

    Vector3D v3dCamera = RC.GetViewport()->m_v3dPosition;


    

    Vector3D LightPos = v3dCamera+RC.m_VRP*0.0f*sinf(fTime)+RC.m_VUP*0.0f*cosf(fTime)+RC.m_VPN*0.0f;
        
    
   FVF_PosDiffuseSpecularTex1* pV = pVertices;      
   for (i=0 ; i<SIZE*SIZE ; i++)
   {
      pvUVertexGradient[i].Normalize();
      pvVVertexGradient[i] = Vector3D::Cross(pvVertexNormal[i], pvUVertexGradient[i]);

      Vector3D Light = (LightPos-Vector3D(pV[i].x, pV[i].y, pV[i].z)).Normalize();

      Vector3D v3dEye = v3dCamera-Vector3D(pV[i].x, pV[i].y, pV[i].z);
      v3dEye.Normalize();
      Vector3D H = (v3dEye+Light)*0.5f;
      H.Normalize();


      if ((i+1)%SIZE == 0)
      {
        pV[i].diffuse = pV[i-SIZE+1].diffuse;
        pV[i].specular= pV[i-SIZE+1].specular;
      }
      else
      {
        pV[i].diffuse = ColorFromVector( Light.x*pvUVertexGradient[i].x+Light.y*pvUVertexGradient[i].y+ Light.z*pvUVertexGradient[i].z,
                                         Light.x*pvVVertexGradient[i].x+Light.y*pvVVertexGradient[i].y+ Light.z*pvVVertexGradient[i].z,
                                         Light.x*pvVertexNormal[i].x   +Light.y*pvVertexNormal[i].y   + Light.z*pvVertexNormal[i].z);
        pV[i].specular = ColorFromVector( H.x*pvUVertexGradient[i].x+H.y*pvUVertexGradient[i].y+ H.z*pvUVertexGradient[i].z,
                                          H.x*pvVVertexGradient[i].x+H.y*pvVVertexGradient[i].y+ H.z*pvVVertexGradient[i].z,
                                          H.x*pvVertexNormal[i].x   +H.y*pvVertexNormal[i].y   + H.z*pvVertexNormal[i].z);
      }      
    }
       
    ShaderManager::GetShader(m_iShader)->SetShaderState(0);

    SM_D3d::SetRenderState(D3DRS_LIGHTING, FALSE);  
    SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*) &Matrix4X4::Identity);
    SM_D3d::Device()->SetVertexShader(FVF_POSDIFFUSESPECULARTEX1);    


    //SM_D3d::SetRenderState(D3DRS_WRAP0, D3DWRAP_U |D3DWRAP_V);
    //SM_D3d::SetRenderState(D3DRS_WRAP1, D3DWRAP_U |D3DWRAP_V);

    SM_D3d::SetRenderState(D3DRS_ALPHATESTENABLE,          FALSE);

    //SM_D3d::SetTextureStageState(0,  D3DTSS_MIPFILTER, D3DTEXF_NONE);
    //SM_D3d::SetTextureStageState(1,  D3DTSS_MIPFILTER, D3DTEXF_NONE);


    
    SM_D3d::Device()->SetTexture(0, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[1].m_iTextureID));

    SM_D3d::SetRenderState(D3DRS_TEXTUREFACTOR, 0xFF606060);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP  , D3DTOP_MODULATE);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAOP  , D3DTOP_SELECTARG1);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
  

    
    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP   , D3DTOP_DISABLE); 
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP   , D3DTOP_DISABLE); 
    

    SM_D3d::Device()->DrawIndexedPrimitiveUP(
            D3DPT_TRIANGLELIST  ,
            0,
            SIZE*SIZE,
            (SIZE-1)*(SIZE-1)*2,
            pusIndices,
            D3DFMT_INDEX16 ,
            pVertices,
            sizeof(FVF_PosDiffuseSpecularTex1));   


    SM_D3d::Device()->SetTexture(0, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[0].m_iTextureID));
    
    SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP  , D3DTOP_DOTPRODUCT3);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAOP  , D3DTOP_SELECTARG1);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
  

    
    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP   , D3DTOP_MODULATE); 
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG1 , D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG2 , D3DTA_TEXTURE);
    

    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP  , D3DTOP_SELECTARG1);
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

    SM_D3d::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);  
    SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);  
    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

    
    SM_D3d::Device()->SetTexture(1, 0);
    SM_D3d::Device()->SetTexture(1, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[1].m_iTextureID));
    
    SM_D3d::Device()->DrawIndexedPrimitiveUP(
            D3DPT_TRIANGLELIST  ,
            0,
            SIZE*SIZE,
            (SIZE-1)*(SIZE-1)*2,
            pusIndices,
            D3DFMT_INDEX16 ,
            pVertices,
            sizeof(FVF_PosDiffuseSpecularTex1));   

    SM_D3d::Device()->SetTexture(0, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[0].m_iTextureID));
    SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP  , D3DTOP_DOTPRODUCT3);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_SPECULAR);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);

    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP  , D3DTOP_MODULATE);
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);

    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP  , D3DTOP_MODULATE);
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);

    SM_D3d::SetRenderState(D3DRS_SRCBLEND , D3DBLEND_SRCALPHA);
    SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);  
    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

        
    SM_D3d::Device()->DrawIndexedPrimitiveUP(
            D3DPT_TRIANGLELIST  ,
            0,
            SIZE*SIZE,
            (SIZE-1)*(SIZE-1)*2,
            pusIndices,
            D3DFMT_INDEX16 ,
            pVertices,
            sizeof(FVF_PosDiffuseSpecularTex1));   
    
    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP  , D3DTOP_DISABLE);    
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP  , D3DTOP_DISABLE);
    SM_D3d::Device()->SetTexture(0, 0);
    SM_D3d::Device()->SetTexture(1, 0);    


    
    RenderPipeline::Flush();
            
    return 1;
  }

  int      Command           (float fTime, const char* pcCommand)
  {
    SM_DemoEffect::Command(fTime, pcCommand);
    char* pcCopy =0;
    char* pcToken;
    int   iReturn=-1;
    
    pcCopy=new char[strlen(pcCommand)+1];
    strcpy(pcCopy, pcCommand);

    pcToken=strtok(pcCopy, " \t");

    if (pcToken)
    if (strcmp(pcToken, "FILL THIS UP")==0)
    {
    }

    iReturn  = 0;
    
    if (pcCopy) { delete[] pcCopy; pcCopy=0; }
    return iReturn;
  }  
};

PlasmaFX Trauma("PLASMA");

DEFINE_EFFECT(PlasmaFX)


